Просмотр исходного кода

Add AB test for plans (+15 squashed commits)
Squashed commits:
[45c2237] Add a `subscription-form-switch-to-student` event
[1ad9b8f] change experiment name, and re-enable switch-to-student workflow
[f7cdb78] Remove debug
[4b9778a] Incorporate collaborator alternative plans
[701e80b] Add collaborator plans for heron and ibis
[287aa0f] AB test plans from editor page
[c74052e] Fix change-plan view for default plans
[1a947d6] Use correct plan codes
[1eecda7] Adjust prices
[69c4c7b] Introduce two plans
[8b8d5f8] Rename sixpack experiment
[c332002] Fix up the change-plan page
[c7af52d] Overhaul change-plan page, show only plans from current generation
[33d86bf] update plan
[5bbd946] Add a basic plans AB test

Shane Kilkelly 10 лет назад
Родитель
Сommit
d5a7514923

+ 20 - 6
services/web/app/coffee/Features/Subscription/SubscriptionController.coffee

@@ -8,6 +8,7 @@ Settings   = require 'settings-sharelatex'
 logger     = require('logger-sharelatex')
 logger     = require('logger-sharelatex')
 GeoIpLookup = require("../../infrastructure/GeoIpLookup")
 GeoIpLookup = require("../../infrastructure/GeoIpLookup")
 SubscriptionDomainHandler = require("./SubscriptionDomainHandler")
 SubscriptionDomainHandler = require("./SubscriptionDomainHandler")
+UserGetter = require "../User/UserGetter"
 
 
 module.exports = SubscriptionController =
 module.exports = SubscriptionController =
 
 
@@ -21,14 +22,27 @@ module.exports = SubscriptionController =
 		if req.query.v?
 		if req.query.v?
 			viewName = "#{viewName}_#{req.query.v}"
 			viewName = "#{viewName}_#{req.query.v}"
 		logger.log viewName:viewName, "showing plans page"
 		logger.log viewName:viewName, "showing plans page"
+		currentUser = null
 		GeoIpLookup.getCurrencyCode req.query?.ip || req.ip, (err, recomendedCurrency)->
 		GeoIpLookup.getCurrencyCode req.query?.ip || req.ip, (err, recomendedCurrency)->
 			return next(err) if err?
 			return next(err) if err?
-			res.render viewName,
-				title: "plans_and_pricing"
-				plans: plans
-				baseUrl: baseUrl
-				gaExperiments: Settings.gaExperiments.plansPage
-				recomendedCurrency:recomendedCurrency
+			render = () ->
+				res.render viewName,
+					title: "plans_and_pricing"
+					plans: plans
+					baseUrl: baseUrl
+					gaExperiments: Settings.gaExperiments.plansPage
+					recomendedCurrency:recomendedCurrency
+					shouldABTestPlans: currentUser == null or (currentUser?.signUpDate? and currentUser.signUpDate >= (new Date('2011-10-18')))
+			user_id = AuthenticationController.getLoggedInUserId(req)
+			if user_id?
+				console.log '>> user is logged in'
+				UserGetter.getUser user_id, {signUpDate: 1}, (err, user) ->
+					return next(err) if err?
+					currentUser = user
+					render()
+			else
+				console.log '>> not logged in'
+				render()
 
 
 	#get to show the recurly.js page
 	#get to show the recurly.js page
 	paymentPage: (req, res, next) ->
 	paymentPage: (req, res, next) ->

+ 2 - 3
services/web/app/coffee/Features/Subscription/SubscriptionViewModelBuilder.coffee

@@ -44,7 +44,7 @@ module.exports =
 		allPlans = {}
 		allPlans = {}
 		plans.forEach (plan)->
 		plans.forEach (plan)->
 			allPlans[plan.planCode] = plan
 			allPlans[plan.planCode] = plan
-			
+
 		result =
 		result =
 			allPlans: allPlans
 			allPlans: allPlans
 
 
@@ -54,7 +54,7 @@ module.exports =
 
 
 		result.studentAccounts = _.filter plans, (plan)->
 		result.studentAccounts = _.filter plans, (plan)->
 			plan.planCode.indexOf("student") != -1
 			plan.planCode.indexOf("student") != -1
-			
+
 		result.groupMonthlyPlans = _.filter plans, (plan)->
 		result.groupMonthlyPlans = _.filter plans, (plan)->
 			plan.groupPlan and !plan.annual
 			plan.groupPlan and !plan.annual
 
 
@@ -68,4 +68,3 @@ module.exports =
 			!plan.groupPlan and plan.annual and plan.planCode.indexOf("student") == -1
 			!plan.groupPlan and plan.annual and plan.planCode.indexOf("student") == -1
 
 
 		return result
 		return result
-

+ 6 - 5
services/web/app/views/subscriptions/dashboard.jade

@@ -12,8 +12,8 @@ block scripts
 
 
 mixin printPlan(plan)
 mixin printPlan(plan)
 	-if (!plan.hideFromUsers)
 	-if (!plan.hideFromUsers)
-		tr(ng-controller="ChangePlanFormController")
-			td(ng-init="plan=#{JSON.stringify(plan)}")
+		tr(ng-controller="ChangePlanFormController", ng-init="plan=#{JSON.stringify(plan)}", ng-show="shouldShowPlan(plan.planCode)")
+			td
 				strong #{plan.name}
 				strong #{plan.name}
 			td {{refreshPrice(plan.planCode)}}
 			td {{refreshPrice(plan.planCode)}}
 				-if (plan.annual)
 				-if (plan.annual)
@@ -46,8 +46,8 @@ block content
 							|  
 							|  
 							| #{translate("your_billing_details_were_saved")}
 							| #{translate("your_billing_details_were_saved")}
 					.card(ng-if="view == 'overview'")
 					.card(ng-if="view == 'overview'")
