Merge pull request #1738 from jgunderson/money2
authorLinda Nichols <lynnaloo@gmail.com>
Tue, 12 Aug 2014 16:56:46 +0000 (12:56 -0400)
committerLinda Nichols <lynnaloo@gmail.com>
Tue, 12 Aug 2014 16:56:46 +0000 (12:56 -0400)
24106, number spinner issues

26 files changed:
.travis.yml
enyo-client/application/source/ext/session.js
enyo-client/application/source/preliminaries.js
enyo-client/database/source/update_version.sql
foundation-database/public/functions/postcashreceipt.sql
foundation-database/public/tables/metric.sql
node-datasource/main.js
npm-shrinkwrap.json
package.json
scripts/explode_manifest.js
scripts/lib/build_all.js
scripts/lib/build_client.js
scripts/lib/build_database.js
scripts/lib/build_database_util.js [deleted file]
scripts/lib/util/convert_specialized.js [new file with mode: 0644]
scripts/lib/util/default_extensions.js [new file with mode: 0644]
scripts/lib/util/init_database.js [new file with mode: 0644]
scripts/lib/util/inspect_database.js [new file with mode: 0644]
scripts/lib/util/process_manifest.js [new file with mode: 0644]
scripts/lib/util/send_to_database.js [new file with mode: 0644]
scripts/lib/util/unregister.js [new file with mode: 0644]
scripts/xml/distribution_install.xml
scripts/xml/distribution_package.xml
scripts/xml/postbooks_package.xml
scripts/xml/xtmfg_install.xml
scripts/xml/xtmfg_package.xml

index 7d0d3a4..387c683 100644 (file)
@@ -14,7 +14,6 @@ before_script:
 script:
   - "npm run-script test-datasource"
   - "npm run-script test"
-  - "npm run-script jshint"
 
   # test an upgrade from 4.4.0
   - "wget http://sourceforge.net/projects/postbooks/files/03%20PostBooks-databases/4.4.0/postbooks_demo-4.4.0.backup"
index 46cef52..65a80c2 100644 (file)
@@ -22,6 +22,8 @@ white:true*/
     },
 
     _didValidateSession: function (payload, callback) {
+      var coreVersion;
+
       if (payload.code === 1) {
         // If this is a valid session acquisition, go ahead
         // and store the database config details in
@@ -30,10 +32,28 @@ white:true*/
         this.setConfig(payload);
         this.setDetails(payload.data);
 
-        if (payload.version && XT.setVersion) {
+        if (payload.versions && XT.setVersion) {
           // announce to the client what our version is, if we have
           // a way of doing it.
-          XT.setVersion(payload.version);
+
+          _.each(payload.versions,  function (version, extensionName) {
+            // default to the core version (temp until all core extensions are in npm)
+            if (extensionName === "core") {
+              coreVersion = version;
+              extensionName = "";
+            } else if (version === "none") {
+              version = coreVersion;
+            }
+
+            var aboutVersionLabel = XT.app.$.postbooks.$.navigator.$.aboutVersion,
+              versionText = extensionName + " " + "_version".loc().toLowerCase() + " " + version;
+
+            if (aboutVersionLabel.getContent()) {
+              versionText = aboutVersionLabel.getContent() + "<br>" + versionText;
+            }
+
+            aboutVersionLabel.setContent(versionText);
+          });
         }
 
         // Start the client loading process.
index 813e23f..9e75865 100644 (file)
@@ -26,20 +26,8 @@ XT = typeof XT !== 'undefined' ? XT : {};
   };
 
   XT.setVersion = function (version, qualifier) {
-    // default to the core version
-    version = version || XT.session.config.version;
-
-    var aboutVersionLabel = XT.app.$.postbooks.$.navigator.$.aboutVersion,
-      versionText = "_version".loc() + " " + version;
-
-    if (qualifier) {
-      versionText = ("_" + qualifier).loc() + " " + versionText;
-    }
-    if (aboutVersionLabel.getContent()) {
-      versionText = aboutVersionLabel.getContent() + "<br>" + versionText;
-    }
-
-    aboutVersionLabel.setContent(versionText);
+    XT.log("XT.setVersion is now deprecated. The app now reads extension versions from " +
+      "package.json or manifest.js (" + qualifier + ")");
   };
 
 }());
index 6458648..a23ed94 100644 (file)
@@ -1 +1 @@
-UPDATE pkghead SET pkghead_version = '4.6.0' WHERE pkghead_name = 'xt';
+UPDATE pkghead SET pkghead_version = '4.7.0Beta' WHERE pkghead_name = 'xt';
index b6f20b2..f132e8f 100644 (file)
@@ -336,7 +336,8 @@ BEGIN
 
   -- Post any gain/loss from the alternate currency exchange rate
   IF (COALESCE(_p.cashrcpt_alt_curr_rate, 0.0) <> 0.0) THEN
-    _exchGain := ROUND((_p.cashrcpt_curr_rate - _p.cashrcpt_alt_curr_rate) * _p.cashrcpt_amount_base, 2);
+    _exchGain := ROUND((_p.cashrcpt_amount / _p.cashrcpt_alt_curr_rate) -
+                       (_p.cashrcpt_amount / _p.cashrcpt_curr_rate), 2);
 
     IF (_exchGain <> 0) THEN
       PERFORM insertIntoGLSeries( _sequence, 'A/R', 'CR',
index 9e39884..bf8d03a 100644 (file)
@@ -1 +1 @@
-SELECT setMetric('ServerVersion', '4.6.0');
+SELECT setMetric('ServerVersion', '4.7.0Beta');
index 25801b5..7eec11e 100755 (executable)
@@ -114,9 +114,15 @@ var app;
     var extensionLocation = extension.location === "npm" ? extension.location : extension.location + "/source";
     useClientDir(extensionLocation + "/" + extension.name + "/client", X.path.join(getExtensionDir(extension), "client"));
   };
