e083ee8240920937aaba78715e956086285797a6
[xtuple] / node-datasource / routes / app.js
1 /*jshint node:true, indent:2, curly:false, eqeqeq:true, immed:true, latedef:true, newcap:true, noarg:true,
2 regexp:true, undef:true, strict:true, trailing:true, white:true */
3 /*global X:true, SYS:true, XT:true, _:true */
4
5 var async = require("async"),
6   path = require("path"),
7   routes = require("./routes");
8
9 (function () {
10   "use strict";
11
12   //
13   // Get the most recent version of the core code
14   // XXX: we'll need to sometimes give older versions
15   // @param {String} language Can be "js" or "css".
16   //
17   var getCoreUuid = function (language, organization, callback) {
18     var coll = new SYS.ClientCodeRelationCollection();
19     coll.fetch({
20       username: X.options.databaseServer.user,
21       database: organization,
22       query: {
23         parameters: [
24           { attribute: "language", value: language },
25           { attribute: "extension", value: null, includeNull: true }
26         ]
27       },
28       success: function (coll, res) {
29         var sortedModels = _.sortBy(coll.models, function (model) {
30           return -1 * getVersionSize(model.get("version"));
31         });
32         callback(null, sortedModels[0].get("uuid"));
33       }
34     });
35   };
36
37   /**
38     Just get a sense of how recent a version is without the dots.
39     Higher version number string inputs will result in higher int outputs.
40     Works with three or four dot-separated numbers.
41   */
42   var getVersionSize = function (version) {
43     var versionSplit = version.split('.'),                  // e.g. "4.5.0-beta2".
44       versionSize = 1000000 * versionSplit[0] +             // Get "4" from "4.5.0-beta2".
45         10000 * versionSplit[1] +                           // Get "5" from "4.5.0-beta2".
46         100 * versionSplit[2].match(/^[0-9]+/g, '')[0],     // Get "0" from "0-beta2".
47       prerelease = versionSplit[2].replace(/^[0-9]+/g, ''), // Get "-beta2" from "0-beta2".
48       preRegEx = /([a-zA-Z]+)([0-9]*)/g,                    // Capture pre-release as ["beta2", "beta", "2"].
49       preMatch = preRegEx.exec(prerelease),
50       preVersion,
51       preNum;
52
53     if (versionSplit.length > 3) {
54       versionSize += versionSplit[3];
55     }
56
57     if (preMatch && preMatch.length && preMatch[0] !== '') {
58       if (preMatch[1] !== '') {
59         preVersion = preMatch[1].match(/[a-zA-Z]+/g);       // Get ["beta"] from ["beta2", "beta", "2"].
60
61         // Decrease versionSize for pre-releasees.
62         switch (preVersion[0].toLowerCase()) {
63           case 'alpha':
64             versionSize = versionSize - 50;
65             break;
66           case 'beta':
67             versionSize = versionSize - 40;
68             break;
69           case 'rc':
70             versionSize = versionSize - 20;
71             break;
72           default :
73             X.err("Cannot get pre-release version number.");
74         }
75       }
76
77       // Add pre-release version to versionSize.
78       if (preMatch[2] !== '') {
79         preNum = preMatch[2].match(/[0-9]+/g);              // Get ["2"] from ["beta2", "beta", "2"].
80         versionSize = versionSize + parseInt(preNum);
81       }
82     }
83
84     return versionSize;
85   };
86
87
88   /**
89    Figures out what extensions the user is entitled to. Queries to determine
90    client code UUIDs for core and those extensions. Sends on to the app view
91    to render.
92    */
93   var serveApp = exports.serveApp = function (req, res) {
94     if (!req.session.passport.user) {
95       routes.logout(req, res);
96       return;
97     }
98
99     var user = new SYS.User(),
100       fetchError = function (err) {
101         X.log("Extension fetch error", err);
102         res.send({isError: true, message: "Error fetching extensions"});
103       },
104       fetchSuccess = function (model, result) {
105         var sendExtensions = function (res, extensions) {
106           extensions.sort(function (ext1, ext2) {
107             if (ext1.loadOrder !== ext2.loadOrder) {
108               return ext1.loadOrder - ext2.loadOrder;
109             } else {
110               return ext1.name > ext2.name ? 1 : -1;
111             }
112           });
113           var getLatestUuid = function (extensions, language) {
114             var uuids = _.map(extensions, function (ext) {
115               var jsModels = _.filter(ext.codeInfo, function (codeInfo) {
116                 return codeInfo.language === language;
117               });
118               var sortedModels = _.sortBy(jsModels, function (codeInfo) {
119                 return -1 * getVersionSize(codeInfo.version);
120               });
121               if (sortedModels[0]) {
122                 return sortedModels[0].uuid;
123               } else {
124                 X.log("Could not find js uuid for extension " + ext.description);
125                 return null;
126               }
127             });
128             return _.compact(uuids); // eliminate any null values
129           };
130           var extJsUuids = getLatestUuid(extensions, "js");
131           var extCssUuids = getLatestUuid(extensions, "css");
132           var extensionPaths = _.compact(_.map(extensions, function (ext) {
133             var locationName = ext.location.indexOf("/") === 0 ?
134               path.join(ext.location, "source") :
135               "/" + ext.location;
136             return path.join(locationName, ext.name);
137           }));
138           getCoreUuid('js', req.session.passport.user.organization, function (err, jsUuid) {
139             if (err) {
140               res.send({isError: true, error: err});
141               return;
142             }
143             getCoreUuid('css', req.session.passport.user.organization, function (err, cssUuid) {
144               if (err) {
145                 res.send({isError: true, error: err});
146                 return;
147               }
148               res.render(req.viewName || 'app', {
149                 org: req.session.passport.user.organization,
150                 coreJs: jsUuid,
151                 coreCss: cssUuid,
152                 extensionJsArray: extJsUuids,
153                 extensionCssArray: extCssUuids,
154                 extensionPaths: extensionPaths
155               });
156             });
157           });
158         };
159         var getExtensionFromRole = function (role, callback) {
160           var id = role.userAccountRole,
161             roleModel = new SYS.UserAccountRole();
162
163           roleModel.fetch({id: id,
164             username: X.options.databaseServer.user,
165             database: req.session.passport.user.organization,
166             success: function (model, result) {
167               var extensions = _.map(model.get("grantedExtensions"), function (ext) {
168                 return ext.extension;
169               });
170               callback(null, extensions);
171             },
172             error: function (err) {
173               callback(err);
174             }
175           });
176         };
177         var extensions = _.map(user.get("grantedExtensions"), function (ext) {
178           return ext.extension;
179         });
180         // Add the extensions that the user gets from his roles
181         var userAccountRoles = user.get("grantedUserAccountRoles");
182         if (userAccountRoles.length > 0) {
183           // we're going to have to do more async calls to get all the
184           // extensions related to these roles.
185           async.map(userAccountRoles, getExtensionFromRole, function (err, results) {
186             if (err) {
187               res.send({isError: true, message: "error in extension route"});
188               return;
189             }
190             // add all role-granted extensions that are not already there
191             _.each(results, function (result) {
192               _.each(result, function (newExt) {
193                 var preExistingExt = _.find(extensions, function (currentExt) {
194                   return currentExt.id === newExt.id;
195                 });
196                 if (!preExistingExt) {
197                   extensions.push(newExt);
198                 }
199               });
200             });
201             sendExtensions(res, extensions);
202           });
203
204         } else {
205           // no second async call necessary
206           sendExtensions(res, extensions);
207         }
208       };
209
210     user.fetch({
211       id: req.session.passport.user.username,
212       success: fetchSuccess,
213       error: fetchError,
214       username: X.options.databaseServer.user,
215       database: req.session.passport.user.organization
216     });
217   };
218
219   exports.serveDebug = function (req, res) {
220     req.viewName = "debug";
221     serveApp(req, res);
222   };
223 }());