UNPKG

apostrophe

Version:

Apostrophe is a user-friendly content management system. You'll need more than this core module. See apostrophenow.org to get started.

244 lines (209 loc) • 7.38 kB
/** * tasks * @augments Augments the apos object with facilities for command line tasks that run * with access to the same resources that would otherwise be available to web requests. * TODO: this is a good candidate for an independent npm module. */ var _ = require('lodash'); var argv = require('optimist').argv; var async = require('async'); module.exports = function(self) { var taskActive = 0; // If a task event listener needs to return and keep working it should // invoke this callback to signify that. Apostrophe will not exit until // all busy tasks are marked done. self.taskBusy = function() { taskActive++; }; // Call when no longer busy self.taskDone = function() { taskActive--; }; // Return a `req` object suitable for use with putPage, getPage, etc. // that has full admin permissions. For use in command line tasks self.getTaskReq = function() { return { user: { permissions: { admin: true } }, baseUrl: self.options.baseUrl, res: { __: function(s) { return s; } }, // TODO: this will be hard to sustain // as we add more methods to the request // prototype. traceIn: function() {}, traceOut: function() {} }; }; var taskFailed = false; // Call if the final exit status should not be 0 (something didn't work, and you want // shell scripts invoking this command line task to be able to tell) self.taskFailed = function() { taskFailed = true; }; self.isTask = function() { return !!argv._.length; }; // Call this method just before invoking listen(). If it returns true, do not invoke // listen(). Just let the Apostrophe command line task that has been invoked // come to a graceful end on its own. This method only takes over if the // first argument begins with apostrophe:, so you can easily implement your own // command line processing without conflicts. // // "Why not have standalone task apps?" Because all the configuration and // initialization you do for a server is typically needed for command line tasks // to succeed as well (for instance, the right database connection). // // Typically this method is called for you by `apostrophe-site`. self.startTask = function(taskGroups) { if (!argv._.length) { return false; } if (!taskGroups) { taskGroups = {}; } if (!taskGroups.apostrophe) { taskGroups.apostrophe = {}; } // apostrophe:tasks reports JSON information about tasks self.tasks.tasks = function(callback) { // Joel: right now this prints user-readable stuff. // It's up to you to output it in a format you // consider more machine-readable. This is just a // copy and paste of the usage() function. -Tom var groups = _.keys(taskGroups); groups.sort(); _.each(groups, function(group) { var cmds = _.keys(taskGroups[group]); cmds.sort(); _.each(cmds, function(cmd) { console.error(self.cssName(group) + ':' + self.cssName(cmd)); }); // Separate groups with a blank line console.error(''); }); return callback(null); }; _.defaults(taskGroups.apostrophe, self.tasks); // A chance for other modules to register tasks by modifying taskGroups self.emit('tasks:register', taskGroups); // Accept . as well as : to please javascriptizens and symfonians var matches = argv._[0].match(/^(.*?)[\:|\.](.*)$/); if (!matches) { return false; } var group = matches[1]; var cmd = matches[2]; var camelGroup = self.camelName(group); if (!taskGroups[camelGroup]) { console.error('There are no tasks in the ' + group + ' group.'); return usage(); } group = taskGroups[camelGroup]; function wait(callback) { var interval = setInterval(function() { if (!taskActive) { clearInterval(interval); return callback(null); } }, 10); } var camelCmd = self.camelName(cmd); if (_.has(group, camelCmd)) { // Think about switching to an event emitter that can wait. async.series({ before: function(callback) { self.emit('task:' + argv._[0] + ':before'); return wait(callback); }, run: function(callback) { // Tasks can accept site, apos, argv and a callback; // OR apos, argv, and a callback; // OR just a callback; // OR no arguments at all. // // If they accept no arguments at all, they must // utilize apos.taskBusy() and apos.taskDone(), and // call apos.taskFailed() in the event of an error. var task = group[camelCmd]; if (task.length === 4) { return task(self._taskContext, self, argv, callback); } else if (task.length === 3) { return task(self, argv, callback); } else if (task.length === 1) { return task(callback); } else { task(); return wait(callback); } }, after: function(callback) { self.emit('task:' + argv._[0] + ':after'); return wait(callback); } }, function(err) { if (err) { console.error('Task failed:'); console.error(err); process.exit(1); } process.exit(taskFailed ? 1 : 0); }); return true; } else { console.error('There is no such task.'); return usage(); } function usage() { console.error('\nAvailable tasks:\n'); var groups = _.keys(taskGroups); groups.sort(); _.each(groups, function(group) { var cmds = _.keys(taskGroups[group]); cmds.sort(); _.each(cmds, function(cmd) { console.error(self.cssName(group) + ':' + self.cssName(cmd)); }); // Separate groups with a blank line console.error(''); }); console.error('\nYou may also use periods (.) as separators and camelCaseForNames.\n'); process.exit(1); } }; // Register our standard tasks self.tasks.migrate = function(site, apos, argv, callback) { return self.migrate(argv, callback); }; self.tasks.reset = function(callback) { return require('./tasks/reset.js')(self, callback); }; self.tasks.index = function(callback) { return require('./tasks/index.js')(self, callback); }; self.tasks.rescale = function(site, apos, argv, callback) { return require('./tasks/rescale.js')(self, argv, callback); }; self.tasks.emptyTrash = function(site, apos, argv, callback) { return require('./tasks/emptyTrash.js')(self, argv, callback); }; self.tasks.hideTrash = function(callback) { require('./tasks/hideTrash.js')(self, callback); }; // This is not a migration because it is not mandatory to have search on your site self.tasks.search = function(callback) { return require('./tasks/search.js')(self, callback); }; self.tasks.dropTestData = function(callback) { require('./tasks/dropTestData.js')(self, callback); }; // "What about apostrophe:generation?" That task // is implemented directly by apostrophe-site because // it must do things *before* Apostrophe is fully awake. -Tom };