diff --git a/README.md b/README.md index 8e0cb507f9..caa3a995ff 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,6 @@ # Adapt Builder [![Build Status](https://secure.travis-ci.org/adaptlearning/adapt_authoring.png)](http://travis-ci.org/adaptlearning/adapt_authoring) + +[![Join the chat at https://gitter.im/adaptlearning/adapt_authoring](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/adaptlearning/adapt_authoring?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) A web-based authoring tool for the [Adapt Framework](https://community.adaptlearning.org/). @@ -49,7 +51,7 @@ git config --global user.email "youremail@domain.com" [NodeJS](http://nodejs.org/) and [NPM](https://www.npmjs.org/) are required. Installing Node.js will also install NPM. -The NodeJS version should must be 0.10.33. Earlier versions of NodeJS are less stable and the code is currently not compatibile with 0.12.x. +IMPORTANT: The NodeJS version must be 0.10.33. Earlier versions of NodeJS are less stable and the code is currently not compatibile with 0.12.x. You should use a NodeJS version manager. We recommend using [NVM](https://github.com/creationix/nvm) on non-Windows machines. On Windows, try [nodist](https://github.com/marcelklehr/nodist) @@ -105,4 +107,4 @@ The ouput on the console will tell you what url to use to access the tool. By de You can run the install script again at anytime. If you chose the same values for the master database connection, you may overwrite any existing data, but this is occasionally desired. -We hope you enjoy using the tool! For help and support, please visit the community page at [http://community.adaptlearning.org](http://community.adaptlearning.org) \ No newline at end of file +We hope you enjoy using the tool! For help and support, please visit the community page at [http://community.adaptlearning.org](http://community.adaptlearning.org) diff --git a/frontend/src/core/assetManagement/less/assetManagementModalTags.less b/frontend/src/core/assetManagement/less/assetManagementModalTags.less index be176ab9d5..b0adacb05d 100644 --- a/frontend/src/core/assetManagement/less/assetManagementModalTags.less +++ b/frontend/src/core/assetManagement/less/assetManagementModalTags.less @@ -6,6 +6,7 @@ background-color: #fff; overflow-y: hidden; display:none; + z-index: 5; .asset-management-modal-tags-toolbar { border-bottom:1px solid #e2e2e2; diff --git a/frontend/src/core/editor/config/models/editorConfigModel.js b/frontend/src/core/editor/config/models/editorConfigModel.js index 8ec5b4e030..fecc54321e 100644 --- a/frontend/src/core/editor/config/models/editorConfigModel.js +++ b/frontend/src/core/editor/config/models/editorConfigModel.js @@ -6,11 +6,12 @@ define(function(require) { sync: function(method, model, options) { options = options || {}; - switch(method.toLowerCase()) { + switch (method.toLowerCase()) { case 'read': options.url = '/api/content/config/' + this.get('_courseId'); break; case 'update': + case 'patch': options.url = '/api/content/config/' + this.get('_id'); break; } diff --git a/frontend/src/core/editor/config/views/editorConfigEditView.js b/frontend/src/core/editor/config/views/editorConfigEditView.js index 369221bb99..125cc2a687 100644 --- a/frontend/src/core/editor/config/views/editorConfigEditView.js +++ b/frontend/src/core/editor/config/views/editorConfigEditView.js @@ -4,7 +4,7 @@ define(function(require) { var Backbone = require('backbone'); var Origin = require('coreJS/app/origin'); var EditorOriginView = require('editorGlobal/views/editorOriginView'); - + var EditorConfigEditView = EditorOriginView.extend({ tagName: "div", @@ -25,7 +25,12 @@ define(function(require) { return; } - this.model.save(null, { + // Ensure the _id is always passed. + var attributesToUpdate = _.extend(this.model.changedAttributes(), + {_id: this.model.get('_id'), _courseId: this.model.get('_courseId')}); + + this.model.save(attributesToUpdate, { + patch: true, error: function() { alert('An error occurred doing the save'); }, @@ -39,7 +44,7 @@ define(function(require) { }, this); }, this) - }) + }); } }, { diff --git a/install.js b/install.js index 2248edd490..50b033e3ca 100644 --- a/install.js +++ b/install.js @@ -200,7 +200,15 @@ var steps = [ return exitInstall(1, 'Framework install failed. See console output for possible reasons.'); } - return next(); + // Remove the default course + rimraf(path.resolve(__dirname, 'adapt_framework', 'src', 'course'), function(err) { + if (err) { + console.log('ERROR: ', err); + return exitInstall(1, 'Framework install error -- unable to remove default course.'); + } + + return next(); + }); }); }); }, diff --git a/lib/helpers.js b/lib/helpers.js index 31f8fd3eb9..ea7d2e0e9e 100644 --- a/lib/helpers.js +++ b/lib/helpers.js @@ -103,7 +103,7 @@ function grantSuperPermissions (userId, next) { }); } -function isMasterCourseShared(courseId, next) { +function isMasterPreviewAccessible(courseId, userId, next) { database.getDatabase(function(err, db) { if (err) { logger.log('error', err); @@ -119,7 +119,7 @@ function isMasterCourseShared(courseId, next) { if (results && results.length == 1) { var course = results[0]; - if (course._isShared) { + if (course._isShared || course.createdBy == userId) { // Shared courses on the same tenant are open to users on the same tenant return next(null, true); } @@ -187,6 +187,6 @@ exports = module.exports = { schemaToObject: schemaToObject, grantSuperPermissions: grantSuperPermissions, hasCoursePermission: hasCoursePermission, - isMasterCourseShared: isMasterCourseShared, + isMasterPreviewAccessible: isMasterPreviewAccessible, replaceAll: replaceAll }; diff --git a/plugins/content/config/index.js b/plugins/content/config/index.js index f3346d78e0..fbe157370c 100644 --- a/plugins/content/config/index.js +++ b/plugins/content/config/index.js @@ -5,10 +5,12 @@ var ContentPlugin = require('../../../lib/contentmanager').ContentPlugin, configuration = require('../../../lib/configuration'), + usermanager = require('../../../lib/usermanager'), permissions = require('../../../lib/permissions'), database = require('../../../lib/database'), helpers = require('../../../lib/helpers'), logger = require('../../../lib/logger'), + usermanager = require('../../../lib/usermanager'), origin = require('../../../'), util = require('util'), path = require('path'), @@ -95,6 +97,17 @@ function initialize () { next(null, course); }]); }); + + app.contentmanager.addContentHook('update', 'config', { when: 'pre' }, function (data, next) { + if (data[1].hasOwnProperty('_generateSourcemap')) { + var tenant = usermanager.getCurrentUser().tenant._id; + var course = data[0]._id; + + app.emit('rebuildCourse', tenant, course); + } + + next(null, data); + }); }); } diff --git a/plugins/content/config/model.schema b/plugins/content/config/model.schema index 8e83263776..f30b87ed81 100644 --- a/plugins/content/config/model.schema +++ b/plugins/content/config/model.schema @@ -127,10 +127,10 @@ "required": true, "default": "easeOutQuart", "title": "Hide Easing", - "inputType": { - "type": "Select", + "inputType": { + "type": "Select", "options": [ - "easeInSine", + "easeInSine", "easeOutSine", "easeInOutSine", "easeInQuad", @@ -169,10 +169,10 @@ "required": true, "default": "easeInQuart", "title": "Hide Easing", - "inputType": { - "type": "Select", + "inputType": { + "type": "Select", "options": [ - "easeInSine", + "easeInSine", "easeOutSine", "easeInOutSine", "easeInQuad", @@ -215,6 +215,15 @@ "validators": ["required", "number"] } } + }, + "_generateSourcemap": { + "type": "bool", + "default": "false", + "isSetting": true, + "inputType": {"type": "Boolean", "options": [true, false]}, + "validators": [], + "title": "Generate sourcemap", + "help": "Creates a JavaScript sourcemap for the output code - useful for debugging (in browsers that support it)." } } } diff --git a/plugins/output/adapt/index.js b/plugins/output/adapt/index.js index c9dda8f1d6..fa32997fd0 100644 --- a/plugins/output/adapt/index.js +++ b/plugins/output/adapt/index.js @@ -24,35 +24,35 @@ var OutputPlugin = require('../../../lib/outputmanager').OutputPlugin, version = require('../../../version'), logger = require('../../../lib/logger'); -function AdaptOutput () { +function AdaptOutput() { } util.inherits(AdaptOutput, OutputPlugin); -AdaptOutput.prototype.publish = function (courseId, isPreview, request, response, next) { - var self = this; - var user = usermanager.getCurrentUser(), - tenantId = user.tenant._id, - outputJson = {}, - isRebuildRequired = false, - themeName = Constants.Defaults.ThemeName; - menuName = Constants.Defaults.MenuName; +AdaptOutput.prototype.publish = function(courseId, isPreview, request, response, next) { + var self = this; + var user = usermanager.getCurrentUser(), + tenantId = user.tenant._id, + outputJson = {}, + isRebuildRequired = false, + themeName = Constants.Defaults.ThemeName; + menuName = Constants.Defaults.MenuName; - var resultObject = {}; + var resultObject = {}; - var FRAMEWORK_ROOT_FOLDER = path.join(configuration.tempDir, configuration.getConfig('masterTenantID'), Constants.Folders.Framework); + var FRAMEWORK_ROOT_FOLDER = path.join(configuration.tempDir, configuration.getConfig('masterTenantID'), Constants.Folders.Framework); - async.series([ + async.series([ function(callback) { self.getCourseJSON(tenantId, courseId, function(err, data) { - if (err) { - return callback(err); - } + if (err) { + return callback(err); + } - // Store off the retrieved collections - outputJson = data; + // Store off the retrieved collections + outputJson = data; - callback(null); + callback(null); }); }, function(callback) { @@ -119,7 +119,7 @@ AdaptOutput.prototype.publish = function (courseId, isPreview, request, response }); }, function(callback) { - var assetsFolder = path.join(FRAMEWORK_ROOT_FOLDER, Constants.Folders.AllCourses, tenantId, courseId, + var assetsFolder = path.join(FRAMEWORK_ROOT_FOLDER, Constants.Folders.AllCourses, tenantId, courseId, Constants.Folders.Build, Constants.Folders.Course, outputJson['config']._defaultLanguage, Constants.Folders.Assets); self.writeCourseAssets(tenantId, courseId, assetsFolder, outputJson, function(err, modifiedJson) { @@ -143,18 +143,18 @@ AdaptOutput.prototype.publish = function (courseId, isPreview, request, response }); }, function(callback) { - fs.exists(path.join(FRAMEWORK_ROOT_FOLDER, Constants.Folders.AllCourses, tenantId, courseId, Constants.Folders.Build, Constants.Filenames.Main), function (exists) { + fs.exists(path.join(FRAMEWORK_ROOT_FOLDER, Constants.Folders.AllCourses, tenantId, courseId, Constants.Folders.Build, Constants.Filenames.Main), function(exists) { if (!exists || isRebuildRequired) { logger.log('info', '3.1. Ensuring framework build exists'); var args = []; var outputFolder = path.join(Constants.Folders.AllCourses, tenantId, courseId); - + // Append the 'build' folder to later versions of the framework if (semver.gte(semver.clean(version.adapt_framework), semver.clean('2.0.0'))) { outputFolder = path.join(outputFolder, Constants.Folders.Build); } - + args.push('--outputdir=' + outputFolder); args.push('--theme=' + themeName); args.push('--menu=' + menuName); @@ -162,32 +162,35 @@ AdaptOutput.prototype.publish = function (courseId, isPreview, request, response logger.log('info', '3.2. Using theme: ' + themeName); logger.log('info', '3.3. Using menu: ' + menuName); - logger.log('info', 'grunt server-build ' + args.join(' ')); - - child = exec('grunt server-build ' + args.join(' '), {cwd: path.join(FRAMEWORK_ROOT_FOLDER)}, - function (error, stdout, stderr) { - if (error !== null) { - logger.log('error', 'exec error: ' + error); - logger.log('error', 'stdout error: ' + stdout); - resultObject.success = true; - return callback(error, 'Error building framework'); - } - - if (stdout.length != 0) { - logger.log('info', 'stdout: ' + stdout); - resultObject.success = true; - return callback(null, 'Framework built OK'); - } - - if (stderr.length != 0) { - logger.log('error', 'stderr: ' + stderr); - resultObject.success = false; - return callback(stderr, 'Error (stderr) building framework!'); - } - - resultObject.success = true; - return callback(null, 'Framework built'); - }); + var generateSourcemap = outputJson.config._generateSourcemap; + var buildMode = generateSourcemap === true ? 'dev' : 'prod'; + + logger.log('info', 'grunt server-build:' + buildMode + ' ' + args.join(' ')); + + child = exec('grunt server-build:' + buildMode + ' ' + args.join(' '), {cwd: path.join(FRAMEWORK_ROOT_FOLDER)}, + function(error, stdout, stderr) { + if (error !== null) { + logger.log('error', 'exec error: ' + error); + logger.log('error', 'stdout error: ' + stdout); + resultObject.success = true; + return callback(error, 'Error building framework'); + } + + if (stdout.length != 0) { + logger.log('info', 'stdout: ' + stdout); + resultObject.success = true; + return callback(null, 'Framework built OK'); + } + + if (stderr.length != 0) { + logger.log('error', 'stderr: ' + stderr); + resultObject.success = false; + return callback(stderr, 'Error (stderr) building framework!'); + } + + resultObject.success = true; + return callback(null, 'Framework built'); + }); } else { resultObject.success = true; callback(null, 'Framework already built, nothing to do') @@ -215,14 +218,14 @@ AdaptOutput.prototype.publish = function (courseId, isPreview, request, response archive.pipe(output); archive.bulk([ - { expand: true, cwd: path.join(FRAMEWORK_ROOT_FOLDER, Constants.Folders.AllCourses, tenantId, courseId, Constants.Folders.Build), src: ['**/*'] } - ]).finalize(); + { expand: true, cwd: path.join(FRAMEWORK_ROOT_FOLDER, Constants.Folders.AllCourses, tenantId, courseId, Constants.Folders.Build), src: ['**/*'] }, + ]).finalize(); } else { // No download required -- skip this step callback(); - } - } + } + } ], function(err) { if (err) { @@ -230,9 +233,9 @@ AdaptOutput.prototype.publish = function (courseId, isPreview, request, response return next(err); } - + return next(null, resultObject); - }); + }); }; /** @@ -240,4 +243,4 @@ AdaptOutput.prototype.publish = function (courseId, isPreview, request, response * */ -exports = module.exports = AdaptOutput; \ No newline at end of file +exports = module.exports = AdaptOutput; diff --git a/routes/preview/index.js b/routes/preview/index.js index 288d200993..c52b14f485 100644 --- a/routes/preview/index.js +++ b/routes/preview/index.js @@ -56,7 +56,7 @@ server.get('/preview/:tenant/:course/*', function (req, res, next) { if (tenantId == masterTenantId) { // Viewing a preview on master courses, so check that the course is shared // Store in the session that the user has access to this course. - helpers.isMasterCourseShared(courseId, function(err, hasPermission) { + helpers.isMasterPreviewAccessible(courseId, user._id, function(err, hasPermission) { if (err) { logger.log('error', err); diff --git a/version.json b/version.json index d70285e3ff..d5e9c9d5ff 100644 --- a/version.json +++ b/version.json @@ -1,4 +1,4 @@ { - "adapt_authoring": "v0.1.2", + "adapt_authoring": "v0.1.3", "adapt_framework": "v2.0.0" }