From b72bbe1a4f81c54a6d18d33972e22f949aa54b6c Mon Sep 17 00:00:00 2001 From: Lucas Constantino Date: Mon, 14 Jul 2014 23:07:12 -0300 Subject: [PATCH 01/14] Created hook responseAlter to allow changing a response right before it is sent. --- .../extensions/route/lib/route-controller.js | 27 ++++++++++--------- .../default/extensions/route/route.js | 2 +- 2 files changed, 16 insertions(+), 13 deletions(-) diff --git a/applications/default/extensions/route/lib/route-controller.js b/applications/default/extensions/route/lib/route-controller.js index 8546fc7..db55b7a 100644 --- a/applications/default/extensions/route/lib/route-controller.js +++ b/applications/default/extensions/route/lib/route-controller.js @@ -41,33 +41,33 @@ RouteController.prototype.handle = function(request, response) { this.access(request, response, function(err, allow) { if (err) { - RouteController.error(request, response, err); + RouteController.error.call(self, request, response, err); } if (allow) { // A route can have either a content or callback property. if (settings.content) { - RouteController.respond(request, response, settings.content); + RouteController.respond.call(self, request, response, settings.content); } else if (settings.callback) { settings.callback(request, response, function(err, content, code) { if (err) { - RouteController.error(request, response, err); + RouteController.error.call(self, request, response, err); } if (content) { - RouteController.respond(request, response, content, code); + RouteController.respond.call(self, request, response, content, code); } else { // If there's no content, return 404 error. - RouteController.notFound(request, response); + RouteController.notFound.call(self, request, response); } }); } } else { // Access denied. - RouteController.forbidden(request, response); + RouteController.forbidden.call(self, request, response); } }); }; @@ -110,9 +110,9 @@ RouteController.prototype.access = function(request, response, callback) { * @param {Number} [code] HTTP status code. */ RouteController.respond = function(request, response, content, code) { - // Default to 200 (success). - var code = code || 200; + // Set defaults. + code = typeof code != 'undefined' ? code : 200; var payload = { status: { code: code @@ -123,7 +123,10 @@ RouteController.respond = function(request, response, content, code) { payload.data = content; } - response.send(code, payload); + // Run responseAlter() hook on all extensions. + this.application.invoke('responseAlter', payload, request, response, function() { + response.send(payload.status.code, responseData.payload); + }); }; /** @@ -133,7 +136,7 @@ RouteController.respond = function(request, response, content, code) { * @param {Response} response Response object. */ RouteController.error = function(request, response, error) { - RouteController.respond(request, response, { + RouteController.respond.call(this, request, response, { title: 'Server error', description: "The server couldn't process the request.", error: error.toString() @@ -147,7 +150,7 @@ RouteController.error = function(request, response, error) { * @param {Response} response Response object. */ RouteController.notFound = function(request, response) { - RouteController.respond(request, response, { + RouteController.respond.call(this, request, response, { title: 'Page not found', description: "The page you're looking for wasn't found." }, 404); @@ -160,7 +163,7 @@ RouteController.notFound = function(request, response) { * @param {Response} response Response object. */ RouteController.forbidden = function(request, response) { - RouteController.respond(request, response, { + RouteController.respond.call(this, request, response, { title: 'Forbidden', description: "You don't have permission to access this page." }, 403); diff --git a/applications/default/extensions/route/route.js b/applications/default/extensions/route/route.js index b550653..ec1ca9b 100644 --- a/applications/default/extensions/route/route.js +++ b/applications/default/extensions/route/route.js @@ -25,7 +25,7 @@ route.init = function(application, callback) { // The last middleware is the one that catches 404 errors. self.application.application.use(function(req, res, next){ - RouteController.notFound(req, res); + RouteController.notFound.call(self, req, res); }); callback(); From 437a4e1ad65a20691459113e4cccd4e07803e57a Mon Sep 17 00:00:00 2001 From: Lucas Constantino Date: Mon, 14 Jul 2014 23:08:40 -0300 Subject: [PATCH 02/14] Fixed wrong variable usage. --- applications/default/extensions/route/lib/route-controller.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/applications/default/extensions/route/lib/route-controller.js b/applications/default/extensions/route/lib/route-controller.js index db55b7a..f8a596d 100644 --- a/applications/default/extensions/route/lib/route-controller.js +++ b/applications/default/extensions/route/lib/route-controller.js @@ -125,7 +125,7 @@ RouteController.respond = function(request, response, content, code) { // Run responseAlter() hook on all extensions. this.application.invoke('responseAlter', payload, request, response, function() { - response.send(payload.status.code, responseData.payload); + response.send(payload.status.code, payload); }); }; From 98d7126d41ed37b9a8c29ece0dc29caa163b4694 Mon Sep 17 00:00:00 2001 From: Lucas Constantino Date: Mon, 14 Jul 2014 20:35:06 -0300 Subject: [PATCH 03/14] Altered layout responses to send emptneess information about regions. --- .../default/extensions/panel/panel.js | 49 ++++++++++++++++++- 1 file changed, 47 insertions(+), 2 deletions(-) diff --git a/applications/default/extensions/panel/panel.js b/applications/default/extensions/panel/panel.js index 83a4753..0acc756 100644 --- a/applications/default/extensions/panel/panel.js +++ b/applications/default/extensions/panel/panel.js @@ -199,19 +199,24 @@ panel.contextReactionType = function(reactionTypes, callback) { } }, react: function(request, response, regionPanels, callback) { + + // Get panel type. + var Panel = self.application.type('panel'); + // Initialize panels container if not initialized yet. response.payload.panels = response.payload.panels || {}; // Add all panels to response payload. async.each(Object.keys(regionPanels), function(regionName, next) { + // Initialize panels container if not initialized yet. response.payload.panels[regionName] = response.payload.panels[regionName] || []; var panels = regionPanels[regionName]; + // A context can set several panels for a region. async.each(panels, function(regionPanel, next) { - // Load the panel. - var Panel = self.application.type('panel'); + // Load the panel. Panel.load(regionPanel.name, function(err, panel) { if (err) { return callback(err); @@ -239,3 +244,43 @@ panel.contextReactionType = function(reactionTypes, callback) { callback(null, newReactionTypes); }; + +/** + * The responseAlter() hook. + */ +panel.responseAlter = function (data, callback) { + + var payload = data.payload || {}; + var layout = payload.data && payload.data.layout; + var panels = payload.data && payload.data.panels; + + if (layout && panels) { + + /** + * Recursively verifies if a row/column is empty. + */ + function findContent(region, childType) { + + // Consider empty by default. + region.empty = true; + + if (region.region == true) { + region.empty = !Boolean(panels[region.name] && panels[region.name].length); + } + // Iterate recursively + else if (region[childType] && region[childType].length) { + region[childType].forEach(function (childRegion) { + var childEmpty = findContent(childRegion, childType == 'rows' ? 'columns' : 'rows'); + region.empty = !region.empty ? region.empty : childEmpty; + }); + } + + return region.empty; + } + + // As a layout contains rows, it behaves much like a column itself. + findContent(layout, 'rows'); + } + + callback(); +}; From 414840be79d0754f89bd71fafe9ecead86ad0d01 Mon Sep 17 00:00:00 2001 From: Lucas Constantino Date: Mon, 14 Jul 2014 20:48:13 -0300 Subject: [PATCH 04/14] Handle regions that are both panel-region and wrappers. --- applications/default/extensions/panel/panel.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/applications/default/extensions/panel/panel.js b/applications/default/extensions/panel/panel.js index 0acc756..9defae2 100644 --- a/applications/default/extensions/panel/panel.js +++ b/applications/default/extensions/panel/panel.js @@ -264,11 +264,13 @@ panel.responseAlter = function (data, callback) { // Consider empty by default. region.empty = true; + // Check if region has content. if (region.region == true) { region.empty = !Boolean(panels[region.name] && panels[region.name].length); } + // Iterate recursively - else if (region[childType] && region[childType].length) { + if (region[childType] && region[childType].length) { region[childType].forEach(function (childRegion) { var childEmpty = findContent(childRegion, childType == 'rows' ? 'columns' : 'rows'); region.empty = !region.empty ? region.empty : childEmpty; From 7cab975f9ca87249d45f9212177f7a4a01e22241 Mon Sep 17 00:00:00 2001 From: Lucas Constantino Date: Mon, 14 Jul 2014 20:48:41 -0300 Subject: [PATCH 05/14] Updated templates to be aware of empty regions. --- .../default/extensions/layout/public/templates/column.html | 4 ++-- .../default/extensions/layout/public/templates/layout.html | 2 +- .../default/extensions/layout/public/templates/row.html | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/applications/default/extensions/layout/public/templates/column.html b/applications/default/extensions/layout/public/templates/column.html index bd722e0..569ee3d 100644 --- a/applications/default/extensions/layout/public/templates/column.html +++ b/applications/default/extensions/layout/public/templates/column.html @@ -1,3 +1,3 @@ -
+
-
+
diff --git a/applications/default/extensions/layout/public/templates/layout.html b/applications/default/extensions/layout/public/templates/layout.html index 7afa99c..c405cf3 100644 --- a/applications/default/extensions/layout/public/templates/layout.html +++ b/applications/default/extensions/layout/public/templates/layout.html @@ -1,2 +1,2 @@ -
+
diff --git a/applications/default/extensions/layout/public/templates/row.html b/applications/default/extensions/layout/public/templates/row.html index eeac641..63c5836 100644 --- a/applications/default/extensions/layout/public/templates/row.html +++ b/applications/default/extensions/layout/public/templates/row.html @@ -1,5 +1,5 @@
-
+
-
+
From 6a606e7441b63c613aea4f737e7174075668c23b Mon Sep 17 00:00:00 2001 From: Lucas Constantino Date: Mon, 14 Jul 2014 23:11:44 -0300 Subject: [PATCH 06/14] Updated panel.responseAlter hook usage. --- applications/default/extensions/panel/panel.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/applications/default/extensions/panel/panel.js b/applications/default/extensions/panel/panel.js index 9defae2..aafd71a 100644 --- a/applications/default/extensions/panel/panel.js +++ b/applications/default/extensions/panel/panel.js @@ -248,9 +248,8 @@ panel.contextReactionType = function(reactionTypes, callback) { /** * The responseAlter() hook. */ -panel.responseAlter = function (data, callback) { +panel.responseAlter = function (payload, request, response, callback) { - var payload = data.payload || {}; var layout = payload.data && payload.data.layout; var panels = payload.data && payload.data.panels; From 55f0d1981d22dd92217da43c5c584a264e5f6bfc Mon Sep 17 00:00:00 2001 From: Lucas Constantino Date: Mon, 14 Jul 2014 23:44:30 -0300 Subject: [PATCH 07/14] Allow for a region to always be visible. --- applications/default/extensions/panel/panel.js | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/applications/default/extensions/panel/panel.js b/applications/default/extensions/panel/panel.js index aafd71a..7b26008 100644 --- a/applications/default/extensions/panel/panel.js +++ b/applications/default/extensions/panel/panel.js @@ -260,11 +260,12 @@ panel.responseAlter = function (payload, request, response, callback) { */ function findContent(region, childType) { - // Consider empty by default. - region.empty = true; + // Consider empty by default, unless when region it told to always be + // visible. + region.empty = region.alwaysVisible || true; // Check if region has content. - if (region.region == true) { + if (region.empty && region.region == true) { region.empty = !Boolean(panels[region.name] && panels[region.name].length); } From 5b7d903b997f6bc4aa138db3102366565285ee6c Mon Sep 17 00:00:00 2001 From: Lucas Constantino Date: Wed, 16 Jul 2014 15:08:38 -0300 Subject: [PATCH 08/14] Minor change to roll back code to what it looked before. --- .../default/extensions/route/lib/route-controller.js | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/applications/default/extensions/route/lib/route-controller.js b/applications/default/extensions/route/lib/route-controller.js index f8a596d..44c900c 100644 --- a/applications/default/extensions/route/lib/route-controller.js +++ b/applications/default/extensions/route/lib/route-controller.js @@ -110,9 +110,8 @@ RouteController.prototype.access = function(request, response, callback) { * @param {Number} [code] HTTP status code. */ RouteController.respond = function(request, response, content, code) { - - // Set defaults. - code = typeof code != 'undefined' ? code : 200; + // Default to 200 (success). + var code = code || 200; var payload = { status: { code: code From 63e8a5e198949025ed2032733d2a5194509b79db Mon Sep 17 00:00:00 2001 From: Lucas Constantino Date: Thu, 17 Jul 2014 19:05:05 -0300 Subject: [PATCH 09/14] Renamed hook from responseAlter to response. --- applications/default/extensions/route/lib/route-controller.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/applications/default/extensions/route/lib/route-controller.js b/applications/default/extensions/route/lib/route-controller.js index 44c900c..fa9482e 100644 --- a/applications/default/extensions/route/lib/route-controller.js +++ b/applications/default/extensions/route/lib/route-controller.js @@ -122,8 +122,8 @@ RouteController.respond = function(request, response, content, code) { payload.data = content; } - // Run responseAlter() hook on all extensions. - this.application.invoke('responseAlter', payload, request, response, function() { + // Run response() hook on all extensions. + this.application.invoke('response', payload, request, response, function() { response.send(payload.status.code, payload); }); }; From 25c612837fa5c708b62d056a050ac6ded091e931 Mon Sep 17 00:00:00 2001 From: Lucas Constantino Date: Thu, 17 Jul 2014 19:28:12 -0300 Subject: [PATCH 10/14] Changed variable name from alwaysVisible to alwaysRender, to avoid confusion. --- applications/default/extensions/panel/panel.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/applications/default/extensions/panel/panel.js b/applications/default/extensions/panel/panel.js index 7b26008..b7c716b 100644 --- a/applications/default/extensions/panel/panel.js +++ b/applications/default/extensions/panel/panel.js @@ -260,9 +260,9 @@ panel.responseAlter = function (payload, request, response, callback) { */ function findContent(region, childType) { - // Consider empty by default, unless when region it told to always be - // visible. - region.empty = region.alwaysVisible || true; + // Consider empty by default, unless when region is told to always be + // rendered. + region.empty = region.alwaysRender || true; // Check if region has content. if (region.empty && region.region == true) { From 77e02b1ae330b26857995d14008a4ea123332ecd Mon Sep 17 00:00:00 2001 From: Lucas Constantino Date: Thu, 17 Jul 2014 19:28:42 -0300 Subject: [PATCH 11/14] Added alwaysRender field to the regions. --- applications/default/extensions/layout/layout.js | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/applications/default/extensions/layout/layout.js b/applications/default/extensions/layout/layout.js index e71ffc7..b76b6b9 100644 --- a/applications/default/extensions/layout/layout.js +++ b/applications/default/extensions/layout/layout.js @@ -80,6 +80,10 @@ layout.type = function(types, callback) { region: { title: 'Region', type: 'boolean' + }, + alwaysRender: { + title: 'Always Render', + type: 'boolean' } } }; @@ -111,6 +115,10 @@ layout.type = function(types, callback) { region: { title: 'Region', type: 'boolean' + }, + alwaysRender: { + title: 'Always Render', + type: 'boolean' } } }; From a789b4b6486dabb43de2bf6ec349b3a846ac06aa Mon Sep 17 00:00:00 2001 From: Lucas Constantino Date: Fri, 18 Jul 2014 18:19:32 -0300 Subject: [PATCH 12/14] Fixed wrong validation of alwaysRender option. --- applications/default/extensions/panel/panel.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/applications/default/extensions/panel/panel.js b/applications/default/extensions/panel/panel.js index b7c716b..71610b4 100644 --- a/applications/default/extensions/panel/panel.js +++ b/applications/default/extensions/panel/panel.js @@ -262,7 +262,7 @@ panel.responseAlter = function (payload, request, response, callback) { // Consider empty by default, unless when region is told to always be // rendered. - region.empty = region.alwaysRender || true; + region.empty = region.alwaysRender ? false : true; // Check if region has content. if (region.empty && region.region == true) { From 35c13e45e354f34131f6134ddf9b42792622c7b5 Mon Sep 17 00:00:00 2001 From: Lucas Constantino Date: Fri, 18 Jul 2014 18:24:16 -0300 Subject: [PATCH 13/14] Modified name to match new response hook name. --- applications/default/extensions/panel/panel.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/applications/default/extensions/panel/panel.js b/applications/default/extensions/panel/panel.js index 71610b4..e773c64 100644 --- a/applications/default/extensions/panel/panel.js +++ b/applications/default/extensions/panel/panel.js @@ -246,9 +246,9 @@ panel.contextReactionType = function(reactionTypes, callback) { }; /** - * The responseAlter() hook. + * The response() hook. */ -panel.responseAlter = function (payload, request, response, callback) { +panel.response = function (payload, request, response, callback) { var layout = payload.data && payload.data.layout; var panels = payload.data && payload.data.panels; From 4608e4c2e36cb28e2991faf6201778afde6d87d7 Mon Sep 17 00:00:00 2001 From: Henrique Recidive Date: Fri, 29 Aug 2014 15:46:12 -0300 Subject: [PATCH 14/14] Introducing response() hook to provide a last opportunity for extensions to alter the response before it's sent to the client. Fixes #133. Conflicts: applications/default/extensions/route/lib/route-controller.js --- .../extensions/route/lib/route-controller.js | 67 ++++++++++--------- 1 file changed, 36 insertions(+), 31 deletions(-) diff --git a/applications/default/extensions/route/lib/route-controller.js b/applications/default/extensions/route/lib/route-controller.js index fa9482e..c2afc33 100644 --- a/applications/default/extensions/route/lib/route-controller.js +++ b/applications/default/extensions/route/lib/route-controller.js @@ -39,35 +39,35 @@ RouteController.prototype.handle = function(request, response) { var self = this; var settings = this.settings; - this.access(request, response, function(err, allow) { - if (err) { - RouteController.error.call(self, request, response, err); - } + // Response decorator function, used to allow extensions to alter the response + // for content responses via response() hook. + var responseDecorator = function(payload, request, response, callback) { + // Run response() hook on all extensions. + self.application.invoke('response', payload, request, response, callback); + }; - if (allow) { - // A route can have either a content or callback property. - if (settings.content) { - RouteController.respond.call(self, request, response, settings.content); - } - else if (settings.callback) { - settings.callback(request, response, function(err, content, code) { - if (err) { - RouteController.error.call(self, request, response, err); - } - - if (content) { - RouteController.respond.call(self, request, response, content, code); - } - else { - // If there's no content, return 404 error. - RouteController.notFound.call(self, request, response); - } - }); - } + this.access(request, response, function(error, allow) { + if (error) { + return RouteController.error(request, response, error); } - else { + + if (allow !== true) { // Access denied. - RouteController.forbidden.call(self, request, response); + return RouteController.forbidden(request, response); + } + + if (settings.content) { + return RouteController.respond(request, response, settings.content, responseDecorator); + } + + if (settings.callback) { + return settings.callback(request, response, function(error, content, code) { + if (error) { + return RouteController.error(request, response, error); + } + + RouteController.respond(request, response, content, code, responseDecorator); + }); } }); }; @@ -108,8 +108,10 @@ RouteController.prototype.access = function(request, response, callback) { * @param {Response} response Response object. * @param {Object|String} content Content to send. * @param {Number} [code] HTTP status code. + * @param {Function} [decorator] A function to run on the response data before + * sending it. */ -RouteController.respond = function(request, response, content, code) { +RouteController.respond = function(request, response, content, code, decorator) { // Default to 200 (success). var code = code || 200; var payload = { @@ -122,10 +124,13 @@ RouteController.respond = function(request, response, content, code) { payload.data = content; } - // Run response() hook on all extensions. - this.application.invoke('response', payload, request, response, function() { - response.send(payload.status.code, payload); - }); + if (decorator) { + return decorator(payload, request, response, function() { + response.status(payload.status.code).send(payload); + }); + } + + response.status(code).send(payload); }; /**