landmark-serve
Version:
Web Application Framework and Admin GUI / Content Management System built on Express.js and Mongoose
387 lines (308 loc) • 11.2 kB
JavaScript
var fs = require('fs'),
path = require('path'),
http = require('http'),
https = require('https'),
_ = require('underscore'),
express = require('express'),
async = require('async'),
jade = require('jade'),
moment = require('moment'),
numeral = require('numeral'),
cloudinary = require('cloudinary'),
mandrillapi = require('mandrill-api'),
utils = require('landmark-utils');
var templateCache = {};
var dashes = '\n------------------------------------------------\n';
/**
* Don't use process.cwd() as it breaks module encapsulation
* Instead, let's use module.parent if it's present, or the module itself if there is no parent (probably testing landmark directly if that's the case)
* This way, the consuming app/module can be an embedded node_module and path resolutions will still work
* (process.cwd() breaks module encapsulation if the consuming app/module is itself a node_module)
*/
var moduleRoot = (function(_rootPath) {
var parts = _rootPath.split(path.sep);
parts.pop(); //get rid of /node_modules from the end of the path
return parts.join(path.sep);
})(module.parent ? module.parent.paths[0] : module.paths[0]);
/**
* Landmark Class
*
* @api public
*/
var Landmark = function() {
this.lists = {};
this.paths = {};
this._options = {
'name': 'Landmark',
'brand': 'Landmark',
'compress': true,
'headless': false,
'logger': 'dev',
'auto update': false,
'model prefix': null
};
this._pre = {
routes: [],
render: []
};
this._redirects = {};
// expose express
this.express = express;
// init environment defaults
this.set('env', process.env.NODE_ENV || 'development');
this.set('port', process.env.PORT || process.env.OPENSHIFT_NODEJS_PORT);
this.set('host', process.env.HOST || process.env.IP || process.env.OPENSHIFT_NODEJS_IP);
this.set('listen', process.env.LISTEN);
this.set('ssl', process.env.SSL);
this.set('ssl port', process.env.SSL_PORT);
this.set('ssl host', process.env.SSL_HOST || process.env.SSL_IP);
this.set('ssl key', process.env.SSL_KEY);
this.set('ssl cert', process.env.SSL_CERT);
this.set('cookie secret', process.env.COOKIE_SECRET);
this.set('cookie signin', (this.get('env') === 'development') ? true : false);
this.set('embedly api key', process.env.EMBEDLY_API_KEY || process.env.EMBEDLY_APIKEY);
this.set('mandrill api key', process.env.MANDRILL_API_KEY || process.env.MANDRILL_APIKEY);
this.set('mandrill username', process.env.MANDRILL_USERNAME);
this.set('google api key', process.env.GOOGLE_BROWSER_KEY);
this.set('google server api key', process.env.GOOGLE_SERVER_KEY);
this.set('ga property', process.env.GA_PROPERTY);
this.set('ga domain', process.env.GA_DOMAIN);
this.set('chartbeat property', process.env.CHARTBEAT_PROPERTY);
this.set('chartbeat domain', process.env.CHARTBEAT_DOMAIN);
this.set('allowed ip ranges', process.env.ALLOWED_IP_RANGES);
if (process.env.S3_BUCKET && process.env.S3_KEY && process.env.S3_SECRET) {
this.set('s3 config', { bucket: process.env.S3_BUCKET, key: process.env.S3_KEY, secret: process.env.S3_SECRET, region: process.env.S3_REGION });
}
if (process.env.AZURE_STORAGE_ACCOUNT && process.env.AZURE_STORAGE_ACCESS_KEY) {
this.set('azurefile config', { account: process.env.AZURE_STORAGE_ACCOUNT, key: process.env.AZURE_STORAGE_ACCESS_KEY });
}
if (process.env.CLOUDINARY_URL) {
// process.env.CLOUDINARY_URL is processed by the cloudinary package when this is set
this.set('cloudinary config', true);
}
// Attach middleware packages, bound to this instance
this.initAPI = require('./lib/middleware/initAPI')(this);
};
_.extend(Landmark.prototype, require('./lib/core/options')(moduleRoot));
/**
* Registers a pre-event handler.
*
* Valid events include:
* - `routes` - calls the function before any routes are matched, after all other middleware
*
* @param {String} event
* @param {Function} function to call
* @api public
*/
Landmark.prototype.pre = function(event, fn) {
if (!this._pre[event]) {
throw new Error('landmark.pre() Error: event ' + event + ' does not exist.');
}
this._pre[event].push(fn);
return this;
};
Landmark.prototype.prefixModel = function (key) {
var modelPrefix = this.get('model prefix');
if (modelPrefix)
key = modelPrefix + '_' + key;
return require('mongoose/lib/utils').toCollectionName(key);
};
/* Attach core functionality to Landmark.prototype */
Landmark.prototype.init = require('./lib/core/init');
Landmark.prototype.initNav = require('./lib/core/initNav');
Landmark.prototype.connect = require('./lib/core/connect');
Landmark.prototype.start = require('./lib/core/start');
Landmark.prototype.mount = require('./lib/core/mount');
Landmark.prototype.routes = require('./lib/core/routes');
Landmark.prototype.static = require('./lib/core/static');
Landmark.prototype.importer = require('./lib/core/importer');
Landmark.prototype.createItems = require('./lib/core/createItems');
Landmark.prototype.redirect = require('./lib/core/redirect');
Landmark.prototype.list = require('./lib/core/list');
Landmark.prototype.getOrphanedLists = require('./lib/core/getOrphanedLists');
Landmark.prototype.bindEmailTestRoutes = require('./lib/core/bindEmailTestRoutes');
Landmark.prototype.wrapHTMLError = require('./lib/core/wrapHTMLError');
/**
* The exports object is an instance of Landmark.
*
* @api public
*/
var landmark = module.exports = exports = new Landmark();
// Expose modules and Classes
landmark.utils = utils;
landmark.content = require('./lib/content');
landmark.List = require('./lib/list');
landmark.Field = require('./lib/field');
landmark.Field.Types = require('./lib/fieldTypes');
landmark.View = require('./lib/view');
landmark.Email = require('./lib/email');
var security = landmark.security = {
csrf: require('./lib/security/csrf')
};
/**
* returns all .js modules (recursively) in the path specified, relative
* to the module root (where the landmark project is being consumed from).
*
* ####Example:
*
* var models = landmark.import('models');
*
* @param {String} dirname
* @api public
*/
Landmark.prototype.import = function(dirname) {
var initialPath = path.join(moduleRoot, dirname);
var doImport = function(fromPath) {
var imported = {};
fs.readdirSync(fromPath).forEach(function(name) {
var fsPath = path.join(fromPath, name),
info = fs.statSync(fsPath);
// recur
if (info.isDirectory()) {
imported[name] = doImport(fsPath);
} else {
// only import .js or .coffee files
var parts = name.split('.');
var ext = parts.pop();
if (ext === 'js' || ext === 'coffee') {
imported[parts.join('-')] = require(fsPath);
}
}
});
return imported;
};
return doImport(initialPath);
};
/**
* Applies Application updates
*/
Landmark.prototype.applyUpdates = function(callback) {
require('./lib/updates').apply(callback);
};
/**
* Renders a Landmark View
*
* @api private
*/
Landmark.prototype.render = function(req, res, view, ext) {
var landmark = this;
var templatePath = __dirname + '/templates/views/' + view + '.jade';
var jadeOptions = {
filename: templatePath,
pretty: landmark.get('env') !== 'production'
};
// TODO: Allow custom basePath for extensions... like this or similar
// if (landmark.get('extensions')) {
// jadeOptions.basedir = landmark.getPath('extensions') + '/templates';
// }
var compileTemplate = function() {
return jade.compile(fs.readFileSync(templatePath, 'utf8'), jadeOptions);
};
var template = this.get('viewCache')
? templateCache[view] || (templateCache[view] = compileTemplate())
: compileTemplate();
var flashMessages = {
info: res.req.flash('info'),
success: res.req.flash('success'),
warning: res.req.flash('warning'),
error: res.req.flash('error'),
hilight: res.req.flash('hilight')
};
var locals = {
_: _,
moment: moment,
numeral: numeral,
env: this.get('env'),
brand: landmark.get('brand'),
nav: landmark.nav,
messages: _.any(flashMessages, function(msgs) { return msgs.length; }) ? flashMessages : false,
lists: landmark.lists,
js: 'javascript:;',
utils: utils,
user: req.user,
title: 'Landmark',
signout: this.get('signout url'),
backUrl: this.get('back url') || '/',
section: {},
version: this.version,
csrf_token_key: landmark.security.csrf.TOKEN_KEY,
csrf_token_value: landmark.security.csrf.getToken(req, res),
csrf_query: '&' + landmark.security.csrf.TOKEN_KEY + '=' + landmark.security.csrf.getToken(req, res),
ga: {
property: this.get('ga property'),
domain: this.get('ga domain')
},
wysiwygOptions: {
enableImages: landmark.get('wysiwyg images') ? true : false,
enableCloudinaryUploads: landmark.get('wysiwyg cloudinary images') ? true : false,
additionalButtons: landmark.get('wysiwyg additional buttons') || ''
}
};
// optional extensions to the local scope
_.extend(locals, ext);
// add cloudinary locals if configured
if (landmark.get('cloudinary config')) {
try {
var cloudinaryUpload = cloudinary.uploader.direct_upload();
locals.cloudinary = {
cloud_name: landmark.get('cloudinary config').cloud_name,
api_key: landmark.get('cloudinary config').api_key,
timestamp: cloudinaryUpload.hidden_fields.timestamp,
signature: cloudinaryUpload.hidden_fields.signature,
prefix: landmark.get('cloudinary prefix') || '',
uploader: cloudinary.uploader
};
locals.cloudinary_js_config = cloudinary.cloudinary_js_config();
} catch(e) {
if (e === 'Must supply api_key') {
throw new Error('Invalid Cloudinary Config Provided\n\n' +
'See http://getlandmarkproject.com/docs/cms/configuration/#cloudinary for more information.');
} else {
throw e;
}
}
}
// fieldLocals defines locals that are provided to each field's `render` method
locals.fieldLocals = _.pick(locals, '_', 'moment', 'numeral', 'env', 'js', 'utils', 'user', 'cloudinary');
var html = template(_.extend(locals, ext));
res.send(html);
};
/**
* Populates relationships on a document or array of documents
*
* WARNING: This is currently highly inefficient and should only be used in development, or for
* small data sets. There are lots of things that can be done to improve performance... later.
*
* @api public
*/
Landmark.prototype.populateRelated = function(docs, relationships, callback) {
if (Array.isArray(docs)) {
async.each(docs, function(doc, done) {
doc.populateRelated(relationships, done);
}, callback);
} else if (docs && docs.populateRelated) {
docs.populateRelated(relationships, callback);
} else {
callback();
}
};
/**
* Logs a configuration error to the console
*
* @api public
*/
Landmark.prototype.console = {};
Landmark.prototype.console.err = function(type, msg) {
if (landmark.get('logger')) {
var dashes = '\n------------------------------------------------\n';
console.log(dashes + 'LandmarkJS: ' + type + ':\n\n' + msg + dashes);
}
};
/**
* Landmark version
*
* @api public
*/
landmark.version = require('./package.json').version;
// Expose Modules
landmark.session = require('./lib/session');