diff --git a/app.js b/app.js index 00c253662..252e29904 100644 --- a/app.js +++ b/app.js @@ -48,7 +48,7 @@ function bootApplication(next) { app.isCluster = false; // Load configuration - var Config = require(path + "/lib/Config").Config; + var Config = calipso.config; //require(path + "/lib/core/Config").Config; app.config = new Config(); app.config.init(); diff --git a/cluster.js b/cluster.js index dedf92496..86890fce5 100644 --- a/cluster.js +++ b/cluster.js @@ -37,7 +37,7 @@ function launchServer() { // Load configuration - var Config = require(rootpath + "lib/Config").Config, + var Config = require(rootpath + "lib/core/Config"), config = new Config(); config.init(); @@ -48,6 +48,7 @@ function launchServer() { 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(); } diff --git a/lib/calipso.js b/lib/calipso.js index 1861fc2e5..ddaef7792 100644 --- a/lib/calipso.js +++ b/lib/calipso.js @@ -29,135 +29,118 @@ * */ -var app, - rootpath = process.cwd(), - path = require('path'), - events = require('events'), - mongoStore = require(path.join(rootpath, 'support/connect-mongodb')), - mongoose = require('mongoose'), - calipso = exports = module.exports = { - - //Export module dependencies, reduces need for later modules to require everything. - lib: { - fs: require('fs'), - path: require('path'), - express: require('express'), - step: require('step'), - util: require('util'), - mongoose: require('mongoose'), - url: require('url'), - ejs: require('ejs'), - pager: require(path.join(rootpath, 'utils/pager')), - prettyDate: require(path.join(rootpath, 'utils/prettyDate.js')), - crypto: require(path.join(rootpath, 'utils/crypto.js')), - connect: require('connect'), - _:require('underscore'), - async: require('async') - }, - sessionCache: {}, - dynamicHelpers: require('./Helpers'), +var rootpath = process.cwd() + '/', + path = require('path'), + fs = require('fs'), + events = require('events'); + +// Core object +var calipso = module.exports = { + + // View Helpers getDynamicHelpers: function(req, res) { req.helpers = {}; - for(var helper in this.dynamicHelpers) { - req.helpers[helper] = this.dynamicHelpers[helper](req, res, this); + for(var helper in this.helpers) { + req.helpers[helper] = this.helpers[helper](req, res, this); } }, - mongoConnect: mongoConnect, + + // Configuration exposed reloadConfig: reloadConfig, - // Track running MR (Map Reduce) operations - mr: {}, - // Loaded theme + + // Core objects - themes, data, modules theme: {}, - // Holds global config data data: {}, modules: {}, - // Core libraries - date: require('./Date').CalipsoDate, - table: require('./Table').CalipsoTable, - link: require('./Link').CalipsoLink, - menu: require('./Menu'), - event: require('./Event'), - utils: require('./Utils'), - logging: require('./Logging'), - permissions: require('./Permission').PermissionHelpers, - form: require('./Form').CalipsoForm, - notifyDependenciesOfInit: notifyDependenciesOfInit, - notifyDependenciesOfRoute: notifyDependenciesOfRoute, - e: {}, - - /** - * Core router and initialisation function. - * - * Returns a connect middleware function that manages the roucting - * of requests to modules. - */ - calipsoRouter: function(app, initCallback) { - - calipso.app = app; - - // Load the calipso package.json into about - loadAbout(app, rootpath, 'package.json'); - - calipso.config = app.config; - - // Configure the cache - calipso.cache = require('./Cache').Cache({ttl:calipso.config.get('performance:cache:ttl')}); - - // Store the callback function for later - calipso.initCallback = function() { - initCallback(); - }; - - // Create our calipso event emitter - calipso.e = new calipso.event.CalipsoEventEmitter(); - - // Load configuration - initialiseCalipso(); - - // Return the function that manages the routing - // Ok being non-synchro - return function(req,res,next) { - - // Default menus and blocks for each request - // More of these can be added in modules, these are jsut the defaults - res.menu = {admin:new calipso.menu.CalipsoMenu('admin','weight','root',{cls:'admin'}), - adminToolbar:new calipso.menu.CalipsoMenu('adminToolbar','weight','root',{cls:'admin-toolbar toolbar'}), // TODO - Configurable! - userToolbar:new calipso.menu.CalipsoMenu('userToolbar','weight','root',{cls:'user-toolbar toolbar'}), - primary:new calipso.menu.CalipsoMenu('primary','name','root',{cls:'primary'}), - secondary:new calipso.menu.CalipsoMenu('secondary','name','root',{cls:'secondary'})}; - - - // Initialise our clientJS library linked to this request - var Client = require('./client/Client'); - res.client = new Client(); - - // Initialise helpers - first pass - calipso.getDynamicHelpers(req, res); - - // Deal with any form content - // This is due to node-formidable / connect / express - // https://github.com/felixge/node-formidable/issues/30 - // Best to parse the form very early in the chain - if(req.form) { - - calipso.form.process(req, function() { - - // Route the modules - eventRouteModules(req, res, next); - }); + // Express Router + calipsoRouter: router - } else { +}; + +// Load libraries in the core folder +loadCore(calipso); + +function loadCore(calipso) { + + fs.readdirSync(rootpath + 'lib/core').forEach(function(library){ + var libName = library.split(".")[0].toLowerCase(); + calipso[libName] = require(rootpath + 'lib/core/' + library); + }); + +} + +/** + * Core router and initialisation function. + * + * Returns a connect middleware function that manages the roucting + * of requests to modules. + */ +function router(app, initCallback) { + + calipso.app = app; + + // Load the calipso package.json into about + calipso.module.loadAbout(app, rootpath, 'package.json'); + + calipso.config = app.config; + + // Configure the cache + calipso.cacheService = calipso.cache.Cache({ttl:calipso.config.get('performance:cache:ttl')}); + + // Store the callback function for later + calipso.initCallback = function() { + initCallback(); + }; + + // Create our calipso event emitter + calipso.e = new calipso.event.CalipsoEventEmitter(); + + // Load configuration + initialiseCalipso(); + + // Return the function that manages the routing + // Ok being non-synchro + return function(req,res,next) { + + // Default menus and blocks for each request + // More of these can be added in modules, these are jsut the defaults + res.menu = {admin:new calipso.menu.CalipsoMenu('admin','weight','root',{cls:'admin'}), + adminToolbar:new calipso.menu.CalipsoMenu('adminToolbar','weight','root',{cls:'admin-toolbar toolbar'}), // TODO - Configurable! + userToolbar:new calipso.menu.CalipsoMenu('userToolbar','weight','root',{cls:'user-toolbar toolbar'}), + primary:new calipso.menu.CalipsoMenu('primary','name','root',{cls:'primary'}), + secondary:new calipso.menu.CalipsoMenu('secondary','name','root',{cls:'secondary'})}; + + + // Initialise our clientJS library linked to this request + var Client = require('./client/Client'); + res.client = new Client(); + + // Initialise helpers - first pass + calipso.getDynamicHelpers(req, res); + + // Deal with any form content + // This is due to node-formidable / connect / express + // https://github.com/felixge/node-formidable/issues/30 + // Best to parse the form very early in the chain + if(req.form) { + + calipso.form.process(req, function() { // Route the modules - eventRouteModules(req, res, next); + calipso.module.eventRouteModules(req, res, next); - } + }); - }; - } + } else { -}; + // Route the modules + calipso.module.eventRouteModules(req, res, next); + + } + + }; +} /** * Load the application configuration @@ -183,7 +166,7 @@ function initialiseCalipso(reloadConfig) { calipso.logging.configureLogging(); // Check / Connect Mongo - mongoConnect(calipso.config.get('database:uri'), false, function(err, connected) { + calipso.storage.mongoConnect(calipso.config.get('database:uri'), false, function(err, connected) { if(err) { console.log("There was an error connecting to the database: " + err.message); @@ -197,10 +180,12 @@ function initialiseCalipso(reloadConfig) { configureTheme(function() { // Load all the modules - loadModules(); + calipso.module.loadModules(function() { - // Initialise, callback via calipso.initCallback - initModules(); + // Initialise, callback via calipso.initCallback + calipso.module.initModules(); + + }); }); @@ -210,214 +195,6 @@ function initialiseCalipso(reloadConfig) { } -/** - * Route all of the modules based on the module event model - * This replaces an earlier version that only executed modules in - * parallel via step - */ -function eventRouteModules(req, res, next) { - - // Ignore static content - // TODO : Make this more connect friendly or at least configurable - if(req.url.match(/^\/images|^\/js|^\/css|^\/favicon.ico|png$|jpg$|gif$|css$|js$/)) { - return next(); - } - - req.timeStart = new Date(); - - // Attach our event listener to this request - attachRequestEvents(req, res, next); - - // Initialise the response re. matches - // Later used to decide to show 404 - res.routeMatched = false; - - // Now, route each of the modules - for(var module in calipso.modules) { - routeModule(req, res, module, false, false, next); - } - -} - -/** - * Attach module event emitters and request event listener - * to this request instance. - * This will only last for the context of a current request - */ -function attachRequestEvents(req, res, next) { - - // Create a request event listener for this request - req.event = new calipso.event.RequestEventListener(); - - // Attach all the modules to it - for(var module in calipso.modules) { - req.event.registerModule(req, res, module); - } - -} - -/** - * Route a specific module - * Called by both the eventRouteModules but also by when dependencies trigger - * a module to be routed - * - * req, res : request/resposne - * module : the module to route - * depends : has this route been triggered by an event based on dependencies being met - * last : final modules, after all others have routed - * - */ -function routeModule(req, res, moduleName, depends, last, next) { - - var module = calipso.modules[moduleName]; - - // If module is enabled and has no dependencies, or if we are explicitly triggering this via depends - // Ignore modules that are specified as post route only - if(module.enabled && (depends || !module.fn.depends) && (last || !module.fn.last)) { - - // Fire event to start - req.event.modules[moduleName].route_start(); - - // Route - module.fn.route(req, res, module, app, function(err, moduleName) { - - // Gracefully deal with errors - if(err) { - res.statusCode = 500; - calipso.error(err.message); - res.errorMessage = "Module " + moduleName + ", error: " + err.message + err.stack; - } - - // Finish event - req.event.modules[moduleName].route_finish(); - - // Check to see if we have completed routing all modules - if(!last) { - checkAllModulesRouted(req, res, next); - } - - }); - - } - -} - -/** - * Check that all enabled modules have been initialised - * Don't check disabled modules or modules that are setup for postRoute only - */ -function checkAllModulesRouted(req, res, next) { - - var allRouted = true; - - for(var module in req.event.modules) { - var moduleRouted = (req.event.modules[module].routed || (calipso.modules[module].enabled && calipso.modules[module].fn.last) || !calipso.modules[module].enabled); - allRouted = moduleRouted && allRouted; - } - - if(allRouted && !req.event.routeComplete) { - req.event.routeComplete = true; - doLastModules(req, res, function() { - req.timeFinish = new Date(); - req.timeDuration = req.timeFinish - req.timeStart; - calipso.silly("All modules routed in " + req.timeDuration + " ms"); - doResponse(req, res, next); - }); - } - -} - -/** - * RUn any modules that are defined as last routing modules - * via last: true, dependencies are ignored for these atm. - */ -function doLastModules(req, res, next) { - - // Get all the postRoute modules - var lastModules = []; - for(var moduleName in calipso.modules) { - if(calipso.modules[moduleName].enabled && calipso.modules[moduleName].fn.last) { - lastModules.push(calipso.modules[moduleName]); - } - } - - // Execute their routing functions - calipso.lib.step( - function doLastModules() { - var group = this.group(); - lastModules.forEach(function(module) { - module.fn.route(req, res, module, app, group()); - }); - }, - function done(err) { - - // Gracefully deal with errors - if(err) { - res.statusCode = 500; - console.log(err.message); - res.errorMessage = err.message + err.stack; - } - - next(); - - } - ); - -} - -/** - * Standard response to all modules completing their routing - */ -function doResponse(req, res, next) { - - // If we are in install mode, and are not in the installation process, then redirect - if(!calipso.config.get('installed') && !req.url.match(/^\/admin\/install/)) { - calipso.silly("Redirecting to admin/install ..."); - calipso.app.doingInstall = true; - res.redirect("/admin/install"); - return; - } - - // If nothing could be matched ... - if(!res.routeMatched) { - calipso.log("No Calipso module routes matched the current URL."); - res.statusCode = 404; - } - - // Render statuscodes dealt with by themeing engine - // TODO - this is not very clean - if(res.statusCode === 404 || res.statusCode === 500 || res.statusCode === 200) { - - calipso.theme.render(req, res, function(err, content) { - - if(err) { - - // Something went wrong at the layout, cannot use layout to render. - res.statusCode = 500; - res.end("
" + (err.xMessage ? err.xMessage : err.message) + "
" + - "" + err.stack + ""); - - } else { - - res.setHeader('Content-Type', 'text/html'); - // Who am I? - res.setHeader('X-Powered-By','Calipso'); - // render - res.end(content, 'utf-8'); - - } - - }); - - } else { - - // Otherwise just let the default express router deal with it - - } - -} - /** * Called both via a hook.io event as * well as via the server that initiated it. @@ -429,480 +206,17 @@ function reloadConfig(event, data, next) { // If called via event emitter rather than hook if(typeof next === "function") next(err); } - initialiseCalipso(true); - return; - -} - -/** - * Initialise the modules currently enabled. - * This iterates through the modules loaded by loadModules (it places them in an array in the calipso object), - * and calls the 'init' function exposed by each module (in parallel controlled via step). - */ -function initModules() { - - // Reset - calipso.initComplete = false; - - // Create a list of all our enabled modules - var enabledModules = []; - for(var module in calipso.modules) { - if(calipso.modules[module].enabled) { - enabledModules.push(module); - } - } - - // Initialise them all - enabledModules.forEach(function(module) { - initModule(module, false); - }); - -} - -/** - * Init a specific module, called by event listeners re. dependent modules - */ -function initModule(module, depends) { - - - // If the module has no dependencies, kick start it - if(depends || !calipso.modules[module].fn.depends) { - - // Init start event - calipso.modules[module].event.init_start(); - - // Next run any init functions - calipso.modules[module].fn.init(calipso.modules[module], calipso.app, function(err) { - - // Init finish event - calipso.modules[module].inited = true; - calipso.modules[module].event.init_finish(); - - // Now, load any routes to go along with it - if(calipso.modules[module].fn.routes && calipso.modules[module].fn.routes.length > 0) { - calipso.lib.async.map(calipso.modules[module].fn.routes, function(options, next) { calipso.modules[module].router.addRoute(options, next) }, function(err, data) { - if(err) calipso.error(err); - checkAllModulesInited(); - }); - } else { - checkAllModulesInited(); - } - - }); - - } - -} - -/** - * Check that all enabled modules have been initialised - * If they have been initialised, then call the callback supplied on initialisation - */ -function checkAllModulesInited() { - - var allLoaded = true; - for(var module in calipso.modules) { - allLoaded = (calipso.modules[module].inited || !calipso.modules[module].enabled) && allLoaded; - } - - if(allLoaded && !calipso.initComplete) { - calipso.initComplete = true; - calipso.initCallback(); - } - -} - -/** - * Load the modules from the file system, into a 'modules' array - * that can be managed and iterated. - * - * The first level folder is the module type (e.g. core, contrib, ui). - * It doesn't actually change the processing, but that folder structure is - * now stored as a property of the module (so makes admin easier). - * - * It will take in an options object that holds the configuration parameters - * for the modules (e.g. if they are enabled or not). - * If they are switching (e.g. enabled > disabled) it will run the disable hook. - * - */ -function loadModules() { - - var configuredModules = calipso.config.get('modules') || {}; - - // Run any disable hooks - for(var module in calipso.modules) { - // Check to see if the module is currently enabled, if we are disabling it. - if (calipso.modules[module].enabled && configuredModules[module].enabled === false && typeof calipso.modules[module].fn.disable === 'function') { - calipso.modules[module].fn.disable(); - } - } - - // Clear the modules object (not sure if this is required, but was getting strange errors initially) - delete calipso.modules; // 'Delete' it. - calipso.modules = {}; // Always reset it - - // Read the modules in from the file system, sync is fine as we do it once on load. - calipso.lib.fs.readdirSync(__dirname + '/../modules').forEach(function(type){ - - if(type != "README" && type != '.DS_Store') { // Ignore the readme file and .DS_Store file for Macs - - calipso.lib.fs.readdirSync(__dirname + '/../modules/' + type).forEach(function(moduleFolderName){ - - if(moduleFolderName != "README" && moduleFolderName != '.DS_Store') { // Ignore the readme file and .DS_Store file for Macs - - // Create the basic module - var modulePath = 'modules/' + type + '/' + moduleFolderName; - var module = { - name: moduleFolderName, - folder: moduleFolderName, - library: moduleFolderName, - type: type, - path: modulePath, - enabled: false, - inited: false - }; - - // Add about info to it - loadAbout(module, modulePath, 'package.json'); - - // Set the module name to what is in the package.json, default to folder name - module.name = module.about.name ? module.about.name : moduleFoldername; - - // Now set the module - calipso.modules[module.name] = module; - - // Set if it is enabled or not - module.enabled = configuredModules[module.name] ? configuredModules[module.name].enabled : false; - - if(module.enabled) { - - // Load the module itself via require - requireModule(calipso.modules[module.name], modulePath); - - // Load the templates (factored out so it can be recalled by watcher) - loadModuleTemplates(calipso.modules[module.name], modulePath + '/templates'); - - } - - } - - }); - } - - }); - - // Now that all are loaded, attach events & depends - attachModuleEventsAndDependencies(); - -} - -/** - * Load data from package.json or theme.json - */ -function loadAbout(obj, fromPath, file) { - - var fs = calipso.lib.fs, path = require('path'); - var packageFile = calipso.lib.path.join(fromPath,file); - - if(path.existsSync(packageFile)) { - var json = fs.readFileSync(packageFile); - try { - obj.about = JSON.parse(json.toString()); - if(obj.about && obj.about.name) { - obj.library = obj.about.name - } else { - obj.library = obj.name - } - } catch(ex) { - obj.about = {description:'Invalid ' + file}; - } - }; - -} - -/** - * Connect up events and dependencies - * Must come after all modules are loaded - */ -function attachModuleEventsAndDependencies() { - - for(var module in calipso.modules) { - - // Register dependencies - registerModuleDependencies(calipso.modules[module]); - - // Attach event listener - calipso.event.addModuleEventListener(calipso.modules[module]); - - } - - // Sweep through the dependency tree and make sure any broken dependencies are disabled - disableBrokenDependencies(); - -} - -/** - * Ensure dependencies are mapped and registered against parent and child - */ -function registerModuleDependencies(module) { - - if(module.fn && module.fn.depends && module.enabled) { - - // Create object to hold dependent status - module.check = {}; - - // Register depends on parent - module.fn.depends.forEach(function(dependentModule) { - - module.check[dependentModule] = false; - - if(calipso.modules[dependentModule] && calipso.modules[dependentModule].enabled) { - - // Create a notification array to allow this module to notify modules that depend on it - calipso.modules[dependentModule].notify = calipso.modules[dependentModule].notify || []; - calipso.modules[dependentModule].notify.push(module.name); - - } else { - - calipso.modules[module.name].error = "Module " + module.name + " depends on " + dependentModule + ", but it does not exist or is disabled - this module will not load."; - calipso.error(calipso.modules[module.name].error); - calipso.modules[module.name].enabled = false; - - } - - }); - - } - -} - - -/** - * Disable everythign in a broken dependency tree - */ -function disableBrokenDependencies() { - - var disabled = 0; - for(var moduleName in calipso.modules) { - var module = calipso.modules[moduleName]; - if(module.enabled && module.fn && module.fn.depends) { - module.fn.depends.forEach(function(dependentModule) { - if(!calipso.modules[dependentModule].enabled) { - calipso.modules[module.name].error = "Module " + module.name + " depends on " + dependentModule + ", but it does not exist or is disabled - this module will not load."; - calipso.error(calipso.modules[module.name].error); - calipso.modules[module.name].enabled = false; - disabled = disabled + 1; - } - }); - } - } - - // Recursive - if(disabled > 0) - disableBrokenDependencies(); - -} - -/** - * Notify dependencies for initialisation - */ -function notifyDependenciesOfInit(moduleName,options) { - - var module = calipso.modules[moduleName]; - if(module.notify) { - module.notify.forEach(function(notifyModuleName) { - notifyDependencyOfInit(moduleName,notifyModuleName,options); - }); - } - -} - - -/** - * Notify dependencies for routing - */ -function notifyDependenciesOfRoute(req,res,moduleName,reqModules) { - - var module = calipso.modules[moduleName]; - if(module.notify) { - module.notify.forEach(function(notifyModuleName) { - notifyDependencyOfRoute(req, res, moduleName, notifyModuleName); - }); - } - -} - -/** - * Notify dependency - * moduleName - module that has init'd - * notifyModuleName - module to tell - */ -function notifyDependencyOfInit(moduleName,notifyModuleName,options) { - - // Set it to true - var module = calipso.modules[notifyModuleName]; - module.check[moduleName] = true; - checkInit(module); - -} - - - -/** - * Notify dependency - * req - request - * res - response - * moduleName - module that has init'd - * notifyModuleName - module to tell - */ -function notifyDependencyOfRoute(req,res,moduleName,notifyModuleName) { - - var module = req.event.modules[notifyModuleName]; - module.check[moduleName] = true; - checkRouted(req,res,moduleName,notifyModuleName); - -} - -/** - * Check if all dependencies are met and we should init the module - */ -function checkInit(module,next) { - - var doInit = true; - for(var check in module.check) { - doInit = doInit & module.check[check]; - } - if(doInit) { - // Initiate the module, no req for callback - initModule(module.name,true,function() {}); - } - -} - -/** - * Check if all dependencies are met and we should route the module - */ -function checkRouted(req,res,moduleName,notifyModuleName) { - - var doRoute = true; - - for(var check in req.event.modules[notifyModuleName].check) { - // console.log("CHK" + moduleName + " " + notifyModuleName + " " + check + " " + reqModules[notifyModuleName].check[check]) - doRoute = doRoute && req.event.modules[notifyModuleName].check[check]; - } - - if(doRoute) { - // Initiate the module, no req for callback - // initModule(module.name,true,function() {}); - routeModule(req,res,notifyModuleName,true,false,function() {}); - } - -} - -/** - * Load the module itself, refactored out to enable watch / reload - * Note, while it was refactored out, you can't currently reload - * a module, will patch in node-supervisor to watch the js files and restart - * the whole server (only option :()) - */ -function requireModule(module, modulePath, reload) { - - var fs = calipso.lib.fs; - var moduleFile = path.join(rootpath, modulePath + '/' + module.name); - - try { - - // Require the module - module.fn = require(moduleFile); - - // Attach a router - legacy check for default routes - module.router = new require('./Router').Router(module.name, modulePath); - - // Load the routes if specified as either array or function - if(typeof module.fn.routes === "function") module.fn.routes = module.fn.routes(); - module.fn.routes = module.fn.routes || []; - - } catch(ex) { - - calipso.error("Module " + module.name + " has been disabled because " + ex.message); - calipso.modules[module.name].enabled = false; - - } - -} - -/** - * Pre load all the templates in a module, synch, but only happens on app start up and config reload - * This is attached to the templates attribute so used later. - * - * @param calipso - * @param moduleTemplatePath - * @returns template object - */ -function loadModuleTemplates(module, moduleTemplatePath) { - - var templates = {}; - - // Default the template to any loaded in the theme (overrides) - var fs = calipso.lib.fs; - - if(!calipso.lib.path.existsSync(moduleTemplatePath)) { - return null; - } - - fs.readdirSync(moduleTemplatePath).forEach(function(name){ - - // Template paths and functions - var templatePath = moduleTemplatePath + "/" + name; - var templateExtension = templatePath.match(/([^\.]+)$/)[0]; - var template = fs.readFileSync(templatePath, 'utf8'); - var templateName = name.replace(/\.([^\.]+)$/,''); - - // Load the template - only if not already loaded by theme (e.g. overriden) - var hasTemplate = calipso.utils.hasProperty('theme.cache.modules.' + module.name + '.templates.' + templateName, calipso); - - if (hasTemplate) { - - // Use the theme version - templates[templateName] = calipso.theme.cache.modules[module.name].templates[templateName]; - - } else { - - // Else load it - if(template) { - // calipso.theme.compileTemplate => ./Theme.js - templates[templateName] = calipso.theme.compileTemplate(template,templatePath,templateExtension); - - // Watch / unwatch files - always unwatch (e.g. around config changes) - if(calipso.config.get('performance:watchFiles')) { - - fs.unwatchFile(templatePath); // Always unwatch first due to recursive behaviour - fs.watchFile(templatePath, {persistent: true, interval: 200}, function(curr,prev) { - loadModuleTemplates(module, moduleTemplatePath); - - calipso.silly("Module " + module.name + " template " + name + " reloaded."); - - }); - - } - - } - } - }); - - module.templates = templates; + return initialiseCalipso(true); } - /** * Load the available themes into the calipso.themes object */ - function loadThemes(next) { // Load the available themes - calipso.themes = calipso.themes || {}; + calipso.availableThemes = calipso.availableThemes || {}; var themeBasePath = calipso.config.get('server:themePath'); @@ -928,12 +242,12 @@ function loadThemes(next) { if(theme != "README" && theme != '.DS_Store') var themePath = calipso.lib.path.join(calipso.app.path,themeBasePath,folder,theme); // Create the theme object - calipso.themes[theme] = { + calipso.availableThemes[theme] = { name: theme, path: themePath }; // Load the about info from package.json - loadAbout(calipso.themes[theme], themePath, 'theme.json'); + calipso.module.loadAbout(calipso.availableThemes[theme], themePath, 'theme.json'); }); } } @@ -952,13 +266,14 @@ function configureTheme(next, overrideTheme) { var defaultTheme = calipso.config.get("theme:default"); var themeName = overrideTheme ? overrideTheme : calipso.config.get('theme:front'); - var theme = calipso.themes[themeName]; // Reference to theme.json - var Theme = require('./Theme'); + var themeConfig = calipso.availableThemes[themeName]; // Reference to theme.json - if(theme) { + if(themeConfig) { - Theme.Theme(theme, function(err, loadedTheme) { + // Themes is the library + calipso.themes.Theme(themeConfig, function(err, loadedTheme) { + // Current theme is always in calipso.theme calipso.theme = loadedTheme; if(err) { @@ -967,7 +282,7 @@ function configureTheme(next, overrideTheme) { if (!calipso.theme) { - if(theme.name === defaultTheme) { + if(loadedTheme.name === defaultTheme) { calipso.error('There has been a failure loading the default theme, calipso cannot start until this is fixed, terminating.'); process.exit(); return; @@ -985,13 +300,13 @@ function configureTheme(next, overrideTheme) { if(middleware.handle.tag === 'theme.stylus') { foundMiddleware = true; - mw = calipso.app.mwHelpers.stylusMiddleware(theme.path); + mw = calipso.app.mwHelpers.stylusMiddleware(themeConfig.path); calipso.app.stack[key].handle = mw; } if(middleware.handle.tag === 'theme.static') { foundMiddleware = true; - mw = calipso.app.mwHelpers.staticMiddleware(theme.path); + mw = calipso.app.mwHelpers.staticMiddleware(themeConfig.path); calipso.app.stack[key].handle = mw; } @@ -1017,86 +332,3 @@ function configureTheme(next, overrideTheme) { } -/** - * Check that the mongodb instance specified in the configuration is valid. - */ -function mongoConnect(dbUri, checkInstalling, next) { - - // Test the mongodb configuration - var isInstalled = calipso.config.get('installed'); - - // If first option is callback, ste dbUri to config value - if(typeof dbUri === "function") { - next = dbUri; - dbUri = calipso.config.get('database:uri'); - checkInstalling = false; - } - - // Check we are installing ... - if(checkInstalling) { - var db = mongoose.createConnection(dbUri, function(err) { - next(err, false); - }); - return; - } - - if(isInstalled) { - - // Always disconnect first just in case any left overs from installation - mongoose.disconnect(function() { - - // TODO - what the hell is going on with mongoose? - calipso.db = mongoose.createConnection(dbUri, function(err) { - - if (err) { - - // Add additional detail to know errors - switch (err.code) { - case "ECONNREFUSED": - calipso.error("Unable to connect to the specified database: ".red + dbUri); - break; - default: - calipso.error("Fatal unknown error: ".magenta + err); - } - - mongoose.disconnect(function() { - next(err); - }); - - } - - }); - - calipso.silly("Database connection to " + dbUri + " was successful."); - - // Replace the inmemory session with mongodb backed one - var foundMiddleware = false, mw; - calipso.app.stack.forEach(function(middleware,key) { - if(middleware.handle.tag === 'session') { - foundMiddleware = true; - mw = calipso.lib.express.session({ secret: calipso.config.get('session:secret') , store: mongoStore({ url: calipso.config.get('database:uri') }) }); - mw.tag = 'session' - calipso.app.stack[key].handle = mw; - } - }); - - if(!foundMiddleware) { - return next(new Error("Unable to load the MongoDB backed session, please check your session and db configuration"),false); - } - - return next(null, true); - - }); - - } else { - - calipso.silly("Database connection not attempted to " + dbUri + " as in installation mode.") - - // Create a dummy connection to enable models to be defined - calipso.db = mongoose.createConnection(''); - - next(null,false); - - } - -} diff --git a/lib/Blocks.js b/lib/core/Blocks.js similarity index 96% rename from lib/Blocks.js rename to lib/core/Blocks.js index 17507e2b9..c25bb9107 100644 --- a/lib/Blocks.js +++ b/lib/core/Blocks.js @@ -35,7 +35,7 @@ function RenderedBlocks(cache) { RenderedBlocks.prototype.set = function (block, content, layout, params, next) { var calipso = require(path.join(rootpath, 'lib/calipso')); - var cacheKey = calipso.cache.getCacheKey(['block', block], params); + var cacheKey = calipso.cacheService.getCacheKey(['block', block], params); this.content[block] = this.content[block] || []; this.content[block].push(content); diff --git a/lib/Cache.js b/lib/core/Cache.js similarity index 100% rename from lib/Cache.js rename to lib/core/Cache.js diff --git a/lib/Config.js b/lib/core/Config.js similarity index 70% rename from lib/Config.js rename to lib/core/Config.js index c74bbb732..91968335b 100644 --- a/lib/Config.js +++ b/lib/core/Config.js @@ -5,6 +5,7 @@ var rootpath = process.cwd() + '/', path = require('path'), step = require('step'), + _ = require('underscore'), fs = require('fs'); /** @@ -24,6 +25,9 @@ function Config(options) { // Default to file based on environment this.options = options && options.options ? options.options : { file: this.file }; + // Track if changes have been made to config + this.dirty = false; + } Config.prototype.init = function() { @@ -67,7 +71,6 @@ Config.prototype.load = function(next) { // Initialise nconf try { this.nconf.use(this.type, this.options,function(err,test) { - console.dir("HERE"); }); } catch(ex) { next(new Error("Unable to initialise configuration engine, reason: " + ex.message)); @@ -91,17 +94,55 @@ Config.prototype.get = function(key) { return this.nconf.get(key); } +/** + * Get config for module - wrapper + */ +Config.prototype.getModuleConfig = function(moduleName, key) { + var moduleKey = 'modules:'+ moduleName + ':config' + (key ? ':' + key : ''); + return this.nconf.get(moduleKey); +} + + /** * Set config */ Config.prototype.set = function(key,value) { + this.dirty = true; this.nconf.set(key,value); } +/** + * Set config for module - wrapper + */ +Config.prototype.setModuleConfig = function(moduleName, key, value) { + var moduleKey = 'modules:'+ moduleName + ':config' + (key ? ':' + key : ''); + this.dirty = true; + this.nconf.set(moduleKey, value); +} + +/** + * Set default config for module - wrapper + */ +Config.prototype.setDefaultModuleConfig = function(moduleName, config) { + + var moduleKey = 'modules:'+ moduleName + ':config'; + this.dirty = true; + + // Extract the defaults from the config + var defaultConfig = _.reduce(_.keys(config), function(memo, key) { + memo[key] = config[key].default; + return memo; + }, {}) + + this.nconf.set(moduleKey, defaultConfig); + +} + /** * Save config */ Config.prototype.save = function(next) { + this.dirty = false; this.nconf.save(next); } @@ -111,10 +152,11 @@ Config.prototype.save = function(next) { Config.prototype.setSave = function(key,value,next) { this.set(key,value); + this.dirty = false; this.save(next); } /** * Export the config object */ -exports.Config = Config; +module.exports = Config; diff --git a/lib/Date.js b/lib/core/Date.js similarity index 99% rename from lib/Date.js rename to lib/core/Date.js index 252b764ba..e8e18010a 100644 --- a/lib/Date.js +++ b/lib/core/Date.js @@ -313,8 +313,7 @@ CalipsoDate.prototype.formatDate = function (format, date, settings) { return output; } - /** * Export an instance of our date object */ -exports.CalipsoDate = new CalipsoDate(); \ No newline at end of file +module.exports = new CalipsoDate(); \ No newline at end of file diff --git a/lib/Event.js b/lib/core/Event.js similarity index 97% rename from lib/Event.js rename to lib/core/Event.js index 642024787..f5fbf7d56 100644 --- a/lib/Event.js +++ b/lib/core/Event.js @@ -41,9 +41,6 @@ function CalipsoEventEmitter(options) { var self = this; - // Re-require calipso - calipso = require(path.join(rootpath, 'lib/calipso')); - // Initialise options this.options = options || {}; @@ -218,8 +215,6 @@ function ModuleInitEventEmitter(moduleName) { events.EventEmitter.call(this); - // Refresh the require - calipso = require(path.join(rootpath, 'lib/calipso')); var self = this; this.moduleName = moduleName; @@ -285,7 +280,7 @@ function addModuleEventListener(module) { moduleEventEmitter.once(exports.INIT_FINISH, function(moduleName, options) { // Check for dependent modules, init them - calipso.notifyDependenciesOfInit(moduleName, options); + calipso.module.notifyDependenciesOfInit(moduleName, options); }); } @@ -317,7 +312,7 @@ function RequestEventListener() { // self.res = res; // Response var notifyDependencies = function(moduleName) { - calipso.notifyDependenciesOfRoute(req, res, moduleName, self.modules); + calipso.module.notifyDependenciesOfRoute(req, res, moduleName, self.modules); } // Register depends on parent diff --git a/lib/Form.js b/lib/core/Form.js similarity index 99% rename from lib/Form.js rename to lib/core/Form.js index 6688f6237..a4361f0db 100644 --- a/lib/Form.js +++ b/lib/core/Form.js @@ -694,9 +694,6 @@ me.render = function(formJson, values, req, next) { var self = this; - // Refresh the reference, as it is initially loaded at startup - calipso = require('./calipso'); - // Store local reference to the request for use during translation t = req.t; @@ -1164,4 +1161,4 @@ function replaceDates(output) { /** * Export an instance of our form object */ -exports.CalipsoForm = f; \ No newline at end of file +module.exports = f; \ No newline at end of file diff --git a/lib/Helpers.js b/lib/core/Helpers.js similarity index 100% rename from lib/Helpers.js rename to lib/core/Helpers.js diff --git a/lib/core/Lib.js b/lib/core/Lib.js new file mode 100644 index 000000000..6cf6b16fe --- /dev/null +++ b/lib/core/Lib.js @@ -0,0 +1,20 @@ + + +var rootpath = process.cwd() + '/'; + +module.exports = { + fs: require('fs'), + path: require('path'), + express: require('express'), + step: require('step'), + util: require('util'), + mongoose: require('mongoose'), + url: require('url'), + ejs: require('ejs'), + pager: require(rootpath + 'utils/pager'), + prettyDate: require(rootpath + 'utils/prettyDate.js'), + crypto: require(rootpath + 'utils/crypto.js'), + connect: require('connect'), + _:require('underscore'), + async: require('async') +}; \ No newline at end of file diff --git a/lib/Link.js b/lib/core/Link.js similarity index 92% rename from lib/Link.js rename to lib/core/Link.js index bf4946b9a..283ec869e 100644 --- a/lib/Link.js +++ b/lib/core/Link.js @@ -30,7 +30,7 @@ function CalipsoLink() { /** * Export an instance of our link object */ -exports.CalipsoLink = new CalipsoLink(); +module.exports = new CalipsoLink(); /** @@ -50,9 +50,6 @@ exports.CalipsoLink = new CalipsoLink(); */ CalipsoLink.prototype.render = function(item) { - // Refresh the reference, as it is initially loaded at startup - calipso = require('./calipso'); - return ( this.render_link(item) ); diff --git a/lib/Logging.js b/lib/core/Logging.js similarity index 100% rename from lib/Logging.js rename to lib/core/Logging.js diff --git a/lib/Menu.js b/lib/core/Menu.js similarity index 97% rename from lib/Menu.js rename to lib/core/Menu.js index 0751070c7..0cfa4ab11 100644 --- a/lib/Menu.js +++ b/lib/core/Menu.js @@ -16,9 +16,7 @@ var rootpath = process.cwd() + '/', path = require('path'), utils = require('connect').utils, merge = utils.merge, - calipso = require(path.join(rootpath, 'lib/calipso')), - PermissionFilter = require(path.join(rootpath, 'lib/Permission')).PermissionFilter; - + calipso = require(path.join(rootpath, 'lib/calipso')); /** * Exports */ @@ -76,7 +74,7 @@ CalipsoMenu.prototype.addMenuItem = function(req, options) { // Check security if(options.permit) { - var permitFn = new PermissionFilter(options.path, options.permit), + var permitFn = new calipso.permission.Filter(options.path, options.permit), permit = permitFn.check(req); if(typeof permit !== "object") return; diff --git a/lib/core/Module.js b/lib/core/Module.js new file mode 100644 index 000000000..2ea4903dc --- /dev/null +++ b/lib/core/Module.js @@ -0,0 +1,707 @@ + +var app, + rootpath = process.cwd() + '/', + path = require('path'), + calipso = require(path.join(rootpath, 'lib/calipso')), + mongoStore = require(path.join(rootpath, 'support/connect-mongodb')), + mongoose = require('mongoose'); + + + + +/** + * Route all of the modules based on the module event model + * This replaces an earlier version that only executed modules in + * parallel via step + */ +function eventRouteModules(req, res, next) { + + // Ignore static content + // TODO : Make this more connect friendly or at least configurable + if(req.url.match(/^\/images|^\/js|^\/css|^\/favicon.ico|png$|jpg$|gif$|css$|js$/)) { + return next(); + } + + req.timeStart = new Date(); + + // Attach our event listener to this request + attachRequestEvents(req, res, next); + + // Initialise the response re. matches + // Later used to decide to show 404 + res.routeMatched = false; + + // Now, route each of the modules + for(var module in calipso.modules) { + routeModule(req, res, module, false, false, next); + } + +} + +/** + * Attach module event emitters and request event listener + * to this request instance. + * This will only last for the context of a current request + */ +function attachRequestEvents(req, res, next) { + + // Create a request event listener for this request + req.event = new calipso.event.RequestEventListener(); + + // Attach all the modules to it + for(var module in calipso.modules) { + req.event.registerModule(req, res, module); + } + +} + +/** + * Route a specific module + * Called by both the eventRouteModules but also by when dependencies trigger + * a module to be routed + * + * req, res : request/resposne + * module : the module to route + * depends : has this route been triggered by an event based on dependencies being met + * last : final modules, after all others have routed + * + */ +function routeModule(req, res, moduleName, depends, last, next) { + + var module = calipso.modules[moduleName]; + + // If module is enabled and has no dependencies, or if we are explicitly triggering this via depends + // Ignore modules that are specified as post route only + if(module.enabled && (depends || !module.fn.depends) && (last || !module.fn.last)) { + + // Fire event to start + req.event.modules[moduleName].route_start(); + + // Route + module.fn.route(req, res, module, calipso.app, function(err, moduleName) { + + // Gracefully deal with errors + if(err) { + res.statusCode = 500; + calipso.error(err.message); + res.errorMessage = "Module " + moduleName + ", error: " + err.message + err.stack; + } + + // Expose configuration if module has it + if(calipso.modules[moduleName].fn.config) { + var modulePermit = calipso.permission.Helper.hasPermission("admin:module:configuration"); + res.menu.admin.addMenuItem(req, {name:moduleName,path:'admin/modules/' + moduleName,url:'/admin/modules?module=' + moduleName,description:'Manage ' + moduleName + ' settings ...',permit:modulePermit}); + } + + // Finish event + req.event.modules[moduleName].route_finish(); + + // Check to see if we have completed routing all modules + if(!last) { + checkAllModulesRouted(req, res, next); + } + + }); + + } + +} + +/** + * Check that all enabled modules have been initialised + * Don't check disabled modules or modules that are setup for postRoute only + */ +function checkAllModulesRouted(req, res, next) { + + var allRouted = true; + + for(var module in req.event.modules) { + var moduleRouted = (req.event.modules[module].routed || (calipso.modules[module].enabled && calipso.modules[module].fn.last) || !calipso.modules[module].enabled); + allRouted = moduleRouted && allRouted; + } + + if(allRouted && !req.event.routeComplete) { + req.event.routeComplete = true; + doLastModules(req, res, function() { + req.timeFinish = new Date(); + req.timeDuration = req.timeFinish - req.timeStart; + calipso.silly("All modules routed in " + req.timeDuration + " ms"); + doResponse(req, res, next); + }); + } + +} + +/** + * RUn any modules that are defined as last routing modules + * via last: true, dependencies are ignored for these atm. + */ +function doLastModules(req, res, next) { + + // Get all the postRoute modules + var lastModules = []; + for(var moduleName in calipso.modules) { + if(calipso.modules[moduleName].enabled && calipso.modules[moduleName].fn.last) { + lastModules.push(calipso.modules[moduleName]); + } + } + + // Execute their routing functions + calipso.lib.step( + function doLastModules() { + var group = this.group(); + lastModules.forEach(function(module) { + module.fn.route(req, res, module, calipso.app, group()); + }); + }, + function done(err) { + + // Gracefully deal with errors + if(err) { + res.statusCode = 500; + console.log(err.message); + res.errorMessage = err.message + err.stack; + } + + next(); + + } + ); + +} + +/** + * Standard response to all modules completing their routing + */ +function doResponse(req, res, next) { + + // If we are in install mode, and are not in the installation process, then redirect + if(!calipso.config.get('installed') && !req.url.match(/^\/admin\/install/)) { + calipso.silly("Redirecting to admin/install ..."); + calipso.app.doingInstall = true; + res.redirect("/admin/install"); + return; + } + + // If nothing could be matched ... + if(!res.routeMatched) { + calipso.log("No Calipso module routes matched the current URL."); + res.statusCode = 404; + } + + // Render statuscodes dealt with by themeing engine + // TODO - this is not very clean + if(res.statusCode === 404 || res.statusCode === 500 || res.statusCode === 200) { + + calipso.theme.render(req, res, function(err, content) { + + if(err) { + + // Something went wrong at the layout, cannot use layout to render. + res.statusCode = 500; + res.end("
" + (err.xMessage ? err.xMessage : err.message) + "
" + + "" + err.stack + ""); + + } else { + + res.setHeader('Content-Type', 'text/html'); + // Who am I? + res.setHeader('X-Powered-By','Calipso'); + // render + res.end(content, 'utf-8'); + + } + + }); + + } else { + + // Otherwise just let the default express router deal with it + + } + +} + + +/** + * Initialise the modules currently enabled. + * This iterates through the modules loaded by loadModules (it places them in an array in the calipso object), + * and calls the 'init' function exposed by each module (in parallel controlled via step). + */ +function initModules() { + + // Reset + calipso.initComplete = false; + + // Create a list of all our enabled modules + var enabledModules = []; + for(var module in calipso.modules) { + if(calipso.modules[module].enabled) { + enabledModules.push(module); + } + } + + // Initialise them all + enabledModules.forEach(function(module) { + initModule(module, false); + }); + +} + +/** + * Init a specific module, called by event listeners re. dependent modules + */ +function initModule(module, depends) { + + + // If the module has no dependencies, kick start it + if(depends || !calipso.modules[module].fn.depends) { + + // Init start event + calipso.modules[module].event.init_start(); + + // Next run any init functions + calipso.modules[module].fn.init(calipso.modules[module], calipso.app, function(err) { + + // Init finish event + calipso.modules[module].inited = true; + calipso.modules[module].event.init_finish(); + + // Now, load any routes to go along with it + if(calipso.modules[module].fn.routes && calipso.modules[module].fn.routes.length > 0) { + calipso.lib.async.map(calipso.modules[module].fn.routes, function(options, next) { calipso.modules[module].router.addRoute(options, next) }, function(err, data) { + if(err) calipso.error(err); + checkAllModulesInited(); + }); + } else { + checkAllModulesInited(); + } + + }); + + } + +} + +/** + * Check that all enabled modules have been initialised + * If they have been initialised, then call the callback supplied on initialisation + */ +function checkAllModulesInited() { + + var allLoaded = true; + for(var module in calipso.modules) { + allLoaded = (calipso.modules[module].inited || !calipso.modules[module].enabled) && allLoaded; + } + + if(allLoaded && !calipso.initComplete) { + calipso.initComplete = true; + calipso.initCallback(); + } + +} + +/** + * Load the modules from the file system, into a 'modules' array + * that can be managed and iterated. + * + * The first level folder is the module type (e.g. core, contrib, ui). + * It doesn't actually change the processing, but that folder structure is + * now stored as a property of the module (so makes admin easier). + * + * It will take in an options object that holds the configuration parameters + * for the modules (e.g. if they are enabled or not). + * If they are switching (e.g. enabled > disabled) it will run the disable hook. + * + */ +function loadModules(next) { + + var configuredModules = calipso.config.get('modules') || {}; + + // Run any disable hooks + for(var module in calipso.modules) { + // Check to see if the module is currently enabled, if we are disabling it. + if (calipso.modules[module].enabled && configuredModules[module].enabled === false && typeof calipso.modules[module].fn.disable === 'function') { + calipso.modules[module].fn.disable(); + } + } + + // Clear the modules object (not sure if this is required, but was getting strange errors initially) + delete calipso.modules; // 'Delete' it. + calipso.modules = {}; // Always reset it + + // Read the modules in from the file system, sync is fine as we do it once on load. + calipso.lib.fs.readdirSync(rootpath + 'modules').forEach(function(type){ + + if(type != "README" && type != '.DS_Store') { // Ignore the readme file and .DS_Store file for Macs + + calipso.lib.fs.readdirSync(rootpath + 'modules/' + type).forEach(function(moduleFolderName){ + + if(moduleFolderName != "README" && moduleFolderName != '.DS_Store') { // Ignore the readme file and .DS_Store file for Macs + + // Create the basic module + var modulePath = 'modules/' + type + '/' + moduleFolderName; + var module = { + name: moduleFolderName, + folder: moduleFolderName, + library: moduleFolderName, + type: type, + path: modulePath, + enabled: false, + inited: false + }; + + // Add about info to it + loadAbout(module, modulePath, 'package.json'); + + // Set the module name to what is in the package.json, default to folder name + module.name = module.about.name ? module.about.name : moduleFoldername; + + // Now set the module + calipso.modules[module.name] = module; + + // Set if it is enabled or not + module.enabled = configuredModules[module.name] ? configuredModules[module.name].enabled : false; + + if(module.enabled) { + + // Load the module itself via require + requireModule(calipso.modules[module.name], modulePath); + + // Load the templates (factored out so it can be recalled by watcher) + loadModuleTemplates(calipso.modules[module.name], modulePath + '/templates'); + + } + + } + + }); + } + + }); + + // Now that all are loaded, attach events & depends + attachModuleEventsAndDependencies(); + + // Save configuration changes (if required) + if(calipso.config.dirty) { + calipso.config.save(next); + } else { + return next(); + } + +} + +/** + * Load data from package.json or theme.json + */ +function loadAbout(obj, fromPath, file) { + + var fs = calipso.lib.fs, path = require('path'); + var packageFile = calipso.lib.path.join(fromPath, file); + + if(path.existsSync(packageFile)) { + var json = fs.readFileSync(packageFile); + try { + obj.about = JSON.parse(json.toString()); + if(obj.about && obj.about.name) { + obj.library = obj.about.name + } else { + obj.library = obj.name + } + } catch(ex) { + obj.about = {description:'Invalid ' + file}; + } + }; + +} + +/** + * Connect up events and dependencies + * Must come after all modules are loaded + */ +function attachModuleEventsAndDependencies() { + + for(var module in calipso.modules) { + + // Register dependencies + registerModuleDependencies(calipso.modules[module]); + + // Attach event listener + calipso.event.addModuleEventListener(calipso.modules[module]); + + } + + // Sweep through the dependency tree and make sure any broken dependencies are disabled + disableBrokenDependencies(); + +} + +/** + * Ensure dependencies are mapped and registered against parent and child + */ +function registerModuleDependencies(module) { + + if(module.fn && module.fn.depends && module.enabled) { + + // Create object to hold dependent status + module.check = {}; + + // Register depends on parent + module.fn.depends.forEach(function(dependentModule) { + + module.check[dependentModule] = false; + + if(calipso.modules[dependentModule] && calipso.modules[dependentModule].enabled) { + + // Create a notification array to allow this module to notify modules that depend on it + calipso.modules[dependentModule].notify = calipso.modules[dependentModule].notify || []; + calipso.modules[dependentModule].notify.push(module.name); + + } else { + + calipso.modules[module.name].error = "Module " + module.name + " depends on " + dependentModule + ", but it does not exist or is disabled - this module will not load."; + calipso.error(calipso.modules[module.name].error); + calipso.modules[module.name].enabled = false; + + } + + }); + + } + +} + + +/** + * Disable everythign in a broken dependency tree + */ +function disableBrokenDependencies() { + + var disabled = 0; + for(var moduleName in calipso.modules) { + var module = calipso.modules[moduleName]; + if(module.enabled && module.fn && module.fn.depends) { + module.fn.depends.forEach(function(dependentModule) { + if(!calipso.modules[dependentModule].enabled) { + calipso.modules[module.name].error = "Module " + module.name + " depends on " + dependentModule + ", but it does not exist or is disabled - this module will not load."; + calipso.error(calipso.modules[module.name].error); + calipso.modules[module.name].enabled = false; + disabled = disabled + 1; + } + }); + } + } + + // Recursive + if(disabled > 0) + disableBrokenDependencies(); + +} + +/** + * Notify dependencies for initialisation + */ +function notifyDependenciesOfInit(moduleName,options) { + + var module = calipso.modules[moduleName]; + if(module.notify) { + module.notify.forEach(function(notifyModuleName) { + notifyDependencyOfInit(moduleName,notifyModuleName,options); + }); + } + +} + + +/** + * Notify dependencies for routing + */ +function notifyDependenciesOfRoute(req,res,moduleName,reqModules) { + + var module = calipso.modules[moduleName]; + if(module.notify) { + module.notify.forEach(function(notifyModuleName) { + notifyDependencyOfRoute(req, res, moduleName, notifyModuleName); + }); + } + +} + +/** + * Notify dependency + * moduleName - module that has init'd + * notifyModuleName - module to tell + */ +function notifyDependencyOfInit(moduleName,notifyModuleName,options) { + + // Set it to true + var module = calipso.modules[notifyModuleName]; + module.check[moduleName] = true; + checkInit(module); + +} + + + +/** + * Notify dependency + * req - request + * res - response + * moduleName - module that has init'd + * notifyModuleName - module to tell + */ +function notifyDependencyOfRoute(req,res,moduleName,notifyModuleName) { + + var module = req.event.modules[notifyModuleName]; + module.check[moduleName] = true; + checkRouted(req,res,moduleName,notifyModuleName); + +} + +/** + * Check if all dependencies are met and we should init the module + */ +function checkInit(module,next) { + + var doInit = true; + for(var check in module.check) { + doInit = doInit & module.check[check]; + } + if(doInit) { + // Initiate the module, no req for callback + initModule(module.name,true,function() {}); + } + +} + +/** + * Check if all dependencies are met and we should route the module + */ +function checkRouted(req, res, moduleName, notifyModuleName) { + + var doRoute = true; + + for(var check in req.event.modules[notifyModuleName].check) { + doRoute = doRoute && req.event.modules[notifyModuleName].check[check]; + } + + if(doRoute) { + // Initiate the module, no req for callback + // initModule(module.name,true,function() {}); + routeModule(req,res,notifyModuleName,true,false,function() {}); + } + +} + +/** + * Load the module itself, refactored out to enable watch / reload + * Note, while it was refactored out, you can't currently reload + * a module, will patch in node-supervisor to watch the js files and restart + * the whole server (only option :()) + */ +function requireModule(module, modulePath, reload, next) { + + var fs = calipso.lib.fs; + var moduleFile = path.join(rootpath, modulePath + '/' + module.name); + + try { + + // Require the module + module.fn = require(moduleFile); + + // Attach a router - legacy check for default routes + module.router = new calipso.router(module.name, modulePath); + + // Load the routes if specified as either array or function + if(typeof module.fn.routes === "function") module.fn.routes = module.fn.routes(); + module.fn.routes = module.fn.routes || []; + + // Ensure the defaultConfig exists (e.g. if it hasn't been required before) + // This is saved in the wider loadModules loop to ensure only one config save action (if required) + if(module.fn.config && !calipso.config.getModuleConfig(module.name,'')) { + calipso.config.setDefaultModuleConfig(module.name, module.fn.config); + } + + } catch(ex) { + + calipso.error("Module " + module.name + " has been disabled because " + ex.message); + calipso.modules[module.name].enabled = false; + + } + +} + +/** + * Pre load all the templates in a module, synch, but only happens on app start up and config reload + * This is attached to the templates attribute so used later. + * + * @param calipso + * @param moduleTemplatePath + * @returns template object + */ +function loadModuleTemplates(module, moduleTemplatePath) { + + var templates = {}; + + // Default the template to any loaded in the theme (overrides) + var fs = calipso.lib.fs; + + if(!calipso.lib.path.existsSync(moduleTemplatePath)) { + return null; + } + + fs.readdirSync(moduleTemplatePath).forEach(function(name){ + + // Template paths and functions + var templatePath = moduleTemplatePath + "/" + name; + var templateExtension = templatePath.match(/([^\.]+)$/)[0]; + var template = fs.readFileSync(templatePath, 'utf8'); + var templateName = name.replace(/\.([^\.]+)$/,''); + + // Load the template - only if not already loaded by theme (e.g. overriden) + var hasTemplate = calipso.utils.hasProperty('theme.cache.modules.' + module.name + '.templates.' + templateName, calipso); + + if (hasTemplate) { + + // Use the theme version + templates[templateName] = calipso.theme.cache.modules[module.name].templates[templateName]; + + } else { + + // Else load it + if(template) { + // calipso.theme.compileTemplate => ./Theme.js + templates[templateName] = calipso.theme.compileTemplate(template,templatePath,templateExtension); + + // Watch / unwatch files - always unwatch (e.g. around config changes) + if(calipso.config.get('performance:watchFiles')) { + + fs.unwatchFile(templatePath); // Always unwatch first due to recursive behaviour + fs.watchFile(templatePath, {persistent: true, interval: 200}, function(curr,prev) { + loadModuleTemplates(module, moduleTemplatePath); + calipso.silly("Module " + module.name + " template " + name + " reloaded."); + }); + + } + + } + } + }); + + module.templates = templates; + +} + +/** + * Exports + */ +module.exports = { + loadModules: loadModules, + initModules: initModules, + eventRouteModules: eventRouteModules, + notifyDependenciesOfInit: notifyDependenciesOfInit, + notifyDependenciesOfRoute: notifyDependenciesOfRoute, + loadAbout: loadAbout +} \ No newline at end of file diff --git a/lib/Permission.js b/lib/core/Permission.js similarity index 90% rename from lib/Permission.js rename to lib/core/Permission.js index d7cd79b07..9c2a9dc14 100644 --- a/lib/Permission.js +++ b/lib/core/Permission.js @@ -28,6 +28,7 @@ PermissionFilter.prototype.check = function(req) { var user = req.session.user; var isAdmin = req.session.user && req.session.user.isAdmin; + if(isAdmin) return {allow:true}; // Admins always access everything // Else check for a specific permission @@ -74,7 +75,7 @@ var PermissionHelpers = { if(isCrud) { calipso.lib._.map(["view","create","update","delete"], function(crudAction) { var crudPermission = permission + ":" + crudAction; - self.permissions[crudPermission] = {roles: [], queries:[], description: description}; + self.permissions[crudPermission] = {roles: [], queries:[], description: crudAction + " " + description}; self.sortedPermissions.push(crudPermission); }) } else { @@ -139,8 +140,9 @@ var PermissionHelpers = { hasRole: function(role) { // Curried filter return function(user) { - var isAllowed = user.roles.indexOf(role) >= 0 ? true : false; - return {allow: isAllowed , msg: 'You dont have the appropriate roles to view that page!'} + var isAllowed = user.roles.indexOf(role) >= 0 ? true : false, + message = isAllowed ? ('User has role ' + role) : 'You dont have the appropriate roles to view that page!'; + return {allow: isAllowed , msg: message} } }, @@ -158,9 +160,12 @@ var PermissionHelpers = { var userRoles = user.roles, permissionRoles = self.permissions[permission] ? self.permissions[permission].roles : []; // Check if allowed based on intersection of user roles and roles that have permission - var isAllowed = calipso.lib._.intersect(permissionRoles, userRoles).length > 0; + var isAllowed = calipso.lib._.intersect(permissionRoles, userRoles).length > 0, + message = isAllowed ? ('User has permission ' + permission) : 'You do not have any of the roles required to perform that action.'; + + + return {allow: isAllowed, msg: message}; - return {allow:isAllowed, msg:'You do not have any of the roles required to view this page or perform that action'}; } } @@ -170,5 +175,5 @@ var PermissionHelpers = { /** * Export an instance of our object */ -exports.PermissionFilter = PermissionFilter; -exports.PermissionHelpers = PermissionHelpers; \ No newline at end of file +exports.Filter = PermissionFilter; +exports.Helper = PermissionHelpers; \ No newline at end of file diff --git a/lib/Router.js b/lib/core/Router.js similarity index 90% rename from lib/Router.js rename to lib/core/Router.js index e4fe1aff3..67221607f 100644 --- a/lib/Router.js +++ b/lib/core/Router.js @@ -19,46 +19,8 @@ var rootpath = process.cwd() + '/', calipso = require(path.join(rootpath, 'lib/calipso')), url = require('url'), fs = require('fs'), - PermissionFilter = require(path.join(rootpath, 'lib/Permission')).PermissionFilter, - blocks = require(path.join(rootpath, 'lib/Blocks')); - -/** - * Normalize the given path string, - * returning a regular expression. - * - * An empty array should be passed, - * which will contain the placeholder - * key names. For example "/user/:id" will - * then contain ["id"]. - * - * BORROWED FROM Connect - * - * @param {String} path - * @param {Array} keys - * @return {RegExp} - * @api private - */ - -function normalizePath(path, keys) { - path = path - .concat('/?') - .replace(/\/\(/g, '(?:/') - .replace(/(\/)?(\.)?:(\w+)(?:(\(.*?\)))?(\?)?/g, function (_, slash, format, key, capture, optional) { - keys.push(key); - slash = slash || ''; - return '' - + (optional ? '' : slash) - + '(?:' - + (optional ? slash : '') - + (format || '') + (capture || '([^/]+?)') + ')' - + (optional || ''); - }) - .replace(/([\/.])/g, '\\$1') - .replace(/\*/g, '(.+)'); - - return new RegExp('^' + path + '$', 'i'); -} - + PermissionFilter = calipso.permission.Filter, + blocks = require('./Blocks'); /** * Core router object, use the return model to ensure @@ -179,28 +141,12 @@ var Router = function (moduleName, modulePath) { // TODO var isAdmin = req.session.user && req.session.user.isAdmin; - - /*if (route.admin && !isAdmin) { - req.flash('error', req.t('You need to be an administrative user to view that page.')); - res.statusCode = 401; - res.redirect("/"); - group()(); - return; - } - - // Check to see if it requires logged in user access - var isUser = req.session.user && req.session.user.username; - if (route.user && !isUser) { - req.flash('error', req.t('You need to be logged in to view that page.')); - res.statusCode = 401; - res.redirect("/"); - group()(); - return; - }*/ - + // Check to see if it requires logged in user access if (route.permit) { + var permit = route.permitFn.check(req); + if(typeof permit !== "object") permit = {allow: false, msg:'You don\'t have the appropriate permissions to view that page.'}; if(!permit.allow) { if(!allPages) { @@ -232,7 +178,7 @@ var Router = function (moduleName, modulePath) { // Set the object to hold the rendered blocks if it hasn't been created already if (!res.renderedBlocks) { - res.renderedBlocks = new blocks.RenderedBlocks(calipso.cache); + res.renderedBlocks = new blocks.RenderedBlocks(calipso.cacheService); } // Copy over any params that make sense from the url @@ -269,9 +215,9 @@ var Router = function (moduleName, modulePath) { if(route.block && cacheBlock && cacheEnabled) { - var cacheKey = calipso.cache.getCacheKey(['block',route.block], res.params); + var cacheKey = calipso.cacheService.getCacheKey(['block',route.block], res.params); - calipso.cache.check(cacheKey,function(err, isCached) { + calipso.cacheService.check(cacheKey,function(err, isCached) { if(isCached) { // Set the block from the cache, set layout if needed res.renderedBlocks.getCache(cacheKey, route.block, function(err,layout) { @@ -335,7 +281,45 @@ var Router = function (moduleName, modulePath) { }; + +/** + * Normalize the given path string, + * returning a regular expression. + * + * An empty array should be passed, + * which will contain the placeholder + * key names. For example "/user/:id" will + * then contain ["id"]. + * + * BORROWED FROM Connect + * + * @param {String} path + * @param {Array} keys + * @return {RegExp} + * @api private + */ + +function normalizePath(path, keys) { + path = path + .concat('/?') + .replace(/\/\(/g, '(?:/') + .replace(/(\/)?(\.)?:(\w+)(?:(\(.*?\)))?(\?)?/g, function (_, slash, format, key, capture, optional) { + keys.push(key); + slash = slash || ''; + return '' + + (optional ? '' : slash) + + '(?:' + + (optional ? slash : '') + + (format || '') + (capture || '([^/]+?)') + ')' + + (optional || ''); + }) + .replace(/([\/.])/g, '\\$1') + .replace(/\*/g, '(.+)'); + + return new RegExp('^' + path + '$', 'i'); +} + /** * Exports */ -module.exports.Router = Router; +module.exports = Router; diff --git a/lib/core/Storage.js b/lib/core/Storage.js new file mode 100644 index 000000000..489fc22c3 --- /dev/null +++ b/lib/core/Storage.js @@ -0,0 +1,105 @@ +/*! + * Calipso MongoDB Storage Library + * Copyright(c) 2011 Clifton Cunningham + * MIT Licensed + * + * This library provides a few simple functions that can be used to help manage MongoDB and Mongoose. + */ + +var rootpath = process.cwd(), + path = require('path'), + events = require('events'), + mongoStore = require(path.join(rootpath, 'support/connect-mongodb')), + mongoose = require('mongoose'), + calipso = require(path.join(rootpath, 'lib/calipso')); + +function Storage() { + // Store running map reduce functions + this.mr = {}; +} + +/** + * Check that the mongodb instance specified in the configuration is valid. + */ +Storage.prototype.mongoConnect = function(dbUri, checkInstalling, next) { + + // Test the mongodb configuration + var isInstalled = calipso.config.get('installed'); + + // If first option is callback, ste dbUri to config value + if(typeof dbUri === "function") { + next = dbUri; + dbUri = calipso.config.get('database:uri'); + checkInstalling = false; + } + + // Check we are installing ... + if(checkInstalling) { + var db = mongoose.createConnection(dbUri, function(err) { + next(err, false); + }); + return; + } + + if(isInstalled) { + + // Always disconnect first just in case any left overs from installation + mongoose.disconnect(function() { + + // TODO - what the hell is going on with mongoose? + calipso.db = mongoose.createConnection(dbUri, function(err) { + + if (err) { + + // Add additional detail to know errors + switch (err.code) { + case "ECONNREFUSED": + calipso.error("Unable to connect to the specified database: ".red + dbUri); + break; + default: + calipso.error("Fatal unknown error: ".magenta + err); + } + + mongoose.disconnect(function() { + next(err); + }); + + } + + }); + + calipso.silly("Database connection to " + dbUri + " was successful."); + + // Replace the inmemory session with mongodb backed one + var foundMiddleware = false, mw; + calipso.app.stack.forEach(function(middleware,key) { + if(middleware.handle.tag === 'session') { + foundMiddleware = true; + mw = calipso.lib.express.session({ secret: calipso.config.get('session:secret') , store: mongoStore({ url: calipso.config.get('database:uri') }) }); + mw.tag = 'session' + calipso.app.stack[key].handle = mw; + } + }); + + if(!foundMiddleware) { + return next(new Error("Unable to load the MongoDB backed session, please check your session and db configuration"),false); + } + + return next(null, true); + + }); + + } else { + + calipso.silly("Database connection not attempted to " + dbUri + " as in installation mode.") + + // Create a dummy connection to enable models to be defined + calipso.db = mongoose.createConnection(''); + + next(null,false); + + } + +} + +module.exports = new Storage(); \ No newline at end of file diff --git a/lib/Table.js b/lib/core/Table.js similarity index 97% rename from lib/Table.js rename to lib/core/Table.js index 03a402c08..64f4e065f 100644 --- a/lib/Table.js +++ b/lib/core/Table.js @@ -34,7 +34,7 @@ function CalipsoTable() { /** * Export an instance of our table object */ -exports.CalipsoTable = new CalipsoTable(); +module.exports = new CalipsoTable(); /** @@ -66,9 +66,6 @@ exports.CalipsoTable = new CalipsoTable(); */ CalipsoTable.prototype.render = function(item,req) { - // Refresh the reference, as it is initially loaded at startup - calipso = require('./calipso'); - // Store local reference to the request for use during translation t = req.t; diff --git a/lib/Theme.js b/lib/core/Themes.js similarity index 98% rename from lib/Theme.js rename to lib/core/Themes.js index 6a772e847..3d6cbbd4e 100644 --- a/lib/Theme.js +++ b/lib/core/Themes.js @@ -336,7 +336,7 @@ function processTheme(req, res, layout, theme, next) { if(!disable) { if(cache && cacheEnabled && !isAdmin) { var keys = [layout, 'section', currentSection]; - var cacheKey = calipso.cache.getCacheKey(keys, params); + var cacheKey = calipso.cacheService.getCacheKey(keys, params); sectionCache(req, res, cacheKey, section, sectionPath, layoutConfig, theme, localNext); } else { processSection(req, res, section, sectionPath, layoutConfig, theme, localNext); @@ -353,10 +353,10 @@ function processTheme(req, res, layout, theme, next) { */ function sectionCache(req, res, cacheKey, section, templateName, layoutConfig, theme, next) { - calipso.cache.check(cacheKey,function(err,isCached) { + calipso.cacheService.check(cacheKey,function(err,isCached) { if(isCached) { calipso.silly("Cache hit for " + cacheKey + ", section " + section); - calipso.cache.get(cacheKey,function(err,cache) { + calipso.cacheService.get(cacheKey,function(err,cache) { if(!err) { res.bufferedOutput[section] = cache.content; } @@ -367,7 +367,7 @@ function sectionCache(req, res, cacheKey, section, templateName, layoutConfig, t processSection(req, res, section, templateName, layoutConfig, theme, function(err) { if(!err) { var content = res.bufferedOutput[section]; - calipso.cache.set(cacheKey,{content:content},null,next); + calipso.cacheService.set(cacheKey,{content:content},null,next); } else { next(err); } diff --git a/lib/Utils.js b/lib/core/Utils.js similarity index 98% rename from lib/Utils.js rename to lib/core/Utils.js index 1dd904f33..0a0d385d7 100644 --- a/lib/Utils.js +++ b/lib/core/Utils.js @@ -3,7 +3,7 @@ */ var _ = require('underscore'); -exports = module.exports = { +module.exports = { /** * Basically like getProperty, different return * @method hasProperty diff --git a/lib/cache/memory.js b/lib/core/cache/memory.js similarity index 100% rename from lib/cache/memory.js rename to lib/core/cache/memory.js diff --git a/lib/cache/store.js b/lib/core/cache/store.js similarity index 100% rename from lib/cache/store.js rename to lib/core/cache/store.js diff --git a/modules/core/admin/admin.js b/modules/core/admin/admin.js index c700ab8d9..f1cc53592 100644 --- a/modules/core/admin/admin.js +++ b/modules/core/admin/admin.js @@ -16,16 +16,18 @@ exports = module.exports = { function route(req, res, module, app, next) { // Config helpers - var corePermit = calipso.permissions.hasPermission("admin:core:configuration"), - cachePermit = calipso.permissions.hasPermission("admin:core:cache"); + var corePermit = calipso.permission.Helper.hasPermission("admin:core:configuration"), + modulePermit = calipso.permission.Helper.hasPermission("admin:module:configuration"), + cachePermit = calipso.permission.Helper.hasPermission("admin:core:cache"); // Menu items res.menu.admin.addMenuItem(req, {name:'Administration',path:'admin',url:'/admin',description:'Calipso administration ...',permit:corePermit}); - res.menu.admin.addMenuItem(req, {name:'Calipso Core',path:'admin/core',url:'/admin',description:'Manage core settings for Calipso ...',permit:corePermit}); - res.menu.admin.addMenuItem(req, {name:'Configuration Options',path:'admin/core/config',url:'/admin/core/config',description:'Core configuration ...',permit:corePermit}); + res.menu.admin.addMenuItem(req, {name:'Core',path:'admin/core',url:'/admin',description:'Manage core settings for Calipso ...',permit:corePermit}); + res.menu.admin.addMenuItem(req, {name:'Configuration',path:'admin/core/config',url:'/admin/core/config',description:'Core configuration ...',permit:corePermit}); res.menu.admin.addMenuItem(req, {name:'View Languages',path:'admin/core/languages',url:'/admin/core/languages',description:'Languages ...',permit:corePermit}); res.menu.admin.addMenuItem(req, {name:'View Cache',path:'admin/core/cache',url:'/admin/core/cache',description:'Cache ...',permit:cachePermit}); res.menu.admin.addMenuItem(req, {name:'Clear Cache',path:'admin/core/cache/clear',url:'/admin/core/cache/clear',description:'Clear Cache ...',permit:cachePermit}); + res.menu.admin.addMenuItem(req, {name:'Modules',path:'admin/modules',url:'/admin',description:'Manage module settings ...',permit:modulePermit}); // Routing and Route Handler module.router.route(req, res, next); @@ -39,13 +41,14 @@ function route(req, res, module, app, next) { function init(module, app, next) { // Initialise administration events - enabled for hook.io - calipso.e.addEvent('CONFIG_UPDATE',{enabled:true,hookio:true}); + calipso.e.addEvent('CONFIG_UPDATE',{enabled:true}); // Add listener to config_update calipso.e.post('CONFIG_UPDATE',module.name,calipso.reloadConfig); - calipso.permissions.addPermission("admin:core:configuration","Manage core configuration."); - calipso.permissions.addPermission("admin:core:cache","View and clear cache."); + calipso.permission.Helper.addPermission("admin:core:configuration","Manage core configuration."); + calipso.permission.Helper.addPermission("admin:module:configuration","Manage module configuration."); + calipso.permission.Helper.addPermission("admin:core:cache","View and clear cache."); // Admin routes calipso.lib.step( @@ -53,8 +56,9 @@ function init(module, app, next) { function defineRoutes() { // Permissions - var corePermit = calipso.permissions.hasPermission("admin:core:configuration"), - cachePermit = calipso.permissions.hasPermission("admin:core:cache"); + var corePermit = calipso.permission.Helper.hasPermission("admin:core:configuration"), + modulePermit = calipso.permission.Helper.addPermission("admin:core:configuration","Manage core configuration."), + cachePermit = calipso.permission.Helper.hasPermission("admin:core:cache"); // Core Administration dashboard module.router.addRoute('GET /admin', showAdmin, { @@ -98,6 +102,17 @@ function init(module, app, next) { }, this.parallel()); + module.router.addRoute('GET /admin/modules', modulesConfig, { + admin: true, + block:'admin.show', + permit: modulePermit + }, this.parallel()); + + module.router.addRoute('POST /admin/modules/save', saveModulesConfig, { + admin: true, + permit: modulePermit + }, this.parallel()); + // Default installation routers - only accessible in install mode module.router.addRoute('GET /admin/install', install, null, this.parallel()); module.router.addRoute('POST /admin/install', install, null, this.parallel()); @@ -553,8 +568,8 @@ function coreConfig(req, res, template, block, next) { calipso.data.themes = []; calipso.data.adminThemes = []; // TODO - for(var themeName in calipso.themes){ - var theme = calipso.themes[themeName]; + for(var themeName in calipso.availableThemes){ + var theme = calipso.availableThemes[themeName]; if(theme.about.type === "full" || theme.about.type === "frontend") { calipso.data.themes.push(themeName); } @@ -650,34 +665,27 @@ function coreConfig(req, res, template, block, next) { ] }, { - label:'Hook.IO', - legend:'Hook.IO', + label:'Clustering', + legend:'Clustering', type:'fieldset', fields:[ { - label:'Hook.IO Name', - name:'server:hookio:name', + label:'Number Workers', + description:'Number of workers to start, set to 0 to have Calipso default to number of available cpus.', + name:'server:cluster:workers', type:'text' }, { - label:'Hook.IO Port', - name:'server:hookio:port', - type:'text' - }, - { - label:'Hook.IO Host Name', - name:'server:hookio:host', - type:'text' - }, - { - label:'Hook.IO Debug', - name:'server:hookio:debug', + label:'Restart Workers', + name:'server:cluster:restartWorkers', + description:'Automatically restart workers if they die.', type:'checkbox', labelFirst: true }, { - label:'Hook.IO Max Listeners', - name:'server:hookio:maxListeners', + label:'Maximum Restarts', + name:'server:cluster:maximumRestarts', + description:'Number of failures before it will stop attempting to restart a worker.', type:'text' } ] @@ -816,6 +824,141 @@ function coreConfig(req, res, template, block, next) { } +/** + * Show the current configuration + * TODO Refactor this to a proper form + */ +function modulesConfig(req, res, template, block, next) { + + var moduleName = req.query.module || ''; + + if(!moduleName || !calipso.modules[moduleName]) { + req.flash('error','You need to specify a valid module.'); + res.redirect('/admin'); + return next(); + } + + var configForm = { + id:'module-config-form', + title:'Configure: ' + moduleName, + type:'form', + method:'POST', + action:'/admin/modules/save', + tabs:false, + fields:[ + { + label:'', + value:moduleName, + name:'moduleName', + type:'hidden' + } + ], + buttons:[ + { + name:'submit', + type:'submit', + value:'Save Configuration' + }, + {name:'cancel',type:'button',href:'/admin', value:'Cancel'} + ] + }; + + // Values can come straight off the config. + var values = calipso.config.getModuleConfig(moduleName); + + // Fields come from the module + var config = calipso.modules[moduleName].fn.config; + + calipso.lib._.keys(config).forEach(function(key) { + var field = {}; + field.label = config[key].label || key; + field.name = key; + + if(config[key].type) { + + field.type = config[key].type; + + // select boxes + if(config[key].options) { + field.options = config[key].options; + } + + } else { + // infer from value + if(typeof values[key] === 'boolean') { + field.type = 'checkbox'; + field.labelFirst = true; + } else { + field.type = 'text'; + } + } + + + field.description = config[key].description || ''; + configForm.fields.push(field); + }) + + res.layout = 'admin'; + + calipso.form.render(configForm, values, req, function(form) { + calipso.theme.renderItem(req, res, form, block, {}, next); + }); + +} + + +/** + * Save module configuratino + */ +function saveModulesConfig(req, res, template, block, next) { + + calipso.form.process(req, function(moduleConfig) { + + if (moduleConfig) { + + var moduleName = moduleConfig.moduleName; + + // Clean the submitted object + delete moduleConfig.moduleName + delete moduleConfig.submit + + calipso.config.setModuleConfig(moduleName, '', moduleConfig); + + calipso.e.pre_emit('CONFIG_UPDATE',{module: moduleName, config: moduleConfig}, function(config) { + + calipso.config.save(function(err) { + + if(err) { + + req.flash('error', req.t('Could not save the updated configuration, there was an error: ' + err.message)); + res.redirect('/admin/modules?module=' + moduleName); + + } else { + + // Set the reload config flag for event handler to pick up + calipso.e.post_emit('CONFIG_UPDATE', {module: moduleName, config: moduleConfig}, function(config) { + + req.flash('info', req.t('Changes to configuration saved.')); + res.redirect('/admin'); + next(); + + }); + + } + }); + + }); + + } else { + + req.flash('error', req.t('Could not process the updated module configuration.')); + res.redirect('/admin'); + next(); + + } + + }); +} /** * Create a form field for a module diff --git a/modules/core/content/content.js b/modules/core/content/content.js index 020038f8d..d1b5ff4f4 100644 --- a/modules/core/content/content.js +++ b/modules/core/content/content.js @@ -23,10 +23,10 @@ exports = module.exports = { */ function route(req,res,module,app,next) { - var cPerm = calipso.permissions.hasPermission("content:create"), - uPerm = calipso.permissions.hasPermission("content:update"), - dPerm = calipso.permissions.hasPermission("content:delete"), - vPerm = calipso.permissions.hasPermission("content:view"); + var cPerm = calipso.permission.Helper.hasPermission("content:manage:create"), + uPerm = calipso.permission.Helper.hasPermission("content:manage:update"), + dPerm = calipso.permission.Helper.hasPermission("content:manage:delete"), + vPerm = calipso.permission.Helper.hasPermission("content:manage:view"); res.menu.admin.addMenuItem(req, {name:'Content Management',path:'cms',url:'/content',description:'Manage content ...',permit:vPerm}); res.menu.admin.addMenuItem(req, {name:'Content',path:'cms/content',url:'/content',description:'Manage content ...',permit:vPerm}); @@ -47,15 +47,15 @@ function init(module,app,next) { calipso.e.addEvent('CONTENT_CREATE_FORM'); calipso.e.addEvent('CONTENT_UPDATE_FORM'); - calipso.permissions.addPermission("content","Manage content.",true); + calipso.permission.Helper.addPermission("content:manage","Content",true); calipso.lib.step( function defineRoutes() { - var cPerm = calipso.permissions.hasPermission("content:create"), - uPerm = calipso.permissions.hasPermission("content:update"), - dPerm = calipso.permissions.hasPermission("content:delete"), - vPerm = calipso.permissions.hasPermission("content:view"); + var cPerm = calipso.permission.Helper.hasPermission("content:manage:create"), + uPerm = calipso.permission.Helper.hasPermission("content:manage:update"), + dPerm = calipso.permission.Helper.hasPermission("content:manage:delete"), + vPerm = calipso.permission.Helper.hasPermission("content:manage:view"); // Default routes module.router.addRoute('GET /',homePage,{template:'list',block:'content.home'},this.parallel()); @@ -75,7 +75,7 @@ function init(module,app,next) { module.router.addRoute('GET /content/list.:format?',listContent,{admin:true,permit:vPerm,template:'listAdmin',block:'content.list'},this.parallel()); module.router.addRoute('POST /content',createContent,{admin:true,permit:cPerm},this.parallel()); module.router.addRoute('GET /content/new',createContentForm,{admin:true,permit:vPerm,block:'content.create'},this.parallel()); - module.router.addRoute('GET /content/show/:id.:format?',showContentByID,{admin:true,permit:vPerm,template:'show',block:'content.show'},this.parallel()); + module.router.addRoute('GET /content/show/:id',showContentByID,{admin:true,permit:vPerm,template:'show',block:'content.show'},this.parallel()); module.router.addRoute('GET /content/edit/:id',editContentForm,{admin:true,permit:uPerm,block:'content.edit'},this.parallel()); module.router.addRoute('GET /content/delete/:id',deleteContent,{admin:true,permit:dPerm},this.parallel()); module.router.addRoute('POST /content/:id',updateContent,{admin:true,permit:uPerm},this.parallel()); @@ -84,12 +84,12 @@ function init(module,app,next) { function done() { // Add dynamic helpers - calipso.dynamicHelpers.getContent = function() { + calipso.helpers.getContent = function() { return getContent; } // Get content list helper - calipso.dynamicHelpers.getContentList = function() { + calipso.helpers.getContentList = function() { return getContentList; } @@ -499,10 +499,10 @@ function editContentForm(req,res,template,block,next) { var returnTo = req.moduleParams.returnTo ? req.moduleParams.returnTo : ""; - var cPerm = calipso.permissions.hasPermission("content:create"), - uPerm = calipso.permissions.hasPermission("content:update"), - dPerm = calipso.permissions.hasPermission("content:delete"), - vPerm = calipso.permissions.hasPermission("content:view"); + var cPerm = calipso.permission.Helper.hasPermission("content:manage:create"), + uPerm = calipso.permission.Helper.hasPermission("content:manage:update"), + dPerm = calipso.permission.Helper.hasPermission("content:manage:delete"), + vPerm = calipso.permission.Helper.hasPermission("content:manage:view"); res.menu.adminToolbar.addMenuItem(req, {name:'List',weight:1,path:'list',url:'/content/',description:'List all ...',permit:vPerm}); res.menu.adminToolbar.addMenuItem(req, {name:'View',weight:2,path:'show',url:'/content/show/' + id,description:'Show current ...',permit:vPerm}); @@ -752,10 +752,10 @@ function showContent(req,res,template,block,next,err,content,format) { } else { - var cPerm = calipso.permissions.hasPermission("content:create"), - uPerm = calipso.permissions.hasPermission("content:update"), - dPerm = calipso.permissions.hasPermission("content:delete"), - vPerm = calipso.permissions.hasPermission("content:view"); + var cPerm = calipso.permission.Helper.hasPermission("content:manage:create"), + uPerm = calipso.permission.Helper.hasPermission("content:manage:update"), + dPerm = calipso.permission.Helper.hasPermission("content:manage:delete"), + vPerm = calipso.permission.Helper.hasPermission("content:manage:view"); res.menu.adminToolbar.addMenuItem(req, {name:'Create',weight:3,path:'new',url:'/content/new',description:'Create content ...',permit:cPerm}); res.menu.adminToolbar.addMenuItem(req, {name:'List',weight:1,path:'list',url:'/content/',description:'List all ...',permit:vPerm}); @@ -799,8 +799,8 @@ function listContent(req,res,template,block,next) { // Re-retrieve our object var Content = calipso.db.model('Content'); - var cPerm = calipso.permissions.hasPermission("content:create"), - vPerm = calipso.permissions.hasPermission("content:view"); + var cPerm = calipso.permission.Helper.hasPermission("content:manage:create"), + vPerm = calipso.permission.Helper.hasPermission("content:manage:view"); res.menu.adminToolbar.addMenuItem(req, {name:'Create',weight:1,path:'new',url:'/content/new',description:'Create content ...',permit:cPerm}); @@ -857,7 +857,7 @@ function listContent(req,res,template,block,next) { /** * Helper function for link to user */ -function contentLink(req,content) { +function contentLink(req, content) { return calipso.link.render({id:content._id,title:req.t('View {content}',{content:content.title}),label:content.title,url:'/content/show/' + content._id}); } @@ -894,7 +894,7 @@ function getContentList(query,out,next) { var qry = Content.find(query).skip(from).limit(limit); // Add sort - qry = calipso.table.sortQuery(qry,out.sortBy); + // qry = calipso.table.sortQuery(qry, out.sortBy); qry.find(function (err, contents) { @@ -903,27 +903,10 @@ function getContentList(query,out,next) { // Render the item into the response if(out.format === 'html') { - var table = {id:'content-list',sort:true,cls:'table-admin', - columns:[{name:'_id',sort:'title',label:'Title',fn:contentLink}, - {name:'contentType',label:'Type'}, - {name:'status',label:'Status'}, - {name:'published',label:'Published'} - ], - data:contents, - view:{ - pager:true, - from:from, - limit:limit, - total:total, - url:out.req.url, - sort:calipso.table.parseSort(out.sortBy) - } - }; - - var tableHtml = calipso.table.render(table,out.req); + - //calipso.theme.renderItem(out.req,out.res,out.template,out.block,{contents:contents, pager: pagerHtml},next); - calipso.theme.renderItem(out.req,out.res,tableHtml,out.block,null,next); + calipso.theme.renderItem(out.req,out.res,out.template,out.block,{contents:contents, pager: pagerHtml},next); + // calipso.theme.renderItem(out.req,out.res,tableHtml,out.block,null,next); } diff --git a/modules/core/contentTypes/contentTypes.js b/modules/core/contentTypes/contentTypes.js index 09baa18d4..026a95da2 100644 --- a/modules/core/contentTypes/contentTypes.js +++ b/modules/core/contentTypes/contentTypes.js @@ -13,14 +13,14 @@ var rootpath = process.cwd() + '/', * Define the routes that this module will repsond to. */ var routes = [ - {path:'GET /content/type',fn:listContentType,admin:true,permit:calipso.permissions.hasPermission("admin:content:type:view"),template:'list',block:'content.type.show'}, - {path:'GET /content/type/list.:format?',fn:listContentType,admin:true,permit:calipso.permissions.hasPermission("admin:content:type:view"),template:'list',block:'content.type.list'}, - {path:'POST /content/type/create',fn:createContentType,admin:true,permit:calipso.permissions.hasPermission("admin:content:type:create")}, - {path:'GET /content/type/new',fn:createContentTypeForm,admin:true,permit:calipso.permissions.hasPermission("admin:content:type:create"),block:'content.type.new',template:'form'}, - {path:'GET /content/type/show/:id.:format?',fn:showContentType,admin:true,permit:calipso.permissions.hasPermission("admin:content:type:view"),template:'show',block:'content.type.show'}, - {path:'GET /content/type/edit/:id',fn:editContentTypeForm,admin:true,permit:calipso.permissions.hasPermission("admin:content:type:update"),block:'content.type.edit'}, - {path:'GET /content/type/delete/:id',fn:deleteContentType,admin:true,permit:calipso.permissions.hasPermission("admin:content:type:delete")}, - {path:'POST /content/type/update/:id',fn:updateContentType,admin:true,permit:calipso.permissions.hasPermission("admin:content:type:update")} + {path:'GET /content/type',fn:listContentType,admin:true,permit:calipso.permission.Helper.hasPermission("admin:content:type:view"),template:'list',block:'content.type.show'}, + {path:'GET /content/type/list.:format?',fn:listContentType,admin:true,permit:calipso.permission.Helper.hasPermission("admin:content:type:view"),template:'list',block:'content.type.list'}, + {path:'POST /content/type/create',fn:createContentType,admin:true,permit:calipso.permission.Helper.hasPermission("admin:content:type:create")}, + {path:'GET /content/type/new',fn:createContentTypeForm,admin:true,permit:calipso.permission.Helper.hasPermission("admin:content:type:create"),block:'content.type.new',template:'form'}, + {path:'GET /content/type/show/:id.:format?',fn:showContentType,admin:true,permit:calipso.permission.Helper.hasPermission("admin:content:type:view"),template:'show',block:'content.type.show'}, + {path:'GET /content/type/edit/:id',fn:editContentTypeForm,admin:true,permit:calipso.permission.Helper.hasPermission("admin:content:type:update"),block:'content.type.edit'}, + {path:'GET /content/type/delete/:id',fn:deleteContentType,admin:true,permit:calipso.permission.Helper.hasPermission("admin:content:type:delete")}, + {path:'POST /content/type/update/:id',fn:updateContentType,admin:true,permit:calipso.permission.Helper.hasPermission("admin:content:type:update")} ] /** @@ -42,7 +42,7 @@ function route(req,res,module,app,next) { /** * Menu items */ - res.menu.admin.addMenuItem(req, {name:'Content Types',path:'cms/type',url:'/content/type',description:'Manage content types ...',permit:calipso.permissions.hasPermission("admin:content:type:view")}); + res.menu.admin.addMenuItem(req, {name:'Content Types',path:'cms/type',url:'/content/type',description:'Manage content types ...',permit:calipso.permission.Helper.hasPermission("admin:content:type:view")}); /** * Routing and Route Handler @@ -73,7 +73,7 @@ function init(module,app,next) { calipso.e.pre('CONTENT_TYPE_UPDATE',module.name,compileTemplates); // Define permissions - calipso.permissions.addPermission("admin:content:type","Manage content types.",true); + calipso.permission.Helper.addPermission("admin:content:type","Content Types",true); // Schemea var ContentType = new calipso.lib.mongoose.Schema({ @@ -237,10 +237,10 @@ function editContentTypeForm(req,res,template,block,next) { var item; - res.menu.adminToolbar.addMenuItem(req, {name:'List',path:'list',url:'/content/type/',description:'List all ...',permit:calipso.permissions.hasPermission("admin:content:type:view")}); - res.menu.adminToolbar.addMenuItem(req, {name:'View',path:'show',url:'/content/type/show/' + id,description:'Current item ...',permit:calipso.permissions.hasPermission("admin:content:type:view")}); - res.menu.adminToolbar.addMenuItem(req, {name:'Edit',path:'edit',url:'/content/type/edit/' + id,description:'Edit content type ...',permit:calipso.permissions.hasPermission("admin:content:type:edit")}); - res.menu.adminToolbar.addMenuItem(req, {name:'Delete',path:'delete',url:'/content/type/delete/' + id,description:'Delete content type ...',permit:calipso.permissions.hasPermission("admin:content:type:delete")}); + res.menu.adminToolbar.addMenuItem(req, {name:'List',path:'list',url:'/content/type/',description:'List all ...',permit:calipso.permission.Helper.hasPermission("admin:content:type:view")}); + res.menu.adminToolbar.addMenuItem(req, {name:'View',path:'show',url:'/content/type/show/' + id,description:'Current item ...',permit:calipso.permission.Helper.hasPermission("admin:content:type:view")}); + res.menu.adminToolbar.addMenuItem(req, {name:'Edit',path:'edit',url:'/content/type/edit/' + id,description:'Edit content type ...',permit:calipso.permission.Helper.hasPermission("admin:content:type:edit")}); + res.menu.adminToolbar.addMenuItem(req, {name:'Delete',path:'delete',url:'/content/type/delete/' + id,description:'Delete content type ...',permit:calipso.permission.Helper.hasPermission("admin:content:type:delete")}); ContentType.findById(id, function(err, c) { @@ -335,10 +335,10 @@ function showContentType(req,res,template,block,next) { } else { - res.menu.adminToolbar.addMenuItem(req, {name:'List',path:'list',url:'/content/type/',description:'List all ...',permit:calipso.permissions.hasPermission("admin:content:type:view")}); - res.menu.adminToolbar.addMenuItem(req, {name:'View',path:'show',url:'/content/type/show/' + id,description:'Current item ...',permit:calipso.permissions.hasPermission("admin:content:type:view")}); - res.menu.adminToolbar.addMenuItem(req, {name:'Edit',path:'edit',url:'/content/type/edit/' + id,description:'Edit content type ...',permit:calipso.permissions.hasPermission("admin:content:type:edit")}); - res.menu.adminToolbar.addMenuItem(req, {name:'Delete',path:'delete',url:'/content/type/delete/' + id,description:'Delete content type ...',permit:calipso.permissions.hasPermission("admin:content:type:delete")}); + res.menu.adminToolbar.addMenuItem(req, {name:'List',path:'list',url:'/content/type/',description:'List all ...',permit:calipso.permission.Helper.hasPermission("admin:content:type:view")}); + res.menu.adminToolbar.addMenuItem(req, {name:'View',path:'show',url:'/content/type/show/' + id,description:'Current item ...',permit:calipso.permission.Helper.hasPermission("admin:content:type:view")}); + res.menu.adminToolbar.addMenuItem(req, {name:'Edit',path:'edit',url:'/content/type/edit/' + id,description:'Edit content type ...',permit:calipso.permission.Helper.hasPermission("admin:content:type:edit")}); + res.menu.adminToolbar.addMenuItem(req, {name:'Delete',path:'delete',url:'/content/type/delete/' + id,description:'Delete content type ...',permit:calipso.permission.Helper.hasPermission("admin:content:type:delete")}); item = {id:content._id,type:'content',meta:content.toObject()}; @@ -380,7 +380,7 @@ function listContentType(req,res,template,block,next) { // Re-retrieve our object var ContentType = calipso.db.model('ContentType'); - res.menu.adminToolbar.addMenuItem(req, {name:'New Type',path:'new',url:'/content/type/new',description:'Create content type ...',permit:calipso.permissions.hasPermission("admin:content:type:create")}); + res.menu.adminToolbar.addMenuItem(req, {name:'New Type',path:'new',url:'/content/type/new',description:'Create content type ...',permit:calipso.permission.Helper.hasPermission("admin:content:type:create")}); var format = req.moduleParams.format || 'html'; diff --git a/modules/core/contentVersions/contentVersions.js b/modules/core/contentVersions/contentVersions.js index e0d69250b..519e07f59 100644 --- a/modules/core/contentVersions/contentVersions.js +++ b/modules/core/contentVersions/contentVersions.js @@ -43,20 +43,20 @@ function init(module,app,next) { calipso.e.addEvent('CONTENT_VERSION'); // Permissions - calipso.permissions.addPermission("content:versions:view","View content versions."); - calipso.permissions.addPermission("content:versions:diff","Diff content versions."); - calipso.permissions.addPermission("content:versions:revert","Revert content versions."); + calipso.permission.Helper.addPermission("content:versions:view","View content versions."); + calipso.permission.Helper.addPermission("content:versions:diff","Diff content versions."); + calipso.permission.Helper.addPermission("content:versions:revert","Revert content versions."); calipso.lib.step( function defineRoutes() { - var vPerm = calipso.permissions.hasPermission("content:versions:view"), - dPerm = calipso.permissions.hasPermission("content:versions:diff"), - rPerm = calipso.permissions.hasPermission("content:versions:revert"); + var vPerm = calipso.permission.Helper.hasPermission("content:versions:view"), + dPerm = calipso.permission.Helper.hasPermission("content:versions:diff"), + rPerm = calipso.permission.Helper.hasPermission("content:versions:revert"); // Menus - module.router.addRoute('GET /content/show/:id',showContent,{admin:true,permit:vPerm},this.parallel()); + module.router.addRoute('GET /content/show/:id',showContentVersionsMenu,{admin:true},this.parallel()); // Crud operations module.router.addRoute('GET /content/show/:id/versions',listVersions,{admin:true,permit:vPerm,template:'list',block:'content.version'},this.parallel()); @@ -115,8 +115,8 @@ var contentVersionFormSection = { /** * Show content menu */ -function showContent(req,res,template,block,next) { - var id = req.moduleParams.id, vPerm = calipso.permissions.hasPermission("content:versions:view"); +function showContentVersionsMenu(req,res,template,block,next) { + var id = req.moduleParams.id, vPerm = calipso.permission.Helper.hasPermission("content:versions:view"); res.menu.adminToolbar.addMenuItem(req, {name:'Versions',permit:vPerm,weight:10,path:'versions',url:'/content/show/' + id + '/versions',description:'Show versions ...',security:[]}); next(); } @@ -166,8 +166,8 @@ function showVersion(req,res,template,block,next) { var ContentVersion = calipso.db.model('ContentVersion'); - var vPerm = calipso.permissions.hasPermission("content:versions:view"), - rPerm = calipso.permissions.hasPermission("content:versions:revert"); + var vPerm = calipso.permission.Helper.hasPermission("content:versions:view"), + rPerm = calipso.permission.Helper.hasPermission("content:versions:revert"); res.menu.adminToolbar.addMenuItem(req, {name:'Return',path:'return',permit:vPerm,url:'/content/show/' + contentId + '/versions',description:'Show content ...',security:[]}); res.menu.adminToolbar.addMenuItem(req, {name:'Revert',path:'revert',permit:rPerm,url:'/content/show/' + contentId + '/version/' + id + '/revert',description:'Revert to this version of content ...',security:[]}); @@ -265,8 +265,8 @@ function listVersions(req,res,template,block,next) { // Re-retrieve our object var ContentVersion = calipso.db.model('ContentVersion'); - var vPerm = calipso.permissions.hasPermission("content:versions:view"), - dPerm = calipso.permissions.hasPermission("content:versions:diff"); + var vPerm = calipso.permission.Helper.hasPermission("content:versions:view"), + dPerm = calipso.permission.Helper.hasPermission("content:versions:diff"); res.menu.adminToolbar.addMenuItem(req, {name:'Diff',permit:dPerm,path:'diff',url:'',description:'Diff versions ...',security:[]}); res.menu.adminToolbar.addMenuItem(req, {name:'Return',permit:vPerm,path:'return',url:'/content/show/' + id,description:'Show content ...',security:[]}); diff --git a/modules/core/permissions/permissions.js b/modules/core/permissions/permissions.js index fa26b7437..b0ab9e694 100644 --- a/modules/core/permissions/permissions.js +++ b/modules/core/permissions/permissions.js @@ -10,8 +10,8 @@ var rootpath = process.cwd() + '/', * Routes this module will respond to */ var routes = [ - {path: 'GET /admin/permissions', fn: showPermissions, admin:true, template: 'permissions',block: 'admin.permissions'}, - {path: 'POST /admin/permissions', fn: updatePermissions, admin:true} + {path: 'GET /admin/permissions', fn: showPermissions, permit:calipso.permission.Helper.hasPermission("admin:permission:configuration"), admin:true, template: 'permissions',block: 'admin.permissions'}, + {path: 'POST /admin/permissions', fn: updatePermissions, permit:calipso.permission.Helper.hasPermission("admin:permission:configuration"), admin:true} ]; @@ -29,8 +29,11 @@ exports = module.exports = { */ function route(req, res, module, app, next) { + var permPermit = calipso.permission.Helper.hasPermission("admin:permission:configuration"); + + // Menu - res.menu.admin.addMenuItem(req, {name:'Permissions', path: 'admin/security/permissions', weight: 10, url: '/admin/permissions', description: 'Manage permissions ...', security: [] }); + res.menu.admin.addMenuItem(req, {name:'Permissions', permit: permPermit, path: 'admin/security/permissions', weight: 10, url: '/admin/permissions', description: 'Manage permissions ...', security: [] }); // Router module.router.route(req, res, next); @@ -47,6 +50,9 @@ function init(module, app, next) { calipso.e.post('PERMISSIONS_UPDATE',module.name,loadPermissionRoles); + calipso.permission.Helper.addPermission("admin:permission:configuration","Manage role based permissions."); + + var PermissionRole = new calipso.lib.mongoose.Schema({ permission:{type: String, required: true}, role:{type: String, required: true} @@ -64,7 +70,7 @@ function init(module, app, next) { */ function loadPermissionRoles(next) { - var perm = calipso.permissions, + var perm = calipso.permission.Helper, PermissionRole = calipso.db.model('PermissionRole'); // Clear down first - this may cause strange behaviour to anyone @@ -73,17 +79,10 @@ function loadPermissionRoles(next) { // Load the permissions PermissionRole.find({}).sort('permission',1).sort('role',1).find(function (err, prs) { + prs.forEach(function(pr) { perm.addPermissionRole(pr.permission, pr.role); }); - - perm.addPermissionRole("admin:content:type:view","Contributor"); - perm.addPermissionRole("admin:content:type:create","Contributor"); - perm.addPermissionRole("admin:content:type:delete","Contributor"); - perm.addPermissionRole("admin:core:configuration","Contributor"); - perm.addPermissionRole("content:view","Contributor"); - perm.addPermissionRole("content:update","Contributor"); - perm.addPermissionRole("content:create","Contributor"); perm.structureAndSort(); @@ -92,13 +91,13 @@ function loadPermissionRoles(next) { }); } - + /** * Show permissions */ function showPermissions(req, res, options, next) { - var structuredPermissions = calipso.permissions.structuredPermissions, + var structuredPermissions = calipso.permission.Helper.structuredPermissions, Role = calipso.db.model('Role'), PermissionRole = calipso.db.model('PermissionRole'); @@ -126,20 +125,24 @@ function renderPermissionTable(structuredPermissions, roles) { op = recursePermissions(structuredPermissions, '', 0, op); op.forEach(function(item) { - if(calipso.permissions.permissions[item.key]) { - output += "