2
0
Fork 0

WIP open file for a while

This commit is contained in:
Willem 2017-06-10 16:53:29 +02:00
parent f937019e42
commit d280fb9af3
122 changed files with 5702 additions and 10 deletions

4
.gitignore vendored
View file

@ -10,10 +10,10 @@ npm-debug.log
test/data
# Ignore example data
example/node_modules
example/npm-debug.log
example/www_static/css/lib
example/www_static/js/lib
example/www_logs
example/lib_app
# Ignore binary files
*.o

20
TODO.md Normal file
View file

@ -0,0 +1,20 @@
TODO
=========
Small todo list
## angular
* add downloads
* (paging)
* field types
* validation
* tabs for child entities
* i18n
## server
* ldap support
* route page
* keyyed sub list
* menu

View file

@ -0,0 +1,45 @@
{
"winston": {
"console": {
"level": "debug",
"silent": false,
"colorize": true,
"timestamp": null
},
"file": {
"level": "debug",
"silent": false,
"colorize": false,
"timestamp": null,
"json": false,
"filename": "www_logs/server.log"
}
},
"server": {
"httpPort": 8008,
"httpTrustProxy": true,
"sessionSecret": "tcrudIDKEy",
"sessionTTL": "14 * 24 * 60 * 60"
},
"application": {
"name": "TCrud Example Server",
"index": {
"pageTitle": "TCrud Example Server",
"pageKeywords": "node,crud,api,json,xml,views"
}
},
"options": {
"static": {
"maxAge": 86400000
},
"cookieParser": {
"secretKey": "test88test"
},
"rss": {
"link": "http://localhost:8008",
"author": "TCrud",
"options": {
}
}
}
}

61
example/example.js Normal file
View file

@ -0,0 +1,61 @@
'use strict';
var config = require('./example-config.json');
var favicon = require('static-favicon');
var cookieParser = require('cookie-parser')
var path = require('path');
var winston = require('winston');
winston.loggers.add('main',config.winston);
var log = winston.loggers.get('main');
var expressWinston = require('express-winston');
var tcrud = require('../lib/node-ff-tcrud');
var fs = require('fs');
// PHASE_1: load extra plugins
//tcrud.setup.pluginLoad(new MyPlugin());
require('./lib/pg-moviedb').load(tcrud);
require('./lib/pg-pagila').load(tcrud);
// PHASE_2: enable plugins
tcrud.setup.pluginEnableAll();
// PHASE_3: start config
tcrud.setup.phaseConfig();
tcrud.config.getRootTEntity().tmeta.tplugin.formatXML.tslug = 'xml_is_free';
tcrud.config.getRootTEntity().tmeta.tplugin.formatCSV.tslug = 'csv4all';
var tPostgresDB = tcrud.config.createTEntityNode(tcrud.config.getRootTEntity(),'pg');
var tMongoose = tcrud.config.createTEntityNode(tcrud.config.getRootTEntity(),'mongoose');
require('./lib/pg-moviedb').setup(tcrud,tPostgresDB);
require('./lib/pg-pagila').setup(tcrud,tPostgresDB);
//require('./lib/mongoose-blog').setup(tcrud,tMongoose);
// PHASE_4: finalize config
tcrud.setup.phaseServer();
//tcrud.setup.expressSimple(); or complex;
var server = tcrud.setup.expressCreate();
server.use(expressWinston.logger({
transports: [new winston.transports.Console({json: false,colorize: true})],
meta: false,
expressFormat: true
}));
var buildOptions = {
viewsDir: path.join(__dirname, 'www_views')
}
tcrud.setup.expressBuild(buildOptions);
server.use(favicon());
server.use(cookieParser(config.options.cookieParser.secretKey));
//server.get('/ui/thtml/*', tcrud.setup.express.renderTemplatePath('thtml/'));
server.use(expressWinston.errorLogger({
transports: [new winston.transports.Console({json: false,colorize: true})]
}));
tcrud.setup.expressListen();
//PHASE_6: post boot to from self.
tcrud.setup.phaseServerUp();

View file

@ -0,0 +1,26 @@
var ldap = require('ldapjs');
module.exports = {
setup: setup
};
function setup(tcrud,tcrudModel) {
var client = ldap.createClient({
url: 'ldap://127.0.0.1:389'
});
// Create backend with id and uri
tcrud.plugin.backend.ldapjs.registrate('ldapjs/main',client);
// Create tcrud models
var tc = tcrud.config;
var t = tc.createTEntityNode(tcrudModel,'ldapjs');
t.tmeta.tmodel.tbackend = 'ldapjs/main';
// Define model and columns
var tUser = tc.createTEntity(t,'cn=foo, o=example','cn');
var tUserId = tc.createTField(tUser,'uid');
var tUserName = tc.createTField(tUser,'sn');
var tUserEmail = tc.createTField(tUser,'email');
}

View file

@ -0,0 +1,153 @@
var mongoose = require('mongoose');
var log = require('winston').loggers.get('main');
var tcrud = require('../../lib/node-ff-tcrud');
module.exports = {
setup: setup
};
function setup(tcrud,tcrudModel) {
var mongoUrl = 'mongodb://localhost:27017/blog';
var mongoOptions = {
db: {
fsync: false,
journal: false,
native_parser: true,
forceServerObjectId: true
},
server: {
poolSize: 4,
socketOptions: {
connectTimeoutMS: 500,
keepAlive: 1,
auto_reconnect: true
}
}
};
log.info('Connecting to: ' + mongoUrl);
var conn = mongoose.createConnection(mongoUrl,mongoOptions);
tcrud.plugin.backend.mongoose.registrate('mongoose/blog',conn);
conn.model('blog-state', modelSchemaBlogState, 'blog_state');
var tc = tcrud.config;
var crudAdmin = tc.createTEntityNode(tcrudModel,'admin');
crudAdmin.troles.push('admin');
var crudAdminModels = tcrud.plugin.backend.mongoose.buildTEntityModels(conn,crudAdmin);
log.info('crud admin models created: '+crudAdminModels.length);
crudAdminModels.forEach(function(model) {
model.tmeta.tmodel.tbackend = 'mongoose/blog';
});
}
var modelMetaBlogState = {
name: {
type: String,
trim: true,
index: { unique: true },
tfield: {
tvalidate: { io: 'string' },
},
},
type: {
type: String,
trim: true,
index: { unique: false },
tfield: {
tvalidate: { io: 'string' },
},
},
value: {
type: String,
trim: true,
tfield: {
xtype: 'textarea',
tvalidate: { io: 'string' },
},
},
description: {
type: String,
trim: true,
tfield: {
ttype: 'textarea',
tvalidate: { io: 'string' },
},
},
changed_date: {
type: Date,
default: Date.now,
tfield: {
tlist: { tenable: false },
},
},
created_date: {
type: Date,
default: Date.now,
tfield: {
tlist: { tenable: false },
},
}
};
var modelSchemaBlogState = new mongoose.Schema(modelMetaBlogState);
modelSchemaBlogState.statics = tcrud.plugin.backend.mongoose.buildStaticsModelValidated(modelMetaBlogState,modelSchemaBlogState, {
findLastChangedLimit5: function (callback) {
log.debug(modelBackend+'.findLastChangedLimit5');
this.find({}).sort('-changed_date').limit(5).exec(callback);
},
findOneByName: function (name, callback) {
log.debug(modelBackend+'.findByName name='+name);
this.findOne({name:name}).exec(callback);
},
ensureExcists: function (name, type, defaultValue, description, callback) {
this.findOneByName(name, function(err, xprop) {
if (err) {
callback(err);
return;
}
if (xprop == null) {
log.debug(modelBackend+'.getByName create name='+name+' defaultValue='+defaultValue);
var model = mongoose.model('blog-state');
xprop = new model();
xprop.name = name;
xprop.type = type;
xprop.value = defaultValue;
xprop.description = description;
xprop.save(function(err,xprop) {
if (callback) {
callback(err, xprop);
}
});
} else {
log.debug(modelBackend+'.getByName fetched name='+name);
if (callback) {
callback(null, xprop);
}
}
});
},
setByName: function (name, value, callback) {
this.findOneByName(name, function(err, xprop) {
if (err) { throw err }
log.debug(modelBackend+'.setByName name='+name+' valueNew='+value+' valueOld='+xprop.value);
xprop.value = value;
xprop.save(function(err) {
callback(err, xprop);
});
});
},
incByName: function (name, callback) {
this.findOneByName(name, function(err, xprop) {
if (err) { throw err }
xprop.value++;
log.debug(modelBackend+'.incByName name='+name+' value='+xprop.value);
xprop.save(function(err) {
callback(err, xprop);
});
});
},
});

60
example/lib/pg-moviedb.js Normal file
View file

@ -0,0 +1,60 @@
var pgDB = require('pg');
var pgDBNamed = require('node-postgres-named');
module.exports = {
setup: setup,
load: load
};
function load(tcrud) {
// Create backend with id and uri
tcrud.backend.database.loadPostgres('pg/moviedb','postgres://postgres:postgresql@localhost/moviedb',pgDB,pgDBNamed);
}
function setup(tcrud,tcrudModel) {
// Create tcrud models
var tc = tcrud.config;
var t = tc.createTEntityNode(tcrudModel,'moviedb');
t.tmeta.tmodel.tbackend = 'pg/moviedb';
// Define model and columns
var tCompany = tc.createTEntity(t,'company','company_id');
var tCompanyId = tc.createTField(tCompany,'company_id');
var tCompanyName = tc.createTField(tCompany,'name');
var tCompanyDateEst = tc.createTField(tCompany,'date_est');
var tCompanyRemarks = tc.createTField(tCompany,'remarks');
var tCompanyCountryId = tc.createTField(tCompany,'country_id');
var tCountry = tc.createTEntity(t,'country','country_id');
var tCountryCompany = tc.createTEntity(tCountry,'company','company_id');
var tCountryCompanyId = tc.createTField(tCountryCompany,'company_id');
var tCountryCompanyName = tc.createTField(tCountryCompany,'name');
var tCountryCompanyDateEst = tc.createTField(tCountryCompany,'date_est');
var tCountryCompanyRemarks = tc.createTField(tCountryCompany,'remarks');
var tCountryCompanyCountryId = tc.createTField(tCountryCompany,'country_id');
var tCountryId = tc.createTField(tCountry,'country_id');
var tCountryCode = tc.createTField(tCountry,'code');
var tCountryName = tc.createTField(tCountry,'name');
var tDirector = tc.createTEntity(t,'director','director_id');
var tDirectorId = tc.createTField(tDirector,'director_id');
var tDirectorDateBorn = tc.createTField(tDirector,'date_born');
var tDirectorDateDied = tc.createTField(tDirector,'date_died');
var tDirectorFirstName = tc.createTField(tDirector,'first_name');
var tDirectorLastName = tc.createTField(tDirector,'last_name');
var tGenre = tc.createTEntity(t,'genre','genre_id');
var tGenreId = tc.createTField(tGenre,'genre_id');
var tGenreCode = tc.createTField(tGenre,'code');
var tGenreName = tc.createTField(tGenre,'name');
tCompany.tmeta.tmenu.ticon='fa fa-building';
//tCountryCompany.tmeta.tmenu.ticon='fa fa-building';
tCountry.tmeta.tmenu.ticon='fa fa-globe';
tDirector.tmeta.tmenu.ticon='fa fa-cogs';
tGenre.tmeta.tmenu.ticon='fa fa-star';
}

31
example/lib/pg-pagila.js Normal file
View file

@ -0,0 +1,31 @@
var pgDB = require('pg');
var pgDBNamed = require('node-postgres-named');
module.exports = {
load: load,
setup: setup
};
function load(tcrud) {
// Create backend with id and uri
tcrud.backend.database.loadPostgres('pg/pagila','postgres://postgres:postgresql@localhost/pagila',pgDB,pgDBNamed);
}
function setup(tcrud,tcrudModel) {
// Create tcrud models
var tc = tcrud.config;
var t = tc.createTEntityNode(tcrudModel,'pagila');
t.tmeta.tmodel.tbackend = 'pg/pagila';
// Define model and columns
var tLanguage = tc.createTEntity(t,'language','language_id');
var tLanguageId = tc.createTField(tLanguage,'language_id');
var tLanguageName = tc.createTField(tLanguage,'name');
var tLanguageLastUpdate = tc.createTField(tLanguage,'last_update');
var tCountry = tc.createTEntity(t,'country','country_id');
var tCountryId = tc.createTField(tCountry,'country_id');
var tCountryCountry = tc.createTField(tCountry,'country');
var tCountryLastUpdate = tc.createTField(tCountry,'last_update');
}

21
example/package.json Normal file
View file

@ -0,0 +1,21 @@
{
"name": "node-ff-tcrud-example",
"description": "node-ff-tcrud-example",
"version": "1.0.0",
"private": true,
"scripts": {
"start": "node example.js"
},
"dependencies": {
"cookie-parser": "^1.0.1",
"debug": "^2.2.0",
"express-winston": "0.3.1",
"static-favicon": "~1.0.0",
"winston": "~0.7.3",
"node-postgres-named": "^2.2.0",
"pg": "^4.4.0",
"mssql": "^2.1.6",
"mysql2": "^0.15.8",
"mongoose": "~3.8.13"
}
}

View file

@ -0,0 +1,10 @@
pageRouteInit.push(function ($routeProvider, $locationProvider) {
$routeProvider.when('/ui/bar', {
templateUrl: '/ui/thtml/bar',
controller: PageFoo
});
});
function PageFoo($scope, $http) {
}

View file

@ -0,0 +1,10 @@
pageRouteInit.push(function ($routeProvider, $locationProvider) {
$routeProvider.when('/ui/foo', {
templateUrl: '/ui/thtml/foo',
controller: PageFoo
});
});
function PageFoo($scope, $http) {
}

View file

@ -0,0 +1,10 @@
pageRouteInit.push(function ($routeProvider, $locationProvider) {
$routeProvider.when('/ui', {
templateUrl: '/ui/thtml/home',
controller: PageHome
});
});
function PageHome($scope, $http) {
}

View file

@ -0,0 +1,4 @@
<div>
<h2>Bar</h2>
<p>Welcome to the bar.</p>
</div>

View file

@ -0,0 +1,4 @@
<div>
<h2>Foo</h2>
<p>Welcome to the foo.</p>
</div>

View file

@ -0,0 +1,4 @@
<div>
<h2>Example UI Index</h2>
<p>Welcome make yourself at home.</p>
</div>

247
lib/backend/database.js Normal file
View file

@ -0,0 +1,247 @@
var tcrudSetup = require('./../tcrud-setup');
var debug = require('debug')('ff:tcrud:backend:database');
module.exports = {
loadModule: function(key,dbModule) {
tcrudSetup.pluginLoad(new DatabasePlugin(key,dbModule));
},
loadPostgres: function(key,dbUri,pgDB,pgDBNamed) {
tcrudSetup.pluginLoad(new DatabasePlugin(key,new PostgresModule(dbUri,pgDB,pgDBNamed)));
},
loadMysql2: function(key,pool) {
tcrudSetup.pluginLoad(new DatabasePlugin(key,new Mysql2Module(pool)));
},
loadMssql: function(key,driver,conn) {
tcrudSetup.pluginLoad(new DatabasePlugin(key,new MssqlModule(driver,conn)));
}
}
//------- PostgresModule Object
function PostgresModule(dbUri,pgDB,pgDBNamed) {
this.dbUri = dbUri;
this.pgDB = pgDB;
this.pgDBNamed = pgDBNamed;
}
PostgresModule.prototype.query = function(sqlQuery,params, cb) {
var self = this;
self.pgDB.connect(self.dbUri, function(err, client, done) {
self.pgDBNamed.patch(client);
client.query(sqlQuery, params, function(err, result) {
done(); // release client from pool
cb(err,result);
});
});
}
function Mysql2Module(pool) {
this.pool = pool;
}
Mysql2Module.prototype.query = function(sqlQuery,params, cb) {
var self = this;
self.pool.getConnection(function(err, connection) {
connection.config.namedPlaceholders = true;
connection.execute(sqlQuery,params, function(err, rows) {
connection.release();
cb(err,rows);
});
});
}
function MssqlModule(driver,conn) {
this.driver = driver;
this.conn = conn;
}
MssqlModule.prototype.query = function(sqlQuery,params, cb) {
var self = this;
var request = new self.driver.Request(self.conn);
request.query(sqlQuery,params,function(err, rows) {
cb(err,rows);
});
}
//------- DatabasePlugin Object
function DatabasePlugin(key,dbModule) {
this.key = key;
this.dbModule = dbModule;
}
DatabasePlugin.prototype.configPlugin = function (ctx) {
ctx.key='db#'+this.key;
ctx.description='Database api adapter for '+this.key;
};
DatabasePlugin.prototype.createBackend = function() {
return new DatabaseBackend(this);
}
DatabasePlugin.prototype.query = function(sqlQuery,params, cb) {
this.dbModule.query(sqlQuery,params,cb);
}
// -------- DatabaseBackend
function findKeysView(tview) {
return tview.tkeys;
}
function findKeysModel(tview) {
return tview.tmeta.tmodel.tkeys;
}
function selectField(tview,crudType) {
var result = [];
tview[crudType].tfields.forEach(function (tfieldKey) {
var tfield = tview.tmeta.tfields[tfieldKey];
result.push(tfield.tid);
});
var resultSql = '';
for (var i = 0; i < result.length; i++) {
var key = result[i];
resultSql += key;
if (i < (result.length - 1)) {
resultSql += ',';
}
}
return resultSql;
}
function whereKeys(tview) {
var resultSql = '';
for (var i = 0; i < tview.tmeta.tmodel.tkeys.length; i++) {
var key = tview.tmeta.tmodel.tkeys[i];
//resultSql += key +' = $'+(i+1);
resultSql += key +' = $'+key;
if (i < (tview.tmeta.tmodel.tkeys.length - 1)) {
resultSlug += ' AND ';
}
}
return resultSql;
}
function updateValues(tview,data) {
var resultSql = 'SET ';
var dataKeys = Object.keys(data);
for (var i = 0; i < dataKeys.length; i++) {
var key = dataKeys[i];
var skip = false;
for (var ii = 0; ii < tview.tmeta.tmodel.tkeys.length; ii++) {
var keyPK = tview.tmeta.tmodel.tkeys[ii];
if (key === keyPK) {
skip = true;
break;
}
}
if (skip === true) {
continue;
}
resultSql += key +' = $'+key;
if (i < (dataKeys.length - 1)) {
resultSql += ',';
}
}
return resultSql;
}
function DatabaseBackend(plugin) {
this.plugin = plugin;
}
DatabaseBackend.prototype.getKey = function() {
return this.plugin.key;
}
DatabaseBackend.prototype.findAll = function(tview,crudType) {
var self = this;
return function(data, dataParam, cb) {
var querySql = 'SELECT '+selectField(tview,crudType)+' FROM '+tview.tmeta.tmodel.tid+'';
debug('findAll %s',querySql);
var query = self.plugin.query(querySql,[],function (err, result) {
if (err) {
debug(err);
cb(err);
} else {
cb(err,result.rows);
}
});
};
}
DatabaseBackend.prototype.createNew = function(tview,crudType) {
var self = this;
return function(data, dataParam, cb) {
};
}
DatabaseBackend.prototype.findOne = function(tview,crudType) {
var self = this;
return function(data, dataParam, cb) {
var querySql = 'SELECT '+selectField(tview,crudType)+' FROM '+tview.tmeta.tmodel.tid+' WHERE '+whereKeys(tview)+'';
debug('findOne param %s',JSON.stringify(dataParam));
debug('findOne sql %s',querySql);
var query = self.plugin.query(querySql, dataParam, function (err, result) {
if (err) {
debug(err);
cb(err);
} else {
cb(err,result.rows[0]);
}
});
};
}
DatabaseBackend.prototype.updateOne = function(tview,crudType) {
var self = this;
return function(data, dataParam, cb) {
var querySql = 'UPDATE '+tview.tmeta.tmodel.tid+' '+updateValues(tview,data)+' WHERE '+whereKeys(tview);
debug('updateOne param %s',JSON.stringify(dataParam));
debug('updateOne data %s',JSON.stringify(data));
debug('updateOne sql %s',querySql);
var query = self.plugin.query(querySql, data, function (err, result) {
if (err) {
debug(err);
cb(err);
} else {
cb(err,result.rows[0]);
}
});
};
}
DatabaseBackend.prototype.deleteOne = function(tview,crudType) {
var self = this;
return function(data, dataParam, cb) {
var querySql = 'DELETE FROM '+tview.tmeta.tmodel.tid+' WHERE '+whereKeys(tview);
debug('deleteOne param %s',JSON.stringify(dataParam));
debug('deleteOne sql %s',querySql);
var query = self.plugin.query(querySql, dataParam, function (err, result) {
if (err) {
debug(err);
cb(err);
} else {
cb(err,result.rows[0]);
}
});
};
}
DatabaseBackend.prototype.findAllCount = function(tview,crudType) {
var self = this;
return function(data, dataParam, cb) {
var querySql = 'SELECT count(*) FROM '+tview.tmeta.tmodel.tid; //+' WHERE '+whereKeys(tview);
debug('findAllCount param %s',JSON.stringify(dataParam));
debug('findAllCount sql %s',querySql);
var query = self.plugin.query(querySql, dataParam, function (err, result) {
if (err) {
debug(err);
cb(err);
} else {
cb(err,result.rows[0]);
}
});
};
}

