From 0f131d940d88eb8a83422f014d5a909717f46ec2 Mon Sep 17 00:00:00 2001 From: Shane Kilkelly Date: Mon, 24 Jul 2017 11:06:47 +0100 Subject: [PATCH 01/17] Enforce stricter password policy. - Check minimum password lengths - Set default policy to 6-128 chars --- .../AuthenticationManager.coffee | 6 +++++- services/web/config/settings.defaults.coffee | 4 ++-- .../public/coffee/directives/asyncForm.coffee | 9 +++++---- .../AuthenticationManagerTests.coffee | 18 ++++++++++++++++++ 4 files changed, 30 insertions(+), 7 deletions(-) diff --git a/services/web/app/coffee/Features/Authentication/AuthenticationManager.coffee b/services/web/app/coffee/Features/Authentication/AuthenticationManager.coffee index b661455028..49bd994b2c 100644 --- a/services/web/app/coffee/Features/Authentication/AuthenticationManager.coffee +++ b/services/web/app/coffee/Features/Authentication/AuthenticationManager.coffee @@ -29,8 +29,12 @@ module.exports = AuthenticationManager = callback null, null setUserPassword: (user_id, password, callback = (error) ->) -> - if Settings.passwordStrengthOptions?.length?.max? and Settings.passwordStrengthOptions?.length?.max < password.length + if (Settings.passwordStrengthOptions?.length?.max? and + Settings.passwordStrengthOptions?.length?.max < password.length) return callback("password is too long") + if (Settings.passwordStrengthOptions?.length?.min? and + Settings.passwordStrengthOptions?.length?.min > password.length) + return callback("password is too short") bcrypt.genSalt BCRYPT_ROUNDS, (error, salt) -> return callback(error) if error? diff --git a/services/web/config/settings.defaults.coffee b/services/web/config/settings.defaults.coffee index 8fb00aff31..046903de7b 100644 --- a/services/web/config/settings.defaults.coffee +++ b/services/web/config/settings.defaults.coffee @@ -226,8 +226,8 @@ module.exports = settings = # passwordStrengthOptions: # pattern: "aA$3" # length: - # min: 1 - # max: 10 + # min: 6 + # max: 128 # Email support # ------------- diff --git a/services/web/public/coffee/directives/asyncForm.coffee b/services/web/public/coffee/directives/asyncForm.coffee index 2fc336c934..0e6ae19ec2 100644 --- a/services/web/public/coffee/directives/asyncForm.coffee +++ b/services/web/public/coffee/directives/asyncForm.coffee @@ -103,8 +103,8 @@ define [ defaultPasswordOpts = pattern: "" length: - min: 1 - max: 50 + min: 6 + max: 128 allowEmpty: false allowAnyChars: false isMasked: true @@ -127,8 +127,6 @@ define [ [asyncFormCtrl, ngModelCtrl] = ctrl ngModelCtrl.$parsers.unshift (modelValue) -> - - isValid = passField.validatePass() email = asyncFormCtrl.getEmail() || window.usersEmail if !isValid @@ -141,5 +139,8 @@ define [ if opts.length.max? and modelValue.length == opts.length.max isValid = false scope.complexPasswordErrorMessage = "Maximum password length #{opts.length.max} reached" + if opts.length.min? and modelValue.length < opts.length.min + isValid = false + scope.complexPasswordErrorMessage = "Password too short, minimum #{opts.length.min}" ngModelCtrl.$setValidity('complexPassword', isValid) return modelValue diff --git a/services/web/test/UnitTests/coffee/Authentication/AuthenticationManagerTests.coffee b/services/web/test/UnitTests/coffee/Authentication/AuthenticationManagerTests.coffee index 0ebf9f8112..0a041a0865 100644 --- a/services/web/test/UnitTests/coffee/Authentication/AuthenticationManagerTests.coffee +++ b/services/web/test/UnitTests/coffee/Authentication/AuthenticationManagerTests.coffee @@ -116,6 +116,24 @@ describe "AuthenticationManager", -> expect(err).to.exist done() + it "should not start the bcrypt process", (done)-> + @AuthenticationManager.setUserPassword @user_id, @password, (err)=> + @bcrypt.genSalt.called.should.equal false + @bcrypt.hash.called.should.equal false + done() + + describe "too short", -> + beforeEach -> + @settings.passwordStrengthOptions = + length: + max:10 + min:6 + @password = "dsd" + + it "should return and error", (done)-> + @AuthenticationManager.setUserPassword @user_id, @password, (err)-> + expect(err).to.exist + done() it "should not start the bcrypt process", (done)-> @AuthenticationManager.setUserPassword @user_id, @password, (err)=> From 10683cbd8c73d25ac32f950b778a6b68c221eda5 Mon Sep 17 00:00:00 2001 From: Joe Green Date: Thu, 27 Jul 2017 10:58:20 +0100 Subject: [PATCH 02/17] upgraded mongoose package --- services/web/npm-shrinkwrap.json | 1020 +++++++++++++++++++++++++++--- services/web/package.json | 14 +- 2 files changed, 940 insertions(+), 94 deletions(-) diff --git a/services/web/npm-shrinkwrap.json b/services/web/npm-shrinkwrap.json index f22ecf0219..a8357be7fd 100644 --- a/services/web/npm-shrinkwrap.json +++ b/services/web/npm-shrinkwrap.json @@ -54,6 +54,12 @@ "from": "ansi-regex@>=2.0.0 <3.0.0", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz" }, + "ansi-styles": { + "version": "2.2.1", + "from": "ansi-styles@>=2.2.1 <3.0.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", + "dev": true + }, "aproba": { "version": "1.1.1", "from": "aproba@>=1.0.3 <2.0.0", @@ -145,6 +151,12 @@ "from": "asynckit@>=0.4.0 <0.5.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz" }, + "autoprefixer": { + "version": "6.7.7", + "from": "autoprefixer@>=6.6.1 <7.0.0", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-6.7.7.tgz", + "dev": true + }, "aws-sdk": { "version": "2.41.0", "from": "aws-sdk@>=2.2.36 <3.0.0", @@ -287,25 +299,17 @@ "from": "brace-expansion@>=1.0.0 <2.0.0", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.7.tgz" }, + "browserslist": { + "version": "1.7.7", + "from": "browserslist@>=1.7.6 <2.0.0", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-1.7.7.tgz", + "dev": true + }, "bson": { "version": "1.0.4", "from": "bson@>=1.0.4 <1.1.0", "resolved": "https://registry.npmjs.org/bson/-/bson-1.0.4.tgz" }, - "bson-ext": { - "version": "0.1.13", - "from": "bson-ext@>=0.1.0 <0.2.0", - "resolved": "https://registry.npmjs.org/bson-ext/-/bson-ext-0.1.13.tgz", - "optional": true, - "dependencies": { - "nan": { - "version": "2.0.9", - "from": "nan@>=2.0.9 <2.1.0", - "resolved": "https://registry.npmjs.org/nan/-/nan-2.0.9.tgz", - "optional": true - } - } - }, "buffer": { "version": "4.9.1", "from": "buffer@4.9.1", @@ -372,6 +376,12 @@ "from": "camelcase@>=1.0.2 <2.0.0", "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-1.2.1.tgz" }, + "caniuse-db": { + "version": "1.0.30000708", + "from": "caniuse-db@>=1.0.30000634 <2.0.0", + "resolved": "https://registry.npmjs.org/caniuse-db/-/caniuse-db-1.0.30000708.tgz", + "dev": true + }, "caseless": { "version": "0.12.0", "from": "caseless@>=0.12.0 <0.13.0", @@ -392,6 +402,20 @@ "from": "chai-spies@latest", "resolved": "https://registry.npmjs.org/chai-spies/-/chai-spies-0.7.1.tgz" }, + "chalk": { + "version": "1.1.3", + "from": "chalk@>=1.1.3 <2.0.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "dev": true, + "dependencies": { + "supports-color": { + "version": "2.0.0", + "from": "supports-color@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", + "dev": true + } + } + }, "character-parser": { "version": "1.2.0", "from": "character-parser@1.2.0", @@ -545,6 +569,12 @@ "from": "cookie-signature@1.0.6", "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz" }, + "cookies": { + "version": "0.7.0", + "from": "cookies@>=0.2.2", + "resolved": "https://registry.npmjs.org/cookies/-/cookies-0.7.0.tgz", + "dev": true + }, "core-js": { "version": "1.2.7", "from": "core-js@>=1.2.0 <2.0.0", @@ -639,6 +669,13 @@ } } }, + "ctype": { + "version": "0.5.3", + "from": "ctype@0.5.3", + "resolved": "https://registry.npmjs.org/ctype/-/ctype-0.5.3.tgz", + "dev": true, + "optional": true + }, "dashdash": { "version": "1.14.1", "from": "dashdash@>=1.12.0 <2.0.0", @@ -730,6 +767,38 @@ "from": "doctypes@>=1.1.0 <2.0.0", "resolved": "https://registry.npmjs.org/doctypes/-/doctypes-1.1.0.tgz" }, + "dom-serializer": { + "version": "0.1.0", + "from": "dom-serializer@>=0.0.0 <1.0.0", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.1.0.tgz", + "dev": true, + "dependencies": { + "domelementtype": { + "version": "1.1.3", + "from": "domelementtype@>=1.1.1 <1.2.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.1.3.tgz", + "dev": true + } + } + }, + "domelementtype": { + "version": "1.3.0", + "from": "domelementtype@>=1.3.0 <2.0.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.3.0.tgz", + "dev": true + }, + "domhandler": { + "version": "2.4.1", + "from": "domhandler@>=2.3.0 <3.0.0", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-2.4.1.tgz", + "dev": true + }, + "domutils": { + "version": "1.6.2", + "from": "domutils@>=1.5.1 <2.0.0", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.6.2.tgz", + "dev": true + }, "dottie": { "version": "1.1.1", "from": "dottie@>=1.0.0 <2.0.0", @@ -761,6 +830,12 @@ "from": "ejs@>=0.8.3 <0.9.0", "resolved": "https://registry.npmjs.org/ejs/-/ejs-0.8.8.tgz" }, + "electron-to-chromium": { + "version": "1.3.16", + "from": "electron-to-chromium@>=1.2.7 <2.0.0", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.16.tgz", + "dev": true + }, "encoding": { "version": "0.1.12", "from": "encoding@>=0.1.0 <0.2.0", @@ -778,6 +853,18 @@ "from": "end-of-stream@>=0.1.3 <0.2.0", "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-0.1.5.tgz" }, + "entities": { + "version": "1.1.1", + "from": "entities@>=1.1.1 <2.0.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-1.1.1.tgz", + "dev": true + }, + "es6-promise": { + "version": "4.1.1", + "from": "es6-promise@>=4.0.5 <5.0.0", + "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.1.1.tgz", + "dev": true + }, "escape-html": { "version": "1.0.2", "from": "escape-html@1.0.2", @@ -887,6 +974,12 @@ "from": "failure@>=1.1.0 <1.2.0", "resolved": "https://registry.npmjs.org/failure/-/failure-1.1.1.tgz" }, + "faye-websocket": { + "version": "0.10.0", + "from": "faye-websocket@>=0.10.0 <0.11.0", + "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.10.0.tgz", + "dev": true + }, "file-utils": { "version": "0.1.5", "from": "file-utils@>=0.1.5 <0.2.0", @@ -960,6 +1053,12 @@ "from": "form-data@>=2.1.1 <2.2.0", "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.1.4.tgz" }, + "formatio": { + "version": "1.1.1", + "from": "formatio@1.1.1", + "resolved": "https://registry.npmjs.org/formatio/-/formatio-1.1.1.tgz", + "dev": true + }, "forwarded": { "version": "0.1.0", "from": "forwarded@>=0.1.0 <0.2.0", @@ -1029,6 +1128,12 @@ "from": "gauge@>=2.7.1 <2.8.0", "resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.3.tgz" }, + "gaze": { + "version": "1.1.2", + "from": "gaze@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/gaze/-/gaze-1.1.2.tgz", + "dev": true + }, "generic-pool": { "version": "2.4.2", "from": "generic-pool@2.4.2", @@ -1063,6 +1168,26 @@ } } }, + "globule": { + "version": "1.2.0", + "from": "globule@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/globule/-/globule-1.2.0.tgz", + "dev": true, + "dependencies": { + "glob": { + "version": "7.1.2", + "from": "glob@>=7.1.1 <7.2.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", + "dev": true + }, + "minimatch": { + "version": "3.0.4", + "from": "minimatch@>=3.0.2 <3.1.0", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "dev": true + } + } + }, "graceful-fs": { "version": "4.1.11", "from": "graceful-fs@>=4.1.2 <5.0.0", @@ -1130,6 +1255,26 @@ } } }, + "grunt-available-tasks": { + "version": "0.4.1", + "from": "grunt-available-tasks@0.4.1", + "resolved": "https://registry.npmjs.org/grunt-available-tasks/-/grunt-available-tasks-0.4.1.tgz", + "dev": true, + "dependencies": { + "lodash": { + "version": "2.4.2", + "from": "lodash@>=2.4.0 <2.5.0", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-2.4.2.tgz", + "dev": true + }, + "underscore.string": { + "version": "2.3.3", + "from": "underscore.string@>=2.3.3 <2.4.0", + "resolved": "https://registry.npmjs.org/underscore.string/-/underscore.string-2.3.3.tgz", + "dev": true + } + } + }, "grunt-bunyan": { "version": "0.5.0", "from": "grunt-bunyan@>=0.5.0 <0.6.0", @@ -1142,11 +1287,145 @@ } } }, + "grunt-contrib-clean": { + "version": "0.5.0", + "from": "grunt-contrib-clean@0.5.0", + "resolved": "https://registry.npmjs.org/grunt-contrib-clean/-/grunt-contrib-clean-0.5.0.tgz", + "dev": true + }, + "grunt-contrib-coffee": { + "version": "0.10.0", + "from": "grunt-contrib-coffee@0.10.0", + "resolved": "https://registry.npmjs.org/grunt-contrib-coffee/-/grunt-contrib-coffee-0.10.0.tgz", + "dev": true, + "dependencies": { + "ansi-styles": { + "version": "1.0.0", + "from": "ansi-styles@>=1.0.0 <1.1.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-1.0.0.tgz", + "dev": true + }, + "chalk": { + "version": "0.4.0", + "from": "chalk@>=0.4.0 <0.5.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-0.4.0.tgz", + "dev": true + }, + "coffee-script": { + "version": "1.7.1", + "from": "coffee-script@>=1.7.0 <1.8.0", + "resolved": "https://registry.npmjs.org/coffee-script/-/coffee-script-1.7.1.tgz", + "dev": true + }, + "lodash": { + "version": "2.4.2", + "from": "lodash@~2.4.1", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-2.4.2.tgz", + "dev": true + }, + "mkdirp": { + "version": "0.3.5", + "from": "mkdirp@>=0.3.5 <0.4.0", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.3.5.tgz", + "dev": true + }, + "strip-ansi": { + "version": "0.1.1", + "from": "strip-ansi@>=0.1.0 <0.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-0.1.1.tgz", + "dev": true + } + } + }, + "grunt-contrib-less": { + "version": "0.9.0", + "from": "grunt-contrib-less@0.9.0", + "resolved": "https://registry.npmjs.org/grunt-contrib-less/-/grunt-contrib-less-0.9.0.tgz", + "dev": true, + "dependencies": { + "ansi-styles": { + "version": "1.0.0", + "from": "ansi-styles@>=1.0.0 <1.1.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-1.0.0.tgz", + "dev": true + }, + "chalk": { + "version": "0.4.0", + "from": "chalk@>=0.4.0 <0.5.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-0.4.0.tgz", + "dev": true + }, + "strip-ansi": { + "version": "0.1.1", + "from": "strip-ansi@~0.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-0.1.1.tgz", + "dev": true + } + } + }, + "grunt-contrib-requirejs": { + "version": "0.4.1", + "from": "grunt-contrib-requirejs@0.4.1", + "resolved": "https://registry.npmjs.org/grunt-contrib-requirejs/-/grunt-contrib-requirejs-0.4.1.tgz", + "dev": true + }, + "grunt-contrib-watch": { + "version": "1.0.0", + "from": "grunt-contrib-watch@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/grunt-contrib-watch/-/grunt-contrib-watch-1.0.0.tgz", + "dev": true, + "dependencies": { + "async": { + "version": "1.5.2", + "from": "async@>=1.5.0 <2.0.0", + "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz", + "dev": true + }, + "lodash": { + "version": "3.10.1", + "from": "lodash@>=3.10.1 <4.0.0", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-3.10.1.tgz", + "dev": true + } + } + }, + "grunt-env": { + "version": "0.4.4", + "from": "grunt-env@0.4.4", + "resolved": "https://registry.npmjs.org/grunt-env/-/grunt-env-0.4.4.tgz", + "dev": true, + "dependencies": { + "lodash": { + "version": "2.4.2", + "from": "lodash@~2.4.1", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-2.4.2.tgz", + "dev": true + } + } + }, + "grunt-exec": { + "version": "0.4.7", + "from": "grunt-exec@>=0.4.7 <0.5.0", + "resolved": "https://registry.npmjs.org/grunt-exec/-/grunt-exec-0.4.7.tgz", + "dev": true + }, "grunt-execute": { "version": "0.2.2", "from": "grunt-execute@>=0.2.2 <0.3.0", "resolved": "https://registry.npmjs.org/grunt-execute/-/grunt-execute-0.2.2.tgz" }, + "grunt-file-append": { + "version": "0.0.6", + "from": "grunt-file-append@0.0.6", + "resolved": "https://registry.npmjs.org/grunt-file-append/-/grunt-file-append-0.0.6.tgz", + "dev": true + }, + "grunt-git-rev-parse": { + "version": "0.1.5", + "from": "grunt-git-rev-parse@>=0.1.4 <0.2.0", + "resolved": "https://registry.npmjs.org/grunt-git-rev-parse/-/grunt-git-rev-parse-0.1.5.tgz", + "dev": true + }, "grunt-legacy-log": { "version": "0.1.3", "from": "grunt-legacy-log@>=0.1.0 <0.2.0", @@ -1198,6 +1477,84 @@ } } }, + "grunt-lib-contrib": { + "version": "0.6.1", + "from": "grunt-lib-contrib@>=0.6.1 <0.7.0", + "resolved": "https://registry.npmjs.org/grunt-lib-contrib/-/grunt-lib-contrib-0.6.1.tgz", + "dev": true + }, + "grunt-mocha-test": { + "version": "0.9.0", + "from": "grunt-mocha-test@0.9.0", + "resolved": "https://registry.npmjs.org/grunt-mocha-test/-/grunt-mocha-test-0.9.0.tgz", + "dev": true + }, + "grunt-newer": { + "version": "1.3.0", + "from": "grunt-newer@>=1.2.0 <2.0.0", + "resolved": "https://registry.npmjs.org/grunt-newer/-/grunt-newer-1.3.0.tgz", + "dev": true, + "dependencies": { + "async": { + "version": "1.5.2", + "from": "async@^1.5.2", + "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz", + "dev": true + }, + "glob": { + "version": "7.1.2", + "from": "glob@^7.0.5", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", + "dev": true + }, + "minimatch": { + "version": "3.0.4", + "from": "minimatch@^3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "dev": true + }, + "rimraf": { + "version": "2.6.1", + "from": "rimraf@>=2.5.2 <3.0.0", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.1.tgz", + "dev": true + } + } + }, + "grunt-parallel": { + "version": "0.5.1", + "from": "grunt-parallel@>=0.5.1 <0.6.0", + "resolved": "https://registry.npmjs.org/grunt-parallel/-/grunt-parallel-0.5.1.tgz", + "dev": true, + "dependencies": { + "q": { + "version": "0.8.12", + "from": "q@>=0.8.12 <0.9.0", + "resolved": "https://registry.npmjs.org/q/-/q-0.8.12.tgz", + "dev": true + } + } + }, + "grunt-postcss": { + "version": "0.8.0", + "from": "grunt-postcss@>=0.8.0 <0.9.0", + "resolved": "https://registry.npmjs.org/grunt-postcss/-/grunt-postcss-0.8.0.tgz", + "dev": true, + "dependencies": { + "diff": { + "version": "2.2.3", + "from": "diff@>=2.0.2 <3.0.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-2.2.3.tgz", + "dev": true + } + } + }, + "grunt-sed": { + "version": "0.1.1", + "from": "grunt-sed@>=0.1.1 <0.2.0", + "resolved": "https://registry.npmjs.org/grunt-sed/-/grunt-sed-0.1.1.tgz", + "dev": true + }, "hang": { "version": "1.0.0", "from": "hang@>=1.0.0 <1.1.0", @@ -1218,6 +1575,24 @@ "from": "has@>=1.0.1 <2.0.0", "resolved": "https://registry.npmjs.org/has/-/has-1.0.1.tgz" }, + "has-ansi": { + "version": "2.0.0", + "from": "has-ansi@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", + "dev": true + }, + "has-color": { + "version": "0.1.7", + "from": "has-color@>=0.1.0 <0.2.0", + "resolved": "https://registry.npmjs.org/has-color/-/has-color-0.1.7.tgz", + "dev": true + }, + "has-flag": { + "version": "1.0.0", + "from": "has-flag@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz", + "dev": true + }, "has-unicode": { "version": "2.0.1", "from": "has-unicode@>=2.0.0 <3.0.0", @@ -1244,9 +1619,41 @@ "resolved": "https://registry.npmjs.org/hooker/-/hooker-0.2.3.tgz" }, "hooks-fixed": { - "version": "1.1.0", - "from": "hooks-fixed@1.1.0", - "resolved": "https://registry.npmjs.org/hooks-fixed/-/hooks-fixed-1.1.0.tgz" + "version": "2.0.0", + "from": "hooks-fixed@2.0.0", + "resolved": "https://registry.npmjs.org/hooks-fixed/-/hooks-fixed-2.0.0.tgz" + }, + "htmlparser2": { + "version": "3.9.2", + "from": "htmlparser2@>=3.9.0 <4.0.0", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.9.2.tgz", + "dev": true, + "dependencies": { + "isarray": { + "version": "1.0.0", + "from": "isarray@>=1.0.0 <1.1.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "dev": true + }, + "readable-stream": { + "version": "2.3.3", + "from": "readable-stream@>=2.0.2 <3.0.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.3.tgz", + "dev": true + }, + "safe-buffer": { + "version": "5.1.1", + "from": "safe-buffer@>=5.1.1 <5.2.0", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz", + "dev": true + }, + "string_decoder": { + "version": "1.0.3", + "from": "string_decoder@>=1.0.3 <1.1.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz", + "dev": true + } + } }, "http-errors": { "version": "1.6.1", @@ -1263,6 +1670,12 @@ "from": "http-signature@>=1.1.0 <1.2.0", "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.1.1.tgz" }, + "i18next": { + "version": "1.7.10", + "from": "i18next@>=1.7.1 <1.8.0", + "resolved": "https://registry.npmjs.org/i18next/-/i18next-1.7.10.tgz", + "dev": true + }, "iconv-lite": { "version": "0.2.11", "from": "iconv-lite@>=0.2.11 <0.3.0", @@ -1285,7 +1698,7 @@ }, "inherits": { "version": "2.0.3", - "from": "inherits@>=2.0.0 <3.0.0", + "from": "inherits@>=2.0.1 <2.1.0", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz" }, "ini": { @@ -1398,6 +1811,12 @@ "resolved": "https://registry.npmjs.org/jodid25519/-/jodid25519-1.0.2.tgz", "optional": true }, + "js-base64": { + "version": "2.1.9", + "from": "js-base64@>=2.1.9 <3.0.0", + "resolved": "https://registry.npmjs.org/js-base64/-/js-base64-2.1.9.tgz", + "dev": true + }, "js-stringify": { "version": "1.0.2", "from": "js-stringify@>=1.0.1 <2.0.0", @@ -1429,6 +1848,12 @@ "from": "json-stringify-safe@>=5.0.1 <5.1.0", "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz" }, + "json5": { + "version": "0.2.0", + "from": "json5@>=0.2.0 <0.3.0", + "resolved": "https://registry.npmjs.org/json5/-/json5-0.2.0.tgz", + "dev": true + }, "jsonfile": { "version": "1.1.1", "from": "jsonfile@>=1.1.0 <1.2.0", @@ -1469,23 +1894,15 @@ } }, "kareem": { - "version": "1.0.1", - "from": "kareem@1.0.1", - "resolved": "https://registry.npmjs.org/kareem/-/kareem-1.0.1.tgz" + "version": "1.5.0", + "from": "kareem@1.5.0", + "resolved": "https://registry.npmjs.org/kareem/-/kareem-1.5.0.tgz" }, - "kerberos": { - "version": "0.0.23", - "from": "kerberos@>=0.0.0 <0.1.0", - "resolved": "https://registry.npmjs.org/kerberos/-/kerberos-0.0.23.tgz", - "optional": true, - "dependencies": { - "nan": { - "version": "2.5.1", - "from": "nan@>=2.5.1 <2.6.0", - "resolved": "https://registry.npmjs.org/nan/-/nan-2.5.1.tgz", - "optional": true - } - } + "keygrip": { + "version": "1.0.1", + "from": "keygrip@>=1.0.1 <1.1.0", + "resolved": "https://registry.npmjs.org/keygrip/-/keygrip-1.0.1.tgz", + "dev": true }, "kind-of": { "version": "3.1.0", @@ -1628,6 +2045,42 @@ } } }, + "less": { + "version": "1.6.3", + "from": "less@>=1.6.0 <1.7.0", + "resolved": "https://registry.npmjs.org/less/-/less-1.6.3.tgz", + "dev": true, + "dependencies": { + "clean-css": { + "version": "2.0.8", + "from": "clean-css@>=2.0.0 <2.1.0", + "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-2.0.8.tgz", + "dev": true, + "optional": true + }, + "commander": { + "version": "2.0.0", + "from": "commander@>=2.0.0 <2.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.0.0.tgz", + "dev": true, + "optional": true + }, + "mime": { + "version": "1.2.11", + "from": "mime@>=1.2.0 <1.3.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.2.11.tgz", + "dev": true, + "optional": true + }, + "mkdirp": { + "version": "0.3.5", + "from": "mkdirp@~0.3.5", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.3.5.tgz", + "dev": true, + "optional": true + } + } + }, "libbase64": { "version": "0.1.0", "from": "libbase64@0.1.0", @@ -1650,6 +2103,12 @@ "from": "libqp@1.1.0", "resolved": "https://registry.npmjs.org/libqp/-/libqp-1.1.0.tgz" }, + "livereload-js": { + "version": "2.2.2", + "from": "livereload-js@>=2.2.0 <3.0.0", + "resolved": "https://registry.npmjs.org/livereload-js/-/livereload-js-2.2.2.tgz", + "dev": true + }, "loads": { "version": "0.0.4", "from": "loads@>=0.0.0 <0.1.0", @@ -1862,11 +2321,23 @@ } } }, + "lolex": { + "version": "1.3.2", + "from": "lolex@1.3.2", + "resolved": "https://registry.npmjs.org/lolex/-/lolex-1.3.2.tgz", + "dev": true + }, "longest": { "version": "1.0.1", "from": "longest@>=1.0.1 <2.0.0", "resolved": "https://registry.npmjs.org/longest/-/longest-1.0.1.tgz" }, + "lpad": { + "version": "0.1.0", + "from": "lpad@>=0.1.0 <0.2.0", + "resolved": "https://registry.npmjs.org/lpad/-/lpad-0.1.0.tgz", + "dev": true + }, "lru-cache": { "version": "2.7.3", "from": "lru-cache@>=2.0.0 <3.0.0", @@ -2090,46 +2561,54 @@ } }, "mongoose": { - "version": "4.1.0", - "from": "mongoose@4.1.0", - "resolved": "https://registry.npmjs.org/mongoose/-/mongoose-4.1.0.tgz", + "version": "4.11.4", + "from": "mongoose@4.11.4", + "resolved": "https://registry.npmjs.org/mongoose/-/mongoose-4.11.4.tgz", "dependencies": { "async": { - "version": "0.9.0", - "from": "async@0.9.0", - "resolved": "https://registry.npmjs.org/async/-/async-0.9.0.tgz" + "version": "2.1.4", + "from": "async@2.1.4", + "resolved": "https://registry.npmjs.org/async/-/async-2.1.4.tgz" }, - "bson": { - "version": "0.3.2", - "from": "bson@>=0.3.0 <0.4.0", - "resolved": "https://registry.npmjs.org/bson/-/bson-0.3.2.tgz" + "es6-promise": { + "version": "3.2.1", + "from": "es6-promise@3.2.1", + "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-3.2.1.tgz" + }, + "isarray": { + "version": "1.0.0", + "from": "isarray@>=1.0.0 <1.1.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz" }, "mongodb": { - "version": "2.0.34", - "from": "mongodb@2.0.34", - "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-2.0.34.tgz" + "version": "2.2.27", + "from": "mongodb@2.2.27", + "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-2.2.27.tgz" }, "mongodb-core": { - "version": "1.2.0", - "from": "mongodb-core@1.2.0", - "resolved": "https://registry.npmjs.org/mongodb-core/-/mongodb-core-1.2.0.tgz", - "dependencies": { - "bson": { - "version": "0.4.23", - "from": "bson@>=0.4.0 <0.5.0", - "resolved": "https://registry.npmjs.org/bson/-/bson-0.4.23.tgz" - } - } + "version": "2.1.11", + "from": "mongodb-core@2.1.11", + "resolved": "https://registry.npmjs.org/mongodb-core/-/mongodb-core-2.1.11.tgz" }, "ms": { - "version": "0.1.0", - "from": "ms@0.1.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-0.1.0.tgz" + "version": "2.0.0", + "from": "ms@2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz" }, "readable-stream": { - "version": "1.0.31", - "from": "readable-stream@1.0.31", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.31.tgz" + "version": "2.2.7", + "from": "readable-stream@2.2.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.2.7.tgz" + }, + "safe-buffer": { + "version": "5.1.1", + "from": "safe-buffer@>=5.1.0 <5.2.0", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz" + }, + "string_decoder": { + "version": "1.0.3", + "from": "string_decoder@>=1.0.0 <1.1.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz" } } }, @@ -2139,34 +2618,39 @@ "resolved": "https://registry.npmjs.org/monocle/-/monocle-1.1.51.tgz" }, "mpath": { - "version": "0.1.1", - "from": "mpath@0.1.1", - "resolved": "https://registry.npmjs.org/mpath/-/mpath-0.1.1.tgz" + "version": "0.3.0", + "from": "mpath@0.3.0", + "resolved": "https://registry.npmjs.org/mpath/-/mpath-0.3.0.tgz" }, "mpromise": { - "version": "0.5.4", - "from": "mpromise@0.5.4", - "resolved": "https://registry.npmjs.org/mpromise/-/mpromise-0.5.4.tgz" + "version": "0.5.5", + "from": "mpromise@0.5.5", + "resolved": "https://registry.npmjs.org/mpromise/-/mpromise-0.5.5.tgz" }, "mquery": { - "version": "1.6.1", - "from": "mquery@1.6.1", - "resolved": "https://registry.npmjs.org/mquery/-/mquery-1.6.1.tgz", + "version": "2.3.1", + "from": "mquery@2.3.1", + "resolved": "https://registry.npmjs.org/mquery/-/mquery-2.3.1.tgz", "dependencies": { "bluebird": { - "version": "2.9.26", - "from": "bluebird@2.9.26", - "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-2.9.26.tgz" + "version": "2.10.2", + "from": "bluebird@2.10.2", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-2.10.2.tgz" }, "debug": { - "version": "2.2.0", - "from": "debug@2.2.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.2.0.tgz" + "version": "2.6.8", + "from": "debug@2.6.8", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.8.tgz" }, "ms": { - "version": "0.7.1", - "from": "ms@0.7.1", - "resolved": "https://registry.npmjs.org/ms/-/ms-0.7.1.tgz" + "version": "2.0.0", + "from": "ms@2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz" + }, + "sliced": { + "version": "0.0.5", + "from": "sliced@0.0.5", + "resolved": "https://registry.npmjs.org/sliced/-/sliced-0.0.5.tgz" } } }, @@ -2208,9 +2692,9 @@ } }, "muri": { - "version": "1.0.0", - "from": "muri@1.0.0", - "resolved": "https://registry.npmjs.org/muri/-/muri-1.0.0.tgz" + "version": "1.2.2", + "from": "muri@1.2.2", + "resolved": "https://registry.npmjs.org/muri/-/muri-1.2.2.tgz" }, "mv": { "version": "0.0.5", @@ -2311,16 +2795,48 @@ "from": "nodemailer-wellknown@0.1.7", "resolved": "https://registry.npmjs.org/nodemailer-wellknown/-/nodemailer-wellknown-0.1.7.tgz" }, + "nomnom": { + "version": "1.6.2", + "from": "nomnom@>=1.6.0 <1.7.0", + "resolved": "https://registry.npmjs.org/nomnom/-/nomnom-1.6.2.tgz", + "dev": true, + "dependencies": { + "colors": { + "version": "0.5.1", + "from": "colors@0.5.x", + "resolved": "https://registry.npmjs.org/colors/-/colors-0.5.1.tgz", + "dev": true + }, + "underscore": { + "version": "1.4.4", + "from": "underscore@>=1.4.4 <1.5.0", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.4.4.tgz", + "dev": true + } + } + }, "nopt": { "version": "3.0.6", "from": "nopt@>=3.0.1 <3.1.0", "resolved": "https://registry.npmjs.org/nopt/-/nopt-3.0.6.tgz" }, + "normalize-range": { + "version": "0.1.2", + "from": "normalize-range@>=0.1.2 <0.2.0", + "resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz", + "dev": true + }, "npmlog": { "version": "4.0.2", "from": "npmlog@>=4.0.0 <5.0.0", "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.0.2.tgz" }, + "num2fraction": { + "version": "1.2.2", + "from": "num2fraction@>=1.2.2 <2.0.0", + "resolved": "https://registry.npmjs.org/num2fraction/-/num2fraction-1.2.2.tgz", + "dev": true + }, "number-is-nan": { "version": "1.0.1", "from": "number-is-nan@>=1.0.0 <2.0.0", @@ -2356,6 +2872,154 @@ "from": "one-time@>=0.0.0 <0.1.0", "resolved": "https://registry.npmjs.org/one-time/-/one-time-0.0.4.tgz" }, + "onesky": { + "version": "0.1.6", + "from": "onesky@>=0.1.5 <0.2.0", + "resolved": "https://registry.npmjs.org/onesky/-/onesky-0.1.6.tgz", + "dev": true, + "dependencies": { + "asn1": { + "version": "0.1.11", + "from": "asn1@0.1.11", + "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.1.11.tgz", + "dev": true, + "optional": true + }, + "assert-plus": { + "version": "0.1.5", + "from": "assert-plus@>=0.1.5 <0.2.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-0.1.5.tgz", + "dev": true, + "optional": true + }, + "async": { + "version": "0.9.2", + "from": "async@>=0.9.0 <0.10.0", + "resolved": "https://registry.npmjs.org/async/-/async-0.9.2.tgz", + "dev": true, + "optional": true + }, + "aws-sign2": { + "version": "0.5.0", + "from": "aws-sign2@>=0.5.0 <0.6.0", + "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.5.0.tgz", + "dev": true, + "optional": true + }, + "boom": { + "version": "0.4.2", + "from": "boom@>=0.4.0 <0.5.0", + "resolved": "https://registry.npmjs.org/boom/-/boom-0.4.2.tgz", + "dev": true + }, + "combined-stream": { + "version": "0.0.7", + "from": "combined-stream@>=0.0.4 <0.1.0", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-0.0.7.tgz", + "dev": true, + "optional": true + }, + "cryptiles": { + "version": "0.2.2", + "from": "cryptiles@>=0.2.0 <0.3.0", + "resolved": "https://registry.npmjs.org/cryptiles/-/cryptiles-0.2.2.tgz", + "dev": true, + "optional": true + }, + "delayed-stream": { + "version": "0.0.5", + "from": "delayed-stream@0.0.5", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-0.0.5.tgz", + "dev": true, + "optional": true + }, + "forever-agent": { + "version": "0.5.2", + "from": "forever-agent@>=0.5.0 <0.6.0", + "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.5.2.tgz", + "dev": true + }, + "form-data": { + "version": "0.1.4", + "from": "form-data@>=0.1.0 <0.2.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-0.1.4.tgz", + "dev": true, + "optional": true + }, + "hawk": { + "version": "1.1.1", + "from": "hawk@1.1.1", + "resolved": "https://registry.npmjs.org/hawk/-/hawk-1.1.1.tgz", + "dev": true, + "optional": true + }, + "hoek": { + "version": "0.9.1", + "from": "hoek@>=0.9.0 <0.10.0", + "resolved": "https://registry.npmjs.org/hoek/-/hoek-0.9.1.tgz", + "dev": true + }, + "http-signature": { + "version": "0.10.1", + "from": "http-signature@>=0.10.0 <0.11.0", + "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-0.10.1.tgz", + "dev": true, + "optional": true + }, + "mime": { + "version": "1.2.11", + "from": "mime@~1.2.11", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.2.11.tgz", + "dev": true, + "optional": true + }, + "mime-types": { + "version": "1.0.2", + "from": "mime-types@>=1.0.1 <1.1.0", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-1.0.2.tgz", + "dev": true + }, + "node-uuid": { + "version": "1.4.8", + "from": "node-uuid@>=1.4.0 <1.5.0", + "resolved": "https://registry.npmjs.org/node-uuid/-/node-uuid-1.4.8.tgz", + "dev": true + }, + "oauth-sign": { + "version": "0.3.0", + "from": "oauth-sign@>=0.3.0 <0.4.0", + "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.3.0.tgz", + "dev": true, + "optional": true + }, + "qs": { + "version": "1.0.2", + "from": "qs@>=1.0.0 <1.1.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-1.0.2.tgz", + "dev": true + }, + "request": { + "version": "2.40.0", + "from": "request@>=2.40.0 <2.41.0", + "resolved": "https://registry.npmjs.org/request/-/request-2.40.0.tgz", + "dev": true + }, + "sntp": { + "version": "0.2.4", + "from": "sntp@>=0.2.0 <0.3.0", + "resolved": "https://registry.npmjs.org/sntp/-/sntp-0.2.4.tgz", + "dev": true, + "optional": true + }, + "tunnel-agent": { + "version": "0.4.3", + "from": "tunnel-agent@>=0.4.0 <0.5.0", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.4.3.tgz", + "dev": true, + "optional": true + } + } + }, "optimist": { "version": "0.6.1", "from": "optimist@0.6.1", @@ -2479,6 +3143,26 @@ } } }, + "postcss": { + "version": "5.2.17", + "from": "postcss@>=5.2.16 <6.0.0", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.17.tgz", + "dev": true, + "dependencies": { + "source-map": { + "version": "0.5.6", + "from": "source-map@>=0.5.6 <0.6.0", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.6.tgz", + "dev": true + } + } + }, + "postcss-value-parser": { + "version": "3.3.0", + "from": "postcss-value-parser@>=3.2.3 <4.0.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.0.tgz", + "dev": true + }, "precond": { "version": "0.2.3", "from": "precond@>=0.2.0 <0.3.0", @@ -2935,11 +3619,31 @@ "from": "regexp-clone@0.0.1", "resolved": "https://registry.npmjs.org/regexp-clone/-/regexp-clone-0.0.1.tgz" }, + "regexp-quote": { + "version": "0.0.0", + "from": "regexp-quote@0.0.0", + "resolved": "https://registry.npmjs.org/regexp-quote/-/regexp-quote-0.0.0.tgz", + "dev": true + }, "repeat-string": { "version": "1.6.1", "from": "repeat-string@>=1.5.2 <2.0.0", "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz" }, + "replace": { + "version": "0.2.10", + "from": "replace@>=0.2.4 <0.3.0", + "resolved": "https://registry.npmjs.org/replace/-/replace-0.2.10.tgz", + "dev": true, + "dependencies": { + "colors": { + "version": "0.5.1", + "from": "colors@>=0.5.0 <0.6.0", + "resolved": "https://registry.npmjs.org/colors/-/colors-0.5.1.tgz", + "dev": true + } + } + }, "request": { "version": "2.81.0", "from": "request@>=2.69.0 <3.0.0", @@ -2967,6 +3671,12 @@ "from": "require-like@0.1.2", "resolved": "https://registry.npmjs.org/require-like/-/require-like-0.1.2.tgz" }, + "requirejs": { + "version": "2.1.22", + "from": "requirejs@>=2.1.0 <2.2.0", + "resolved": "https://registry.npmjs.org/requirejs/-/requirejs-2.1.22.tgz", + "dev": true + }, "requires-port": { "version": "1.0.0", "from": "requires-port@>=1.0.0 <2.0.0", @@ -3035,6 +3745,26 @@ "from": "samsam@1.1.2", "resolved": "https://registry.npmjs.org/samsam/-/samsam-1.1.2.tgz" }, + "sandboxed-module": { + "version": "0.2.0", + "from": "sandboxed-module@0.2.0", + "resolved": "https://registry.npmjs.org/sandboxed-module/-/sandboxed-module-0.2.0.tgz", + "dev": true, + "dependencies": { + "stack-trace": { + "version": "0.0.6", + "from": "stack-trace@0.0.6", + "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.6.tgz", + "dev": true + } + } + }, + "sanitize-html": { + "version": "1.14.1", + "from": "sanitize-html@>=1.14.1 <2.0.0", + "resolved": "https://registry.npmjs.org/sanitize-html/-/sanitize-html-1.14.1.tgz", + "dev": true + }, "sanitizer": { "version": "0.1.1", "from": "sanitizer@0.1.1", @@ -3195,15 +3925,21 @@ "from": "signal-exit@>=3.0.0 <4.0.0", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz" }, + "sinon": { + "version": "1.17.7", + "from": "sinon@>=1.17.0 <2.0.0", + "resolved": "https://registry.npmjs.org/sinon/-/sinon-1.17.7.tgz", + "dev": true + }, "sixpack-client": { "version": "1.0.0", "from": "sixpack-client@>=1.0.0 <2.0.0", "resolved": "https://registry.npmjs.org/sixpack-client/-/sixpack-client-1.0.0.tgz" }, "sliced": { - "version": "0.0.5", - "from": "sliced@0.0.5", - "resolved": "https://registry.npmjs.org/sliced/-/sliced-0.0.5.tgz" + "version": "1.0.1", + "from": "sliced@1.0.1", + "resolved": "https://registry.npmjs.org/sliced/-/sliced-1.0.1.tgz" }, "smtp-connection": { "version": "2.0.1", @@ -3287,6 +4023,12 @@ "from": "strip-json-comments@>=2.0.1 <2.1.0", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz" }, + "supports-color": { + "version": "3.2.3", + "from": "supports-color@>=3.2.3 <4.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", + "dev": true + }, "tar": { "version": "2.2.1", "from": "tar@>=2.2.0 <2.3.0", @@ -3369,6 +4111,78 @@ "from": "timekeeper@", "resolved": "https://registry.npmjs.org/timekeeper/-/timekeeper-1.0.0.tgz" }, + "tiny-lr": { + "version": "0.2.1", + "from": "tiny-lr@>=0.2.1 <0.3.0", + "resolved": "https://registry.npmjs.org/tiny-lr/-/tiny-lr-0.2.1.tgz", + "dev": true, + "dependencies": { + "body-parser": { + "version": "1.14.2", + "from": "body-parser@>=1.14.0 <1.15.0", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.14.2.tgz", + "dev": true, + "dependencies": { + "qs": { + "version": "5.2.0", + "from": "qs@5.2.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-5.2.0.tgz", + "dev": true + } + } + }, + "bytes": { + "version": "2.2.0", + "from": "bytes@2.2.0", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-2.2.0.tgz", + "dev": true + }, + "debug": { + "version": "2.2.0", + "from": "debug@>=2.2.0 <2.3.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.2.0.tgz", + "dev": true + }, + "http-errors": { + "version": "1.3.1", + "from": "http-errors@>=1.3.1 <1.4.0", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.3.1.tgz", + "dev": true + }, + "iconv-lite": { + "version": "0.4.13", + "from": "iconv-lite@0.4.13", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.13.tgz", + "dev": true + }, + "ms": { + "version": "0.7.1", + "from": "ms@0.7.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-0.7.1.tgz", + "dev": true + }, + "qs": { + "version": "5.1.0", + "from": "qs@>=5.1.0 <5.2.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-5.1.0.tgz", + "dev": true + }, + "raw-body": { + "version": "2.1.7", + "from": "raw-body@>=2.1.5 <2.2.0", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.1.7.tgz", + "dev": true, + "dependencies": { + "bytes": { + "version": "2.4.0", + "from": "bytes@2.4.0", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-2.4.0.tgz", + "dev": true + } + } + } + } + }, "to-mongodb-core": { "version": "2.0.0", "from": "to-mongodb-core@>=2.0.0 <3.0.0", @@ -3406,6 +4220,20 @@ } } }, + "translations-sharelatex": { + "version": "0.1.4", + "from": "git+https://github.com/sharelatex/translations-sharelatex.git#master", + "resolved": "git+https://github.com/sharelatex/translations-sharelatex.git#c2bcc5c8b39034d3dfa2f4509fcc8e1e0be6ed75", + "dev": true, + "dependencies": { + "async": { + "version": "2.5.0", + "from": "async@>=2.1.4 <3.0.0", + "resolved": "https://registry.npmjs.org/async/-/async-2.5.0.tgz", + "dev": true + } + } + }, "tsscmp": { "version": "1.0.5", "from": "tsscmp@1.0.5", @@ -3634,6 +4462,18 @@ "from": "void-elements@>=2.0.1 <3.0.0", "resolved": "https://registry.npmjs.org/void-elements/-/void-elements-2.0.1.tgz" }, + "websocket-driver": { + "version": "0.6.5", + "from": "websocket-driver@>=0.5.1", + "resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.6.5.tgz", + "dev": true + }, + "websocket-extensions": { + "version": "0.1.1", + "from": "websocket-extensions@>=0.1.1", + "resolved": "https://registry.npmjs.org/websocket-extensions/-/websocket-extensions-0.1.1.tgz", + "dev": true + }, "which": { "version": "1.0.9", "from": "which@>=1.0.5 <1.1.0", @@ -3759,6 +4599,12 @@ "resolved": "https://registry.npmjs.org/lodash/-/lodash-2.4.2.tgz" } } + }, + "zlib-browserify": { + "version": "0.0.1", + "from": "zlib-browserify@0.0.1", + "resolved": "https://registry.npmjs.org/zlib-browserify/-/zlib-browserify-0.0.1.tgz", + "dev": true } } } diff --git a/services/web/package.json b/services/web/package.json index 9c7c7b58de..eac07a06e5 100644 --- a/services/web/package.json +++ b/services/web/package.json @@ -30,8 +30,8 @@ "ioredis": "^2.4.0", "jade": "~1.3.1", "ldapjs": "^0.7.1", - "logger-sharelatex": "git+https://github.com/sharelatex/logger-sharelatex.git#master", "lodash": "^4.13.1", + "logger-sharelatex": "git+https://github.com/sharelatex/logger-sharelatex.git#master", "lynx": "0.1.1", "marked": "^0.3.5", "method-override": "^2.3.3", @@ -39,7 +39,7 @@ "mimelib": "0.2.14", "mocha": "1.17.1", "mongojs": "2.4.0", - "mongoose": "4.1.0", + "mongoose": "4.11.4", "multer": "^0.1.8", "nodemailer": "2.1.0", "nodemailer-sendgrid-transport": "^0.2.0", @@ -48,23 +48,23 @@ "passport": "^0.3.2", "passport-ldapauth": "^0.6.0", "passport-local": "^1.0.0", + "passport-saml": "^0.15.0", + "pug": "^2.0.0-beta6", "redis": "0.10.1", "redis-sharelatex": "git+https://github.com/sharelatex/redis-sharelatex.git#v1.0.2", "request": "^2.69.0", "requests": "^0.1.7", "rimraf": "2.2.6", + "rolling-rate-limiter": "git+https://github.com/ShaneKilkelly/rolling-rate-limiter.git#master", "sanitizer": "0.1.1", "sequelize": "^3.2.0", "settings-sharelatex": "git+https://github.com/sharelatex/settings-sharelatex.git#v1.0.0", "sixpack-client": "^1.0.0", "temp": "^0.8.3", "underscore": "1.6.0", - "v8-profiler": "^5.2.3", - "xml2js": "0.2.0", - "passport-saml": "^0.15.0", - "pug": "^2.0.0-beta6", "uuid": "^3.0.1", - "rolling-rate-limiter": "git+https://github.com/ShaneKilkelly/rolling-rate-limiter.git#master" + "v8-profiler": "^5.2.3", + "xml2js": "0.2.0" }, "devDependencies": { "autoprefixer": "^6.6.1", From b44d0bfb858f83e46948060345feb7cfe3568bc4 Mon Sep 17 00:00:00 2001 From: Paulo Reis Date: Fri, 28 Jul 2017 14:41:33 +0100 Subject: [PATCH 03/17] Ignore deleted projects when determining the number of uncategorized projects. --- .../web/public/coffee/main/project-list/project-list.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/web/public/coffee/main/project-list/project-list.coffee b/services/web/public/coffee/main/project-list/project-list.coffee index 5d0341656e..cafbd7c81b 100644 --- a/services/web/public/coffee/main/project-list/project-list.coffee +++ b/services/web/public/coffee/main/project-list/project-list.coffee @@ -20,7 +20,7 @@ define [ , 10 $scope.$watch(( - () -> $scope.projects.filter((project) -> !project.tags? or project.tags.length == 0).length + () -> $scope.projects.filter((project) -> (!project.tags? or project.tags.length == 0) and !project.archived).length ), (newVal) -> $scope.nUntagged = newVal) storedUIOpts = JSON.parse(localStorage("project_list")) From 2fcbafa72d56df186465d5d29d1455dbf93d6190 Mon Sep 17 00:00:00 2001 From: Paulo Reis Date: Fri, 28 Jul 2017 17:30:57 +0100 Subject: [PATCH 04/17] Add HTML encoder lib. --- services/web/package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/services/web/package.json b/services/web/package.json index eac07a06e5..14c7f00070 100644 --- a/services/web/package.json +++ b/services/web/package.json @@ -41,6 +41,7 @@ "mongojs": "2.4.0", "mongoose": "4.11.4", "multer": "^0.1.8", + "node-html-encoder": "0.0.2", "nodemailer": "2.1.0", "nodemailer-sendgrid-transport": "^0.2.0", "nodemailer-ses-transport": "^1.3.0", From 4849c705de8368d036639cf3ecb5b884d7f9023d Mon Sep 17 00:00:00 2001 From: Paulo Reis Date: Fri, 28 Jul 2017 17:31:28 +0100 Subject: [PATCH 05/17] Optionally ask the translate local method to HTML encode; use it in the problematic tooltip. --- services/web/app/coffee/infrastructure/ExpressLocals.coffee | 6 ++++-- services/web/app/views/project/editor/editor.pug | 2 +- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/services/web/app/coffee/infrastructure/ExpressLocals.coffee b/services/web/app/coffee/infrastructure/ExpressLocals.coffee index 4c6b3a7722..498127cdbd 100644 --- a/services/web/app/coffee/infrastructure/ExpressLocals.coffee +++ b/services/web/app/coffee/infrastructure/ExpressLocals.coffee @@ -11,6 +11,7 @@ async = require("async") Modules = require "./Modules" Url = require "url" PackageVersions = require "./PackageVersions" +htmlEncoder = new require("node-html-encoder").Encoder("numerical") fingerprints = {} Path = require 'path' @@ -151,9 +152,10 @@ module.exports = (app, webRouter, privateApiRouter, publicApiRouter)-> next() webRouter.use (req, res, next)-> - res.locals.translate = (key, vars = {}) -> + res.locals.translate = (key, vars = {}, htmlEncode = false) -> vars.appName = Settings.appName - req.i18n.translate(key, vars) + str = req.i18n.translate(key, vars) + if htmlEncode then htmlEncoder.htmlEncode(str) else str # Don't include the query string parameters, otherwise Google # treats ?nocdn=true as the canonical version res.locals.currentUrl = Url.parse(req.originalUrl).pathname diff --git a/services/web/app/views/project/editor/editor.pug b/services/web/app/views/project/editor/editor.pug index 8b58c98bcd..6007f2e0be 100644 --- a/services/web/app/views/project/editor/editor.pug +++ b/services/web/app/views/project/editor/editor.pug @@ -82,7 +82,7 @@ div.full-size( i.fa.fa-long-arrow-right br a.btn.btn-default.btn-xs( - tooltip-html="'"+translate('go_to_pdf_location_in_code')+"'" + tooltip-html="'"+translate('go_to_pdf_location_in_code', {}, true)+"'" tooltip-placement="right" tooltip-append-to-body="true" ng-click="syncToCode()" From ba3550759dbabc11e1bc0d9a8bb4a10ffc9215a9 Mon Sep 17 00:00:00 2001 From: Shane Kilkelly Date: Mon, 31 Jul 2017 08:36:13 +0100 Subject: [PATCH 06/17] Persist `autoPairDelimiters` setting. --- services/web/app/coffee/Features/User/UserController.coffee | 2 ++ 1 file changed, 2 insertions(+) diff --git a/services/web/app/coffee/Features/User/UserController.coffee b/services/web/app/coffee/Features/User/UserController.coffee index 65000985e1..9954294577 100644 --- a/services/web/app/coffee/Features/User/UserController.coffee +++ b/services/web/app/coffee/Features/User/UserController.coffee @@ -74,6 +74,8 @@ module.exports = UserController = user.ace.fontSize = req.body.fontSize if req.body.autoComplete? user.ace.autoComplete = req.body.autoComplete + if req.body.autoPairDelimiters? + user.ace.autoPairDelimiters = req.body.autoPairDelimiters if req.body.spellCheckLanguage? user.ace.spellCheckLanguage = req.body.spellCheckLanguage if req.body.pdfViewer? From eaf9ae5b948767db3f10d3e4ccc08d8b4abd17cb Mon Sep 17 00:00:00 2001 From: Shane Kilkelly Date: Thu, 27 Jul 2017 11:57:01 +0100 Subject: [PATCH 07/17] Begin work on autocomplete for `includegraphics` --- .../ide/editor/directives/aceEditor.coffee | 6 +-- .../auto-complete/AutoCompleteManager.coffee | 40 ++++++++++++++++++- .../ide/file-tree/FileTreeManager.coffee | 2 + .../ide/graphics/services/graphics.coffee | 16 ++++++++ .../ide/preamble/services/preamble.coffee | 12 ++++++ 5 files changed, 71 insertions(+), 5 deletions(-) create mode 100644 services/web/public/coffee/ide/graphics/services/graphics.coffee create mode 100644 services/web/public/coffee/ide/preamble/services/preamble.coffee diff --git a/services/web/public/coffee/ide/editor/directives/aceEditor.coffee b/services/web/public/coffee/ide/editor/directives/aceEditor.coffee index af633ea369..990678ddc4 100644 --- a/services/web/public/coffee/ide/editor/directives/aceEditor.coffee +++ b/services/web/public/coffee/ide/editor/directives/aceEditor.coffee @@ -11,6 +11,7 @@ define [ "ide/editor/directives/aceEditor/track-changes/TrackChangesManager" "ide/editor/directives/aceEditor/labels/LabelsManager" "ide/labels/services/labels" + "ide/graphics/services/graphics" ], (App, Ace, SearchBox, ModeList, UndoManager, AutoCompleteManager, SpellCheckManager, HighlightsManager, CursorPositionManager, TrackChangesManager, LabelsManager) -> EditSession = ace.require('ace/edit_session').EditSession ModeList = ace.require('ace/ext/modelist') @@ -33,9 +34,8 @@ define [ url = ace.config._moduleUrl(args...) + "?fingerprint=#{window.aceFingerprint}" return url - App.directive "aceEditor", ($timeout, $compile, $rootScope, event_tracking, localStorage, $cacheFactory, labels) -> + App.directive "aceEditor", ($timeout, $compile, $rootScope, event_tracking, localStorage, $cacheFactory, labels, graphics) -> monkeyPatchSearch($rootScope, $compile) - return { scope: { @@ -102,7 +102,7 @@ define [ cursorPositionManager = new CursorPositionManager(scope, editor, element, localStorage) trackChangesManager = new TrackChangesManager(scope, editor, element) labelsManager = new LabelsManager(scope, editor, element, labels) - autoCompleteManager = new AutoCompleteManager(scope, editor, element, labelsManager) + autoCompleteManager = new AutoCompleteManager(scope, editor, element, labelsManager, graphics) # Prevert Ctrl|Cmd-S from triggering save dialog diff --git a/services/web/public/coffee/ide/editor/directives/aceEditor/auto-complete/AutoCompleteManager.coffee b/services/web/public/coffee/ide/editor/directives/aceEditor/auto-complete/AutoCompleteManager.coffee index c320266a5a..6259e893ab 100644 --- a/services/web/public/coffee/ide/editor/directives/aceEditor/auto-complete/AutoCompleteManager.coffee +++ b/services/web/public/coffee/ide/editor/directives/aceEditor/auto-complete/AutoCompleteManager.coffee @@ -17,7 +17,7 @@ define [ commandFragment?.match(/\\(\w+)\{/)?[1] class AutoCompleteManager - constructor: (@$scope, @editor, @element, @labelsManager) -> + constructor: (@$scope, @editor, @element, @labelsManager, @graphics) -> @suggestionManager = new SuggestionManager() @monkeyPatchAutocomplete() @@ -44,6 +44,36 @@ define [ SnippetCompleter = new SnippetManager() + Graphics = @graphics + GraphicsCompleter = + getCompletions: (editor, session, pos, prefix, callback) -> + upToCursorRange = new Range(pos.row, 0, pos.row, pos.column) + lineUpToCursor = editor.getSession().getTextRange(upToCursorRange) + commandFragment = getLastCommandFragment(lineUpToCursor) + if commandFragment + match = commandFragment.match(/^~?\\(includegraphics(?:\[.*])?){([^}]*, *)?(\w*)/) + if match + beyondCursorRange = new Range(pos.row, pos.column, pos.row, 99999) + lineBeyondCursor = editor.getSession().getTextRange(beyondCursorRange) + needsClosingBrace = !lineBeyondCursor.match(/^[^{]*}/) + commandName = match[1] + currentArg = match[3] + result = [] + # result.push { + # caption: "\\#{commandName}{}", + # snippet: "\\#{commandName}{}", + # meta: "graphic", + # score: 60 + # } + for graphic in Graphics.getGraphicsFiles() + result.push { + caption: "\\#{commandName}{#{graphic.path}#{if needsClosingBrace then '}' else ''}", + value: "\\#{commandName}{#{graphic.path}#{if needsClosingBrace then '}' else ''}", + meta: "graphic", + score: 50 + } + callback null, result + labelsManager = @labelsManager LabelsCompleter = getCompletions: (editor, session, pos, prefix, callback) -> @@ -112,7 +142,13 @@ define [ else callback null, result - @editor.completers = [@suggestionManager, SnippetCompleter, ReferencesCompleter, LabelsCompleter] + @editor.completers = [ + @suggestionManager, + SnippetCompleter, + ReferencesCompleter, + LabelsCompleter, + GraphicsCompleter + ] disable: () -> @editor.setOptions({ diff --git a/services/web/public/coffee/ide/file-tree/FileTreeManager.coffee b/services/web/public/coffee/ide/file-tree/FileTreeManager.coffee index 0881e75cc8..f6b8a92710 100644 --- a/services/web/public/coffee/ide/file-tree/FileTreeManager.coffee +++ b/services/web/public/coffee/ide/file-tree/FileTreeManager.coffee @@ -202,6 +202,8 @@ define [ childPath = path + "/" + entity.name else childPath = entity.name + # FIXME: this is a hack + entity.path = childPath callback(entity, folder, childPath) if entity.children? @_forEachEntityInFolder(entity, childPath, callback) diff --git a/services/web/public/coffee/ide/graphics/services/graphics.coffee b/services/web/public/coffee/ide/graphics/services/graphics.coffee new file mode 100644 index 0000000000..6e0a03e849 --- /dev/null +++ b/services/web/public/coffee/ide/graphics/services/graphics.coffee @@ -0,0 +1,16 @@ +define [ + "base" +], (App) -> + + App.factory 'graphics', (ide) -> + + graphics = { + getGraphicsFiles: () -> + graphicsFiles = [] + ide.fileTreeManager.forEachEntity (f) -> + if f?.name?.match?(/.*\.(png|jpg|jpeg)/) + graphicsFiles.push f + return graphicsFiles + } + + return graphics diff --git a/services/web/public/coffee/ide/preamble/services/preamble.coffee b/services/web/public/coffee/ide/preamble/services/preamble.coffee new file mode 100644 index 0000000000..114f530d77 --- /dev/null +++ b/services/web/public/coffee/ide/preamble/services/preamble.coffee @@ -0,0 +1,12 @@ +define [ + "base" +], (App) -> + + App.factory 'preamble', (ide) -> + + Preamble = { + getPreambleText: () -> + + } + + return Preamble From a841646559b7d4495bec59e3c16442b6d8ffb2f9 Mon Sep 17 00:00:00 2001 From: Shane Kilkelly Date: Mon, 31 Jul 2017 11:28:21 +0100 Subject: [PATCH 08/17] Add preamble parser --- .../ide/preamble/preamble/services/preamble.coffee | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 services/web/public/coffee/ide/preamble/preamble/services/preamble.coffee diff --git a/services/web/public/coffee/ide/preamble/preamble/services/preamble.coffee b/services/web/public/coffee/ide/preamble/preamble/services/preamble.coffee new file mode 100644 index 0000000000..bd52713952 --- /dev/null +++ b/services/web/public/coffee/ide/preamble/preamble/services/preamble.coffee @@ -0,0 +1,14 @@ +define [ + "base" +], (App) -> + + App.factory 'preamble', (ide) -> + + Preamble = { + getPreambleText: () -> + text = ide.editorManager.getCurrentDocValue().slice(0, 5000) + preamble = text.match(/([^]*)^\\begin\{document\}/m)[1] + + } + + return Preamble From eeabac76988815ed7d34e065f2f831df354aca83 Mon Sep 17 00:00:00 2001 From: Shane Kilkelly Date: Mon, 31 Jul 2017 11:28:52 +0100 Subject: [PATCH 09/17] Get graphics paths from preamble --- .../ide/editor/directives/aceEditor.coffee | 3 ++- .../ide/graphics/services/graphics.coffee | 5 ++--- .../ide/preamble/services/preamble.coffee | 18 +++++++++++++++--- 3 files changed, 19 insertions(+), 7 deletions(-) diff --git a/services/web/public/coffee/ide/editor/directives/aceEditor.coffee b/services/web/public/coffee/ide/editor/directives/aceEditor.coffee index 990678ddc4..19c34142c5 100644 --- a/services/web/public/coffee/ide/editor/directives/aceEditor.coffee +++ b/services/web/public/coffee/ide/editor/directives/aceEditor.coffee @@ -12,6 +12,7 @@ define [ "ide/editor/directives/aceEditor/labels/LabelsManager" "ide/labels/services/labels" "ide/graphics/services/graphics" + "ide/preamble/services/preamble" ], (App, Ace, SearchBox, ModeList, UndoManager, AutoCompleteManager, SpellCheckManager, HighlightsManager, CursorPositionManager, TrackChangesManager, LabelsManager) -> EditSession = ace.require('ace/edit_session').EditSession ModeList = ace.require('ace/ext/modelist') @@ -34,7 +35,7 @@ define [ url = ace.config._moduleUrl(args...) + "?fingerprint=#{window.aceFingerprint}" return url - App.directive "aceEditor", ($timeout, $compile, $rootScope, event_tracking, localStorage, $cacheFactory, labels, graphics) -> + App.directive "aceEditor", ($timeout, $compile, $rootScope, event_tracking, localStorage, $cacheFactory, labels, graphics, preamble) -> monkeyPatchSearch($rootScope, $compile) return { diff --git a/services/web/public/coffee/ide/graphics/services/graphics.coffee b/services/web/public/coffee/ide/graphics/services/graphics.coffee index 6e0a03e849..fdd4126b16 100644 --- a/services/web/public/coffee/ide/graphics/services/graphics.coffee +++ b/services/web/public/coffee/ide/graphics/services/graphics.coffee @@ -4,13 +4,12 @@ define [ App.factory 'graphics', (ide) -> - graphics = { + Graphics = getGraphicsFiles: () -> graphicsFiles = [] ide.fileTreeManager.forEachEntity (f) -> if f?.name?.match?(/.*\.(png|jpg|jpeg)/) graphicsFiles.push f return graphicsFiles - } - return graphics + return Graphics diff --git a/services/web/public/coffee/ide/preamble/services/preamble.coffee b/services/web/public/coffee/ide/preamble/services/preamble.coffee index 114f530d77..a8312ca4d7 100644 --- a/services/web/public/coffee/ide/preamble/services/preamble.coffee +++ b/services/web/public/coffee/ide/preamble/services/preamble.coffee @@ -4,9 +4,21 @@ define [ App.factory 'preamble', (ide) -> - Preamble = { + Preamble = getPreambleText: () -> - - } + text = ide.editorManager.getCurrentDocValue().slice(0, 5000) + preamble = text.match(/([^]*)^\\begin\{document\}/m)?[1] || "" + return preamble + + getGraphicsPaths: () -> + preamble = Preamble.getPreambleText() + graphicsPathsArgs = preamble.match(/\\graphicspath\{(.*)\}/)?[1] || "" + paths = [] + re = /\{([^}]*)\}/g + while match = re.exec(graphicsPathsArgs) + paths.push(match[1]) + return paths + + window.Preamble = Preamble return Preamble From b5486155c0e1e411c9f7a65fcbd864e15fec629a Mon Sep 17 00:00:00 2001 From: Paulo Reis Date: Mon, 31 Jul 2017 14:01:22 +0100 Subject: [PATCH 10/17] Update shrinkwrap to include the HTML encoder. --- services/web/npm-shrinkwrap.json | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/services/web/npm-shrinkwrap.json b/services/web/npm-shrinkwrap.json index a8357be7fd..6f6d9e475e 100644 --- a/services/web/npm-shrinkwrap.json +++ b/services/web/npm-shrinkwrap.json @@ -2728,6 +2728,11 @@ "from": "node-forge@0.2.24", "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-0.2.24.tgz" }, + "node-html-encoder": { + "version": "0.0.2", + "from": "node-html-encoder@0.0.2", + "resolved": "https://registry.npmjs.org/node-html-encoder/-/node-html-encoder-0.0.2.tgz" + }, "node-pre-gyp": { "version": "0.6.30", "from": "node-pre-gyp@0.6.30", From f057f788e3270130678261e3bef34c5ca3d75c3e Mon Sep 17 00:00:00 2001 From: Shane Kilkelly Date: Mon, 31 Jul 2017 14:51:22 +0100 Subject: [PATCH 11/17] Autocomplete for graphics --- .../ide/editor/directives/aceEditor.coffee | 2 +- .../auto-complete/AutoCompleteManager.coffee | 19 ++++++++++--------- .../ide/file-tree/FileTreeManager.coffee | 2 -- .../ide/graphics/services/graphics.coffee | 8 +++++--- .../ide/preamble/services/preamble.coffee | 2 -- .../web/public/stylesheets/app/editor.less | 5 +++++ 6 files changed, 21 insertions(+), 17 deletions(-) diff --git a/services/web/public/coffee/ide/editor/directives/aceEditor.coffee b/services/web/public/coffee/ide/editor/directives/aceEditor.coffee index 19c34142c5..9b272841be 100644 --- a/services/web/public/coffee/ide/editor/directives/aceEditor.coffee +++ b/services/web/public/coffee/ide/editor/directives/aceEditor.coffee @@ -103,7 +103,7 @@ define [ cursorPositionManager = new CursorPositionManager(scope, editor, element, localStorage) trackChangesManager = new TrackChangesManager(scope, editor, element) labelsManager = new LabelsManager(scope, editor, element, labels) - autoCompleteManager = new AutoCompleteManager(scope, editor, element, labelsManager, graphics) + autoCompleteManager = new AutoCompleteManager(scope, editor, element, labelsManager, graphics, preamble) # Prevert Ctrl|Cmd-S from triggering save dialog diff --git a/services/web/public/coffee/ide/editor/directives/aceEditor/auto-complete/AutoCompleteManager.coffee b/services/web/public/coffee/ide/editor/directives/aceEditor/auto-complete/AutoCompleteManager.coffee index 6259e893ab..7cc89c785a 100644 --- a/services/web/public/coffee/ide/editor/directives/aceEditor/auto-complete/AutoCompleteManager.coffee +++ b/services/web/public/coffee/ide/editor/directives/aceEditor/auto-complete/AutoCompleteManager.coffee @@ -17,7 +17,7 @@ define [ commandFragment?.match(/\\(\w+)\{/)?[1] class AutoCompleteManager - constructor: (@$scope, @editor, @element, @labelsManager, @graphics) -> + constructor: (@$scope, @editor, @element, @labelsManager, @graphics, @preamble) -> @suggestionManager = new SuggestionManager() @monkeyPatchAutocomplete() @@ -45,6 +45,7 @@ define [ SnippetCompleter = new SnippetManager() Graphics = @graphics + Preamble = @preamble GraphicsCompleter = getCompletions: (editor, session, pos, prefix, callback) -> upToCursorRange = new Range(pos.row, 0, pos.row, pos.column) @@ -58,17 +59,17 @@ define [ needsClosingBrace = !lineBeyondCursor.match(/^[^{]*}/) commandName = match[1] currentArg = match[3] + graphicsPaths = Preamble.getGraphicsPaths() result = [] - # result.push { - # caption: "\\#{commandName}{}", - # snippet: "\\#{commandName}{}", - # meta: "graphic", - # score: 60 - # } for graphic in Graphics.getGraphicsFiles() + path = graphic.path + for graphicsPath in graphicsPaths + if path.indexOf(graphicsPath) == 0 + path = path.slice(graphicsPath.length) + break result.push { - caption: "\\#{commandName}{#{graphic.path}#{if needsClosingBrace then '}' else ''}", - value: "\\#{commandName}{#{graphic.path}#{if needsClosingBrace then '}' else ''}", + caption: "\\#{commandName}{#{path}#{if needsClosingBrace then '}' else ''}", + value: "\\#{commandName}{#{path}#{if needsClosingBrace then '}' else ''}", meta: "graphic", score: 50 } diff --git a/services/web/public/coffee/ide/file-tree/FileTreeManager.coffee b/services/web/public/coffee/ide/file-tree/FileTreeManager.coffee index f6b8a92710..0881e75cc8 100644 --- a/services/web/public/coffee/ide/file-tree/FileTreeManager.coffee +++ b/services/web/public/coffee/ide/file-tree/FileTreeManager.coffee @@ -202,8 +202,6 @@ define [ childPath = path + "/" + entity.name else childPath = entity.name - # FIXME: this is a hack - entity.path = childPath callback(entity, folder, childPath) if entity.children? @_forEachEntityInFolder(entity, childPath, callback) diff --git a/services/web/public/coffee/ide/graphics/services/graphics.coffee b/services/web/public/coffee/ide/graphics/services/graphics.coffee index fdd4126b16..355614b7f4 100644 --- a/services/web/public/coffee/ide/graphics/services/graphics.coffee +++ b/services/web/public/coffee/ide/graphics/services/graphics.coffee @@ -7,9 +7,11 @@ define [ Graphics = getGraphicsFiles: () -> graphicsFiles = [] - ide.fileTreeManager.forEachEntity (f) -> - if f?.name?.match?(/.*\.(png|jpg|jpeg)/) - graphicsFiles.push f + ide.fileTreeManager.forEachEntity (entity, folder, path) -> + if entity.type == 'file' && entity?.name?.match?(/.*\.(png|jpg|jpeg|pdf|eps)/) + cloned = _.clone(entity) + cloned.path = path + graphicsFiles.push cloned return graphicsFiles return Graphics diff --git a/services/web/public/coffee/ide/preamble/services/preamble.coffee b/services/web/public/coffee/ide/preamble/services/preamble.coffee index a8312ca4d7..f95cfd9bbe 100644 --- a/services/web/public/coffee/ide/preamble/services/preamble.coffee +++ b/services/web/public/coffee/ide/preamble/services/preamble.coffee @@ -19,6 +19,4 @@ define [ paths.push(match[1]) return paths - window.Preamble = Preamble - return Preamble diff --git a/services/web/public/stylesheets/app/editor.less b/services/web/public/stylesheets/app/editor.less index 3c3aae945a..979bdd2bf6 100644 --- a/services/web/public/stylesheets/app/editor.less +++ b/services/web/public/stylesheets/app/editor.less @@ -504,3 +504,8 @@ height: auto; border-bottom: 1px solid @modal-header-border-color; } + +// Widen autocomplete popup +.ace_autocomplete { + width: 380px !important; +} From e3bf4c539b847798d3b0cf5d91d07dfc5440c713 Mon Sep 17 00:00:00 2001 From: James Allen Date: Tue, 1 Aug 2017 09:51:27 +0200 Subject: [PATCH 12/17] Style tab panes --- services/web/public/stylesheets/components/navs.less | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/services/web/public/stylesheets/components/navs.less b/services/web/public/stylesheets/components/navs.less index 262524f864..0616da1b8c 100755 --- a/services/web/public/stylesheets/components/navs.less +++ b/services/web/public/stylesheets/components/navs.less @@ -225,6 +225,10 @@ // Hide tabbable panes to start, show them when `.active` .tab-content { + background-color: @nav-tabs-active-link-hover-bg; + border: 1px solid @nav-tabs-border-color; + border-top: none; + padding: @line-height-computed / 2; > .tab-pane { display: none; } From 9a85e42ddbdfc4dc3020c6b1e135b1776c4ade2f Mon Sep 17 00:00:00 2001 From: Shane Kilkelly Date: Tue, 1 Aug 2017 09:53:43 +0100 Subject: [PATCH 13/17] Remove stray file --- .../ide/preamble/preamble/services/preamble.coffee | 14 -------------- 1 file changed, 14 deletions(-) delete mode 100644 services/web/public/coffee/ide/preamble/preamble/services/preamble.coffee diff --git a/services/web/public/coffee/ide/preamble/preamble/services/preamble.coffee b/services/web/public/coffee/ide/preamble/preamble/services/preamble.coffee deleted file mode 100644 index bd52713952..0000000000 --- a/services/web/public/coffee/ide/preamble/preamble/services/preamble.coffee +++ /dev/null @@ -1,14 +0,0 @@ -define [ - "base" -], (App) -> - - App.factory 'preamble', (ide) -> - - Preamble = { - getPreambleText: () -> - text = ide.editorManager.getCurrentDocValue().slice(0, 5000) - preamble = text.match(/([^]*)^\\begin\{document\}/m)[1] - - } - - return Preamble From 3ffef7fe56d70038c2268bca63109d7f86521ff6 Mon Sep 17 00:00:00 2001 From: Shane Kilkelly Date: Tue, 1 Aug 2017 13:40:30 +0100 Subject: [PATCH 14/17] Dynamically adjust the width of autocomplete popup. --- .../auto-complete/AutoCompleteManager.coffee | 13 ++++++++++++- services/web/public/stylesheets/app/editor.less | 4 ---- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/services/web/public/coffee/ide/editor/directives/aceEditor/auto-complete/AutoCompleteManager.coffee b/services/web/public/coffee/ide/editor/directives/aceEditor/auto-complete/AutoCompleteManager.coffee index 7cc89c785a..348fe32f6b 100644 --- a/services/web/public/coffee/ide/editor/directives/aceEditor/auto-complete/AutoCompleteManager.coffee +++ b/services/web/public/coffee/ide/editor/directives/aceEditor/auto-complete/AutoCompleteManager.coffee @@ -282,7 +282,18 @@ define [ editor.completer.autoSelect = true editor.completer.showPopup(editor) editor.completer.cancelContextMenu() - $(editor.completer.popup?.container).css({'font-size': @$scope.fontSize + 'px'}) + container = $(editor.completer.popup?.container) + container.css({'font-size': @$scope.fontSize + 'px'}) + # Dynamically set width of autocomplete popup + if filtered = editor?.completer?.completions?.filtered + longestCaption = _.max(filtered.map( (c) -> c.caption.length )) + longestMeta = _.max(filtered.map( (c) -> c.meta.length )) + charScale = @$scope.fontSize * 0.7 + width = Math.min( + Math.round(longestCaption*charScale + longestMeta*charScale + 25), + 700 + ) + container.css({width: "#{width}px"}) if editor.completer?.completions?.filtered?.length == 0 editor.completer.detach() bindKey: "Ctrl-Space|Ctrl-Shift-Space|Alt-Space" diff --git a/services/web/public/stylesheets/app/editor.less b/services/web/public/stylesheets/app/editor.less index 979bdd2bf6..aae2ecdc9f 100644 --- a/services/web/public/stylesheets/app/editor.less +++ b/services/web/public/stylesheets/app/editor.less @@ -505,7 +505,3 @@ border-bottom: 1px solid @modal-header-border-color; } -// Widen autocomplete popup -.ace_autocomplete { - width: 380px !important; -} From 8a0f58c63d70fc42d155f7ff6e9a429accf46e34 Mon Sep 17 00:00:00 2001 From: Shane Kilkelly Date: Tue, 1 Aug 2017 14:28:10 +0100 Subject: [PATCH 15/17] Use correct character width --- .../auto-complete/AutoCompleteManager.coffee | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/services/web/public/coffee/ide/editor/directives/aceEditor/auto-complete/AutoCompleteManager.coffee b/services/web/public/coffee/ide/editor/directives/aceEditor/auto-complete/AutoCompleteManager.coffee index 348fe32f6b..751e534a32 100644 --- a/services/web/public/coffee/ide/editor/directives/aceEditor/auto-complete/AutoCompleteManager.coffee +++ b/services/web/public/coffee/ide/editor/directives/aceEditor/auto-complete/AutoCompleteManager.coffee @@ -288,10 +288,14 @@ define [ if filtered = editor?.completer?.completions?.filtered longestCaption = _.max(filtered.map( (c) -> c.caption.length )) longestMeta = _.max(filtered.map( (c) -> c.meta.length )) - charScale = @$scope.fontSize * 0.7 - width = Math.min( - Math.round(longestCaption*charScale + longestMeta*charScale + 25), - 700 + charWidth = editor.renderer.characterWidth + # between 280 and 700 px + width = Math.max( + Math.min( + Math.round(longestCaption*charWidth + longestMeta*charWidth + 5*charWidth), + 700 + ), + 280 ) container.css({width: "#{width}px"}) if editor.completer?.completions?.filtered?.length == 0 From 10f362a77a3415dd45a82a1dd3988041697b780e Mon Sep 17 00:00:00 2001 From: James Allen Date: Wed, 2 Aug 2017 09:59:45 +0200 Subject: [PATCH 16/17] Add SubscriptionUpdater.deleteSubscription --- .../Subscription/SubscriptionUpdater.coffee | 9 +++++ .../SubscriptionUpdaterTests.coffee | 33 +++++++++++++++++++ 2 files changed, 42 insertions(+) diff --git a/services/web/app/coffee/Features/Subscription/SubscriptionUpdater.coffee b/services/web/app/coffee/Features/Subscription/SubscriptionUpdater.coffee index 7b857c6460..12faf0e234 100644 --- a/services/web/app/coffee/Features/Subscription/SubscriptionUpdater.coffee +++ b/services/web/app/coffee/Features/Subscription/SubscriptionUpdater.coffee @@ -62,6 +62,15 @@ module.exports = SubscriptionUpdater = invited_emails: email }, callback + deleteSubscription: (subscription_id, callback = (error) ->) -> + SubscriptionLocator.getSubscription subscription_id, (err, subscription) -> + return callback(err) if err? + affected_user_ids = [subscription.admin_id].concat(subscription.member_ids or []) + logger.log {subscription_id, affected_user_ids}, "deleting subscription and downgrading users" + Subscription.remove {_id: ObjectId(subscription_id)}, (err) -> + return callback(err) if err? + async.mapSeries affected_user_ids, SubscriptionUpdater._setUsersMinimumFeatures, callback + _createNewSubscription: (adminUser_id, callback)-> logger.log adminUser_id:adminUser_id, "creating new subscription" subscription = new Subscription(admin_id:adminUser_id) diff --git a/services/web/test/UnitTests/coffee/Subscription/SubscriptionUpdaterTests.coffee b/services/web/test/UnitTests/coffee/Subscription/SubscriptionUpdaterTests.coffee index ffef94157b..87ff474a0a 100644 --- a/services/web/test/UnitTests/coffee/Subscription/SubscriptionUpdaterTests.coffee +++ b/services/web/test/UnitTests/coffee/Subscription/SubscriptionUpdaterTests.coffee @@ -37,6 +37,7 @@ describe "SubscriptionUpdater", -> constructor: (opts)-> subscription.admin_id = opts.admin_id return subscription + @remove: sinon.stub().yields() @SubscriptionModel.update = @updateStub @SubscriptionModel.findAndModify = @findAndModifyStub @@ -230,3 +231,35 @@ describe "SubscriptionUpdater", -> @ReferalAllocator.assignBonus.calledWith(@adminuser_id).should.equal true done() + describe "deleteSubscription", -> + beforeEach (done) -> + @subscription_id = ObjectId().toString() + @subscription = { + "mock": "subscription", + admin_id: ObjectId(), + member_ids: [ ObjectId(), ObjectId(), ObjectId() ] + } + @SubscriptionLocator.getSubscription = sinon.stub().yields(null, @subscription) + @SubscriptionUpdater._setUsersMinimumFeatures = sinon.stub().yields() + @SubscriptionUpdater.deleteSubscription @subscription_id, done + + it "should look up the subscription", -> + @SubscriptionLocator.getSubscription + .calledWith(@subscription_id) + .should.equal true + + it "should remove the subscription", -> + @SubscriptionModel.remove + .calledWith({_id: ObjectId(@subscription_id)}) + .should.equal true + + it "should downgrade the admin_id", -> + @SubscriptionUpdater._setUsersMinimumFeatures + .calledWith(@subscription.admin_id) + .should.equal true + + it "should downgrade all of the members", -> + for user_id in @subscription.member_ids + @SubscriptionUpdater._setUsersMinimumFeatures + .calledWith(user_id) + .should.equal true From 66d18bcbcd9433dc09ac60efd8d8cf507268460d Mon Sep 17 00:00:00 2001 From: James Allen Date: Wed, 2 Aug 2017 14:59:03 +0200 Subject: [PATCH 17/17] Validate and add error status to contact form --- services/web/app/views/contact-us-modal.pug | 87 ++++++++++++------- .../web/public/coffee/main/contact-us.coffee | 14 ++- 2 files changed, 67 insertions(+), 34 deletions(-) diff --git a/services/web/app/views/contact-us-modal.pug b/services/web/app/views/contact-us-modal.pug index d3e5aa0e87..aad68a53d3 100644 --- a/services/web/app/views/contact-us-modal.pug +++ b/services/web/app/views/contact-us-modal.pug @@ -7,36 +7,61 @@ script(type='text/ng-template', id='supportModalTemplate') ) × h3 #{translate("contact_us")} .modal-body.contact-us-modal - span(ng-show="sent == false") - label - | #{translate("subject")} - .form-group - input.field.text.medium.span8.form-control( - ng-model="form.subject", - ng-model-options="{ updateOn: 'default blur', debounce: {'default': 350, 'blur': 0} }" - maxlength='255', - tabindex='1', - onkeyup='') - .contact-suggestions(ng-show="suggestions.length") - p.contact-suggestion-label !{translate("kb_suggestions_enquiry", { kbLink: "__kb__", kb: translate("knowledge_base") })} - ul.contact-suggestion-list - li(ng-repeat="suggestion in suggestions") - a.contact-suggestion-list-item(ng-href="{{ suggestion.url }}", ng-click="clickSuggestionLink(suggestion.url);" target="_blank") - span(ng-bind-html="suggestion.name") - i.fa.fa-angle-right - label.desc(ng-show="'"+getUserEmail()+"'.length < 1") - | #{translate("email")} - .form-group(ng-show="'"+getUserEmail()+"'.length < 1") - input.field.text.medium.span8.form-control(ng-model="form.email", ng-init="form.email = '"+getUserEmail()+"'", type='email', spellcheck='false', value='', maxlength='255', tabindex='2') - label#title12.desc - | #{translate("project_url")} (#{translate("optional")}) - .form-group - input.field.text.medium.span8.form-control(ng-model="form.project_url", tabindex='3', onkeyup='') - label.desc - | #{translate("contact_message_label")} - .form-group - textarea.field.text.medium.span8.form-control(ng-model="form.message",type='text', value='', tabindex='4', onkeyup='') - .form-group.text-center - input.btn-success.btn.btn-lg(type='submit', ng-disabled="sending", ng-click="contactUs()" value=translate("contact_us")) + form(name="contactForm") + span(ng-show="sent == false") + .alert.alert-danger(ng-show="error") Something went wrong sending your request :( + label + | #{translate("subject")} + .form-group + input.field.text.medium.span8.form-control( + name="subject", + required + ng-model="form.subject", + ng-model-options="{ updateOn: 'default blur', debounce: {'default': 350, 'blur': 0} }" + maxlength='255', + tabindex='1', + onkeyup='') + .contact-suggestions(ng-show="suggestions.length") + p.contact-suggestion-label !{translate("kb_suggestions_enquiry", { kbLink: "__kb__", kb: translate("knowledge_base") })} + ul.contact-suggestion-list + li(ng-repeat="suggestion in suggestions") + a.contact-suggestion-list-item(ng-href="{{ suggestion.url }}", ng-click="clickSuggestionLink(suggestion.url);" target="_blank") + span(ng-bind-html="suggestion.name") + i.fa.fa-angle-right + label.desc(ng-show="'"+getUserEmail()+"'.length < 1") + | #{translate("email")} + .form-group(ng-show="'"+getUserEmail()+"'.length < 1") + input.field.text.medium.span8.form-control( + name="email", + required + ng-model="form.email", + ng-init="form.email = '"+getUserEmail()+"'", + type='email', spellcheck='false', + value='', + maxlength='255', + tabindex='2') + label#title12.desc + | #{translate("project_url")} (#{translate("optional")}) + .form-group + input.field.text.medium.span8.form-control(ng-model="form.project_url", tabindex='3', onkeyup='') + label.desc + | #{translate("contact_message_label")} + .form-group + textarea.field.text.medium.span8.form-control( + name="body", + required + ng-model="form.message", + type='text', + value='', + tabindex='4', + onkeyup='' + ) + .form-group.text-center + input.btn-success.btn.btn-lg( + type='submit', + ng-disabled="contactForm.$invalid || sending", + ng-click="contactUs()" + value=translate("contact_us") + ) span(ng-show="sent") p #{translate("request_sent_thank_you")} diff --git a/services/web/public/coffee/main/contact-us.coffee b/services/web/public/coffee/main/contact-us.coffee index 2f3a2c61e0..7bb86a6b93 100644 --- a/services/web/public/coffee/main/contact-us.coffee +++ b/services/web/public/coffee/main/contact-us.coffee @@ -30,7 +30,7 @@ define [ $scope.suggestions = suggestions $scope.contactUs = -> - if !$scope.form.email? + if !$scope.form.email? or $scope.form.email == "" console.log "email not set" return $scope.sending = true @@ -46,8 +46,16 @@ define [ about: "
browser: #{platform?.name} #{platform?.version}
os: #{platform?.os?.family} #{platform?.os?.version}
" - Groove.createTicket params, (err, json)-> - $scope.sent = true + Groove.createTicket params, (response)-> + $scope.sending = false + if response.responseText == "" # Blocked request or similar + $scope.error = true + else + data = JSON.parse(response.responseText) + if data.errors? + $scope.error = true + else + $scope.sent = true $scope.$apply() $scope.$watch "form.subject", (newVal, oldVal) ->