From 367b16a3ab27ee534b4e497c2699d6fc0dc7368f Mon Sep 17 00:00:00 2001 From: hugh-obrien Date: Wed, 11 Jul 2018 16:36:03 +0100 Subject: [PATCH 01/14] removes checking params for submit modal --- .../web/app/coffee/Features/Project/ProjectController.coffee | 1 - 1 file changed, 1 deletion(-) diff --git a/services/web/app/coffee/Features/Project/ProjectController.coffee b/services/web/app/coffee/Features/Project/ProjectController.coffee index e2c71a956e..5a1bbb11f6 100644 --- a/services/web/app/coffee/Features/Project/ProjectController.coffee +++ b/services/web/app/coffee/Features/Project/ProjectController.coffee @@ -346,7 +346,6 @@ module.exports = ProjectController = useV2History: !!project.overleaf?.history?.display showRichText: req.query?.rt == 'true' showTestControls: req.query?.tc == 'true' || user.isAdmin - showPublishModal: req.query?.pm == 'true' timer.done() _buildProjectList: (allProjects, v1Projects = [])-> From 2dcd2339ed71248148e6ba8bda9175a703b165c0 Mon Sep 17 00:00:00 2001 From: Nate Stemen Date: Thu, 26 Jul 2018 15:03:55 -0400 Subject: [PATCH 02/14] still trigger autocomplete if % is escaped --- .../aceEditor/auto-complete/AutoCompleteManager.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 638ab20b30..e91c92f643 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 @@ -162,7 +162,7 @@ define [ cursorPosition = @editor.getCursorPosition() end = change.end {lineUpToCursor, commandFragment} = Helpers.getContext(@editor, end) - if lineUpToCursor.match(/.*%.*/) + if lineUpToCursor.match(/.*((?![\\]).|^)%.*/) return lastCharIsBackslash = lineUpToCursor.slice(-1) == "\\" lastTwoChars = lineUpToCursor.slice(-2) From 05aedf1e099e92b9c792ab579a01d1165cdbef82 Mon Sep 17 00:00:00 2001 From: Brian Gough Date: Tue, 31 Jul 2018 16:54:09 +0100 Subject: [PATCH 03/14] add script to increase paid compile timeouts to 240s --- .../web/scripts/increase_compile_timeouts.js | 64 +++++++++++++++++++ 1 file changed, 64 insertions(+) create mode 100644 services/web/scripts/increase_compile_timeouts.js diff --git a/services/web/scripts/increase_compile_timeouts.js b/services/web/scripts/increase_compile_timeouts.js new file mode 100644 index 0000000000..8aa451545a --- /dev/null +++ b/services/web/scripts/increase_compile_timeouts.js @@ -0,0 +1,64 @@ +const mongojs = require('../app/js/infrastructure/mongojs') +const { db } = mongojs +const async = require('async') +const minilist = require('minimist') + +const newTimeout = 240 +const oldTimeoutLimits = {$gt: 60, $lt: 240} + +const updateUser = function (user, callback) { + console.log(`Updating user ${user._id}`) + const update = { + $set: { + 'features.compileTimeout': newTimeout + } + } + db.users.update({ + _id: user._id, + 'features.compileTimeout': oldTimeoutLimits + }, update, callback) +} + +const updateUsers = (users, callback) => + async.eachLimit(users, ASYNC_LIMIT, updateUser, function (error) { + if (error) { + callback(error) + return + } + counter += users.length + console.log(`${counter} users updated`) + loopForUsers(callback) + }) + +var loopForUsers = callback => + db.users.find( + { 'features.compileTimeout': oldTimeoutLimits }, + { 'features.compileTimeout': 1 } + ).limit(FETCH_LIMIT, function (error, users) { + if (error) { + callback(error) + return + } + if (users.length === 0) { + console.log(`DONE (${counter} users updated)`) + return callback() + } + updateUsers(users, callback) + }) + +var counter = 0 +var run = () => + loopForUsers(function (error) { + if (error) { throw error } + process.exit() + }) + +let FETCH_LIMIT, ASYNC_LIMIT +var setup = function () { + let args = minilist(process.argv.slice(2)) + FETCH_LIMIT = (args.fetch) ? args.fetch : 100 + ASYNC_LIMIT = (args.async) ? args.async : 10 +} + +setup() +run() From 7c9c0fbf06dc2420866e9e5441763b058a21fa0d Mon Sep 17 00:00:00 2001 From: Brian Gough Date: Thu, 2 Aug 2018 13:49:56 +0100 Subject: [PATCH 04/14] add --all option for increase_compile_timeouts script --- services/web/scripts/increase_compile_timeouts.js | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/services/web/scripts/increase_compile_timeouts.js b/services/web/scripts/increase_compile_timeouts.js index 8aa451545a..69323134e9 100644 --- a/services/web/scripts/increase_compile_timeouts.js +++ b/services/web/scripts/increase_compile_timeouts.js @@ -26,8 +26,13 @@ const updateUsers = (users, callback) => return } counter += users.length - console.log(`${counter} users updated`) - loopForUsers(callback) + console.log(`${counter} users updated`); + if (DO_ALL) { + return loopForUsers(callback) + } else { + console.log('run again to continue updating'); + return callback() + } }) var loopForUsers = callback => @@ -53,11 +58,15 @@ var run = () => process.exit() }) -let FETCH_LIMIT, ASYNC_LIMIT +let FETCH_LIMIT, ASYNC_LIMIT, DO_ALL var setup = function () { let args = minilist(process.argv.slice(2)) + // --fetch N get N users each time FETCH_LIMIT = (args.fetch) ? args.fetch : 100 + // --async M run M updates in parallel ASYNC_LIMIT = (args.async) ? args.async : 10 + // --all means run to completion + DO_ALL = (args.all) } setup() From d6ab6ce6eececf0627b4439d98fc9418e5550916 Mon Sep 17 00:00:00 2001 From: Brian Gough Date: Thu, 2 Aug 2018 14:06:51 +0100 Subject: [PATCH 05/14] improve --all switch to remove fetch limit --- .../web/scripts/increase_compile_timeouts.js | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/services/web/scripts/increase_compile_timeouts.js b/services/web/scripts/increase_compile_timeouts.js index 69323134e9..6889f26962 100644 --- a/services/web/scripts/increase_compile_timeouts.js +++ b/services/web/scripts/increase_compile_timeouts.js @@ -30,7 +30,7 @@ const updateUsers = (users, callback) => if (DO_ALL) { return loopForUsers(callback) } else { - console.log('run again to continue updating'); + console.log('*** run again to continue updating ***') return callback() } }) @@ -66,7 +66,19 @@ var setup = function () { // --async M run M updates in parallel ASYNC_LIMIT = (args.async) ? args.async : 10 // --all means run to completion - DO_ALL = (args.all) + if (args.all) { + if (args.fetch) { + console.error('error: do not use --fetch with --all') + process.exit(1) + } else { + DO_ALL = true + // if we are updating for all users then ignore the fetch limit. + FETCH_LIMIT = 0 + // A limit() value of 0 (i.e. .limit(0)) is equivalent to setting + // no limit. + // https://docs.mongodb.com/manual/reference/method/cursor.limit + } + } } setup() From b9597358a959b22e9b2b002b5f8ff1108b57e57e Mon Sep 17 00:00:00 2001 From: Brian Gough Date: Thu, 2 Aug 2018 14:14:23 +0100 Subject: [PATCH 06/14] lint fix --- services/web/scripts/increase_compile_timeouts.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/web/scripts/increase_compile_timeouts.js b/services/web/scripts/increase_compile_timeouts.js index 6889f26962..185c563277 100644 --- a/services/web/scripts/increase_compile_timeouts.js +++ b/services/web/scripts/increase_compile_timeouts.js @@ -26,7 +26,7 @@ const updateUsers = (users, callback) => return } counter += users.length - console.log(`${counter} users updated`); + console.log(`${counter} users updated`) if (DO_ALL) { return loopForUsers(callback) } else { From d8c9a966197ac88f0caac7656c300af54bb96cbc Mon Sep 17 00:00:00 2001 From: Shane Kilkelly Date: Tue, 31 Jul 2018 15:53:05 +0100 Subject: [PATCH 07/14] If we're creating v1 accounts, don't allow login for users already linked up --- .../AuthenticationController.coffee | 44 ++++++++++++------- .../AuthenticationControllerTests.coffee | 2 +- 2 files changed, 29 insertions(+), 17 deletions(-) diff --git a/services/web/app/coffee/Features/Authentication/AuthenticationController.coffee b/services/web/app/coffee/Features/Authentication/AuthenticationController.coffee index 832f01043f..951506891f 100644 --- a/services/web/app/coffee/Features/Authentication/AuthenticationController.coffee +++ b/services/web/app/coffee/Features/Authentication/AuthenticationController.coffee @@ -1,6 +1,5 @@ AuthenticationManager = require ("./AuthenticationManager") LoginRateLimiter = require("../Security/LoginRateLimiter") -UserGetter = require "../User/UserGetter" UserUpdater = require "../User/UserUpdater" Metrics = require('metrics-sharelatex') logger = require("logger-sharelatex") @@ -64,7 +63,10 @@ module.exports = AuthenticationController = if user # `user` is either a user object or false AuthenticationController.finishLogin(user, req, res, next) else - res.json message: info + if info.redir? + res.json {redir: info.redir} + else + res.json message: info )(req, res, next) finishLogin: (user, req, res, next) -> @@ -81,20 +83,30 @@ module.exports = AuthenticationController = doPassportLogin: (req, username, password, done) -> email = username.toLowerCase() - LoginRateLimiter.processLoginRequest email, (err, isAllowed)-> - return done(err) if err? - if !isAllowed - logger.log email:email, "too many login requests" - return done(null, null, {text: req.i18n.translate("to_many_login_requests_2_mins"), type: 'error'}) - AuthenticationManager.authenticate email: email, password, (error, user) -> - return done(error) if error? - if user? - # async actions - return done(null, user) - else - AuthenticationController._recordFailedLogin() - logger.log email: email, "failed log in" - return done(null, false, {text: req.i18n.translate("email_or_password_wrong_try_again"), type: 'error'}) + Modules = require "../../infrastructure/Modules" + Modules.hooks.fire 'preDoPassportLogin', email, (err, infoList) -> + return next(err) if err? + info = infoList.find((i) => i?) + if info? + return done(null, false, info) + LoginRateLimiter.processLoginRequest email, (err, isAllowed)-> + return done(err) if err? + if !isAllowed + logger.log email:email, "too many login requests" + return done(null, null, {text: req.i18n.translate("to_many_login_requests_2_mins"), type: 'error'}) + AuthenticationManager.authenticate email: email, password, (error, user) -> + return done(error) if error? + if user? + # async actions + return done(null, user) + else + AuthenticationController._recordFailedLogin() + logger.log email: email, "failed log in" + return done( + null, + false, + {text: req.i18n.translate("email_or_password_wrong_try_again"), type: 'error'} + ) _loginAsyncHandlers: (req, user) -> UserHandler.setupLoginData(user, ()->) diff --git a/services/web/test/unit/coffee/Authentication/AuthenticationControllerTests.coffee b/services/web/test/unit/coffee/Authentication/AuthenticationControllerTests.coffee index 8482c28e04..030c1cd0df 100644 --- a/services/web/test/unit/coffee/Authentication/AuthenticationControllerTests.coffee +++ b/services/web/test/unit/coffee/Authentication/AuthenticationControllerTests.coffee @@ -15,7 +15,6 @@ describe "AuthenticationController", -> tk.freeze(Date.now()) @AuthenticationController = SandboxedModule.require modulePath, requires: "./AuthenticationManager": @AuthenticationManager = {} - "../User/UserGetter" : @UserGetter = {} "../User/UserUpdater" : @UserUpdater = {} "metrics-sharelatex": @Metrics = { inc: sinon.stub() } "../Security/LoginRateLimiter": @LoginRateLimiter = { processLoginRequest:sinon.stub(), recordSuccessfulLogin:sinon.stub() } @@ -29,6 +28,7 @@ describe "AuthenticationController", -> trackSession: sinon.stub() untrackSession: sinon.stub() revokeAllUserSessions: sinon.stub().callsArgWith(1, null) + "../../infrastructure/Modules": {hooks: {fire: sinon.stub().callsArgWith(2, null, [])}} @user = _id: ObjectId() email: @email = "USER@example.com" From 05df55c89e183baffa736fb4fd41210bf63b4690 Mon Sep 17 00:00:00 2001 From: Shane Kilkelly Date: Fri, 3 Aug 2018 16:10:50 +0100 Subject: [PATCH 08/14] Add a unit test for the `preDoPassportLogin` module hook. --- .../AuthenticationControllerTests.coffee | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/services/web/test/unit/coffee/Authentication/AuthenticationControllerTests.coffee b/services/web/test/unit/coffee/Authentication/AuthenticationControllerTests.coffee index 030c1cd0df..ae8fedc9c8 100644 --- a/services/web/test/unit/coffee/Authentication/AuthenticationControllerTests.coffee +++ b/services/web/test/unit/coffee/Authentication/AuthenticationControllerTests.coffee @@ -28,7 +28,7 @@ describe "AuthenticationController", -> trackSession: sinon.stub() untrackSession: sinon.stub() revokeAllUserSessions: sinon.stub().callsArgWith(1, null) - "../../infrastructure/Modules": {hooks: {fire: sinon.stub().callsArgWith(2, null, [])}} + "../../infrastructure/Modules": @Modules = {hooks: {fire: sinon.stub().callsArgWith(2, null, [])}} @user = _id: ObjectId() email: @email = "USER@example.com" @@ -214,6 +214,7 @@ describe "AuthenticationController", -> beforeEach -> @AuthenticationController._recordFailedLogin = sinon.stub() @AuthenticationController._recordSuccessfulLogin = sinon.stub() + @Modules.hooks.fire = sinon.stub().callsArgWith(2, null, []) # @AuthenticationController.establishUserSession = sinon.stub().callsArg(2) @req.body = email: @email @@ -222,6 +223,17 @@ describe "AuthenticationController", -> postLoginRedirect: "/path/to/redir/to" @cb = sinon.stub() + describe "when the preDoPassportLogin hooks produce an info object", -> + beforeEach -> + @Modules.hooks.fire = sinon.stub().callsArgWith(2, null, [null, {redir: '/somewhere'}, null]) + + it "should stop early and call done with this info object", (done) -> + @AuthenticationController.doPassportLogin(@req, @req.body.email, @req.body.password, @cb) + @cb.callCount.should.equal 1 + @cb.calledWith(null, false, {redir: '/somewhere'}).should.equal true + @LoginRateLimiter.processLoginRequest.callCount.should.equal 0 + done() + describe "when the users rate limit", -> beforeEach -> From 7da8a926bbacdf6c04b0aca106fad640f18256fc Mon Sep 17 00:00:00 2001 From: Brian Gough Date: Mon, 6 Aug 2018 10:52:10 +0100 Subject: [PATCH 09/14] increase bodyparser limit to 64kb 16kb is not enough for bibtex files with more escaping. --- services/web/app/coffee/infrastructure/Server.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/web/app/coffee/infrastructure/Server.coffee b/services/web/app/coffee/infrastructure/Server.coffee index cf2757c80e..bf9d6652f4 100644 --- a/services/web/app/coffee/infrastructure/Server.coffee +++ b/services/web/app/coffee/infrastructure/Server.coffee @@ -68,7 +68,7 @@ Modules.loadViewIncludes app app.use bodyParser.urlencoded({ extended: true, limit: "2mb"}) # Make sure we can process the max doc length plus some overhead for JSON encoding -app.use bodyParser.json({limit: Settings.max_doc_length + 16 * 1024}) # 16kb overhead +app.use bodyParser.json({limit: Settings.max_doc_length + 64 * 1024}) # 64kb overhead app.use multer(dest: Settings.path.uploadFolder) app.use methodOverride() From efcd3577ce758c7705b069931d3ba9949ad0e40e Mon Sep 17 00:00:00 2001 From: Brian Gough Date: Mon, 6 Aug 2018 16:56:44 +0100 Subject: [PATCH 10/14] avoid clobbering imported image names --- .../Features/Project/ProjectCreationHandler.coffee | 3 ++- .../coffee/Project/ProjectCreationHandlerTests.coffee | 10 +++++++++- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/services/web/app/coffee/Features/Project/ProjectCreationHandler.coffee b/services/web/app/coffee/Features/Project/ProjectCreationHandler.coffee index 529164a49e..f83d6e7d21 100644 --- a/services/web/app/coffee/Features/Project/ProjectCreationHandler.coffee +++ b/services/web/app/coffee/Features/Project/ProjectCreationHandler.coffee @@ -55,7 +55,8 @@ module.exports = ProjectCreationHandler = if Settings.apis?.project_history?.displayHistoryForNewProjects project.overleaf.history.display = true if Settings.currentImageName? - project.imageName = Settings.currentImageName + # avoid clobbering any imageName already set in attributes (e.g. importedImageName) + project.imageName ?= Settings.currentImageName project.rootFolder[0] = rootFolder User.findById owner_id, "ace.spellCheckLanguage", (err, user)-> project.spellCheckLanguage = user.ace.spellCheckLanguage diff --git a/services/web/test/unit/coffee/Project/ProjectCreationHandlerTests.coffee b/services/web/test/unit/coffee/Project/ProjectCreationHandlerTests.coffee index 6d86545622..15499e6f71 100644 --- a/services/web/test/unit/coffee/Project/ProjectCreationHandlerTests.coffee +++ b/services/web/test/unit/coffee/Project/ProjectCreationHandlerTests.coffee @@ -111,7 +111,7 @@ describe 'ProjectCreationHandler', -> project.spellCheckLanguage.should.equal "de" done() - it "should set the imageName to currentImageName if set", (done) -> + it "should set the imageName to currentImageName if set and no imageName attribute", (done) -> @Settings.currentImageName = "mock-image-name" @handler.createBlankProject ownerId, projectName, (err, project)=> project.imageName.should.equal @Settings.currentImageName @@ -123,6 +123,14 @@ describe 'ProjectCreationHandler', -> expect(project.imageName).to.not.exist done() + it "should set the imageName to the attribute value if set and not overwrite it with the currentImageName", (done) -> + @Settings.currentImageName = "mock-image-name" + attributes = + imageName: "attribute-image-name" + @handler.createBlankProject ownerId, projectName, attributes, (err, project)=> + project.imageName.should.equal attributes.imageName + done() + it "should not set the overleaf.history.display if not configured in settings", (done) -> @Settings.apis.project_history.displayHistoryForNewProjects = false @handler.createBlankProject ownerId, projectName, (err, project)=> From c26a85c02c40df5e28b879aabae14637ed79ce9f Mon Sep 17 00:00:00 2001 From: Nate Stemen Date: Tue, 7 Aug 2018 09:49:50 -0400 Subject: [PATCH 11/14] get rid of confusing regex --- .../aceEditor/auto-complete/AutoCompleteManager.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 e91c92f643..3be4369213 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 @@ -162,7 +162,7 @@ define [ cursorPosition = @editor.getCursorPosition() end = change.end {lineUpToCursor, commandFragment} = Helpers.getContext(@editor, end) - if lineUpToCursor.match(/.*((?![\\]).|^)%.*/) + if (i = lineUpToCursor.indexOf('%') > -1 and lineUpToCursor[i-1] != '\\') return lastCharIsBackslash = lineUpToCursor.slice(-1) == "\\" lastTwoChars = lineUpToCursor.slice(-2) From d6ab993519bff56a3c5082738e20aa9535fd269f Mon Sep 17 00:00:00 2001 From: Paulo Reis Date: Thu, 9 Aug 2018 12:00:53 +0100 Subject: [PATCH 12/14] Dont hide history entries while loading. --- services/web/app/views/project/editor/history/entriesListV2.pug | 1 - 1 file changed, 1 deletion(-) diff --git a/services/web/app/views/project/editor/history/entriesListV2.pug b/services/web/app/views/project/editor/history/entriesListV2.pug index e08de0effd..ca0c48b722 100644 --- a/services/web/app/views/project/editor/history/entriesListV2.pug +++ b/services/web/app/views/project/editor/history/entriesListV2.pug @@ -119,7 +119,6 @@ script(type="text/ng-template", id="historyEntriesListTpl") users="$ctrl.users" on-select="$ctrl.onEntrySelect({ selectedEntry: selectedEntry })" on-label-delete="$ctrl.onLabelDelete({ label: label })" - ng-show="!$ctrl.isLoading" ) .loading(ng-show="$ctrl.isLoading") i.fa.fa-spin.fa-refresh From 4f4ab579583f2c0b237a68e4dc5aed2fd4eefadc Mon Sep 17 00:00:00 2001 From: Paulo Reis Date: Thu, 9 Aug 2018 12:05:00 +0100 Subject: [PATCH 13/14] Use controller binding in the label tooltip. --- services/web/app/views/project/editor/history.pug | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/web/app/views/project/editor/history.pug b/services/web/app/views/project/editor/history.pug index 2f3f317561..fec7327d6f 100644 --- a/services/web/app/views/project/editor/history.pug +++ b/services/web/app/views/project/editor/history.pug @@ -92,7 +92,7 @@ script(type="text/ng-template", id="historyLabelTooltipTpl") i.fa.fa-tag |  {{ $ctrl.labelText }} p.history-label-tooltip-owner #{translate("history_label_created_by")} {{ $ctrl.labelOwnerName }} - time.history-label-tooltip-datetime {{ labelCreationDateTime | formatDate }} + time.history-label-tooltip-datetime {{ $ctrl.labelCreationDateTime | formatDate }} script(type="text/ng-template", id="historyV2DeleteLabelModalTemplate") From 64a66aafd5452ddd9728511423186d974678360b Mon Sep 17 00:00:00 2001 From: Paulo Reis Date: Thu, 9 Aug 2018 12:16:36 +0100 Subject: [PATCH 14/14] Protect against reading the project members list too soon. --- .../ide/history/controllers/HistoryListController.coffee | 8 ++++++-- .../history/controllers/HistoryV2ListController.coffee | 7 ++++++- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/services/web/public/coffee/ide/history/controllers/HistoryListController.coffee b/services/web/public/coffee/ide/history/controllers/HistoryListController.coffee index cfd5bb75c5..6c9890c6e5 100644 --- a/services/web/public/coffee/ide/history/controllers/HistoryListController.coffee +++ b/services/web/public/coffee/ide/history/controllers/HistoryListController.coffee @@ -6,13 +6,17 @@ define [ App.controller "HistoryListController", ["$scope", "$modal", "ide", ($scope, $modal, ide) -> $scope.hoveringOverListSelectors = false - projectUsers = $scope.project.members.concat $scope.project.owner + $scope.projectUsers = [] + + $scope.$watch "project.members", (newVal) -> + if newVal? + $scope.projectUsers = newVal.concat $scope.project.owner # This method (and maybe the one below) will be removed soon. User details data will be # injected into the history API responses, so we won't need to fetch user data from other # local data structures. _getUserById = (id) -> - _.find projectUsers, (user) -> + _.find $scope.projectUsers, (user) -> curUserId = user?._id or user?.id curUserId == id diff --git a/services/web/public/coffee/ide/history/controllers/HistoryV2ListController.coffee b/services/web/public/coffee/ide/history/controllers/HistoryV2ListController.coffee index f7c602733f..1635ec9f0a 100644 --- a/services/web/public/coffee/ide/history/controllers/HistoryV2ListController.coffee +++ b/services/web/public/coffee/ide/history/controllers/HistoryV2ListController.coffee @@ -7,8 +7,13 @@ define [ $scope.hoveringOverListSelectors = false $scope.listConfig = showOnlyLabelled: false - $scope.projectUsers = $scope.project.members.concat $scope.project.owner + $scope.projectUsers = [] + + $scope.$watch "project.members", (newVal) -> + if newVal? + $scope.projectUsers = newVal.concat $scope.project.owner + $scope.loadMore = () => ide.historyManager.fetchNextBatchOfUpdates()