Преглед изворни кода

Merge pull request #1176 from sharelatex/ja-manage-v1-subs

Manage v1 subscriptions from v2

GitOrigin-RevId: 1fd63b3630f781e8b4cc3dc1413966540e8d0076
James Allen пре 7 година
родитељ
комит
ee800f7448

+ 16 - 3
services/web/app/coffee/Features/Subscription/SubscriptionController.coffee

@@ -12,6 +12,7 @@ UserGetter = require "../User/UserGetter"
 FeaturesUpdater = require './FeaturesUpdater'
 planFeatures = require './planFeatures'
 GroupPlansData = require './GroupPlansData'
+V1SubscriptionManager = require "./V1SubscriptionManager"
 
 module.exports = SubscriptionController =
 
@@ -97,7 +98,8 @@ module.exports = SubscriptionController =
 				managedGroupSubscriptions,
 				confirmedMemberInstitutions,
 				managedInstitutions,
-				v1Subscriptions
+				v1Subscriptions,
+				v1SubscriptionStatus
 			} = results
 			logger.log {
 				user,
@@ -106,7 +108,8 @@ module.exports = SubscriptionController =
 				managedGroupSubscriptions,
 				confirmedMemberInstitutions,
 				managedInstitutions,
-				v1Subscriptions
+				v1Subscriptions,
+				v1SubscriptionStatus
 			}, "showing subscription dashboard"
 			plans = SubscriptionViewModelBuilder.buildViewModel()
 			data = {
@@ -118,7 +121,8 @@ module.exports = SubscriptionController =
 				managedGroupSubscriptions,
 				confirmedMemberInstitutions,
 				managedInstitutions,
-				v1Subscriptions
+				v1Subscriptions,
+				v1SubscriptionStatus
 			}
 			res.render "subscriptions/dashboard", data
 
@@ -158,6 +162,15 @@ module.exports = SubscriptionController =
 				return next(err)
 			res.redirect "/user/subscription"
 
+	cancelV1Subscription: (req, res, next) ->
+		user_id = AuthenticationController.getLoggedInUserId(req)
+		logger.log {user_id}, "canceling v1 subscription"
+		V1SubscriptionManager.cancelV1Subscription user_id, (err)->
+			if err?
+				logger.err err:err, user_id:user_id, "something went wrong canceling v1 subscription"
+				return next(err)
+			res.redirect "/user/subscription"
+
 	updateSubscription: (req, res, next)->
 		_origin = req?.query?.origin || null
 		user = AuthenticationController.getSessionUser(req)

+ 2 - 0
services/web/app/coffee/Features/Subscription/SubscriptionRouter.coffee

@@ -40,6 +40,8 @@ module.exports =
 		webRouter.post '/user/subscription/cancel',     AuthenticationController.requireLogin(), SubscriptionController.cancelSubscription
 		webRouter.post '/user/subscription/reactivate', AuthenticationController.requireLogin(), SubscriptionController.reactivateSubscription
 
+		webRouter.post '/user/subscription/v1/cancel', AuthenticationController.requireLogin(), SubscriptionController.cancelV1Subscription
+
 		webRouter.put '/user/subscription/extend', AuthenticationController.requireLogin(), SubscriptionController.extendTrial
 
 		webRouter.get "/user/subscription/upgrade-annual",  AuthenticationController.requireLogin(), SubscriptionController.renderUpgradeToAnnualPlanPage

+ 8 - 1
services/web/app/coffee/Features/Subscription/SubscriptionViewModelBuilder.coffee

@@ -51,6 +51,10 @@ module.exports =
 					return cb(error) if error?
 					# Only return one argument to async.auto, otherwise it returns an array
 					cb(null, subscriptions)
+			v1SubscriptionStatus: (cb) ->
+				V1SubscriptionManager.getSubscriptionStatusFromV1 user._id, (error, status, v1Id) ->
+					return cb(error) if error?
+					cb(null, status)
 		}, (err, results) ->
 			return callback(err) if err?
 			{
@@ -60,6 +64,7 @@ module.exports =
 				confirmedMemberInstitutions,
 				managedInstitutions,
 				v1Subscriptions,
+				v1SubscriptionStatus,
 				recurlySubscription,
 				plan
 			} = results
@@ -68,6 +73,7 @@ module.exports =
 			confirmedMemberInstitutions ?= []
 			managedInstitutions ?= []
 			v1Subscriptions ?= {}
+			v1SubscriptionStatus ?= {}
 
 
 			if personalSubscription?.toObject?
