mirror of
https://github.com/overleaf/overleaf.git
synced 2024-11-21 20:47:08 -05:00
Merge branch 'master' into hb-fetch-licences-graph
This commit is contained in:
commit
29253c5a93
11 changed files with 114 additions and 56 deletions
|
@ -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) ->
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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(
|
||||||
|
|
|
@ -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")}
|
||||||
|
|
|
@ -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'"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -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,8 +391,11 @@ 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()
|
||||||
|
timestamp24hoursAgo = dateTimeNow.setDate(dateTimeNow.getDate() - 1)
|
||||||
|
cutOffIndex = null
|
||||||
|
|
||||||
for update in updates or []
|
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)
|
||||||
|
@ -402,8 +409,16 @@ define [
|
||||||
|
|
||||||
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)
|
||||||
|
|
||||||
|
|
|
@ -30,6 +30,8 @@ define [
|
||||||
loadInitialize: "<"
|
loadInitialize: "<"
|
||||||
isLoading: "<"
|
isLoading: "<"
|
||||||
currentUser: "<"
|
currentUser: "<"
|
||||||
|
freeHistoryLimitHit: "<"
|
||||||
|
currentUserIsOwner: "<"
|
||||||
onEntrySelect: "&"
|
onEntrySelect: "&"
|
||||||
onLabelDelete: "&"
|
onLabelDelete: "&"
|
||||||
controller: historyEntriesListController
|
controller: historyEntriesListController
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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 =
|
||||||
|
@ -41,6 +41,14 @@ describe "InstitutionsAPI", ->
|
||||||
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)->
|
||||||
@institutionId = 123
|
@institutionId = 123
|
||||||
|
@ -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 })
|
||||||
|
|
|
@ -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([{
|
||||||
|
|
Loading…
Reference in a new issue