mirror of
https://github.com/overleaf/overleaf.git
synced 2025-01-23 21:21:44 +00:00
Merge branch 'master' of github.com:sharelatex/web-sharelatex
This commit is contained in:
commit
bafd3a1cb4
11 changed files with 112 additions and 134 deletions
|
@ -11,6 +11,7 @@ module.exports = (grunt) ->
|
|||
grunt.loadNpmTasks 'grunt-bunyan'
|
||||
grunt.loadNpmTasks 'grunt-sed'
|
||||
grunt.loadNpmTasks 'grunt-git-rev-parse'
|
||||
grunt.loadNpmTasks 'grunt-file-append'
|
||||
|
||||
config =
|
||||
execute:
|
||||
|
@ -129,6 +130,14 @@ module.exports = (grunt) ->
|
|||
options:
|
||||
prop: 'commit'
|
||||
|
||||
|
||||
file_append:
|
||||
default_options: files: [ {
|
||||
append: '\n//ide.js is complete - used for automated testing'
|
||||
input: 'public/minjs/ide.js'
|
||||
output: 'public/minjs/ide.js'
|
||||
}]
|
||||
|
||||
sed:
|
||||
version:
|
||||
path: "app/views/sentry.jade"
|
||||
|
@ -272,7 +281,7 @@ module.exports = (grunt) ->
|
|||
grunt.registerTask 'compile:server', 'Compile the server side coffee script', ['clean:app', 'coffee:app', 'coffee:app_dir', 'compile:modules:server']
|
||||
grunt.registerTask 'compile:client', 'Compile the client side coffee script', ['coffee:client', 'coffee:sharejs', 'wrap_sharejs', "compile:modules:client", 'compile:modules:inject_clientside_includes']
|
||||
grunt.registerTask 'compile:css', 'Compile the less files to css', ['less']
|
||||
grunt.registerTask 'compile:minify', 'Concat and minify the client side js', ['requirejs']
|
||||
grunt.registerTask 'compile:minify', 'Concat and minify the client side js', ['requirejs', "file_append"]
|
||||
grunt.registerTask 'compile:unit_tests', 'Compile the unit tests', ['clean:unit_tests', 'coffee:unit_tests']
|
||||
grunt.registerTask 'compile:smoke_tests', 'Compile the smoke tests', ['coffee:smoke_tests']
|
||||
grunt.registerTask 'compile:tests', 'Compile all the tests', ['compile:smoke_tests', 'compile:unit_tests']
|
||||
|
|
|
@ -32,46 +32,35 @@ module.exports = ReferalAllocator =
|
|||
|
||||
|
||||
assignBonus: (user_id, callback = (error) ->) ->
|
||||
SubscriptionLocator.getUsersSubscription user_id, (error, subscription) ->
|
||||
return callback(error) if error?
|
||||
logger.log
|
||||
subscription: subscription,
|
||||
user_id: user_id,
|
||||
"checking user doesn't have a subsciption before assigning bonus"
|
||||
if !subscription? or !subscription.planCode?
|
||||
query = _id: user_id
|
||||
User.findOne query, (error, user) ->
|
||||
return callback(error) if error
|
||||
return callback(new Error("user not found")) if !user?
|
||||
logger.log
|
||||
user_id: user_id,
|
||||
refered_user_count: user.refered_user_count,
|
||||
"assigning bonus"
|
||||
logger.log user_id: user_id, refered_user_count: user.refered_user_count, "assigning bonus"
|
||||
if user.refered_user_count? and user.refered_user_count > 0
|
||||
newFeatures = ReferalAllocator._calculateBonuses(user)
|
||||
newFeatures = ReferalAllocator._calculateFeatures(user)
|
||||
if _.isEqual newFeatures, user.features
|
||||
return callback()
|
||||
User.update query, { $set: features: newFeatures }, callback
|
||||
|
||||
else
|
||||
callback()
|
||||
else
|
||||
callback()
|
||||
|
||||
_calculateBonuses : (user)->
|
||||
_calculateFeatures : (user)->
|
||||
bonusLevel = ReferalAllocator._getBonusLevel(user)
|
||||
|
||||
newFeatures = {}
|
||||
currentFeatures = _.clone(user.features) #need to clone because we exend with underscore later
|
||||
betterBonusFeatures = {}
|
||||
_.each Settings.bonus_features["#{bonusLevel}"], (bonusLevel, key)->
|
||||
currentLevel = user?.features?[key]
|
||||
if _.isBoolean(currentLevel) and currentLevel == false
|
||||
newFeatures[key] = bonusLevel
|
||||
betterBonusFeatures[key] = bonusLevel
|
||||
|
||||
if _.isNumber(currentLevel)
|
||||
if currentLevel == -1
|
||||
return
|
||||
bonusIsGreaterThanCurrent = currentLevel < bonusLevel
|
||||
if bonusIsGreaterThanCurrent or bonusLevel == -1
|
||||
newFeatures[key] = bonusLevel
|
||||
|
||||
betterBonusFeatures[key] = bonusLevel
|
||||
newFeatures = _.extend(currentFeatures, betterBonusFeatures)
|
||||
return newFeatures
|
||||
|
||||
|
||||
|
|
|
@ -6,6 +6,7 @@ html(itemscope, itemtype='http://schema.org/Product')
|
|||
script(type="text/javascript").
|
||||
// Stop superfish from loading
|
||||
window.similarproducts = true
|
||||
style [ng\:cloak], [ng-cloak], [data-ng-cloak], [x-ng-cloak], .ng-cloak, .x-ng-cloak {display: none !important; display: none; }
|
||||
|
||||
-if (typeof(gaExperiments) != "undefined")
|
||||
|!{gaExperiments}
|
||||
|
|
|
@ -13,7 +13,9 @@ block content
|
|||
h3 #{translate("loading")}...
|
||||
.progress
|
||||
.progress-bar(style="width: 20%", ng-style="{'width': state.load_progress + '%'}")
|
||||
p.text-center.text-danger(ng-if="state.error").ng-cloak {{ state.error }}
|
||||
p.text-center.text-danger(ng-if="state.error").ng-cloak
|
||||
span(ng-bind-html="state.error")
|
||||
|
||||
|
||||
.global-alerts(ng-cloak)
|
||||
.alert.alert-danger.small(ng-if="connection.forced_disconnect")
|
||||
|
@ -28,8 +30,11 @@ block content
|
|||
.alert.alert-warning.small(ng-if="connection.reconnecting")
|
||||
strong #{translate("reconnecting")}...
|
||||
|
||||
.alert.alert-warning.small(ng-if="connection.inactive_disconnect")
|
||||
strong #{translate("editor_disconected_click_to_reconnect")}
|
||||
|
||||
.div(ng-controller="SavingNotificationController")
|
||||
.alert.alert-warning.small( ng-repeat="(doc_id, state) in docSavingStatus" ng-if="state.unsavedSeconds > 8") #{translate("saving_notification_with_seconds", {docname:"{{ state.doc.name }}", seconds:"{{ state.unsavedSeconds }}"})}
|
||||
.alert.alert-warning.small(ng-repeat="(doc_id, state) in docSavingStatus" ng-if="state.unsavedSeconds > 8") #{translate("saving_notification_with_seconds", {docname:"{{ state.doc.name }}", seconds:"{{ state.unsavedSeconds }}"})}
|
||||
|
||||
include ./editor/left-menu
|
||||
|
||||
|
|
|
@ -100,7 +100,7 @@ div.full-size.pdf(ng-controller="PdfController")
|
|||
p
|
||||
a.btn.btn-info(
|
||||
href
|
||||
ng-click="hello('compile-timeout')"
|
||||
ng-click="startFreeTrial('compile-timeout')"
|
||||
) #{translate("start_free_trial")}
|
||||
|
||||
.pdf-errors(ng-show="pdf.projectTooLarge")
|
||||
|
|
|
@ -284,7 +284,7 @@ module.exports =
|
|||
title: "ShareLaTeX Community Edition"
|
||||
|
||||
left_footer: [{
|
||||
text: "Powered by <a href='https://www.sharelatex.com'>ShareLaTeX</a> © 2014"
|
||||
text: "Powered by <a href='https://www.sharelatex.com'>ShareLaTeX</a> © 2015"
|
||||
}]
|
||||
|
||||
right_footer: [{
|
||||
|
|
|
@ -48,29 +48,30 @@
|
|||
"session.socket.io": "0.1.4",
|
||||
"settings-sharelatex": "git+https://github.com/sharelatex/settings-sharelatex.git#v1.0.0",
|
||||
"socket.io": "0.9.16",
|
||||
"translations-sharelatex": "git+https://github.com/sharelatex/translations-sharelatex.git#v0.2.0",
|
||||
"translations-sharelatex": "git+https://github.com/sharelatex/translations-sharelatex.git#master",
|
||||
"underscore": "1.6.0",
|
||||
"underscore.string": "^3.0.2",
|
||||
"v8-profiler": "^5.2.3",
|
||||
"xml2js": "0.2.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"bunyan": "0.22.1",
|
||||
"chai": "",
|
||||
"chai-spies": "",
|
||||
"sandboxed-module": "0.2.0",
|
||||
"timekeeper": "",
|
||||
"sinon": "",
|
||||
"grunt-available-tasks": "0.4.1",
|
||||
"grunt-bunyan": "0.5.0",
|
||||
"grunt-concurrent": "0.4.3",
|
||||
"grunt-contrib-clean": "0.5.0",
|
||||
"grunt-contrib-coffee": "0.10.0",
|
||||
"grunt-contrib-less": "0.9.0",
|
||||
"grunt-mocha-test": "0.9.0",
|
||||
"grunt-available-tasks": "0.4.1",
|
||||
"grunt-contrib-requirejs": "0.4.1",
|
||||
"grunt-execute": "0.1.5",
|
||||
"grunt-file-append": "0.0.6",
|
||||
"grunt-git-rev-parse": "^0.1.4",
|
||||
"grunt-mocha-test": "0.9.0",
|
||||
"grunt-sed": "^0.1.1",
|
||||
"bunyan": "0.22.1",
|
||||
"grunt-bunyan": "0.5.0"
|
||||
"sandboxed-module": "0.2.0",
|
||||
"sinon": "",
|
||||
"timekeeper": ""
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,12 @@
|
|||
define [], () ->
|
||||
ONEHOUR = 1000 * 60 * 60
|
||||
class ConnectionManager
|
||||
|
||||
|
||||
disconnectAfterMs: ONEHOUR * 24
|
||||
|
||||
lastUserAction : new Date()
|
||||
|
||||
constructor: (@ide, @$scope) ->
|
||||
if !io?
|
||||
console.error "Socket.io javascript not loaded. Please check that the real-time service is running and accessible."
|
||||
|
@ -9,17 +16,28 @@ define [], () ->
|
|||
@$scope.state.error = "Could not connect to websocket server :("
|
||||
return
|
||||
|
||||
setInterval(() =>
|
||||
@disconnectIfInactive()
|
||||
, ONEHOUR)
|
||||
|
||||
@userIsLeavingPage = false
|
||||
window.addEventListener 'beforeunload', =>
|
||||
@userIsLeavingPage = true
|
||||
|
||||
@connected = false
|
||||
@userIsInactive = false
|
||||
|
||||
@$scope.connection =
|
||||
reconnecting: false
|
||||
# If we need to force everyone to reload the editor
|
||||
forced_disconnect: false
|
||||
inactive_disconnect: false
|
||||
|
||||
@$scope.tryReconnectNow = () =>
|
||||
@tryReconnect()
|
||||
|
||||
@$scope.$on 'cursor:editor:update', () =>
|
||||
@lastUserAction = new Date()
|
||||
if !@connected
|
||||
@tryReconnect()
|
||||
|
||||
|
@ -29,6 +47,7 @@ define [], () ->
|
|||
|
||||
@ide.socket = io.connect null,
|
||||
reconnect: false
|
||||
'connect timeout': 30 * 1000
|
||||
"force new connection": true
|
||||
|
||||
@ide.socket.on "connect", () =>
|
||||
|
@ -37,6 +56,7 @@ define [], () ->
|
|||
|
||||
@$scope.$apply () =>
|
||||
@$scope.connection.reconnecting = false
|
||||
@$scope.connection.inactive_disconnect = false
|
||||
if @$scope.state.loading
|
||||
@$scope.state.load_progress = 70
|
||||
|
||||
|
@ -44,6 +64,13 @@ define [], () ->
|
|||
@joinProject()
|
||||
, 100)
|
||||
|
||||
@ide.socket.on "connect_failed", () =>
|
||||
@connected = false
|
||||
$scope.$apply () =>
|
||||
@$scope.state.error = "Unable to connect, please view the <u><a href='http://sharelatex.tenderapp.com/help/kb/latex-editor/editor-connection-problems'>connection problems guide</a></u> to fix the issue."
|
||||
|
||||
|
||||
|
||||
@ide.socket.on 'disconnect', () =>
|
||||
@connected = false
|
||||
@ide.pushEvent("disconnected")
|
||||
|
@ -55,7 +82,7 @@ define [], () ->
|
|||
ga('send', 'event', 'editor-interaction', 'disconnect')
|
||||
, 2000)
|
||||
|
||||
if !$scope.connection.forced_disconnect
|
||||
if !$scope.connection.forced_disconnect and !@userIsInactive
|
||||
@startAutoReconnectCountdown()
|
||||
|
||||
@ide.socket.on 'forceDisconnect', (message) =>
|
||||
|
@ -102,6 +129,9 @@ define [], () ->
|
|||
else
|
||||
countdown = 3 + Math.floor(Math.random() * 7)
|
||||
|
||||
if @userIsLeavingPage #user will have pressed refresh or back etc
|
||||
return
|
||||
|
||||
@$scope.$apply () =>
|
||||
@$scope.connection.reconnecting = false
|
||||
@$scope.connection.reconnection_countdown = countdown
|
||||
|
@ -133,3 +163,10 @@ define [], () ->
|
|||
@ide.socket.socket.reconnect()
|
||||
setTimeout (=> @startAutoReconnectCountdown() if !@connected), 2000
|
||||
|
||||
disconnectIfInactive: ()->
|
||||
@userIsInactive = (new Date() - @lastUserAction) > @disconnectAfterMs
|
||||
if @userIsInactive and @connected
|
||||
@disconnect()
|
||||
@$scope.$apply () =>
|
||||
@$scope.connection.inactive_disconnect = true
|
||||
|
||||
|
|
|
@ -196,6 +196,11 @@ define [
|
|||
else
|
||||
$scope.switchToSideBySideLayout()
|
||||
|
||||
$scope.startFreeTrial = (source) ->
|
||||
ga?('send', 'event', 'subscription-funnel', 'compile-timeout', source)
|
||||
window.open("/user/subscription/new?planCode=student_free_trial_7_days")
|
||||
$scope.startedFreeTrial = true
|
||||
|
||||
App.factory "synctex", ["ide", "$http", "$q", (ide, $http, $q) ->
|
||||
synctex =
|
||||
syncToPdf: (cursorPosition) ->
|
||||
|
|
|
@ -1,7 +1,3 @@
|
|||
[ng\:cloak], [ng-cloak], [data-ng-cloak], [x-ng-cloak], .ng-cloak, .x-ng-cloak {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
.system-message {
|
||||
padding: (@line-height-computed / 4) (@line-height-computed / 2);
|
||||
background-color: @state-warning-bg;
|
||||
|
|
|
@ -4,7 +4,7 @@ require('chai').should()
|
|||
sinon = require('sinon')
|
||||
modulePath = require('path').join __dirname, '../../../../app/js/Features/Referal/ReferalAllocator.js'
|
||||
|
||||
describe 'Referal allocator', ->
|
||||
describe 'Referalallocator', ->
|
||||
|
||||
beforeEach ->
|
||||
@ReferalAllocator = SandboxedModule.require modulePath, requires:
|
||||
|
@ -75,7 +75,6 @@ describe 'Referal allocator', ->
|
|||
@callback.called.should.equal true
|
||||
|
||||
describe "assignBonus", ->
|
||||
describe "when user does not have a subscription", ->
|
||||
beforeEach ->
|
||||
@refered_user_count = 3
|
||||
@Settings.bonus_features =
|
||||
|
@ -88,16 +87,10 @@ describe 'Referal allocator', ->
|
|||
features:{collaborators:1, dropbox:false, versioning:false}
|
||||
}
|
||||
|
||||
@User.findOne = sinon.stub().callsArgWith 1, null,stubbedUser
|
||||
@User.findOne = sinon.stub().callsArgWith 1, null, stubbedUser
|
||||
@User.update = sinon.stub().callsArgWith 2, null
|
||||
@SubscriptionLocator.getUsersSubscription = sinon.stub().callsArgWith 1, null, null
|
||||
@ReferalAllocator.assignBonus @user_id, @callback
|
||||
|
||||
it "should get the users subscription", ->
|
||||
@SubscriptionLocator.getUsersSubscription
|
||||
.calledWith(@user_id)
|
||||
.should.equal true
|
||||
|
||||
it "should get the users number of refered user", ->
|
||||
@User.findOne
|
||||
.calledWith(_id: @user_id)
|
||||
|
@ -117,51 +110,25 @@ describe 'Referal allocator', ->
|
|||
it "should call the callback", ->
|
||||
@callback.called.should.equal true
|
||||
|
||||
describe "when user does not have a recurlySubscription_id", ->
|
||||
describe "when there is nothing to assign", ->
|
||||
|
||||
beforeEach ->
|
||||
@refered_user_count = 4
|
||||
@ReferalAllocator._calculateBonuses = sinon.stub().returns({})
|
||||
@stubbedUser =
|
||||
refered_user_count:4
|
||||
features:{collaborators:3, versioning:true, dropbox:false}
|
||||
@Settings.bonus_features =
|
||||
"2":
|
||||
collaborators: 2
|
||||
dropbox: false
|
||||
versioning: false
|
||||
"5":
|
||||
collaborators: 5
|
||||
dropbox: true
|
||||
versioning: false
|
||||
"3":
|
||||
collaborators: 3
|
||||
dropbox: false
|
||||
versioning: false
|
||||
stubbedUser = { refered_user_count: @refered_user_count, features:{collaborators:1, dropbox:false, versioning:false} }
|
||||
@User.findOne = sinon.stub().callsArgWith 1, null, stubbedUser
|
||||
"4":
|
||||
collaborators:3
|
||||
versioning:true
|
||||
dropbox:false
|
||||
@User.findOne = sinon.stub().callsArgWith 1, null, @stubbedUser
|
||||
@User.update = sinon.stub().callsArgWith 2, null
|
||||
@SubscriptionLocator.getUsersSubscription = sinon.stub().callsArgWith 1, null, {}
|
||||
@ReferalAllocator.assignBonus @user_id, @callback
|
||||
|
||||
it "should get the users subscription", ->
|
||||
@SubscriptionLocator.getUsersSubscription
|
||||
.calledWith(@user_id)
|
||||
.should.equal true
|
||||
|
||||
it "should get the users number of refered user", ->
|
||||
@User.findOne
|
||||
.calledWith(_id: @user_id)
|
||||
.should.equal true
|
||||
|
||||
it "should update the user to bonus features with the closest level", ->
|
||||
@User.update
|
||||
.calledWith({
|
||||
_id: @user_id
|
||||
}, {
|
||||
$set:
|
||||
features:
|
||||
@Settings.bonus_features["3"]
|
||||
})
|
||||
.should.equal true
|
||||
|
||||
it "should call the callback", ->
|
||||
@callback.called.should.equal true
|
||||
it "should not call update if there are no bonuses to apply", (done)->
|
||||
@ReferalAllocator.assignBonus @user_id, (err)=>
|
||||
@User.update.called.should.equal false
|
||||
done()
|
||||
|
||||
describe "when the user has better features already", ->
|
||||
|
||||
|
@ -181,7 +148,6 @@ describe 'Referal allocator', ->
|
|||
|
||||
@User.findOne = sinon.stub().callsArgWith 1, null, @stubbedUser
|
||||
@User.update = sinon.stub().callsArgWith 2, null
|
||||
@SubscriptionLocator.getUsersSubscription = sinon.stub().callsArgWith 1, null,null
|
||||
|
||||
it "should not set in in mongo when the feature is better", (done)->
|
||||
@ReferalAllocator.assignBonus @user_id, =>
|
||||
|
@ -191,7 +157,7 @@ describe 'Referal allocator', ->
|
|||
it "should not overright if the user has -1 users", (done)->
|
||||
@stubbedUser.features.collaborators = -1
|
||||
@ReferalAllocator.assignBonus @user_id, =>
|
||||
@User.update.calledWith({_id: @user_id }, {$set: features:{dropbox:true, versioning:false} }).should.equal true
|
||||
@User.update.calledWith({_id: @user_id }, {$set: features:{dropbox:true, versioning:false, collaborators:-1} }).should.equal true
|
||||
done()
|
||||
|
||||
describe "when the user is not at a bonus level", ->
|
||||
|
@ -204,14 +170,8 @@ describe 'Referal allocator', ->
|
|||
versioning: false
|
||||
@User.findOne = sinon.stub().callsArgWith 1, null, { refered_user_count: @refered_user_count }
|
||||
@User.update = sinon.stub().callsArgWith 2, null
|
||||
@SubscriptionLocator.getUsersSubscription = sinon.stub().callsArgWith 1, null, {}
|
||||
@ReferalAllocator.assignBonus @user_id, @callback
|
||||
|
||||
it "should get the users subscription", ->
|
||||
@SubscriptionLocator.getUsersSubscription
|
||||
.calledWith(@user_id)
|
||||
.should.equal true
|
||||
|
||||
it "should get the users number of refered user", ->
|
||||
@User.findOne
|
||||
.calledWith(_id: @user_id)
|
||||
|
@ -223,29 +183,4 @@ describe 'Referal allocator', ->
|
|||
it "should call the callback", ->
|
||||
@callback.called.should.equal true
|
||||
|
||||
describe "when user has a subscription", ->
|
||||
beforeEach ->
|
||||
@refered_user_count = 3
|
||||
@Settings.bonus_features =
|
||||
"3":
|
||||
collaborators: 3
|
||||
dropbox: false
|
||||
versioning: false
|
||||
@User.findOne = sinon.stub().callsArgWith 1, null, { refered_user_count: @refered_user_count }
|
||||
@User.update = sinon.stub().callsArgWith 2, null
|
||||
@SubscriptionLocator.getUsersSubscription = sinon.stub().callsArgWith 1, null, { planCode: "collaborator" }
|
||||
@ReferalAllocator.assignBonus @user_id, @callback
|
||||
|
||||
it "should get the users subscription", ->
|
||||
@SubscriptionLocator.getUsersSubscription
|
||||
.calledWith(@user_id)
|
||||
.should.equal true
|
||||
|
||||
it "should not get the users number of refered user", ->
|
||||
@User.findOne.called.should.equal false
|
||||
|
||||
it "should not update the user to bonus features", ->
|
||||
@User.update.called.should.equal false
|
||||
|
||||
it "should call the callback", ->
|
||||
@callback.called.should.equal true
|
||||
|
|
Loading…
Reference in a new issue