@@ -97,7 +103,8 @@ module.exports =
 				memberGroupSubscriptions,
 				confirmedMemberInstitutions,
 				managedInstitutions,
-				v1Subscriptions
+				v1Subscriptions,
+				v1SubscriptionStatus
 			}
 
 	buildViewModel : ->

+ 13 - 1
services/web/app/coffee/Features/Subscription/V1SubscriptionManager.coffee

@@ -39,6 +39,18 @@ module.exports = V1SubscriptionManager =
 			url: (v1Id) -> "/api/v1/sharelatex/users/#{v1Id}/subscriptions"
 		}, callback
 
+	getSubscriptionStatusFromV1: (userId, callback=(err, status) ->) ->
+		V1SubscriptionManager._v1Request userId, {
+			method: 'GET',
+			url: (v1Id) -> "/api/v1/sharelatex/users/#{v1Id}/subscription_status"
+		}, callback
+
+	cancelV1Subscription: (userId, callback=(err)->) ->
+		V1SubscriptionManager._v1Request userId, {
+			method: 'DELETE',
+			url: (v1Id) -> "/api/v1/sharelatex/users/#{v1Id}/subscription"
+		}, callback
+
 	v1IdForUser: (userId, callback=(err, v1Id) ->) ->
 		UserGetter.getUser userId, {'overleaf.id': 1}, (err, user) ->
 			return callback(err) if err?
@@ -76,7 +88,7 @@ module.exports = V1SubscriptionManager =
 					pass: settings.apis.v1.pass
 					sendImmediately: true
 				json: true,
-				timeout: 5 * 1000
+				timeout: 15 * 1000
 			}, (error, response, body) ->
 				if error?
 					# Specially handle no connection err, so warning can be shown

+ 2 - 0
services/web/app/coffee/infrastructure/ExpressLocals.coffee

@@ -16,6 +16,7 @@ hashedFiles = {}
 Path = require 'path'
 Features = require "./Features"
 Modules = require "./Modules"
+moment = require 'moment'
 
 jsPath =
 	if Settings.useMinifiedJs
@@ -126,6 +127,7 @@ module.exports = (app, webRouter, privateApiRouter, publicApiRouter)->
 		res.locals.fullJsPath = Url.resolve(staticFilesBase, jsPath)
 		res.locals.lib = PackageVersions.lib
 
+		res.locals.moment = moment
 
 		res.locals.buildJsPath = (jsFile, opts = {})->
 			path = Path.join(jsPath, jsFile)

+ 3 - 0
services/web/app/views/subscriptions/dashboard.pug

@@ -35,6 +35,9 @@ block content
 						-if (settings.overleaf && v1Subscriptions)
 							include ./dashboard/_v1_subscriptions
 
+						-if (v1SubscriptionStatus)
+							include ./dashboard/_v1_subscription_status
+
 						-if (!hasAnySubscription)
 							p You're on the #{settings.appName} Free plan.
 							|

+ 60 - 0
services/web/app/views/subscriptions/dashboard/_v1_subscription_status.pug

@@ -0,0 +1,60 @@
+- if (v1SubscriptionStatus['team'])
+  p
+    | You have a legacy group licence from Overleaf v1.
+  - if (v1SubscriptionStatus['team']['will_end_at'])
+    p
+      | Your current group licence ends on
+      |
+      strong= moment(v1SubscriptionStatus['team']['will_end_at']).format('Do MMM YY')
+      |
+      | and will
+      |
+      - if (v1SubscriptionStatus['team']['will_renew'])
+        | be automatically renewed.
+      - else 
+        | not be automatically renewed.
+  - if (v1SubscriptionStatus['can_cancel_team'])
+    p
+      form(method="POST", action="/user/subscription/v1/cancel")
+        input(type="hidden", name="_csrf", value=csrfToken)
+        button().btn.btn-danger Stop automatic renewal
+  - else
+    p
+      | Please
+      |
+      a(href="/contact") contact support
+      |
+      | to make changes to your plan
+  hr
+
+- if (v1SubscriptionStatus['product'])
+  p  
+    | You have a legacy Overleaf v1
+    |
+    strong= v1SubscriptionStatus['product']['display_name']
+    |
+    | plan.
+  p
+    | Your plan ends on
+    |
+    strong= moment(v1SubscriptionStatus['product']['will_end_at']).format('Do MMM YY')
+    |
+    | and will
+    |
+    - if (v1SubscriptionStatus['product']['will_renew'])
+      | be automatically renewed.
+    - else 
+      | not be automatically renewed.
+  - if (v1SubscriptionStatus['can_cancel'])
+    p
+      form(method="POST", action="/user/subscription/v1/cancel")
+        input(type="hidden", name="_csrf", value=csrfToken)
+        button().btn.btn-danger Stop automatic renewal
+  - else
+    p
+      | Please
+      |
+      a(href="/contact") contact support
+      |
+      | to make changes to your plan
+  hr