150
lib/backend/ldapjs.js Normal file
View file

@ -0,0 +1,150 @@
var config = require('./../tcrud-config');
var debug = require('debug')('ff:tcrud:ldapjs');
module.exports = {
registrate: function(key,client) {
config.registratePlugin(new LdapJSPlugin(key,client));
}
}
// ------- LdapJSPlugin Object
function LdapJSPlugin(key,client) {
this.key = key;
this.client = client;
}
LdapJSPlugin.prototype.configPlugin = function (ctx) {
ctx.key='ldapjs#'+this.key;
ctx.description='Adds ldap backend support.';
};
LdapJSPlugin.prototype.createBackend = function() {
return new LdapJSBackend(this);
}
// -------- LdapJSBackend Object
function LdapJSBackend(plugin) {
this.plugin = plugin;
}
LdapJSBackend.prototype.getKey = function() {
return this.plugin.key;
}
LdapJSBackend.prototype.findAll = function(tview,crudType) {
var self = this;
return function(data, dataParam, cb) {
var opts = {
filter: '(&(l=Seattle)(email=*@foo.com))',
scope: 'sub'
};
debug('findAll %s',opts);
self.plugin.client.search('o=example', opts, function(err, res) {
if (err) {
debug(err);
cb(err);
return;
}
//var querySql = 'SELECT '+selectField(tview,crudType)+' FROM '+tview.tmeta.tmodel.tid+'';
var result = [];
res.on('searchEntry', function(entry) {
result.push(entry);
});
res.on('end', function(result) {
cb(err,result);
});
});
};
}
LdapJSBackend.prototype.createNew = function(tview,crudType) {
var self = this;
return function(data, dataParam, cb) {
var entry = {
cn: 'foo',
sn: 'bar',
email: ['foo@bar.com', 'foo1@bar.com'],
objectclass: 'fooPerson'
};
self.plugin.client.add('cn=foo, o=example', entry, function(err) {
if (err) {
debug(err);
cb(err);
} else {
cb(err,result.rows);
}
});
};
}
LdapJSBackend.prototype.findOne = function(tview,crudType) {
var self = this;
return function(data, dataParam, cb) {
var querySql = 'SELECT '+selectField(tview,crudType)+' FROM '+tview.tmeta.tmodel.tid+' WHERE '+whereKeys(tview)+'';
debug('findOne param %s',JSON.stringify(dataParam));
debug('findOne sql %s',querySql);
var query = self.db.query(querySql, dataParam, function (err, result) {
if (err) {
debug(err);
cb(err);
} else {
cb(err,result.rows[0]);
}
});
};
}
LdapJSBackend.prototype.updateOne = function(tview,crudType) {
var self = this;
return function(data, dataParam, cb) {
var querySql = 'UPDATE '+tview.tmeta.tmodel.tid+' '+updateValues(tview,data)+' WHERE '+whereKeys(tview);
debug('updateOne param %s',JSON.stringify(dataParam));
debug('updateOne data %s',JSON.stringify(data));
debug('updateOne sql %s',querySql);
var query = self.db.query(querySql, data, function (err, result) {
if (err) {
debug(err);
cb(err);
} else {
cb(err,result.rows[0]);
}
});
};
}
LdapJSBackend.prototype.deleteOne = function(tview,crudType) {
var self = this;
return function(data, dataParam, cb) {
var querySql = 'DELETE FROM '+tview.tmeta.tmodel.tid+' WHERE '+whereKeys(tview);
debug('deleteOne param %s',JSON.stringify(dataParam));
debug('deleteOne sql %s',querySql);
var query = self.db.query(querySql, dataParam, function (err, result) {
if (err) {
debug(err);
cb(err);
} else {
cb(err,result.rows[0]);
}
});
};
}
LdapJSBackend.prototype.findAllCount = function(tview,crudType) {
var self = this;
return function(data, dataParam, cb) {
var querySql = 'SELECT count(*) FROM '+tview.tmeta.tmodel.tid+' WHERE '+whereKeys(tview);
debug('findAllCount param %s',JSON.stringify(dataParam));
debug('findAllCount sql %s',querySql);
var query = self.db.query(querySql, dataParam, function (err, result) {
if (err) {
debug(err);
cb(err);
} else {
cb(err,result.rows[0]);
}
});
};
}

127
lib/backend/mongo.js Normal file
View file

@ -0,0 +1,127 @@
var config = require('./../tcrud-config');
var debug = require('debug')('ff:tcrud:mongo');
module.exports = {
registrate: function(key,server) {
config.registratePlugin(new MongoPlugin(key,server));
}
}
// ------- MongoPlugin Object
function MongoPlugin(key,server) {
this.key = key;
this.server = server;
}
MongoPlugin.prototype.configPlugin = function (ctx) {
ctx.key='mongo#'+this.key;
ctx.description='Adds mongo backend support.';
};
MongoPlugin.prototype.createBackend = function() {
return new MongoBackend(this);
}
// -------- MongoBackend
function MongoBackend(plugin) {
this.plugin = plugin;
}
MongoBackend.prototype.getKey = function() {
return this.plugin.key;
}
MongoBackend.prototype.findAll = function(tview,crudType) {
var self = this;
return function(data, dataParam, cb) {
var querySql = 'SELECT '+selectField(tview,crudType)+' FROM '+tview.tmeta.tmodel.tid+'';
debug('findAll %s',querySql);
var query = self.db.query(querySql,[],function (err, result) {
if (err) {
debug(err);
cb(err);
} else {
cb(err,result.rows);
}
});
};
}
MongoBackend.prototype.createNew = function(tview,crudType) {
var self = this;
return function(data, dataParam, cb) {
};
}
MongoBackend.prototype.findOne = function(tview,crudType) {
var self = this;
return function(data, dataParam, cb) {
var querySql = 'SELECT '+selectField(tview,crudType)+' FROM '+tview.tmeta.tmodel.tid+' WHERE '+whereKeys(tview)+'';
debug('findOne param %s',JSON.stringify(dataParam));
debug('findOne sql %s',querySql);
var query = self.db.query(querySql, dataParam, function (err, result) {
if (err) {
debug(err);
cb(err);
} else {
cb(err,result.rows[0]);
}
});
};
}
MongoBackend.prototype.updateOne = function(tview,crudType) {
var self = this;
return function(data, dataParam, cb) {
var querySql = 'UPDATE '+tview.tmeta.tmodel.tid+' '+updateValues(tview,data)+' WHERE '+whereKeys(tview);
debug('updateOne param %s',JSON.stringify(dataParam));
debug('updateOne data %s',JSON.stringify(data));
debug('updateOne sql %s',querySql);
var query = self.db.query(querySql, data, function (err, result) {
if (err) {
debug(err);
cb(err);
} else {
cb(err,result.rows[0]);
}
});
};
}
MongoBackend.prototype.deleteOne = function(tview,crudType) {
var self = this;
return function(data, dataParam, cb) {
var querySql = 'DELETE FROM '+tview.tmeta.tmodel.tid+' WHERE '+whereKeys(tview);
debug('deleteOne param %s',JSON.stringify(dataParam));
debug('deleteOne sql %s',querySql);
var query = self.db.query(querySql, dataParam, function (err, result) {
if (err) {
debug(err);
cb(err);
} else {
cb(err,result.rows[0]);
}
});
};
}
MongoBackend.prototype.findAllCount = function(tview,crudType) {
var self = this;
return function(data, dataParam, cb) {
var querySql = 'SELECT count(*) FROM '+tview.tmeta.tmodel.tid+' WHERE '+whereKeys(tview);
debug('findAllCount param %s',JSON.stringify(dataParam));
debug('findAllCount sql %s',querySql);
var query = self.db.query(querySql, dataParam, function (err, result) {
if (err) {
debug(err);
cb(err);
} else {
cb(err,result.rows[0]);
}
});
};
}

271
lib/backend/mongoose.js Normal file
View file

@ -0,0 +1,271 @@
var config = require('./../tcrud-config');
var debug = require('debug')('ff:tcrud:mongoose');
var clone = require('clone');
var validate = require('validate.io');
// ------- MongoosePlugin Object
function MongoosePlugin(key,conn) {
this.key = key;
this.conn = conn;
}
MongoosePlugin.prototype.configPlugin = function (ctx) {
ctx.key='mongoose#'+this.key;
ctx.description='Adds ldap backend support.';
};
MongoosePlugin.prototype.createBackend = function() {
return new MongooseBackend(this);
}
//------- MongooseBackend Object
function MongooseBackend(plugin) {
this.plugin = plugin;
}
MongooseBackend.prototype.getKey = function() {
return this.plugin.key;
}
MongooseBackend.prototype.findAll = function(tview,crudType) {
var self = this;
return function(data, dataParam, cb) {
debug('find all..'+tview.tmeta.tmodel.tid);
var model = self.plugin.conn.model(tview.tmeta.tmodel.tid);
model.find({},function (err, result) {
if (err) {
debug(err);
cb(err);
} else {
cb(err,result.rows);
}
});
// cb();
// var querySql = 'SELECT '+selectField(tview,crudType)+' FROM '+tview.tmeta.tmodel.tid+'';
// debug('findAll %s',querySql);
// var query = self.db.query(querySql,[],
};
}
MongooseBackend.prototype.createNew = function(tview,crudType) {
var self = this;
return function(data, dataParam, cb) {
};
}
MongooseBackend.prototype.findOne = function(tview,crudType) {
var self = this;
return function(data, dataParam, cb) {
debug('findOne param %s',JSON.stringify(dataParam));
//debug('findOne sql %s',querySql);
var model = self.plugin.conn.model(tview.tmeta.tmodel.tid);
model.findById(dataParam,function (err, result) {
if (err) {
debug(err);
cb(err);
} else {
cb(err,result.rows);
}
});
// var querySql = 'SELECT '+selectField(tview,crudType)+' FROM '+tview.tmeta.tmodel.tid+' WHERE '+whereKeys(tview)+'';
//
};
}
MongooseBackend.prototype.updateOne = function(tview,crudType) {
var self = this;
return function(data, dataParam, cb) {
debug('updateOne param %s',JSON.stringify(dataParam));
debug('updateOne data %s',JSON.stringify(data));
var model = self.plugin.conn.model(tview.tmeta.tmodel.tid);
model.save(data,function (err, result) {
if (err) {
debug(err);
cb(err);
} else {
cb(err,result.rows);
}
});
// var querySql = 'UPDATE '+tview.tmeta.tmodel.tid+' '+updateValues(tview,data)+' WHERE '+whereKeys(tview);
//
// debug('updateOne sql %s',querySql);
// var query = self.db.query(querySql, data, function (err, result) {
// if (err) {
// debug(err);
// cb(err);
// } else {
// cb(err,result.rows[0]);
// }
// });
};
}
MongooseBackend.prototype.deleteOne = function(tview,crudType) {
var self = this;
return function(data, dataParam, cb) {
// var querySql = 'DELETE FROM '+tview.tmeta.tmodel.tid+' WHERE '+whereKeys(tview);
// debug('deleteOne param %s',JSON.stringify(dataParam));
// debug('deleteOne sql %s',querySql);
// var query = self.db.query(querySql, dataParam, function (err, result) {
// if (err) {
// debug(err);
// cb(err);
// } else {
// cb(err,result.rows[0]);
// }
// });
};
}
MongooseBackend.prototype.findAllCount = function(tview,crudType) {
var self = this;
return function(data, dataParam, cb) {
// var querySql = 'SELECT count(*) FROM '+tview.tmeta.tmodel.tid+' WHERE '+whereKeys(tview);
// debug('findAllCount param %s',JSON.stringify(dataParam));
// debug('findAllCount sql %s',querySql);
// var query = self.db.query(querySql, dataParam, function (err, result) {
// if (err) {
// debug(err);
// cb(err);
// } else {
// cb(err,result.rows[0]);
// }
// });
};
}
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';
}
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) {
debug('tfield model cloned');
tfield = clone(value.tfield);
tfield.tid = key;
tfield.tname = tfield.tname;
tfield.type = autoFieldType(value,tfield.ttype);
} else if (key && value) {
debug('tfield model auto created');
tfield = config.createTField(key);
tfield.type = autoFieldType(value);
}
if (tfield.tvalidate && tfield.tvalidate.io) {
debug('tfield validate rule: '+tfield.tvalidate.io);
}
tfields[tfield.tid] = tfield;
}
return tfields;
}
function ss(valueRule) {
return function (value, response) {
response(validate(valueRule,value));
};
}
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);
}
}
buildStatics = function(modelFields,modelStatics) {
if (!modelFields) {
throw new Error('no modelFields');
}
if (!modelStatics) {
modelStatics = {};
}
modelStatics['ff_tcrud_fields'] = modelFields;
return modelStatics;
}
buildTEntityModel = function(mongoose,tcrudParent,name) {
var model = mongoose.model(name);
var tcrud = config.createTEntity(tcrudParent,name, '_id');
var tfields = model['ff_tcrud_fields'];
if (tfields) {
tcrud.tmeta.tfields = tfields;
}
tcrud.tmodel = name;
return tcrud;
}
buildTEntityModels = function(conn,tcrudParent) {
var result = [];
var modelNames = conn.modelNames();
for (var i = 0; i < modelNames.length; i++) {
result.push(buildTEntityModel(conn,tcrudParent,modelNames[i]))
}
return result;
}
// ----- wrappers
buildStaticsModel = function(modelMeta,modelStatics) {
return buildStatics(buildFields(modelMeta),modelStatics);
}
buildStaticsModelValidated = function(modelMeta,modelSchema,modelStatics) {
var modelFields = buildFields(modelMeta);
createModelValidators(modelSchema,modelFields);
return buildStatics(modelFields,modelStatics);
}
module.exports = {
buildTEntityModels: buildTEntityModels,
buildStaticsModelValidated: buildStaticsModelValidated,
buildFields: buildFields,
registrate: function(key,conn) {
config.registratePlugin(new MongoosePlugin(key,conn));
}
}

View file