-						.page-header
-							h1 #{translate("your_subscription")}
+						.page-header(x-current-plan="#{subscription.planCode}")
+							h1 #{translate("your_subscription")} 
 
 
 						- if (subscription && user._id+'' == subscription.admin_id+'')
 						- if (subscription && user._id+'' == subscription.admin_id+'')
 							case subscription.state
 							case subscription.state
@@ -56,7 +56,8 @@ block content
 									
 									
 								when "active"
 								when "active"
 									p !{translate("currently_subscribed_to_plan", {planName:"<strong>" + subscription.name + "</strong>"})} 
 									p !{translate("currently_subscribed_to_plan", {planName:"<strong>" + subscription.name + "</strong>"})} 
-										a(href, ng-click="changePlan = true") !{translate("change_plan")}.
+										span(ng-show="!isNextGenPlan")
+											a(href, ng-click="changePlan = true") !{translate("change_plan")}.
 									p !{translate("next_payment_of_x_collectected_on_y", {paymentAmmount:"<strong>" + subscription.price + "</strong>", collectionDate:"<strong>" + subscription.nextPaymentDueAt + "</strong>"})}
 									p !{translate("next_payment_of_x_collectected_on_y", {paymentAmmount:"<strong>" + subscription.price + "</strong>", collectionDate:"<strong>" + subscription.nextPaymentDueAt + "</strong>"})}
 									p.pull-right
 									p.pull-right
 									p
 									p

+ 131 - 125
services/web/app/views/subscriptions/plans.jade

@@ -3,6 +3,7 @@ block scripts
 	script(type='text/javascript').
 	script(type='text/javascript').
 		window.recomendedCurrency = '#{recomendedCurrency}'
 		window.recomendedCurrency = '#{recomendedCurrency}'
 		window.abCurrencyFlag = '#{abCurrencyFlag}'
 		window.abCurrencyFlag = '#{abCurrencyFlag}'
