diff --git a/lib/cli/Download.js b/lib/cli/Download.js index 262b981fb..ff90b2ee5 100644 --- a/lib/cli/Download.js +++ b/lib/cli/Download.js @@ -13,10 +13,8 @@ * */ var sys; -var zip; try { sys = require('util'); - zip = require('zipfile') } catch (e) { sys = require('sys'); @@ -30,10 +28,10 @@ calipso = require(path.join(rootpath, 'lib/calipso')), exec = require('child_process').exec, colors = require('colors'), util = require('util'), - zip, fs = require('fs'), - rimraf = require('rimraf') -semver = require('semver'); + rimraf = require('rimraf'), + semver = require('semver'), + zip = require('adm-zip'); /** ======= @@ -371,23 +369,23 @@ function unzipDownload(type, file, callback) { tmpFolder, tmpName; - try { - zf = new zip.ZipFile(file) - } - catch (ex) { - return callback(ex); - } + zf = new zip(file, 'r'); + entries = zf.getEntries(); - zf.names.forEach(function (name) { - - // First result is the basefolder - if (!baseFolder) { - baseFolder = name; // Store + entries.forEach(function (entry) { + var name = entry.entryName; + if (entry.isDirectory) { + // First result is the basefolder + if (!baseFolder) { + baseFolder = name; + } // Store + return; } + // Now, lets find the package.json if (type === 'module' && name === (baseFolder + "package.json")) { - var buffer = zf.readFileSync(name); + var buffer = entry.getData(); var packageJson = JSON.parse(buffer); tmpName = packageJson.name; tmpFolder = path.join(path.dirname(file), tmpName + "/"); // Extraction will go here @@ -395,7 +393,7 @@ function unzipDownload(type, file, callback) { // Now, lets find the theme.json if (type === 'theme' && name === (baseFolder + "theme.json")) { - var buffer = zf.readFileSync(name); + var buffer = entry.getData(); var themeJson = JSON.parse(buffer); tmpName = themeJson.name; tmpFolder = path.join(path.dirname(file), tmpName + "/"); // Extraction will go here @@ -413,8 +411,13 @@ function unzipDownload(type, file, callback) { // First run through and create every directory synchronously var folders = []; - zf.names.forEach(function (name) { - folders.push(name.replace(baseFolder, "").split("/")); + entries.forEach(function (entry) { + var name = entry.entryName; + var p = name.replace(baseFolder, "").split("/"); + if (!entry.isDirectory) { + p.splice(-1, 1); + } + folders.push(p); }); folders.forEach(function (folderList) { @@ -429,55 +432,60 @@ function unzipDownload(type, file, callback) { }); // Now, lets extract all the files - var remaining = zf.names.length; - - zf.names.forEach(function (name) { + var remaining = entries.length; + entries.forEach(function (entry) { + var name = entry.entryName; var dest = path.join( tmpFolder, name.replace(baseFolder, "") ); // Skip directories, hiddens. - var isDir = (!path.extname(name) || name[0] === '.' || name[name.length] === "/"); - if (isDir) { + if (entry.isDirectory) { remaining--; if (!remaining) { return callback(null); } } else { - zf.readFile(name, function (err, buff) { - if (err) { - return callback(err); + var buff; + try { + buff = entry.getData(); + if (!buff) { + return callback(new Error('Unable to read content')); } - fs.open(dest, 'w', 0644, function (err, fd) { - if (err) { - if (err.code !== "EISDIR") { - // fs.unlinkSync(file); + } + catch (err) { + return callback(err); + } + console.log('Downloaded ', name, buff.length); + fs.open(dest, 'w', 0644, function (err, fd) { + if (err) { + if (err.code !== "EISDIR") { + // fs.unlinkSync(file); + return callback(err); + } else { + remaining--; + } + + } else { + fs.write(fd, buff, 0, buff.length, null, function (err) { + if (err) { + fs.unlinkSync(file); return callback(err); - } else { - remaining--; } - - } else { - fs.write(fd, buff, 0, buff.length, null, function (err) { + fs.close(fd, function (err) { if (err) { - fs.unlinkSync(file); return callback(err); } - fs.close(fd, function (err) { - if (err) { - return callback(err); - } - remaining--; - if (!remaining) { - fs.unlinkSync(file); - callback(null, tmpName, tmpFolder); - } - }); + remaining--; + if (!remaining) { + fs.unlinkSync(file); + callback(null, tmpName, tmpFolder); + } }); - } - }); + }); + } }); } }); diff --git a/lib/core/Module.js b/lib/core/Module.js index d0664c447..337baddc2 100644 --- a/lib/core/Module.js +++ b/lib/core/Module.js @@ -370,38 +370,45 @@ function initModule(module, depends) { var express = require('express'); calipso.app.use(express["static"](p, {maxAge:86400000})); } - - // 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); - } + p = path.join(calipso.modules[module].router.modulePath, 'theme-extension.json'); + if ((fs.existsSync || path.existsSync)(p)) { + var moduleThemeConfig = fs.readFileSync(p, 'utf8'); + calipso.theme.cacheModuleTheme(JSON.parse(moduleThemeConfig), calipso.modules[module].router.modulePath, finish); + return; + } + finish(); + function finish() { + // 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(); - }); - } else { - checkAllModulesInited(); - } + } - }); + }); + } } - } /** @@ -799,9 +806,13 @@ function loadModuleTemplates(module, moduleTemplatePath) { } fs.readdirSync(moduleTemplatePath).forEach(function (name) { - // Template paths and functions var templatePath = moduleTemplatePath + "/" + name; + + if (fs.statSync(templatePath).isDirectory()) { + return; + } + var templateExtension = templatePath.match(/([^\.]+)$/)[0]; var template = fs.readFileSync(templatePath, 'utf8'); var templateName = name.replace(/\.([^\.]+)$/, ''); diff --git a/lib/core/Themes.js b/lib/core/Themes.js index 448529eef..c53279b9c 100644 --- a/lib/core/Themes.js +++ b/lib/core/Themes.js @@ -147,8 +147,56 @@ module.exports.Theme = function (theme, next) { } return layouts; - } + }, + cacheModuleTheme:function (moduleThemeConfig, moduleThemePath, next) { + var templates = [], + layout, layoutConfig, section, template, module, templateFiles, errorCodeTemplates; + + // Scan through each layout + if (moduleThemeConfig) { + + for (layout in moduleThemeConfig.layouts) { + // Scan through each layout + layoutConfig = moduleThemeConfig.layouts[layout].layout; + + if (!this.config.layouts[layout]) { + theme.config.layouts[layout] = moduleThemeConfig.layouts[layout]; + // Add the layout template + templates.push({ + name:layout, + templatePath:calipso.lib.path.join("templates", layoutConfig.template) + }); + } + + // Add the templates + for (section in layoutConfig.sections) { + template = layoutConfig.sections[section].template; + if (template && !this.cache[layout + "." + section]) { + templates.push({ + name:layout + "." + section, + templatePath:calipso.lib.path.join("templates", layout, template) + }); + } + } + + } + var self = this; + var templateIterator = function (templateName, cb) { + loadTemplate(self.cache, templateName, moduleThemePath, cb); + }; + + calipso.lib.async.map(templates, templateIterator, function (err, result) { + if (err) { + // May not be a problem as missing templates default to default + calipso.error("Error loading templates, msg: " + err.message + ", stack: " + err.stack); + next(err); + } else { + next(null); + } + }); + } + } }; next(null, theme); @@ -422,6 +470,7 @@ function loadTheme(theme, themePath, next) { }); } + /** * Load all of the theme templates into the theme * @param theme diff --git a/modules/core/assets/assets.js b/modules/core/assets/assets.js index 72f2ac04b..bf7d52487 100644 --- a/modules/core/assets/assets.js +++ b/modules/core/assets/assets.js @@ -194,7 +194,7 @@ function init(module, app, next) { var s = asset.key.split('/'); bucket = s[0]; } - var expat = require('node-expat'); + var nodeXml = require('node-xml'); var knox = require('knox').createClient({ key:calipso.config.getModuleConfig("assets", "s3key"), secret:calipso.config.getModuleConfig("assets", "s3secret"), @@ -212,7 +212,6 @@ function init(module, app, next) { } knox.get((info && info.prefix) ? ('?prefix=' + info.prefix) : '').on('response',function (s3res) { s3res.setEncoding('utf8'); - var parser = new expat.Parser(); var items = []; var item = null; var dirs = {}; @@ -274,11 +273,9 @@ function init(module, app, next) { parser.addListener('text', function (s) { if (property === 'code') { code = s; - } - if (property === 'message') { + } else if (property === 'message') { message = s; - } - if (property === 'author') { + } else if (property === 'author') { owner = s; } if (property && item) { @@ -638,8 +635,9 @@ function init(module, app, next) { if (bucket !== '') { list = assetsToDelete[bucket].splice(0, 1000); // Pop off the first 1000 assets for one of the buckets. if (assetsToDelete[bucket].length == 0) { + // If we're done with the bucket then remove it from here. delete assetsToDelete[bucket]; - } // If we're done with the bucket then remove it from here. + } break; } } @@ -2014,15 +2012,15 @@ function updateAsset(req, res, template, block, next) { var errorMsg = ''; if (err.errors) { - for (var error in - err.errors) { + for (var error in err.errors) { errorMessage = error + " " + err.errors[error] + '\r\n'; } } else { errorMessage = err.message; } req.flash('error', req.t('Could not update content because {msg}', {msg:errorMessage})); - if (res.statusCode != 302) { // Don't redirect if we already are, multiple errors + if (res.statusCode != 302) { + // Don't redirect if we already are, multiple errors res.redirect('back'); } next(); diff --git a/modules/core/user/user.roles.js b/modules/core/user/user.roles.js index 1bac6b0d9..d839b11db 100644 --- a/modules/core/user/user.roles.js +++ b/modules/core/user/user.roles.js @@ -245,7 +245,8 @@ function updateRole(req, res, template, block, next) { c.save(function (err) { if (err) { req.flash('error', req.t('Could not update role because {msg}.', {msg:err.message})); - if (res.statusCode != 302) { // Don't redirect if we already are, multiple errors + if (res.statusCode != 302) { + // Don't redirect if we already are, multiple errors res.redirect('/user/role/edit/' + id); } next(); diff --git a/package.json b/package.json index df1af730c..8b4940fd1 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name":"calipso", "description":"A NodeJS CMS", - "version":"0.3.15", + "version":"0.3.17", "homepage":"http://calip.so", "repository":{ "type":"git", @@ -41,7 +41,6 @@ "request":"2.9.x", "pool":"0.4.x", "mime":"1.2.x", - "cluster":"0.7.x", "step":"0.0.x", "optimist":"0.3.x", "colors":"0.6.x", @@ -51,14 +50,14 @@ "async":"0.1.x", "hook.io":"0.8.x", "mailer":"~0.6.4", - "everyauth":"0.3.x" + "everyauth":"0.3.x", + "adm-zip":"0.1.x", + "node-xml":"1.0.x", + "knox":"0.0.x" }, "optionalDependencies":{ "bcrypt":"0.7.x", - "knox":"0.0.x", - "node-expat":"1.4.x", - "imagemagick":"0.1.x", - "zipfile":"0.3.x" + "imagemagick":"0.1.x" }, "scripts":{ "install":"bash ./bin/install.sh", diff --git a/test/config.test.js b/test/config.test.js index 0ff1b28a3..6cfa3fbc8 100644 --- a/test/config.test.js +++ b/test/config.test.js @@ -20,81 +20,87 @@ var defaultConfig = { /** * Tests */ -exports['I can create a development configuration'] = function () { - - mkdir(path.join(rootpath, 'tmp'), function () { - fs.writeFileSync(path.join(rootpath, 'tmp', 'default.json'), JSON.stringify(defaultConfig)); - var conf = new Config({env:'development', path:path.join(rootpath, 'tmp')}); - conf.type.should.equal('file'); - conf.file.should.equal(path.join(rootpath, 'tmp', 'development.json')); +describe('Calipso config test', function () { + it('I can create a development configuration', function (done) { + mkdir(path.join(rootpath, 'tmp'), function () { + fs.writeFileSync(path.join(rootpath, 'tmp', 'default.json'), JSON.stringify(defaultConfig)); + var conf = new Config({env:'development', path:path.join(rootpath, 'tmp')}); + conf.type.should.equal('file'); + conf.file.should.equal(path.join(rootpath, 'tmp', 'development.json')); + done(); + }); }); -}; - -exports['I can add and retrieve configuration'] = function () { + it('I can add and retrieve configuration', function (done) { - var conf = new Config({env:'development', path:path.join(rootpath, 'tmp')}); - conf.init(function () { - conf.set('test:v1', 'v1'); - conf.get('test:v1').should.equal('v1'); - conf.set('test:v2', 'v2'); - conf.get('test:v2').should.equal('v2'); - - var test = conf.get('test'); - test.v1.should.equal('v1'); - test.v2.should.equal('v2'); + var conf = new Config({env:'development', path:path.join(rootpath, 'tmp')}); + conf.init(function () { + conf.set('test:v1', 'v1'); + conf.get('test:v1').should.equal('v1'); + conf.set('test:v2', 'v2'); + conf.get('test:v2').should.equal('v2'); + + var test = conf.get('test'); + test.v1.should.equal('v1'); + test.v2.should.equal('v2'); + done(); + }); }); -}; - -exports['I can use different environments'] = function () { - var confDev = new Config({env:'development', path:path.join(rootpath, 'tmp')}); - var confTest = new Config({env:'test', path:path.join(rootpath, 'tmp')}); + it('I can use different environments (dev config)', function (done) { + var confDev = new Config({env:'development', path:path.join(rootpath, 'tmp')}); - confDev.init(function () { - confDev.set('test:v1', 'v1'); - confDev.get('test:v1').should.equal('v1'); - confDev.save(function (err) { - (fs.existsSync || path.existsSync)(confDev.file); + confDev.init(function () { + confDev.set('test:v1', 'v1'); + confDev.get('test:v1').should.equal('v1'); + confDev.save(function (err) { + (fs.existsSync || path.existsSync)(confDev.file); + done(); + }); }); }); - confTest.init(function () { - confTest.set('test:v1', 'v1'); - confTest.get('test:v1').should.equal('v1'); - confTest.save(function (err) { - (fs.existsSync || path.existsSync)(confTest.file); + it('I can use different environments (test config)', function (done) { + var confTest = new Config({env:'test', path:path.join(rootpath, 'tmp')}); + confTest.init(function () { + confTest.set('test:v1', 'v1'); + confTest.get('test:v1').should.equal('v1'); + confTest.save(function (err) { + (fs.existsSync || path.existsSync)(confTest.file); + done(); + }); }); }); -}; -exports['I can use the setSave shortcut'] = function () { + it('I can use the setSave shortcut', function (done) { - var conf = new Config({path:path.join(rootpath, 'tmp')}); - conf.init(function () { - conf.setSave('test:setsave', 'Yah!', function (err) { - var confTest = new Config({path:path.join(rootpath, 'tmp')}); - confTest.init(function () { - confTest.get('test:setsave').should.equal('Yah!'); + var conf = new Config({path:path.join(rootpath, 'tmp')}); + conf.init(function () { + conf.setSave('test:setsave', 'Yah!', function (err) { + var confTest = new Config({path:path.join(rootpath, 'tmp')}); + confTest.init(function () { + confTest.get('test:setsave').should.equal('Yah!'); + done(); + }); }); }); }); -}; -/** - * Mkdir -p. - * - * TODO - these are unix only ... - * - * @param {String} path - * @param {Function} fn - */ - -function mkdir(path, fn) { - exec('mkdir -p ' + path, function (err) { - if (err) { - throw err; - } - fn && fn(); - }); -} + /** + * Mkdir -p. + * + * TODO - these are unix only ... + * + * @param {String} path + * @param {Function} fn + */ + + function mkdir(path, fn) { + exec('mkdir -p ' + path, function (err) { + if (err) { + throw err; + } + fn && fn(); + }); + } +}); diff --git a/themes/core/cleanslate/public/css/primary-menu.css b/themes/core/cleanslate/public/css/primary-menu.css index 1332b1096..ba883e4a0 100644 --- a/themes/core/cleanslate/public/css/primary-menu.css +++ b/themes/core/cleanslate/public/css/primary-menu.css @@ -1,3 +1,6 @@ +/* Main menu */ +/* Excellent CSS from http://www.red-team-design.com/css3-dropdown-menu */ +/* line 6, ../sass/primary-menu.scss */ .primary-menu { width: 100%; list-style: none; diff --git a/themes/core/cleanslate/public/css/variables.css b/themes/core/cleanslate/public/css/variables.css index e69de29bb..82c4ecbcc 100644 --- a/themes/core/cleanslate/public/css/variables.css +++ b/themes/core/cleanslate/public/css/variables.css @@ -0,0 +1,3 @@ +/*Grid*/ +/*Type*/ +/* Transitions*/ diff --git a/themes/core/cleanslate/public/js/cleanslate-core.js b/themes/core/cleanslate/public/js/cleanslate-core.js index e092d9b28..c2be21c91 100644 --- a/themes/core/cleanslate/public/js/cleanslate-core.js +++ b/themes/core/cleanslate/public/js/cleanslate-core.js @@ -71,8 +71,7 @@ cleanslate = { // //} - } - else { + } else { // not logged in userWelcomeOrLoginBox.find('a').eq(0).click(function (e) { // if the 'plain layout' module is active, then prevent default and get the login form via ajax diff --git a/themes/core/cleanslate/public/sass/admin.scss b/themes/core/cleanslate/public/sass/admin.scss index a2febf81f..62f58df40 100644 --- a/themes/core/cleanslate/public/sass/admin.scss +++ b/themes/core/cleanslate/public/sass/admin.scss @@ -879,4 +879,4 @@ form .section:first-child { @include border-radius($borderRadius); } -@import "media-queries.scss"; \ No newline at end of file +@import "media-queries.scss"; diff --git a/themes/core/cleanslate/public/sass/media-queries.scss b/themes/core/cleanslate/public/sass/media-queries.scss index 6e99c60fe..02d4f213d 100644 --- a/themes/core/cleanslate/public/sass/media-queries.scss +++ b/themes/core/cleanslate/public/sass/media-queries.scss @@ -38,9 +38,11 @@ .btn { padding: 2.6% 5%; } -/* #admin-body { - max-width: $rowWidth / 1.3; - }*/ +/* + #admin-body { + max-width: $rowWidth / 1.3; + } +*/ } @media screen and (max-width: 1200px) {