diff --git a/application/controllers/ModulesController.php b/application/controllers/ModulesController.php index f8f40a3b3..afd3b9178 100755 --- a/application/controllers/ModulesController.php +++ b/application/controllers/ModulesController.php @@ -35,7 +35,11 @@ class ModulesController extends ActionController public function enableAction() { $this->manager->enableModule($this->_getParam('name')); - $this->redirectNow('modules/overview?_render=body'); + $this->manager->loadModule($this->_getParam('name')); + $this->getResponse()->setHeader('X-Icinga-Enable-Module', $this->_getParam('name')); + $this->replaceLayout = true; + $this->indexAction(); + } public function disableAction() diff --git a/public/js/icinga/icinga.js b/public/js/icinga/icinga.js index ff59321f6..d538ab552 100755 --- a/public/js/icinga/icinga.js +++ b/public/js/icinga/icinga.js @@ -19,16 +19,19 @@ define([ var failedModules = []; var initialize = function () { + registerLazyModuleLoading(); enableInternalModules(); - + containerMgr.registerAsyncMgr(async); containerMgr.initializeContainers(document); log.debug("Initialization finished"); enableModules(); }; - + var registerLazyModuleLoading = function() { + async.registerHeaderListener("X-Icinga-Enable-Module", loadModuleScript, this); + }; var enableInternalModules = function() { $.each(internalModules,function(idx,module) { @@ -37,6 +40,7 @@ define([ }; var loadModuleScript = function(name) { + console.log("Loading ", name); moduleMgr.enableModule("modules/"+name+"/"+name, function(error) { failedModules.push({ name: name, diff --git a/public/js/icinga/util/async.js b/public/js/icinga/util/async.js index 15a6be7df..1b564b176 100644 --- a/public/js/icinga/util/async.js +++ b/public/js/icinga/util/async.js @@ -3,7 +3,8 @@ "use strict"; var asyncMgrInstance = null; - define(['icinga/container','logging','icinga/behaviour','jquery'],function(containerMgr,log,behaviour,$) { + define(['icinga/container','logging','jquery'],function(containerMgr,log,$) { + var headerListeners = {}; var pending = { @@ -18,12 +19,30 @@ return target; }; - var handleResponse = function(html) { + var applyHeaderListeners = function(headers) { + for (var header in headerListeners) { + if (headers.getResponseHeader(header) === null) { + // see if the browser/server converts headers to lowercase + if (headers.getResponseHeader(header.toLowerCase()) === null) { + continue; + } + header = header.toLowerCase(); + } + var value = headers.getResponseHeader(header); + var listeners = headerListeners[header]; + for (var i=0;i")); $('a.test').click(); @@ -223,4 +226,55 @@ describe('The icinga module bootstrap', function() { should.equal(icinga.getFailedModules()[0].name, "test2"); tearDownTestDOM(); }); + + it("Should load modules lazily when discovering a X-Icinga-Enable-Module header", function() { + rjsmock.purgeDependencies(); + + requireNew("icinga/util/async.js"); + var async = rjsmock.getDefine(); + + rjsmock.registerDependencies({ + "icinga/module": module, + "icinga/util/async": async, + "modules/test/test" : { + eventHandler: { + "a.test" : { + click : function() { + testClick = true; + } + } + } + }, + "icinga/container" : { + registerAsyncMgr: function() {}, + initializeContainers: function() {} + }, + "modules/list" : [ + ] + }); + + tearDownTestDOM(); + + requireNew("icinga/icinga.js"); + var icinga = rjsmock.getDefine(); + + var testClick = false; + // The module shouldn't be loaded + $('body').append($("")); + $('a.test').click(); + should.equal(testClick, false, "Unregistered module was loaded"); + + asyncMock.setNextAsyncResult(async,"result", false, { + "X-Icinga-Enable-Module" : "test" + }); + async.createRequest(); + // The module shouldn't be loaded + $('body').append($("")); + $('a.test').click(); + should.equal(testClick, true, "Module wasn't automatically loaded on header!"); + + + tearDownTestDOM(); + + }); }); diff --git a/test/js/testlib/asyncmock.js b/test/js/testlib/asyncmock.js new file mode 100644 index 000000000..aad6d5eea --- /dev/null +++ b/test/js/testlib/asyncmock.js @@ -0,0 +1,33 @@ +/** + * Helper for mocking $.async's XHR requests + * + */ + + +var getCallback = function(empty, response, succeed, headers) { + if (empty) + return function() {}; + return function(callback) { + callback(response, succeed, { + getAllResponseHeaders: function() { + return headers; + }, + getResponseHeader: function(header) { + return headers[header] || null; + } + }); + }; +}; + +module.exports = { + setNextAsyncResult: function(async, response, fails, headers) { + headers = headers || {}; + var succeed = fails ? "fail" : "success"; + async.__internalXHRImplementation = function(config) { + return { + done: getCallback(fails, response, succeed, headers), + fail: getCallback(!fails, response, succeed, headers) + }; + }; + } +}; diff --git a/test/js/testlib/requiremock.js b/test/js/testlib/requiremock.js index 8181092da..e61b08694 100644 --- a/test/js/testlib/requiremock.js +++ b/test/js/testlib/requiremock.js @@ -13,6 +13,7 @@ * to console. * **/ +var path = require('path'); var registeredDependencies = {}; /** @@ -21,11 +22,13 @@ var registeredDependencies = {}; * in dependencies and calls fn with them as the parameter * **/ +var debug = false; var requireJsMock = function(dependencies, fn) { var fnArgs = []; for (var i=0;i