+ 0 - 8
services/web/app/views/subscriptions/dashboard/_v1_subscriptions.pug

@@ -1,11 +1,3 @@
-- if (v1Subscriptions.has_subscription)
-	-hasAnySubscription = true
-	p
-		| You are subscribed to Overleaf through Overleaf v1
-	p
-		a.btn.btn-primary(href='/sign_in_to_v1?return_to=/users/edit%23status') Manage v1 Subscription
-	hr
-
 - if (v1Subscriptions.teams && v1Subscriptions.teams.length > 0)
 	-hasAnySubscription = true
 	for team in v1Subscriptions.teams

+ 32 - 2
services/web/test/acceptance/coffee/SubscriptionTests.coffee

@@ -234,7 +234,8 @@ describe 'Subscriptions', ->
 			before (done) ->
 				v1Id = MockV1Api.nextV1Id()
 				MockV1Api.setUser v1Id, {
-					subscription: {}
+					subscription: {},
+					subscription_status: {}
 				}
 				MockV1Api.setAffiliations [{
 					email: 'confirmed-affiliation-email@stanford.example.edu'
@@ -281,6 +282,10 @@ describe 'Subscriptions', ->
 							name: 'Test team'
 						}]
 					}
+					subscription_status: @subscription_status = {
+						product: { 'mock': 'product' }
+						team: null
+					}
 				}
 				@user.setV1Id v1Id, (error) =>
 					return done(error) if error?
@@ -295,4 +300,29 @@ describe 'Subscriptions', ->
 				expect(@data.memberGroupSubscriptions).to.deep.equal []
 
 			it 'should return a v1Subscriptions', ->
-				expect(@data.v1Subscriptions).to.deep.equal @subscription
+				expect(@data.v1Subscriptions).to.deep.equal @subscription
+
+			it 'should return a v1SubscriptionStatus', ->
+				expect(@data.v1SubscriptionStatus).to.deep.equal @subscription_status
+
+	describe.only 'canceling', ->
+		before (done) ->
+			@user = new User()
+			MockV1Api.setUser v1Id = MockV1Api.nextV1Id(), @v1_user = {}
+			async.series [
+				(cb) => @user.login(cb)
+				(cb) => @user.setV1Id(v1Id, cb)
+			], (error) =>
+				@user.request {
+					method: 'POST',
+					url: '/user/subscription/v1/cancel'
+				}, (error, @response) =>
+					return done(error) if error?
+					done()
+
+		it 'should tell v1 to cancel the subscription', ->
+			expect(@v1_user.canceled).to.equal true
+
+		it 'should redirect to the subscription dashboard', ->
+			expect(@response.statusCode).to.equal 302
+			expect(@response.headers.location).to.equal '/user/subscription'

+ 14 - 0
services/web/test/acceptance/coffee/helpers/MockV1Api.coffee

@@ -53,6 +53,20 @@ module.exports = MockV1Api =
 			else
 				res.sendStatus 404
 
+		app.get "/api/v1/sharelatex/users/:v1_user_id/subscription_status", (req, res, next) =>
+			user = @users[req.params.v1_user_id]
+			if user?.subscription_status?
+				res.json user.subscription_status
+			else
+				res.sendStatus 404
+
+		app.delete "/api/v1/sharelatex/users/:v1_user_id/subscription", (req, res, next) =>
+			user = @users[req.params.v1_user_id]
+			if user?
+				user.canceled = true
+				res.sendStatus 200
+			else
+				res.sendStatus 404
 
 		app.post "/api/v1/sharelatex/users/:v1_user_id/sync", (req, res, next) =>
 			@syncUserFeatures(req.params.v1_user_id)

+ 1 - 0
services/web/test/unit/coffee/Subscription/SubscriptionControllerTests.coffee

@@ -79,6 +79,7 @@ describe "SubscriptionController", ->
 			"./RecurlyWrapper": @RecurlyWrapper = {}
 			"./FeaturesUpdater": @FeaturesUpdater = {}
 			"./GroupPlansData": @GroupPlansData = {}
+			"./V1SubscriptionManager": @V1SubscriptionManager = {}
 
 
 		@res = new MockResponse()