@ -0,0 +1,261 @@
var debug = require('debug')('ff:tcrud:build:server:api:tentity');
var fs = require('fs');
var ejs = require('ejs');
var ncrud = require('node-crud');
var configTCrud = require('./tcrud-config');
var configRegistry = require('./config-registry');
var buildServerApiUrl = require('./build-server-api-url');
var buildServerExpress = require('./build-server-express');
var _ = require('underscore');
var mod = (function () {
var renderTemplateDataRead = function(tview, exportType, contentType) {
return function(data, query, cb) {
var res = this.response;
res.set('Content-Type', contentType);
//var templateReadHeader = fs.readFileSync('www_views/node-ff-tcrud/'+exportType+'/read-header.ejs', 'utf8');
var templateReadRecord = fs.readFileSync('./../lib/www_views/node-ff-tcrud/'+exportType+'/read-record.ejs', 'utf8');
//var templateReadFooter = fs.readFileSync('www_views/node-ff-tcrud/'+exportType+'/read-footer.ejs', 'utf8');
//var result = ejs.render(templateHeader,{
// tview: tview,
//});
for (var i = 0; i < data.length; i++) {
var row = data[i];
result += ejs.render(templateRecord,{
tview: tview,
record: row,
});
}
//result +=ejs.render(templateFooter,{
// tview: tview,
//});
res.write(result);
res.end();
this.close(); // end chain (cb set headers)
}
};
var renderTemplateDataList = function(tview, exportType, contentType) {
return function(data, query, cb) {
var res = this.response;
//res.set('Content-Type', contentType);
var templateHeader = fs.readFileSync('./../lib/www_views/node-ff-tcrud/'+exportType+'/list-header.ejs', 'utf8');
var templateRecord = fs.readFileSync('./../lib/www_views/node-ff-tcrud/'+exportType+'/list-record.ejs', 'utf8');
var templateFooter = fs.readFileSync('./../lib/www_views/node-ff-tcrud/'+exportType+'/list-footer.ejs', 'utf8');
var result = ejs.render(templateHeader,{
tview: tview,
});
for (var i = 0; i < data.length; i++) {
var row = data[i];
result += ejs.render(templateRecord,{
tview: tview,
record: row,
});
}
result +=ejs.render(templateFooter,{
tview: tview,
});
res.write(result);
res.end();
this.close(); // end chain (cb set headers)
};
};
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 createMethodObject = function (entityObject,httpMethod) {
if ('GET' === httpMethod) {
return entityObject.Read();
} else if ('POST' === httpMethod) {
return entityObject.Create();
} else if ('PUT' === httpMethod) {
return entityObject.Update();
} else if ('DELETE' === httpMethod) {
return entityObject.Delete();
} else {
throw 'unknown httpMethod: '+httpMethod;
}
};
var createMethodExport = function (ctx,crudType,backendPipe,keySlug,exportMethod,exportMethodPre,exportMethodUse) {
var plugin = ctx.tplugin;
if (plugin[exportMethod] && allowCrudType(ctx.tview,crudType)) {
var apiUrl = buildServerApiUrl.createSlugApiTEntity(plugin.tmeta.key,ctx.tview,crudType) + keySlug;
var entityObject = ncrud.entity(apiUrl);
for (var ii = 0; ii < ctx.tview[crudType].tmethods.length; ii++) {
var httpMethod = ctx.tview[crudType].tmethods[ii];
var methodObject = createMethodObject(entityObject,httpMethod);
debug(plugin.tmeta.key+'.'+httpMethod+' for: '+crudType);
if (plugin[exportMethodUse]) {
methodObject.use(plugin[exportMethodUse](ctx));
}
//methodObject.pipe(filterQueryList(model))
//methodObject.pipe(filterDateUpdate(model))
if (plugin[exportMethodPre]) {
methodObject.pipe(plugin[exportMethodPre](ctx));
}
methodObject.pipe(backendPipe);
methodObject.pipe(plugin[exportMethod](ctx));
methodObject.on('close', function(event) {
debug('event close: '+event)
});
ctx.tplugin['troutes'].push({
urlPath: ctx.tview.tmeta.tserver.tslugs.tbase + '/' + apiUrl,
httpMethod: httpMethod
});
}
}
};
var registrateMenu2 = function(menu,key) {
if (menu['items'] === undefined) {
menu['items'] = [];
}
configRegistry.getMasterConfig().rootTMenu[key] = menu; // TODO; move to reg
};
var buildCrud = function (server,tcrud) {
if (!tcrud.tenable) {
debug('skip tentity by tenable: %s',tcrud.tid);
return;
}
var tview = configTCrud.createTView(tcrud /*, fetchCrudRoles()*/);
if (tview.tmeta.tmenu.tenable && tview.tmeta.tmenu.tkey !== null && tview.tmeta.tmenu.titem === false) {
registrateMenu2({
name: tview.tmeta.tmenu.tname,
icon: tview.tmeta.tmenu.ticon
},tview.tmeta.tmenu.tkey);
}
if (!tview.tmeta.tmodel.tbackend) {
debug('skip tentity no backend: %s',tcrud.tid);
return;
}
if (!tview.tmeta.tmodel.tid) {
debug('skip tentity no model: %s',tcrud.tid);
return;
}
var backend = configTCrud.getBackend(tview.tmeta.tmodel.tbackend);
if (!backend) {
debug('skip tentity illegal backend: %s',tcrud.tid);
return;
}
var keySlug = '/'+tview.tmeta.tmodel.tkey; // TODO: remove me
for (var i = 0; i < configRegistry.getMasterConfig().plugins.length; i++) {
var plugin = configRegistry.getMasterConfig().plugins[i];
var pluginKey = plugin.tmeta.key;
if (!_.contains(tview.tmeta.tserver.tformats,pluginKey)) {
continue;
}
var ctx = {
tplugin: plugin,
tview: tview,
createSlugApiTEntityBasePlugin: function(key,postfix) {
return buildServerApiUrl.createSlugApiTEntityBase(key,ctx.tview,postfix);
},
createSlugApiTEntityBase: function(postfix) {
return buildServerApiUrl.createSlugApiTEntityBase(pluginKey,ctx.tview,postfix); //createSlugApiTEntity: buildServerUrl.createSlugApiTEntity,
},
renderTemplateDataList: function(contentType) {
return renderTemplateDataList(ctx.tview,pluginKey,contentType);
},
renderTemplateDataRead: function(contentType) {
return renderTemplateDataRead(ctx.tview,pluginKey,contentType);
},
registrateMenu: function(menu,key) {
if (menu['items'] === undefined) {
menu['items'] = [];
}
configRegistry.getMasterConfig().rootTMenu[key] = menu;
},
registrateMenuItem: function(menuItem,key) {
if (configRegistry.getMasterConfig().rootTMenu[key] === undefined) {
return; // sub tentities
}
configRegistry.getMasterConfig().rootTMenu[key].items.push(menuItem);
},
registrateClientJSResource: function(clientResource) {
configRegistry.registrateClientResource(clientResource,'js');
},
registrateClientCSSResource: function(clientResource) {
configRegistry.registrateClientResource(clientResource,'css'); // +data or rm others
},
renderFunctionJSON: buildServerExpress.renderFunctionJSON,
server: server
}
if (plugin.configApi) {
debug(pluginKey+'.configApi(tview='+tview.tid+',backend='+tview.tmeta.tmodel.tbackend+')');
plugin.configApi(ctx);
}
createMethodExport(ctx,'tlist', backend.findAll(tview, 'tlist'), '', 'configApiTListExport', 'configApiTListExportPre', 'configApiTListExportUse');
createMethodExport(ctx,'tcreate', backend.createNew(tview, 'tcreate'), '', 'configApiTCreateExport', 'configApiTCreateExportPre', 'configApiTCreateExportUse');
createMethodExport(ctx,'tread', backend.findOne(tview, 'tread'), keySlug, 'configApiTReadExport', 'configApiTReadExportPre', 'configApiTReadExportUse');
createMethodExport(ctx,'tedit', backend.updateOne(tview, 'tedit'), keySlug, 'configApiTEditExport', 'configApiTEditExportPre', 'configApiTEditExportUse');
createMethodExport(ctx,'tdelete', backend.deleteOne(tview, 'tdelete'), keySlug, 'configApiTDeleteExport', 'configApiTDeleteExportPre', 'configApiTDeleteExportUse');
createMethodExport(ctx,'tcount', backend.findAllCount(tview,'tcount'), '', 'configApiTCountExport', 'configApiTCountExportPre', 'configApiTCountExportUse');
if (allowCrudType(tview,'tverify')) {
debug('todo verift');
//ncrud.entity(buildView.createBaseApiUri(tview,'json','tverify') + '/:_id').Update()
// .pipe(mcrud.findOne(model));
}
}
};
function walkTEntityTree(server,tentity) {
if (tentity.tparent) { // TODO: move skip root node
buildCrud(server,tentity);
}
for (var i = 0; i < tentity.tchilds.length; i++) {
walkTEntityTree(server,tentity.tchilds[i]);
}
};
return function BuildServerApiTEntity() {
this.build = function(server) {
debug('buildServerApiTEntity');
configRegistry.assertPhaseServer();
var troot = configTCrud.getRootTEntity();
walkTEntityTree(server,troot);
ncrud.launch(server,{
base: troot.tmeta.tserver.tslugs.tbase,
cors: false,
timeoute: troot.tmeta.tserver.tapi.timeoute
});
};
};
})();
module.exports = new mod();

View file

@ -0,0 +1,59 @@
var debug = require('debug')('ff:tcrud:build:server:api:url');
var configTCrud = require('./tcrud-config');
var mod = (function () {
var createSlugApi = function(pluginId,postfix,slugType) {
var troot = configTCrud.getRootTEntity();
var pluginSlug = pluginId;
if (troot.tmeta.tplugin[pluginId] !== undefined && troot.tmeta.tplugin[pluginId].tslug !== undefined) {
pluginSlug = troot.tmeta.tplugin[pluginId].tslug;
}
var result = troot.tmeta.tserver.tslugs.tbase+
'/'+troot.tmeta.tserver.tslugs[slugType]+
'/'+pluginSlug;
if (postfix) {
result +='/'+postfix;
}
debug('createSlugApi plugin: '+pluginId+' result: '+result);
return result;
};
var createSlugApiTEntityAction = function(pluginId,tview, action) {
var uriEntity = tview.tmeta.tserver.tslugs.tentity;
var uriView = tview.tslug;
var uriTech = tview.tmeta.tplugin[pluginId].tslug;
var uriAction = '';
if (action) {
uriAction = '/' + tview[action].tplugin[pluginId].tslug;
}
//debug('createBaseApiUri uriTech: '+uriTech+' uriView: '+uriView+' uriAction: '+uriAction);
if (tview.tmeta.tserver.tpopfix) {
return uriEntity + '/' + uriTech + '/' + uriView + uriAction;
} else {
return uriEntity + '/' +uriView + '/' + uriTech + uriAction;
}
};
return function BuildServerApiUrl() {
this.createSlugApiServerBase = function(pluginId,postfix) {
return createSlugApi(pluginId,postfix,'tserver');
};
this.createSlugApiPluginBase = function(pluginId,postfix) {
return createSlugApi(pluginId,postfix,'tplugin');
};
this.createSlugApiTEntityBase = function(pluginId,tview,action) {
var troot = configTCrud.getRootTEntity();
return troot.tmeta.tserver.tslugs.tbase+'/'+createSlugApiTEntityAction(pluginId,tview,action);
};
this.createSlugApiTEntity = function(pluginId,tview,action) {
return createSlugApiTEntityAction(pluginId,tview,action);
};
};
})();
module.exports = new mod();

253
lib/build-server-api.js Normal file
View file

@ -0,0 +1,253 @@
var debug = require('debug')('ff:tcrud:build:server:api');
var clone = require('clone');
var Fontmin = require('fontmin');
var fs = require('fs');
var configTCrud = require('./tcrud-config');
var configRegistry = require('./config-registry');
var buildServerApiUrl = require('./build-server-api-url');
var buildServerExpress = require('./build-server-express');
var mod = (function () {
var renderTemplate = function(pluginSlug,templateFile, contentType) {
var renderFile = 'node-ff-tcrud/'+pluginSlug+'/'+templateFile;
var troot = configTCrud.getRootTEntity();
return function (req, res, next) {
res.set('Content-Type', contentType);
res.render(renderFile,{
troot: troot,
query: req.query
});
};
}
var hostTemplate = function(ctx, templateFile, contentType, skipReg) {
var pluginKey = ctx.tplugin.tmeta.key;
var apiPath = buildServerApiUrl.createSlugApiPluginBase(pluginKey,templateFile);
debug('hostTemplate apiPath: '+apiPath+' templateFile: '+templateFile+' content-Type: '+contentType);
ctx.server.get(apiPath, renderTemplate(pluginKey,templateFile, contentType)); // TODO: check sluging
if (skipReg !== undefined) {
return;
}
if (contentType.indexOf('css') !== -1) {
configRegistry.registrateClientResource(apiPath,'css');
}
if (contentType.indexOf('javascript') !== -1) {
configRegistry.registrateClientResource(apiPath,'js');
}
// TODO: no (t)html resources for caching ?
}
var filterContent = function(data,regexData) {
if (regexData === undefined) {
return data;
}
data = ''+data;
var regexList = Object.keys(regexData);
for (var i = 0; i < regexList.length; i++) {
var regex = new RegExp(regexList[i],'gm');
var regexReplace = regexData[regexList[i]];
data = data.replace(regex,regexReplace);
}
return data;
};
var hostFile = function (ctx,opt,basePath,optContentTypeHint) {
if (opt === undefined) {
throw new Error('No options given');
}
if (opt.file === undefined) {
throw new Error('No file option given');
}
if (optContentTypeHint !== undefined) {
if (optContentTypeHint === 'js') {
opt.contentType = 'text/javascript';
} else {
opt.contentType = 'text/css';
}
}
if (opt.fileCharset === undefined) {
opt.fileCharset = 'utf8';
}
if (opt.contentType === undefined) {
opt.contentType = 'text/html';
}
if (opt.registrate === undefined) {
opt.registrate = true;
}
var file = basePath;
if (opt.path !== undefined) {
file = file + opt.path;
}
file = file + '/' + opt.file;
debug('read host file: '+file);
var fileContent = fs.readFileSync(file, opt.fileCharset);
if (opt.filterFn !== undefined) {
fileContent = opt.filterFn(fileContent);
}
var serverContent = filterContent(fileContent, opt.filterRegex);
var serverContentType = opt.contentType;
var apiPath = buildServerApiUrl.createSlugApiPluginBase(ctx.tplugin.tmeta.key,opt.file);
ctx.server.get(apiPath, function(req, res) {
res.set('Content-Type', serverContentType);
res.send(serverContent);
});
if (opt.registrate && opt.contentType === 'text/javascript') {
configRegistry.registrateClientResource(apiPath,'js');
}
if (opt.registrate && opt.contentType === 'text/css') {
configRegistry.registrateClientResource(apiPath,'css');
}
};
// FIXME: a bit hacky and not compleet or ready...
var hostFileCssFont = function(ctx,opt,basePath) {
if (opt === undefined) {
throw new Error('No options given');
}
if (opt.file === undefined) {
throw new Error('No file option given');
}
var filename = basePath;
if (opt.path !== undefined) {
filename += opt.path;
}
filename += opt.file;
var fontmin = new Fontmin()
fontmin.src(fs.readFileSync(filename));
if (filename.toLowerCase().indexOf('.otf') > 0) {
fontmin.use(Fontmin.otf2ttf());
}
//.use(Fontmin.otf2ttf()).use(Fontmin.ttf2woff()); // slow...
fontmin.run(function (err, files, stream) {
var fontBuffer = files[0].contents;
debug('fond build done..'+files.length);
debug('fond build done..'+Object.keys(files[0]));
var fontStr = fontBuffer.toString('base64');
var fontWeight = '';
if (opt.fontWeight !== undefined) {
fontWeight = '\tfont-weight: '+opt.fontWeight+';\n';
}
var result = '\n'+
'@font-face {\n'+
' font-family: "'+opt.fontFamily+'";\n'+
' src: url("data:font/ttf;charset=utf-8;base64,'+fontStr+'") format("truetype");\n'+
fontWeight+
'}\n';
///' src: url("data:font/ttf;charset=utf-8;base64,'+fontStr+'") format("truetype");\n'+
//cb(result); // ' font-style: normal;\n'+ //font/opentype
//' src: url("data:application/x-font-woff;charset=utf-8;base64,'+fontData+'") format("woff");\n'+
ctx.server.get(apiPath, function(req, res) {
res.set('Content-Type', 'text/css');
res.write(result);
res.end();
});
});
// TODO: async build path ?
var apiPath = ctx.createSlugApiPluginBase(opt.file);
ctx.registrateClientCSSDataResource(apiPath);
}
var buildCrudApi = function(server) {
var troot = configTCrud.getRootTEntity();
var ctx = {
server: server,
troot: troot,
registrateMenu: function(menu,key) {
if (menu['items'] === undefined) {
menu['items'] = [];
}
configRegistry.getMasterConfig().rootTMenu[key] = menu;
},
registrateMenuItem: function(menuItem,key) {
configRegistry.getMasterConfig().rootTMenu[key].items.push(menuItem);
},
registrateClientJSResource: function(clientResource) {
return configRegistry.registrateClientResource(clientResource,'js');
},
registrateClientCSSResource: function(clientResource) {
return configRegistry.registrateClientResource(clientResource,'css');
},
registrateClientCSSDataResource: function(clientResource) {
return configRegistry.registrateClientResource(clientResource,'dss');
},
createSlugApiServerBase: function(postfix) {
return buildServerApiUrl.createSlugApiServerBase(ctx.tplugin.tmeta.key,postfix);
},
createSlugApiPluginBase: function(postfix) {
return buildServerApiUrl.createSlugApiPluginBase(ctx.tplugin.tmeta.key,postfix);
},
hostFileCSSLocal: function (opt) {
hostFile(ctx,opt,ctx.tplugin.tmeta.localDir,'css');
},
hostFileJSLocal: function (opt) {
hostFile(ctx,opt,ctx.tplugin.tmeta.localDir,'js');
},
hostFileCSSFontNodeModule: function (opt) {
hostFileCssFont(ctx,opt,__dirname+'/../node_modules/');
},
hostFileCSSNodeModule: function (opt) {
hostFile(ctx,opt,__dirname+'/../node_modules/','css');
},
hostFileJSNodeModule: function (opt) {
hostFile(ctx,opt,__dirname+'/../node_modules/','js');
},
hostTemplateJS: function(templateFile, skipReg) {
hostTemplate(ctx,templateFile, 'text/javascript', skipReg);
},
hostTemplateHTML: function(templateFile, skipReg) {
hostTemplate(ctx,templateFile, 'text/html', skipReg);
},
hostTemplateCSS: function(templateFile, skipReg) {
hostTemplate(ctx,templateFile, 'text/css', skipReg);
},
renderRedirect: buildServerExpress.renderRedirect,
renderFunctionJSON: buildServerExpress.renderFunctionJSON,
renderTemplate: function (templateFile, contentType) {
return renderTemplate(ctx.tplugin.tmeta.key,templateFile, contentType);
}
}
buildServerExpress.saveRoutes(server);
configRegistry.pluginCall('configServer',ctx, function(plugin) {
buildServerExpress.saveRoutes(server);
Array.prototype.push.apply(plugin['troutes'],buildServerExpress.getRoutesDiff(server));
});
}
return function BuildServerApi() {
this.build = function(server) {
debug('buildServerApi');
configRegistry.assertPhaseServer();
var cors = require('cors');
server.use(cors({credentials: true, origin: '*'}));
//troot.tmeta.tserver.tapi.cors
buildCrudApi(server);
};
};
})();
module.exports = new mod();

View file

@ -0,0 +1,99 @@
var debug = require('debug')('ff:tcrud:build:server:express');
var mod = (function () {
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;
}
var buildRouteLine = function(routes) {
var result = [];
for (var routeIdx in routes) {
}
return result;
}
var buildRouteDiff = function(obj,objOld) {
var result = [];
for (var objIdx in obj) {
var objResult = obj[objIdx];
var objAdd = true;
for (var objOldIdx in objOld) {
var objOldResult = objOld[objOldIdx];
if (objResult.uriPath === objOldResult.uriPath && objResult.httpMethod === objOldResult.httpMethod) {
objAdd = false;
break;
}
}
if (objAdd) {
result.push(objResult);
}
}
return result;
}
return function BuildServerExpress() {
this.saveRoutes = function(server) {
if (!server) {
throw new Error('no server given');
}
var resultOld = server.get('ff_tcrud_route_list');
var result = walkRouteTree([],server._router.stack);
result.sort(function(a, b) {
return a.uriPath.localeCompare(b.uriPath);
});
server.set('ff_tcrud_route_list',result);
server.set('ff_tcrud_route_list_diff',buildRouteDiff(result,resultOld));
};
this.getRoutes = function(server) {
return server.get('ff_tcrud_route_list');
};
this.getRoutesDiff = function(server) {
return server.get('ff_tcrud_route_list_diff');
};
this.renderRedirect = function(location) {
if (!location) {
throw 'no location';
}
return function(req, res) {
res.redirect(location);
};
};
this.renderFunctionJSON = function(fn) {
return function (req, res, next) {
res.json({
data: fn()
});
};
};
};
})();
module.exports = new mod();

405
lib/config-registry.js Normal file
View file