+		window.shouldABTestPlans = #{shouldABTestPlans || false}
 
 
 	script(type='text/javascript').
 	script(type='text/javascript').
 		(function() {var s=document.createElement('script'); s.type='text/javascript';s.async=true; 
 		(function() {var s=document.createElement('script'); s.type='text/javascript';s.async=true; 
@@ -56,133 +57,138 @@ block content
 										ng-click="changeCurreny(currency)"
 										ng-click="changeCurreny(currency)"
 									) {{currency}} ({{value['symbol']}})
 									) {{currency}} ({{value['symbol']}})
 
 
-				.row(ng-cloak)
-					.col-md-10.col-md-offset-1
-						.row
-							.card-group.text-centered(ng-if="ui.view == 'monthly' || ui.view == 'annual'")
-								.col-md-4
-									.card.card-first
-										.card-header
-											h2 #{translate("personal")} 
-										.circle #{translate("free")} 
-										ul.list-unstyled
-											li #{translate("one_collaborator")}
-											li &nbsp;
-											li &nbsp;
-											li &nbsp;
-											li
-												br
-												a.btn.btn-info(
-													href="/register"
-													style=(getLoggedInUserId() === undefined ? "" : "visibility: hidden")
-												) #{translate("sign_up_now")}
-								.col-md-4
-									.card.card-highlighted
-										.card-header
-											h2 #{translate("collaborator")}
-										.circle
-											span(ng-if="ui.view == 'monthly'")
-												| {{plans[currencyCode]['collaborator']['monthly']}}
-												span.small /mo
-											span(ng-if="ui.view == 'annual'")
-												| {{plans[currencyCode]['collaborator']['annual']}}
-												span.small /yr
-										ul.list-unstyled
-											li 
-												strong #{translate("collabs_per_proj", {collabcount:10})}
-											li #{translate("full_doc_history")}
-											li #{translate("sync_to_dropbox")}
-											li #{translate("sync_to_github")}
-											li
-												br
-												a.btn.btn-info(
+				div
+					.row(ng-cloak)
+						.col-md-10.col-md-offset-1
+							.row
+								.card-group.text-centered(ng-if="ui.view == 'monthly' || ui.view == 'annual'")
+									.col-md-4
+										.card.card-first
+											.card-header
+												h2 #{translate("personal")} 
+											.circle #{translate("free")} 
+											ul.list-unstyled
+												li #{translate("one_collaborator")}
+												li &nbsp;
+												li &nbsp;
+												li &nbsp;
+												li
+													br
+													a.btn.btn-info(
+														href="/register"
+														style=(getLoggedInUserId() === undefined ? "" : "visibility: hidden")
+													) #{translate("sign_up_now")}
+									.col-md-4
+										.card.card-highlighted
+											.card-header
+												h2 #{translate("collaborator")}
+											.circle
+												span(ng-if="ui.view == 'monthly'")
+													| {{plans[currencyCode]['collaborator']['monthly']}}
+													span.small /mo
+												span(ng-if="ui.view == 'annual'")
+													| {{plans[currencyCode]['collaborator']['annual']}}
+													span.small /yr
+											ul.list-unstyled
+												li 
+													strong #{translate("collabs_per_proj", {collabcount:10})}
+												li #{translate("full_doc_history")}
+												li #{translate("sync_to_dropbox")}
+												li #{translate("sync_to_github")}
+												li
+													br
+													a.btn.btn-info(
+
+														ng-href="#{baseUrl}/user/subscription/new?planCode=collaborator{{ (ui.view == 'annual' ? '-annual' : '') + (plansVariant == 'default' ? planQueryString : '_'+plansVariant)}}&currency={{currencyCode}}", ng-click="signUpNowClicked('collaborator')"
+													)
+														span(ng-show="ui.view != 'annual'") #{translate("start_free_trial")}
+														span(ng-show="ui.view == 'annual'") #{translate("buy_now")}
+									.col-md-4
+										.card.card-last
+											.card-header
+												h2 #{translate("professional")}
+											.circle 
+												span(ng-if="ui.view == 'monthly'")
+													| {{plans[currencyCode]['professional']['monthly']}}
+													span.small /mo
+												span(ng-if="ui.view == 'annual'")
+													| {{plans[currencyCode]['professional']['annual']}}
+													span.small /yr
+											ul.list-unstyled
+												li
+													strong #{translate("unlimited_collabs")}
+												li #{translate("full_doc_history")}
+												li #{translate("sync_to_dropbox")}
+												li #{translate("sync_to_github")}
+												li
+													br
+													a.btn.btn-info(
+														ng-href="#{baseUrl}/user/subscription/new?planCode=professional{{ ui.view == 'annual' && '-annual' || planQueryString}}&currency={{currencyCode}}", ng-click="signUpNowClicked('professional')"
+													)
+														span(ng-show="ui.view != 'annual'") #{translate("start_free_trial")}
+														span(ng-show="ui.view == 'annual'") #{translate("buy_now")}
+
+								.card-group.text-centered(ng-if="ui.view == 'student'")
+									.col-md-4
+										.card.card-first
+											.card-header
+												h2 #{translate("personal")}
+											.circle #{translate("free")}
+											ul.list-unstyled
+												li #{translate("one_collaborator")}
+												li &nbsp;
+												li &nbsp;
+												li &nbsp;
+												li
+													br
+													a.btn.btn-info(
+														href="/register"
+														style=(getLoggedInUserId() === undefined ? "" : "visibility: hidden")
+													) #{translate("sign_up_now")}
+
+									.col-md-4
+										.card.card-highlighted
+											.card-header
+												h2 #{translate("student")}
+											.circle
+												span
+													| {{plans[currencyCode]['student']['monthly']}}
+													span.small /mo
+											ul.list-unstyled
+												li
+													strong #{translate("collabs_per_proj", {collabcount:6})}
+												li #{translate("full_doc_history")}
+												li #{translate("sync_to_dropbox")}
+												li #{translate("sync_to_github")}
+												li
+													br
+													a.btn.btn-info(
+														ng-href="#{baseUrl}/user/subscription/new?planCode=student{{ plansVariant == 'default' ? planQueryString : '_'+plansVariant }}&currency={{currencyCode}}",
+														ng-click="signUpNowClicked('student')"
+													) #{translate("start_free_trial")}
+
+									.col-md-4
+										.card.card-last
+											.card-header
+												h2 #{translate("student")} (#{translate("annual")})
+											.circle
+												span
+													| {{plans[currencyCode]['student']['annual']}}
+													span.small /yr
+											ul.list-unstyled
+												li
+													strong #{translate("collabs_per_proj", {collabcount:6})}
+												li #{translate("full_doc_history")}
+												li #{translate("sync_to_dropbox")}
+												li #{translate("sync_to_github")}
+												li
+													br
+													a.btn.btn-info(
+														ng-href="#{baseUrl}/user/subscription/new?planCode=student-annual{{ plansVariant == 'default' ? '' : '_'+plansVariant }}&currency={{currencyCode}}",
+														ng-click="signUpNowClicked('student')"
+													) #{translate("buy_now")}
 
 
-													ng-href="#{baseUrl}/user/subscription/new?planCode=collaborator{{ ui.view == 'annual' && '-annual' || planQueryString}}&currency={{currencyCode}}", ng-click="signUpNowClicked('collaborator')"
-												)
-													span(ng-show="ui.view != 'annual'") #{translate("start_free_trial")}
-													span(ng-show="ui.view == 'annual'") #{translate("buy_now")}
-								.col-md-4
-									.card.card-last
-										.card-header
-											h2 #{translate("professional")}
-										.circle 
-											span(ng-if="ui.view == 'monthly'")
-												| {{plans[currencyCode]['professional']['monthly']}}
-												span.small /mo
-											span(ng-if="ui.view == 'annual'")
-												| {{plans[currencyCode]['professional']['annual']}}
-												span.small /yr
-										ul.list-unstyled
-											li
-												strong #{translate("unlimited_collabs")}
-											li #{translate("full_doc_history")}
-											li #{translate("sync_to_dropbox")}
-											li #{translate("sync_to_github")}
-											li
-												br
-												a.btn.btn-info(
-													ng-href="#{baseUrl}/user/subscription/new?planCode=professional{{ ui.view == 'annual' && '-annual' || planQueryString}}&currency={{currencyCode}}", ng-click="signUpNowClicked('professional')"
-												)
-													span(ng-show="ui.view != 'annual'") #{translate("start_free_trial")}
-													span(ng-show="ui.view == 'annual'") #{translate("buy_now")}
 
 
-							.card-group.text-centered(ng-if="ui.view == 'student'")
-								.col-md-4
-									.card.card-first
-										.card-header
-											h2 #{translate("personal")}
-										.circle #{translate("free")}
-										ul.list-unstyled
-											li #{translate("one_collaborator")}
-											li &nbsp;
-											li &nbsp;
-											li &nbsp;
-											li
-												br
-												a.btn.btn-info(
-													href="/register"
-													style=(getLoggedInUserId() === undefined ? "" : "visibility: hidden")
-												) #{translate("sign_up_now")}
-								
-								.col-md-4
-									.card.card-highlighted
-										.card-header
-											h2 #{translate("student")}
-										.circle
-											span
-												| {{plans[currencyCode]['student']['monthly']}}
-												span.small /mo
-										ul.list-unstyled
-											li
-												strong #{translate("collabs_per_proj", {collabcount:6})}
-											li #{translate("full_doc_history")}
-											li #{translate("sync_to_dropbox")}
-											li #{translate("sync_to_github")}
-											li
-												br
-												a.btn.btn-info(
-													ng-href="#{baseUrl}/user/subscription/new?planCode=student{{planQueryString}}&currency={{currencyCode}}", ng-click="signUpNowClicked('student')"
-												) #{translate("start_free_trial")}
-								
-								.col-md-4
-									.card.card-last
-										.card-header
-											h2 #{translate("student")} (#{translate("annual")})
-										.circle
-											span
-												| {{plans[currencyCode]['student']['annual']}}
-												span.small /yr
-										ul.list-unstyled
-											li
-												strong #{translate("collabs_per_proj", {collabcount:6})}
-											li #{translate("full_doc_history")}
-											li #{translate("sync_to_dropbox")}
-											li #{translate("sync_to_github")}
-											li
-												br
-												a.btn.btn-info(
-													ng-href="#{baseUrl}/user/subscription/new?planCode=student-annual&currency={{currencyCode}}", ng-click="signUpNowClicked('student')"
-												) #{translate("buy_now")}
 
 
 				.row.row-spaced(ng-cloak)
 				.row.row-spaced(ng-cloak)
 					p.text-centered #{translate("choose_plan_works_for_you", {len:'{{trial_len}}'})}
 					p.text-centered #{translate("choose_plan_works_for_you", {len:'{{trial_len}}'})}

+ 15 - 3
services/web/public/coffee/ide.coffee

@@ -66,6 +66,18 @@ define [
 			pdfLayout: 'sideBySide'
 			pdfLayout: 'sideBySide'
 		}
 		}
 		$scope.user = window.user
 		$scope.user = window.user
+
+
+		$scope.startTrialPlanCode = 'collaborator_free_trial_7_days'
+		$scope.shouldABTestPlans = false
+		$scope._plansVariant = 'default'
+		if $scope.user.signUpDate >= '2011-10-18'
+			$scope.shouldABTestPlans = true
+			sixpack.participate 'plans-1610', ['default', 'heron', 'ibis'], (chosenVariation, rawResponse)->
+				$scope._plansVariant = chosenVariation
+				if chosenVariation in ['heron', 'ibis']
+					$scope.startTrialPlanCode = "collaborator_#{chosenVariation}"
+
 		$scope.settings = window.userSettings
 		$scope.settings = window.userSettings
 		$scope.anonymous = window.anonymous
 		$scope.anonymous = window.anonymous
 
 
@@ -75,7 +87,7 @@ define [
 		# Only run the header AB test for newly registered users.
 		# Only run the header AB test for newly registered users.
 		_abTestStartDate = new Date(Date.UTC(2016, 8, 28))
 		_abTestStartDate = new Date(Date.UTC(2016, 8, 28))
 		_userSignUpDate = new Date(window.user.signUpDate)
 		_userSignUpDate = new Date(window.user.signUpDate)
-		
+
 		$scope.shouldABTestHeaderLabels = _userSignUpDate > _abTestStartDate
 		$scope.shouldABTestHeaderLabels = _userSignUpDate > _abTestStartDate
 		$scope.headerLabelsABVariant = ""
 		$scope.headerLabelsABVariant = ""
 
 
@@ -92,7 +104,7 @@ define [
 		# Tracking code.
 		# Tracking code.
 		$scope.$watch "ui.view", (newView, oldView) ->
 		$scope.$watch "ui.view", (newView, oldView) ->
 			if newView? and newView != "editor" and newView != "pdf"
 			if newView? and newView != "editor" and newView != "pdf"
-				event_tracking.sendMBOnce "ide-open-view-#{ newView }-once" 
+				event_tracking.sendMBOnce "ide-open-view-#{ newView }-once"
 
 
 		$scope.$watch "ui.chatOpen", (isOpen) ->
 		$scope.$watch "ui.chatOpen", (isOpen) ->
 			event_tracking.sendMBOnce "ide-open-chat-once" if isOpen
 			event_tracking.sendMBOnce "ide-open-chat-once" if isOpen
@@ -105,7 +117,7 @@ define [
 		# End of tracking code.
 		# End of tracking code.
 
 
 		window._ide = ide
 		window._ide = ide
-		
+
 		ide.validFileRegex = '^[^\*\/]*$' # Don't allow * and /
 		ide.validFileRegex = '^[^\*\/]*$' # Don't allow * and /
 
 
 		ide.project_id = $scope.project_id = window.project_id
 		ide.project_id = $scope.project_id = window.project_id

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

@@ -29,12 +29,12 @@ define [
 
 
 		$scope.$watch "shouldShowLogs", (shouldShow) ->
 		$scope.$watch "shouldShowLogs", (shouldShow) ->
 			if shouldShow
 			if shouldShow
-				$scope.$applyAsync () -> 
+				$scope.$applyAsync () ->
 					$scope.shouldDropUp = getFilesDropdownTopCoordAsRatio() > 0.65
 					$scope.shouldDropUp = getFilesDropdownTopCoordAsRatio() > 0.65
 
 
 		# log hints tracking
 		# log hints tracking
 		$scope.logHintsNegFeedbackValues = logHintsFeedback.feedbackOpts
 		$scope.logHintsNegFeedbackValues = logHintsFeedback.feedbackOpts
-		
+
 		$scope.trackLogHintsLearnMore = () ->
 		$scope.trackLogHintsLearnMore = () ->
 			event_tracking.sendMB "logs-hints-learn-more"
 			event_tracking.sendMB "logs-hints-learn-more"
 
 
@@ -108,7 +108,7 @@ define [
 				_csrf: window.csrfToken
 				_csrf: window.csrfToken
 			}, {params: params}
 			}, {params: params}
 
 
-		parseCompileResponse = (response) ->		
+		parseCompileResponse = (response) ->
 
 
 			# keep last url
 			# keep last url
 			last_pdf_url = $scope.pdf.url
 			last_pdf_url = $scope.pdf.url
@@ -469,7 +469,7 @@ define [
 
 
 			event_tracking.sendMB "subscription-start-trial", { source }
 			event_tracking.sendMB "subscription-start-trial", { source }
 
 
-			window.open("/user/subscription/new?planCode=student_free_trial_7_days")
+			window.open("/user/subscription/new?planCode=#{$scope.startTrialPlanCode}")
 			$scope.startedFreeTrial = true
 			$scope.startedFreeTrial = true
 
 
 	App.factory "synctex", ["ide", "$http", "$q", (ide, $http, $q) ->
 	App.factory "synctex", ["ide", "$http", "$q", (ide, $http, $q) ->

+ 6 - 7
services/web/public/coffee/main/account-upgrade.coffee

@@ -10,10 +10,9 @@ define [
 
 
 			w = window.open()
 			w = window.open()
 			sixpack.convert "track-changes-discount", ->
 			sixpack.convert "track-changes-discount", ->
-				sixpack.participate 'in-editor-free-trial-plan', ['student', 'collaborator'], (planName, rawResponse)->
-					ga?('send', 'event', 'subscription-funnel', 'upgraded-free-trial', source)
-					url = "/user/subscription/new?planCode=#{planName}_free_trial_7_days&ssp=#{planName == 'collaborator'}"
-					if couponCode?
-						url = "#{url}&cc=#{couponCode}"
-					$scope.startedFreeTrial = true
-					w.location = url
+				ga?('send', 'event', 'subscription-funnel', 'upgraded-free-trial', source)
+				url = "/user/subscription/new?planCode=#{$scope.startTrialPlanCode}&ssp=true"
+				if couponCode?
+					url = "#{url}&cc=#{couponCode}"
+				$scope.startedFreeTrial = true
+				w.location = url

+ 5 - 2
services/web/public/coffee/main/new-subscription.coffee

@@ -5,12 +5,15 @@ define [
 
 
 	App.controller "NewSubscriptionController", ($scope, MultiCurrencyPricing, abTestManager, $http, sixpack, event_tracking, ccUtils)->
 	App.controller "NewSubscriptionController", ($scope, MultiCurrencyPricing, abTestManager, $http, sixpack, event_tracking, ccUtils)->
 		throw new Error("Recurly API Library Missing.")  if typeof recurly is "undefined"
 		throw new Error("Recurly API Library Missing.")  if typeof recurly is "undefined"
-	
+
 		$scope.currencyCode = MultiCurrencyPricing.currencyCode
 		$scope.currencyCode = MultiCurrencyPricing.currencyCode
 		$scope.plans = MultiCurrencyPricing.plans
 		$scope.plans = MultiCurrencyPricing.plans
 
 
 		$scope.switchToStudent = ()->
 		$scope.switchToStudent = ()->
-			window.location = "/user/subscription/new?planCode=student_free_trial_7_days&currency=#{$scope.currencyCode}&cc=#{$scope.data.coupon}"
+			currentPlanCode = window.plan_code
+			planCode = currentPlanCode.replace('collaborator', 'student')
+			event_tracking.sendMB 'subscription-form-switch-to-student', { plan: window.plan_code }
+			window.location = "/user/subscription/new?planCode=#{planCode}&currency=#{$scope.currencyCode}&cc=#{$scope.data.coupon}"
 
 
 		event_tracking.sendMB "subscription-form", { plan : window.plan_code }
 		event_tracking.sendMB "subscription-form", { plan : window.plan_code }
 
 

+ 186 - 12
services/web/public/coffee/main/plans.coffee

@@ -5,12 +5,171 @@ define [
 
 
 
 
 	App.factory "MultiCurrencyPricing", () ->
 	App.factory "MultiCurrencyPricing", () ->
-		
+
 		currencyCode = window.recomendedCurrency
 		currencyCode = window.recomendedCurrency
 
 
 		return {
 		return {
 			currencyCode:currencyCode
 			currencyCode:currencyCode
-			plans: 
+
+			heron:
+				USD:
+					student:
+						monthly: "$6"
+						annual: "$60"
+					collaborator:
+						monthly: "$12"
+						annual: "$144"
+				EUR:
+					student:
+						monthly: "€5"
+						annual: "€50"
+					collaborator:
+						monthly: "€10"
+						annual: "€120"
+				GBP:
+					student:
+						monthly: "£5"
+						annual: "£50"
+					collaborator:
+						monthly: "£10"
+						annual: "£120"
+				SEK:
+					student:
+						monthly: "45 kr"
+						annual: "450 kr"
+					collaborator:
+						monthly: "90 kr"
+						annual: "1080 kr"
+				CAD:
+					student:
+						monthly: "$7"
+						annual: "$70"
+					collaborator:
+						monthly: "$14"
+						annual: "$168"
+				NOK:
+					student:
+						monthly: "45 kr"
+						annual: "450 kr"
+					collaborator:
+						monthly: "90 kr"
+						annual: "1080 kr"
+				DKK:
+					student:
+						monthly: "40 kr"
+						annual: "400 kr"
+					collaborator:
+						monthly: "80 kr"
+						annual: "960 kr"
+				AUD:
+					student:
+						monthly: "$8"
+						annual: "$80"
+					collaborator:
+						monthly: "$16"
+						annual: "$192"
+				NZD:
+					student:
+						monthly: "$8"
+						annual: "$80"
+					collaborator:
+						monthly: "$16"
+						annual: "$192"
+				CHF:
+					student:
+						monthly: "Fr 6"
+						annual: "Fr 60"
+					collaborator:
+						monthly: "Fr 12"
+						annual: "Fr 144"
+				SGD:
+					student:
+						monthly: "$8"
+						annual: "$80"
+					collaborator:
+						monthly: "$16"
+						annual: "$192"
+
+			ibis:
+				USD:
+					student:
+						monthly: "$10"
+						annual: "$100"
+					collaborator:
+						monthly: "$18"
+						annual: "$216"
+				EUR:
+					student:
+						monthly: "€9"
+						annual: "€90"
+					collaborator:
+						monthly: "€16"
+						annual: "€192"
+				GBP:
+					student:
+						monthly: "£7"
+						annual: "£70"
+					collaborator:
+						monthly: "£13"
+						annual: "£156"
+				SEK:
+					student:
+						monthly: "75 kr"
+						annual: "750 kr"
+					collaborator:
+						monthly: "140 kr"
+						annual: "1680 kr"
+				CAD:
+					student:
+						monthly: "$12"
+						annual: "$120"
+					collaborator:
+						monthly: "$22"
+						annual: "$264"
+				NOK:
+					student:
+						monthly: "75 kr"
+						annual: "750 kr"
+					collaborator:
+						monthly: "140 kr"
+						annual: "1680 kr"
+				DKK:
+					student:
+						monthly: "68 kr"
+						annual: "680 kr"
+					collaborator:
+						monthly: "122 kr"
+						annual: "1464 kr"
+				AUD:
+					student:
+						monthly: "$13"
+						annual: "$130"
+					collaborator:
+						monthly: "$24"
+						annual: "$288"
+				NZD:
+					student:
+						monthly: "$14"
+						annual: "$140"
+					collaborator:
+						monthly: "$25"
+						annual: "$300"
+				CHF:
+					student:
+						monthly: "Fr 10"
+						annual: "Fr 100"
+					collaborator:
+						monthly: "Fr 18"
+						annual: "Fr 216"
+				SGD:
+					student:
+						monthly: "$14"
+						annual: "$140"
+					collaborator:
+						monthly: "$25"
+						annual: "$300"
+
+			plans:
 				USD:
 				USD:
 					symbol: "$"
 					symbol: "$"
 					student:
 					student:
@@ -23,7 +182,7 @@ define [
 						monthly: "$30"
 						monthly: "$30"
 						annual: "$360"
 						annual: "$360"
 
 
-				EUR: 
+				EUR:
 					symbol: "€"
 					symbol: "€"
 					student:
 					student:
 						monthly: "€7"
 						monthly: "€7"
@@ -34,7 +193,7 @@ define [
 					professional:
 					professional:
 						monthly: "€28"
 						monthly: "€28"
 						annual: "€336"
 						annual: "€336"
-						
+
 				GBP:
 				GBP:
 					symbol: "£"
 					symbol: "£"
 					student:
 					student:
@@ -117,7 +276,7 @@ define [
 					professional:
 					professional:
 						monthly: "$35"
 						monthly: "$35"
 						annual: "$420"
 						annual: "$420"
-						
+
 				CHF:
 				CHF:
 					symbol: "Fr"
 					symbol: "Fr"
 					student:
 					student:
@@ -141,36 +300,51 @@ define [
 					professional:
 					professional:
 						monthly: "$40"
 						monthly: "$40"
 						annual: "$480"
 						annual: "$480"
+
 		}
 		}
-	
 
 
 
 
+	App.controller "PlansController", ($scope, $modal, event_tracking, abTestManager, MultiCurrencyPricing, $http, sixpack) ->
+
+		$scope.plansVariant = 'default'
+		$scope.shouldABTestPlans = window.shouldABTestPlans
 
 
-	App.controller "PlansController", ($scope, $modal, event_tracking, abTestManager, MultiCurrencyPricing, $http) ->
+		if $scope.shouldABTestPlans
+			sixpack.participate 'plans-1610', ['default', 'heron', 'ibis'], (chosenVariation, rawResponse)->
+				$scope.plansVariant = chosenVariation
+				if chosenVariation in ['heron', 'ibis']
+					# overwrite student plans with alternative
+					for currency, _v of $scope.plans
+						$scope.plans[currency]['student'] = MultiCurrencyPricing[chosenVariation][currency]['student']
+						$scope.plans[currency]['collaborator'] = MultiCurrencyPricing[chosenVariation][currency]['collaborator']
 
 
 		$scope.plans = MultiCurrencyPricing.plans
 		$scope.plans = MultiCurrencyPricing.plans
+
 		$scope.currencyCode = MultiCurrencyPricing.currencyCode
 		$scope.currencyCode = MultiCurrencyPricing.currencyCode
 
 
 		$scope.trial_len = 7
 		$scope.trial_len = 7
+
 		$scope.planQueryString = '_free_trial_7_days'
 		$scope.planQueryString = '_free_trial_7_days'
 
 
 		$scope.ui =
 		$scope.ui =
 			view: "monthly"
 			view: "monthly"
 
 
-
 		$scope.changeCurreny = (newCurrency)->
 		$scope.changeCurreny = (newCurrency)->
 			$scope.currencyCode = newCurrency
 			$scope.currencyCode = newCurrency
 
 
 		$scope.signUpNowClicked = (plan, annual)->
 		$scope.signUpNowClicked = (plan, annual)->
 			if $scope.ui.view == "annual"
 			if $scope.ui.view == "annual"
 				plan = "#{plan}_annual"
 				plan = "#{plan}_annual"
-			
-			event_tracking.send 'subscription-funnel', 'sign_up_now_button', plan
+
+			event_tracking.send   'subscription-funnel', 'sign_up_now_button', plan
+			# TODO: double check this is correct
+			if $scope.shouldABTestPlans and plan in ['student', 'collaborator']
+				sixpack.convert 'plans-1610', () ->
 
 
 		$scope.switchToMonthly = ->
 		$scope.switchToMonthly = ->
 			$scope.ui.view = "monthly"
 			$scope.ui.view = "monthly"
 			event_tracking.send 'subscription-funnel', 'plans-page', 'monthly-prices'
 			event_tracking.send 'subscription-funnel', 'plans-page', 'monthly-prices'
-		
+
 		$scope.switchToStudent = ->
 		$scope.switchToStudent = ->
 			$scope.ui.view = "student"
 			$scope.ui.view = "student"
 			event_tracking.send 'subscription-funnel', 'plans-page', 'student-prices'
 			event_tracking.send 'subscription-funnel', 'plans-page', 'student-prices'
@@ -178,7 +352,7 @@ define [
 		$scope.switchToAnnual = ->
 		$scope.switchToAnnual = ->
 			$scope.ui.view = "annual"
 			$scope.ui.view = "annual"
 			event_tracking.send 'subscription-funnel', 'plans-page', 'student-prices'
 			event_tracking.send 'subscription-funnel', 'plans-page', 'student-prices'
-			
+
 		$scope.openGroupPlanModal = () ->
 		$scope.openGroupPlanModal = () ->
 			$modal.open {
 			$modal.open {
 				templateUrl: "groupPlanModalTemplate"
 				templateUrl: "groupPlanModalTemplate"

+ 20 - 11
services/web/public/coffee/main/subscription-dashboard.coffee

@@ -9,7 +9,7 @@ define [
 
 
 	App.controller "CurrenyDropdownController", ($scope, MultiCurrencyPricing, $q)->
 	App.controller "CurrenyDropdownController", ($scope, MultiCurrencyPricing, $q)->
 
 
-		$scope.plans = MultiCurrencyPricing.plans
+		# $scope.plans = MultiCurrencyPricing.plans
 		$scope.currencyCode = MultiCurrencyPricing.currencyCode
 		$scope.currencyCode = MultiCurrencyPricing.currencyCode
 
 
 		$scope.changeCurrency = (newCurrency)->
 		$scope.changeCurrency = (newCurrency)->
@@ -31,7 +31,7 @@ define [
 			$scope.currencyCode = MultiCurrencyPricing.currencyCode
 			$scope.currencyCode = MultiCurrencyPricing.currencyCode
 
 
 		$scope.pricing = MultiCurrencyPricing
 		$scope.pricing = MultiCurrencyPricing
-		$scope.plans = MultiCurrencyPricing.plans
+		# $scope.plans = MultiCurrencyPricing.plans
 		$scope.currencySymbol = MultiCurrencyPricing.plans[MultiCurrencyPricing.currencyCode].symbol
 		$scope.currencySymbol = MultiCurrencyPricing.plans[MultiCurrencyPricing.currencyCode].symbol
 
 
 		$scope.currencyCode = MultiCurrencyPricing.currencyCode
 		$scope.currencyCode = MultiCurrencyPricing.currencyCode
@@ -53,9 +53,9 @@ define [
 			price = ""
 			price = ""
 
 
 	App.controller "ConfirmChangePlanController", ($scope, $modalInstance, $http)->
 	App.controller "ConfirmChangePlanController", ($scope, $modalInstance, $http)->
-		
+
 		$scope.confirmChangePlan = ->
 		$scope.confirmChangePlan = ->
-			body = 
+			body =
 				plan_code: $scope.plan.planCode
 				plan_code: $scope.plan.planCode
 				_csrf : window.csrfToken
 				_csrf : window.csrfToken
 
 
@@ -74,7 +74,7 @@ define [
 		$scope.confirmLeaveGroup = ->
 		$scope.confirmLeaveGroup = ->
 			$scope.inflight = true
 			$scope.inflight = true
 			$http({
 			$http({
-				url: "/subscription/group/user", 
+				url: "/subscription/group/user",
 				method: "DELETE",
 				method: "DELETE",
 				params: {admin_user_id: $scope.admin_id, _csrf: window.csrfToken}
 				params: {admin_user_id: $scope.admin_id, _csrf: window.csrfToken}
 			}).success ->
 			}).success ->
@@ -87,6 +87,8 @@ define [
 
 
 
 
 	App.controller "UserSubscriptionController", ($scope, MultiCurrencyPricing, $http, sixpack, $modal) ->
 	App.controller "UserSubscriptionController", ($scope, MultiCurrencyPricing, $http, sixpack, $modal) ->
+		$scope.plans = MultiCurrencyPricing.plans
+
 		freeTrialEndDate = new Date(subscription?.trial_ends_at)
 		freeTrialEndDate = new Date(subscription?.trial_ends_at)
 
 
 		sevenDaysTime = new Date()
 		sevenDaysTime = new Date()
@@ -96,6 +98,16 @@ define [
 		freeTrialExpiresUnderSevenDays = freeTrialEndDate < sevenDaysTime
 		freeTrialExpiresUnderSevenDays = freeTrialEndDate < sevenDaysTime
 
 
 		$scope.view = 'overview'
 		$scope.view = 'overview'
+		$scope.getSuffix = (planCode) ->
+			planCode?.match(/(.*?)_(.*)/)?[2] || null
+		$scope.subscriptionSuffix = $scope.getSuffix(window?.subscription?.planCode)
+		if $scope.subscriptionSuffix == 'free_trial_7_days'
+			$scope.subscriptionSuffix = ''
+		$scope.isNextGenPlan = $scope.subscriptionSuffix in ['heron', 'ibis']
+
+		$scope.shouldShowPlan = (planCode) ->
+			$scope.getSuffix(planCode) not in ['heron', 'ibis']
+
 		isMonthlyCollab = subscription?.planCode?.indexOf("collaborator") != -1 and subscription?.planCode?.indexOf("ann") == -1
 		isMonthlyCollab = subscription?.planCode?.indexOf("collaborator") != -1 and subscription?.planCode?.indexOf("ann") == -1
 		stillInFreeTrial = freeTrialInFuture and freeTrialExpiresUnderSevenDays
 		stillInFreeTrial = freeTrialInFuture and freeTrialExpiresUnderSevenDays
 
 
@@ -118,7 +130,7 @@ define [
 					$scope.studentPrice = $scope.currencySymbol + (totalPriceExTax + taxAmmount)
 					$scope.studentPrice = $scope.currencySymbol + (totalPriceExTax + taxAmmount)
 
 
 		$scope.downgradeToStudent = ->
 		$scope.downgradeToStudent = ->
-			body = 
+			body =
 				plan_code: 'student'
 				plan_code: 'student'
 				_csrf : window.csrfToken
 				_csrf : window.csrfToken
 			$scope.inflight = true
 			$scope.inflight = true
@@ -129,7 +141,7 @@ define [
 					console.log "something went wrong changing plan"
 					console.log "something went wrong changing plan"
 
 
 		$scope.cancelSubscription = ->
 		$scope.cancelSubscription = ->
-			body = 
+			body =
 				_csrf : window.csrfToken
 				_csrf : window.csrfToken
 
 
 			$scope.inflight = true
 			$scope.inflight = true
@@ -158,7 +170,7 @@ define [
 
 
 
 
 		$scope.exendTrial = ->
 		$scope.exendTrial = ->
-			body = 
+			body =
 				_csrf : window.csrfToken
 				_csrf : window.csrfToken
 			$scope.inflight = true
 			$scope.inflight = true
 			$http.put("/user/subscription/extend", body)
 			$http.put("/user/subscription/extend", body)
@@ -166,6 +178,3 @@ define [
 					location.reload()
 					location.reload()
 				.error ->
 				.error ->
 					console.log "something went wrong changing plan"
 					console.log "something went wrong changing plan"
-
-
-

+ 23 - 1
services/web/test/UnitTests/coffee/Subscription/SubscriptionControllerTests.coffee

@@ -20,7 +20,7 @@ mockSubscriptions =
 describe "SubscriptionController sanboxed", ->
 describe "SubscriptionController sanboxed", ->
 
 
 	beforeEach ->
 	beforeEach ->
-		@user = {email:"tom@yahoo.com", _id: 'one'}
+		@user = {email:"tom@yahoo.com", _id: 'one', signUpDate: new Date('2000-10-01')}
 		@activeRecurlySubscription = mockSubscriptions["subscription-123-active"]
 		@activeRecurlySubscription = mockSubscriptions["subscription-123-active"]
 
 
 		@AuthenticationController =
 		@AuthenticationController =
@@ -63,6 +63,8 @@ describe "SubscriptionController sanboxed", ->
 			getCurrencyCode:sinon.stub()
 			getCurrencyCode:sinon.stub()
 		@SubscriptionDomainHandler =
 		@SubscriptionDomainHandler =
 			getDomainLicencePage:sinon.stub()
 			getDomainLicencePage:sinon.stub()
+		@UserGetter =
+			getUser: sinon.stub().callsArgWith(2, null, @user)
 		@SubscriptionController = SandboxedModule.require modulePath, requires:
 		@SubscriptionController = SandboxedModule.require modulePath, requires:
 			'../Authentication/AuthenticationController': @AuthenticationController
 			'../Authentication/AuthenticationController': @AuthenticationController
 			'./SubscriptionHandler': @SubscriptionHandler
 			'./SubscriptionHandler': @SubscriptionHandler
@@ -76,6 +78,7 @@ describe "SubscriptionController sanboxed", ->
 				warn:->
 				warn:->
 			"settings-sharelatex": @settings
 			"settings-sharelatex": @settings
 			"./SubscriptionDomainHandler":@SubscriptionDomainHandler
 			"./SubscriptionDomainHandler":@SubscriptionDomainHandler
+			"../User/UserGetter": @UserGetter
 
 
 
 
 		@res = new MockResponse()
 		@res = new MockResponse()
@@ -92,12 +95,31 @@ describe "SubscriptionController sanboxed", ->
 			@GeoIpLookup.getCurrencyCode.callsArgWith(1, null, @stubbedCurrencyCode)
 			@GeoIpLookup.getCurrencyCode.callsArgWith(1, null, @stubbedCurrencyCode)
 			@res.callback = done
 			@res.callback = done
 			@SubscriptionController.plansPage(@req, @res)
 			@SubscriptionController.plansPage(@req, @res)
+			@UserGetter.getUser = sinon.stub().callsArgWith(2, null, @user)
 
 
 		it "should set the recommended currency from the geoiplookup", (done)->
 		it "should set the recommended currency from the geoiplookup", (done)->
 			@res.renderedVariables.recomendedCurrency.should.equal(@stubbedCurrencyCode)
 			@res.renderedVariables.recomendedCurrency.should.equal(@stubbedCurrencyCode)
 			@GeoIpLookup.getCurrencyCode.calledWith(@req.ip).should.equal true
 			@GeoIpLookup.getCurrencyCode.calledWith(@req.ip).should.equal true
 			done()
 			done()
 
 
+		it 'should fetch the current user', (done) ->
+			@UserGetter.getUser.callCount.should.equal 1
+			done()
+
+		it 'should decide not to AB test the plans', (done) ->
+			@res.renderedVariables.shouldABTestPlans.should.equal false
+			done()
+
+		describe 'when user is not logged in', (done) ->
+
+			beforeEach ->
+				@AuthenticationController.getLoggedInUserId.returns(null)
+
+			it 'should not fetch the current user', (done) ->
+				@UserGetter.getUser.callCount.should.equal 0
+				done()
+
+
 	describe "editBillingDetailsPage", ->
 	describe "editBillingDetailsPage", ->
 		describe "with a user with a subscription", ->
 		describe "with a user with a subscription", ->
 			beforeEach (done) ->
 			beforeEach (done) ->