diff --git a/services/web/app/coffee/Features/Analytics/AnalyticsController.coffee b/services/web/app/coffee/Features/Analytics/AnalyticsController.coffee index 38029219ee..e407feb488 100644 --- a/services/web/app/coffee/Features/Analytics/AnalyticsController.coffee +++ b/services/web/app/coffee/Features/Analytics/AnalyticsController.coffee @@ -1,6 +1,7 @@ AnalyticsManager = require "./AnalyticsManager" Errors = require "../Errors/Errors" AuthenticationController = require("../Authentication/AuthenticationController") +InstitutionsAPI = require("../Institutions/InstitutionsAPI") GeoIpLookup = require '../../infrastructure/GeoIpLookup' module.exports = AnalyticsController = @@ -23,6 +24,14 @@ module.exports = AnalyticsController = AnalyticsManager.recordEvent user_id, req.params.event, req.body, (error) -> respondWith(error, res, next) + licences: (req, res, next) -> + {resource_id, start_date, end_date, lag} = req.query + InstitutionsAPI.getInstitutionLicences resource_id, start_date, end_date, lag, (error, licences) -> + if error? + next(error) + else + res.send licences + respondWith = (error, res, next) -> if error instanceof Errors.ServiceNotConfiguredError # ignore, no-op diff --git a/services/web/app/coffee/Features/Analytics/AnalyticsRouter.coffee b/services/web/app/coffee/Features/Analytics/AnalyticsRouter.coffee index 06ca2bfa1c..57b131326f 100644 --- a/services/web/app/coffee/Features/Analytics/AnalyticsRouter.coffee +++ b/services/web/app/coffee/Features/Analytics/AnalyticsRouter.coffee @@ -23,4 +23,4 @@ module.exports = publicApiRouter.use '/analytics/uniExternalCollaboration', AuthenticationController.httpAuth, - AnalyticsProxy.call('/uniExternalCollaboration') \ No newline at end of file + AnalyticsProxy.call('/uniExternalCollaboration') diff --git a/services/web/app/coffee/Features/Institutions/InstitutionsAPI.coffee b/services/web/app/coffee/Features/Institutions/InstitutionsAPI.coffee index c62a8a01bc..dfc04e63b1 100644 --- a/services/web/app/coffee/Features/Institutions/InstitutionsAPI.coffee +++ b/services/web/app/coffee/Features/Institutions/InstitutionsAPI.coffee @@ -9,15 +9,22 @@ module.exports = InstitutionsAPI = method: 'GET' path: "/api/v2/institutions/#{institutionId.toString()}/affiliations" defaultErrorMessage: "Couldn't get institution affiliations" - }, callback + }, (error, body) -> callback(error, body or []) + getInstitutionLicences: (institutionId, startDate, endDate, lag, callback = (error, body) ->) -> + makeAffiliationRequest { + method: 'GET' + path: "/api/v2/institutions/#{institutionId.toString()}/institution_licences" + body: {start_date: startDate, end_date: endDate, lag} + defaultErrorMessage: "Couldn't get institution licences" + }, callback getUserAffiliations: (userId, callback = (error, body) ->) -> makeAffiliationRequest { method: 'GET' path: "/api/v2/users/#{userId.toString()}/affiliations" defaultErrorMessage: "Couldn't get user affiliations" - }, callback + }, (error, body) -> callback(error, body or []) addAffiliation: (userId, email, affiliationOptions, callback) -> @@ -25,11 +32,11 @@ module.exports = InstitutionsAPI = callback = affiliationOptions affiliationOptions = {} - { university, department, role } = affiliationOptions + { university, department, role, confirmedAt } = affiliationOptions makeAffiliationRequest { method: 'POST' path: "/api/v2/users/#{userId.toString()}/affiliations" - body: { email, university, department, role } + body: { email, university, department, role, confirmedAt } defaultErrorMessage: "Couldn't create affiliation" }, callback diff --git a/services/web/app/coffee/Features/User/UserUpdater.coffee b/services/web/app/coffee/Features/User/UserUpdater.coffee index 9ee81c1ca3..3abffdf444 100644 --- a/services/web/app/coffee/Features/User/UserUpdater.coffee +++ b/services/web/app/coffee/Features/User/UserUpdater.coffee @@ -166,7 +166,7 @@ module.exports = UserUpdater = email = EmailHelper.parseEmail(email) return callback(new Error('invalid email')) if !email? logger.log {userId, email}, 'confirming user email' - addAffiliation userId, email, (error) => + addAffiliation userId, email, {confirmedAt: confirmedAt}, (error) => if error? logger.err error: error, 'problem adding affiliation while confirming email' return callback(error) diff --git a/services/web/app/views/_mixins/faq_search.pug b/services/web/app/views/_mixins/faq_search.pug new file mode 100644 index 0000000000..b5d00f9832 --- /dev/null +++ b/services/web/app/views/_mixins/faq_search.pug @@ -0,0 +1,21 @@ +mixin faq_search(headerText, headerClass) + - if(typeof(settings.algolia) != "undefined" && typeof(settings.algolia.indexes) != "undefined" && typeof(settings.algolia.indexes.wiki) != "undefined") + if headerText + div(class=headerClass) #{headerText} + .wiki(ng-controller="SearchWikiController") + form.project-search.form-horizontal(role="form") + .form-group.has-feedback.has-feedback-left + .col-sm-12 + input.form-control(type='text', ng-model='searchQueryText', ng-keyup='search()', placeholder="Search help library....") + i.fa.fa-search.form-control-feedback-left + i.fa.fa-times.form-control-feedback( + ng-click="clearSearchText()", + style="cursor: pointer;", + ng-show="searchQueryText.length > 0" + ) + + .row + .col-md-12(ng-cloak) + a(ng-href='{{hit.url}}',ng-repeat='hit in hits').search-result.card.card-thin + span(ng-bind-html='hit.name') + div.search-result-content(ng-show="hit.content != ''", ng-bind-html='hit.content') diff --git a/services/web/app/views/_mixins_links.pug b/services/web/app/views/_mixins/links.pug similarity index 100% rename from services/web/app/views/_mixins_links.pug rename to services/web/app/views/_mixins/links.pug diff --git a/services/web/app/views/mixins/_pagination.pug b/services/web/app/views/mixins/_pagination.pug new file mode 100644 index 0000000000..4ac2d924e8 --- /dev/null +++ b/services/web/app/views/mixins/_pagination.pug @@ -0,0 +1,74 @@ +mixin paginate(pages, page_path, max_btns) + //- @param pages.current_page the current page viewed + //- @param pages.total_pages previously calculated, + //- based on total entries and entries per page + //- @param page_path the relative path, minus a trailing slash and page param + //- @param max_btns max number of buttons on either side of the current page + //- button and excludes first, prev, next, last + + if pages && pages.current_page && pages.total_pages + - var max_btns = max_btns || 4 + - var prev_page = Math.max(parseInt(pages.current_page, 10) - max_btns, 1) + - var next_page = parseInt(pages.current_page, 10) + 1 + - var next_index = 0; + - var full_page_path = page_path + "/page/" + + nav(role="navigation" aria-label="Pagination Navigation") + ul.pagination + if pages.current_page > 1 + li + a( + aria-label="Go to first page" + href=page_path + ) « First + li + a( + aria-label="Go to previous page" + href=full_page_path + (parseInt(pages.current_page, 10) - 1) + rel="prev" + ) ‹ Prev + + if pages.current_page - max_btns > 1 + li + span … + + while prev_page < pages.current_page + li + a( + aria-label="Go to page " + prev_page + href=full_page_path + prev_page + ) #{prev_page} + - prev_page++ + + li(class="active") + span( + aria-label="Current Page, Page " + pages.current_page + aria-current="true" + ) #{pages.current_page} + + if pages.current_page < pages.total_pages + while next_page <= pages.total_pages && next_index < max_btns + li + a( + aria-label="Go to page " + next_page + href=full_page_path + next_page + ) #{next_page} + - next_page++ + - next_index++ + + if next_page <= pages.total_pages + li + span … + + li + a( + aria-label="Go to next page" + href=full_page_path + (parseInt(pages.current_page, 10) + 1) + rel="next" + ) Next › + + li + a( + aria-label="Go to last page" + href=full_page_path + pages.total_pages + ) Last » diff --git a/services/web/app/views/project/editor/history.pug b/services/web/app/views/project/editor/history.pug index 35de68957f..f968639cca 100644 --- a/services/web/app/views/project/editor/history.pug +++ b/services/web/app/views/project/editor/history.pug @@ -1,45 +1,4 @@ div#history(ng-show="ui.view == 'history'") - span - .upgrade-prompt(ng-if="project.features.versioning === false && ui.view === 'history'") - .message(ng-if="project.owner._id == user.id") - p.text-center: strong #{translate("upgrade_to_get_feature", {feature:"full Project History"})} - p.text-center.small(ng-show="startedFreeTrial") #{translate("refresh_page_after_starting_free_trial")} - ul.list-unstyled - li - i.fa.fa-check   - | #{translate("unlimited_projects")} - - li - i.fa.fa-check   - | #{translate("collabs_per_proj", {collabcount:'Multiple'})} - - li - i.fa.fa-check   - | #{translate("full_doc_history")} - - li - i.fa.fa-check   - | #{translate("sync_to_dropbox")} - - li - i.fa.fa-check   - | #{translate("sync_to_github")} - - li - i.fa.fa-check   - |#{translate("compile_larger_projects")} - p.text-center(ng-controller="FreeTrialModalController") - a.btn.btn-success( - href - ng-class="buttonClass" - ng-click="startFreeTrial('history')" - ) #{translate("start_free_trial")} - - .message(ng-show="project.owner._id != user.id") - p #{translate("ask_proj_owner_to_upgrade_for_history")} - p - a.small(href, ng-click="toggleHistory()") #{translate("cancel")} - include ./history/entriesListV1 include ./history/entriesListV2 diff --git a/services/web/app/views/project/editor/history/entriesListV2.pug b/services/web/app/views/project/editor/history/entriesListV2.pug index 5e4c4b66c6..640a761296 100644 --- a/services/web/app/views/project/editor/history/entriesListV2.pug +++ b/services/web/app/views/project/editor/history/entriesListV2.pug @@ -6,11 +6,13 @@ aside.change-list( ng-if="!history.showOnlyLabels && !history.error" entries="history.updates" current-user="user" + current-user-is-owner="project.owner._id === user.id" users="projectUsers" load-entries="loadMore()" load-disabled="history.loading || history.atEnd" load-initialize="ui.view == 'history'" is-loading="history.loading" + free-history-limit-hit="history.freeHistoryLimitHit" on-entry-select="handleEntrySelect(selectedEntry)" on-label-delete="handleLabelDelete(label)" ) @@ -134,6 +136,48 @@ script(type="text/ng-template", id="historyEntriesListTpl") .loading(ng-show="$ctrl.isLoading") i.fa.fa-spin.fa-refresh |    #{translate("loading")}... + .history-entries-list-upgrade-prompt( + ng-if="$ctrl.freeHistoryLimitHit && $ctrl.currentUserIsOwner" + ng-controller="FreeTrialModalController" + ) + p #{translate("currently_seeing_only_24_hrs_history")} + p: strong #{translate("upgrade_to_get_feature", {feature:"full Project History"})} + ul.list-unstyled + li + i.fa.fa-check   + | #{translate("unlimited_projects")} + + li + i.fa.fa-check   + | #{translate("collabs_per_proj", {collabcount:'Multiple'})} + + li + i.fa.fa-check   + | #{translate("full_doc_history")} + + li + i.fa.fa-check   + | #{translate("sync_to_dropbox")} + + li + i.fa.fa-check   + | #{translate("sync_to_github")} + + li + i.fa.fa-check   + |#{translate("compile_larger_projects")} + p.text-center + a.btn.btn-success( + href + ng-class="buttonClass" + ng-click="startFreeTrial('history')" + ) #{translate("start_free_trial")} + p.small(ng-show="startedFreeTrial") #{translate("refresh_page_after_starting_free_trial")} + .history-entries-list-upgrade-prompt( + ng-if="$ctrl.freeHistoryLimitHit && !$ctrl.currentUserIsOwner" + ) + p #{translate("currently_seeing_only_24_hrs_history")} + strong #{translate("ask_proj_owner_to_upgrade_for_full_history")} script(type="text/ng-template", id="historyEntryTpl") .history-entry( diff --git a/services/web/app/views/project/editor/history/toolbarV2.pug b/services/web/app/views/project/editor/history/toolbarV2.pug index a683facba4..2acb93854b 100644 --- a/services/web/app/views/project/editor/history/toolbarV2.pug +++ b/services/web/app/views/project/editor/history/toolbarV2.pug @@ -6,7 +6,7 @@ i.fa.fa-spin.fa-refresh |    #{translate("loading")}... span.history-toolbar-selected-version( - ng-show="!history.loadingFileTree && !history.showOnlyLabels && !history.error" + ng-show="!history.loadingFileTree && !history.showOnlyLabels && history.selection.updates.length && !history.error" ) #{translate("browsing_project_as_of")}  time.history-toolbar-time {{ history.selection.updates[0].meta.end_ts | formatDate:'Do MMM YYYY, h:mm a' }} span.history-toolbar-selected-version( @@ -19,13 +19,13 @@ button.history-toolbar-btn( ng-click="showAddLabelDialog();" ng-if="!history.showOnlyLabels" - ng-disabled="history.loadingFileTree" + ng-disabled="history.loadingFileTree || history.selection.updates.length == 0" ) i.fa.fa-tag |  #{translate("history_label_this_version")} button.history-toolbar-btn( ng-click="toggleHistoryViewMode();" - ng-disabled="history.loadingFileTree" + ng-disabled="history.loadingFileTree || history.selection.updates.length == 0" ) i.fa.fa-exchange |  #{translate("compare_to_another_version")} diff --git a/services/web/app/views/project/editor/pdf.pug b/services/web/app/views/project/editor/pdf.pug index 4a9b101f60..44410a44a8 100644 --- a/services/web/app/views/project/editor/pdf.pug +++ b/services/web/app/views/project/editor/pdf.pug @@ -243,7 +243,7 @@ div.full-size.pdf(ng-controller="PdfController") dbl-click-callback="syncToCode" ) iframe( - ng-src="{{ pdf.url }}" + ng-src="{{ pdf.url | trusted }}" ng-if="settings.pdfViewer == 'native'" ) diff --git a/services/web/public/coffee/ide/history/HistoryV2Manager.coffee b/services/web/public/coffee/ide/history/HistoryV2Manager.coffee index 3f231c046b..66b4375c8e 100644 --- a/services/web/public/coffee/ide/history/HistoryV2Manager.coffee +++ b/services/web/public/coffee/ide/history/HistoryV2Manager.coffee @@ -82,6 +82,8 @@ define [ viewMode: null nextBeforeTimestamp: null atEnd: false + userHasFullFeature: @$scope.project?.features?.versioning or false + freeHistoryLimitHit: false selection: { label: null updates: [] @@ -232,9 +234,11 @@ define [ @$scope.history.labels = @_sortLabelsByVersionAndDate response.labels.data @_loadUpdates(updatesData.updates) @$scope.history.nextBeforeTimestamp = updatesData.nextBeforeTimestamp - if !updatesData.nextBeforeTimestamp? + if !updatesData.nextBeforeTimestamp? or @$scope.history.freeHistoryLimitHit @$scope.history.atEnd = true @$scope.history.loading = false + if @$scope.history.updates.length == 0 + @$scope.history.loadingFileTree = false .catch (error) => { status, statusText } = error @$scope.history.error = { status, statusText } @@ -387,23 +391,34 @@ define [ _loadUpdates: (updates = []) -> previousUpdate = @$scope.history.updates[@$scope.history.updates.length - 1] - - for update in updates or [] + dateTimeNow = new Date() + timestamp24hoursAgo = dateTimeNow.setDate(dateTimeNow.getDate() - 1) + cutOffIndex = null + + for update, i in updates or [] for user in update.meta.users or [] if user? user.hue = ColorManager.getHueForUserId(user.id) if !previousUpdate? or !moment(previousUpdate.meta.end_ts).isSame(update.meta.end_ts, "day") update.meta.first_in_day = true - + update.selectedFrom = false update.selectedTo = false update.inSelection = false previousUpdate = update + if !@$scope.history.userHasFullFeature and update.meta.end_ts < timestamp24hoursAgo + cutOffIndex = i or 1 # Make sure that we show at least one entry (to allow labelling). + @$scope.history.freeHistoryLimitHit = true + break + firstLoad = @$scope.history.updates.length == 0 + if !@$scope.history.userHasFullFeature and cutOffIndex? + updates = updates.slice 0, cutOffIndex + @$scope.history.updates = @$scope.history.updates.concat(updates) diff --git a/services/web/public/coffee/ide/history/components/historyEntriesList.coffee b/services/web/public/coffee/ide/history/components/historyEntriesList.coffee index de4e4f1b92..7e97121868 100644 --- a/services/web/public/coffee/ide/history/components/historyEntriesList.coffee +++ b/services/web/public/coffee/ide/history/components/historyEntriesList.coffee @@ -30,6 +30,8 @@ define [ loadInitialize: "<" isLoading: "<" currentUser: "<" + freeHistoryLimitHit: "<" + currentUserIsOwner: "<" onEntrySelect: "&" onLabelDelete: "&" controller: historyEntriesListController diff --git a/services/web/public/coffee/ide/pdf/controllers/PdfController.coffee b/services/web/public/coffee/ide/pdf/controllers/PdfController.coffee index 1d8aabe842..c0359b1190 100644 --- a/services/web/public/coffee/ide/pdf/controllers/PdfController.coffee +++ b/services/web/public/coffee/ide/pdf/controllers/PdfController.coffee @@ -11,6 +11,10 @@ define [ # and then again on ack. AUTO_COMPILE_DEBOUNCE = 2000 + App.filter('trusted', ['$sce', ($sce)-> + return (url)-> return $sce.trustAsResourceUrl(url); + ]) + App.controller "PdfController", ($scope, $http, ide, $modal, synctex, event_tracking, localStorage) -> # enable per-user containers by default perUserCompile = true @@ -212,7 +216,7 @@ define [ buildPdfDownloadUrl = (pdfDownloadDomain, path)-> #we only download builds from compiles server for security reasons - if pdfDownloadDomain? and path.indexOf("build") != -1 + if pdfDownloadDomain? and path? and path.indexOf("build") != -1 return "#{pdfDownloadDomain}#{path}" else return path diff --git a/services/web/public/stylesheets/app/editor/history-v2.less b/services/web/public/stylesheets/app/editor/history-v2.less index cfefbb462c..35190cd662 100644 --- a/services/web/public/stylesheets/app/editor/history-v2.less +++ b/services/web/public/stylesheets/app/editor/history-v2.less @@ -19,6 +19,7 @@ white-space: nowrap; overflow: hidden; text-overflow: ellipsis; + margin-right: (@line-height-computed / 2); } .history-toolbar-time, .history-toolbar-selected-label { @@ -33,7 +34,7 @@ .btn-xs; padding-left: @padding-small-horizontal; padding-right: @padding-small-horizontal; - margin-left: (@line-height-computed / 2); + margin-right: (@line-height-computed / 2); } .history-toolbar-entries-list { flex: 0 0 @changesListWidth; @@ -173,6 +174,12 @@ } } +.history-entries-list-upgrade-prompt { + background-color: #FFF; + margin-bottom: 2px; + padding: 5px 10px; +} + .history-labels-list { .history-entries; overflow-y: auto; diff --git a/services/web/public/stylesheets/core/ol-variables.less b/services/web/public/stylesheets/core/ol-variables.less index e6a20c6ad8..7c22b1f01c 100644 --- a/services/web/public/stylesheets/core/ol-variables.less +++ b/services/web/public/stylesheets/core/ol-variables.less @@ -277,6 +277,21 @@ @chat-new-message-textarea-bg : @ol-blue-gray-1; @chat-new-message-textarea-color : @ol-blue-gray-6; +// Pagination +@pagination-active-bg : @ol-dark-green; +@pagination-active-border : @gray-lighter; +@pagination-active-color : #FFF; +@pagination-bg : #FFF; +@pagination-border : @gray-lighter; +@pagination-color : @ol-dark-green; +@pagination-disabled-color : @gray-dark; +@pagination-disabled-bg : @gray-lightest; +@pagination-disabled-border : @gray-lighter; +@pagination-hover-color : @ol-dark-green; +@pagination-hover-bg : @gray-lightest; +@pagination-hover-border : @gray-lighter; + + // PDF @pdf-top-offset : @toolbar-small-height; @pdf-bg : @ol-blue-gray-1; diff --git a/services/web/public/stylesheets/ol-style.less b/services/web/public/stylesheets/ol-style.less index 774e70a2ab..2be148a497 100644 --- a/services/web/public/stylesheets/ol-style.less +++ b/services/web/public/stylesheets/ol-style.less @@ -8,6 +8,7 @@ @import "_ol_style_includes.less"; @import "components/embed-responsive.less"; @import "components/icons.less"; +@import "components/pagination.less"; // Pages @import "app/about.less"; diff --git a/services/web/test/unit/coffee/Analytics/AnalyticsControllerTests.coffee b/services/web/test/unit/coffee/Analytics/AnalyticsControllerTests.coffee index c3e3802f37..0aa0517074 100644 --- a/services/web/test/unit/coffee/Analytics/AnalyticsControllerTests.coffee +++ b/services/web/test/unit/coffee/Analytics/AnalyticsControllerTests.coffee @@ -17,9 +17,13 @@ describe 'AnalyticsController', -> updateEditingSession: sinon.stub().callsArgWith(3) recordEvent: sinon.stub().callsArgWith(3) + @InstitutionsAPI = + getInstitutionLicences: sinon.stub().callsArgWith(4) + @controller = SandboxedModule.require modulePath, requires: "./AnalyticsManager":@AnalyticsManager "../Authentication/AuthenticationController":@AuthenticationController + "../Institutions/InstitutionsAPI":@InstitutionsAPI "logger-sharelatex": log:-> '../../infrastructure/GeoIpLookup': @GeoIpLookup = @@ -66,3 +70,19 @@ describe 'AnalyticsController', -> @controller.recordEvent @req, @res @AnalyticsManager.recordEvent.calledWith(@req.sessionID, @req.params["event"], @req.body).should.equal true done() + + describe "licences", -> + beforeEach -> + @req = + query: + resource_id:1 + start_date:'1514764800' + end_date:'1530662400' + resource_type:'institution' + sessionID: "sessionIDHere" + session: {} + + it "should trigger institutions api to fetch licences graph data", (done)-> + @controller.licences @req, @res + @InstitutionsAPI.getInstitutionLicences.calledWith(@req.query["resource_id"], @req.query["start_date"], @req.query["end_date"], @req.query["lag"]).should.equal true + done() diff --git a/services/web/test/unit/coffee/Institutions/InstitutionsAPITests.coffee b/services/web/test/unit/coffee/Institutions/InstitutionsAPITests.coffee index 7500e46e40..0c4e7ae445 100644 --- a/services/web/test/unit/coffee/Institutions/InstitutionsAPITests.coffee +++ b/services/web/test/unit/coffee/Institutions/InstitutionsAPITests.coffee @@ -11,12 +11,12 @@ describe "InstitutionsAPI", -> beforeEach -> @logger = err: sinon.stub(), log: -> - settings = apis: { v1: { url: 'v1.url', user: '', pass: '' } } + @settings = apis: { v1: { url: 'v1.url', user: '', pass: '' } } @request = sinon.stub() @InstitutionsAPI = SandboxedModule.require modulePath, requires: "logger-sharelatex": @logger "metrics-sharelatex": timeAsyncMethod: sinon.stub() - 'settings-sharelatex': settings + 'settings-sharelatex': @settings 'request': @request @stubbedUser = @@ -40,6 +40,34 @@ describe "InstitutionsAPI", -> should.not.exist(requestOptions.body) body.should.equal responseBody done() + + it 'handle empty response', (done)-> + @settings.apis = null + @InstitutionsAPI.getInstitutionAffiliations @institutionId, (err, body) => + should.not.exist(err) + expect(body).to.be.a 'Array' + body.length.should.equal 0 + done() + + describe 'getInstitutionLicences', -> + it 'get licences', (done)-> + @institutionId = 123 + responseBody = {"lag":"monthly","data":[{"key":"users","values":[{"x":"2018-01-01","y":1}]}]} + @request.yields(null, { statusCode: 200 }, responseBody) + startDate = '1417392000' + endDate = '1420848000' + @InstitutionsAPI.getInstitutionLicences @institutionId, startDate, endDate, 'monthly', (err, body) => + should.not.exist(err) + @request.calledOnce.should.equal true + requestOptions = @request.lastCall.args[0] + expectedUrl = "v1.url/api/v2/institutions/#{@institutionId}/institution_licences" + requestOptions.url.should.equal expectedUrl + requestOptions.method.should.equal 'GET' + requestOptions.body['start_date'].should.equal startDate + requestOptions.body['end_date'].should.equal endDate + requestOptions.body.lag.should.equal 'monthly' + body.should.equal responseBody + done() describe 'getUserAffiliations', -> it 'get affiliations', (done)-> @@ -65,6 +93,14 @@ describe "InstitutionsAPI", -> err.message.should.have.string body.errors done() + it 'handle empty response', (done)-> + @settings.apis = null + @InstitutionsAPI.getUserAffiliations @stubbedUser._id, (err, body) => + should.not.exist(err) + expect(body).to.be.a 'Array' + body.length.should.equal 0 + done() + describe 'addAffiliation', -> beforeEach -> @request.callsArgWith(1, null, { statusCode: 201 }) @@ -74,6 +110,7 @@ describe "InstitutionsAPI", -> university: { id: 1 } role: 'Prof' department: 'Math' + confirmedAt: new Date() @InstitutionsAPI.addAffiliation @stubbedUser._id, @newEmail, affiliationOptions, (err)=> should.not.exist(err) @request.calledOnce.should.equal true @@ -83,11 +120,12 @@ describe "InstitutionsAPI", -> requestOptions.method.should.equal 'POST' body = requestOptions.body - Object.keys(body).length.should.equal 4 + Object.keys(body).length.should.equal 5 body.email.should.equal @newEmail body.university.should.equal affiliationOptions.university body.department.should.equal affiliationOptions.department body.role.should.equal affiliationOptions.role + body.confirmedAt.should.equal affiliationOptions.confirmedAt done() it 'handle error', (done)-> diff --git a/services/web/test/unit/coffee/User/UserUpdaterTests.coffee b/services/web/test/unit/coffee/User/UserUpdaterTests.coffee index 17f691edba..3be7733153 100644 --- a/services/web/test/unit/coffee/User/UserUpdaterTests.coffee +++ b/services/web/test/unit/coffee/User/UserUpdaterTests.coffee @@ -241,7 +241,7 @@ describe "UserUpdater", -> @UserUpdater.confirmEmail @stubbedUser._id, @newEmail, (err)=> should.not.exist(err) @addAffiliation.calledOnce.should.equal true - sinon.assert.calledWith(@addAffiliation, @stubbedUser._id, @newEmail) + sinon.assert.calledWith(@addAffiliation, @stubbedUser._id, @newEmail, { confirmedAt: new Date() } ) done() it 'handle error', (done)-> @@ -264,7 +264,7 @@ describe "UserUpdater", -> done() it 'handle affiliation error', (done)-> - @addAffiliation.callsArgWith(2, new Error('nope')) + @addAffiliation.callsArgWith(3, new Error('nope')) @UserUpdater.confirmEmail @stubbedUser._id, @newEmail, (err)=> should.exist(err) @UserUpdater.updateUser.called.should.equal false diff --git a/services/web/test/unit_frontend/coffee/ide/history/HistoryV2ManagerTests.coffee b/services/web/test/unit_frontend/coffee/ide/history/HistoryV2ManagerTests.coffee index 2fc5c5b3b6..542ee3e8eb 100644 --- a/services/web/test/unit_frontend/coffee/ide/history/HistoryV2ManagerTests.coffee +++ b/services/web/test/unit_frontend/coffee/ide/history/HistoryV2ManagerTests.coffee @@ -4,16 +4,21 @@ define ['ide/history/HistoryV2Manager'], (HistoryV2Manager) -> @scope = $watch: sinon.stub() $on: sinon.stub() + project: + features: + versioning: true @ide = {} @historyManager = new HistoryV2Manager(@ide, @scope) - it "should setup the history scope on intialization", -> + it "should setup the history scope on initialization", -> expect(@scope.history).to.deep.equal({ isV2: true updates: [] viewMode: null nextBeforeTimestamp: null atEnd: false + userHasFullFeature: true + freeHistoryLimitHit: false selection: { label: null updates: [] @@ -32,6 +37,12 @@ define ['ide/history/HistoryV2Manager'], (HistoryV2Manager) -> selectedFile: null }) + + it "should setup history without full access to the feature if the project does not have versioning", -> + @scope.project.features.versioning = false + @historyManager = new HistoryV2Manager(@ide, @scope) + expect(@scope.history.userHasFullFeature).to.equal false + describe "_perDocSummaryOfUpdates", -> it "should return the range of updates for the docs", -> result = @historyManager._perDocSummaryOfUpdates([{