@ -0,0 +1,405 @@
var path = require('path');
var fs = require('fs');
var debug = require('debug')('ff:tcrud:config:registry');
var configUtil = require('./config-util');
var mod = (function () {
var phaseInit = true;
var phaseConfig = false;
var phaseServer = false;
var phaseServerUp = false;
// Master template for plugins.
this.masterTemplates = {
masterTFieldTemplate: {},
masterTFieldTHelp: {},
masterTEntityTemplate: {},
masterTEntityTHelp: {},
masterTViewTemplate: {},
masterTViewTHelp: {}
};
// Master config of tcrud
this.masterConfig = {
rootTEntity: null,
rootTMenu: {},
plugins: [],
backends: {},
validators: {},
clientResources: {
js: [],
css: [],
dss: []
}
};
this.pluginKeysAllowed = [
'configPlugin',
'configTemplate',
'configServer',
'configApi',
'configApiTListExport',
'configApiTCreateExport',
'configApiTReadExport',
'configApiTEditExport',
'configApiTDeleteExport',
'configApiTCountExport',
'configPostBoot',
'fillTEntity',
'fillTField',
'fillTView',
'createBackend',
'key','dbModule','query','conn'];
var mergeMaster = function(objectDest,pluginKey) {
return function(objectSrc) {
//console.log('mergeMasterSrc:'+JSON.stringify(objectSrc));
//console.log('mergeMasterDst:'+JSON.stringify(objectDest));
var resPre = {keys: 0,values: 0};
configUtil.countTree(objectDest,resPre);
configUtil.copyByTemplate('',objectDest,objectSrc,objectSrc);
var resPost = {keys: 0,values: 0};
configUtil.countTree(objectDest,resPost);
// TODO: check pre+post counters so first 2 arrays grow equal !
debug('mergeMaster'
+' start: '+resPre.keys+' -> '+resPre.values
+' end: '+resPost.keys+' -> '+resPost.values
+' diff: '+(resPost.keys-resPre.keys)+' -> '+(resPost.values-resPre.values)
+' plugin: '+pluginKey);
}
};
var tentityMergeDown = function(prefix,objectDest,objectSrc) {
var keys = Object.keys(objectSrc);
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
}
if (!objectDest[key]) {
objectDest[key] = configUtil.clone(value);
} else if (typeof value === 'object') {
if (key === 'tchilds') {
continue;
}
//console.log(prefix+'.'+key+' going deeper');
tentityMergeDown(prefix+'.'+key,objectDest[key],value)
}
}
};
var tentityFillTree = function(parent) {
debug('tentityFillTree.tid: %s',parent.tid);
for (var key in parent.tmeta.tfields) {
pluginCall('fillTField',{
filterValue: function(a,b,c,d) {return configUtil.filterValue(a,b,c,d);},
tfield: parent.tmeta.tfields[key]
});
}
for (var i = 0; i < parent.tchilds.length; i++) {
var child = parent.tchilds[i];
tentityMergeDown('',child,parent);
tentityFillTree(child);
}
pluginCall('fillTEntity',{
filterValue: function(a,b,c,d) {return configUtil.filterValue(a,b,c,d);},
tentity: parent
});
};
var pluginCall = function(fnName,ctx,afterPluginCb) {
var startTime = new Date().getTime();
var callCount = 0;
var keys = Object.keys(masterConfig.plugins);
for (var i = 0; i < keys.length; i++) {
var pluginKey = keys[i];
var plugin = masterConfig.plugins[pluginKey];
if (plugin.tenable === false) {
continue;
}
ctx.tplugin = plugin;
if (plugin[fnName]) {
var result = plugin[fnName](ctx);
callCount++;
if (afterPluginCb !== undefined) {
afterPluginCb(plugin, result);
}
}
}
debug('pluginCall.function: %s done %s plugins in %s ms.',fnName,callCount,(new Date().getTime()-startTime));
};
var assertPhaseInit = function() {
if (!phaseInit) {
throw Error('Not in init phase.')
}
};
var assertPhaseConfig = function() {
if (!phaseConfig) {
throw Error('Not in config phase.')
}
};
var assertPhaseServer = function() {
if (!phaseServer) {
throw Error('Not in server phase.')
}
};
var assertPhaseServerUp = function() {
if (!phaseServerUp) {
throw Error('Not in serverUp phase.')
}
};
var pluginLoad = function(plugin) {
assertPhaseInit();
if (plugin === undefined) {
throw new Error('No object given');
}
if (plugin.configPlugin === undefined) {
throw new Error('Object is not a plugin: Missing configPlugin()');
}
// run config to have key
var ctx = {
key: null,
description: null,
dependencies: [],
localDir: null,
localConfigTemplate: null
}
plugin.configPlugin(ctx);
if (ctx.key === null) {
throw new Error('Plugin does not provide pluginKey.');
}
// Validate plugin before injection
for (var objectKey in plugin) {
if (objectKey.charAt(0) === '_') {
continue; // tmp
}
if (pluginKeysAllowed.indexOf(objectKey) !== -1) {
continue;
}
throw new Error('Illegal plugin objectKey: '+objectKey+' plugin: '+plugin.tmeta.key);
}
plugin['tmeta'] = ctx;
plugin['troutes'] = [];
plugin['tenable'] = false;
if (ctx.localConfigTemplate) {
var pluginJsonFile = path.join(ctx.localDir,ctx.localConfigTemplate);
var pluginJsonTemplate = fs.readFileSync(pluginJsonFile, 'utf8');
plugin['configTemplateLocal'] = function (ctx) {
debug('configTemplateLocal file %s',pluginJsonFile);
ctx.mergeMaster(JSON.parse(pluginJsonTemplate));
};
debugPostfix = ' localConfigTemplate:\t'+pluginJsonFile;
}
//masterConfig.plugins[plugin.tmeta.key]=plugin;
masterConfig.plugins.push(plugin);
debug('pluginLoad %s',plugin.tmeta.key);
};
var pluginLoadTree = function (plugins) {
var keys = Object.keys(plugins);
var result = [];
for (var i = 0; i < keys.length; i++) {
var key = keys[i];
var value = plugins[key];
if (typeof value === 'function') {
pluginLoad(new value());
} else {
pluginLoadTree(value);
}
}
};
var pluginEnable = function(pluginFilter, value) {
assertPhaseInit();
if (pluginFilter === undefined) {
throw new Error('No pluginKey given');
}
if (value === undefined) {
value = true;
}
var keys = Object.keys(masterConfig.plugins);
for (var i = 0; i < keys.length; i++) {
var pluginIdx = keys[i];
var plugin = masterConfig.plugins[pluginIdx];
var pluginKey = plugin.tmeta.key;
if (pluginKey.match(pluginFilter)) {
debug('pluginEnable plugin: '+pluginKey+' value: '+value);
plugin.tenable = value;
}
}
};
var pluginIndexOf = function(checkKey) {
var keys = Object.keys(masterConfig.plugins);
for (var i = 0; i < keys.length; i++) {
var pluginKey = keys[i];
var plugin = masterConfig.plugins[pluginKey];
if (plugin.tmeta.key === checkKey) {
return i;
}
}
return -1;
}
var pluginOrder = function() {
var keys = Object.keys(masterConfig.plugins);
for (var pluginId = 0; pluginId < keys.length; pluginId++) {
var pluginKey = keys[pluginId];
var plugin = masterConfig.plugins[pluginKey];
if (plugin.tmeta.dependencies.length === 0) {
continue;
}
for (var depId in plugin.tmeta.dependencies) {
var dep = plugin.tmeta.dependencies[depId];
var depIdx = pluginIndexOf(dep);
if (depIdx === -1) {
throw new Error('Missing plugin: '+dep+' requested by: '+plugin.tmeta.key);
}
if (depIdx < pluginId) {
continue;
}
debug('pluginOrder dependency move: '+dep+' before: '+plugin.tmeta.key);
var depMove = masterConfig.plugins[depIdx];
var oldPlugins = masterConfig.plugins;
var newPlugins = [];
newPlugins = oldPlugins.slice(0,pluginId);
newPlugins.push(depMove);
newPlugins = newPlugins.concat(oldPlugins.slice(pluginId,depIdx));
newPlugins = newPlugins.concat(oldPlugins.slice(depIdx+1));
masterConfig.plugins = newPlugins;
return false;
}
}
return true;
}
return function ConfigRegistry() {
this.assertPhaseInit = assertPhaseInit;
this.assertPhaseConfig = assertPhaseConfig;
this.assertPhaseServer = assertPhaseServer;
this.assertPhaseServerUp = assertPhaseServerUp;
this.pluginCall = pluginCall;
this.pluginLoad = pluginLoad;
this.pluginLoadTree = pluginLoadTree;
this.pluginEnable = pluginEnable;
this.phaseNext = function() {
if (phaseInit) {
debug('phaseNext init->config');
phaseInit = false;
phaseConfig = true;
debug('pluginOrder start');
while (!pluginOrder()) {
}
debug('pluginOrder done');
var ctx = {
mergeMaster: function(template) {
mergeMaster(masterTemplates,ctx.tplugin.tmeta.key)(template);
}
};
pluginCall('configTemplate',ctx);
pluginCall('configTemplateLocal',ctx);
pluginCall('createBackend',{},function (plugin,backend) {
masterConfig.backends[backend.getKey()]=backend;
debug('createBackend: %s',backend.getKey());
});
} else if (phaseConfig) {
debug('phaseNext config->server');
phaseConfig = false;
phaseServer = true;
tentityFillTree(masterConfig.rootTEntity);
} else if (phaseServer) {
debug('phaseNext server->serverUp');
phaseServer = false;
phaseServerUp = true;
} else if (phaseServerUp) {
debug('phaseNext serverUp->done');
phaseServerUp = false;
} else {
throw new Error('All phases are done.');
}
};
this.getRootTEntity = function() {
if (masterConfig.rootTEntity === null) {
masterConfig.rootTEntity = configUtil.clone(masterTemplates.masterTEntityTemplate);
masterConfig.rootTEntity.tid = 'root';
debug('getRootTEntity.created');
}
return masterConfig.rootTEntity;
};
this.getMasterTemplates = function() {
return masterTemplates;
};
this.getMasterConfig = function() {
return masterConfig;
}
this.registrateClientResource = function(clientResource, resourceType) {
if (clientResource === undefined) {
throw new Error('No resource provided');
}
if (resourceType === undefined) {
throw new Error('No resource provided');
}
debug('registrateClientResource: '+clientResource+' type: '+resourceType);
masterConfig.clientResources[resourceType].push(clientResource);
};
this.createClientResourceFetchList = function() {
var fetchList = [];
for (var clientResourceIdx in masterConfig.clientResources.js) {
var url = masterConfig.clientResources.js[clientResourceIdx];
fetchList.push({url:url,type:'js'});
}
for (var clientResourceIdx in masterConfig.clientResources.css) {
var url = masterConfig.clientResources.css[clientResourceIdx];
fetchList.push({url:url,type:'css'});
}
for (var clientResourceIdx in masterConfig.clientResources.dss) {
var url = masterConfig.clientResources.dss[clientResourceIdx];
fetchList.push({url:url,type:'dss'});
}
return fetchList;
};
};
})();
module.exports = new mod();

100
lib/config-util.js Normal file
View file

@ -0,0 +1,100 @@
var cloneImpl = require('clone');
var debug = require('debug')('ff:tcrud:config:util');
var configRegistry = require('./config-registry');
var mod = (function () {
var clone = function(obj) {
return cloneImpl(obj);
};
var filterValuePart = function(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;
};
var copyByTemplate = function(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 === null) {
//debug('copyByTemplate '+prefix+'.'+key+' src object has null value.');
objectDest[key] = null;
continue; // no src value
}
var templateValue = copyTemplate[key];
if (templateValue === null) {
if (objectDest[key] !== null) {
//debug('copyByTemplate '+prefix+'.'+key+' has own value: '+objectDest[key]);
continue;
} else {
//debug('copyByTemplate '+prefix+'.'+key+' copy value: '+value);
objectDest[key] = clone(value);
}
} else {
if (objectDest[key] === undefined) {
objectDest[key] = clone(templateValue);
} else {
//debug('copyByTemplate '+prefix+'.'+key+' going deeper');
copyByTemplate(prefix+'.'+key,objectDest[key],value,templateValue)
}
}
}
};
var countTree = function(obj,result) {
if (typeof obj === 'object') {
for (var key in obj) {
result.keys++;
countTree(obj[key],result)
}
} else {
result.values++;
}
};
return function ConfigUtil() {
this.countTree = countTree;
this.copyByTemplate = copyByTemplate;
this.clone = clone;
this.filterValue = function(value,removeChars,replaceChar,partFn) {
var valueOrg = value;
for (var i = 0; i < removeChars.length; i++) {
value = filterValuePart(value,replaceChar,removeChars[i],partFn);
}
//debug('filterValue.result: %s from: %s',value,valueOrg);
return value;
};
this.stringHash = function (str) {
var hash = 31; // prime
for (var i = 0; i < str.length; i++) {
hash = ((hash<<5)-hash)+str.charCodeAt(i);
hash = hash & hash; // keep 32b
}
return hash;
};
};
})();
module.exports = new mod();

View file

@ -0,0 +1,28 @@
module.exports = (function () {
return function DefaultTEntityPlugin() {
this.configPlugin = function (ctx) {
ctx.key = 'defaultTEntity';
ctx.description = 'Adds the basic data and structure of the tentity.';
ctx.localDir = __dirname;
ctx.localConfigTemplate = 'default-tentity.json';
};
this.fillTEntity = function(ctx) {
if (ctx.tentity === undefined) {
throw new Error('no tentity');
}
if (ctx.tentity.tid === undefined) {
throw new Error('no tentity.tid');
}
if (ctx.tentity.tid === null) {
throw new Error('null tentity.tid');
}
if (ctx.tentity.tid.length === 0) {
throw new Error('empty tentity.tid');
}
};
};
})();

View file

@ -0,0 +1,83 @@
{
"masterTEntityTemplate": {
"tid": null,
"tname": null,
"tplural": null,
"tslug": null,
"tenable": null,
"tcode": null,
"tkeys": [],
"troles": [],
"tparent": null,
"tchilds": [],
"tlist": {
"tenable": null,
"ttext": "Listing of ..",
"troles": [],
"tmethods": ["GET"],
"tplugin": {}
},
"tcreate": {
"tenable": null,
"ttext": null,
"troles": [],
"tmethods": ["POST"],
"tplugin": {}
},
"tedit": {
"tenable": null,
"ttext": null,
"troles": [],
"tmethods": ["PUT","POST"],
"tplugin": {}
},
"tread": {
"tenable": null,
"ttext": null,
"troles": [],
"tslug": null,
"tmethods": ["GET"],
"tplugin": {}
},
"tdelete": {
"tenable": null,
"ttext": null,
"troles": [],
"tmethods": ["DELETE","POST"],
"tplugin": {}
},
"tcount": {
"tenable": null,
"ttext": "Count records",
"troles": [],
"tmethods": ["GET"],
"tplugin": {}
},
"tverify": {
"tenable": null,
"ttext": "Verify data",
"troles": [],
"tmethods": ["POST"],
"tplugin": {}
},
"tmeta": {
"tmodel": {
"tkey": null,
"tkeys": [],
"tid": null,
"tbackend": null
},
"tmenu": {
"tenable": null,
"tkey": null,
"tname": null,
"titem": null,
"ticon": null
},
"troles": [],
"tfields": {},
"tplugin": {}
}
},
"masterTEntityTHelp": {}
}

View file

@ -0,0 +1,32 @@
module.exports = (function () {
return function DefaultTFieldPlugin() {
this.configPlugin = function (ctx) {
ctx.key = 'defaultTField';
ctx.description = 'Adds the basic data and structure of the tfield.';
ctx.localDir = __dirname;
ctx.localConfigTemplate = 'default-tfield.json';
};
this.fillTField = function(ctx) {
if (ctx.tfield === undefined) {
throw new Error('no tfield');
}
if (ctx.tfield.tid === undefined) {
throw new Error('no tfield.tid');
}
if (ctx.tfield.tid === null) {
throw new Error('null tfield.tid');
}
if (ctx.tfield.tid.length === 0) {
throw new Error('empty tfield.tid');
}
if (ctx.tfield.ttype === null) {
ctx.tfield.ttype = 'text'; // move ?
}
};
};
})();

View file

@ -0,0 +1,54 @@
{
"masterTFieldTemplate": {
"tid": null,
"tname": null,
"ttext": null,
"ttype": null,
"tslug": null,
"tvalidate": {
"io": null
},
"tlist": {
"tenable": true,
"troles": []
},
"tread": {
"tenable": true,
"troles": []
},
"tedit": {
"tenable": true,
"troles": []
},
"tcreate": {
"tenable": true,
"troles": []
}
},
"masterTFieldTHelp": {
"tid": "The id of the field.",
"tname": "The name of the field.",
"ttext": "The description of the field.",
"ttype": "The field type",
"tslug": "The slug of the field.",
"tvalidate": {
"io": "Named validate-io rules for the field."
},
"tlist": {
"tenable": "Is field enabled in crud listing.",
"troles": "Needed roles for listing."
},
"tread": {
"tenable": "Is field enabled in crud reading.",
"troles": "Needed roles for reading."
},
"tedit": {
"tenable": "Is field enabled in crud editing.",
"troles": "Needed roles for editing."
},
"tcreate": {
"tenable": "Is field enabled in crud creating.",
"troles": "Needed roles for creating."
}
}
}

View file

@ -0,0 +1,13 @@
module.exports = (function () {
return function DefaultTServerPlugin() {
this.configPlugin = function (ctx) {
ctx.key = 'defaultTServer';
ctx.description = 'Adds the basic data and structure of the tserver.';
ctx.localDir = __dirname;
ctx.localConfigTemplate = 'default-tserver.json';
};
};
})();

View file

@ -0,0 +1,44 @@
{
"masterTEntityTemplate": {
"tlist": { "tserver": {
"tpipe": {
"query": {
"defaults": { "limit": 10 },
"maxes": { "limit": 50 }
}
}
}},
"tmeta": { "tserver": {
"thost": "http://localhost:8080",
"tslugs": {
"tbase": "/api",
"tserver": "server",
"tentity": "tentity",
"tplugin": "plugin"
},
"tpopfix": true,
"tformats": ["formatJSON","formatXML","formatCSV","serverConfigTView","angular"],
"tapi": {
"cors": true,
"timeoute": 30000
}
}}
},
"masterTEntityTHelp": {
"tmeta": { "tserver": {
"thost": "The host of the server.",
"tslugs": {
"tbase": "The base api prefix slug",
"tserver": "The server slug",
"tentity": "The tentity slug",
"tplugin": "The plugin slug"
},
"tpopfix": "Use tech or format first.",
"tformats": "list of exported formats.",
"tapi": {
"cors": "Allow cors requests on api.",
"timeoute": "Timeout of api requests."
}
}}
}
}

View file

@ -0,0 +1,38 @@
module.exports = (function () {
var addFilteredFields = function(tview,type,tcrud) {
//debug('addFilterFields of: '+type+' from: '+tview.tid);
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) {
continue;
}
}
result.push(key); // default is true..
}
tview[type].tfields = result;
};
return function DefaultTViewPlugin() {
this.configPlugin = function (ctx) {
ctx.key = 'defaultTView';
ctx.description = 'Add copy template for building the tview from the tentity.';
ctx.localDir = __dirname;
ctx.localConfigTemplate = 'default-tview.json';
};
this.fillTView = function(ctx) {
addFilteredFields(ctx.tview,'tlist',ctx.tentity);
addFilteredFields(ctx.tview,'tread',ctx.tentity);
addFilteredFields(ctx.tview,'tedit',ctx.tentity);
addFilteredFields(ctx.tview,'tcreate',ctx.tentity);
};
};
})();

View file

@ -0,0 +1,135 @@
{
"masterTViewTemplate": {
"tid": null,
"tname": null,
"tplural": null,
"tslug": null,
"tcode": null,
"tenable": null,
"troles": null,
"tkeys": [],
"tcount": {
"ttext": null,
"tenable": null,
"troles": null,
"tmethods": null,
"tplugin": null
},
"tlist": {
"tfields": [],
"ttext": null,
"tlinks": null,
"tenable": null,
"troles": null,
"tmethods": null,
"tplugin": null
},
"tread": {
"tfields": [],
"ttext": null,
"tenable": null,
"troles": null,
"tmethods": null,
"tplugin": null
},
"tedit": {
"tfields": [],
"ttext": null,
"tenable": null,
"troles": null,
"tmethods": null,
"tplugin": null
},
"tcreate": {
"tfields": [],
"ttext": null,
"tenable": null,
"troles": null,
"tmethods": null,
"tplugin": null
},
"tdelete": {
"ttext": null,
"tenable": null,
"troles": null,
"tmethods": null,
"tplugin": null
},
"tverify": {
"ttext": null,
"tenable": null,
"troles": null,
"tmethods": null,
"tplugin": null
},
"tmeta": {
"tmodel": null,
"tfields": null,
"tvalidate": null,
"tserver": null,
"tplugin": null,
"tmenu": null
}
},
"masterTViewTHelp": {
"tid": "The name of the view.",
"tplural": "The plural name of the view.",
"tslug": "The slug of the view.",
"tcode": null,
"tkeys": "The primary key fields.",
"tcount": {
"ttext": null,
"tenable": null,
"tmethods": null,
"tplugin": null
},
"tlist": {
"tfields": [],
"ttext": null,
"tlinks": null,
"tenable": null,
"tmethods": null,
"tplugin": null
},
"tread": {
"tfields": [],
"ttext": null,
"tenable": null,
"tmethods": null,
"tplugin": null
},
"tedit": {
"tfields": [],
"ttext": null,
"tenable": null,
"tmethods": null,
"tplugin": null
},
"tcreate": {
"tfields": [],
"ttext": null,
"tenable": null,
"tmethods": null,
"tplugin": null
},
"tdelete": {
"ttext": null,
"tenable": null,
"tmethods": null,
"tplugin": null
},
"tverify": {
"ttext": null,
"tenable": null,
"tmethods": null,
"tplugin": null
},
"tmeta": {
"tmodel": null,
"tfields": null,
"tvalidate": null,
"tserver": null,
"tplugin": null
}
}
}

