Merge branch 'master' into hb-fetch-licences-graph

This commit is contained in:
Hugh O'Brien 2018-08-23 16:08:10 +01:00 committed by hugh-obrien
commit 29253c5a93
11 changed files with 114 additions and 56 deletions

View file

@ -9,7 +9,7 @@ module.exports = InstitutionsAPI =
method: 'GET' method: 'GET'
path: "/api/v2/institutions/#{institutionId.toString()}/affiliations" path: "/api/v2/institutions/#{institutionId.toString()}/affiliations"
defaultErrorMessage: "Couldn't get institution affiliations" defaultErrorMessage: "Couldn't get institution affiliations"
}, callback }, (error, body) -> callback(error, body or [])
getInstitutionLicences: (institutionId, startDate, endDate, lag, callback = (error, body) ->) -> getInstitutionLicences: (institutionId, startDate, endDate, lag, callback = (error, body) ->) ->
makeAffiliationRequest { makeAffiliationRequest {
@ -24,7 +24,7 @@ module.exports = InstitutionsAPI =
method: 'GET' method: 'GET'
path: "/api/v2/users/#{userId.toString()}/affiliations" path: "/api/v2/users/#{userId.toString()}/affiliations"
defaultErrorMessage: "Couldn't get user affiliations" defaultErrorMessage: "Couldn't get user affiliations"
}, callback }, (error, body) -> callback(error, body or [])
addAffiliation: (userId, email, affiliationOptions, callback) -> addAffiliation: (userId, email, affiliationOptions, callback) ->

View file

@ -1,45 +1,4 @@
div#history(ng-show="ui.view == 'history'") 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/entriesListV1
include ./history/entriesListV2 include ./history/entriesListV2

View file

