From cf5101e48496e9fae8df361d7e161ca19559febf Mon Sep 17 00:00:00 2001 From: Chris Brame Date: Thu, 11 May 2017 01:01:59 -0400 Subject: [PATCH] MailCheck Revamp. Fixes #9 --- app.js | 31 +++-- package.json | 2 +- src/controllers/settings.js | 2 + src/mailer/mailCheck.js | 128 ++++++++++++------ src/models/setting.js | 2 +- src/models/tickettype.js | 16 +++ .../js/angularjs/controllers/settings.js | 21 ++- src/public/js/plugins/snackbar.js | 4 +- src/views/settings.hbs | 9 ++ yarn.lock | 81 ++++++----- 10 files changed, 206 insertions(+), 90 deletions(-) diff --git a/app.js b/app.js index d8e022426..2e9762f2e 100644 --- a/app.js +++ b/app.js @@ -9,7 +9,8 @@ ======================================================================== **/ -var async = require('async'), +var _ = require('underscore'), + async = require('async'), path = require('path'), fs = require('fs'), winston = require('winston'), @@ -178,28 +179,38 @@ function dbCallback(err, db) { async.series([ function(next) { require('./src/socketserver')(ws); - next(); + return next(); }, function(next) { //Start Mailer var mailQueue = require('./src/mailer'); mailQueue.queue(); - next(); + return next(); }, function(next) { //Start Check Mail - var mailerEnabled = nconf.get('mailer:enable'); - if (mailerEnabled) { - var mailCheck = require('./src/mailer/mailCheck'); - mailCheck.init(); - } + var settingSchema = require('./src/models/setting'); + settingSchema.getSettings(function(err, settings) { + if (err) { + winston.warn(err); + return next(); + } + + var mailerCheckEnabled = _.find(settings, function(x) { return x.name === 'mailer:check:enable' }); + mailerCheckEnabled = (mailerCheckEnabled === undefined) ? {value: false} : mailerCheckEnabled; + if (mailerCheckEnabled.value) { + var mailCheck = require('./src/mailer/mailCheck'); + winston.debug('Starting MailCheck...'); + mailCheck.init(settings); + } - next(); + return next(); + }); }, function(next) { //Start Task Runners require('./src/taskrunner'); - next(); + return next(); }, function(next) { //var pm2 = require('pm2'); diff --git a/package.json b/package.json index 3d68146cc..ee7fdd616 100644 --- a/package.json +++ b/package.json @@ -55,7 +55,7 @@ "kerberos": "0.0.21", "lodash": "^4.17.4", "logrotate-stream": "0.2.5", - "mailparser": "0.6.1", + "mailparser": "^2.0.5", "marked": "0.3.5", "matchdep": "1.0.1", "moment": "2.10.2", diff --git a/src/controllers/settings.js b/src/controllers/settings.js index c1ca89d94..060304ad3 100644 --- a/src/controllers/settings.js +++ b/src/controllers/settings.js @@ -62,12 +62,14 @@ settingsController.get = function(req, res) { s.mailerCheckPort = _.find(settings, function(x) { return x.name === 'mailer:check:port' }); s.mailerCheckUsername = _.find(settings, function(x) { return x.name === 'mailer:check:username' }); s.mailerCheckPassword = _.find(settings, function(x) { return x.name === 'mailer:check:password' }); + s.mailerCheckTicketType = _.find(settings, function(x) { return x.name === 'mailer:check:ticketype' }); s.mailerCheckEnabled = (s.mailerCheckEnabled === undefined) ? {value: false} : s.mailerCheckEnabled; s.mailerCheckHost = (s.mailerCheckHost === undefined) ? {value: ''} : s.mailerCheckHost; s.mailerCheckPort = (s.mailerCheckPort === undefined) ? {value: 143} : s.mailerCheckPort; s.mailerCheckUsername = (s.mailerCheckUsername === undefined) ? {value: ''} : s.mailerCheckUsername; s.mailerCheckPassword = (s.mailerCheckPassword === undefined) ? {value: ''} : s.mailerCheckPassword; + s.mailerCheckTicketType = (s.mailerCheckTicketType === undefined) ? {value: ''} : s.mailerCheckTicketType; s.showTour = _.find(settings, function(x) { return x.name === 'showTour:enable' }); s.showTour = (s.showTour === undefined) ? {value: true} : s.showTour; diff --git a/src/mailer/mailCheck.js b/src/mailer/mailCheck.js index 634fe9bc5..566fce7b3 100644 --- a/src/mailer/mailCheck.js +++ b/src/mailer/mailCheck.js @@ -15,11 +15,9 @@ var _ = require('underscore'); var async = require('async'); var Imap = require('imap'); -//var inspect = require('util').inspect; -//var MailParser = require('mailparser').MailParser; var winston = require('winston'); -var nconf = require('nconf'); var marked = require('marked'); +var simpleParser = require('mailparser').simpleParser; var emitter = require('../emitter'); var userSchema = require('../models/user'); @@ -27,33 +25,48 @@ var groupSchema = require('../models/group'); var ticketTypeSchema = require('../models/tickettype'); var Ticket = require('../models/ticket'); -var MAILERCHECK_ENABLED = nconf.get('mailer:check:enable'); -var MAILERCHECK_USER = nconf.get('mailer:check:user') ? nconf.get('mailer:check:user') : MAILERCHECK_ENABLED = false; -var MAILERCHECK_PASS = nconf.get('mailer:check:pass') ? nconf.get('mailer:check:pass') : MAILERCHECK_ENABLED = false; -var MAILERCHECK_HOST = nconf.get('mailer:check:host') ? nconf.get('mailer:check:host') : MAILERCHECK_ENABLED = false; -var POLLING_INTERVAL = nconf.get('mailer:check:polling') ? nconf.get('mailer:check:polling') : 600000; //10 min -var DEFAULT_TICKET_TYPE = nconf.get('mailer:check:defaultTicketType') ? nconf.get('mailer:check:defaultTicketType') : 'Issue'; - var mailCheck = {}; -mailCheck.Imap = new Imap({ - user: MAILERCHECK_USER, - password: MAILERCHECK_PASS, - host: MAILERCHECK_HOST, - port: 143 -}); - mailCheck.inbox = []; -mailCheck.init = function() { +mailCheck.init = function(settings) { + var s = {}; + s.mailerCheckEnabled = _.find(settings, function(x) { return x.name === 'mailer:check:enable' }); + s.mailerCheckHost = _.find(settings, function(x) { return x.name === 'mailer:check:host' }); + s.mailerCheckPort = _.find(settings, function(x) { return x.name === 'mailer:check:port' }); + s.mailerCheckUsername = _.find(settings, function(x) { return x.name === 'mailer:check:username' }); + s.mailerCheckPassword = _.find(settings, function(x) { return x.name === 'mailer:check:password' }); + s.mailerCheckTicketType = _.find(settings, function(x) { return x.name === 'mailer:check:ticketype' }); + + s.mailerCheckEnabled = (s.mailerCheckEnabled === undefined) ? {value: false} : s.mailerCheckEnabled; + s.mailerCheckHost = (s.mailerCheckHost === undefined) ? {value: ''} : s.mailerCheckHost; + s.mailerCheckPort = (s.mailerCheckPort === undefined) ? {value: 143} : s.mailerCheckPort; + s.mailerCheckUsername = (s.mailerCheckUsername === undefined) ? {value: ''} : s.mailerCheckUsername; + s.mailerCheckPassword = (s.mailerCheckPassword === undefined) ? {value: ''} : s.mailerCheckPassword; + s.mailerCheckTicketType = (s.mailerCheckTicketType === undefined) ? {value: 'Issue'} : s.mailerCheckTicketType; + + var MAILERCHECK_ENABLED = s.mailerCheckEnabled.value; + var MAILERCHECK_HOST = s.mailerCheckHost.value; + var MAILERCHECK_USER = s.mailerCheckUsername.value; + var MAILERCHECK_PASS = s.mailerCheckPassword.value; + var POLLING_INTERVAL = 600000; //10 min + var DEFAULT_TICKET_TYPE = s.mailerCheckTicketType.value; + if (!MAILERCHECK_ENABLED) return true; - mailCheck.fetchMail(); + mailCheck.Imap = new Imap({ + user: MAILERCHECK_USER, + password: MAILERCHECK_PASS, + host: MAILERCHECK_HOST, + port: 143 + }); + + mailCheck.fetchMail(DEFAULT_TICKET_TYPE); setInterval(function() { - mailCheck.fetchMail(); + mailCheck.fetchMail(DEFAULT_TICKET_TYPE); }, POLLING_INTERVAL); }; -mailCheck.fetchMail = function() { +mailCheck.fetchMail = function(DEFAULT_TICKET_TYPE) { mailCheck.Imap.connect(); mailCheck.Imap.once('error', function(err) { winston.warn(err); @@ -83,7 +96,8 @@ mailCheck.fetchMail = function() { var f = mailCheck.Imap.fetch(results, { //markSeen: true, - bodies: ['HEADER.FIELDS (FROM SUBJECT)','TEXT'] + // bodies: ['HEADER.FIELDS (FROM SUBJECT CONTENT-TYPE)','TEXT'] + bodies: '' }); f.on('message', function(msg, seqno) { @@ -93,17 +107,36 @@ mailCheck.fetchMail = function() { count += chunk.length; buffer += chunk.toString('utf8'); }); + stream.once('end', function() { - if (info.which !== 'TEXT') { - var header = Imap.parseHeader(buffer); - if (_.isArray(header.subject) && _.size(header.subject) > 0) - message.subject = header.subject[0]; - if (_.isArray(header.from) && _.size(header.from) > 0) - message.from = parseEmail(header.from[0]); - } else - message.body = buffer; + simpleParser(buffer, function(err, mail) { + if (err) winston.warn(err); + + if (mail.headers.has('from')) { + message.from = mail.headers.get('from').value[0].address; + } + if (mail.subject) { + message.subject = mail.subject; + } else { + message.subject = message.from; + } + + message.body = mail.textAsHtml; + }); + + // if (info.which !== 'TEXT') { + // var header = Imap.parseHeader(buffer); + // if (_.isArray(header.subject) && _.size(header.subject) > 0) + // message.subject = header.subject[0]; + // if (_.isArray(header.from) && _.size(header.from) > 0) + // message.from = parseEmail(header.from[0]); + // if (_.isArray(header['content-type']) && _.size(header['content-type']) > 0) + // message.contentType = header['content-type'][0]; + // } else + // message.body = buffer; }); }); + async.series([ function(cb) { msg.once('end', function() { @@ -122,8 +155,25 @@ mailCheck.fetchMail = function() { if (_.size(group) < 1) return cb(); - ticketTypeSchema.getTypeByName(DEFAULT_TICKET_TYPE, function(err, type) { - if (err) return cb(err); + async.waterfall([ + function(d) { + if (DEFAULT_TICKET_TYPE !== 'Issue') return d(null, null); + ticketTypeSchema.getTypeByName(DEFAULT_TICKET_TYPE, function(err, t) { + if (err) return d(err); + + return d(null, t); + }); + }, + function(type, d) { + if (type !== null) return d(null, type); + ticketTypeSchema.getType(DEFAULT_TICKET_TYPE, function(err, t) { + if (err) return d(err); + + return d(null, t); + }); + } + ], function(err, type) { + if (err || type == null) return cb(err); var HistoryItem = { action: 'ticket:created', @@ -139,7 +189,7 @@ mailCheck.fetchMail = function() { status: 0, priority: 1, subject: message.subject, - issue: parseBody(message.body), + issue: message.body, history: [HistoryItem] }, function(err, t) { if (err) { @@ -149,19 +199,19 @@ mailCheck.fetchMail = function() { emitter.emit('ticket:created', {socketId: '', ticket: t}); - cb(); + return cb(); }); }); }); } else - cb(); + return cb(); } else { mailCheck.Imap.seq.addFlags(seqno, '\\Deleted', function(err) { if (err) winston.warn(err); - cb(); + return cb(); }); } }); @@ -173,11 +223,11 @@ mailCheck.fetchMail = function() { f.once('error', function(err) { winston.error('Fetch error: ' + err); - next(err); + return next(err); }); f.once('end', function() { - next(); + return next(); }); }); }); @@ -185,7 +235,7 @@ mailCheck.fetchMail = function() { ], function(err) { if (err) winston.warn(err); mailCheck.Imap.closeBox(true, function(err) { - winston.debug(err); + if (err) winston.debug(err); mailCheck.Imap.end(); }); }); diff --git a/src/models/setting.js b/src/models/setting.js index 8047af7f6..1b83e83bf 100644 --- a/src/models/setting.js +++ b/src/models/setting.js @@ -41,7 +41,7 @@ var settingSchema = mongoose.Schema({ * @param {QueryCallback} callback MongoDB Query Callback */ settingSchema.statics.getSettings = function(callback) { - var q = this.model(COLLECTION).find({}); + var q = this.model(COLLECTION).find({}).select('name value'); return q.exec(callback); }; diff --git a/src/models/tickettype.js b/src/models/tickettype.js index dc8130dfa..585f02945 100644 --- a/src/models/tickettype.js +++ b/src/models/tickettype.js @@ -44,6 +44,22 @@ ticketTypeSchema.statics.getTypes = function(callback) { return q.exec(callback); }; +/** + * Return Single Ticket Types + * + * @memberof TicketType + * @static + * @method getType + * + * @param {String} id Object Id of ticket type + * @param {QueryCallback} callback MongoDB Query Callback + */ +ticketTypeSchema.statics.getType = function(id, callback) { + var q = this.model(COLLECTION).findOne({_id: id}).lean(); + + return q.exec(callback); +}; + /** * Return Single Ticket Type based on given type name * diff --git a/src/public/js/angularjs/controllers/settings.js b/src/public/js/angularjs/controllers/settings.js index 3f51ba0e5..d92233b80 100644 --- a/src/public/js/angularjs/controllers/settings.js +++ b/src/public/js/angularjs/controllers/settings.js @@ -27,6 +27,14 @@ define(['angular', 'underscore', 'jquery', 'modules/helpers', 'modules/ui', 'uik s.addClass('md-input-filled'); } }); + + if ($scope.mailerCheckTicketType !== '') { + var $mailerCheckTicketTypeSelect = $('#mailerCheckTicketType'); + $mailerCheckTicketTypeSelect.find('option[value="' + $scope.mailerCheckTicketType + '"]').prop('selected', true); + var $selectizeTicketType = $mailerCheckTicketTypeSelect[0].selectize; + $selectizeTicketType.setValue($scope.mailerCheckTicketType, true); + $selectizeTicketType.refreshItems(); + } }, 0); }; @@ -118,6 +126,10 @@ define(['angular', 'underscore', 'jquery', 'modules/helpers', 'modules/ui', 'uik $('input#mailerCheckUsername').attr('disabled', !newVal); $('input#mailerCheckPassword').attr('disabled', !newVal); $('button#mailerCheckSubmit').attr('disabled', !newVal); + if (!newVal == true) + $('select#mailerCheckTicketType').selectize()[0].selectize.disable(); + else + $('select#mailerCheckTicketType').selectize()[0].selectize.enable(); }); $scope.mailerCheckEnabledChange = function() { @@ -140,12 +152,13 @@ define(['angular', 'underscore', 'jquery', 'modules/helpers', 'modules/ui', 'uik $scope.mailerCheckFormSubmit = function($event) { $event.preventDefault(); - + var mailerCheckTicketTypeValue = $('#mailerCheckTicketType option[selected]').val(); $http.put('/api/v1/settings', [ {name: 'mailer:check:host', value: $scope.mailerCheckHost}, {name: 'mailer:check:port', value: $scope.mailerCheckPort}, {name: 'mailer:check:username', value: $scope.mailerCheckUsername}, - {name: 'mailer:check:password', value: $scope.mailerCheckPassword} + {name: 'mailer:check:password', value: $scope.mailerCheckPassword}, + {name: 'mailer:check:ticketype', value: mailerCheckTicketTypeValue} ], { headers: { @@ -153,8 +166,10 @@ define(['angular', 'underscore', 'jquery', 'modules/helpers', 'modules/ui', 'uik } }).then(function successCallback() { helpers.UI.showSnackbar('Mail Check Settings Saved', false); + }, function errorCallback(err) { - helpers.UI.showSnackbar(err, true); + helpers.UI.showSnackbar(err.data.error, true); + $log.error(err); }); }; diff --git a/src/public/js/plugins/snackbar.js b/src/public/js/plugins/snackbar.js index b12f7b6ab..e0729c1ed 100644 --- a/src/public/js/plugins/snackbar.js +++ b/src/public/js/plugins/snackbar.js @@ -49,7 +49,7 @@ }; Snackbar.show = function ($options) { - var options = Extend(true, $defaults, $options); + var options = extend(true, $defaults, $options); if (Snackbar.current) { Snackbar.current.style.opacity = 0; @@ -131,7 +131,7 @@ // Pure JS Extend // http://gomakethings.com/vanilla-javascript-version-of-jquery-extend/ - var Extend = function () { + var extend = function () { var extended = {}; var deep = false; diff --git a/src/views/settings.hbs b/src/views/settings.hbs index 36f37bdfe..b654cba86 100644 --- a/src/views/settings.hbs +++ b/src/views/settings.hbs @@ -12,6 +12,7 @@ mailerCheckPort={{data.settings.mailerCheckPort.value}}; mailerCheckUsername='{{data.settings.mailerCheckUsername.value}}'; mailerCheckPassword='{{data.settings.mailerCheckPassword.value}}'; + mailerCheckTicketType='{{data.settings.mailerCheckTicketType.value}}'; showTour={{data.settings.showTour.value}}; showOverdueTickets={{data.settings.showOverdueTickets.value}}; @@ -210,6 +211,14 @@ +
+ + +
diff --git a/yarn.lock b/yarn.lock index 154a28161..895dd357f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -48,10 +48,6 @@ addressparser@1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/addressparser/-/addressparser-1.0.1.tgz#47afbe1a2a9262191db6838e4fd1d39b40821746" -addressparser@~0.3.2: - version "0.3.2" - resolved "https://registry.yarnpkg.com/addressparser/-/addressparser-0.3.2.tgz#59873f35e8fcf6c7361c10239261d76e15348bb2" - after@0.8.1: version "0.8.1" resolved "https://registry.yarnpkg.com/after/-/after-0.8.1.tgz#ab5d4fb883f596816d3515f8f791c0af486dd627" @@ -1332,12 +1328,6 @@ encodeurl@~1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.1.tgz#79e3d58655346909fe6f0f45a5de68103b294d20" -encoding@^0.1.11, encoding@~0.1.7: - version "0.1.12" - resolved "https://registry.yarnpkg.com/encoding/-/encoding-0.1.12.tgz#538b66f3ee62cd1ab51ec323829d1f9480c74beb" - dependencies: - iconv-lite "~0.4.13" - engine.io-client@1.6.11: version "1.6.11" resolved "https://registry.yarnpkg.com/engine.io-client/-/engine.io-client-1.6.11.tgz#7d250d8fa1c218119ecde51390458a57d5171376" @@ -2335,6 +2325,10 @@ hawk@~3.1.0, hawk@~3.1.3: hoek "2.x.x" sntp "1.x.x" +he@^1.0.0, he@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/he/-/he-1.1.1.tgz#93410fd21b009735151f8868c2f271f3427e23fd" + hoek@2.x.x: version "2.16.3" resolved "https://registry.yarnpkg.com/hoek/-/hoek-2.16.3.tgz#20bb7403d3cea398e91dc4710a8ff1b8274a25ed" @@ -2351,6 +2345,16 @@ hosted-git-info@^2.1.4: version "2.2.0" resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.2.0.tgz#7a0d097863d886c0fabbdcd37bf1758d8becf8a5" +html-to-text@3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/html-to-text/-/html-to-text-3.2.0.tgz#0dfa5d27ff816b07281c79eaf60d408744ac6d89" + dependencies: + he "^1.0.0" + htmlparser2 "^3.9.2" + optimist "^0.6.1" + underscore "^1.8.3" + underscore.string "^3.2.3" + htmlhint@~0.9.13: version "0.9.13" resolved "https://registry.yarnpkg.com/htmlhint/-/htmlhint-0.9.13.tgz#08163cb1e6aa505048ebb0b41063a7ca07dc6c88" @@ -2375,7 +2379,7 @@ htmlparser2@3.8.x, htmlparser2@~3.8.1: entities "1.0" readable-stream "1.1" -htmlparser2@^3.9.0: +htmlparser2@^3.9.0, htmlparser2@^3.9.2: version "3.9.2" resolved "https://registry.yarnpkg.com/htmlparser2/-/htmlparser2-3.9.2.tgz#1bdf87acca0f3f9e53fa4fcceb0f4b4cbb00b338" dependencies: @@ -2432,10 +2436,14 @@ iconv-lite@0.4.13, iconv-lite@~0.4.13: version "0.4.13" resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.13.tgz#1f88aba4ab0b1508e8312acc39345f36e992e2f2" -iconv-lite@^0.4.15: +iconv-lite@0.4.15, iconv-lite@^0.4.15: version "0.4.15" resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.15.tgz#fe265a218ac6a57cfe854927e9d04c19825eddeb" +iconv-lite@0.4.17: + version "0.4.17" + resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.17.tgz#4fdaa3b38acbc2c031b045d0edcdfe1ecab18c8d" + ieee754@^1.1.4: version "1.1.8" resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.1.8.tgz#be33d40ac10ef1926701f6f08a2d86fbfd1ad3e4" @@ -2938,6 +2946,14 @@ libmime@2.0.3: libbase64 "0.1.0" libqp "1.1.0" +libmime@3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/libmime/-/libmime-3.1.0.tgz#115012f1672051adc8809a8f93955ffc3648edf9" + dependencies: + iconv-lite "0.4.15" + libbase64 "0.1.0" + libqp "1.1.0" + libqp@1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/libqp/-/libqp-1.1.0.tgz#f5e6e06ad74b794fb5b5b66988bf728ef1dedbe8" @@ -3082,14 +3098,24 @@ mailcomposer@3.10.0: buildmail "3.8.0" libmime "2.0.3" -mailparser@0.6.1: - version "0.6.1" - resolved "https://registry.yarnpkg.com/mailparser/-/mailparser-0.6.1.tgz#3de4db3f4a90c160c06d8cb8b825a7f1c6f6a7c3" +mailparser@^2.0.5: + version "2.0.5" + resolved "https://registry.yarnpkg.com/mailparser/-/mailparser-2.0.5.tgz#24e66fec4fea97b847a62be8ed0113f312c1605c" dependencies: - encoding "^0.1.11" - mime "^1.3.4" - mimelib "^0.2.19" - uue "^3.0.0" + addressparser "1.0.1" + he "^1.1.1" + html-to-text "3.2.0" + iconv-lite "0.4.17" + libmime "3.1.0" + mailsplit "4.0.2" + +mailsplit@4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/mailsplit/-/mailsplit-4.0.2.tgz#29dcbdd5705b29eecb0ebf4f9056a6886a2c6dfe" + dependencies: + libbase64 "0.1.0" + libmime "3.1.0" + libqp "1.1.0" map-obj@^1.0.0, map-obj@^1.0.1: version "1.0.1" @@ -3244,13 +3270,6 @@ mime@1.3.4, mime@^1.3.4: version "1.3.4" resolved "https://registry.yarnpkg.com/mime/-/mime-1.3.4.tgz#115f9e3b6b3daf2959983cb38f149a2d40eb5d53" -mimelib@^0.2.19: - version "0.2.19" - resolved "https://registry.yarnpkg.com/mimelib/-/mimelib-0.2.19.tgz#37ec90a6ac7d00954851d0b2c31618f0a49da0ee" - dependencies: - addressparser "~0.3.2" - encoding "~0.1.7" - mimer@*: version "0.2.1" resolved "https://registry.yarnpkg.com/mimer/-/mimer-0.2.1.tgz#c63c5a17fe86423f5161a85d55c3ed5189baaffc" @@ -5187,7 +5206,7 @@ underscore-contrib@~0.3.0: dependencies: underscore "1.6.0" -underscore.string@3.3.4: +underscore.string@3.3.4, underscore.string@^3.2.3: version "3.3.4" resolved "https://registry.yarnpkg.com/underscore.string/-/underscore.string-3.3.4.tgz#2c2a3f9f83e64762fdc45e6ceac65142864213db" dependencies: @@ -5206,7 +5225,7 @@ underscore@1.6.0, underscore@~1.6.0: version "1.6.0" resolved "https://registry.yarnpkg.com/underscore/-/underscore-1.6.0.tgz#8b38b10cacdef63337b8b24e4ff86d45aea529a8" -underscore@1.8.3, underscore@~1.8.3: +underscore@1.8.3, underscore@^1.8.3, underscore@~1.8.3: version "1.8.3" resolved "https://registry.yarnpkg.com/underscore/-/underscore-1.8.3.tgz#4f3fb53b106e6097fcf9cb4109f2a5e9bdfa5022" @@ -5279,12 +5298,6 @@ utils-merge@1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.0.tgz#0294fb922bb9375153541c4f7096231f287c8af8" -uue@^3.0.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/uue/-/uue-3.1.0.tgz#5d67d37030e66efebbb4b8aac46daf9b55befbf6" - dependencies: - extend "~3.0.0" - uuid@^3.0.0: version "3.0.1" resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.0.1.tgz#6544bba2dfda8c1cf17e629a3a305e2bb1fee6c1"