View file

@ -1,6 +1,26 @@
'use strict';
var debug = require('debug')('ff:tcrud');
var startTime = new Date().getTime();
debug('Init welcome');
var assets = {
debug('Init objects...');
var requireAll = require('require-all');
var configRegistry = require('./config-registry');
var tcrudSetup = require('./tcrud-setup');
var tcrudConfig = require('./tcrud-config');
debug('Init objects done');
debug('Init plugins...');
configRegistry.pluginLoadTree(requireAll({dirname: __dirname + '/default',filter: /^([^\.].*)\.js?$/}));
configRegistry.pluginLoadTree(requireAll({dirname: __dirname + '/plugin',filter: /^([^\.].*)\.js?$/}));
debug('Init plugins done');
debug('Init backends...');
var tcrudBackend = requireAll({dirname: __dirname + '/backend',filter: /^([^\.].*)\.js?$/});
debug('Init backends done');
debug('Init done in %s ms.',(new Date().getTime()-startTime));
module.exports = {
setup: tcrudSetup,
config: tcrudConfig,
backend: tcrudBackend
};
module.exports = assets;

View file

@ -0,0 +1,54 @@
module.exports = (function () {
var autoFieldEnable = function(tfield,type) {
if (type === undefined) {
throw new Error('no type');
}
if (tfield[type] === undefined) {
tfield[type] = {}; // todo rm this
}
if (tfield[type].tenable !== null) {
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;
}
return function AutoTEnablePlugin() {
this.configPlugin = function (ctx) {
ctx.key='autoTEnable';
ctx.description='Auto enables to true if undefined.';
};
this.fillTEntity = function(ctx) {
if (ctx.tentity.tenable === null || ctx.tentity.tenable === undefined) {
ctx.tentity.tenable = true;
}
autoFieldEnable(ctx.tentity,'tlist');
autoFieldEnable(ctx.tentity,'tread');
autoFieldEnable(ctx.tentity,'tedit');
autoFieldEnable(ctx.tentity,'tcreate');
autoFieldEnable(ctx.tentity,'tdelete');
autoFieldEnable(ctx.tentity,'tcount');
autoFieldEnable(ctx.tentity,'tverify');
}
this.fillTField = function(ctx) {
autoFieldEnable(ctx.tfield,'tlist');
autoFieldEnable(ctx.tfield,'tread');
autoFieldEnable(ctx.tfield,'tedit');
autoFieldEnable(ctx.tfield,'tcreate');
}
};
})();

View file

@ -0,0 +1,45 @@
module.exports = (function () {
return function AutoTMenuPlugin() {
this.configPlugin = function (ctx) {
ctx.key='autoTMenu';
ctx.description='Auto enables menu items.';
ctx.dependencies.push('autoTEntityTPlural');
};
this.fillTEntity = function(ctx) {
if (ctx.tentity.tparent === null) {
return; // root node
}
if (ctx.tentity.tmeta.tmodel.tid === null) {
ctx.tentity.tmeta.tmenu.titem = false; // auto menu for items
}
if (ctx.tentity.tmeta.tmenu.tenable === null || ctx.tentity.tmeta.tmenu.tenable === undefined) {
ctx.tentity.tmeta.tmenu.tenable = true;
}
if (ctx.tentity.tmeta.tmenu.titem === null || ctx.tentity.tmeta.tmenu.titem === undefined) {
ctx.tentity.tmeta.tmenu.titem = true;
}
if (ctx.tentity.tmeta.tmenu.tname === null || ctx.tentity.tmeta.tmenu.tname === undefined) {
ctx.tentity.tmeta.tmenu.tname = ctx.tentity.tplural.substring(0,1).toUpperCase()+ctx.tentity.tplural.substring(1);
}
if (ctx.tentity.tmeta.tmenu.tkey === null || ctx.tentity.tmeta.tmenu.tkey === undefined) {
if (ctx.tentity.tmeta.tmenu.titem) {
ctx.tentity.tmeta.tmenu.tkey = ctx.tentity.tparent.tid;
} else {
ctx.tentity.tmeta.tmenu.tkey = ctx.tentity.tid;
}
}
if (ctx.tentity.tmeta.tmenu.ticon === null || ctx.tentity.tmeta.tmenu.ticon === undefined) {
if (ctx.tentity.tmeta.tmenu.titem) {
ctx.tentity.tmeta.tmenu.ticon = 'fa fa-table';
} else {
ctx.tentity.tmeta.tmenu.ticon = 'fa fa-cubes';
}
}
}
};
})();

View file

@ -0,0 +1,50 @@
module.exports = (function () {
var createName = 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.substring(1); // remove first space
};
var filterName = function(ctx,value) {
return ctx.filterValue(value,['_','-','.',','],' ', function (part) {
return part.substring(0,1).toUpperCase()+part.substring(1).toLowerCase();
});
};
return function AutoTNamePlugin() {
this.configPlugin = function (ctx) {
ctx.key='autoTName';
ctx.description='Auto fills and checks the tname fields.';
};
this.fillTField = function(ctx) {
if (ctx.tfield.tname === null) {
ctx.tfield.tname = createName(ctx.tfield.tid);
} else {
//ctx.tfield.tname = filterName(ctx,ctx.tfield.tname);
}
};
this.fillTEntity = function(ctx) {
if (ctx.tentity.tname === null) {
ctx.tentity.tname = ctx.tentity.tid;//
}
};
};
})();

View file

@ -0,0 +1,47 @@
module.exports = (function () {
var filterSlug = function(ctx,value) {
return ctx.filterValue(value,['.',',','/','=','&','?',' '],'', function (part) {
return part; // todo use fully correct uri removeal and escaping.
})
};
var createTViewSlug = function(tentity) {
var uriViewSlash = '/';
var slug = uriViewSlash + tentity.tslug;
if (!tentity.tenable || !tentity.tparent) {
slug = '';
}
if (tentity.tparent) {
return createTViewSlug(tentity.tparent)+slug;
}
return slug;
};
return function AutoTSlugPlugin() {
this.configPlugin = function (ctx) {
ctx.key='autoTSlug';
ctx.description='Auto fills and checks the tslug fields.';
};
this.fillTField = function(ctx) {
if (ctx.tfield.tslug === undefined || ctx.tfield.tslug === null) {
ctx.tfield.tslug = ctx.tfield.tid;
}
ctx.tfield.tslug = filterSlug(ctx,ctx.tfield.tslug);
};
this.fillTEntity = function(ctx) {
if (ctx.tentity.tslug === null) {
ctx.tentity.tslug = ctx.tentity.tid;
}
ctx.tentity.tslug = filterSlug(ctx,ctx.tentity.tslug);
};
this.fillTView = function(ctx) {
ctx.tview.tslug = createTViewSlug(ctx.tentity).substring(1);
}
};
})();

View file

@ -0,0 +1,24 @@
module.exports = (function () {
var filterCode = function(ctx,value) {
return ctx.filterValue(value,[' ','_','-','.',','],'',function (part) {
return part.toLowerCase();
});
}
return function AutoTEntityTCodePlugin() {
this.configPlugin = function (ctx) {
ctx.key='autoTEntityTCode';
ctx.description='Fills and filters tcode to be code-safe for generating variables/functions.';
};
this.fillTEntity = function(ctx) {
if (ctx.tentity.tcode === null || ctx.tentity.tcode === null) {
ctx.tentity.tcode = ctx.tentity.tid;
}
ctx.tentity.tcode = filterCode(ctx,ctx.tentity.tcode);
};
};
})();

View file

@ -0,0 +1,29 @@
module.exports = (function () {
var makeTKey = function(ctx) {
var keySlug = '';
for (var i = 0; i < ctx.tentity.tmeta.tmodel.tkeys.length; i++) {
var key = ctx.tentity.tmeta.tmodel.tkeys[i];
keySlug += ':'+key;
if (i < (ctx.tentity.tmeta.tmodel.tkeys.length - 1)) {
keySlug += '/';
}
}
return keySlug;
};
return function AutoTEntityTKeyPlugin() {
this.configPlugin = function (ctx) {
ctx.key='autoTEntityTKey';
ctx.description='Auto creates the tmode.tkey parameter slug.';
};
this.fillTEntity = function(ctx) {
if (ctx.tentity.tmeta.tmodel.tkey === null) {
ctx.tentity.tmeta.tmodel.tkey = makeTKey(ctx);
}
};
};
})();

View file

@ -0,0 +1,32 @@
module.exports = (function () {
var makePlural = function(name) {
if (name.slice(-1) === 's') {
return name;
}
if (name.slice(-1) === 'y') {
name = name.slice(0,-1) + 'ie';
}
return name + 's';
}
return function AutoTEntityTPluralPlugin() {
this.configPlugin = function (ctx) {
ctx.key='autoTEntityTPlural';
ctx.description='Auto create tplurals from the tname/tid field.';
ctx.dependencies.push('autoTName');
};
this.fillTEntity = function(ctx) {
if (ctx.tentity.tplural === null) {
if (ctx.tentity.tname === null) {
ctx.tentity.tplural = makePlural(ctx.tentity.tid);
} else {
ctx.tentity.tplural = makePlural(ctx.tentity.tname);
}
}
};
};
})();

View file

@ -0,0 +1,53 @@
module.exports = (function () {
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 forceLookupKeySimple() {
var low = 100000;
var high = 999999;
return Math.floor(Math.random() * (high - low + 1) + low).toString(16).toUpperCase();
}
return function AutoTViewFTLPlugin() {
this.configPlugin = function (ctx) {
ctx.key='autoTViewFTL';
ctx.description='Automatic tview tfields key change to force table lookup.';
};
this.fillTView = function(ctx) {
forceLookupTFields(ctx.tview);
};
};
})();

View file

@ -0,0 +1,21 @@
module.exports = (function () {
return function FormatCsvPlugin() {
this.configPlugin = function (ctx) {
ctx.key = 'formatCSV';
ctx.description = 'Export tentity list/read api in csv format.';
ctx.localDir = __dirname;
ctx.localConfigTemplate = 'format-csv.json';
};
this.configApiTListExport = function (ctx) {
return ctx.renderTemplateDataList(ctx.tview.tmeta.tplugin.formatCSV.tmime);
};
this.configApiTReadExport = function (ctx) {
return ctx.renderTemplateDataRead(ctx.tview.tmeta.tplugin.formatCSV.tmime);
};
};
})();

View file

@ -0,0 +1,18 @@
{
"masterTEntityTemplate": {
"tlist": { "tplugin": { "formatCSV": { "tslug": "list" }}},
"tread": { "tplugin": { "formatCSV": { "tslug": "read" }}},
"tmeta": { "tplugin": { "formatCSV": {
"tslug": "csv",
"tmime": "text/csv"
}}}
},
"masterTEntityTHelp": {
"tlist": { "tplugin": { "formatCSV": { "tslug": "csv slug of api url" }}},
"tread": { "tplugin": { "formatCSV": { "tslug": "csv slug of api url" }}},
"tmeta": { "tplugin": { "formatCSV": {
"tslug": "The csv slug of api url.",
"tmime": "The csv mime type header."
}}}
}
}

View file

@ -0,0 +1,35 @@
// TODO: move to default-format-json.js ??
module.exports = (function () {
return function FormatJsonPlugin() {
this.configPlugin = function (ctx) {
ctx.key = 'formatJSON';
ctx.description = 'Export the tentity api in json format.';
ctx.localDir = __dirname;
ctx.localConfigTemplate = 'format-json.json';
};
// flag that we support these all crud types.
this.configApiTListExport = function (ctx) {
};
this.configApiTCreateExport = function (ctx) {
};
this.configApiTReadExport = function (ctx) {
};
this.configApiTEditExport = function (ctx) {
};
this.configApiTDeleteExport = function (ctx) {
};
this.configApiTCountExport = function (ctx) {
};
};
})();

View file

@ -0,0 +1,22 @@
{
"masterTEntityTemplate": {
"tlist": { "tplugin": { "formatJSON": { "tslug": "list" }}},
"tcreate": { "tplugin": { "formatJSON": { "tslug": "create" }}},
"tedit": { "tplugin": { "formatJSON": { "tslug": "edit" }}},
"tread": { "tplugin": { "formatJSON": { "tslug": "read" }}},
"tdelete": { "tplugin": { "formatJSON": { "tslug": "delete" }}},
"tcount": { "tplugin": { "formatJSON": { "tslug": "list-count" }}},
"tverify": { "tplugin": { "formatJSON": { "tslug": "verify" }}},
"tmeta": { "tplugin": { "formatJSON": { "tslug": "json" }}}
},
"masterTEntityTHelp": {
"tlist": { "tplugin": { "formatJSON": { "tslug": "tlist json slug part api url" }}},
"tcreate": { "tplugin": { "formatJSON": { "tslug": "tcreate json slug part api url" }}},
"tedit": { "tplugin": { "formatJSON": { "tslug": "tedit json slug part api url" }}},
"tread": { "tplugin": { "formatJSON": { "tslug": "tread json slug part api url" }}},
"delete": { "tplugin": { "formatJSON": { "tslug": "tdelete json slug part api url" }}},
"tcount": { "tplugin": { "formatJSON": { "tslug": "tcount json slug part api url" }}},
"tverify": { "tplugin": { "formatJSON": { "tslug": "tverifiy json slug part api url"}}},
"tmeta": { "tplugin": { "formatJSON": { "tslug": "json group part api url" }}}
}
}

View file

@ -0,0 +1,13 @@
module.exports = (function () {
return function FormatRssPlugin() {
this.configPlugin = function (ctx) {
ctx.key = 'formatRSS';
ctx.description = 'Export tentity list/read api in rss format.';
ctx.localDir = __dirname;
ctx.localConfigTemplate = 'format-rss.json';
};
};
})();

View file

@ -0,0 +1,17 @@
{
"masterTEntityTemplate": {
"tlist": { "tplugin": { "formatRSS": { "tslug": "list" }}},
"tmeta": { "tplugin": { "formatRSS": {
"tslug": "rss",
"itemTitle": "$data._id",
"itemLink": "$siteLink/ui/$modelUri/read/$[\"data._id\"]",
"itemDate": "",
"itemDescription": "",
"itemFields": ""
}}}
},
"masterTEntityTHelp": {
"tlist": { "tplugin": { "formatRSS": { "tslug": "rss slug of api url" }}},
"tmeta": { "tplugin": { "formatRSS": { "tslug": "rss slug of api url" }}}
}
}

View file

@ -0,0 +1,75 @@
var xmlmapping = require('xml-mapping');
var rawbody = require('raw-body');
var typeis = require('type-is');
module.exports = (function () {
function bodyParserXml() {
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();
})
}
}
return function FormatXmlPlugin() {
this.configPlugin = function (ctx) {
ctx.key = 'formatXML';
ctx.description = 'Export the tentity api in xml format.';
ctx.localDir = __dirname;
ctx.localConfigTemplate = 'format-xml.json';
};
this.configServer = function(ctx) {
ctx.server.use(bodyParserXml());
}
// flag that we support these all crud types.
this.configApiTListExport = function (ctx) {
return ctx.renderTemplateDataList(ctx.tview.tmeta.tplugin.formatXML.tmime);
};
this.configApiTCreateExport = function (ctx) {
};
this.configApiTReadExport = function (ctx) {
return ctx.renderTemplateDataRead(ctx.tview.tmeta.tplugin.formatXML.tmime);
};
this.configApiTEditExport = function (ctx) {
};
this.configApiTDeleteExport = function (ctx) {
};
this.configApiTCountExport = function (ctx) {
};
};
})();

View file

@ -0,0 +1,28 @@
{
"masterTEntityTemplate": {
"tlist": { "tplugin": { "formatXML": { "tslug": "list" }}},
"tcreate": { "tplugin": { "formatXML": { "tslug": "create" }}},
"tedit": { "tplugin": { "formatXML": { "tslug": "edit" }}},
"tread": { "tplugin": { "formatXML": { "tslug": "read" }}},
"tdelete": { "tplugin": { "formatXML": { "tslug": "delete" }}},
"tcount": { "tplugin": { "formatXML": { "tslug": "list-count" }}},
"tverify": { "tplugin": { "formatXML": { "tslug": "verify" }}},
"tmeta": { "tplugin": { "formatXML": {
"tslug": "xml",
"tmime": "text/xml"
}}}
},
"masterTEntityTHelp": {
"tlist": { "tplugin": { "formatXML": { "tslug": "xml slug of api url" }}},
"tcreate": { "tplugin": { "formatXML": { "tslug": "xml slug of api url" }}},
"tedit": { "tplugin": { "formatXML": { "tslug": "xml slug of api url" }}},
"tread": { "tplugin": { "formatXML": { "tslug": "xml slug of api url" }}},
"tdelete": { "tplugin": { "formatXML": { "tslug": "xml slug of api url" }}},
"tcount": { "tplugin": { "formatXML": { "tslug": "xml slug of api url" }}},
"tverify": { "tplugin": { "formatXML": { "tslug": "xml slug of api url" }}},
"tmeta": { "tplugin": { "formatXML": {
"tslug": "The xml slug of api url.",
"tmime": "The xml mime type header."
}}}
}
}

View file

@ -0,0 +1,63 @@
var debug = require('debug')('ff:tcrud:plugin:server:config:resouces:mobile');
var configRegistry = require('./../../../config-registry');
var configUtil = require('./../../../config-util');
var fetch = require('fetch');
module.exports = (function () {
var webAssets = [];
var fetchHashResource = function(fetchEntry,cb) {
fetch.fetchUrl(fetchEntry.url,function(err, meta, data) {
debug('fetched url: '+fetchEntry.url+' length: '+meta.responseHeaders['content-length']);
var assetHash = configUtil.stringHash(''+data);
webAssets.push({
url: fetchEntry.url,
type: fetchEntry.type,
hash: assetHash
});
cb();
});
};
var fetchHashResources = function(fetchList, cb) {
var startTime = new Date().getTime();
debug('createHashResources: '+fetchList.length);
var resourceStack = fetchList;
var resourceLoader = function() {
resourceStack = resourceStack.slice(1);
if (resourceStack.length === 0) {
debug('fetchHashResources done in '+(new Date().getTime()-startTime)+' ms.');
cb();
} else {
fetchHashResource(resourceStack[0],resourceLoader);
}
};
fetchHashResource(resourceStack[0],resourceLoader);
};
return function ServerConfigResourcesMobilePlugin() {
this.configPlugin = function (ctx) {
ctx.key = 'serverConfigResourcesMobile';
ctx.description = 'Exports client resources mobile.';
ctx.localDir = __dirname;
ctx.localConfigTemplate = 'config-resources-mobile.json';
};
this.configServer = function(ctx) {
ctx.server.get(ctx.createSlugApiServerBase(),ctx.renderFunctionJSON(function () {
return {
resources: webAssets
}
}));
};
this.configPostBoot = function(ctx) {
fetchHashResources(configRegistry.createClientResourceFetchList(), function() {
debug('Total assets build: '+webAssets.length);
});
}
};
})();