-  var loadExtensionRoutes = function (extension) {
-    var manifest = JSON.parse(X.fs.readFileSync(X.path.join(getExtensionDir(extension),
-        "database/source/manifest.js")));
+  var loadExtensionServerside = function (extension) {
+    var packagePath = X.path.join(getExtensionDir(extension), "package.json");
+    var packageJson = X.fs.existsSync(packagePath) ? require(packagePath) : undefined;
+    var manifestPath = X.path.join(getExtensionDir(extension), "database/source/manifest.js");
+    var manifest = X.fs.existsSync(manifestPath) ? JSON.parse(X.fs.readFileSync(manifestPath)) : {};
+    var version = packageJson ? packageJson.version : manifest.version;
+    X.versions[extension.name] = version || "none"; // XXX the "none" is temporary until we have core extensions in npm
+
+    // TODO: be able to define routes in package.json
     _.each(manifest.routes || [], function (routeDetails) {
       var verb = (routeDetails.verb || "all").toLowerCase(),
         func = require(X.path.join(getExtensionDir(extension),
@@ -149,7 +155,7 @@ var app;
           return;
         }
         useClientDir("/client", "../enyo-client/application");
-        _.each(results, loadExtensionRoutes);
+        _.each(results, loadExtensionServerside);
         _.each(results, loadExtensionClientside);
       }
     });
@@ -168,11 +174,9 @@ var app;
  */
 
 var packageJson = X.fs.readFileSync("../package.json");
-try {
-  X.version = JSON.parse(packageJson).version;
-} catch (error) {
-
-}
+X.versions = {
+  core: JSON.parse(packageJson).version
+};
 
 /**
  * Module dependencies.
@@ -638,10 +642,9 @@ io.of('/clientsock').authorization(function (handshakeData, callback) {
           data: session.passport.user,
           code: 1,
           debugging: X.options.datasource.debugging,
-          biAvailable: _.isObject(X.options.biServer) && !_.isEmpty(X.options.biServer),
           emailAvailable: _.isString(X.options.datasource.smtpHost) && X.options.datasource.smtpHost !== "",
           printAvailable: _.isString(X.options.datasource.printer) && X.options.datasource.printer !== "",
-          version: X.version
+          versions: X.versions
         });
       callback(callbackObj);
     }, data && data.payload);
index 1a94b39..ea6f324 100644 (file)
@@ -1,50 +1,50 @@
 {
   "name": "xtuple",
-  "version": "4.4.1",
+  "version": "4.6.0",
   "dependencies": {
     "async": {
       "version": "0.2.10",
-      "from": "async@0.2.x"
+      "from": "async@0.2.10"
     },
     "backbone": {
       "version": "0.9.10",
-      "from": "backbone@0.9.10",
+      "from": "https://registry.npmjs.org/backbone/-/backbone-0.9.10.tgz",
       "resolved": "https://registry.npmjs.org/backbone/-/backbone-0.9.10.tgz"
     },
     "backbone-relational": {
       "version": "0.8.0",
-      "from": "backbone-relational@0.8.0",
+      "from": "https://registry.npmjs.org/backbone-relational/-/backbone-relational-0.8.0.tgz",
       "resolved": "https://registry.npmjs.org/backbone-relational/-/backbone-relational-0.8.0.tgz"
     },
     "bcrypt": {
       "version": "0.7.8",
-      "from": "bcrypt@0.7.x",
+      "from": "https://registry.npmjs.org/bcrypt/-/bcrypt-0.7.8.tgz",
       "resolved": "https://registry.npmjs.org/bcrypt/-/bcrypt-0.7.8.tgz",
       "dependencies": {
         "bindings": {
           "version": "1.0.0",
-          "from": "bindings@1.0.0",
+          "from": "https://registry.npmjs.org/bindings/-/bindings-1.0.0.tgz",
           "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.0.0.tgz"
         }
       }
     },
     "colors": {
       "version": "0.6.2",
-      "from": "colors@0.6.x"
+      "from": "colors@0.6.2"
     },
     "commander": {
       "version": "1.2.0",
-      "from": "commander@1.2.x",
+      "from": "commander@1.2.0",
       "dependencies": {
         "keypress": {
           "version": "0.1.0",
-          "from": "keypress@0.1.x"
+          "from": "keypress@0.1.0"
         }
       }
     },
     "congruence": {
       "version": "1.2.9",
-      "from": "congruence@~1.2.4",
+      "from": "congruence@1.2.9",
       "dependencies": {
         "moment": {
           "version": "2.5.1",
     },
     "connect-ensure-login": {
       "version": "0.1.1",
-      "from": "connect-ensure-login@0.1.x"
+      "from": "connect-ensure-login@0.1.1"
     },
     "ejs": {
       "version": "0.8.8",
-      "from": "ejs@0.8.x"
+      "from": "ejs@0.8.8"
     },
     "express": {
       "version": "3.1.2",
-      "from": "express@3.1.x",
+      "from": "express@3.1.2",
       "dependencies": {
         "connect": {
           "version": "2.7.5",
@@ -80,7 +80,7 @@
             },
             "formidable": {
               "version": "1.0.11",
-              "from": "formidable@1.0.11",
+              "from": "https://registry.npmjs.org/formidable/-/formidable-1.0.11.tgz",
               "resolved": "https://registry.npmjs.org/formidable/-/formidable-1.0.11.tgz"
             },
             "buffer-crc32": {
         },
         "mkdirp": {
           "version": "0.3.5",
-          "from": "mkdirp@~0.3.4"
+          "from": "mkdirp@0.3.5"
         },
         "cookie": {
           "version": "0.0.5",
         },
         "buffer-crc32": {
           "version": "0.2.1",
-          "from": "buffer-crc32@~0.2.1"
+          "from": "buffer-crc32@0.2.1"
         },
         "fresh": {
           "version": "0.1.0",
         },
         "send": {
           "version": "0.1.0",
-          "from": "send@0.1.0",
+          "from": "https://registry.npmjs.org/send/-/send-0.1.0.tgz",
           "resolved": "https://registry.npmjs.org/send/-/send-0.1.0.tgz",
           "dependencies": {
             "mime": {
               "version": "1.2.6",
-              "from": "mime@1.2.6",
+              "from": "https://registry.npmjs.org/mime/-/mime-1.2.6.tgz",
               "resolved": "https://registry.npmjs.org/mime/-/mime-1.2.6.tgz"
             }
           }
         },
         "cookie-signature": {
           "version": "1.0.0",
-          "from": "cookie-signature@1.0.0",
+          "from": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.0.tgz",
           "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.0.tgz"
         },
         "debug": {
           "version": "0.8.1",
-          "from": "debug@*",
+          "from": "https://registry.npmjs.org/debug/-/debug-0.8.1.tgz",
           "resolved": "https://registry.npmjs.org/debug/-/debug-0.8.1.tgz"
         }
       }
     },
     "fluentreports": {
       "version": "0.0.2",
-      "from": "fluentreports@git://github.com/xtuple/fluentreports.git",
+      "from": "fluentreports@git://github.com/xtuple/fluentreports.git#161ecebe081ade6fdd56c1fd11471c39297f8d29",
       "resolved": "git://github.com/xtuple/fluentreports.git#161ecebe081ade6fdd56c1fd11471c39297f8d29",
       "dependencies": {
         "pdfkit": {
           "version": "0.2.6",
-          "from": "pdfkit@git://github.com/Nathanaela/pdfkit.git#Release",
+          "from": "pdfkit@git://github.com/Nathanaela/pdfkit.git#5e393ce15484afc47b35edbca79f6d518cbce67c",
           "resolved": "git://github.com/Nathanaela/pdfkit.git#5e393ce15484afc47b35edbca79f6d518cbce67c",
           "dependencies": {
             "png-js": {
               "version": "0.1.1",
-              "from": "png-js@>=0.1.0"
+              "from": "png-js@0.1.1"
             }
           }
         }
     },
     "ipp": {
       "version": "0.0.5",
-      "from": "ipp@0.0.5",
+      "from": "https://registry.npmjs.org/ipp/-/ipp-0.0.5.tgz",
       "resolved": "https://registry.npmjs.org/ipp/-/ipp-0.0.5.tgz"
     },
     "json-patch": {
       "version": "0.0.1",
-      "from": "json-patch@git://github.com/xtuple/JSON-Patch.git",
+      "from": "json-patch@git://github.com/xtuple/JSON-Patch.git#eb69a78f6d041b2f630a9747a5b227c42c8df077",
       "resolved": "git://github.com/xtuple/JSON-Patch.git#eb69a78f6d041b2f630a9747a5b227c42c8df077"
     },
     "less": {
       "version": "1.5.0",
-      "from": "less@1.5.0",
+      "from": "https://registry.npmjs.org/less/-/less-1.5.0.tgz",
       "resolved": "https://registry.npmjs.org/less/-/less-1.5.0.tgz",
       "dependencies": {
         "mime": {
           "version": "1.2.11",
-          "from": "mime@1.2.x",
+          "from": "https://registry.npmjs.org/mime/-/mime-1.2.11.tgz",
           "resolved": "https://registry.npmjs.org/mime/-/mime-1.2.11.tgz"
         },
         "mkdirp": {
           "version": "0.3.5",
-          "from": "mkdirp@~0.3.4"
+          "from": "mkdirp@0.3.5"
         },
         "clean-css": {
           "version": "1.0.12",
-          "from": "clean-css@1.0.x",
+          "from": "clean-css@1.0.12",
           "dependencies": {
             "commander": {
               "version": "1.3.2",
-              "from": "commander@1.3.x",
+              "from": "commander@1.3.2",
               "dependencies": {
                 "keypress": {
                   "version": "0.1.0",
-                  "from": "keypress@0.1.x"
+                  "from": "keypress@0.1.0"
                 }
               }
             }
         },
         "source-map": {
           "version": "0.1.33",
-          "from": "source-map@0.1.x",
+          "from": "source-map@0.1.33",
           "dependencies": {
             "amdefine": {
               "version": "0.1.0",
-              "from": "amdefine@>=0.0.4"
+              "from": "amdefine@0.1.0"
             }
           }
         }
     },
     "moment": {
       "version": "2.4.0",
-      "from": "moment@2.4.x"
+      "from": "moment@2.4.0"
     },
     "nodemailer": {
       "version": "0.3.44",
-      "from": "nodemailer@0.3.x",
+      "from": "nodemailer@0.3.44",
       "dependencies": {
         "mailcomposer": {
           "version": "0.2.9",
-          "from": "mailcomposer@>= 0.1.29",
+          "from": "mailcomposer@0.2.9",
           "dependencies": {
             "mimelib": {
               "version": "0.2.14",
-              "from": "mimelib@~0.2.14",
+              "from": "mimelib@0.2.14",
               "dependencies": {
                 "encoding": {
                   "version": "0.1.7",
-                  "from": "encoding@~0.1",
+                  "from": "encoding@0.1.7",
                   "dependencies": {
                     "iconv-lite": {
                       "version": "0.2.11",
-                      "from": "iconv-lite@~0.2.11"
+                      "from": "iconv-lite@0.2.11"
                     }
                   }
                 },
                 "addressparser": {
                   "version": "0.2.1",
-                  "from": "addressparser@~0.2.0"
+                  "from": "addressparser@0.2.1"
                 }
               }
             },
             "mime": {
               "version": "1.2.11",
-              "from": "mime@~1.2.9",
+              "from": "https://registry.npmjs.org/mime/-/mime-1.2.11.tgz",
               "resolved": "https://registry.npmjs.org/mime/-/mime-1.2.11.tgz"
             },
             "he": {
               "version": "0.3.6",
-              "from": "he@~0.3.6"
+              "from": "he@0.3.6"
             },
             "punycode": {
               "version": "1.2.4",
-              "from": "punycode@~1.2.3"
+              "from": "punycode@1.2.4"
             },
             "follow-redirects": {
               "version": "0.0.3",
-              "from": "follow-redirects@0.0.3",
+              "from": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-0.0.3.tgz",
               "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-0.0.3.tgz"
             },
             "dkim-signer": {
               "version": "0.1.0",
-              "from": "dkim-signer@~0.1.0"
+              "from": "dkim-signer@0.1.0"
             }
           }
         },
         "simplesmtp": {
           "version": "0.3.29",
-          "from": "simplesmtp@>= 0.1.28",
+          "from": "https://registry.npmjs.org/simplesmtp/-/simplesmtp-0.3.29.tgz",
           "resolved": "https://registry.npmjs.org/simplesmtp/-/simplesmtp-0.3.29.tgz",
           "dependencies": {
             "rai": {
               "version": "0.1.10",
-              "from": "rai@~0.1.10",
+              "from": "https://registry.npmjs.org/rai/-/rai-0.1.10.tgz",
               "resolved": "https://registry.npmjs.org/rai/-/rai-0.1.10.tgz"
             },
             "xoauth2": {
               "version": "0.1.8",
-              "from": "xoauth2@~0.1"
+              "from": "xoauth2@0.1.8"
             }
           }
         },
         "optimist": {
           "version": "0.6.1",
-          "from": "optimist@*",
+          "from": "optimist@0.6.1",
           "dependencies": {
             "wordwrap": {
               "version": "0.0.2",
-              "from": "wordwrap@~0.0.2"
+              "from": "wordwrap@0.0.2"
             },
             "minimist": {
               "version": "0.0.9",
-              "from": "minimist@~0.0.1",
+              "from": "https://registry.npmjs.org/minimist/-/minimist-0.0.9.tgz",
               "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.9.tgz"
             }
           }
         }
       }
     },
-    "node-forge": {
-      "version": "0.6.2",
-      "from": "node-forge@0.6.x"
-    },
     "npm": {
-      "version": "1.4.16",
-      "from": "npm@1.4.x",
-      "resolved": "https://registry.npmjs.org/npm/-/npm-1.4.16.tgz",
+      "version": "1.2.30",
+      "from": "https://registry.npmjs.org/npm/-/npm-1.2.30.tgz",
+      "resolved": "https://registry.npmjs.org/npm/-/npm-1.2.30.tgz",
       "dependencies": {
-        "abbrev": {
-          "version": "1.0.5",
-          "from": "abbrev@latest",
-          "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.0.5.tgz"
-        },
-        "ansi": {
-          "version": "0.3.0",
-          "from": "ansi@latest",
-          "resolved": "https://registry.npmjs.org/ansi/-/ansi-0.3.0.tgz"
-        },
-        "ansicolors": {
-          "version": "0.3.2",
-          "from": "ansicolors@latest"
-        },
-        "ansistyles": {
-          "version": "0.1.3",
-          "from": "ansistyles@0.1.3",
-          "resolved": "https://registry.npmjs.org/ansistyles/-/ansistyles-0.1.3.tgz"
-        },
-        "archy": {
-          "version": "0.0.2",
-          "from": "archy@0.0.2"
-        },
-        "block-stream": {
-          "version": "0.0.7",
-          "from": "block-stream@latest"
-        },
-        "char-spinner": {
-          "version": "1.0.1",
-          "from": "char-spinner@latest",
-          "resolved": "https://registry.npmjs.org/char-spinner/-/char-spinner-1.0.1.tgz"
-        },
-        "child-process-close": {
-          "version": "0.1.1",
-          "from": "child-process-close@",
-          "resolved": "https://registry.npmjs.org/child-process-close/-/child-process-close-0.1.1.tgz"
-        },
-        "chmodr": {
-          "version": "0.1.0",
-          "from": "chmodr@latest"
-        },
-        "chownr": {
-          "version": "0.0.1",
-          "from": "../chownr"
-        },
-        "cmd-shim": {
-          "version": "1.1.1",
-          "from": "cmd-shim@latest",
-          "resolved": "https://registry.npmjs.org/cmd-shim/-/cmd-shim-1.1.1.tgz"
+        "semver": {
+          "version": "1.1.4",
+          "from": "semver@1.1.4"
         },
-        "columnify": {
+        "ini": {
           "version": "1.1.0",
-          "from": "columnify@latest",
-          "resolved": "https://registry.npmjs.org/columnify/-/columnify-1.1.0.tgz",
-          "dependencies": {
-            "strip-ansi": {
-              "version": "0.2.2",
-              "from": "strip-ansi@^0.2.1",
-              "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-0.2.2.tgz",
-              "dependencies": {
-                "ansi-regex": {
-                  "version": "0.1.0",
-                  "from": "ansi-regex@^0.1.0",
-                  "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-0.1.0.tgz"
-                }
-              }
-            },
-            "wcwidth.js": {
-              "version": "0.0.4",
-              "from": "wcwidth.js@~0.0.4",
-              "resolved": "https://registry.npmjs.org/wcwidth.js/-/wcwidth.js-0.0.4.tgz",
-              "dependencies": {
-                "underscore": {
-                  "version": "1.6.0",
-                  "from": "underscore@>= 1.3.0",
-                  "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.6.0.tgz"
-                }
-              }
-            }
-          }
+          "from": "ini@latest"
         },
-        "editor": {
-          "version": "0.1.0",
-          "from": "editor@latest",
-          "resolved": "https://registry.npmjs.org/editor/-/editor-0.1.0.tgz"
-        },
-        "fstream": {
-          "version": "0.1.27",
-          "from": "fstream@~0.1.26"
-        },
-        "fstream-npm": {
-          "version": "0.1.7",
-          "from": "fstream-npm@latest",
-          "dependencies": {
-            "fstream-ignore": {
-              "version": "0.0.8",
-              "from": "fstream-ignore@~0.0",
-              "resolved": "https://registry.npmjs.org/fstream-ignore/-/fstream-ignore-0.0.8.tgz"
-            }
-          }
-        },
-        "github-url-from-git": {
-          "version": "1.1.1",
-          "from": "github-url-from-git@1.1.1",
-          "resolved": "https://registry.npmjs.org/github-url-from-git/-/github-url-from-git-1.1.1.tgz"
-        },
-        "github-url-from-username-repo": {
-          "version": "0.2.0",
-          "from": "github-url-from-username-repo@latest",
-          "resolved": "https://registry.npmjs.org/github-url-from-username-repo/-/github-url-from-username-repo-0.2.0.tgz"
+        "slide": {
+          "version": "1.1.4",
+          "from": "slide@latest"
         },
-        "glob": {
-          "version": "4.0.2",
-          "from": "glob@latest",
-          "resolved": "https://registry.npmjs.org/glob/-/glob-4.0.2.tgz"
+        "abbrev": {
+          "version": "1.0.4",
+          "from": "abbrev@latest"
         },
         "graceful-fs": {
-          "version": "3.0.2",
-          "from": "graceful-fs@~3.0.0",
-          "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-3.0.2.tgz"
-        },
-        "inflight": {
-          "version": "1.0.1",
-          "from": "inflight@~1.0.1",
-          "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.1.tgz"
-        },
-        "ini": {
-          "version": "1.2.1",
-          "from": "ini@latest",
-          "resolved": "https://registry.npmjs.org/ini/-/ini-1.2.1.tgz"
-        },
-        "init-package-json": {
-          "version": "0.1.0",
-          "from": "init-package-json@latest",
-          "dependencies": {
-            "promzard": {
-              "version": "0.2.2",
-              "from": "promzard@~0.2.0",
-              "resolved": "https://registry.npmjs.org/promzard/-/promzard-0.2.2.tgz"
-            }
-          }
-        },
-        "lockfile": {
-          "version": "0.4.2",
-          "from": "lockfile@0.4.2",
-          "resolved": "https://registry.npmjs.org/lockfile/-/lockfile-0.4.2.tgz"
-        },
-        "lru-cache": {
-          "version": "2.5.0",
-          "from": "lru-cache@latest"
+          "version": "1.2.2",
+          "from": "graceful-fs@latest"
         },
         "minimatch": {
-          "version": "0.3.0",
+          "version": "0.2.12",
           "from": "minimatch@latest",
-          "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-0.3.0.tgz",
           "dependencies": {
             "sigmund": {
               "version": "1.0.0",
             }
           }
         },
-        "mkdirp": {
-          "version": "0.3.5",
-          "from": "mkdirp@latest"
-        },
-        "node-gyp": {
-          "version": "0.13.1",
-          "from": "node-gyp@~0.13.0",
-          "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-0.13.1.tgz"
-        },
         "nopt": {
-          "version": "3.0.0",
+          "version": "2.1.1",
           "from": "nopt@latest"
         },
-        "npm-cache-filename": {
-          "version": "1.0.1",
-          "from": "npm-cache-filename@latest",
-          "resolved": "https://registry.npmjs.org/npm-cache-filename/-/npm-cache-filename-1.0.1.tgz"
-        },
-        "npm-install-checks": {
-          "version": "1.0.2",
-          "from": "npm-install-checks@latest"
-        },
-        "npm-registry-client": {
-          "version": "2.0.2",
-          "from": "npm-registry-client@latest"
-        },
-        "npm-user-validate": {
-          "version": "0.1.0",
-          "from": "npm-user-validate@latest"
-        },
-        "npmconf": {
-          "version": "1.0.5",
-          "from": "npmconf@latest",
-          "dependencies": {
-            "config-chain": {
-              "version": "1.1.8",
-              "from": "config-chain@~1.1.8",
-              "resolved": "https://registry.npmjs.org/config-chain/-/config-chain-1.1.8.tgz",
-              "dependencies": {
-                "proto-list": {
-                  "version": "1.2.3",
-                  "from": "proto-list@~1.2.1",
-                  "resolved": "https://registry.npmjs.org/proto-list/-/proto-list-1.2.3.tgz"
-                }
-              }
-            }
-          }
-        },
-        "npmlog": {
-          "version": "0.1.1",
-          "from": "npmlog@latest"
-        },
-        "once": {
-          "version": "1.3.0",
-          "from": "once@latest"
-        },
-        "opener": {
-          "version": "1.3.0",
-          "from": "opener@latest"
-        },
-        "osenv": {
-          "version": "0.1.0",
-          "from": "osenv@~0.1.0"
-        },
-        "path-is-inside": {
-          "version": "1.0.1",
-          "from": "path-is-inside@1.0.1",
-          "resolved": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.1.tgz"
-        },
-        "read": {
-          "version": "1.0.5",
-          "from": "read@latest",
-          "dependencies": {
-            "mute-stream": {
-              "version": "0.0.4",
-              "from": "mute-stream@~0.0.4"
-            }
-          }
-        },
-        "read-installed": {
-          "version": "2.0.5",
-          "from": "read-installed@latest",
-          "dependencies": {
-            "util-extend": {
-              "version": "1.0.1",
-              "from": "util-extend@^1.0.1",
-              "resolved": "https://registry.npmjs.org/util-extend/-/util-extend-1.0.1.tgz"
-            }
-          }
-        },
-        "read-package-json": {
-          "version": "1.2.2",
-          "from": "read-package-json@latest",
-          "dependencies": {
-            "normalize-package-data": {
-              "version": "0.3.0",
-              "from": "normalize-package-data@^0.3.0",
-              "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-0.3.0.tgz"
-            }
-          }
+        "rimraf": {
+          "version": "2.1.4",
+          "from": "rimraf@2"
         },
         "request": {
-          "version": "2.30.0",
+          "version": "2.21.0",
           "from": "request@latest",
           "dependencies": {
             "qs": {
-              "version": "0.6.6",
+              "version": "0.6.5",
               "from": "qs@~0.6.0"
             },
             "json-stringify-safe": {
-              "version": "5.0.0",
-              "from": "json-stringify-safe@~5.0.0"
+              "version": "4.0.0",
+              "from": "json-stringify-safe@~4.0.0"
             },
             "forever-agent": {
               "version": "0.5.0",
               "from": "forever-agent@~0.5.0"
             },
-            "node-uuid": {
-              "version": "1.4.1",
-              "from": "node-uuid@~1.4.0"
-            },
-            "mime": {
-              "version": "1.2.11",
-              "from": "mime@~1.2.9"
-            },
-            "tough-cookie": {
-              "version": "0.9.15",
-              "from": "tough-cookie@~0.9.15",
-              "dependencies": {
-                "punycode": {
-                  "version": "1.2.3",
-                  "from": "punycode@>=0.2.0"
-                }
-              }
-            },
-            "form-data": {
-              "version": "0.1.2",
-              "from": "form-data@~0.1.0",
-              "dependencies": {
-                "combined-stream": {
-                  "version": "0.0.4",
-                  "from": "combined-stream@~0.0.4",
-                  "dependencies": {
-                    "delayed-stream": {
-                      "version": "0.0.5",
-                      "from": "delayed-stream@0.0.5"
-                    }
-                  }
-                },
-                "async": {
-                  "version": "0.2.9",
-                  "from": "async@~0.2.9"
-                }
-              }
-            },
             "tunnel-agent": {
               "version": "0.3.0",
               "from": "tunnel-agent@~0.3.0"
             },
             "http-signature": {
-              "version": "0.10.0",
-              "from": "http-signature@~0.10.0",
+              "version": "0.9.11",
+              "from": "http-signature@~0.9.11",
               "dependencies": {
                 "assert-plus": {
                   "version": "0.1.2",
                 }
               }
             },
+            "hawk": {
+              "version": "0.13.1",
+              "from": "hawk@~0.13.0",
+              "dependencies": {
+                "hoek": {
+                  "version": "0.8.5",
+                  "from": "hoek@0.8.x"
+                },
+                "boom": {
+                  "version": "0.4.2",
+                  "from": "boom@0.4.x",
+                  "dependencies": {
+                    "hoek": {
+                      "version": "0.9.1",
+                      "from": "hoek@0.9.x",
+                      "resolved": "https://registry.npmjs.org/hoek/-/hoek-0.9.1.tgz"
+                    }
+                  }
+                },
+                "cryptiles": {
+                  "version": "0.2.1",
+                  "from": "cryptiles@0.2.x"
+                },
+                "sntp": {
+                  "version": "0.2.4",
+                  "from": "sntp@0.2.x",
+                  "dependencies": {
+                    "hoek": {
+                      "version": "0.9.1",
+                      "from": "hoek@0.9.x",
+                      "resolved": "https://registry.npmjs.org/hoek/-/hoek-0.9.1.tgz"
+                    }
+                  }
+                }
+              }
+            },
+            "aws-sign": {
+              "version": "0.3.0",
+              "from": "aws-sign@~0.3.0"
+            },
             "oauth-sign": {
               "version": "0.3.0",
               "from": "oauth-sign@~0.3.0"
             },
-            "hawk": {
-              "version": "1.0.0",
-              "from": "hawk@~1.0.0",
+            "cookie-jar": {
+              "version": "0.3.0",
+              "from": "cookie-jar@~0.3.0"
+            },
+            "node-uuid": {
+              "version": "1.4.0",
+              "from": "node-uuid@~1.4.0"
+            },
+            "mime": {
+              "version": "1.2.9",
+              "from": "mime@~1.2.9"
+            },
+            "form-data": {
+              "version": "0.0.8",
+              "from": "form-data@0.0.8",
+              "dependencies": {
+                "combined-stream": {
+                  "version": "0.0.4",
+                  "from": "combined-stream@~0.0.4",
+                  "dependencies": {
+                    "delayed-stream": {
+                      "version": "0.0.5",
+                      "from": "delayed-stream@0.0.5"
+                    }
+                  }
+                },
+                "async": {
+                  "version": "0.2.9",
+                  "from": "async@~0.2.7"
+                }
+              }
+            }
+          }
+        },
+        "which": {
+          "version": "1.0.5",
+          "from": "which@1"
+        },
+        "tar": {
+          "version": "0.1.17",
+          "from": "tar@0.1.17",
+          "resolved": "https://registry.npmjs.org/tar/-/tar-0.1.17.tgz"
+        },
+        "fstream": {
+          "version": "0.1.22",
+          "from": "fstream@latest"
+        },
+        "block-stream": {
+          "version": "0.0.6",
+          "from": "block-stream@*"
+        },
+        "inherits": {
+          "version": "1.0.0",
+          "from": "git://github.com/isaacs/inherits"
+        },
+        "mkdirp": {
+          "version": "0.3.5",
+          "from": "mkdirp@0.3.5",
+          "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.3.5.tgz"
+        },
+        "read": {
+          "version": "1.0.4",
+          "from": "read@~1.0.3",
+          "dependencies": {
+            "mute-stream": {
+              "version": "0.0.3",
+              "from": "mute-stream@~0.0.2"
+            }
+          }
+        },
+        "lru-cache": {
+          "version": "2.3.0",
+          "from": "lru-cache@latest"
+        },
+        "node-gyp": {
+          "version": "0.10.0",
+          "from": "node-gyp@latest",
+          "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-0.10.0.tgz"
+        },
+        "fstream-npm": {
+          "version": "0.1.4",
+          "from": "fstream-npm@latest",
+          "dependencies": {
+            "fstream-ignore": {
+              "version": "0.0.6",
+              "from": "fstream-ignore@~0.0.5"
+            }
+          }
+        },
+        "uid-number": {
+          "version": "0.0.3",
+          "from": "../uid-number"
+        },
+        "archy": {
+          "version": "0.0.2",
+          "from": "archy@0.0.2"
+        },
+        "chownr": {
+          "version": "0.0.1",
+          "from": "../chownr"
+        },
+        "npmlog": {
+          "version": "0.0.2",
+          "from": "npmlog@0"
+        },
+        "ansi": {
+          "version": "0.1.2",
+          "from": "ansi@~0.1.2"
+        },
+        "npm-registry-client": {
+          "version": "0.2.24",
+          "from": "npm-registry-client@~0.2.22",
+          "dependencies": {
+            "couch-login": {
+              "version": "0.1.17",
+              "from": "couch-login@"
+            }
+          }
+        },
+        "read-package-json": {
+          "version": "0.4.1",
+          "from": "read-package-json@~0.4.1",
+          "dependencies": {
+            "normalize-package-data": {
+              "version": "0.1.6",
+              "from": "normalize-package-data@~0.1.2",
               "dependencies": {
-                "hoek": {
-                  "version": "0.9.1",
-                  "from": "hoek@0.9.x"
-                },
-                "boom": {
-                  "version": "0.4.2",
-                  "from": "boom@0.4.x"
-                },
-                "cryptiles": {
-                  "version": "0.2.2",
-                  "from": "cryptiles@0.2.x"
-                },
-                "sntp": {
-                  "version": "0.2.4",
-                  "from": "sntp@0.2.x"
+                "github-url-from-git": {
+                  "version": "1.1.1",
+                  "from": "github-url-from-git@~1.1.1"
                 }
               }
-            },
-            "aws-sign2": {
-              "version": "0.5.0",
-              "from": "aws-sign2@~0.5.0"
             }
           }
         },
+        "read-installed": {
+          "version": "0.1.1",
+          "from": "read-installed@0"
+        },
+        "glob": {
+          "version": "3.2.1",
+          "from": "glob@3.2.1",
+          "resolved": "https://registry.npmjs.org/glob/-/glob-3.2.1.tgz"
+        },
+        "init-package-json": {
+          "version": "0.0.8",
+          "from": "init-package-json@latest",
+          "dependencies": {
+            "promzard": {
+              "version": "0.2.0",
+              "from": "promzard@~0.2.0"
+            }
+          }
+        },
+        "osenv": {
+          "version": "0.0.3",
+          "from": "osenv@latest"
+        },
+        "lockfile": {
+          "version": "0.3.4",
+          "from": "lockfile@0.3.4",
+          "resolved": "https://registry.npmjs.org/lockfile/-/lockfile-0.3.4.tgz"
+        },
         "retry": {
           "version": "0.6.0",
           "from": "retry"
         },
-        "rimraf": {
-          "version": "2.2.8",
-          "from": "rimraf@latest",
-          "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.2.8.tgz"
-        },
-        "semver": {
-          "version": "2.3.0",
-          "from": "semver@latest"
+        "once": {
+          "version": "1.1.1",
+          "from": "once"
         },
-        "sha": {
-          "version": "1.2.4",
-          "from": "sha@latest",
-          "resolved": "https://registry.npmjs.org/sha/-/sha-1.2.4.tgz",
+        "npmconf": {
+          "version": "0.1.0",
+          "from": "npmconf@latest",
           "dependencies": {
-            "readable-stream": {
-              "version": "1.0.27-1",
-              "from": "readable-stream@1.0",
-              "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.27-1.tgz",
+            "config-chain": {
+              "version": "1.1.7",
+              "from": "config-chain@~1.1.1",
               "dependencies": {
-                "core-util-is": {
-                  "version": "1.0.1",
-                  "from": "core-util-is@~1.0.0",
-                  "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.1.tgz"
-                },
-                "isarray": {
-                  "version": "0.0.1",
-                  "from": "isarray@0.0.1",
-                  "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz"
-                },
-                "string_decoder": {
-                  "version": "0.10.25-1",
-                  "from": "string_decoder@~0.10.x",
-                  "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.25-1.tgz"
+                "proto-list": {
+                  "version": "1.2.2",
+                  "from": "proto-list@~1.2.1"
                 }
               }
             }
           }
         },
-        "slide": {
-          "version": "1.1.5",
-          "from": "slide@latest"
+        "opener": {
+          "version": "1.3.0",
+          "from": "opener@latest"
         },
-        "sorted-object": {
-          "version": "1.0.0",
-          "from": "sorted-object@"
+        "chmodr": {
+          "version": "0.1.0",
+          "from": "chmodr@latest"
         },
-        "tar": {
-          "version": "0.1.19",
-          "from": "tar@0.1.19",
-          "resolved": "https://registry.npmjs.org/tar/-/tar-0.1.19.tgz"
+        "cmd-shim": {
+          "version": "1.1.0",
+          "from": "cmd-shim@"
         },
-        "text-table": {
-          "version": "0.2.0",
-          "from": "text-table@~0.2.0"
+        "sha": {
+          "version": "1.0.1",
+          "from": "sha@~1.0.1"
         },
-        "uid-number": {
-          "version": "0.0.5",
-          "from": "uid-number@latest",
-          "resolved": "https://registry.npmjs.org/uid-number/-/uid-number-0.0.5.tgz"
+        "editor": {
+          "version": "0.0.4",
+          "from": "editor@"
         },
-        "which": {
-          "version": "1.0.5",
-          "from": "which@1"
+        "child-process-close": {
+          "version": "0.1.1",
+          "from": "child-process-close@",
+          "resolved": "https://registry.npmjs.org/child-process-close/-/child-process-close-0.1.1.tgz"
         },
-        "inherits": {
-          "version": "2.0.1",
-          "from": "inherits@"
+        "npm-user-validate": {
+          "version": "0.0.1",
+          "from": "npm-user-validate@0"
         }
       }
     },
+    "node-forge": {
+      "version": "0.6.2",
+      "from": "node-forge@0.6.2"
+    },
     "oauth2orize": {
       "version": "0.1.0",
-      "from": "oauth2orize@0.1.x",
+      "from": "oauth2orize@0.1.0",
       "dependencies": {
         "debug": {
           "version": "0.7.4",
-          "from": "debug@0.7.x"
+          "from": "debug@0.7.4"
         }
       }
     },
     "oauth2orize-jwt-bearer": {
       "version": "0.1.0",
-      "from": "oauth2orize-jwt-bearer@0.1.x",
+      "from": "oauth2orize-jwt-bearer@0.1.0",
       "dependencies": {
         "pkginfo": {
           "version": "0.2.3",
-          "from": "pkginfo@0.2.x"
+          "from": "pkginfo@0.2.3"
         }
       }
     },
     "passport": {
       "version": "0.1.18",
-      "from": "passport@0.1.x",
+      "from": "passport@0.1.18",
       "dependencies": {
         "pkginfo": {
           "version": "0.2.3",
-          "from": "pkginfo@0.2.x"
+          "from": "pkginfo@0.2.3"
         },
         "pause": {
           "version": "0.0.1",
     },
     "passport-http": {
       "version": "0.2.2",
-      "from": "passport-http@0.2.x",
+      "from": "passport-http@0.2.2",
       "dependencies": {
         "pkginfo": {
           "version": "0.2.3",
-          "from": "pkginfo@0.2.x"
+          "from": "pkginfo@0.2.3"
         }
       }
     },
     "passport-http-bearer": {
       "version": "0.2.1",
-      "from": "passport-http-bearer@0.2.x",
+      "from": "passport-http-bearer@0.2.1",
       "dependencies": {
         "pkginfo": {
           "version": "0.2.3",
-          "from": "pkginfo@0.2.x"
+          "from": "pkginfo@0.2.3"
         }
       }
     },
     "passport-local": {
       "version": "0.1.6",
-      "from": "passport-local@0.1.x",
+      "from": "passport-local@0.1.6",
       "dependencies": {
         "pkginfo": {
           "version": "0.2.3",
-          "from": "pkginfo@0.2.x"
+          "from": "pkginfo@0.2.3"
         }
       }
     },
     "passport-oauth2-client-password": {
       "version": "0.1.1",
-      "from": "passport-oauth2-client-password@0.1.x",
+      "from": "passport-oauth2-client-password@0.1.1",
       "dependencies": {
         "pkginfo": {
           "version": "0.2.3",
-          "from": "pkginfo@0.2.x"
+          "from": "pkginfo@0.2.3"
         }
       }
     },
     "passport-oauth2-jwt-bearer": {
       "version": "0.1.1",
-      "from": "passport-oauth2-jwt-bearer@0.1.x",
+      "from": "passport-oauth2-jwt-bearer@0.1.1",
       "dependencies": {
         "pkginfo": {
           "version": "0.2.3",
-          "from": "pkginfo@0.2.x"
+          "from": "pkginfo@0.2.3"
         }
       }
     },
     "paynode": {
       "version": "0.3.6",
-      "from": "paynode@0.3.6",
+      "from": "https://registry.npmjs.org/paynode/-/paynode-0.3.6.tgz",
       "resolved": "https://registry.npmjs.org/paynode/-/paynode-0.3.6.tgz",
       "dependencies": {
         "braintree": {
           "version": "1.14.0",
-          "from": "braintree@*",
+          "from": "braintree@1.14.0",
           "dependencies": {
             "dateformat": {
               "version": "1.0.1-1.2.3",
-              "from": "dateformat@=1.0.1-1.2.3",
+              "from": "https://registry.npmjs.org/dateformat/-/dateformat-1.0.1-1.2.3.tgz",
               "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-1.0.1-1.2.3.tgz"
             },
             "semver": {
               "version": "2.2.1",
-              "from": "semver@=2.2.1",
+              "from": "https://registry.npmjs.org/semver/-/semver-2.2.1.tgz",
               "resolved": "https://registry.npmjs.org/semver/-/semver-2.2.1.tgz"
             },
             "readable-stream": {
               "version": "1.1.10",
-              "from": "readable-stream@=1.1.10",
+              "from": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.10.tgz",
               "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.10.tgz",
               "dependencies": {
                 "core-util-is": {
                   "version": "1.0.1",
-                  "from": "core-util-is@~1.0.0"
+                  "from": "core-util-is@1.0.1"
                 },
                 "string_decoder": {
                   "version": "0.10.25-1",
-                  "from": "string_decoder@~0.10.x"
+                  "from": "string_decoder@0.10.25-1"
                 },
                 "debuglog": {
                   "version": "0.0.2",
-                  "from": "debuglog@0.0.2",
+                  "from": "https://registry.npmjs.org/debuglog/-/debuglog-0.0.2.tgz",
                   "resolved": "https://registry.npmjs.org/debuglog/-/debuglog-0.0.2.tgz"
                 }
               }
             },
             "underscore": {
               "version": "1.3.1",
-              "from": "underscore@=1.3.1",
+              "from": "https://registry.npmjs.org/underscore/-/underscore-1.3.1.tgz",
               "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.3.1.tgz"
             },
             "xml2js": {
               "version": "0.1.13",
-              "from": "xml2js@=0.1.13",
+              "from": "xml2js@0.1.13",
               "dependencies": {
                 "sax": {
                   "version": "0.6.0",
-                  "from": "sax@>=0.1.1"
+                  "from": "sax@0.6.0"
                 }
               }
             },
             "source-map-support": {
               "version": "0.1.2",
-              "from": "source-map-support@=0.1.2",
+              "from": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.1.2.tgz",
               "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.1.2.tgz",
               "dependencies": {
                 "source-map": {
                   "version": "0.1.8",
-                  "from": "source-map@0.1.8",
+                  "from": "https://registry.npmjs.org/source-map/-/source-map-0.1.8.tgz",
                   "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.1.8.tgz",
                   "dependencies": {
                     "amdefine": {
                       "version": "0.1.0",
-                      "from": "amdefine@>=0.0.4"
+                      "from": "amdefine@0.1.0"
                     }
                   }
                 }
     },
     "pg": {
       "version": "0.14.1",
-      "from": "pg@0.14.x",
+      "from": "pg@0.14.1",
       "dependencies": {
         "generic-pool": {
           "version": "2.0.4",
-          "from": "generic-pool@~2.0.2"
+          "from": "generic-pool@2.0.4"
         },
         "deprecate": {
           "version": "0.1.0",
-          "from": "deprecate@~0.1.0"
+          "from": "deprecate@0.1.0"
         }
       }
     },
     "request": {
       "version": "2.14.0",
-      "from": "request@2.14.x",
+      "from": "request@2.14.0",
       "dependencies": {
         "form-data": {
           "version": "0.0.7",
     },
     "rimraf": {
       "version": "2.2.8",
-      "from": "rimraf@2.2.x",
+      "from": "https://registry.npmjs.org/rimraf/-/rimraf-2.2.8.tgz",
       "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.2.8.tgz"
     },
     "rjson": {
       "version": "0.2.4",
-      "from": "rjson@~0.2.4",
+      "from": "rjson@0.2.4",
       "dependencies": {
         "commander": {
           "version": "1.1.1",
-          "from": "commander@~1.1.1",
+          "from": "commander@1.1.1",
           "dependencies": {
             "keypress": {
               "version": "0.1.0",
-              "from": "keypress@0.1.x"
+              "from": "keypress@0.1.0"
             }
           }
         }
     },
     "socket.io": {
       "version": "0.9.16",
-      "from": "socket.io@0.9.x",
+      "from": "socket.io@0.9.16",
       "dependencies": {
         "socket.io-client": {
           "version": "0.9.16",
-          "from": "socket.io-client@0.9.16",
+          "from": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-0.9.16.tgz",
           "resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-0.9.16.tgz",
           "dependencies": {
             "uglify-js": {
             },
             "ws": {
               "version": "0.4.31",
-              "from": "ws@0.4.x",
+              "from": "ws@0.4.31",
               "dependencies": {
                 "commander": {
                   "version": "0.6.1",
-                  "from": "commander@~0.6.1"
+                  "from": "commander@0.6.1"
                 },
                 "nan": {
                   "version": "0.3.2",
-                  "from": "nan@~0.3.0"
+                  "from": "nan@0.3.2"
                 },
                 "tinycolor": {
                   "version": "0.0.1",
-                  "from": "tinycolor@0.x"
+                  "from": "tinycolor@0.0.1"
                 },
                 "options": {
                   "version": "0.0.5",
-                  "from": "options@>=0.0.5"
+                  "from": "options@0.0.5"
                 }
               }
             },
         },
         "policyfile": {
           "version": "0.0.4",
-          "from": "policyfile@0.0.4",
+          "from": "https://registry.npmjs.org/policyfile/-/policyfile-0.0.4.tgz",
           "resolved": "https://registry.npmjs.org/policyfile/-/policyfile-0.0.4.tgz"
         },
         "base64id": {
         },
         "redis": {
           "version": "0.7.3",
-          "from": "redis@0.7.3",
+          "from": "https://registry.npmjs.org/redis/-/redis-0.7.3.tgz",
           "resolved": "https://registry.npmjs.org/redis/-/redis-0.7.3.tgz"
         }
       }
     },
     "underscore": {
       "version": "1.4.4",
-      "from": "underscore@1.4.x"
+      "from": "underscore@1.4.4"
     },
     "winston": {
       "version": "0.7.3",
-      "from": "winston@0.7.x",
+      "from": "winston@0.7.3",
       "dependencies": {
         "cycle": {
           "version": "1.0.3",
-          "from": "cycle@1.0.x"
+          "from": "cycle@1.0.3"
         },
         "eyes": {
           "version": "0.1.8",
-          "from": "eyes@0.1.x"
+          "from": "eyes@0.1.8"
         },
         "pkginfo": {
           "version": "0.3.0",
-          "from": "pkginfo@0.3.x"
+          "from": "pkginfo@0.3.0"
         },
         "request": {
           "version": "2.16.6",
-          "from": "request@2.16.x",
+          "from": "request@2.16.6",
           "dependencies": {
             "form-data": {
               "version": "0.0.10",
-              "from": "form-data@~0.0.3",
+              "from": "form-data@0.0.10",
               "dependencies": {
                 "combined-stream": {
                   "version": "0.0.4",
-                  "from": "combined-stream@~0.0.4",
+                  "from": "combined-stream@0.0.4",
                   "dependencies": {
                     "delayed-stream": {
                       "version": "0.0.5",
-                      "from": "delayed-stream@0.0.5"
+                      "from": "delayed-stream@0.0.5",
+                      "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-0.0.5.tgz"
                     }
                   }
                 }
             },
             "mime": {
               "version": "1.2.11",
-              "from": "mime@~1.2.7"
+              "from": "https://registry.npmjs.org/mime/-/mime-1.2.11.tgz",
+              "resolved": "https://registry.npmjs.org/mime/-/mime-1.2.11.tgz"
             },
             "hawk": {
               "version": "0.10.2",
-              "from": "hawk@~0.10.2",
+              "from": "hawk@0.10.2",
               "dependencies": {
                 "hoek": {
                   "version": "0.7.6",
-                  "from": "hoek@0.7.x"
+                  "from": "hoek@0.7.6"
                 },
                 "boom": {
                   "version": "0.3.8",
-                  "from": "boom@0.3.x"
+                  "from": "boom@0.3.8"
                 },
                 "cryptiles": {
                   "version": "0.1.3",
-                  "from": "cryptiles@0.1.x"
+                  "from": "cryptiles@0.1.3"
                 },
                 "sntp": {
                   "version": "0.1.4",
-                  "from": "sntp@0.1.x"
+                  "from": "sntp@0.1.4"
                 }
               }
             },
             "node-uuid": {
               "version": "1.4.1",
-              "from": "node-uuid@~1.4.0"
+              "from": "node-uuid@1.4.1"
             },
             "cookie-jar": {
               "version": "0.2.0",
-              "from": "cookie-jar@~0.2.0"
+              "from": "cookie-jar@0.2.0"
             },
             "aws-sign": {
               "version": "0.2.0",
-              "from": "aws-sign@~0.2.0"
+              "from": "aws-sign@0.2.0"
             },
             "oauth-sign": {
               "version": "0.2.0",
-              "from": "oauth-sign@~0.2.0"
+              "from": "oauth-sign@0.2.0"
             },
             "forever-agent": {
               "version": "0.2.0",
-              "from": "forever-agent@~0.2.0"
+              "from": "forever-agent@0.2.0"
             },
             "tunnel-agent": {
               "version": "0.2.0",
-              "from": "tunnel-agent@~0.2.0"
+              "from": "tunnel-agent@0.2.0"
             },
             "json-stringify-safe": {
               "version": "3.0.0",
-              "from": "json-stringify-safe@~3.0.0"
+              "from": "json-stringify-safe@3.0.0"
             },
             "qs": {
               "version": "0.5.6",
-              "from": "qs@~0.5.4"
+              "from": "qs@0.5.6"
             }
           }
         },
         "stack-trace": {
           "version": "0.0.9",
-          "from": "stack-trace@0.0.x"
+          "from": "stack-trace@0.0.9"
         }
       }
     },
-    "xmla4js": {
-      "version": "0.0.2",
-      "from": "xmla4js@git://github.com/rpbouman/xmla4js.git",
-      "resolved": "git://github.com/rpbouman/xmla4js.git#40a0be2637c3f18c986d3e5cbe4c32cf8f995531"
-    },
     "underscore.string": {
       "version": "2.3.3",
-      "from": "underscore.string@~2.3.3"
+      "from": "underscore.string@2.3.3"
     },
     "ursa": {
       "version": "0.8.0",
-      "from": "ursa@0.8.x"
+      "from": "ursa@0.8.0"
     },
     "xtuple-query": {
       "version": "1.0.0",
-      "from": "xtuple-query@~1.0.0",
+      "from": "xtuple-query@1.0.0",
       "dependencies": {
         "congruence": {
           "version": "1.2.3",
-          "from": "congruence@1.2.3",
+          "from": "https://registry.npmjs.org/congruence/-/congruence-1.2.3.tgz",
           "resolved": "https://registry.npmjs.org/congruence/-/congruence-1.2.3.tgz"
         },
         "underscore": {
           "version": "1.5.2",
-          "from": "underscore@~1.5.2"
+          "from": "underscore@1.5.2"
         }
       }
     },
     "googleapis": {
       "version": "0.4.7",
-      "from": "googleapis@~0.4.6",
+      "from": "googleapis@0.4.7",
       "dependencies": {
         "request": {
           "version": "2.25.0",
-          "from": "request@~2.25.0",
+          "from": "request@2.25.0",
           "dependencies": {
             "qs": {
               "version": "0.6.6",
-              "from": "qs@~0.6.0"
+              "from": "qs@0.6.6"
             },
             "json-stringify-safe": {
               "version": "5.0.0",
-              "from": "json-stringify-safe@~5.0.0"
+              "from": "json-stringify-safe@5.0.0"
             },
             "forever-agent": {
               "version": "0.5.2",
-              "from": "forever-agent@~0.5.0"
+              "from": "forever-agent@0.5.2"
             },
             "tunnel-agent": {
               "version": "0.3.0",
-              "from": "tunnel-agent@~0.3.0"
+              "from": "tunnel-agent@0.3.0"
             },
             "http-signature": {
               "version": "0.10.0",
-              "from": "http-signature@~0.10.0",
+              "from": "http-signature@0.10.0",
               "dependencies": {
                 "assert-plus": {
                   "version": "0.1.2",
-                  "from": "assert-plus@0.1.2"
+                  "from": "assert-plus@0.1.2",
+                  "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-0.1.2.tgz"
                 },
                 "asn1": {
                   "version": "0.1.11",
-                  "from": "asn1@0.1.11"
+                  "from": "asn1@0.1.11",
+                  "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.1.11.tgz"
                 },
                 "ctype": {
                   "version": "0.5.2",
-                  "from": "ctype@0.5.2"
+                  "from": "ctype@0.5.2",
+                  "resolved": "https://registry.npmjs.org/ctype/-/ctype-0.5.2.tgz"
                 }
               }
             },
             "hawk": {
               "version": "1.0.0",
-              "from": "hawk@~1.0.0",
+              "from": "hawk@1.0.0",
               "dependencies": {
                 "hoek": {
                   "version": "0.9.1",
-                  "from": "hoek@0.9.x"
+                  "from": "hoek@0.9.1"
                 },
                 "boom": {
                   "version": "0.4.2",
-                  "from": "boom@0.4.x"
+                  "from": "boom@0.4.2"
                 },
                 "cryptiles": {
                   "version": "0.2.2",
-                  "from": "cryptiles@0.2.x"
+                  "from": "cryptiles@0.2.2"
                 },
                 "sntp": {
                   "version": "0.2.4",
-                  "from": "sntp@0.2.x"
+                  "from": "sntp@0.2.4"
                 }
               }
             },
             "aws-sign": {
               "version": "0.3.0",
-              "from": "aws-sign@~0.3.0"
+              "from": "aws-sign@0.3.0"
             },
             "oauth-sign": {
               "version": "0.3.0",
-              "from": "oauth-sign@~0.3.0"
+              "from": "oauth-sign@0.3.0"
             },
             "cookie-jar": {
               "version": "0.3.0",
-              "from": "cookie-jar@~0.3.0"
+              "from": "cookie-jar@0.3.0"
             },
             "node-uuid": {
               "version": "1.4.1",
-              "from": "node-uuid@~1.4.0"
+              "from": "node-uuid@1.4.1"
             },
             "mime": {
               "version": "1.2.11",
-              "from": "mime@~1.2.9",
+              "from": "https://registry.npmjs.org/mime/-/mime-1.2.11.tgz",
               "resolved": "https://registry.npmjs.org/mime/-/mime-1.2.11.tgz"
             },
             "form-data": {
               "version": "0.1.2",
-              "from": "form-data@~0.1.0",
+              "from": "form-data@0.1.2",
               "dependencies": {
                 "combined-stream": {
                   "version": "0.0.4",
-                  "from": "combined-stream@~0.0.4",
+                  "from": "combined-stream@0.0.4",
                   "dependencies": {
                     "delayed-stream": {
                       "version": "0.0.5",
-                      "from": "delayed-stream@0.0.5"
+                      "from": "delayed-stream@0.0.5",
+                      "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-0.0.5.tgz"
                     }
                   }
                 },
                 "async": {
                   "version": "0.2.10",
-                  "from": "async@~0.2.9"
+                  "from": "async@0.2.10"
                 }
               }
             }
         },
         "async": {
           "version": "0.2.6",
-          "from": "async@0.2.6",
+          "from": "https://registry.npmjs.org/async/-/async-0.2.6.tgz",
           "resolved": "https://registry.npmjs.org/async/-/async-0.2.6.tgz"
         },
         "gapitoken": {
           "version": "0.1.0",
-          "from": "gapitoken@0.1.0",
+          "from": "https://registry.npmjs.org/gapitoken/-/gapitoken-0.1.0.tgz",
           "resolved": "https://registry.npmjs.org/gapitoken/-/gapitoken-0.1.0.tgz",
           "dependencies": {
             "jws": {
               "dependencies": {
                 "tap": {
                   "version": "0.3.3",
-                  "from": "tap@~0.3.3",
+                  "from": "tap@0.3.3",
                   "dependencies": {
                     "inherits": {
                       "version": "1.0.0"
                     },
                     "slide": {
                       "version": "1.1.5",
-                      "from": "slide@*"
+                      "from": "slide@1.1.5"
                     },
                     "runforcover": {
                       "version": "0.0.2",
-                      "from": "runforcover@~0.0.2",
+                      "from": "runforcover@0.0.2",
                       "dependencies": {
                         "bunker": {
                           "version": "0.1.2",
-                          "from": "bunker@0.1.X",
+                          "from": "bunker@0.1.2",
                           "dependencies": {
                             "burrito": {
                               "version": "0.2.12",
-                              "from": "burrito@>=0.2.5 <0.3",
+                              "from": "burrito@0.2.12",
                               "dependencies": {
                                 "traverse": {
                                   "version": "0.5.2",
-                                  "from": "traverse@~0.5.1"
+                                  "from": "traverse@0.5.2"
                                 },
                                 "uglify-js": {
                                   "version": "1.1.1",
-                                  "from": "uglify-js@~1.1.1"
+                                  "from": "uglify-js@1.1.1"
                                 }
                               }
                             }
                     },
                     "nopt": {
                       "version": "2.2.1",
-                      "from": "nopt@~2",
+                      "from": "https://registry.npmjs.org/nopt/-/nopt-2.2.1.tgz",
                       "resolved": "https://registry.npmjs.org/nopt/-/nopt-2.2.1.tgz",
                       "dependencies": {
                         "abbrev": {
                           "version": "1.0.5",
-                          "from": "abbrev@1",
+                          "from": "https://registry.npmjs.org/abbrev/-/abbrev-1.0.5.tgz",
                           "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.0.5.tgz"
                         }
                       }
                     },
                     "mkdirp": {
                       "version": "0.3.5",
-                      "from": "mkdirp@~0.3"
+                      "from": "mkdirp@0.3.5"
                     },
                     "difflet": {
                       "version": "0.2.6",
-                      "from": "difflet@~0.2.0",
+                      "from": "difflet@0.2.6",
                       "dependencies": {
                         "traverse": {
                           "version": "0.6.6",
-                          "from": "traverse@0.6.x"
+                          "from": "traverse@0.6.6"
                         },
                         "charm": {
                           "version": "0.1.2",
-                          "from": "charm@0.1.x"
+                          "from": "charm@0.1.2"
                         },
                         "deep-is": {
                           "version": "0.1.2",
-                          "from": "deep-is@0.1.x"
+                          "from": "deep-is@0.1.2"
                         }
                       }
                     },
                     "deep-equal": {
                       "version": "0.0.0",
-                      "from": "deep-equal@~0.0.0"
+                      "from": "deep-equal@0.0.0"
                     },
                     "buffer-equal": {
                       "version": "0.0.0",
-                      "from": "buffer-equal@~0.0.0"
+                      "from": "buffer-equal@0.0.0"
                     }
                   }
                 },
                 "base64url": {
                   "version": "0.0.3",
-                  "from": "base64url@0.0.3",
+                  "from": "https://registry.npmjs.org/base64url/-/base64url-0.0.3.tgz",
                   "resolved": "https://registry.npmjs.org/base64url/-/base64url-0.0.3.tgz"
                 }
               }
       "version": "1.5.0",
       "from": "chai@1.5.x"
     },
-    "require-uncache": {
-      "version": "0.0.2",
-      "from": "require-uncache@0.0.x"
-    },
     "csslint": {
       "version": "0.10.0",
       "from": "csslint@~0.10.0",
       "dependencies": {
         "parserlib": {
           "version": "0.2.5",
-          "from": "parserlib@~0.2.2",
-          "resolved": "https://registry.npmjs.org/parserlib/-/parserlib-0.2.5.tgz"
+          "from": "parserlib@~0.2.2"
         }
       }
     },
           "dependencies": {
             "mkdirp": {
               "version": "0.3.0",
-              "from": "mkdirp@0.3.0",
-              "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.3.0.tgz"
+              "from": "mkdirp@0.3.0"
             }
           }
         },
         "diff": {
           "version": "1.0.2",
-          "from": "diff@1.0.2",
-          "resolved": "https://registry.npmjs.org/diff/-/diff-1.0.2.tgz"
+          "from": "diff@1.0.2"
         },
         "debug": {
-          "version": "0.8.1",
+          "version": "1.0.4",
           "from": "debug@*",
-          "resolved": "https://registry.npmjs.org/debug/-/debug-0.8.1.tgz"
+          "dependencies": {
+            "ms": {
+              "version": "0.6.2",
+              "from": "ms@0.6.2"
+            }
+          }
         },
         "mkdirp": {
           "version": "0.3.3",
-          "from": "mkdirp@0.3.3",
-          "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.3.3.tgz"
+          "from": "mkdirp@0.3.3"
         },
         "ms": {
           "version": "0.3.0",
-          "from": "ms@0.3.0",
-          "resolved": "https://registry.npmjs.org/ms/-/ms-0.3.0.tgz"
+          "from": "ms@0.3.0"
         }
       }
     },
               "from": "cssom@0.2.x"
             },
             "cssstyle": {
-              "version": "0.2.11",
+              "version": "0.2.14",
               "from": "cssstyle@>=0.2.3",
               "dependencies": {
                 "cssom": {
               }
             },
             "contextify": {
-              "version": "0.1.7",
+              "version": "0.1.8",
               "from": "contextify@0.1.x",
               "dependencies": {
                 "bindings": {
-                  "version": "1.2.0",
-                  "from": "bindings@*"
+                  "version": "1.2.1",
+                  "from": "bindings@*",
+                  "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.2.1.tgz"
                 },
                 "nan": {
-                  "version": "0.8.0",
-                  "from": "nan@~0.8.0"
+                  "version": "1.0.0",
+                  "from": "nan@~1.0.0"
                 }
               }
             }
           "from": "tough-cookie@~0.9.13",
           "dependencies": {
             "punycode": {
-              "version": "1.2.4",
-              "from": "punycode@~1.2.3"
+              "version": "1.3.1",
+              "from": "punycode@>=0.2.0",
+              "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.1.tgz"
             }
           }
         },
         "ws": {
-          "version": "0.4.31",
+          "version": "0.4.32",
           "from": "ws@~0.4.21",
           "dependencies": {
             "commander": {
-              "version": "0.6.1",
-              "from": "commander@~0.6.1"
+              "version": "2.1.0",
+              "from": "commander@~2.1.0",
+              "resolved": "https://registry.npmjs.org/commander/-/commander-2.1.0.tgz"
             },
             "nan": {
-              "version": "0.3.2",
-              "from": "nan@~0.3.0"
+              "version": "1.0.0",
+              "from": "nan@~1.0.0"
             },
             "tinycolor": {
               "version": "0.0.1",
         }
       }
     },
-    "jshint": {
-      "version": "2.4.4",
-      "from": "jshint@2.4.x",
-      "dependencies": {
-        "shelljs": {
-          "version": "0.1.4",
-          "from": "shelljs@0.1.x"
-        },
-        "cli": {
-          "version": "0.4.5",
-          "from": "cli@0.4.x",
-          "dependencies": {
-            "glob": {
-              "version": "3.2.9",
-              "from": "glob@>= 3.1.4",
-              "dependencies": {
-                "inherits": {
-                  "version": "2.0.1",
-                  "from": "inherits@2"
-                }
-              }
-            }
-          }
-        },
-        "minimatch": {
-          "version": "0.2.14",
-          "from": "minimatch@0.x.x",
-          "dependencies": {
-            "lru-cache": {
-              "version": "2.5.0",
-              "from": "lru-cache@2"
-            },
-            "sigmund": {
-              "version": "1.0.0",
-              "from": "sigmund@~1.0.0"
-            }
-          }
-        },
-        "htmlparser2": {
-          "version": "3.3.0",
-          "from": "htmlparser2@3.3.x",
-          "dependencies": {
-            "domhandler": {
-              "version": "2.1.0",
-              "from": "domhandler@2.1"
-            },
-            "domutils": {
-              "version": "1.1.6",
-              "from": "domutils@1.1"
-            },
-            "domelementtype": {
-              "version": "1.1.1",
-              "from": "domelementtype@1"
-            },
-            "readable-stream": {
-              "version": "1.0.27-1",
-              "from": "readable-stream@1.0",
-              "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.27-1.tgz",
-              "dependencies": {
-                "core-util-is": {
-                  "version": "1.0.1",
-                  "from": "core-util-is@~1.0.0"
-                },
-                "isarray": {
-                  "version": "0.0.1",
-                  "from": "isarray@0.0.1",
-                  "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz"
-                },
-                "string_decoder": {
-                  "version": "0.10.25-1",
-                  "from": "string_decoder@~0.10.x"
-                },
-                "inherits": {
-                  "version": "2.0.1",
-                  "from": "inherits@~2.0.1"
-                }
-              }
-            }
-          }
-        },
-        "console-browserify": {
-          "version": "0.1.6",
-          "from": "console-browserify@0.1.x"
-        },
-        "exit": {
-          "version": "0.1.2",
-          "from": "exit@0.1.x"
-        }
-      }
-    },
     "html5": {
       "version": "0.3.13",
       "from": "html5@0.3.13",
       "dependencies": {
         "jsdom": {
-          "version": "0.10.5",
+          "version": "0.11.1",
           "from": "jsdom@>= 0.6.0",
+          "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-0.11.1.tgz",
           "dependencies": {
             "htmlparser2": {
-              "version": "3.7.1",
+              "version": "3.7.3",
               "from": "htmlparser2@>= 3.1.5 <4",
               "dependencies": {
                 "domhandler": {
                   "from": "domhandler@2.2"
                 },
                 "domutils": {
-                  "version": "1.4.3",
-                  "from": "domutils@1.4"
+                  "version": "1.5.0",
+                  "from": "domutils@1.5"
                 },
                 "domelementtype": {
                   "version": "1.1.1",
                 "readable-stream": {
                   "version": "1.1.13-1",
                   "from": "readable-stream@1.1",
-                  "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.13-1.tgz",
                   "dependencies": {
                     "core-util-is": {
                       "version": "1.0.1",
                     },
                     "isarray": {
                       "version": "0.0.1",
-                      "from": "isarray@0.0.1",
-                      "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz"
+                      "from": "isarray@0.0.1"
                     },
                     "string_decoder": {
                       "version": "0.10.25-1",
               "from": "cssom@~0.3.0"
             },
             "cssstyle": {
-              "version": "0.2.11",
+              "version": "0.2.14",
               "from": "cssstyle@~0.2.9"
             },
             "contextify": {
-              "version": "0.1.7",
+              "version": "0.1.8",
               "from": "contextify@~0.1.5",
               "dependencies": {
                 "bindings": {
-                  "version": "1.2.0",
-                  "from": "bindings@*"
+                  "version": "1.2.1",
+                  "from": "bindings@*",
+                  "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.2.1.tgz"
                 },
                 "nan": {
-                  "version": "0.8.0",
-                  "from": "nan@~0.8.0"
+                  "version": "1.0.0",
+                  "from": "nan@~1.0.0"
                 }
               }
             }
           "from": "html5-entities@~0.5.0"
         }
       }
-    },
-    "nodemon": {
-      "version": "1.0.17",
-      "from": "nodemon@~1.0.15",
-      "dependencies": {
-        "update-notifier": {
-          "version": "0.1.8",
-          "from": "update-notifier@~0.1.7",
-          "dependencies": {
-            "request": {
-              "version": "2.27.0",
-              "from": "request@~2.27.0",
-              "dependencies": {
-                "qs": {
-                  "version": "0.6.6",
-                  "from": "qs@~0.6.0"
-                },
-                "json-stringify-safe": {
-                  "version": "5.0.0",
-                  "from": "json-stringify-safe@~5.0.0"
-                },
-                "forever-agent": {
-                  "version": "0.5.2",
-                  "from": "forever-agent@~0.5.0"
-                },
-                "tunnel-agent": {
-                  "version": "0.3.0",
-                  "from": "tunnel-agent@~0.3.0"
-                },
-                "http-signature": {
-                  "version": "0.10.0",
-                  "from": "http-signature@~0.10.0",
-                  "dependencies": {
-                    "assert-plus": {
-                      "version": "0.1.2",
-                      "from": "assert-plus@0.1.2"
-                    },
-                    "asn1": {
-                      "version": "0.1.11",
-                      "from": "asn1@0.1.11"
-                    },
-                    "ctype": {
-                      "version": "0.5.2",
-                      "from": "ctype@0.5.2"
-                    }
-                  }
-                },
-                "hawk": {
-                  "version": "1.0.0",
-                  "from": "hawk@~1.0.0",
-                  "dependencies": {
-                    "hoek": {
-                      "version": "0.9.1",
-                      "from": "hoek@0.9.x"
-                    },
-                    "boom": {
-                      "version": "0.4.2",
-                      "from": "boom@0.4.x"
-                    },
-                    "cryptiles": {
-                      "version": "0.2.2",
-                      "from": "cryptiles@0.2.x"
-                    },
-                    "sntp": {
-                      "version": "0.2.4",
-                      "from": "sntp@0.2.x"
-                    }
-                  }
-                },
-                "aws-sign": {
-                  "version": "0.3.0",
-                  "from": "aws-sign@~0.3.0"
-                },
-                "oauth-sign": {
-                  "version": "0.3.0",
-                  "from": "oauth-sign@~0.3.0"
-                },
-                "cookie-jar": {
-                  "version": "0.3.0",
-                  "from": "cookie-jar@~0.3.0"
-                },
-                "node-uuid": {
-                  "version": "1.4.1",
-                  "from": "node-uuid@~1.4.0"
-                },
-                "mime": {
-                  "version": "1.2.11",
-                  "from": "mime@~1.2.9",
-                  "resolved": "https://registry.npmjs.org/mime/-/mime-1.2.11.tgz"
-                },
-                "form-data": {
-                  "version": "0.1.2",
-                  "from": "form-data@~0.1.0",
-                  "dependencies": {
-                    "combined-stream": {
-                      "version": "0.0.4",
-                      "from": "combined-stream@~0.0.4",
-                      "dependencies": {
-                        "delayed-stream": {
-                          "version": "0.0.5",
-                          "from": "delayed-stream@0.0.5"
-                        }
-                      }
-                    }
-                  }
-                }
-              }
-            },
-            "configstore": {
-              "version": "0.2.3",
-              "from": "configstore@~0.2.2",
-              "dependencies": {
-                "mkdirp": {
-                  "version": "0.3.5",
-                  "from": "mkdirp@~0.3.5"
-                },
-                "js-yaml": {
-                  "version": "3.0.2",
-                  "from": "js-yaml@~3.0.1",
-                  "dependencies": {
-                    "argparse": {
-                      "version": "0.1.15",
-                      "from": "argparse@~ 0.1.11"
-                    },
-                    "esprima": {
-                      "version": "1.0.4",
-                      "from": "esprima@~ 1.0.2"
-                    }
-                  }
-                },
-                "osenv": {
-                  "version": "0.0.3",
-                  "from": "osenv@0.0.3",
-                  "resolved": "https://registry.npmjs.org/osenv/-/osenv-0.0.3.tgz"
-                },
-                "graceful-fs": {
-                  "version": "2.0.3",
-                  "from": "graceful-fs@~2.0.1"
-                },
-                "uuid": {
-                  "version": "1.4.1",
-                  "from": "uuid@~1.4.1"
-                },
-                "object-assign": {
-                  "version": "0.1.2",
-                  "from": "object-assign@~0.1.1"
-                }
-              }
-            },
-            "semver": {
-              "version": "2.1.0",
-              "from": "semver@~2.1.0"
-            },
-            "chalk": {
-              "version": "0.4.0",
-              "from": "chalk@~0.4.0",
-              "dependencies": {
-                "has-color": {
-                  "version": "0.1.7",
-                  "from": "has-color@~0.1.0",
-                  "resolved": "https://registry.npmjs.org/has-color/-/has-color-0.1.7.tgz"
-                },
-                "ansi-styles": {
-                  "version": "1.0.0",
-                  "from": "ansi-styles@~1.0.0"
-                },
-                "strip-ansi": {
-                  "version": "0.1.1",
-                  "from": "strip-ansi@~0.1.0"
-                }
-              }
-            }
-          }
-        },
-        "minimatch": {
-          "version": "0.2.14",
-          "from": "minimatch@~0.2.14",
-          "dependencies": {
-            "lru-cache": {
-              "version": "2.5.0",
-              "from": "lru-cache@2"
-            },
-            "sigmund": {
-              "version": "1.0.0",
-              "from": "sigmund@~1.0.0"
-            }
-          }
-        }
-      }
     }
   }
 }
index 8f1faad..438ed0a 100644 (file)
@@ -2,7 +2,7 @@
   "author": "xTuple <dev@xtuple.com>",
   "name": "xtuple",
   "description": "xTuple Enterprise Resource Planning Mobile-Web client",
-  "version": "4.6.0",
+  "version": "4.7.0-beta",
   "repository": {
     "type": "git",
     "url": "https://github.com/xtuple/xtuple.git"
@@ -24,7 +24,7 @@
     "less": "1.5.0",
     "moment": "2.4.x",
     "nodemailer": "0.3.x",
-    "npm":"1.4.x",
+    "npm":"1.2.30",
     "node-forge": "0.6.x",
     "oauth2orize": "0.1.x",
     "oauth2orize-jwt-bearer": "0.1.x",
   },
   "devDependencies": {
     "chai": "1.5.x",
-    "jshint": "2.4.x",
     "mocha": "1.9.x",
-    "require-uncache": "0.0.x",
     "html5": "0.3.13",
     "zombie": "1.4.x",
-    "csslint": "~0.10.0",
-    "xtuple-documentation": "~0.0.1",
-    "nodemon": "~1.0.15"
+    "csslint": "~0.10.0"
   },
   "optionalDependencies": {
     "xtuple-query": "~1.0.0",
index 62ee937..12fa090 100755 (executable)
@@ -15,19 +15,16 @@ regexp:true, undef:true, strict:true, trailing:true, white:true */
   var fs = require("fs"),
     program = require("commander"),
     path = require("path"),
-    buildDatabaseUtil = require("./lib/build_database_util");
+    explodeManifest = require("./lib/util/process_manifest").explodeManifest;
 
   program
     .option('-m, --manifest [/path/to/manifest.js]', 'Location of manifest file.')
     .option('-n, --name [inventory_upgrade.sql]', 'Name of destination file.')
     .parse(process.argv);
 
-  // the path is not relative if it starts with a slash
-  var manifestPath = program.manifest.substring(0, 1) === '/' ?
-    program.manifest :
-    path.join(process.cwd(), program.manifest);
+  var manifestFilename = path.resolve(process.cwd(), program.manifest);
 
-  buildDatabaseUtil.explodeManifest(manifestPath, {}, function (err, contents) {
+  explodeManifest({manifestFilename: manifestFilename}, function (err, contents) {
     var outputFile;
     if (err) {
       console.log("error: ", err);
index 611107a..3a1e08b 100644 (file)
@@ -5,14 +5,16 @@ regexp:true, undef:true, strict:true, trailing:true, white:true */
 var _ = require('underscore'),
   async = require('async'),
   buildDatabase = require("./build_database"),
-  buildDatabaseUtil = require("./build_database_util"),
   buildClient = require("./build_client").buildClient,
+  defaultExtensions = require("./util/default_extensions").extensions,
   dataSource = require('../../node-datasource/lib/ext/datasource').dataSource,
   exec = require('child_process').exec,
   fs = require('fs'),
+  initDatabase = require("./util/init_database").initDatabase,
+  inspectDatabaseExtensions = require("./util/inspect_database").inspectDatabaseExtensions,
   npm = require('npm'),
   path = require('path'),
-  unregister = buildDatabaseUtil.unregister,
+  unregister = require("./util/unregister").unregister,
   winston = require('winston');
 
 /*
@@ -31,6 +33,81 @@ var _ = require('underscore'),
 
   var creds;
 
+  var buildAll = function (specs, creds, buildAllCallback) {
+    async.series([
+      function (done) {
+        // step 0: init the database, if requested
+
+        if (specs.length === 1 &&
+            specs[0].initialize &&
+            (specs[0].backup || specs[0].source)) {
+
+          // The user wants to initialize the database first (i.e. Step 0)
+          // Do that, then call this function again
+          initDatabase(specs[0], creds, function (err, res) {
+            specs[0].wasInitialized = true;
+            done(err, res);
+          });
+          return;
+        } else {
+          done();
+        }
+
+      },
+      function (done) {
+        // step 1: npm install extension if necessary
+        // an alternate approach would be only npm install these
+        // extensions on an npm install.
+        var allExtensions = _.reduce(specs, function (memo, spec) {
+          memo.push(spec.extensions);
+          return _.flatten(memo);
+        }, []);
+        var npmExtensions = _.filter(allExtensions, function (extName) {
+          return extName && extName.indexOf("node_modules") >= 0;
+        });
+        if (npmExtensions.length === 0) {
+          done();
+          return;
+        }
+        npm.load(function (err, res) {
+          if (err) {
+            done(err);
+            return;
+          }
+          npm.on("log", function (message) {
+            // log the progress of the installation
+            console.log(message);
+          });
+          async.map(npmExtensions, function (extName, next) {
+            npm.commands.install([path.basename(extName)], next);
+          }, done);
+        });
+      },
+      function (done) {
+        // step 2: build the client
+        buildClient(specs, done);
+      },
+      function (done) {
+        // step 3: build the database
+        buildDatabase.buildDatabase(specs, creds, function (databaseErr, databaseRes) {
+          if (databaseErr) {
+            buildAllCallback(databaseErr);
+            return;
+          }
+          var returnMessage = "\n";
+          _.each(specs, function (spec) {
+            returnMessage += "Database: " + spec.database + '\nDirectories:\n';
+            _.each(spec.extensions, function (ext) {
+              returnMessage += '  ' + ext + '\n';
+            });
+          });
+          done(null, "Build succeeded." + returnMessage);
+        });
+      }
+    ], function (err, results) {
+      buildAllCallback(err, results && results[results.length - 1]);
+    });
+  };
 
   exports.build = function (options, callback) {
     var buildSpecs = {},
@@ -39,7 +116,7 @@ var _ = require('underscore'),
       getRegisteredExtensions = function (database, callback) {
         var credsClone = JSON.parse(JSON.stringify(creds));
         credsClone.database = database;
-        buildDatabaseUtil.inspectDatabaseExtensions(credsClone, function (err, paths) {
+        inspectDatabaseExtensions(credsClone, function (err, paths) {
           callback(null, {
             extensions: paths,
             database: database,
@@ -51,81 +128,6 @@ var _ = require('underscore'),
           });
         });
       },
-      buildAll = function (specs, creds, buildAllCallback) {
-        async.series([
-          function (done) {
-            // step 0: init the database, if requested
-
-            if (specs.length === 1 &&
-                specs[0].initialize &&
-                (specs[0].backup || specs[0].source)) {
-
-              // The user wants to initialize the database first (i.e. Step 0)
-              // Do that, then call this function again
-              buildDatabaseUtil.initDatabase(specs[0], creds, function (err, res) {
-                specs[0].wasInitialized = true;
-                done(err, res);
-              });
-              return;
-            } else {
-              done();
-            }
-
-          },
-          function (done) {
-            // step 1: npm install extension if necessary
-            // an alternate approach would be only npm install these
-            // extensions on an npm install.
-            var allExtensions = _.reduce(specs, function (memo, spec) {
-              memo.push(spec.extensions);
-              return _.flatten(memo);
-            }, []);
-            var npmExtensions = _.filter(allExtensions, function (extName) {
-              return extName && extName.indexOf("node_modules") >= 0;
-            });
-            if (npmExtensions.length === 0) {
-              done();
-              return;
-            }
-            npm.load(function (err, res) {
-              if (err) {
-                done(err);
-                return;
-              }
-              npm.on("log", function (message) {
-                // log the progress of the installation
-                console.log(message);
-              });
-              async.map(npmExtensions, function (extName, next) {
-                npm.commands.install([path.basename(extName)], next);
-              }, done);
-            });
-          },
-          function (done) {
-            // step 2: build the client
-            buildClient(specs, done);
-          },
-          function (done) {
-            // step 3: build the database
-            buildDatabase.buildDatabase(specs, creds, function (databaseErr, databaseRes) {
-              if (databaseErr) {
-                buildAllCallback(databaseErr);
-                return;
-              }
-              var returnMessage = "\n";
-              _.each(specs, function (spec) {
-                returnMessage += "Database: " + spec.database + '\nDirectories:\n';
-                _.each(spec.extensions, function (ext) {
-                  returnMessage += '  ' + ext + '\n';
-                });
-              });
-              done(null, "Build succeeded." + returnMessage);
-            });
-          }
-        ], function (err, results) {
-          buildAllCallback(err, results && results[results.length - 1]);
-        });
-      },
       config;
 
     if (options.config) {
@@ -176,7 +178,7 @@ var _ = require('underscore'),
         // an unmobilized build
         buildSpecs.extensions = options.extension ?
           [options.extension] :
-          buildDatabaseUtil.defaultExtensions;
+          defaultExtensions;
       }
       buildSpecs.initialize = true;
       buildSpecs.keepSql = options.keepSql;
index 2e0826f..adcf21d 100755 (executable)
@@ -73,30 +73,18 @@ var _ = require('underscore'),
           return;
         }
         fs.readFile(path.join(__dirname, "build", extName + ".css"), "utf8", function (err, cssCode) {
-          // get the extension version from the database manifest file
-          fs.readFile(path.join(extPath, "database/source/manifest.js"), "utf8", function (err, manifestContents) {
-            if (err) {
-              callback(err);
-              return;
-            }
-            var manifestDetails = JSON.parse(manifestContents);
-            if (!manifestDetails.version) {
-              // if the extensions don't declare their version, default to the package version
-              fs.readFile(path.join(__dirname, "../../package.json"), "utf8", function (err, packageJson) {
-                if (err) {
-                  callback(err);
-                  return;
-                }
-                var packageDetails = JSON.parse(packageJson);
-                callback(null, constructQuery(cssCode, extName, packageDetails.version, "css") +
-                  constructQuery(jsCode, extName, packageDetails.version, "js"));
-              });
-
-            } else {
-              callback(null, constructQuery(cssCode, extName, manifestDetails.version, "css") +
-                constructQuery(jsCode, extName, manifestDetails.version, "js"));
-            }
-          });
+          var version;
+          if (fs.existsSync(path.resolve(extPath, "package.json"))) {
+            version = require(path.resolve(extPath, "package.json")).version;
+          } else {
+            version = JSON.parse(fs.readFileSync(path.resolve(extPath, "database/source/manifest.js"))).version;
+          }
+          if (!version) {
+            // if the extensions don't declare their version, default to the package version
+            version = require(path.resolve(__dirname, "../../package.json")).version;
+          }
+          callback(null, constructQuery(cssCode, extName, version, "css") +
+            constructQuery(jsCode, extName, version, "js"));
         });
       });
     }
index aadd5d0..6f64695 100644 (file)
@@ -6,8 +6,8 @@ _ = require('underscore');
 
 var  async = require('async'),
   dataSource = require('../../node-datasource/lib/ext/datasource').dataSource,
-  buildDatabaseUtil = require('./build_database_util'),
   exec = require('child_process').exec,
+  explodeManifest = require("./util/process_manifest").explodeManifest,
   fs = require('fs'),
   ormInstaller = require('./orm'),
   dictionaryBuilder = require('./build_dictionary'),
@@ -15,6 +15,7 @@ var  async = require('async'),
   path = require('path'),
   pg = require('pg'),
   os = require('os'),
+  sendToDatabase = require("./util/send_to_database").sendToDatabase,
   winston = require('winston');
 
 (function () {
@@ -73,15 +74,20 @@ var  async = require('async'),
           isPublicExtension = extension.indexOf("xtuple-extensions") >= 0,
           isPrivateExtension = extension.indexOf("private-extensions") >= 0,
           isNpmExtension = baseName.indexOf("xtuple-") >= 0,
+          isExtension = !isFoundation && !isLibOrm && !isApplicationCore,
           dbSourceRoot = (isFoundation || isFoundationExtension) ? extension :
             isLibOrm ? path.join(extension, "source") :
             path.join(extension, "database/source"),
           manifestOptions = {
+            manifestFilename: path.resolve(dbSourceRoot, "manifest.js"),
+            extensionPath: isExtension ?
+              path.resolve(dbSourceRoot, "../../") :
+              undefined,
             useFrozenScripts: spec.frozen,
             useFoundationScripts: baseName.indexOf('inventory') >= 0 ||
               baseName.indexOf('manufacturing') >= 0 ||
               baseName.indexOf('distribution') >= 0,
-            registerExtension: !isFoundation && !isLibOrm && !isApplicationCore,
+            registerExtension: isExtension,
             runJsInit: !isFoundation && !isLibOrm,
             wipeViews: isApplicationCore && spec.wipeViews,
             extensionLocation: isCoreExtension ? "/core-extensions" :
@@ -90,8 +96,7 @@ var  async = require('async'),
               isNpmExtension ? "npm" : "not-applicable"
           };
 
-        buildDatabaseUtil.explodeManifest(path.join(dbSourceRoot, "manifest.js"),
-          manifestOptions, extensionCallback);
+        explodeManifest(manifestOptions, extensionCallback);
       };
 
       // We also need to get the sql that represents the queries to generate
@@ -189,16 +194,9 @@ var  async = require('async'),
         // on the case of error.
         allSql = "\\set ON_ERROR_STOP TRUE;\n" + allSql;
 
-        if (spec.wasInitialized && !_.isEqual(extensions, ["foundation-database"])) {
-          // give the admin user every extension by default
-          allSql = allSql + "insert into xt.usrext (usrext_usr_username, usrext_ext_id) " +
-            "select '" + creds.username +
-            "', ext_id from xt.ext where ext_location = '/core-extensions' and ext_name NOT LIKE 'oauth2';";
-        }
-
         winston.info("Applying build to database " + spec.database);
         credsClone.database = spec.database;
-        buildDatabaseUtil.sendToDatabase(allSql, credsClone, spec, function (err, res) {
+        sendToDatabase(allSql, credsClone, spec, function (err, res) {
           if (spec.populateData && creds.encryptionKeyFile) {
             var populateSql = "DO $$ XT.disableLocks = true; $$ language plv8;";
             var encryptionKey = fs.readFileSync(path.resolve(__dirname, "../../node-datasource", creds.encryptionKeyFile), "utf8");
diff --git a/scripts/lib/build_database_util.js b/scripts/lib/build_database_util.js
deleted file mode 100644 (file)
index 8ce8b9c..0000000
+++ /dev/null
@@ -1,618 +0,0 @@
-/*jshint node:true, indent:2, curly:false, eqeqeq:true, immed:true, latedef:true, newcap:true, noarg:true,
-regexp:true, undef:true, strict:true, trailing:true, white:true */
-/*global _:true */
-
-(function () {
-  "use strict";
-
-  var _ = require('underscore'),
-    async = require('async'),
-    exec = require('child_process').exec,
-    fs = require('fs'),
-    os = require('os'),
-    path = require('path'),
-    dataSource = require('../../node-datasource/lib/ext/datasource').dataSource,
-    winston = require('winston');
-
-  var defaultExtensions = [
-    path.join(__dirname, '../../foundation-database'),
-    path.join(__dirname, '../../lib/orm'),
-    path.join(__dirname, '../../enyo-client'),
-    path.join(__dirname, '../../enyo-client/extensions/source/crm'),
-    path.join(__dirname, '../../enyo-client/extensions/source/project'),
-    path.join(__dirname, '../../enyo-client/extensions/source/sales'),
-    path.join(__dirname, '../../enyo-client/extensions/source/billing'),
-    path.join(__dirname, '../../enyo-client/extensions/source/purchasing'),
-    path.join(__dirname, '../../enyo-client/extensions/source/oauth2')
-  ];
-
-  var convertFromMetasql = function (content, filename, defaultSchema) {
-    var lines = content.split("\n"),
-      schema = defaultSchema ? "'" + defaultSchema + "'" : "NULL",
-      group,
-      i = 2,
-      name,
-      notes = "",
-      grade = 0,
-      deleteSql,
-      insertSql;
-
-    if (lines[0].indexOf("-- Group: ") !== 0 ||
-        lines[1].indexOf("-- Name: ") !== 0 ||
-        lines[2].indexOf("-- Notes:") !== 0) {
-      throw new Error("Improperly formatted metasql: " + filename);
-    }
-    group = lines[0].substring("-- Group: ".length).trim();
-    name = lines[1].substring("-- Name: ".length).trim();
-    while (lines[i].indexOf("--") === 0) {
-      notes = notes + lines[i].substring(2) + "\n";
-      i++;
-    }
-    notes = notes.substring(" Notes:".length);
-    if (notes.indexOf("must be grade 10") >= 0) {
-      grade = 10;
-    }
-
-    insertSql = "select saveMetasql (" +
-      "'" + group + "'," +
-      "'" + name + "'," +
-      "$$" + notes + "$$," +
-      "$$" + content + "$$," +
-      "true, " + schema + ", " + grade + ");";
-
-    return insertSql;
-  };
-
-  var convertFromReport = function (content, filename, defaultSchema) {
-    var lines = content.split("\n"),
-      name,
-      grade = "0",
-      tableName = defaultSchema ? defaultSchema + ".pkgreport" : "report",
-      description,
-      disableSql,
-      updateSql,
-      insertSql,
-      enableSql;
-
-    if (lines[3].indexOf(" <name>") !== 0 ||
-        lines[4].indexOf(" <description>") !== 0) {
-      throw new Error("Improperly formatted report");
-    }
-    name = lines[3].substring(" <name>".length).trim();
-    name = name.substring(0, name.indexOf("<"));
-    description = lines[4].substring(" <description>".length).trim();
-    description = description.substring(0, description.indexOf("<"));
-    if (lines[5].indexOf("grade") >= 0) {
-      grade = lines[5].substring(" <grade>".length).trim();
-      grade = grade.substring(0, grade.indexOf("<"));
-    }
-
-    disableSql = "ALTER TABLE " + tableName + " DISABLE TRIGGER ALL;";
-
-    insertSql = "insert into " + tableName + " (report_name, report_descrip, " +
-      "report_source, report_loaddate, report_grade) select " +
-      "'" + name + "'," +
-      "$$" + description + "$$," +
-      "$$" + content + "$$," +
-      "now(), " + grade +
-      " where not exists (select c.report_id from " + tableName + " c " +
-      "where report_name = '" + name +
-      "' and report_grade = " + grade + ");";
-
-    updateSql = "update " + tableName + " set " +
-      " report_descrip = $$" + description +
-      "$$, report_source = $$" + content +
-      "$$, report_loaddate = now() " +
-      "where report_name = '" + name +
-      "' and report_grade = " + grade + ";";
-
-    enableSql = "ALTER TABLE " + tableName + " ENABLE TRIGGER ALL;";
-
-    return disableSql + insertSql + updateSql + enableSql;
-  };
-
-  var convertFromScript = function (content, filename, defaultSchema) {
-    var name = path.basename(filename, '.js'),
-      tableName = defaultSchema ? defaultSchema + ".pkgscript" : "unknown",
-      notes = "", //"xtMfg package",
-      disableSql,
-      insertSql,
-      updateSql,
-      enableSql;
-
-    disableSql = "ALTER TABLE " + tableName + " DISABLE TRIGGER ALL;";
-
-    insertSql = "insert into " + tableName + " (script_name, script_order, script_enabled, " +
-      "script_source, script_notes) select " +
-      "'" + name + "', 0, TRUE, " +
-      "$$" + content + "$$," +
-      "'" + notes + "'" +
-      " where not exists (select c.script_id from " + tableName + " c " +
-      "where script_name = '" + name + "');";
-
-    updateSql = "update " + tableName + " set " +
-      "script_name = '" + name + "', script_order = 0, script_enabled = TRUE, " +
-      "script_source = $$" + content +
-      "$$, script_notes = '" + notes + "' " +
-      "where script_name = '" + name + "';";
-
-    enableSql = "ALTER TABLE " + tableName + " ENABLE TRIGGER ALL;";
-
-    return disableSql + insertSql + updateSql + enableSql;
-  };
-
-  var convertFromUiform = function (content, filename, defaultSchema) {
-    var name = path.basename(filename, '.ui'),
-      tableName = defaultSchema ? defaultSchema + ".pkguiform" : "unknown",
-      notes = "", //"xtMfg package",
-      disableSql,
-      insertSql,
-      updateSql,
-      enableSql;
-
-    disableSql = "ALTER TABLE " + tableName + " DISABLE TRIGGER ALL;";
-
-    insertSql = "insert into " + tableName + " (uiform_name, uiform_order, uiform_enabled, " +
-      "uiform_source, uiform_notes) select " +
-      "'" + name + "', 0, TRUE, " +
-      "$$" + content + "$$," +
-      "'" + notes + "' " +
-      " where not exists (select c.uiform_id from " + tableName + " c " +
-      "where uiform_name = '" + name + "');";
-
-    updateSql = "update " + tableName + " set uiform_name = '" +
-      name + "', uiform_order = 0, uiform_enabled = TRUE, " +
-      "uiform_source = $$" + content + "$$, uiform_notes = '" + notes + "' " +
-      "where uiform_name = '" + name + "';";
-
-    enableSql = "ALTER TABLE " + tableName + " ENABLE TRIGGER ALL;";
-
-    return disableSql + insertSql + updateSql + enableSql;
-  };
-
-  var conversionMap = {
-    mql: convertFromMetasql,
-    xml: convertFromReport,
-    js: convertFromScript,
-    ui: convertFromUiform,
-    sql: function (content) {
-      // no op
-      return content;
-    }
-  };
-
-  var explodeManifest = function (manifestFilename, options, manifestCallback) {
-    var dbSourceRoot = path.dirname(manifestFilename);
-    //
-    // Step 2:
-    // Read the manifest files.
-    //
-    if (!fs.existsSync(manifestFilename)) {
-      // error condition: no manifest file
-      manifestCallback("Cannot find manifest " + manifestFilename);
-      return;
-    }
-    fs.readFile(manifestFilename, "utf8", function (err, manifestString) {
-      var manifest,
-        databaseScripts,
-        extraManifestPath,
-        defaultSchema,
-        extraManifest,
-        extraManifestScripts,
-        alterPaths = dbSourceRoot.indexOf("foundation-database") < 0,
-        extensionName,
-        loadOrder,
-        extensionComment;
-
-      try {
-        manifest = JSON.parse(manifestString);
-        extensionName = manifest.name;
-        extensionComment = manifest.comment;
-        databaseScripts = manifest.databaseScripts;
-        defaultSchema = manifest.defaultSchema;
-        loadOrder = manifest.loadOrder || 999;
-
-      } catch (error) {
-        // error condition: manifest file is not properly formatted
-        manifestCallback("Manifest is not valid JSON" + manifestFilename);
-        return;
-      }
-
-      //
-      // Step 2b:
-      //
-
-      // supported use cases:
-
-      // 1. add mobilized inventory to quickbooks
-      // need the frozen_manifest, the foundation/manifest, and the mobile manifest
-      // -e ../private-extensions/source/inventory -f
-      // useFrozenScripts, useFoundationScripts
-
-      // 2. add mobilized inventory to masterref (foundation inventory is already there)
-      // need the the foundation/manifest and the mobile manifest
-      // -e ../private-extensions/source/inventory
-      // useFoundationScripts
-
-      // 3. add unmobilized inventory to quickbooks
-      // need the frozen_manifest and the foundation/manifest
-      // -e ../private-extensions/source/inventory/foundation-database -f
-      // useFrozenScripts (useFoundationScripts already taken care of by -e path)
-
-      // 4. upgrade unmobilized inventory
-      // not sure if this is necessary, but it would look like
-      // -e ../private-extensions/source/inventory/foundation-database
-
-      if (options.useFoundationScripts) {
-        extraManifest = JSON.parse(fs.readFileSync(path.join(dbSourceRoot, "../../foundation-database/manifest.js")));
-        defaultSchema = defaultSchema || extraManifest.defaultSchema;
-        extraManifestScripts = extraManifest.databaseScripts;
-        extraManifestScripts = _.map(extraManifestScripts, function (path) {
-          return "../../foundation-database/" + path;
-        });
-        databaseScripts.unshift(extraManifestScripts);
-        databaseScripts = _.flatten(databaseScripts);
-      }
-      if (options.useFrozenScripts) {
-        // Frozen files are not idempotent and should only be run upon first registration
-        extraManifestPath = alterPaths ?
-         path.join(dbSourceRoot, "../../foundation-database/frozen_manifest.js") :
-         path.join(dbSourceRoot, "frozen_manifest.js");
-
-        extraManifest = JSON.parse(fs.readFileSync(extraManifestPath));
-        defaultSchema = defaultSchema || extraManifest.defaultSchema;
-        extraManifestScripts = extraManifest.databaseScripts;
-        if (alterPaths) {
-          extraManifestScripts = _.map(extraManifestScripts, function (path) {
-            return "../../foundation-database/" + path;
-          });
-        }
-        databaseScripts.unshift(extraManifestScripts);
-        databaseScripts = _.flatten(databaseScripts);
-      }
-
-      //
-      // Step 3:
-      // Concatenate together all the files referenced in the manifest.
-      //
-      var getScriptSql = function (filename, scriptCallback) {
-        var fullFilename = path.join(dbSourceRoot, filename);
-        if (!fs.existsSync(fullFilename)) {
-          // error condition: script referenced in manifest.js isn't there
-          scriptCallback(path.join(dbSourceRoot, filename) + " does not exist");
-          return;
-        }
-        fs.readFile(fullFilename, "utf8", function (err, scriptContents) {
-          // error condition: can't read script
-          if (err) {
-            scriptCallback(err);
-            return;
-          }
-          var beforeNoticeSql = "do $$ BEGIN RAISE NOTICE 'Loading file " + path.basename(fullFilename) +
-              "'; END $$ language plpgsql;\n",
-            extname = path.extname(fullFilename).substring(1);
-
-          // convert special files: metasql, uiforms, reports, uijs
-          scriptContents = conversionMap[extname](scriptContents, fullFilename, defaultSchema);
-          //
-          // Allow inclusion of js files in manifest. If it is a js file,
-          // use plv8 to execute it.
-          //
-          //if (fullFilename.substring(fullFilename.length - 2) === 'js') {
-            // this isn't quite working yet
-            // http://adpgtech.blogspot.com/2013/03/loading-useful-modules-in-plv8.html
-            // put in lib/orm's manifest.js: "../../tools/lib/underscore/underscore-min.js",
-          //  scriptContents = "do $$ " + scriptContents + " $$ language plv8;";
-          //}
-
-          //
-          // Incorrectly-ended sql files (i.e. no semicolon) make for unhelpful error messages
-          // when we concatenate 100's of them together. Guard against these.
-          //
-          scriptContents = scriptContents.trim();
-          if (scriptContents.charAt(scriptContents.length - 1) !== ';') {
-            // error condition: script is improperly formatted
-            scriptCallback("Error: " + fullFilename + " contents do not end in a semicolon.");
-          }
-
-          scriptCallback(null, '\n' + scriptContents);
-        });
-      };
-      async.mapSeries(databaseScripts || [], getScriptSql, function (err, scriptSql) {
-        var registerSql,
-          dependencies;
-
-        if (err) {
-          manifestCallback(err);
-          return;
-        }
-        // each String of the scriptContents is the concatenated SQL for the script.
-        // join these all together into a single string for the whole extension.
-        var extensionSql = _.reduce(scriptSql, function (memo, script) {
-          return memo + script;
-        }, "");
-
-        if (options.registerExtension) {
-          // register extension and dependencies
-          extensionSql = 'do $$ plv8.elog(NOTICE, "About to register extension ' +
-            extensionName + '"); $$ language plv8;\n' + extensionSql;
-
-          registerSql = "select xt.register_extension('%@', '%@', '%@', '', %@);\n"
-            .f(extensionName, extensionComment, options.extensionLocation, loadOrder);
-
-          var grantExtToAdmin = "select xt.grant_role_ext('ADMIN', '%@');\n"
-            .f(extensionName);
-
-          extensionSql = grantExtToAdmin + extensionSql;
-
-          dependencies = manifest.dependencies || [];
-          _.each(dependencies, function (dependency) {
-            var dependencySql = "select xt.register_extension_dependency('%@', '%@');\n"
-                .f(extensionName, dependency),
-              grantDependToAdmin = "select xt.grant_role_ext('ADMIN', '%@');\n"
-                .f(dependency);
-
-            extensionSql = dependencySql + grantDependToAdmin + extensionSql;
-          });
-          extensionSql = registerSql + extensionSql;
-        }
-        if (options.runJsInit) {
-          // unless it it hasn't yet been defined (ie. lib/orm),
-          // running xt.js_init() is probably a good idea.
-          extensionSql = "select xt.js_init();" + extensionSql;
-        }
-
-        if (options.wipeViews) {
-          // If we want to pre-emptively wipe out the views, the best place to do it
-          // is at the start of the core application code
-          fs.readFile(path.join(__dirname, "../../enyo-client/database/source/delete_system_orms.sql"),
-              function (err, wipeSql) {
-            if (err) {
-              manifestCallback(err);
-              return;
-            }
-            extensionSql = wipeSql + extensionSql;
-            manifestCallback(null, extensionSql);
-          });
-        } else {
-          manifestCallback(null, extensionSql);
-        }
-
-      });
-      //
-      // End script installation code
-      //
-    });
-  };
-
-  var pathFromExtension = function (name, location) {
-    if (location === '/core-extensions') {
-      return path.join(__dirname, "/../../enyo-client/extensions/source/", name);
-    } else if (location === '/xtuple-extensions') {
-      return path.join(__dirname, "../../../xtuple-extensions/source", name);
-    } else if (location === '/private-extensions') {
-      return path.join(__dirname, "../../../private-extensions/source", name);
-    } else if (location === 'npm') {
-      return path.join(__dirname, "../../node_modules", name);
-    }
-  };
-
-  //
-  // Looks in a database to see which extensions are registered, and
-  // tacks onto that list the core directories.
-  //
-  var inspectMobilizedDatabase = function (creds, done) {
-    var extSql = "SELECT * FROM xt.ext ORDER BY ext_load_order";
-    dataSource.query(extSql, creds, function (err, res) {
-      if (err) {
-        return done(err);
-      }
-
-      var paths = _.map(_.compact(res.rows), function (row) {
-        return pathFromExtension(row.ext_name, row.ext_location);
-      });
-
-      paths.unshift(path.join(__dirname, "../../enyo-client")); // core path
-      paths.unshift(path.join(__dirname, "../../lib/orm")); // lib path
-      paths.unshift(path.join(__dirname, "../../foundation-database")); // foundation path
-      done(null, _.compact(paths));
-    });
-  };
-
-  var inspectUnmobilizedDatabase = function (creds, done) {
-    var extSql = "select * from public.pkghead where pkghead_name in ('xtmfg', 'xwd');",
-      editionMap = {
-        xtmfg: ["inventory", "manufacturing"],
-        xwd: ["inventory", "distribution"]
-      };
-    dataSource.query(extSql, creds, function (err, res) {
-      if (err) {
-        return done(err);
-      }
-      var extensions = _.unique(_.flatten(_.map(res.rows, function (row) {
-        return _.map(editionMap[row.pkghead_name], function (ext) {
-          return path.join(__dirname, "../../../private-extensions/source", ext);
-        });
-      })));
-      done(err, defaultExtensions.concat(extensions));
-    });
-  };
-
-  var inspectDatabaseExtensions = function (creds, done) {
-    var isMobilizedSql = "select * from information_schema.tables where table_schema = 'xt' and table_name = 'ext';";
-
-    dataSource.query(isMobilizedSql, creds, function (err, res) {
-      if (res.rowCount === 0) {
-        inspectUnmobilizedDatabase(creds, done);
-      } else {
-        inspectMobilizedDatabase(creds, done);
-      }
-    });
-  };
-
-  //
-  // Step 0 (optional, triggered by flags), wipe out the database
-  // and load it from scratch using pg_restore something.backup unless
-  // we're building from source.
-  //
-  var initDatabase = function (spec, creds, callback) {
-    var databaseName = spec.database,
-      credsClone = JSON.parse(JSON.stringify(creds)),
-      dropDatabase = function (done) {
-        winston.info("Dropping database " + databaseName);
-        // the calls to drop and create the database need to be run against the database "postgres"
-        credsClone.database = "postgres";
-        dataSource.query("drop database if exists " + databaseName + ";", credsClone, done);
-      },
-      createDatabase = function (done) {
-        winston.info("Creating database " + databaseName);
-        dataSource.query("create database " + databaseName + " template template1;", credsClone, done);
-      },
-      buildSchema = function (done) {
-        var schemaPath = path.join(path.dirname(spec.source), "440_schema.sql");
-        winston.info("Building schema for database " + databaseName);
-
-        exec("psql -U " + creds.username + " -h " + creds.hostname + " --single-transaction -p " +
-          creds.port + " -d " + databaseName + " -f " + schemaPath,
-          {maxBuffer: 40000 * 1024 /* 200x default */}, done);
-      },
-      populateData = function (done) {
-        winston.info("Populating data for database " + databaseName + " from " + spec.source);
-        exec("psql -U " + creds.username + " -h " + creds.hostname + " --single-transaction -p " +
-          creds.port + " -d " + databaseName + " -f " + spec.source,
-          {maxBuffer: 40000 * 1024 /* 200x default */}, done);
-      },
-      // use exec to restore the backup. The alternative, reading the backup file into a string to query
-      // doesn't work because the backup file is binary.
-      restoreBackup = function (done) {
-        exec("pg_restore -U " + creds.username + " -h " + creds.hostname + " -p " +
-          creds.port + " -d " + databaseName + " -j " + os.cpus().length + " " + spec.backup, function (err, res) {
-          if (err) {
-            console.log("ignoring restore db error", err);
-          }
-          done(null, res);
-        });
-      },
-      finish = function (err, results) {
-        if (err) {
-          winston.error("init database error", err.message, err.stack, err);
-        }
-        callback(err, results);
-      };
-
-    if (spec.source) {
-      async.series([
-        dropDatabase,
-        createDatabase,
-        buildSchema,
-        populateData
-      ], finish);
-    } else {
-      async.series([
-        dropDatabase,
-        createDatabase,
-        restoreBackup,
-        function (done) {
-          credsClone.database = databaseName;
-          inspectDatabaseExtensions(credsClone, function (err, paths) {
-            // in the case of a build-from-backup, we ignore any user desires and dictate the extensions
-            spec.extensions = paths;
-            done();
-          });
-        }
-      ], finish);
-    }
-  };
-
-
-  var sendToDatabase = function (query, credsClone, options, callback) {
-    var filename = path.join(__dirname, "temp_query_" + credsClone.database + ".sql");
-    fs.writeFile(filename, query, function (err) {
-      if (err) {
-        winston.error("Cannot write query to file");
-        callback(err);
-        return;
-      }
-      var psqlCommand = 'psql -d ' + credsClone.database +
-        ' -U ' + credsClone.username +
-        ' -h ' + credsClone.hostname +
-        ' -p ' + credsClone.port +
-        ' -f ' + filename +
-        ' --single-transaction';
-
-
-      /**
-       * http://nodejs.org/api/child_process.html#child_process_child_process_exec_command_options_callback
-       * "maxBuffer specifies the largest amount of data allowed on stdout or
-       * stderr - if this value is exceeded then the child process is killed."
-       */
-      exec(psqlCommand, {maxBuffer: 40000 * 1024 /* 200x default */}, function (err, stdout, stderr) {
-        if (err) {
-          winston.error("Cannot install file ", filename);
-          callback(err);
-          return;
-        }
-        if (options.keepSql) {
-          // do not delete the temp query file
-          winston.info("SQL file kept as ", filename);
-          callback();
-        } else {
-          fs.unlink(filename, function (err) {
-            if (err) {
-              winston.error("Cannot delete written query file");
-              callback(err);
-            }
-            callback();
-          });
-        }
-      });
-    });
-  };
-
-  //
-  // Another option: unregister the extension
-  //
-  var unregister = function (specs, creds, masterCallback) {
-    var extension = path.basename(specs[0].extensions[0]),
-      unregisterSql = ["delete from xt.usrext where usrext_id in " +
-        "(select usrext_id from xt.usrext inner join xt.ext on usrext_ext_id = ext_id where ext_name = $1);",
-
-        "delete from xt.grpext where grpext_id in " +
-        "(select grpext_id from xt.grpext inner join xt.ext on grpext_ext_id = ext_id where ext_name = $1);",
-
-        "delete from xt.clientcode where clientcode_id in " +
-        "(select clientcode_id from xt.clientcode inner join xt.ext on clientcode_ext_id = ext_id where ext_name = $1);",
-
-        "delete from xt.dict where dict_id in " +
-        "(select dict_id from xt.dict inner join xt.ext on dict_ext_id = ext_id where ext_name = $1);",
-
-        "delete from xt.extdep where extdep_id in " +
-        "(select extdep_id from xt.extdep inner join xt.ext " +
-        "on extdep_from_ext_id = ext_id or extdep_to_ext_id = ext_id where ext_name = $1);",
-
-        "delete from xt.ext where ext_name = $1;"];
-
-    if (extension.charAt(extension.length - 1) === "/") {
-      // remove trailing slash if present
-      extension = extension.substring(0, extension.length - 1);
-    }
-    winston.info("Unregistering extension:", extension);
-    var unregisterEach = function (spec, callback) {
-      var options = JSON.parse(JSON.stringify(creds));
-      options.database = spec.database;
-      options.parameters = [extension];
-      var queryEach = function (sql, sqlCallback) {
-        dataSource.query(sql, options, sqlCallback);
-      };
-      async.eachSeries(unregisterSql, queryEach, callback);
-    };
-    async.each(specs, unregisterEach, masterCallback);
-  };
-
-  exports.defaultExtensions = defaultExtensions;
-  exports.inspectDatabaseExtensions = inspectDatabaseExtensions;
-  exports.explodeManifest = explodeManifest;
-  exports.initDatabase = initDatabase;
-  exports.sendToDatabase = sendToDatabase;
-  exports.unregister = unregister;
-}());
diff --git a/scripts/lib/util/convert_specialized.js b/scripts/lib/util/convert_specialized.js
new file mode 100644 (file)
index 0000000..95b8d52
--- /dev/null
@@ -0,0 +1,164 @@
+/*jshint node:true, indent:2, curly:false, eqeqeq:true, immed:true, latedef:true, newcap:true, noarg:true,
+regexp:true, undef:true, strict:true, trailing:true, white:true */
+/*global _:true */
+
+(function () {
+  "use strict";
+
+  var path = require('path');
+
+  var convertFromMetasql = function (content, filename, defaultSchema) {
+    var lines = content.split("\n"),
+      schema = defaultSchema ? "'" + defaultSchema + "'" : "NULL",
+      group,
+      i = 2,
+      name,
+      notes = "",
+      grade = 0,
+      deleteSql,
+      insertSql;
+
+    if (lines[0].indexOf("-- Group: ") !== 0 ||
+        lines[1].indexOf("-- Name: ") !== 0 ||
+        lines[2].indexOf("-- Notes:") !== 0) {
+      throw new Error("Improperly formatted metasql: " + filename);
+    }
+    group = lines[0].substring("-- Group: ".length).trim();
+    name = lines[1].substring("-- Name: ".length).trim();
+    while (lines[i].indexOf("--") === 0) {
+      notes = notes + lines[i].substring(2) + "\n";
+      i++;
+    }
+    notes = notes.substring(" Notes:".length);
+    if (notes.indexOf("must be grade 10") >= 0) {
+      grade = 10;
+    }
+
+    insertSql = "select saveMetasql (" +
+      "'" + group + "'," +
+      "'" + name + "'," +
+      "$$" + notes + "$$," +
+      "$$" + content + "$$," +
+      "true, " + schema + ", " + grade + ");";
+
+    return insertSql;
+  };
+
+  var convertFromReport = function (content, filename, defaultSchema) {
+    var lines = content.split("\n"),
+      name,
+      grade = "0",
+      tableName = defaultSchema ? defaultSchema + ".pkgreport" : "report",
+      description,
+      disableSql,
+      updateSql,
+      insertSql,
+      enableSql;
+
+    if (lines[3].indexOf(" <name>") !== 0 ||
+        lines[4].indexOf(" <description>") !== 0) {
+      throw new Error("Improperly formatted report");
+    }
+    name = lines[3].substring(" <name>".length).trim();
+    name = name.substring(0, name.indexOf("<"));
+    description = lines[4].substring(" <description>".length).trim();
+    description = description.substring(0, description.indexOf("<"));
+    if (lines[5].indexOf("grade") >= 0) {
+      grade = lines[5].substring(" <grade>".length).trim();
+      grade = grade.substring(0, grade.indexOf("<"));
+    }
+
+    disableSql = "ALTER TABLE " + tableName + " DISABLE TRIGGER ALL;";
+
+    insertSql = "insert into " + tableName + " (report_name, report_descrip, " +
+      "report_source, report_loaddate, report_grade) select " +
+      "'" + name + "'," +
+      "$$" + description + "$$," +
+      "$$" + content + "$$," +
+      "now(), " + grade +
+      " where not exists (select c.report_id from " + tableName + " c " +
+      "where report_name = '" + name +
+      "' and report_grade = " + grade + ");";
+
+    updateSql = "update " + tableName + " set " +
+      " report_descrip = $$" + description +
+      "$$, report_source = $$" + content +
+      "$$, report_loaddate = now() " +
+      "where report_name = '" + name +
+      "' and report_grade = " + grade + ";";
+
+    enableSql = "ALTER TABLE " + tableName + " ENABLE TRIGGER ALL;";
+
+    return disableSql + insertSql + updateSql + enableSql;
+  };
+
+  var convertFromScript = function (content, filename, defaultSchema) {
+    var name = path.basename(filename, '.js'),
+      tableName = defaultSchema ? defaultSchema + ".pkgscript" : "unknown",
+      notes = "", //"xtMfg package",
+      disableSql,
+      insertSql,
+      updateSql,
+      enableSql;
+
+    disableSql = "ALTER TABLE " + tableName + " DISABLE TRIGGER ALL;";
+
+    insertSql = "insert into " + tableName + " (script_name, script_order, script_enabled, " +
+      "script_source, script_notes) select " +
+      "'" + name + "', 0, TRUE, " +
+      "$$" + content + "$$," +
+      "'" + notes + "'" +
+      " where not exists (select c.script_id from " + tableName + " c " +
+      "where script_name = '" + name + "');";
+
+    updateSql = "update " + tableName + " set " +
+      "script_name = '" + name + "', script_order = 0, script_enabled = TRUE, " +
+      "script_source = $$" + content +
+      "$$, script_notes = '" + notes + "' " +
+      "where script_name = '" + name + "';";
+
+    enableSql = "ALTER TABLE " + tableName + " ENABLE TRIGGER ALL;";
+
+    return disableSql + insertSql + updateSql + enableSql;
+  };
+
+  var convertFromUiform = function (content, filename, defaultSchema) {
+    var name = path.basename(filename, '.ui'),
+      tableName = defaultSchema ? defaultSchema + ".pkguiform" : "unknown",
+      notes = "", //"xtMfg package",
+      disableSql,
+      insertSql,
+      updateSql,
+      enableSql;
+
+    disableSql = "ALTER TABLE " + tableName + " DISABLE TRIGGER ALL;";
+
+    insertSql = "insert into " + tableName + " (uiform_name, uiform_order, uiform_enabled, " +
+      "uiform_source, uiform_notes) select " +
+      "'" + name + "', 0, TRUE, " +
+      "$$" + content + "$$," +
+      "'" + notes + "' " +
+      " where not exists (select c.uiform_id from " + tableName + " c " +
+      "where uiform_name = '" + name + "');";
+
+    updateSql = "update " + tableName + " set uiform_name = '" +
+      name + "', uiform_order = 0, uiform_enabled = TRUE, " +
+      "uiform_source = $$" + content + "$$, uiform_notes = '" + notes + "' " +
+      "where uiform_name = '" + name + "';";
+
+    enableSql = "ALTER TABLE " + tableName + " ENABLE TRIGGER ALL;";
+
+    return disableSql + insertSql + updateSql + enableSql;
+  };
+
+  exports.conversionMap = {
+    mql: convertFromMetasql,
+    xml: convertFromReport,
+    js: convertFromScript,
+    ui: convertFromUiform,
+    sql: function (content) {
+      // no op
+      return content;
+    }
+  };
+}());
diff --git a/scripts/lib/util/default_extensions.js b/scripts/lib/util/default_extensions.js
new file mode 100644 (file)
index 0000000..ffaf37c
--- /dev/null
@@ -0,0 +1,21 @@
+/*jshint node:true, indent:2, curly:false, eqeqeq:true, immed:true, latedef:true, newcap:true, noarg:true,
+regexp:true, undef:true, strict:true, trailing:true, white:true */
+/*global _:true */
+
+(function () {
+  "use strict";
+
+  var path = require('path');
+
+  exports.extensions = [
+    path.join(__dirname, '../../../foundation-database'),
+    path.join(__dirname, '../../../lib/orm'),
+    path.join(__dirname, '../../../enyo-client'),
+    path.join(__dirname, '../../../enyo-client/extensions/source/crm'),
+    path.join(__dirname, '../../../enyo-client/extensions/source/project'),
+    path.join(__dirname, '../../../enyo-client/extensions/source/sales'),
+    path.join(__dirname, '../../../enyo-client/extensions/source/billing'),
+    path.join(__dirname, '../../../enyo-client/extensions/source/purchasing'),
+    path.join(__dirname, '../../../enyo-client/extensions/source/oauth2')
+  ];
+}());
diff --git a/scripts/lib/util/init_database.js b/scripts/lib/util/init_database.js
new file mode 100644 (file)
index 0000000..4dca75c
--- /dev/null
@@ -0,0 +1,91 @@
+/*jshint node:true, indent:2, curly:false, eqeqeq:true, immed:true, latedef:true, newcap:true, noarg:true,
+regexp:true, undef:true, strict:true, trailing:true, white:true */
+/*global _:true */
+
+(function () {
+  "use strict";
+
+  var async = require("async"),
+    exec = require('child_process').exec,
+    path = require('path'),
+    os = require('os'),
+    winston = require('winston'),
+    dataSource = require('../../../node-datasource/lib/ext/datasource').dataSource,
+    inspectDatabaseExtensions = require("./inspect_database").inspectDatabaseExtensions;
+
+  //
+  // Wipe out the database
+  // and load it from scratch using pg_restore something.backup unless
+  // we're building from source.
+  //
+  var initDatabase = function (spec, creds, callback) {
+    var databaseName = spec.database,
+      credsClone = JSON.parse(JSON.stringify(creds)),
+      dropDatabase = function (done) {
+        winston.info("Dropping database " + databaseName);
+        // the calls to drop and create the database need to be run against the database "postgres"
+        credsClone.database = "postgres";
+        dataSource.query("drop database if exists " + databaseName + ";", credsClone, done);
+      },
+      createDatabase = function (done) {
+        winston.info("Creating database " + databaseName);
+        dataSource.query("create database " + databaseName + " template template1;", credsClone, done);
+      },
+      buildSchema = function (done) {
+        var schemaPath = path.join(path.dirname(spec.source), "440_schema.sql");
+        winston.info("Building schema for database " + databaseName);
+
+        exec("psql -U " + creds.username + " -h " + creds.hostname + " --single-transaction -p " +
+          creds.port + " -d " + databaseName + " -f " + schemaPath,
+          {maxBuffer: 40000 * 1024 /* 200x default */}, done);
+      },
+      populateData = function (done) {
+        winston.info("Populating data for database " + databaseName + " from " + spec.source);
+        exec("psql -U " + creds.username + " -h " + creds.hostname + " --single-transaction -p " +
+          creds.port + " -d " + databaseName + " -f " + spec.source,
+          {maxBuffer: 40000 * 1024 /* 200x default */}, done);
+      },
+      // use exec to restore the backup. The alternative, reading the backup file into a string to query
+      // doesn't work because the backup file is binary.
+      restoreBackup = function (done) {
+        exec("pg_restore -U " + creds.username + " -h " + creds.hostname + " -p " +
+          creds.port + " -d " + databaseName + " -j " + os.cpus().length + " " + spec.backup, function (err, res) {
+          if (err) {
+            console.log("ignoring restore db error", err);
+          }
+          done(null, res);
+        });
+      },
+      finish = function (err, results) {
+        if (err) {
+          winston.error("init database error", err.message, err.stack, err);
+        }
+        callback(err, results);
+      };
+
+    if (spec.source) {
+      async.series([
+        dropDatabase,
+        createDatabase,
+        buildSchema,
+        populateData
+      ], finish);
+    } else {
+      async.series([
+        dropDatabase,
+        createDatabase,
+        restoreBackup,
+        function (done) {
+          credsClone.database = databaseName;
+          inspectDatabaseExtensions(credsClone, function (err, paths) {
+            // in the case of a build-from-backup, we ignore any user desires and dictate the extensions
+            spec.extensions = paths;
+            done();
+          });
+        }
+      ], finish);
+    }
+  };
+
+  exports.initDatabase = initDatabase;
+}());
diff --git a/scripts/lib/util/inspect_database.js b/scripts/lib/util/inspect_database.js
new file mode 100644 (file)
index 0000000..e9e80ac
--- /dev/null
@@ -0,0 +1,79 @@
+/*jshint node:true, indent:2, curly:false, eqeqeq:true, immed:true, latedef:true, newcap:true, noarg:true,
+regexp:true, undef:true, strict:true, trailing:true, white:true */
+/*global _:true */
+
+(function () {
+  "use strict";
+
+  var path = require('path'),
+    dataSource = require('../../../node-datasource/lib/ext/datasource').dataSource,
+    defaultExtensions = require("./default_extensions").extensions;
+
+  var pathFromExtension = function (name, location) {
+    if (location === '/core-extensions') {
+      return path.join(__dirname, "/../../enyo-client/extensions/source/", name);
+    } else if (location === '/xtuple-extensions') {
+      return path.join(__dirname, "../../../xtuple-extensions/source", name);
+    } else if (location === '/private-extensions') {
+      return path.join(__dirname, "../../../private-extensions/source", name);
+    } else if (location === 'npm') {
+      return path.join(__dirname, "../../node_modules", name);
+    }
+  };
+
+  //
+  // Looks in a database to see which extensions are registered, and
+  // tacks onto that list the core directories.
+  //
+  var inspectMobilizedDatabase = function (creds, done) {
+    var extSql = "SELECT * FROM xt.ext ORDER BY ext_load_order";
+    dataSource.query(extSql, creds, function (err, res) {
+      if (err) {
+        return done(err);
+      }
+
+      var paths = _.map(_.compact(res.rows), function (row) {
+        return pathFromExtension(row.ext_name, row.ext_location);
+      });
+
+      paths.unshift(path.join(__dirname, "../../enyo-client")); // core path
+      paths.unshift(path.join(__dirname, "../../lib/orm")); // lib path
+      paths.unshift(path.join(__dirname, "../../foundation-database")); // foundation path
+      done(null, _.compact(paths));
+    });
+  };
+
+  var inspectUnmobilizedDatabase = function (creds, done) {
+    var extSql = "select * from public.pkghead where pkghead_name in ('xtmfg', 'xwd');",
+      editionMap = {
+        xtmfg: ["inventory", "manufacturing"],
+        xwd: ["inventory", "distribution"]
+      };
+    dataSource.query(extSql, creds, function (err, res) {
+      if (err) {
+        return done(err);
+      }
+      var extensions = _.unique(_.flatten(_.map(res.rows, function (row) {
+        return _.map(editionMap[row.pkghead_name], function (ext) {
+          return path.join(__dirname, "../../../private-extensions/source", ext);
+        });
+      })));
+      done(err, defaultExtensions.concat(extensions));
+    });
+  };
+
+  var inspectDatabaseExtensions = function (creds, done) {
+    var isMobilizedSql = "select * from information_schema.tables where table_schema = 'xt' and table_name = 'ext';";
+
+    dataSource.query(isMobilizedSql, creds, function (err, res) {
+      if (res.rowCount === 0) {
+        inspectUnmobilizedDatabase(creds, done);
+      } else {
+        inspectMobilizedDatabase(creds, done);
+      }
+    });
+  };
+
+  exports.inspectDatabaseExtensions = inspectDatabaseExtensions;
+
+}());
diff --git a/scripts/lib/util/process_manifest.js b/scripts/lib/util/process_manifest.js
new file mode 100644 (file)
index 0000000..90f6030
--- /dev/null
@@ -0,0 +1,227 @@
+/*jshint node:true, indent:2, curly:false, eqeqeq:true, immed:true, latedef:true, newcap:true, noarg:true,
+regexp:true, undef:true, strict:true, trailing:true, white:true */
+/*global _:true */
+
+(function () {
+  "use strict";
+
+  var _ = require('underscore'),
+    async = require('async'),
+    exec = require('child_process').exec,
+    fs = require('fs'),
+    path = require('path'),
+    conversionMap = require("./convert_specialized").conversionMap,
+    dataSource = require('../../../node-datasource/lib/ext/datasource').dataSource,
+    inspectDatabaseExtensions = require("./inspect_database").inspectDatabaseExtensions;
+
+  // register extension and dependencies
+  var getRegistrationSql = function (options, extensionLocation) {
+    var registerSql = 'do $$ plv8.elog(NOTICE, "About to register extension ' +
+      options.name + '"); $$ language plv8;\n';
+
+    registerSql = registerSql + "select xt.register_extension('%@', '%@', '%@', '', %@);\n"
+      .f(options.name, options.description || options.comment, extensionLocation, options.loadOrder || 9999);
+
+    registerSql = registerSql + "select xt.grant_role_ext('ADMIN', '%@');\n"
+      .f(options.name);
+
+    // TODO: infer dependencies from package.json using peerDependencies
+    var dependencies = options.dependencies || [];
+    _.each(dependencies, function (dependency) {
+      var dependencySql = "select xt.register_extension_dependency('%@', '%@');\n"
+          .f(options.name, dependency),
+        grantDependToAdmin = "select xt.grant_role_ext('ADMIN', '%@');\n"
+          .f(dependency);
+
+      registerSql = registerSql + dependencySql + grantDependToAdmin;
+    });
+    return registerSql;
+  };
+
+  var composeExtensionSql = function (scriptSql, packageFile, options, callback) {
+    // each String of the scriptContents is the concatenated SQL for the script.
+    // join these all together into a single string for the whole extension.
+    var extensionSql = _.reduce(scriptSql, function (memo, script) {
+      return memo + script;
+    }, "");
+
+    if (options.registerExtension) {
+      extensionSql = getRegistrationSql(packageFile, options.extensionLocation) +
+        extensionSql;
+    }
+    if (options.runJsInit) {
+      // unless it it hasn't yet been defined (ie. lib/orm),
+      // running xt.js_init() is probably a good idea.
+      extensionSql = "select xt.js_init();" + extensionSql;
+    }
+
+    if (options.wipeViews) {
+      // If we want to pre-emptively wipe out the views, the best place to do it
+      // is at the start of the core application code
+      fs.readFile(path.join(__dirname, "../../../enyo-client/database/source/delete_system_orms.sql"),
+          function (err, wipeSql) {
+        if (err) {
+          callback(err);
+          return;
+        }
+        extensionSql = wipeSql + extensionSql;
+        callback(null, extensionSql);
+      });
+    } else {
+      callback(null, extensionSql);
+    }
+  };
+
+  var explodeManifest = function (options, manifestCallback) {
+    var manifestFilename = options.manifestFilename;
+    var packageJson;
+    var dbSourceRoot = path.dirname(manifestFilename);
+
+    if (options.extensionPath && fs.existsSync(path.resolve(options.extensionPath, "package.json"))) {
+      packageJson = require(path.resolve(options.extensionPath, "package.json"));
+    }
+    //
+    // Step 2:
+    // Read the manifest files.
+    //
+
+    if (!fs.existsSync(manifestFilename) && packageJson) {
+      console.log("No manifest file " + manifestFilename + ". There is probably no db-side code in the extension.");
+      composeExtensionSql([], packageJson, options, manifestCallback);
+      return;
+
+    } else if (!fs.existsSync(manifestFilename)) {
+      // error condition: no manifest file
+      manifestCallback("Cannot find manifest " + manifestFilename);
+      return;
+    }
+    fs.readFile(manifestFilename, "utf8", function (err, manifestString) {
+      var manifest,
+        databaseScripts,
+        extraManifestPath,
+        defaultSchema,
+        extraManifest,
+        extraManifestScripts,
+        alterPaths = dbSourceRoot.indexOf("foundation-database") < 0;
+
+      try {
+        manifest = JSON.parse(manifestString);
+        databaseScripts = manifest.databaseScripts;
+        defaultSchema = manifest.defaultSchema;
+
+      } catch (error) {
+        // error condition: manifest file is not properly formatted
+        manifestCallback("Manifest is not valid JSON" + manifestFilename);
+        return;
+      }
+
+      //
+      // Step 2b:
+      //
+
+      // supported use cases:
+
+      // 1. add mobilized inventory to quickbooks
+      // need the frozen_manifest, the foundation/manifest, and the mobile manifest
+      // -e ../private-extensions/source/inventory -f
+      // useFrozenScripts, useFoundationScripts
+
+      // 2. add mobilized inventory to masterref (foundation inventory is already there)
+      // need the the foundation/manifest and the mobile manifest
+      // -e ../private-extensions/source/inventory
+      // useFoundationScripts
+
+      // 3. add unmobilized inventory to quickbooks
+      // need the frozen_manifest and the foundation/manifest
+      // -e ../private-extensions/source/inventory/foundation-database -f
+      // useFrozenScripts (useFoundationScripts already taken care of by -e path)
+
+      // 4. upgrade unmobilized inventory
+      // not sure if this is necessary, but it would look like
+      // -e ../private-extensions/source/inventory/foundation-database
+
+      if (options.useFoundationScripts) {
+        extraManifest = JSON.parse(fs.readFileSync(path.join(dbSourceRoot, "../../foundation-database/manifest.js")));
+        defaultSchema = defaultSchema || extraManifest.defaultSchema;
+        extraManifestScripts = extraManifest.databaseScripts;
+        extraManifestScripts = _.map(extraManifestScripts, function (path) {
+          return "../../foundation-database/" + path;
+        });
+        databaseScripts.unshift(extraManifestScripts);
+        databaseScripts = _.flatten(databaseScripts);
+      }
+      if (options.useFrozenScripts) {
+        // Frozen files are not idempotent and should only be run upon first registration
+        extraManifestPath = alterPaths ?
+         path.join(dbSourceRoot, "../../foundation-database/frozen_manifest.js") :
+         path.join(dbSourceRoot, "frozen_manifest.js");
+
+        extraManifest = JSON.parse(fs.readFileSync(extraManifestPath));
+        defaultSchema = defaultSchema || extraManifest.defaultSchema;
+        extraManifestScripts = extraManifest.databaseScripts;
+        if (alterPaths) {
+          extraManifestScripts = _.map(extraManifestScripts, function (path) {
+            return "../../foundation-database/" + path;
+          });
+        }
+        databaseScripts.unshift(extraManifestScripts);
+        databaseScripts = _.flatten(databaseScripts);
+      }
+
+      //
+      // Step 3:
+      // Concatenate together all the files referenced in the manifest.
+      //
+      var getScriptSql = function (filename, scriptCallback) {
+        var fullFilename = path.join(dbSourceRoot, filename);
+        if (!fs.existsSync(fullFilename)) {
+          // error condition: script referenced in manifest.js isn't there
+          scriptCallback(path.join(dbSourceRoot, filename) + " does not exist");
+          return;
+        }
+        fs.readFile(fullFilename, "utf8", function (err, scriptContents) {
+          // error condition: can't read script
+          if (err) {
+            scriptCallback(err);
+            return;
+          }
+          var beforeNoticeSql = "do $$ BEGIN RAISE NOTICE 'Loading file " + path.basename(fullFilename) +
+              "'; END $$ language plpgsql;\n",
+            extname = path.extname(fullFilename).substring(1);
+
+          // convert special files: metasql, uiforms, reports, uijs
+          scriptContents = conversionMap[extname](scriptContents, fullFilename, defaultSchema);
+
+          //
+          // Incorrectly-ended sql files (i.e. no semicolon) make for unhelpful error messages
+          // when we concatenate 100's of them together. Guard against these.
+          //
+          scriptContents = scriptContents.trim();
+          if (scriptContents.charAt(scriptContents.length - 1) !== ';') {
+            // error condition: script is improperly formatted
+            scriptCallback("Error: " + fullFilename + " contents do not end in a semicolon.");
+          }
+
+          scriptCallback(null, '\n' + scriptContents);
+        });
+      };
+      async.mapSeries(databaseScripts || [], getScriptSql, function (err, scriptSql) {
+        var registerSql,
+          dependencies;
+
+        if (err) {
+          manifestCallback(err);
+          return;
+        }
+
+        composeExtensionSql(scriptSql, packageJson || manifest, options, manifestCallback);
+
+      });
+      //
+      // End script installation code
+      //
+    });
+  };
+
+  exports.explodeManifest = explodeManifest;
+}());
diff --git a/scripts/lib/util/send_to_database.js b/scripts/lib/util/send_to_database.js
new file mode 100644 (file)
index 0000000..939f280
--- /dev/null
@@ -0,0 +1,59 @@
+/*jshint node:true, indent:2, curly:false, eqeqeq:true, immed:true, latedef:true, newcap:true, noarg:true,
+regexp:true, undef:true, strict:true, trailing:true, white:true */
+/*global _:true */
+
+(function () {
+  "use strict";
+
+  var exec = require('child_process').exec,
+    fs = require('fs'),
+    path = require('path'),
+    winston = require('winston');
+
+  var sendToDatabase = function (query, credsClone, options, callback) {
+    var filename = path.join(__dirname, "../../output/build_" + credsClone.database + ".sql");
+    if (!fs.existsSync(path.join(__dirname, "../../output"))) {
+      fs.mkdirSync(path.join(__dirname, "../../output"));
+    }
+    fs.writeFile(filename, query, function (err) {
+      if (err) {
+        winston.error("Cannot write query to file");
+        callback(err);
+        return;
+      }
+      var psqlCommand = 'psql -d ' + credsClone.database +
+        ' -U ' + credsClone.username +
+        ' -h ' + credsClone.hostname +
+        ' -p ' + credsClone.port +
+        ' -f ' + filename +
+        ' --single-transaction';
+
+      /**
+       * http://nodejs.org/api/child_process.html#child_process_child_process_exec_command_options_callback
+       * "maxBuffer specifies the largest amount of data allowed on stdout or
+       * stderr - if this value is exceeded then the child process is killed."
+       */
+      exec(psqlCommand, {maxBuffer: 40000 * 1024 /* 200x default */}, function (err, stdout, stderr) {
+        if (err) {
+          winston.error("Cannot install file ", filename);
+          callback(err);
+          return;
+        }
+        if (options.keepSql) {
+          // do not delete the temp query file
+          winston.info("SQL file kept as ", filename);
+          callback();
+        } else {
+          fs.unlink(filename, function (err) {
+            if (err) {
+              winston.error("Cannot delete written query file");
+              callback(err);
+            }
+            callback();
+          });
+        }
+      });
+    });
+  };
+  exports.sendToDatabase = sendToDatabase;
+}());
diff --git a/scripts/lib/util/unregister.js b/scripts/lib/util/unregister.js
new file mode 100644 (file)
index 0000000..d727a5a
--- /dev/null
@@ -0,0 +1,55 @@
+/*jshint node:true, indent:2, curly:false, eqeqeq:true, immed:true, latedef:true, newcap:true, noarg:true,
+regexp:true, undef:true, strict:true, trailing:true, white:true */
+/*global _:true */
+
+(function () {
+  "use strict";
+
+  var async = require("async"),
+    path = require('path'),
+    winston = require('winston'),
+    dataSource = require('../../../node-datasource/lib/ext/datasource').dataSource;
+
+  //
+  // Unregister the extension
+  //
+  var unregister = function (specs, creds, masterCallback) {
+    var extension = path.basename(specs[0].extensions[0]),
+      unregisterSql = ["delete from xt.usrext where usrext_id in " +
+        "(select usrext_id from xt.usrext inner join xt.ext on usrext_ext_id = ext_id where ext_name = $1);",
+
+        "delete from xt.grpext where grpext_id in " +
+        "(select grpext_id from xt.grpext inner join xt.ext on grpext_ext_id = ext_id where ext_name = $1);",
+
+        "delete from xt.clientcode where clientcode_id in " +
+        "(select clientcode_id from xt.clientcode inner join xt.ext on clientcode_ext_id = ext_id where ext_name = $1);",
+
+        "delete from xt.dict where dict_id in " +
+        "(select dict_id from xt.dict inner join xt.ext on dict_ext_id = ext_id where ext_name = $1);",
+
+        "delete from xt.extdep where extdep_id in " +
+        "(select extdep_id from xt.extdep inner join xt.ext " +
+        "on extdep_from_ext_id = ext_id or extdep_to_ext_id = ext_id where ext_name = $1);",
+
+        "delete from xt.ext where ext_name = $1;"];
+
+    if (extension.charAt(extension.length - 1) === "/") {
+      // remove trailing slash if present
+      extension = extension.substring(0, extension.length - 1);
+    }
+    winston.info("Unregistering extension:", extension);
+    var unregisterEach = function (spec, callback) {
+      var options = JSON.parse(JSON.stringify(creds));
+      options.database = spec.database;
+      options.parameters = [extension];
+      var queryEach = function (sql, sqlCallback) {
+        dataSource.query(sql, options, sqlCallback);
+      };
+      async.eachSeries(unregisterSql, queryEach, callback);
+    };
+    async.each(specs, unregisterEach, masterCallback);
+  };
+
+  exports.unregister = unregister;
+}());
+
index e54412d..b82f1bd 100644 (file)
@@ -1,5 +1,5 @@
 <package id        = "distribution-install-460"
-         version   = "4.6.0"
+         version   = "4.7.0Beta"
          developer = "xTuple"
          descrip   = "load PostBooks resources"
          updater   = "2.2.4" >
@@ -19,8 +19,8 @@
 
   <prerequisite type = "query"
                 name = "Checking for too-new xTuple ERP database version" >
-    <query>SELECT NOT fetchMetricText('ServerVersion') >= '4.6.1';</query>
-    <message>This package may not be applied to a database newer than 4.6.0.
+    <query>SELECT NOT fetchMetricText('ServerVersion') >= '4.7.0';</query>
+    <message>This package may not be applied to a database newer than 4.7.0Beta.
     </message>
   </prerequisite>
 
index 36cca32..9ff6ab6 100644 (file)
@@ -1,5 +1,5 @@
 <package id        = "distribution-upgrade-460"
-         version   = "4.6.0"
+         version   = "4.7.0Beta"
          developer = "xTuple"
          descrip   = "load PostBooks resources"
          updater   = "2.2.4" >
@@ -19,8 +19,8 @@
 
   <prerequisite type = "query"
                 name = "Checking for too-new xTuple ERP database version" >
-    <query>SELECT NOT fetchMetricText('ServerVersion') >= '4.6.1';</query>
-    <message>This package may not be applied to a database newer than 4.6.0.
+    <query>SELECT NOT fetchMetricText('ServerVersion') >= '4.7.0';</query>
+    <message>This package may not be applied to a database newer than 4.7.0Beta.
     </message>
   </prerequisite>
 
index fd4a154..041993b 100644 (file)
@@ -1,5 +1,5 @@
 <package id        = "postbooks-upgrade-460"
-         version   = "4.6.0"
+         version   = "4.7.0Beta"
          developer = "xTuple"
          descrip   = "load PostBooks resources"
          updater   = "2.2.4" >
@@ -19,8 +19,8 @@
 
   <prerequisite type = "query"
                 name = "Checking for too-new xTuple ERP database version" >
-    <query>SELECT NOT fetchMetricText('ServerVersion') >= '4.6.1';</query>
-    <message>This package may not be applied to a database newer than 4.6.0.
+    <query>SELECT NOT fetchMetricText('ServerVersion') >= '4.7.0';</query>
+    <message>This package may not be applied to a database newer than 4.7.0Beta.
     </message>
   </prerequisite>
 
index 1643c2c..b6eb595 100644 (file)
@@ -1,5 +1,5 @@
 <package id        = "manufacturing-install-460"
-         version   = "4.6.0"
+         version   = "4.7.0Beta"
          developer = "xTuple"
          descrip   = "load PostBooks resources"
          updater   = "2.2.4" >
@@ -19,8 +19,8 @@
 
   <prerequisite type = "query"
                 name = "Checking for too-new xTuple ERP database version" >
-    <query>SELECT NOT fetchMetricText('ServerVersion') >= '4.6.1';</query>
-    <message>This package may not be applied to a database newer than 4.6.0.
+    <query>SELECT NOT fetchMetricText('ServerVersion') >= '4.7.0';</query>
+    <message>This package may not be applied to a database newer than 4.7.0Beta.
     </message>
   </prerequisite>
 
index 9f4ec08..ff787b4 100644 (file)
@@ -1,5 +1,5 @@
 <package id        = "manufacturing-upgrade-460"
-         version   = "4.6.0"
+         version   = "4.7.0Beta"
          developer = "xTuple"
          descrip   = "load PostBooks resources"
          updater   = "2.2.4" >
@@ -30,8 +30,8 @@
 
 <prerequisite type = "query"
                name = "Checking for too-new xTuple ERP database version" >
-<query>SELECT NOT fetchMetricText('ServerVersion') >= '4.6.1';</query>
-    <message>This package may not be applied to a database newer than 4.6.0.
+<query>SELECT NOT fetchMetricText('ServerVersion') >= '4.7.0';</query>
+    <message>This package may not be applied to a database newer than 4.7.0Beta.
     </message>
 </prerequisite>