Parcourir la source

Merge branch 'master' of github.com:sharelatex/web-sharelatex

James Allen il y a 11 ans
Parent
commit
bafd3a1cb4

+ 11 - 2
services/web/Gruntfile.coffee

@@ -11,7 +11,8 @@ module.exports = (grunt) ->
 	grunt.loadNpmTasks 'grunt-bunyan'
 	grunt.loadNpmTasks 'grunt-sed'
 	grunt.loadNpmTasks 'grunt-git-rev-parse'
-	
+	grunt.loadNpmTasks 'grunt-file-append'
+
 	config =
 		execute:
 			app:
@@ -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']

+ 18 - 29
services/web/app/coffee/Features/Referal/ReferalAllocator.coffee

@@ -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"
-					if user.refered_user_count? and user.refered_user_count > 0
-						newFeatures = ReferalAllocator._calculateBonuses(user)
-						User.update query, { $set: features: newFeatures }, callback
-
-					else
-						callback()
+		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"
+			if user.refered_user_count? and user.refered_user_count > 0
+				newFeatures = ReferalAllocator._calculateFeatures(user)
+				if _.isEqual newFeatures, user.features
+					return callback()
+				User.update query, { $set: features: newFeatures }, callback
 			else
 				callback()
-		
-	_calculateBonuses : (user)->
-		bonusLevel = ReferalAllocator._getBonusLevel(user)
 
-		newFeatures = {}
+	_calculateFeatures : (user)->
+		bonusLevel = ReferalAllocator._getBonusLevel(user)
+		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 _.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
 
 

+ 1 - 0
services/web/app/views/layout.jade

@@ -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}

+ 7 - 2
services/web/app/views/project/editor.jade

@@ -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
 

+ 1 - 1
services/web/app/views/project/editor/pdf.jade

@@ -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")

+ 1 - 1
services/web/config/settings.defaults.coffee

@@ -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: [{

+ 9 - 8
services/web/package.json

@@ -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": ""
   }
 }

+ 40 - 3
services/web/public/coffee/ide/connection/ConnectionManager.coffee

@@ -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."
@@ -8,18 +15,29 @@ define [], () ->
 				$scope.$apply () =>
 					@$scope.state.error = "Could not connect to websocket server :("
 				return
-			
-			@connected = false
 
+			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
+

+ 5 - 0
services/web/public/coffee/ide/pdf/controllers/PdfController.coffee

@@ -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) ->

+ 0 - 4
services/web/public/stylesheets/app/base.less

@@ -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;

+ 19 - 84
services/web/test/UnitTests/coffee/Referal/ReferalAllocatorTests.coffee

@@ -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)
@@ -116,52 +109,26 @@ 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