View file

@ -0,0 +1,12 @@
{
"masterTEntityTemplate": {
"tmeta": { "tplugin": { "serverConfigResourcesMobile": {
"tslug": "config/client-resources-mobile"
}}}
},
"masterTEntityTHelp": {
"tmeta": { "tplugin": { "serverConfigResourcesMobile": {
"tslug": "The client resource for mobile slug."
}}}
}
}

View file

@ -0,0 +1,144 @@
var debug = require('debug')('ff:tcrud:plugin:server:config:resouces:web');
var configRegistry = require('./../../../config-registry');
var configUtil = require('./../../../config-util');
var fetch = require('fetch');
// FIXME: use type as key
var assetCss = '';
var assetCssUrl = '';
var assetCssHash = '';
var assetCssData = '';
var assetCssDataUrl = '';
var assetCssDataHash = '';
var assetJs = '';
var assetJsUrl = '';
var assetJsHash = '';
var plugin = (function () {
var webAssets = [];
var fetchResource = function(fetchEntry,cb) {
fetch.fetchUrl(fetchEntry.url,function(err, meta, data) {
if (err) {
throw err;
}
debug('fetched url: '+fetchEntry.url+' length: '+meta.responseHeaders['content-length']);
//debug('meta: '+JSON.stringify(meta));
if (fetchEntry.type === 'css') {
assetCss += '\n/* FILE: '+fetchEntry.url+' */\n';
assetCss += data;
} else if (fetchEntry.type === 'js') {
assetJs += '\n/* FILE: '+fetchEntry.url+' */\n';
assetJs += data;
} else if (fetchEntry.type === 'dss') {
assetCssData += '\n/* FILE: '+fetchEntry.url+' */\n';
assetCssData += data;
}
cb();
});
};
var fetchResources = function(fetchList, cb) {
var startTime = new Date().getTime();
debug('createResources: '+fetchList.length);
var resourceStack = fetchList;
var resourceLoader = function() {
resourceStack = resourceStack.slice(1);
if (resourceStack.length === 0) {
debug('fetchResources done in '+(new Date().getTime()-startTime)+' ms.');
cb();
} else {
fetchResource(resourceStack[0],resourceLoader);
}
};
fetchResource(resourceStack[0],resourceLoader);
};
return function ServerConfigResourcesWebPlugin() {
this.configPlugin = function (ctx) {
ctx.key = 'serverConfigResourcesWeb';
ctx.description = 'Exports client resources web.';
ctx.localDir = __dirname;
ctx.localConfigTemplate = 'config-resources-web.json';
};
this.configServer = function(ctx) {
ctx.server.get(ctx.createSlugApiServerBase(),ctx.renderFunctionJSON(function () {
return {
resources: webAssets
}
}));
};
this.configPostBoot = function(ctx) {
fetchResources(configRegistry.createClientResourceFetchList(), function() {
assetJsHash = configUtil.stringHash(assetJs);
assetCssHash = configUtil.stringHash(assetCss);
assetCssDataHash = configUtil.stringHash(assetCssData);
debug('Total size assets.js: '+assetJs.length+' hash: '+assetJsHash);
debug('Total size assets.css: '+assetCss.length+' hash: '+assetCssHash);
debug('Total size assets-data.css: '+assetCssData.length+' hash: '+assetCssDataHash);
// note: js first to inject href asap by loader
webAssets.push({
url: assetJsUrl,
type: 'js',
hash: assetJsHash
});
webAssets.push({
url: assetCssUrl,
type: 'css',
hash: assetCssHash
});
webAssets.push({
url: assetCssDataUrl,
type: 'dss',
hash: assetCssDataHash
});
});
}
};
})();
var pluginDownload = (function () {
return function ServerConfigResourcesWebAssetPlugin() {
this.configPlugin = function (ctx) {
ctx.key = 'serverConfigResourcesWebAsset';
ctx.description = 'Exports downloaded client web resources.';
ctx.dependencies.push('serverConfigResourcesWeb');
};
this.configServer = function(ctx) {
assetCssDataUrl = ctx.createSlugApiPluginBase('assets-data.css');
ctx.server.get(assetCssDataUrl,function (req, res, next) {
res.set('Content-Type','text/css');
res.write(assetCssData);
res.end();
});
assetCssUrl = ctx.createSlugApiPluginBase('assets.css');
ctx.server.get(assetCssUrl,function (req, res, next) {
res.set('Content-Type','text/css');
res.write(assetCss);
res.end();
});
assetJsUrl = ctx.createSlugApiPluginBase('assets.js');
ctx.server.get(assetJsUrl,function (req, res, next) {
res.set('Content-Type','application/javascript');
res.write(assetJs);
res.end();
});
};
};
})();
module.exports = {
web: plugin,
webAssets: pluginDownload
};

View file

@ -0,0 +1,12 @@
{
"masterTEntityTemplate": {
"tmeta": { "tplugin": { "serverConfigResourcesWeb": {
"tslug": "config/client-resources-web"
}}}
},
"masterTEntityTHelp": {
"tmeta": { "tplugin": { "serverConfigResourcesWeb": {
"tslug": "The client resources web slug."
}}}
}
}

View file

@ -0,0 +1,68 @@
var buildServerExpress = require('./../../../build-server-express');
module.exports = (function () {
var renderServerRoutes = function (server) {
if (!server) {
throw new Error('no server');
}
return function (req, res, next) {
var routeList = buildServerExpress.getRoutes(server);
if (!routeList) {
routeList = [];
}
var reqGroups = req.query.groups;
var groupList = [];
if (reqGroups) {
groupList = reqGroups.split(',');
}
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
});
};
}
return function ServerConfigRoutesPlugin() {
this.configPlugin = function (ctx) {
ctx.key = 'serverConfigRoutes';
ctx.description = 'Exports all server routes.';
ctx.localDir = __dirname;
ctx.localConfigTemplate = 'config-routes.json';
};
this.configServer = function(ctx) {
ctx.server.get(ctx.createSlugApiServerBase(),renderServerRoutes(ctx.server));
};
this.configPostBoot = function(ctx) {
buildServerExpress.saveRoutes(ctx.server);
};
};
})();

View file

@ -0,0 +1,12 @@
{
"masterTEntityTemplate": {
"tmeta": { "tplugin": { "serverConfigRoutes": {
"tslug": "config/routes"
}}}
},
"masterTEntityTHelp": {
"tmeta": { "tplugin": { "serverConfigRoutes": {
"tslug": "The routes slug."
}}}
}
}

View file

@ -0,0 +1,28 @@
var configRegistry = require('./../../../config-registry');
module.exports = (function () {
return function ServerConfigTMenuPlugin() {
this.configPlugin = function (ctx) {
ctx.key = 'serverConfigTMenu';
ctx.description = 'Exports tmenu.';
ctx.localDir = __dirname;
ctx.localConfigTemplate = 'config-tmenu.json';
};
this.configServer = function(ctx) {
ctx.server.get(ctx.createSlugApiServerBase(),ctx.renderFunctionJSON(function() {
var result = {};
for (var key in configRegistry.getMasterConfig().rootTMenu) {
var value = configRegistry.getMasterConfig().rootTMenu[key];
if (value.items.length > 0) {
result[key] = value; // remove empty menus
}
}
return result;
}));
};
};
})();

View file

@ -0,0 +1,12 @@
{
"masterTEntityTemplate": {
"tmeta": { "tplugin": { "serverConfigTMenu": {
"tslug": "config/menu"
}}}
},
"masterTEntityTHelp": {
"tmeta": { "tplugin": { "serverConfigTMenu": {
"tslug": "The config tmenu slug."
}}}
}
}

View file

@ -0,0 +1,23 @@
var configRegistry = require('./../../../config-registry');
module.exports = (function () {
return function ServerConfigTViewPlugin() {
this.configPlugin = function (ctx) {
ctx.key = 'serverConfigTView';
ctx.description = 'Exports tentity tviews.';
ctx.localDir = __dirname;
ctx.localConfigTemplate = 'config-tview.json';
};
this.configApi = function(ctx) {
ctx.server.get(ctx.createSlugApiTEntityBase(),ctx.renderFunctionJSON(function () {
return {
tview: ctx.tview
}
}));
};
};
})();

View file

@ -0,0 +1,12 @@
{
"masterTEntityTemplate": {
"tmeta": { "tplugin": { "serverConfigTView": {
"tslug": "config/tview"
}}}
},
"masterTEntityTHelp": {
"tmeta": { "tplugin": { "serverConfigTView": {
"tslug": "The config tview slug."
}}}
}
}

View file

@ -0,0 +1,42 @@
var configRegistry = require('./../../config-registry');
module.exports = (function () {
var masterDebug = function(ctx,modelName,modelType,model) {
ctx.server.get(
ctx.createSlugApiServerBase('master')+
'/'+modelName+
'/'+modelType
,ctx.renderFunctionJSON(function() {
var result = {};
result[modelName] = model;
return result;
})
);
};
return function ServerDebugPlugin() {
this.configPlugin = function (ctx) {
ctx.key = 'serverDebug';
ctx.description = 'Exports some debug data.';
ctx.localDir = __dirname;
ctx.localConfigTemplate = 'debug.json';
};
this.configServer = function(ctx) {
var mt = configRegistry.getMasterTemplates();
masterDebug(ctx,'tfield', 'template', mt.masterTFieldTemplate);
masterDebug(ctx,'tfield', 'thelp', mt.masterTFieldTHelp);
masterDebug(ctx,'tentity', 'template', mt.masterTEntityTemplate);
masterDebug(ctx,'tentity', 'thelp', mt.masterTEntityTHelp);
masterDebug(ctx,'tview', 'template', mt.masterTViewTemplate);
masterDebug(ctx,'tview', 'thelp', mt.masterTViewTHelp);
// todo: TypeError: Converting circular structure to JSON
//ctx.server.get(ctx.createSlugApiServerBase('config/root-tentity'),this.renderFunction(function() {
// return configRegistry.rootTEntity;
//}));
};
};
})();

View file

@ -0,0 +1,12 @@
{
"masterTEntityTemplate": {
"tmeta": { "tplugin": { "serverDebug": {
"tslug": "debug"
}}}
},
"masterTEntityTHelp": {
"tmeta": { "tplugin": { "serverDebug": {
"tslug": "The server debug slug."
}}}
}
}

View file

@ -0,0 +1,55 @@
var configRegistry = require('./../../../config-registry');
module.exports = (function () {
var createPluginInfo = function() {
var result = {};
for (var pluginIdx in configRegistry.getMasterConfig().plugins) {
var plugin = configRegistry.getMasterConfig().plugins[pluginIdx];
var pluginExtensions = Object.keys(plugin).slice(0,-1);
//TODO: pluginExtensions.remove('tmeta');
var pluginData = {
extensions: pluginExtensions,
tmeta: plugin.tmeta,
troutes: plugin.troutes
};
result[plugin.tmeta.key] = pluginData;
}
return result;
};
var createPluginKeyList = function() {
var result = [];
for (var pluginIdx in configRegistry.getMasterConfig().plugins) {
var plugin = configRegistry.getMasterConfig().plugins[pluginIdx];
result.push(plugin.tmeta.key);
}
return result;
};
return function ServerInfoPluginsPlugin() {
this.configPlugin = function (ctx) {
ctx.key='serverInfoPlugins';
ctx.description='Makes the server plugins info public as json';
ctx.localDir = __dirname;
ctx.localConfigTemplate = 'info-plugins.json';
};
this.configServer = function(ctx) {
ctx.server.get(ctx.createSlugApiServerBase(),ctx.renderFunctionJSON(function () {
return {
plugins: {
keys: {
registrated: createPluginKeyList(),
backends: Object.keys(configRegistry.getMasterConfig().backends),
validators: Object.keys(configRegistry.getMasterConfig().validators)
},
info: createPluginInfo()
}
}
}));
};
};
})();

View file

@ -0,0 +1,12 @@
{
"masterTEntityTemplate": {
"tmeta": { "tplugin": { "serverInfoPlugins": {
"tslug": "info/plugins"
}}}
},
"masterTEntityTHelp": {
"tmeta": { "tplugin": { "serverInfoPlugins": {
"tslug": "The info plugins slug."
}}}
}
}

View file

@ -0,0 +1,28 @@
module.exports = (function () {
var timeBoot = new Date().getTime();
return function ServerInfoUptimePlugin() {
this.configPlugin = function (ctx) {
ctx.key='serverInfoUptime';
ctx.description='Makes the server uptime info public as json';
ctx.localDir = __dirname;
ctx.localConfigTemplate = 'info-uptime.json';
};
this.configServer = function(ctx) {
ctx.server.get(ctx.createSlugApiServerBase(),ctx.renderFunctionJSON(function () {
var timeNow = new Date().getTime();
return {
serverInfoUptime: {
time_boot: timeBoot,
time_now: timeNow,
uptime: timeNow-timeBoot
}
}
}));
};
};
})();

View file

@ -0,0 +1,12 @@
{
"masterTEntityTemplate": {
"tmeta": { "tplugin": { "serverInfoUptime": {
"tslug": "info/uptime"
}}}
},
"masterTEntityTHelp": {
"tmeta": { "tplugin": { "serverInfoUptime": {
"tslug": "The info uptime slug."
}}}
}
}

View file

@ -0,0 +1,25 @@
module.exports = (function () {
return function UIAngularServerRoutesPlugin() {
this.configPlugin = function (ctx) {
ctx.key = 'uiAngularServerPlugins';
ctx.description = 'Adds an angular server plugins page.';
ctx.dependencies.push('angular');
};
this.configServer = function(ctx) {
ctx.hostTemplateJS('js/server-plugins');
ctx.hostTemplateHTML('thtml/plugins',true);
ctx.registrateMenuItem({
name: 'Server Plugins',
link: ctx.troot.tmeta.tplugin.angular.tbase+'/server/plugins',
enable: true,
roles: [],
icon: 'fa fa-umbrella'
},'server');
};
};
})();

View file

@ -0,0 +1,26 @@
module.exports = (function () {
return function UIAngularServerRoutesPlugin() {
this.configPlugin = function (ctx) {
ctx.key = 'uiAngularServerRoutes';
ctx.description = 'Adds an angular server routes page.';
ctx.dependencies.push('angular');
ctx.dependencies.push('serverInfoPlugins');
};
this.configServer = function(ctx) {
ctx.hostTemplateJS('js/server-routes');
ctx.hostTemplateHTML('thtml/routes',true);
ctx.registrateMenuItem({
name: 'Server Routes',
link: ctx.troot.tmeta.tplugin.angular.tbase+'/server/routes',
enable: true,
roles: [],
icon: 'fa fa-road'
},'server');
};
};
})();

128
lib/plugin/ui/angular/ui-angular.js vendored Normal file
View file

@ -0,0 +1,128 @@
var debug = require('debug')('ff:tcrud:ui:angular:boot');
module.exports = (function () {
var renderCrudController = function (tview, thtmlPrefix, tapiPrefix, tapiPrefix2) {
if (tview.tmeta.tmodel.tkeys.length === 0) {
throw Error('no model keys in: '+tview.tid);
}
var keySlug = '';
for (var i = 0; i < tview.tmeta.tmodel.tkeys.length; i++) {
var key = tview.tmeta.tmodel.tkeys[i];
keySlug += '$routeParams.'+key;
if (i < (tview.tmeta.tmodel.tkeys.length - 1)) {
keySlug += '/';
}
}
var tviewCode = '';
var slugParts = tview.tslug.split('/');
for (var i = 0; i < slugParts.length; i++) {
var part = slugParts[i];
part = part.replace(/-/g,''); // TODO use tcode which is already cleaned ?
tviewCode += part.substring(0,1).toUpperCase()+part.substring(1);
}
//debug('tviewCode: $s',tviewCode);
return function (req, res, next) {
res.set('Content-Type', 'text/javascript');
res.render('node-ff-tcrud/angular/js/crud/controller',{
tview: tview,
tviewCode: tviewCode,
thtmlPrefix: thtmlPrefix,
tapiPrefix: tapiPrefix,
tapiPrefix2: tapiPrefix2,
ejsRouteParams: keySlug
});
};
}
var renderCrudTemplate = function (tview,paction) {
var ejsKeyRow = '';
var ejsKeyData = '';
for (var i = 0; i < tview.tmeta.tmodel.tkeys.length; i++) {
var key = tview.tmeta.tmodel.tkeys[i];
ejsKeyRow += '{{row.'+key+'}}';
ejsKeyData += '{{data.'+key+'}}';
if (i < (tview.tmeta.tmodel.tkeys.length - 1)) {
ejsKeyRow += '/';
ejsKeyData += '/';
}
}
return function (req, res, next) {
res.render('node-ff-tcrud/angular/'+paction+'/'+req.params.action,{
tview: tview,
ejsKeyRow: ejsKeyRow,
ejsKeyData: ejsKeyData
});
};
}
return function UIAngularCorePlugin() {
this.configPlugin = function (ctx) {
ctx.key = 'angular';
ctx.description = 'Exports angular ui data.';
ctx.localDir = __dirname;
ctx.localConfigTemplate = 'ui-angular.json';
ctx.dependencies.push('uiLibAngular');
ctx.dependencies.push('uiLibFFSpaLoader');
//ctx.dependencies.push('uiLibTopcoat');
ctx.dependencies.push('serverConfigTMenu');
ctx.dependencies.push('uiLibFontFaceOnLoad');
};
this.configServer = function(ctx) {
ctx.hostTemplateJS('js/application');
ctx.hostTemplateJS('js/application-font');
ctx.hostTemplateJS('js/application-controller')
ctx.hostTemplateJS('js/navigation-service');
ctx.hostTemplateHTML('thtml/application-view',true);;
ctx.hostTemplateHTML('thtml/application-top',true);
ctx.hostTemplateHTML('thtml/application-top-action',true);
ctx.hostTemplateHTML('thtml/application-top-tabs',true);
var uiPath = ctx.troot.tmeta.tplugin.angular.tbase;
debug('Exported uiPath: %s',uiPath);
ctx.server.get('/', ctx.renderRedirect(uiPath));
ctx.server.get(uiPath, ctx.renderTemplate('index','text/html'));
ctx.server.get(uiPath+'/*', ctx.renderTemplate('index','text/html')); // must be last; for HTML5 history
ctx.registrateMenu({
name: 'Server',
icon: 'fa fa-server'
},'server'); // move ?
};
this.configApi = function(ctx) {
var uriPrefix = ctx.createSlugApiTEntityBase();
var thtmlPrefix = uriPrefix + '/' + ctx.tview.tmeta.tplugin.angular.thtml;
var tapiPrefix = ctx.createSlugApiTEntityBasePlugin('formatJSON'); // TODO: move to tview ?
var tapiPrefix2 = ctx.createSlugApiTEntityBasePlugin('serverConfigTView'); // TODO: move to tview ?
ctx.server.get(uriPrefix + '/thtml/crud/:action', renderCrudTemplate(ctx.tview,'thtml/crud/'));
ctx.server.get(uriPrefix + '/crud/controller.js', renderCrudController(ctx.tview,thtmlPrefix,tapiPrefix,tapiPrefix2));
//ctx.server.get(uriPrefix + '/service.js', renderCrudService(tview))
ctx.registrateClientJSResource(uriPrefix + '/crud/controller.js');
if (ctx.tview.tmeta.tmenu.tenable && ctx.tview.tmeta.tmenu.tkey !== null && ctx.tview.tmeta.tmenu.titem) {
ctx.registrateMenuItem({
name: ctx.tview.tmeta.tmenu.tname,
link: ctx.tview.tmeta.tplugin.angular.tbase+'/'+ctx.tview.tslug+'/'+ctx.tview.tlist.tplugin.angular.tslug,
enable: ctx.tview.tlist.tenable,
roles: ctx.tview.tlist.troles,
icon: ctx.tview.tmeta.tmenu.ticon
},ctx.tview.tmeta.tmenu.tkey);
}
};
};
})();

