diff --git a/.gitignore b/.gitignore index e61c7d609..ca3762d1e 100644 --- a/.gitignore +++ b/.gitignore @@ -4,6 +4,8 @@ node_modules modules/downloaded/* modules/custom modules/community/* +modules/site +modules/private media/* themes/downloaded/* themes/custom/* @@ -15,3 +17,5 @@ docs/coverage.html *.sock *.gz *.sass-cache +themes/community/ +themes/private/ diff --git a/app-cluster.js b/app-cluster.js index d1c9cff27..b2626bc25 100644 --- a/app-cluster.js +++ b/app-cluster.js @@ -1,42 +1,132 @@ -// TODO - THIS NEEDS TO BE REPLACED WITH THE NEW Node 0.5+ multi process, see cliftonc/ted +/** + * Master server process + * Initialises a number of instances of the app, based on the number of CPU's it detects + * is available. + * + * First arg is the port + * Second arg is the num worker threads; + * + * e.g. node server 3000 8 + * + */ +// Dependencies +var rootpath = process.cwd() + '/', + cluster = require('cluster'), + path = require('path'), + logo = require(path.join(rootpath, 'logo')), + colors = require('colors'), + port = process.env.PORT || 3000, + restarts = 0, + totalWorkers = 0, + runningWorkers = 0; + +var argv = processArgs(); + +launchServer(); /** - * Calipso script for running in clustered mode. Usage: node app-cluster, or - * NODE_ENV=production node app-cluster + * Launch server instance, initially master, then each worker instance is forked. + * All instances share same config. */ -var cluster = require('cluster'); -var port = process.env.PORT || 3000; -var path = __dirname; -var app; +function launchServer() { + + // Check if we are the master process + if (cluster.isMaster) { + + //require('./app').boot(function (app) { + + + // Load configuration + var Config = require(rootpath + "lib/core/Configuration"), + config = new Config(); + + config.init(); + + // Print the logo + logo.print(); + + // Set the number of workers + totalWorkers = config.get('server:cluster:workers') || argv.c; + + // Fork workers based on num cpus + console.log("Loading ".green + totalWorkers + " workers, please wait ...".green); + for (var i = 0; i < totalWorkers; i++) { + forkWorker(); + } + + // Log worker death + // TODO : Auto restart with number of retries + cluster.on('death', function(worker) { + + console.error('worker ' + worker.pid + ' died ...'); + + // Manage restarting of workers + if(config.get('server:cluster:restartWorkers')) { + if(restarts > config.get('server:cluster:maximumRestarts')) { + console.error('Maximum number of restarts reached, not restarting this worker.'.red); + } else { + restarts++; + forkWorker(); + } + } + + }); + + //}); + + } else { + + // We are a child worker, so bootstrap the app. + require(rootpath + 'app').boot(true, function (app) { + + //logger.info("Worker [" + argv.m.cyan + "] with pid " + (process.pid + "").grey + " online."); + app.listen(port); + + process.send({ cmd: 'workerStarted', pid: process.pid, port: port }); + + }); + + } + +} + +/** + * Helper function to fork a worker, we need to reset the counter in the master thread + * hence the messaging back, also deal with messaging around job management from worker threads. + */ +function forkWorker() { + + var worker = cluster.fork(); + + worker.on('message', function(msg) { + + if (msg.cmd) { + + if(msg.cmd == 'workerStarted') { + + runningWorkers++; + + if(runningWorkers === parseInt(totalWorkers)) { + console.log("Calipso configured for: ".green + (global.process.env.NODE_ENV || 'development') + " environment.".green); + console.log("Calipso server running ".green + runningWorkers + " workers, listening on port: ".green + port); + } + + } + + } + + }); + +} /** - * Create an instance of calipso via the normal App, + * Process command line arguments using optimist */ -require('./app').boot(function (app) { - - /** - * TODO: Check to ensure that the logs and pids folders exist before launching - */ - - cluster(app) - .set('working directory', path) - .set('socket path', path) - .in('development') - .set('workers', 3) - .use(cluster.logger(path + '/logs', 'debug')) - .use(cluster.debug()) - .use(cluster.pidfiles(path + '/pids')) - .use(cluster.stats({ connections: true, lightRequests: true })) - .in('test') - .set('workers', 3) - .use(cluster.logger(path + '/logs', 'warning')) - .use(cluster.pidfiles(path + '/pids')) - .in('production') - .set('workers', 3) - .use(cluster.logger(path + '/logs')) - .use(cluster.pidfiles(path + '/pids')) - .in('all') - .listen(port); - - -},true); +function processArgs() { + return require('optimist') + .usage('Launch Calipso in Clustered Mode\nUsage: $0') + .describe('c', 'Number of CPUs') + .alias('c', 'cpu') + .default('c', require('os').cpus().length) + .argv; +} diff --git a/app.js b/app.js index 9f1880d9c..de2ea220b 100644 --- a/app.js +++ b/app.js @@ -8,6 +8,86 @@ * */ +var req = require('express/lib/request'); + + var flashFormatters = req.flashFormatters = { + s: function(val){ + return String(val); + } + }; + + /** + * Queue flash `msg` of the given `type`. + * + * Examples: + * + * req.flash('info', 'email sent'); + * req.flash('error', 'email delivery failed'); + * req.flash('info', 'email re-sent'); + * // => 2 + * + * req.flash('info'); + * // => ['email sent', 'email re-sent'] + * + * req.flash('info'); + * // => [] + * + * req.flash(); + * // => { error: ['email delivery failed'], info: [] } + * + * Formatting: + * + * Flash notifications also support arbitrary formatting support. + * For example you may pass variable arguments to `req.flash()` + * and use the %s specifier to be replaced by the associated argument: + * + * req.flash('info', 'email has been sent to %s.', userName); + * + * To add custom formatters use the `exports.flashFormatters` object. + * + * @param {String} type + * @param {String} msg + * @return {Array|Object|Number} + * @api public + */ + + function miniMarkdown(str){ + return String(str) + .replace(/(__|\*\*)(.*?)\1/g, '$2') + .replace(/(_|\*)(.*?)\1/g, '$2') + .replace(/\[([^\]]+)\]\(([^)]+)\)/g, '$1'); + }; + + req.flash = function(type, msg){ + if (this.session === undefined) throw Error('req.flash() requires sessions'); + var msgs = this.session.flash = this.session.flash || {}; + if (type && msg) { + var i = 2 + , args = arguments + , formatters = this.app.flashFormatters || {}; + formatters.__proto__ = flashFormatters; + msg = miniMarkdown(msg); + msg = msg.replace(/%([a-zA-Z])/g, function(_, format){ + var formatter = formatters[format]; + if (formatter) return formatter(utils.escape(args[i++])); + }); + return (msgs[type] = msgs[type] || []).push(msg); + } else if (type) { + var arr = msgs[type]; + delete msgs[type]; + return arr || []; + } else { + this.session.flash = {}; + return msgs; + } + }; + +var sys; +try { + sys = require('util'); +} catch (e) { + sys = require('sys'); +} var rootpath = process.cwd() + '/', path = require('path'), @@ -40,8 +120,8 @@ var path = rootpath, function bootApplication(next) { // Create our express instance, export for later reference - var app = express.createServer(); - app.path = path; + var app = express.createServer ? express.createServer() : express(); + app.path = function() { return path }; app.isCluster = false; // Load configuration @@ -55,8 +135,10 @@ function bootApplication(next) { calipso.defaultTheme = app.config.get('themes:default'); app.use(express.bodyParser()); + // Pause requests if they were not parsed to allow PUT and POST with custom mime types + app.use(function (req, res, next) { if (!req._body) { req.pause(); } next(); }); app.use(express.methodOverride()); - app.use(express.cookieParser()); + app.use(express.cookieParser(app.config.get('session:secret'))); app.use(express.responseTime()); // Create dummy session middleware - tag it so we can later replace diff --git a/bin/calipso b/bin/calipso index c9cc1bbcc..cdbbcd26e 100644 --- a/bin/calipso +++ b/bin/calipso @@ -1,395 +1,3 @@ -#!/usr/bin/env node - -/** - * Calipso command prompt - * @Params - cmd - server | script | params - */ - -/** -* Dependencies on this script -*/ -var fs = require('fs'), - nodepath = require('path'), - exec = require('child_process').exec, - logo = require('../logo'), - colors = require('colors'); - -/** - * Optimist configuration - */ -var argv = require('optimist') - .default('src', false) - .default('port', 3000) - .alias('p', 'port') - .alias('s', 'src') - .boolean('s') - .argv; - -/** - * Paths - * path = directory script being run from - * calipsoPath = calipso library installation path - **/ -var path = fs.realpathSync('.').replace(/\s/g,'\\ '); -var calipsoPath = __dirname + "/../"; -var step = require('step'); - -/** - * Main Command Object - * Defaults to display the help script - */ -var appLauncher = { - command:argv._[0] ? argv._[0] : 'help', - server: { port:argv.port }, - src:argv.src, - script: { - name:'help', - params: argv._.splice(1) - } - }; - -runLauncher(appLauncher); - -/** - * Run the launcher - * @param appLauncher - */ -function runLauncher(appLauncher) { - - // Always use current directory? - console.log('Launching calipso from: '.cyan.bold + path.white); - console.log('Calipso directory: '.cyan.bold + calipsoPath.white); - - // Check if this is a calipso src folder - if(isLibrary() && !appLauncher.src) { - console.log('\r\nWARNING:'.yellow.bold + ' You are running this from a Calipso source folder.'.white.bold); - } - - // Check if this is a calipso site - if(!isCalipso() && appLauncher.command != 'site' && !isLibrary()) { - console.log('\x1b[1mThis is not a Calipso site - you must run:\x1b[0m calipso site SiteName\r\n'); - return; - } - - switch(appLauncher.command) { - case 'test': - runTests(appLauncher.script); - break; - case 'server': - runServer(appLauncher.server.port); - break; - case 'site': - createApplication(path,appLauncher.script.params); - break; - case 'install': - runInstall(path); - break; - case 'modules': - process.chdir(path); - require(path + '/app').boot(false, function(app) { - var modules = require(calipsoPath + '/lib/cli/Modules') - modules.moduleRouter(path,appLauncher.script.params,true,function(err) { - if(err) { - console.log("\r\n" + err.message.red.bold + "\r\n"); - } - process.exit(); - }); - }); - break; - case 'themes': - process.chdir(path); - require(path + '/app').boot(false, function(app) { - var themes = require(calipsoPath + '/lib/cli/Themes') - themes.themeRouter(path,appLauncher.script.params,true,function(err) { - if(err) { - console.log("\r\n" + err.message.red.bold + "\r\n"); - } - process.exit(); - }); - }); - break; - default: - // Default is to display help - appLauncher.command = 'script'; - runScript(appLauncher.script); - } - -} - -/** - * Check if we are running from the library folder (or something cloned out of github) - **/ -function isLibrary() { - return nodepath.existsSync(path + '/bin/calipso'); -} - -/** - * Check if .calipso exists - **/ -function isCalipso() { - return nodepath.existsSync(path + '/.calipso'); -} - -/** - * Run a script - * @param appLauncher - * Runs by default from path where calipso runs via __dirname. - */ -function runScript(scriptLauncher) { - - if(!nodepath.existsSync(path + '/scripts/'+ scriptLauncher.name)) { - scriptLauncher.name = 'help'; - scriptLauncher.params = []; - } - - var script = require(path + '/scripts/'+ scriptLauncher.name); - logo.print(); - script.execute(scriptLauncher.params, path); - - -} - -/** - * Run expresso tests - */ -function runTests(appLauncher) { - - // var test = appLauncher.name ? appLauncher.name : 'all'; - exec('make', { timeout: 60000, cwd:path }, function (error, stdout, stderr) { - console.log(stdout); - console.log(stderr); - }); - -} - -/** - * Launch a server - */ -function runServer(port) { - - logo.print(); - - // Ensure we run in the local folder of the application - process.chdir(path); - require(path + '/app').boot(false, function(app) { - app.listen(port); - console.log("Calipso server listening on port: ".green + app.address().port.toString().white.bold); - console.log("Calipso configured for ".green + (global.process.env.NODE_ENV || 'development').white.bold + " environment\r\n".green); - }); - -} - -/** - * Create application at the given directory `path`. - * - * @param {String} path - */ -function createApplicationAt(path) { - - step( - function create() { - var self = this; - - mkdir(path + '/bin',function() { - copy(calipsoPath + '/bin/*.sh',path + '/bin',self.parallel()); - }); - mkdir(path + '/conf',function() { - copy(calipsoPath + '/conf/*',path + '/conf',self.parallel()); - }); - mkdir(path + '/i18n',function() { - copy(calipsoPath + '/i18n/*',path + '/i18n',self.parallel()); - }); - mkdir(path + '/lib',function() { - copy(calipsoPath + '/lib/*',path + '/lib',self.parallel()); - }); - mkdir(path + '/modules',function() { - copy(calipsoPath + '/modules/*',path + '/modules',self.parallel()); - }); - mkdir(path + '/support',function() { - copy(calipsoPath + '/support/*',path + '/support',self.parallel()); - }); - mkdir(path + '/test',function() { - copy(calipsoPath + '/test/*',path + '/test',self.parallel()); - }); - mkdir(path + '/themes',function() { - copy(calipsoPath + '/themes/*',path + '/themes',self.parallel()); - }); - mkdir(path + '/utils',function() { - copy(calipsoPath + '/utils/*',path + '/utils',self.parallel()); - }); - mkdir(path + '/scripts',function() { - copy(calipsoPath + '/scripts/*',path + '/scripts',self.parallel()); - mkdir(path + '/scripts/templates',function() { - copy(calipsoPath + '/scripts/templates/*',path + '/scripts/templates',self.parallel()); - }); - }); - mkdir(path + '/node_modules',function() { - copy(calipsoPath + '/node_modules/*',path + '/node_modules',self.parallel()); - }); - mkdir(path + '/logs',self.parallel()); - mkdir(path + '/pids',self.parallel()); - mkdir(path + '/media',self.parallel()); - mkdir(path + '/tmp',self.parallel()); - copy(calipsoPath + '/app-cluster.js',path + '/',self.parallel()); - copy(calipsoPath + '/app.js',path + '/',self.parallel()); - copy(calipsoPath + '/package.json',path + '/',self.parallel()); - copy(calipsoPath + '/logo.js',path + '/',self.parallel()); - }, - function done() { - write(path + '/.calipso','Created @ ' + new Date()); - console.log(''); - console.log('Application created at: '.green + path.white.bold); - // CC : Disabled to test default NPM installation process - console.log('Installing any difficult application dependencies via NPM, please wait ... '.green); - runInstall(path); - } - ) - -} - -/** - * Run the install shell script - */ -function runInstall(path) { - path = path.replace(/\\\s/g, ' '); - exec('./bin/siteInstall.sh', { timeout: 60000, cwd:path }, function (error, stdout, stderr) { - console.log(stdout); - console.log(stderr); - }); - -} - -/** - * Create a site - */ -function createApplication(path,siteName) { - - var site; - if(siteName.toString().match(/^\//)) { - // site is a full path - site = siteName.toString(); - } else { - site = path + "/" + siteName; - } - - mkdir(site,function() { - emptyDirectory(site, function(empty){ - if (empty) { - createApplicationAt(site); - } else { - confirm('This will over-write the existing site, continue? '.red.bold, function(ok){ - if (ok) { - process.stdin.destroy(); - createApplicationAt(site); - } else { - abort('aborting'); - } - }); - } - }); - }); -}; - -/** - * Check if the given directory `path` is empty. - * - * @param {String} path - * @param {Function} fn - */ -function emptyDirectory(path, fn) { - fs.readdir(path, function(err, files){ - if (err && 'ENOENT' != err.code) throw err; - fn(!files || !files.length); - }); -} - -/** - * echo str > path. - * - * @param {String} path - * @param {String} str - */ - -function write(path, str) { - fs.writeFile(path, str); - console.log(' create : '.blue + path.white); -} - -/** - * Prompt confirmation with the given `msg`. - * - * @param {String} msg - * @param {Function} fn - */ - -function confirm(msg, fn) { - prompt(msg, function(val){ - fn(/^ *y(es)?/i.test(val)); - }); -} - -/** - * Prompt input with the given `msg` and callback `fn`. - * - * @param {String} msg - * @param {Function} fn - */ - -function prompt(msg, fn) { - // prompt - if (' ' == msg[msg.length - 1]) { - process.stdout.write(msg); - } else { - console.log(msg); - } - - // stdin - process.stdin.setEncoding('ascii'); - process.stdin.once('data', function(data){ - fn(data); - }).resume(); -} - -/** - * Mkdir -p. - * - * TODO - these are unix only ... - * - * @param {String} path - * @param {Function} fn - */ - -function mkdir(path, fn) { - exec('mkdir -p ' + path, function(err){ - if (err) throw err; - console.log(' create: '.blue + path.white); - fn && fn(); - }); -} - - -/** - * cp -r - * - * @param {String} path - * @param {Function} fn - */ - -function copy(from, to, fn) { - exec('cp -R ' + from + ' ' + to, function(err){ - if (err) throw err; - console.log(' Copied: '.blue + to.white); - fn && fn(); - }); -} - -/** - * Exit with the given `str`. - * - * @param {String} str - */ - -function abort(str) { - console.error(str); - process.exit(1); -} +#!/bin/sh +DIR=`dirname $0` +NODE_PATH="${DIR}/.." node "${DIR}/../lib/calipso-cli.js" $* diff --git a/lib/calipso-cli.js b/lib/calipso-cli.js new file mode 100755 index 000000000..302b8efd2 --- /dev/null +++ b/lib/calipso-cli.js @@ -0,0 +1,397 @@ +/** + * Calipso command prompt + * @Params - cmd - server | script | params + */ + +/** +* Dependencies on this script +*/ +var fs = require('fs'), + nodepath = require('path'), + exec = require('child_process').exec, + logo = require('../logo'), + colors = require('colors'); + +/** + * Optimist configuration + */ +var argv = require('optimist') + .default('src', false) + .default('port', 3000) + .alias('p', 'port') + .alias('s', 'src') + .boolean('s') + .argv; + +/** + * Paths + * path = directory script being run from + * calipsoPath = calipso library installation path + **/ +var path = fs.realpathSync('.'); +var calipsoPath = __dirname + "/../"; + +//require.paths.unshift(calipsoPath); //make local paths accessible + +var step = require('step'); + +/** + * Main Command Object + * Defaults to display the help script + */ +var appLauncher = { + command:argv._[0] ? argv._[0] : 'help', + server: { port:argv.port }, + src:argv.src, + script: { + name:'help', + params: argv._.splice(1) + } + }; + +runLauncher(appLauncher); + +/** + * Run the launcher + * @param appLauncher + */ +function runLauncher(appLauncher) { + + // Always use current directory? + console.log('Launching calipso from: '.cyan.bold + path.white); + console.log('Calipso directory: '.cyan.bold + calipsoPath.white); + + // Check if this is a calipso src folder + if(isLibrary() && !appLauncher.src) { + console.log('\r\nWARNING:'.yellow.bold + ' You are running this from a Calipso source folder.'.white.bold); + } + + // Check if this is a calipso site + if(!isCalipso() && appLauncher.command != 'site' && !isLibrary()) { + console.log('\x1b[1mThis is not a Calipso site - you must run:\x1b[0m calipso site SiteName\r\n'); + return; + } + + switch(appLauncher.command) { + case 'test': + runTests(appLauncher.script); + break; + case 'server': + runServer(appLauncher.server.port); + break; + case 'site': + createApplication(path,appLauncher.script.params); + break; + case 'install': + runInstall(path); + break; + case 'modules': + process.chdir(path); + //require.paths.unshift(path); //make local paths accessible + require(path + '/app').boot(function(app) { + var modules = require('lib/cli/Modules') + modules.moduleRouter(path,appLauncher.script.params,true,function(err) { + if(err) { + console.log("\r\n" + err.message.red.bold + "\r\n"); + } + process.exit(); + }); + }); + break; + case 'themes': + process.chdir(path); + //require.paths.unshift(path); //make local paths accessible + require(path + '/app').boot(function(app) { + var themes = require('lib/cli/Themes') + themes.themeRouter(path,appLauncher.script.params,true,function(err) { + if(err) { + console.log("\r\n" + err.message.red.bold + "\r\n"); + } + process.exit(); + }); + }); + break; + default: + // Default is to display help + appLauncher.command = 'script'; + runScript(appLauncher.script); + } + +} + +/** + * Check if we are running from the library folder (or something cloned out of github) + **/ +function isLibrary() { + return (fs.existsSync || nodepath.existsSync)(path + '/bin/calipso'); +} + +/** + * Check if .calipso exists + **/ +function isCalipso() { + return (fs.existsSync || nodepath.existsSync)(path + '/.calipso'); +} + +/** + * Run a script + * @param appLauncher + * Runs by default from path where calipso runs via __dirname. + */ +function runScript(scriptLauncher) { + + if(!(fs.existsSync || nodepath.existsSync)(path + '/scripts/'+ scriptLauncher.name)) { + scriptLauncher.name = 'help'; + scriptLauncher.params = []; + } + + var script = require(path + '/scripts/'+ scriptLauncher.name); + logo.print(); + script.execute(scriptLauncher.params, path); + + +} + +/** + * Run expresso tests + */ +function runTests(appLauncher) { + + // var test = appLauncher.name ? appLauncher.name : 'all'; + exec('make', { timeout: 60000, cwd:path }, function (error, stdout, stderr) { + console.log(stdout); + console.log(stderr); + }); + +} + +/** + * Launch a server + */ +function runServer(port) { + + logo.print(); + + // Ensure we run in the local folder of the application + process.chdir(path); + require(path + '/app').boot(function(app) { + app.listen(port); + console.log("Calipso server listening on port: ".green + app.address().port.toString().white.bold); + console.log("Calipso configured for ".green + (global.process.env.NODE_ENV || 'development').white.bold + " environment\r\n".green); + }); + +} + +/** + * Create application at the given directory `path`. + * + * @param {String} path + */ +function createApplicationAt(path) { + + step( + function create() { + var self = this; + + mkdir(path + '/bin',function() { + copy(calipsoPath + '/bin/*.sh',path + '/bin',self.parallel()); + }); + mkdir(path + '/conf',function() { + copy(calipsoPath + '/conf/*',path + '/conf',self.parallel()); + }); + mkdir(path + '/i18n',function() { + copy(calipsoPath + '/i18n/*',path + '/i18n',self.parallel()); + }); + mkdir(path + '/lib',function() { + copy(calipsoPath + '/lib/*',path + '/lib',self.parallel()); + }); + mkdir(path + '/modules',function() { + copy(calipsoPath + '/modules/*',path + '/modules',self.parallel()); + }); + mkdir(path + '/support',function() { + copy(calipsoPath + '/support/*',path + '/support',self.parallel()); + }); + mkdir(path + '/test',function() { + copy(calipsoPath + '/test/*',path + '/test',self.parallel()); + }); + mkdir(path + '/themes',function() { + copy(calipsoPath + '/themes/*',path + '/themes',self.parallel()); + }); + mkdir(path + '/utils',function() { + copy(calipsoPath + '/utils/*',path + '/utils',self.parallel()); + }); + mkdir(path + '/scripts',function() { + copy(calipsoPath + '/scripts/*',path + '/scripts',self.parallel()); + mkdir(path + '/scripts/templates',function() { + copy(calipsoPath + '/scripts/templates/*',path + '/scripts/templates',self.parallel()); + }); + }); + mkdir(path + '/node_modules',function() { + copy(calipsoPath + '/node_modules/*',path + '/node_modules',self.parallel()); + }); + mkdir(path + '/logs',self.parallel()); + mkdir(path + '/pids',self.parallel()); + mkdir(path + '/media',self.parallel()); + mkdir(path + '/tmp',self.parallel()); + copy(calipsoPath + '/app-cluster.js',path + '/',self.parallel()); + copy(calipsoPath + '/app.js',path + '/',self.parallel()); + copy(calipsoPath + '/package.json',path + '/',self.parallel()); + copy(calipsoPath + '/logo.js',path + '/',self.parallel()); + }, + function done() { + write(path + '/.calipso','Created @ ' + new Date()); + console.log(''); + console.log('Application created at: '.green + path.white.bold); + // CC : Disabled to test default NPM installation process + console.log('Installing any difficult application dependencies via NPM, please wait ... '.green); + runInstall(path); + } + ) + +} + +/** + * Run the install shell script + */ +function runInstall(path) { + exec('./bin/siteInstall.sh', { timeout: 60000, cwd:path }, function (error, stdout, stderr) { + console.log(stdout); + console.log(stderr); + }); + +} + +/** + * Create a site + */ +function createApplication(path,siteName) { + + var site; + if(siteName.toString().match(/^\//)) { + // site is a full path + site = siteName.toString(); + } else { + site = path + "/" + siteName; + } + + mkdir(site,function() { + emptyDirectory(site, function(empty){ + if (empty) { + createApplicationAt(site); + } else { + confirm('This will over-write the existing site, continue? '.red.bold, function(ok){ + if (ok) { + process.stdin.destroy(); + createApplicationAt(site); + } else { + abort('aborting'); + } + }); + } + }); + }); +}; + +/** + * Check if the given directory `path` is empty. + * + * @param {String} path + * @param {Function} fn + */ +function emptyDirectory(path, fn) { + fs.readdir(path, function(err, files){ + if (err && 'ENOENT' != err.code) throw err; + fn(!files || !files.length); + }); +} + +/** + * echo str > path. + * + * @param {String} path + * @param {String} str + */ + +function write(path, str) { + fs.writeFile(path, str); + console.log(' create : '.blue + path.white); +} + +/** + * Prompt confirmation with the given `msg`. + * + * @param {String} msg + * @param {Function} fn + */ + +function confirm(msg, fn) { + prompt(msg, function(val){ + fn(/^ *y(es)?/i.test(val)); + }); +} + +/** + * Prompt input with the given `msg` and callback `fn`. + * + * @param {String} msg + * @param {Function} fn + */ + +function prompt(msg, fn) { + // prompt + if (' ' == msg[msg.length - 1]) { + process.stdout.write(msg); + } else { + console.log(msg); + } + + // stdin + process.stdin.setEncoding('ascii'); + process.stdin.once('data', function(data){ + fn(data); + }).resume(); +} + +/** + * Mkdir -p. + * + * TODO - these are unix only ... + * + * @param {String} path + * @param {Function} fn + */ + +function mkdir(path, fn) { + exec('mkdir -p ' + path, function(err){ + if (err) throw err; + console.log(' create: '.blue + path.white); + fn && fn(); + }); +} + + +/** + * cp -r + * + * @param {String} path + * @param {Function} fn + */ + +function copy(from, to, fn) { + exec('cp -R ' + from + ' ' + to, function(err){ + if (err) throw err; + console.log(' Copied: '.blue + to.white); + fn && fn(); + }); +} + +/** + * Exit with the given `str`. + * + * @param {String} str + */ + +function abort(str) { + console.error(str); + process.exit(1); +} diff --git a/lib/cli/Download.js b/lib/cli/Download.js index b55a85504..ce9b8de61 100644 --- a/lib/cli/Download.js +++ b/lib/cli/Download.js @@ -12,6 +12,12 @@ * Module exports * */ +var sys; +try { + sys = require('util'); +} catch (e) { + sys = require('sys'); +} var rootpath = process.cwd() + '/', path = require('path') calipso = require(path.join(rootpath, 'lib/calipso')), @@ -167,7 +173,7 @@ function downloadFile(url, fileName, toPath, next) { // Ensure we have our download folder var tmpFolder = toPath; - if(!path.existsSync(tmpFolder)) { + if(!(fs.existsSync || path.existsSync)(tmpFolder)) { fs.mkdirSync(tmpFolder, 0755); } @@ -401,7 +407,7 @@ function unzipDownload(type, file, callback) { if(tmpName) { // Make sure we delete any existing tmp folder - if(path.existsSync(tmpFolder)) { + if((fs.existsSync || path.existsSync)(tmpFolder)) { rimraf.sync(tmpFolder); } diff --git a/lib/cli/Modules.js b/lib/cli/Modules.js index 23588c7ff..19602bde0 100644 --- a/lib/cli/Modules.js +++ b/lib/cli/Modules.js @@ -16,6 +16,12 @@ * Module exports * */ +var sys; +try { + sys = require('util'); +} catch (e) { + sys = require('sys'); +} var rootpath = process.cwd() + '/', path = require('path') calipso = require(path.join(rootpath, 'lib/calipso')), @@ -115,7 +121,7 @@ exports.findModule = findModule; * Try to download a module */ function downloadModule(options, cli, next) { - var toPath = calipso.app.path + "/modules/downloaded/"; + var toPath = calipso.app.path() + "/modules/downloaded/"; var fromUrl = options[1]; download('module', fromUrl, toPath, cli, function(err,moduleName,path) { if(err) { diff --git a/lib/cli/RepoApi.js b/lib/cli/RepoApi.js index 98231a69d..de4a3c490 100644 --- a/lib/cli/RepoApi.js +++ b/lib/cli/RepoApi.js @@ -12,6 +12,12 @@ * Module exports * */ +var sys; +try { + sys = require('util'); +} catch (e) { + sys = require('sys'); +} var rootpath = process.cwd() + '/', path = require('path') calipso = require(path.join(rootpath, 'lib/calipso')), diff --git a/lib/cli/Themes.js b/lib/cli/Themes.js index c6ed8ba58..973a8619a 100644 --- a/lib/cli/Themes.js +++ b/lib/cli/Themes.js @@ -16,6 +16,12 @@ * Module exports * */ +var sys; +try { + sys = require('util'); +} catch (e) { + sys = require('sys'); +} var rootpath = process.cwd() + '/', path = require('path') calipso = require(path.join(rootpath, 'lib/calipso')), @@ -94,7 +100,7 @@ exports.findTheme = findTheme; * Try to download a module */ function downloadTheme(options, cli, next) { - var toPath = calipso.app.path + "/themes/downloaded/"; + var toPath = calipso.app.path() + "/themes/downloaded/"; var fromUrl = options[1]; download('theme', fromUrl, toPath, cli, function(err, themeName, path) { if(err) { diff --git a/lib/core/Configuration.js b/lib/core/Configuration.js index 5fa94935b..9f085ea78 100644 --- a/lib/core/Configuration.js +++ b/lib/core/Configuration.js @@ -42,7 +42,7 @@ Configuration.prototype.init = function(next) { */ Configuration.prototype.check = function() { - if (!path.existsSync(this.file)) { + if (!(fs.existsSync || path.existsSync)(this.file)) { try { var defaultFile = fs.readFileSync(this.defaultConfig); // Parse it to make sure there are no errors diff --git a/lib/core/Menu.js b/lib/core/Menu.js index 78ae1f0ca..6fc8a344d 100644 --- a/lib/core/Menu.js +++ b/lib/core/Menu.js @@ -12,6 +12,12 @@ /** * Includes */ +var sys; +try { + sys = require('util'); +} catch (e) { + sys = require('sys'); +} var rootpath = process.cwd() + '/', path = require('path'), utils = require('connect').utils, @@ -224,7 +230,8 @@ CalipsoMenu.prototype.menuStartTag = function(menu, selected) { return "