@ -6,11 +6,13 @@ aside.change-list(
ng-if="!history.showOnlyLabels && !history.error" ng-if="!history.showOnlyLabels && !history.error"
entries="history.updates" entries="history.updates"
current-user="user" current-user="user"
current-user-is-owner="project.owner._id === user.id"
users="projectUsers" users="projectUsers"
load-entries="loadMore()" load-entries="loadMore()"
load-disabled="history.loading || history.atEnd" load-disabled="history.loading || history.atEnd"
load-initialize="ui.view == 'history'" load-initialize="ui.view == 'history'"
is-loading="history.loading" is-loading="history.loading"
free-history-limit-hit="history.freeHistoryLimitHit"
on-entry-select="handleEntrySelect(selectedEntry)" on-entry-select="handleEntrySelect(selectedEntry)"
on-label-delete="handleLabelDelete(label)" on-label-delete="handleLabelDelete(label)"
) )
@ -134,6 +136,48 @@ script(type="text/ng-template", id="historyEntriesListTpl")
.loading(ng-show="$ctrl.isLoading") .loading(ng-show="$ctrl.isLoading")
i.fa.fa-spin.fa-refresh i.fa.fa-spin.fa-refresh
|    #{translate("loading")}... |    #{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") script(type="text/ng-template", id="historyEntryTpl")
.history-entry( .history-entry(

View file

@ -6,7 +6,7 @@
i.fa.fa-spin.fa-refresh i.fa.fa-spin.fa-refresh
|    #{translate("loading")}... |    #{translate("loading")}...
span.history-toolbar-selected-version( 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")}  ) #{translate("browsing_project_as_of")} 
time.history-toolbar-time {{ history.selection.updates[0].meta.end_ts | formatDate:'Do MMM YYYY, h:mm a' }} time.history-toolbar-time {{ history.selection.updates[0].meta.end_ts | formatDate:'Do MMM YYYY, h:mm a' }}
span.history-toolbar-selected-version( span.history-toolbar-selected-version(
@ -19,13 +19,13 @@
button.history-toolbar-btn( button.history-toolbar-btn(
ng-click="showAddLabelDialog();" ng-click="showAddLabelDialog();"
ng-if="!history.showOnlyLabels" ng-if="!history.showOnlyLabels"
ng-disabled="history.loadingFileTree" ng-disabled="history.loadingFileTree || history.selection.updates.length == 0"
) )
i.fa.fa-tag i.fa.fa-tag
|  #{translate("history_label_this_version")} |  #{translate("history_label_this_version")}
button.history-toolbar-btn( button.history-toolbar-btn(
ng-click="toggleHistoryViewMode();" ng-click="toggleHistoryViewMode();"
ng-disabled="history.loadingFileTree" ng-disabled="history.loadingFileTree || history.selection.updates.length == 0"
) )
i.fa.fa-exchange i.fa.fa-exchange
|  #{translate("compare_to_another_version")} |  #{translate("compare_to_another_version")}

View file

@ -305,7 +305,7 @@ div.full-size.pdf(ng-controller="PdfController")
dbl-click-callback="syncToCode" dbl-click-callback="syncToCode"
) )
iframe( iframe(
ng-src="{{ pdf.url }}" ng-src="{{ pdf.url | trusted }}"
ng-if="settings.pdfViewer == 'native'" ng-if="settings.pdfViewer == 'native'"
) )

View file

@ -82,6 +82,8 @@ define [
viewMode: null viewMode: null
nextBeforeTimestamp: null nextBeforeTimestamp: null
atEnd: false atEnd: false
userHasFullFeature: @$scope.project?.features?.versioning or false
freeHistoryLimitHit: false
selection: { selection: {
label: null label: null
updates: [] updates: []
@ -232,9 +234,11 @@ define [
@$scope.history.labels = @_sortLabelsByVersionAndDate response.labels.data @$scope.history.labels = @_sortLabelsByVersionAndDate response.labels.data
@_loadUpdates(updatesData.updates) @_loadUpdates(updatesData.updates)
@$scope.history.nextBeforeTimestamp = updatesData.nextBeforeTimestamp @$scope.history.nextBeforeTimestamp = updatesData.nextBeforeTimestamp
if !updatesData.nextBeforeTimestamp? if !updatesData.nextBeforeTimestamp? or @$scope.history.freeHistoryLimitHit
@$scope.history.atEnd = true @$scope.history.atEnd = true
@$scope.history.loading = false @$scope.history.loading = false
if @$scope.history.updates.length == 0
@$scope.history.loadingFileTree = false
.catch (error) => .catch (error) =>
{ status, statusText } = error { status, statusText } = error
@$scope.history.error = { status, statusText } @$scope.history.error = { status, statusText }
@ -387,23 +391,34 @@ define [
_loadUpdates: (updates = []) -> _loadUpdates: (updates = []) ->
previousUpdate = @$scope.history.updates[@$scope.history.updates.length - 1] previousUpdate = @$scope.history.updates[@$scope.history.updates.length - 1]
dateTimeNow = new Date()
for update in updates or [] timestamp24hoursAgo = dateTimeNow.setDate(dateTimeNow.getDate() - 1)
cutOffIndex = null
for update, i in updates or []
for user in update.meta.users or [] for user in update.meta.users or []
if user? if user?
user.hue = ColorManager.getHueForUserId(user.id) user.hue = ColorManager.getHueForUserId(user.id)
if !previousUpdate? or !moment(previousUpdate.meta.end_ts).isSame(update.meta.end_ts, "day") if !previousUpdate? or !moment(previousUpdate.meta.end_ts).isSame(update.meta.end_ts, "day")
update.meta.first_in_day = true update.meta.first_in_day = true
update.selectedFrom = false update.selectedFrom = false
update.selectedTo = false update.selectedTo = false
update.inSelection = false update.inSelection = false
previousUpdate = update 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 firstLoad = @$scope.history.updates.length == 0
if !@$scope.history.userHasFullFeature and cutOffIndex?
updates = updates.slice 0, cutOffIndex
@$scope.history.updates = @$scope.history.updates =
@$scope.history.updates.concat(updates) @$scope.history.updates.concat(updates)

View file

@ -30,6 +30,8 @@ define [
loadInitialize: "<" loadInitialize: "<"
isLoading: "<" isLoading: "<"
currentUser: "<" currentUser: "<"
freeHistoryLimitHit: "<"
currentUserIsOwner: "<"
onEntrySelect: "&" onEntrySelect: "&"
onLabelDelete: "&" onLabelDelete: "&"
controller: historyEntriesListController controller: historyEntriesListController

View file

@ -12,6 +12,10 @@ define [
# and then again on ack. # and then again on ack.
AUTO_COMPILE_DEBOUNCE = 2000 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, logHintsFeedback, localStorage) -> App.controller "PdfController", ($scope, $http, ide, $modal, synctex, event_tracking, logHintsFeedback, localStorage) ->
# enable per-user containers by default # enable per-user containers by default
perUserCompile = true perUserCompile = true
@ -226,7 +230,7 @@ define [
buildPdfDownloadUrl = (pdfDownloadDomain, path)-> buildPdfDownloadUrl = (pdfDownloadDomain, path)->
#we only download builds from compiles server for security reasons #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}" return "#{pdfDownloadDomain}#{path}"
else else
return path return path

View file

@ -19,6 +19,7 @@
white-space: nowrap; white-space: nowrap;
overflow: hidden; overflow: hidden;
text-overflow: ellipsis; text-overflow: ellipsis;
margin-right: (@line-height-computed / 2);
} }
.history-toolbar-time, .history-toolbar-time,
.history-toolbar-selected-label { .history-toolbar-selected-label {
@ -33,7 +34,7 @@
.btn-xs; .btn-xs;
padding-left: @padding-small-horizontal; padding-left: @padding-small-horizontal;
padding-right: @padding-small-horizontal; padding-right: @padding-small-horizontal;
margin-left: (@line-height-computed / 2); margin-right: (@line-height-computed / 2);
} }
.history-toolbar-entries-list { .history-toolbar-entries-list {
flex: 0 0 @changesListWidth; 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-labels-list {
.history-entries; .history-entries;
overflow-y: auto; overflow-y: auto;

View file

@ -11,12 +11,12 @@ describe "InstitutionsAPI", ->
beforeEach -> beforeEach ->
@logger = err: sinon.stub(), log: -> @logger = err: sinon.stub(), log: ->
settings = apis: { v1: { url: 'v1.url', user: '', pass: '' } } @settings = apis: { v1: { url: 'v1.url', user: '', pass: '' } }
@request = sinon.stub() @request = sinon.stub()
@InstitutionsAPI = SandboxedModule.require modulePath, requires: @InstitutionsAPI = SandboxedModule.require modulePath, requires:
"logger-sharelatex": @logger "logger-sharelatex": @logger
"metrics-sharelatex": timeAsyncMethod: sinon.stub() "metrics-sharelatex": timeAsyncMethod: sinon.stub()
'settings-sharelatex': settings 'settings-sharelatex': @settings
'request': @request 'request': @request
@stubbedUser = @stubbedUser =
@ -40,6 +40,14 @@ describe "InstitutionsAPI", ->
should.not.exist(requestOptions.body) should.not.exist(requestOptions.body)
body.should.equal responseBody body.should.equal responseBody
done() 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', -> describe 'getInstitutionLicences', ->
it 'get licences', (done)-> it 'get licences', (done)->
@ -85,6 +93,14 @@ describe "InstitutionsAPI", ->
err.message.should.have.string body.errors err.message.should.have.string body.errors
done() 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', -> describe 'addAffiliation', ->
beforeEach -> beforeEach ->
@request.callsArgWith(1, null, { statusCode: 201 }) @request.callsArgWith(1, null, { statusCode: 201 })

View file

@ -4,16 +4,21 @@ define ['ide/history/HistoryV2Manager'], (HistoryV2Manager) ->
@scope = @scope =
$watch: sinon.stub() $watch: sinon.stub()
$on: sinon.stub() $on: sinon.stub()
project:
features:
versioning: true
@ide = {} @ide = {}
@historyManager = new HistoryV2Manager(@ide, @scope) @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({ expect(@scope.history).to.deep.equal({
isV2: true isV2: true
updates: [] updates: []
viewMode: null viewMode: null
nextBeforeTimestamp: null nextBeforeTimestamp: null
atEnd: false atEnd: false
userHasFullFeature: true
freeHistoryLimitHit: false
selection: { selection: {
label: null label: null
updates: [] updates: []
@ -32,6 +37,12 @@ define ['ide/history/HistoryV2Manager'], (HistoryV2Manager) ->
selectedFile: null 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", -> describe "_perDocSummaryOfUpdates", ->
it "should return the range of updates for the docs", -> it "should return the range of updates for the docs", ->
result = @historyManager._perDocSummaryOfUpdates([{ result = @historyManager._perDocSummaryOfUpdates([{