View file

@ -0,0 +1,72 @@
{
"masterTEntityTemplate": {
"tlist": { "tplugin": { "angular": {
"tslug": "list",
"thtml": "crud/list",
"tcontroller": {
"prefix": "tcrudAuto",
"postfix": "ListCntr",
"argu": "$scope, $http, $location, $routeParams, navigationService"
},
"tlinks": {
"DataTODO":"/ui/XNodeData/list/XNode/{{row.net_id}}/{{row.net_id}}"
}
}}},
"tcreate": { "tplugin": { "angular": {
"tslug": "create",
"thtml": "crud/create",
"tcontroller": {
"prefix": "tcrudAuto",
"postfix": "CreateCntr",
"argu": "$scope, $http, $location, $routeParams, navigationService"
}
}}},
"tedit": { "tplugin": { "angular": {
"tslug": "edit",
"thtml": "crud/edit",
"tcontroller": {
"prefix": "tcrudAuto",
"postfix": "EditCntr",
"argu": "$scope, $http, $location, $routeParams, navigationService"
}
}}},
"tread": { "tplugin": { "angular": {
"tslug": "read",
"thtml": "crud/read",
"troute": {
},
"tcontroller": {
"prefix": "tcrudAuto",
"postfix": "ReadCntr",
"argu": "$scope, $http, $location, $routeParams, navigationService"
}
}}},
"tdelete": { "tplugin": { "angular": {
"tslug": "delete",
"thtml": "crud/delete",
"tcontroller": {
"prefix": "tcrudAuto",
"postfix": "DeleteCntr",
"argu": "$scope, $http, $location, $routeParams, navigationService"
}
}}},
"tcount": { "tplugin": { "angular": { "tslug": "list-count" }}},
"tverify": { "tplugin": { "angular": { "tslug": "verify" }}},
"tmeta": { "tplugin": { "angular": {
"tslug": "angular",
"tbase": "/ui",
"thtml": "thtml"
}}}
},
"masterTEntityTHelp": {
"tlist": { "tplugin": { "angular": { "tslug": "slug of api url" }}},
"tcreate": { "tplugin": { "angular": { "tslug": "slug of api url" }}},
"tedit": { "tplugin": { "angular": { "tslug": "slug of api url" }}},
"tread": { "tplugin": { "angular": { "tslug": "slug of api url" }}},
"tdelete": { "tplugin": { "angular": { "tslug": "slug of api url" }}},
"tcount": { "tplugin": { "angular": { "tslug": "slug of api url" }}},
"tverify": { "tplugin": { "angular": { "tslug": "slug of api url" }}},
"tmeta": { "tplugin": { "angular": { "tslug": "slug of api url" }}}
}
}

View file

@ -0,0 +1,46 @@
module.exports = (function () {
var incHostFileJSNodeModule = function(ctx,includeFile,namePart) {
if (includeFile) {
ctx.hostFileJSNodeModule({file: 'angular-'+namePart+'.min.js', path: 'angular-'+namePart});
}
};
return function UILibAngularPlugin() {
this.configPlugin = function (ctx) {
ctx.key='uiLibAngular';
ctx.description='Adds angular libs to resources.';
ctx.localDir = __dirname;
ctx.localConfigTemplate = 'ui-lib-angular.json';
ctx.dependencies.push('uiLibJQuery');
};
this.configServer = function(ctx) {
ctx.hostFileJSNodeModule({file: 'angular.min.js', path: 'angular'});
var inc = ctx.troot.tmeta.tplugin.uiLibAngular.include;
incHostFileJSNodeModule(ctx,inc.animate,'animate');
incHostFileJSNodeModule(ctx,inc.resource,'resource');
incHostFileJSNodeModule(ctx,inc.route,'route');
incHostFileJSNodeModule(ctx,inc.touch,'touch');
if (inc['ui-grid']) {
var filter = {
'(^.*url.*?;)': '', // removed all urls
'(@font-face[\\s\\S]*?})': '', // remove font-face
'(^\\s+)': '', // clean up white space
'(\\{[\\s\\/]+\\/.*)': '{', // rm comment on functions
'(^|\\s\\/\\/.*)': '', // rm comment lines
'(\\/\\*[\\s\\*\\!][\\s\\S]*?\\*\\/)': '', // rm comment blocks
};
ctx.hostFileJSNodeModule({file: 'ui-grid.min.js', path: 'angular-ui-grid'});
ctx.hostFileCSSNodeModule({file: 'ui-grid.css', path: 'angular-ui-grid', filterRegex: filter});
ctx.hostFileCSSFontNodeModule({file: 'ui-grid.ttf', path: 'angular-ui-grid/', fontFamily: 'ui-grid'});
}
};
};
})();

View file

@ -0,0 +1,19 @@
{
"masterTEntityTemplate": {
"tmeta": { "tplugin": { "uiLibAngular": {
"tslug": "uiLibAngular",
"include": {
"animate": true,
"resource": true,
"route": true,
"touch": true,
"ui-grid": true
}
}}}
},
"masterTEntityTHelp": {
"tmeta": { "tplugin": { "uiLibAngular": {
"tslug": "The angular lib slug."
}}}
}
}

View file

@ -0,0 +1,27 @@
module.exports = (function () {
return function UILibBootstrapPlugin() {
this.configPlugin = function (ctx) {
ctx.key='uiLibBootswatch';
ctx.description='Adds and hosts bootstrap.css as client resource.';
};
this.configServer = function(ctx) {
var filter = {
'(^.*url.*?;)': '', // removed all urls
'(@font-face[\\s\\S]*?})': '', // remove font-face
//'(body[\\s\\S]*?})': '', // remove body
'(^\\s+)': '', // clean up white space
'(\\{[\\s\\/]+\\/.*)': '{', // rm comment on functions
'(^|\\s\\/\\/.*)': '', // rm comment lines
'(\\/\\*[\\s\\*\\!][\\s\\S]*?\\*\\/)': '', // rm comment blocks
};
ctx.hostFileCSSNodeModule({file: 'bootstrap.css', path: 'bootswatch/paper', filterRegex: filter});
ctx.hostFileJSNodeModule({file: 'bootstrap.js', path: 'bootstrap/dist/js'});
ctx.hostFileCSSFontNodeModule({file: 'glyphicons-halflings-regular.ttf', path: 'bootswatch/fonts/', fontFamily: 'Glyphicons Halflings'});
};
};
})();

View file

@ -0,0 +1,20 @@
module.exports = (function () {
return function UILibSpaLoaderPlugin() {
this.configPlugin = function (ctx) {
ctx.key='uiLibFFSpaLoader';
ctx.description='Adds FFSpaLoader lib to resources.';
ctx.localDir = __dirname;
ctx.dependencies.push('serverConfigResourcesWeb');
ctx.dependencies.push('serverConfigResourcesMobile'); // TODO: is this opt ?
};
this.configServer = function(ctx) {
//ctx.hostFileJSNodeModule({file: 'es5-ff-spa-loader.js', path: 'es5-ff-spa-loader/dist', registrate: false});
ctx.hostFileJSNodeModule({file: 'es5-ff-spa-loader.js', path: '../../es5-ff-spa-loader', registrate: false});
ctx.hostFileCSSNodeModule({file: 'es5-ff-spa-loader.css', path: '../../es5-ff-spa-loader', registrate: false});
};
};
})();

View file

@ -0,0 +1,39 @@
module.exports = (function () {
var incHostFileJSNodeModule = function(ctx,includeFile,namePart) {
if (includeFile) {
ctx.hostFileJSNodeModule({file: 'jquery.flot.'+namePart+'.js',path: 'flot'});
}
};
return function UILibFlotPlugin() {
this.configPlugin = function (ctx) {
ctx.key='uiLibFlot';
ctx.description='Adds basic flot js libs to resources.';
ctx.localDir = __dirname;
ctx.localConfigTemplate = 'ui-lib-flot.json';
ctx.dependencies.push('uiLibJQuery');
};
this.configServer = function(ctx) {
ctx.hostFileJSNodeModule({file: 'jquery.flot.js', path: 'flot'});
var inc = ctx.troot.tmeta.tplugin.uiLibFlot.include;
incHostFileJSNodeModule(ctx,inc.categories, 'categories');
incHostFileJSNodeModule(ctx,inc.crosshair, 'crosshair');
incHostFileJSNodeModule(ctx,inc.errorbars, 'errorbars');
incHostFileJSNodeModule(ctx,inc.fillbetween, 'fillbetween');
incHostFileJSNodeModule(ctx,inc.image, 'image');
incHostFileJSNodeModule(ctx,inc.navigate, 'navigate');
incHostFileJSNodeModule(ctx,inc.pie, 'pie');
incHostFileJSNodeModule(ctx,inc.resize, 'resize');
incHostFileJSNodeModule(ctx,inc.selection, 'selection');
incHostFileJSNodeModule(ctx,inc.stack, 'stack');
incHostFileJSNodeModule(ctx,inc.symbol, 'symbol');
incHostFileJSNodeModule(ctx,inc.threshold, 'threshold');
incHostFileJSNodeModule(ctx,inc.time, 'time');
};
};
})();

View file

@ -0,0 +1,27 @@
{
"masterTEntityTemplate": {
"tmeta": { "tplugin": { "uiLibFlot": {
"tslug": "uiLibFlot",
"include": {
"categories": false,
"crosshair": false,
"errorbars": false,
"fillbetween": false,
"image": false,
"navigate": false,
"pie": true,
"resize": true,
"selection": false,
"stack": true,
"symbol": false,
"threshold": false,
"time": true
}
}}}
},
"masterTEntityTHelp": {
"tmeta": { "tplugin": { "uiLibFlot": {
"tslug": "The server debug slug."
}}}
}
}

View file

@ -0,0 +1,25 @@
module.exports = (function () {
return function UILibBootstrapPlugin() {
this.configPlugin = function (ctx) {
ctx.key='uiLibFontAwesome';
ctx.description='Adds fontawesome client resources.';
};
this.configServer = function(ctx) {
var filter = {
'(^.*url.*?;)': '', // removed all urls
'(@font-face[\\s\\S]*?})': '', // remove font-face
'(^\\s+)': '', // clean up white space
'(\\{[\\s\\/]+\\/.*)': '{', // rm comment on functions
'(^|\\s\\/\\/.*)': '', // rm comment lines
'(\\/\\*[\\s\\*\\!][\\s\\S]*?\\*\\/)': '', // rm comment blocks
};
ctx.hostFileCSSNodeModule({file: 'font-awesome.css', path: 'font-awesome/css', filterRegex: filter});
ctx.hostFileCSSFontNodeModule({file: 'fontawesome-webfont.ttf', path: 'font-awesome/fonts/', fontFamily: 'FontAwesome'});
};
};
})();

View file

@ -0,0 +1,15 @@
module.exports = (function () {
return function UILibFontFaceOnLoadPlugin() {
this.configPlugin = function (ctx) {
ctx.key='uiLibFontFaceOnLoad';
ctx.description='Adds fontfaceonload js lib to resources.';
};
this.configServer = function(ctx) {
ctx.hostFileJSNodeModule({file: 'fontfaceonload.js', path: 'fontfaceonload/dist'});
};
};
})();

View file

@ -0,0 +1,15 @@
module.exports = (function () {
return function UILibJQueryPlugin() {
this.configPlugin = function (ctx) {
ctx.key='uiLibJQuery';
ctx.description='Adds jquery js lib to resources.';
};
this.configServer = function(ctx) {
ctx.hostFileJSNodeModule({file: 'jquery.min.js', path: 'jquery/dist'});
};
};
})();

View file

@ -0,0 +1,18 @@
module.exports = (function () {
return function UISpaStylePlugin() {
this.configPlugin = function (ctx) {
ctx.key='uiSpaStyle';
ctx.description='Adds basic styling resources.';
ctx.dependencies.push('uiSpaTopcoatFont');
};
this.configServer = function(ctx) {
ctx.hostTemplateCSS('css/flot');
ctx.hostTemplateCSS('css/panel');
ctx.hostTemplateCSS('css/style');
};
};
})();

View file

@ -0,0 +1,19 @@
module.exports = (function () {
var fontPath = 'topcoat-fonts/font/SourceSansPro/';
return function UISpaTopcoatFontPlugin() {
this.configPlugin = function (ctx) {
ctx.key='uiSpaTopcoatFont';
ctx.description='Adds Source Sans Pro fonts.';
};
this.configServer = function(ctx) {
ctx.hostFileCSSFontNodeModule({file: 'SourceSansPro-Light.otf', path: fontPath, fontFamily: 'Source Sans', fontWeight: 200});
ctx.hostFileCSSFontNodeModule({file: 'SourceSansPro-Regular.otf', path: fontPath, fontFamily: 'Source Sans', fontWeight: 400});
ctx.hostFileCSSFontNodeModule({file: 'SourceSansPro-Semibold.otf',path: fontPath, fontFamily: 'Source Sans', fontWeight: 600});
};
};
})();

View file

@ -0,0 +1,24 @@
module.exports = (function () {
return function UISpaTopcoatPlugin() {
this.configPlugin = function (ctx) {
ctx.key='uiSpaTopcoat';
ctx.description='Adds and hosts topcoat.css as client resource.';
};
this.configServer = function(ctx) {
var filter = {
'(^.*url.*?;)': '', // removed all urls
'(@font[\\s\\S]*?})': '', // remove font-face
'(body[\\s\\S]*?})': '', // remove body
'(^\\s+)': '', // clean up white space
'(\\{[\\s\\/]+\\/.*)': '{', // rm comment on functions
'(^|\\s\\/\\/.*)': '', // rm comment lines
'(\\/\\*[\\s\\*\\!][\\s\\S]*?\\*\\/)': '', // rm comment blocks
};
ctx.hostFileCSSNodeModule({file: 'topcoat-mobile-dark.css',path: 'topcoat/css', filterRegex: filter});
};
};
})();

79
lib/tcrud-config.js Normal file
View file

@ -0,0 +1,79 @@
var debug = require('debug')('ff:tcrud:config');
var configRegistry = require('./config-registry');
var configUtil = require('./config-util');
var mod = (function () {
return function TCrudConfig() {
this.getClientJSResources = function() {
return configRegistry.getMasterConfig().clientJSResources;
};
this.getClientCSSResources = function() {
return configRegistry.getMasterConfig().clientCSSResources;
};
this.getBackend = function(backendId) {
return configRegistry.getMasterConfig().backends[backendId];
};
this.getRootTEntity = function() {
return configRegistry.getRootTEntity();
};
this.createTEntityNode = function(parent, tid, modelKeys, viewKeys) {
configRegistry.assertPhaseConfig();
var result = configUtil.clone(parent);
result.tchilds = [];
result.tid = tid;
parent.tchilds.push(result);
result.tparent = parent;
if (modelKeys) {
if (Array.isArray(modelKeys)) {
result.tmeta.tmodel.tkeys = modelKeys;
} else {
result.tmeta.tmodel.tkeys.push(modelKeys);
}
}
if (viewKeys) {
if (Array.isArray(modelKeys)) {
result.tkeys = viewKeys;
} else {
result.tkeys.push(viewKeys);
}
}
return result;
};
this.createTEntity = function(parent, tid, modelKeys, viewKeys) {
configRegistry.assertPhaseConfig();
debug('createTEntity.tid: %s',tid);
var result = module.exports.createTEntityNode(parent,tid,modelKeys,viewKeys);
result.tmeta.tmodel.tid = tid;
return result;
};
this.createTField = function(parent, tid) {
configRegistry.assertPhaseConfig();
debug('createTField.tid: %s',tid);
var result = configUtil.clone(configRegistry.getMasterTemplates().masterTFieldTemplate);
result.tid = tid;
parent.tmeta.tfields[tid] = result;
return result;
};
this.createTView = function(tentity) {
debug('createTView: %s',tentity.tid);
configRegistry.assertPhaseServer();
var result = configUtil.clone(configRegistry.getMasterTemplates().masterTViewTemplate);
result.tid = tentity.tid;
configUtil.copyByTemplate('tview',result,tentity,configRegistry.getMasterTemplates().masterTViewTemplate);
configRegistry.pluginCall('fillTView',{tview: result,tentity: tentity});
return result;
}
};
})();
module.exports = new mod;

139
lib/tcrud-setup.js Normal file
View file

@ -0,0 +1,139 @@
var express = require('express');
var bodyParser = require('body-parser');
var path = require('path');
var debug = require('debug')('ff:tcrud:setup');
var configRegistry = require('./config-registry');
var buildServerApi = require('./build-server-api');
var buildServerApiTEntity = require('./build-server-api-tentity');
var mod = (function () {
var server = null;
var expressCreate = function() {
if (server === null) {
server = express();
} else {
throw new Error('Can only create once.');
}
};
var expressBuild = function(options) {
if (!options) {
options = {
viewsDir: null
};
}
var viewsDirs = [];
viewsDirs.push(path.join(__dirname, 'www_views'));
if (options.viewsDir) {
viewsDirs.push(options.viewsDir);
}
server.set('view engine', 'ejs');
server.set('views', viewsDirs);
debug('build.views: '+viewsDirs);
server.use(bodyParser.json());
server.use(bodyParser.urlencoded());
debug('build.bodyParser');
buildServerApi.build(server);
buildServerApiTEntity.build(server);
debug('build.serverApi');
};
var expressListen = function() {
var tcrud = configRegistry.getRootTEntity();
var serverPortIndex = tcrud.tmeta.tserver.thost.indexOf(':',6); // TODO: make robust
var serverPort = tcrud.tmeta.tserver.thost.substring(serverPortIndex+1);
debug('listen port: '+serverPort);
server.listen(serverPort);
return serverPort;
}
return function TCrudSetup() {
this.pluginLoad = function(plugin) {
configRegistry.assertPhaseInit();
configRegistry.pluginLoad(plugin);
};
this.pluginEnable = function(pluginKey) {
configRegistry.assertPhaseInit();
configRegistry.pluginEnable(pluginKey);
};
this.pluginEnableAll = function() {
configRegistry.assertPhaseInit();
configRegistry.pluginEnable('.*');
};
this.pluginDisable = function(pluginKey) {
configRegistry.assertPhaseInit();
configRegistry.pluginEnable(pluginKey,false);
};
this.phaseConfig = function() {
configRegistry.assertPhaseInit();
configRegistry.phaseNext();
};
this.phaseServer = function() {
configRegistry.assertPhaseConfig();
configRegistry.phaseNext();
};
this.phaseServerUp = function() {
configRegistry.assertPhaseServer();
configRegistry.phaseNext();
var ctx = {
server: server
}
configRegistry.pluginCall('configPostBoot',ctx);
};
this.expressSimple = function(options) {
expressCreate();
expressBuild(options);
return expressListen();
}
this.expressCreate = function() {
configRegistry.assertPhaseServer();
expressCreate();
return server;
};
this.expressBuild = function(options) {
configRegistry.assertPhaseServer();
expressBuild(options);
};
this.expressListen = function() {
configRegistry.assertPhaseServer();
return expressListen();
};
};
})();
module.exports = new mod;
//
//ConfigSetupExpress.prototype.renderTemplatePath = function(viewPath) {
// if (!viewPath) {
// viewPath = '';
// }
// return function (req, res) {
// res.locals.query = 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));
// };
//}

View file

@ -0,0 +1,18 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8"/>
<title>Loading</title>
<meta name="viewport" content="user-scalable=no, initial-scale=1, maximum-scale=1, minimum-scale=1, width=device-width">
<script src="/api/plugin/uiLibFFSpaLoader/es5-ff-spa-loader.js"></script>
<link rel="stylesheet" type="text/css" href="/api/plugin/uiLibFFSpaLoader/es5-ff-spa-loader.css" />
</head>
<body>
<script>
FFSpaLoader.options.boot.debug.enable = true;
FFSpaLoader.options.boot.angular.modules.push('tcrudUI');
FFSpaLoader.options.server.assets = '/api/server/config/client-resources-mobile';
FFSpaLoader.start();
</script>
</body>
</html>

View file

@ -0,0 +1,14 @@
'use strict';
tcrudUI.controller('ApplicationController',function($scope,$http,$location,navigationService) {
console.log('start menu controller');
$scope.goLink = function ( path ) {
$location.path( path );
};
$scope.navigationService = navigationService;
$scope.applicationMenu = {};
$http.get('<%= troot.tmeta.tserver.tslugs.tbase %>/<%= troot.tmeta.tserver.tslugs.tserver %>/config/menu').success(function(data, status, headers, config) {
$scope.applicationMenu = data.data;
$scope.applicationMenuKeys = Object.keys(data.data);
});
});

View file

@ -0,0 +1,15 @@
'use strict';
console.log('FFTCrudExample.fontFaceOnload check');
var ffolStartTime = new Date().getTime();
FontFaceOnload('Source Sans', {
timeout: 1234,
success: function() {
console.log('FFTCrudExample.FontFaceOnload success in '+(new Date().getTime()-ffolStartTime)+' ms.');
document.documentElement.className += ' fontLoaded';
},
error: function() {
console.log('FFTCrudExample.fontFaceOnload timeout.');
document.documentElement.className += ' fontLoaded';
}
});

View file

@ -0,0 +1,26 @@
'use strict';
var serverUrl = window.FFServerUrl;
var crudRouteInit = [];
var pageRouteInit = [];
document.title = 'TCrud Example';
$(document.createElement('div')).attr('ng-controller', 'ApplicationController').attr('ng-include','\''+serverUrl+'/api/plugin/angular/thtml/application-top\'').appendTo($('body'));
$(document.createElement('div')).attr('ng-include','\''+serverUrl+'/api/plugin/angular/thtml/application-view\'').appendTo($('body'));
var tcrudUI = angular.module('tcrudUI', ['ngRoute','ngTouch','ngAnimate', 'ui.grid', 'ui.grid.pagination','ui.grid.cellNav', 'ui.grid.edit', 'ui.grid.resizeColumns', 'ui.grid.pinning', 'ui.grid.selection', 'ui.grid.moveColumns', 'ui.grid.exporter', 'ui.grid.importer', 'ui.grid.grouping', 'ui.grid.autoResize']).
config(['$routeProvider', '$locationProvider', '$sceDelegateProvider', function($routeProvider, $locationProvider, $sceDelegateProvider) {
// init routes
pageRouteInit.forEach(function(init) { init($routeProvider, $locationProvider); });
crudRouteInit.forEach(function(init) { init($routeProvider, $locationProvider); });
$sceDelegateProvider.resourceUrlWhitelist(['self',serverUrl+'/**']);
$routeProvider.otherwise({ redirectTo: '/ui' });
$locationProvider.html5Mode({requireBase: false});
}]);
tcrudUI.run(['$route', function($route) {
$route.reload(); // ng-view works inside the ng-include
}]);

View file

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

View file

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

View file

@ -0,0 +1,173 @@
'use strict';
//
// Auto generated controller mapping for: <%= tview.tid %>
//
crudRouteInit.push(auto<%= tviewCode %>Init);
function auto<%= tviewCode %>Init($routeProvider, $locationProvider) {
<% if (tview.tlist) { %>
$routeProvider.when('<%= tview.tmeta.tplugin.angular.tbase %>/<%= tview.tslug %>/', {
redirectTo: '<%= tview.tmeta.tplugin.angular.tbase %>/<%= tview.tslug %>/<%= tview.tlist.tplugin.angular.tslug %>'
});
<%- include('controller-route', {tview: tview,tviewCode: tviewCode,thtmlPrefix:thtmlPrefix,taction: 'tlist', routeEnd: ''}); %>
<% } %>
<% if (tview.tcreate) { %>
<%- include('controller-route', {tview: tview,tviewCode: tviewCode,thtmlPrefix:thtmlPrefix,taction: 'tcreate', routeEnd: ''}); %>
<% } %>
<% if (tview.tedit) { %>
<%- include('controller-route', {tview: tview,tviewCode: tviewCode,thtmlPrefix:thtmlPrefix,taction: 'tedit', routeEnd: '/'+tview.tmeta.tmodel.tkey}); %>
<% } %>
<% if (tview.tread) { %>
<%- include('controller-route', {tview: tview,tviewCode: tviewCode,thtmlPrefix:thtmlPrefix,taction: 'tread', routeEnd: '/'+tview.tmeta.tmodel.tkey}); %>
<% } %>
<% if (tview.tdelete) { %>
<%- include('controller-route', {tview: tview,tviewCode: tviewCode,thtmlPrefix:thtmlPrefix,taction: 'tdelete', routeEnd: '/'+tview.tmeta.tmodel.tkey}); %>
<% } %>
}
<% if (tview.tlist) { %>
function <%= tview.tlist.tplugin.angular.tcontroller.prefix %><%= tviewCode %><%= tview.tlist.tplugin.angular.tcontroller.postfix %>(<%= tview.tlist.tplugin.angular.tcontroller.argu %>) {
$scope.uiTableMain = {};
$scope.uiTableMain.enableColumnResizing = true;
$scope.uiTableMain.enableFiltering = false;
$scope.uiTableMain.enableGridMenu = true;
$scope.uiTableMain.showGridFooter = false;
$scope.uiTableMain.showColumnFooter = false;
$scope.uiTableMain.paginationPageSizes = [25, 50, 75];
$scope.uiTableMain.paginationPageSize = 25;
$scope.uiTableMain.enableFullRowSelection = true;
$scope.uiTableMain.multiSelect = false;
$scope.uiTableMain.columnDefs = [];
$scope.uiTableMain.onRegisterApi = function(gridApi){
$scope.gridApi = gridApi;
gridApi.selection.on.rowSelectionChanged($scope,function(row) {
if ($scope.gridApi.selection.getSelectedRows().length > 0) {
navigationService.actions.open = $scope.doEdit;
} else {
navigationService.actions.open = null;
}
});
};
var fetchData = function() {
$http.get('<%= tapiPrefix %>/<%= tview.tlist.tplugin.formatJSON.tslug %>').success(function(data, status, headers, config) {
$scope.uiTableMain.data = data.data;
});
};
navigationService.actions.refresh = function() {
fetchData();
};
navigationService.actions.create = function() {
$location.url('<%= tview.tmeta.tplugin.angular.tbase %>/<%= tview.tslug %>/<%= tview.tcreate.tplugin.angular.tslug %>');
};
$scope.doEdit = function() {
$location.url('<%= tview.tmeta.tplugin.angular.tbase %>/<%= tview.tslug %>/<%= tview.tedit.tplugin.angular.tslug %>/'+$scope.gridApi.selection.getSelectedRows()[0].country_id);
};
$http.get('<%= tapiPrefix2 %>').success(function(data, status, headers, config) {
var tview = data.data.tview;
tview.tlist.tfields.forEach(function (fieldKey) {
var field = tview.tmeta.tfields[fieldKey];
$scope.uiTableMain.columnDefs.push({
name: field.tname,
field: field.tid,
type2: field.ttype,
});
});
navigationService.pageTitle = tview.tname;
fetchData();
});
}
<% } %>
<% if (tview.tcreate) { %>
function <%= tview.tcreate.tplugin.angular.tcontroller.prefix %><%= tviewCode %><%= tview.tcreate.tplugin.angular.tcontroller.postfix %>(<%= tview.tcreate.tplugin.angular.tcontroller.argu %>) {
$scope.data = {};
$scope.tcreateData = function () {
$http.post('<%= tapiPrefix %>/<%= tview.tcreate.tplugin.formatJSON.tslug %>', $scope.data).success(function(data) {
<% if (tview.tlist) { %>
$location.url('<%= tview.tmeta.tplugin.angular.tbase %>/<%= tview.tslug %>/<%= tview.tlist.tplugin.angular.tslug %>');
<% } else { %>
$location.url('<%= tview.tmeta.tplugin.angular.tbase %>/');
<% } %>
});
}
<%- include('controller-action-none', {tview: tview,taction: 'tcreate'}); %>
}
<% } %>
<% if (tview.tread) { %>
function <%= tview.tread.tplugin.angular.tcontroller.prefix %><%= tviewCode %><%= tview.tread.tplugin.angular.tcontroller.postfix %>(<%= tview.tread.tplugin.angular.tcontroller.argu %>) {
$scope.data = {};
$http.get('<%= tapiPrefix %>/<%= tview.tread.tplugin.formatJSON.tslug %>/' + <%= ejsRouteParams %>).success(function(data) {
$scope.data = data.data;
});
<%- include('controller-action-none', {tview: tview,taction: 'tread'}); %>
}
<% } %>
<% if (tview.tedit) { %>
function <%= tview.tedit.tplugin.angular.tcontroller.prefix %><%= tviewCode %><%= tview.tedit.tplugin.angular.tcontroller.postfix %>(<%= tview.tedit.tplugin.angular.tcontroller.argu %>) {
$scope.data = {};
var fetchData = function() {
$http.get('<%= tapiPrefix %>/<%= tview.tread.tplugin.formatJSON.tslug %>/' + <%= ejsRouteParams %>).success(function(data) {
$scope.data = data.data;
});
};
fetchData();
$scope.teditData = function () {
$http.put('<%= tapiPrefix %>/<%= tview.tedit.tplugin.formatJSON.tslug %>/' + <%= ejsRouteParams %>, $scope.data ).success(function(data) {
<% if (tview.tread) { %>
$location.url('<%= tview.tmeta.tplugin.angular.tbase %>/<%= tview.tslug %>/<%= tview.tread.tplugin.angular.tslug %>/' + <%= ejsRouteParams %>);
<% } else { %>
$location.url('<%= tview.tmeta.tplugin.angular.tbase %>/');
<% } %>
});
}
$scope.tdeleteData = function () {
$http.delete('<%= tapiPrefix %>/<%= tview.tdelete.tplugin.formatJSON.tslug %>/'+ <%= ejsRouteParams %>, $scope.data).success(function(data) {
<% if (tview.tlist) { %>
$location.url('<%= tview.tmeta.tplugin.angular.tbase %>/<%= tview.tslug %>/<%= tview.tlist.tplugin.angular.tslug %>');
<% } else { %>
$location.url('<%= tview.tmeta.tplugin.angular.tbase %>/');
<% } %>
});
}
<%- include('controller-action-none', {tview: tview,taction: 'tedit'}); %>
navigationService.actions.save = $scope.teditData;
navigationService.actions.cancel = $scope.teditNone;
navigationService.actions.delete = $scope.tdeleteData;
navigationService.actions.refresh = function() {
fetchData();
};
}
<% } %>
<% if (tview.tdelete) { %>
function <%= tview.tdelete.tplugin.angular.tcontroller.prefix %><%= tviewCode %><%= tview.tdelete.tplugin.angular.tcontroller.postfix %>(<%= tview.tdelete.tplugin.angular.tcontroller.argu %>) {
$scope.data = {};
$http.get('<%= tapiPrefix %>/<%= tview.tread.tplugin.formatJSON.tslug %>/' + <%= ejsRouteParams %>).success(function(data) {
$scope.data = data.data;
console.log('delete obj: '+JSON.stringify(data.data));
});
$scope.tdeleteData = function () {
$http.delete('<%= tapiPrefix %>/<%= tview.tdelete.tplugin.formatJSON.tslug %>/'+ <%= ejsRouteParams %>, $scope.data).success(function(data) {
<% if (tview.tlist) { %>
$location.url('<%= tview.tmeta.tplugin.angular.tbase %>/<%= tview.tslug %>/<%= tview.tlist.tplugin.angular.tslug %>');
<% } else { %>
$location.url('<%= tview.tmeta.tplugin.angular.tbase %>/');
<% } %>
});
}
<%- include('controller-action-none', {tview: tview,taction: 'tdelete'}); %>
}
<% } %>

View file

@ -0,0 +1,57 @@
'use strict';
tcrudUI.factory('navigationService', ['$route', '$rootScope',
function($route, $rootScope) {
var actions = {
save: null,
cancel: null,
open: null,
delete: null,
create: null,
refresh: null
};
var resetActions = function() {
for (var key in actions) {
if (key.indexOf('Impl') > 0) {
continue;
}
actions[key] = null;
}
};
actions.saveImpl = function() {
actions.save();
resetActions();
};
actions.cancelImpl = function() {
actions.cancel();
resetActions();
};
actions.openImpl = function() {
actions.open();
resetActions();
};
actions.deleteImpl = function() {
actions.delete();
resetActions();
};
var pageTabs = [];
var pageTitle = '';
var pageLocations = [];
$rootScope.$on('$routeChangeSuccess', function() {
pageTabs = [];
pageTitle = '';
pageLocations = [];
resetActions(); // to late gives flikering..
//$rootScope.$apply();
});
return {
actions: actions,
pageTabs: pageTabs,
pageTitle: pageTitle,
pageLocations: pageLocations,
};
}
]);

View file

@ -0,0 +1,20 @@
<div class="navbar-collapse" >
<ul class="nav navbar-nav">
<li>
<button type="button" class="fa fa-plus btn btn-default" ng-click="navigationService.actions.create()" ng-show="navigationService.actions.create !== null">
New
</button>
<button type="button" class="fa fa-caret-left btn btn-default" ng-click="navigationService.actions.cancelImpl()" ng-show="navigationService.actions.cancel !== null">
</button>
<button type="button" class="fa fa-refresh btn btn-default" ng-click="navigationService.actions.refresh()" ng-show="navigationService.actions.refresh !== null">
</button>
<button type="button" class="fa fa-save btn btn-primary" ng-click="navigationService.actions.saveImpl()" ng-show="navigationService.actions.save !== null">
</button>
<button type="button" class="fa fa-file-o btn btn-success" ng-click="navigationService.actions.openImpl()" ng-show="navigationService.actions.open !== null">
Open
</button>
<button type="button" class="fa fa-trash-o btn btn-warning" data-toggle="modal" data-target="#deleteModal" ng-show="navigationService.actions.delete !== null">
</button>
</li>
</ul>
</div>

View file

@ -0,0 +1,9 @@
<div class="navbar-collapse" >
<ul class="nav navbar-nav">
<li>
<button type="button" class="{{tab.icon}} btn btn-default" ng-repeat="tab in navigationService.pageTabs" ng-click="tab.click()">
{{tab.name}}
</button>
</li>
</ul>
</div>

View file

@ -0,0 +1,69 @@
<div class="navbar navbar-default navbar-fixed-top" role="navigation">
<div class="container">
<button type="button" class="navbar-menu" ng-click="isMenuExpanded = !isMenuExpanded;isExpanded = false">
<span class="sr-only">Toggle menu</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<div class="navbar-collapse navbar-header-left" ng-include="'/api/plugin/angular/thtml/application-top-action'"></div>
<div class="navbar-collapse navbar-header-left" ng-include="'/api/plugin/angular/thtml/application-top-tabs'"></div>
<button type="button" class="navbar-toggle navbar-header-right" ng-click="isExpanded = !isExpanded;isMenuExpanded = false">
<span class="sr-only">Toggle navigation</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<div class="for-sm-view-hide">
<div class="navbar-collapse">
<div class="navbar-right">
<ul class="nav navbar-nav navbar-right"><li><a href="/">Logout</a></li></ul>
</div>
</div>
</div>
</div>
<div class="container">
<div class="for-lg-view-hide">
<div class="navbar-collapse" ng-show="isExpanded" ng-click="isExpanded = false">
<div class="navbar-right">
<ul class="nav navbar-nav navbar-right"><li><a href="/">Logout</a></li></ul>
</div>
</div>
</div>
<div class="for-menu-view-hide">
<div class="navbar-collapse" ng-show="isMenuExpanded" ng-click="isMenuExpanded = false">
<ul class="nav navbar-nav-menu" ng-repeat="menuKey in applicationMenuKeys">
<li>
<ul class="nav navbar-nav">
<li class="navbar-nav-menu-group"><i class="{{applicationMenu[menuKey].icon}}"></i>&nbsp;{{applicationMenu[menuKey].name}}</li>
<li class="" ng-repeat="item in applicationMenu[menuKey].items">
<a href="{{item.link}}" ng-click="goLink('{{item.link}}')"><i class="{{item.icon}}"></i>&nbsp;{{item.name}}</a>
</li>
</ul>
</li>
<br/>
</ul>
</div>
</div>
</div>
<p>{{navigationService.pageTitle}}</p>
</div>
<div id="deleteModal" class="modal fade" role="dialog">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal">&times;</button>
<h4 class="modal-title">Confirm Delete</h4>
</div>
<div class="modal-body">
<p>Are you sure to delete this record ?</p>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-danger" data-dismiss="modal" ng-click="navigationService.actions.deleteImpl()">Delete</button>
<button type="button" class="btn btn-default" data-dismiss="modal">Cancel</button>
</div>
</div>
</div>
</div>

View file

@ -0,0 +1,20 @@
<div role="main" class="container-fluid">
<div>
<div class="col-xs-12">
<!--[if lt IE 7]>
<p class="alert alert-error">Your browser is <em>ancient!</em>
<a href="http://browsehappy.com/">Upgrade to a different browser</a> or
<a href="http://www.google.com/chromeframe/?redirect=true">install Google Chrome Frame</a> to experience this site.
</p>
<![endif]-->
<!--[if lt IE 9]>
<div class="alert">
You are using an old version of Internet Explorer.
For better and safer browsing experience please <a href="http://www.microsoft.com/IE9">upgrade IE</a>
or install <a href="http://google.com/chrome">Google Chrome browser</a>.
</div>
<![endif]-->
</div>
<div class="fillHeightView" ng-view/>
</div>
</div>

View file

@ -0,0 +1,16 @@
<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>

View file

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

View file

@ -0,0 +1,12 @@
<h2> Edit <%= tview.tname %></h2>
<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="topcoat-text-input"></input>
</div>
<% }) %>
</form>
<div class="col-lg-4"></div>

View file

@ -0,0 +1 @@
<div ui-grid="uiTableMain" class="grid fillHeightCalc" ng-swipe-left="doEdit()" ui-grid-pagination ui-grid-resize-columns ui-grid-move-columns ui-grid-exporter ui-grid-selection ui-grid-pinning ui-grid-auto-resize></div>

Some files were not shown because too many files have changed in this diff Show more