mirror of
https://github.com/overleaf/overleaf.git
synced 2024-11-21 20:47:08 -05:00
Merge pull request #619 from sharelatex/afc-email-tokens
Use emails with tokens for team invites
This commit is contained in:
commit
f7532e5854
31 changed files with 806 additions and 483 deletions
|
@ -118,14 +118,16 @@ Thank you
|
||||||
description: "Join #{ opts.project.name } at ShareLaTeX"
|
description: "Join #{ opts.project.name } at ShareLaTeX"
|
||||||
})
|
})
|
||||||
|
|
||||||
templates.completeJoinGroupAccount =
|
|
||||||
subject: _.template "Verify Email to join <%= group_name %> group"
|
templates.verifyEmailToJoinTeam =
|
||||||
|
subject: _.template "<%= inviterName %> has invited you to join a team on #{settings.appName}"
|
||||||
layout: BaseWithHeaderEmailLayout
|
layout: BaseWithHeaderEmailLayout
|
||||||
type:"notification"
|
type:"notification"
|
||||||
plainTextTemplate: _.template """
|
plainTextTemplate: _.template """
|
||||||
Hi, please verify your email to join the <%= group_name %> and get your free premium account
|
|
||||||
|
|
||||||
Click this link to verify now: <%= completeJoinUrl %>
|
Please click the button below to join the team and enjoy the benefits of an upgraded <%= appName %> account.
|
||||||
|
|
||||||
|
<%= acceptInviteUrl %>
|
||||||
|
|
||||||
Thank You
|
Thank You
|
||||||
|
|
||||||
|
@ -133,16 +135,15 @@ Thank You
|
||||||
"""
|
"""
|
||||||
compiledTemplate: (opts) ->
|
compiledTemplate: (opts) ->
|
||||||
SingleCTAEmailBody({
|
SingleCTAEmailBody({
|
||||||
title: "Verify Email to join #{ opts.group_name } group"
|
title: "#{opts.inviterName} has invited you to join a team on #{settings.appName}"
|
||||||
greeting: "Hi,"
|
greeting: "Hi,"
|
||||||
message: "please verify your email to join the #{ opts.group_name } group and get your free premium account."
|
message: "Please click the button below to join the team and enjoy the benefits of an upgraded #{ opts.appName } account."
|
||||||
secondaryMessage: null
|
secondaryMessage: null
|
||||||
ctaText: "Verify now"
|
ctaText: "Verify now"
|
||||||
ctaURL: opts.completeJoinUrl
|
ctaURL: opts.acceptInviteUrl
|
||||||
gmailGoToAction: null
|
gmailGoToAction: null
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
templates.testEmail =
|
templates.testEmail =
|
||||||
subject: _.template "A Test Email from ShareLaTeX"
|
subject: _.template "A Test Email from ShareLaTeX"
|
||||||
layout: BaseWithHeaderEmailLayout
|
layout: BaseWithHeaderEmailLayout
|
||||||
|
|
|
@ -56,7 +56,7 @@ V1HistoryNotSyncedError.prototype.__proto___ = Error.prototype
|
||||||
|
|
||||||
ProjectHistoryDisabledError = (message) ->
|
ProjectHistoryDisabledError = (message) ->
|
||||||
error = new Error(message)
|
error = new Error(message)
|
||||||
error.name = "ProjectHistoryDisabledError "
|
error.name = "ProjectHistoryDisabledError"
|
||||||
error.__proto__ = ProjectHistoryDisabledError.prototype
|
error.__proto__ = ProjectHistoryDisabledError.prototype
|
||||||
return error
|
return error
|
||||||
ProjectHistoryDisabledError.prototype.__proto___ = Error.prototype
|
ProjectHistoryDisabledError.prototype.__proto___ = Error.prototype
|
||||||
|
|
|
@ -0,0 +1,47 @@
|
||||||
|
SubscriptionGroupHandler = require("./SubscriptionGroupHandler")
|
||||||
|
logger = require("logger-sharelatex")
|
||||||
|
SubscriptionLocator = require("./SubscriptionLocator")
|
||||||
|
ErrorsController = require("../Errors/ErrorController")
|
||||||
|
SubscriptionDomainHandler = require("./SubscriptionDomainHandler")
|
||||||
|
AuthenticationController = require('../Authentication/AuthenticationController')
|
||||||
|
TeamInvitesHandler = require('./TeamInvitesHandler')
|
||||||
|
|
||||||
|
async = require("async")
|
||||||
|
|
||||||
|
module.exports =
|
||||||
|
join: (req, res)->
|
||||||
|
user = AuthenticationController.getSessionUser(req)
|
||||||
|
licence = SubscriptionDomainHandler.getLicenceUserCanJoin(user)
|
||||||
|
|
||||||
|
if !licence?
|
||||||
|
return ErrorsController.notFound(req, res)
|
||||||
|
|
||||||
|
jobs =
|
||||||
|
partOfGroup: (cb)->
|
||||||
|
SubscriptionGroupHandler.isUserPartOfGroup user.id, licence.group_subscription_id, cb
|
||||||
|
subscription: (cb)->
|
||||||
|
SubscriptionLocator.getUsersSubscription user.id, cb
|
||||||
|
|
||||||
|
async.series jobs, (err, results)->
|
||||||
|
{ partOfGroup, subscription } = results
|
||||||
|
if partOfGroup
|
||||||
|
return res.redirect("/user/subscription/custom_account")
|
||||||
|
else
|
||||||
|
res.render "subscriptions/domain/join",
|
||||||
|
title: "Group Invitation"
|
||||||
|
group_subscription_id: licence.group_subscription_id
|
||||||
|
licenceName: licence.name
|
||||||
|
has_personal_subscription: subscription?
|
||||||
|
|
||||||
|
createInvite: (req, res, next)->
|
||||||
|
user = AuthenticationController.getSessionUser(req)
|
||||||
|
licence = SubscriptionDomainHandler.getLicenceUserCanJoin(user)
|
||||||
|
|
||||||
|
if !licence?
|
||||||
|
return ErrorsController.notFound(req, res)
|
||||||
|
|
||||||
|
TeamInvitesHandler.createDomainInvite user, licence, (err) ->
|
||||||
|
if err?
|
||||||
|
next(err)
|
||||||
|
else
|
||||||
|
res.sendStatus 200
|
|
@ -6,7 +6,7 @@ Settings = require("settings-sharelatex")
|
||||||
CollaboratorsHandler = require("../Collaborators/CollaboratorsHandler")
|
CollaboratorsHandler = require("../Collaborators/CollaboratorsHandler")
|
||||||
CollaboratorsInvitesHandler = require("../Collaborators/CollaboratorsInviteHandler")
|
CollaboratorsInvitesHandler = require("../Collaborators/CollaboratorsInviteHandler")
|
||||||
|
|
||||||
module.exports =
|
module.exports = LimitationsManager =
|
||||||
allowedNumberOfCollaboratorsInProject: (project_id, callback) ->
|
allowedNumberOfCollaboratorsInProject: (project_id, callback) ->
|
||||||
ProjectGetter.getProject project_id, owner_ref: true, (error, project) =>
|
ProjectGetter.getProject project_id, owner_ref: true, (error, project) =>
|
||||||
return callback(error) if error?
|
return callback(error) if error?
|
||||||
|
@ -20,7 +20,6 @@ module.exports =
|
||||||
else
|
else
|
||||||
callback null, Settings.defaultPlanCode.collaborators
|
callback null, Settings.defaultPlanCode.collaborators
|
||||||
|
|
||||||
|
|
||||||
canAddXCollaborators: (project_id, x_collaborators, callback = (error, allowed)->) ->
|
canAddXCollaborators: (project_id, x_collaborators, callback = (error, allowed)->) ->
|
||||||
@allowedNumberOfCollaboratorsInProject project_id, (error, allowed_number) =>
|
@allowedNumberOfCollaboratorsInProject project_id, (error, allowed_number) =>
|
||||||
return callback(error) if error?
|
return callback(error) if error?
|
||||||
|
@ -56,6 +55,13 @@ module.exports =
|
||||||
return callback(err) if err?
|
return callback(err) if err?
|
||||||
callback err, subscriptions.length > 0, subscriptions
|
callback err, subscriptions.length > 0, subscriptions
|
||||||
|
|
||||||
|
teamHasReachedMemberLimit: (subscription) ->
|
||||||
|
currentTotal = (subscription.member_ids or []).length +
|
||||||
|
(subscription.teamInvites or []).length +
|
||||||
|
(subscription.invited_emails or []).length
|
||||||
|
|
||||||
|
return currentTotal >= subscription.membersLimit
|
||||||
|
|
||||||
hasGroupMembersLimitReached: (user_id, callback = (err, limitReached, subscription)->)->
|
hasGroupMembersLimitReached: (user_id, callback = (err, limitReached, subscription)->)->
|
||||||
SubscriptionLocator.getUsersSubscription user_id, (err, subscription)->
|
SubscriptionLocator.getUsersSubscription user_id, (err, subscription)->
|
||||||
if err?
|
if err?
|
||||||
|
@ -64,9 +70,6 @@ module.exports =
|
||||||
if !subscription?
|
if !subscription?
|
||||||
logger.err user_id:user_id, "no subscription found for user"
|
logger.err user_id:user_id, "no subscription found for user"
|
||||||
return callback("no subscription found")
|
return callback("no subscription found")
|
||||||
currentTotal = (subscription.member_ids or []).length + (subscription.invited_emails or []).length
|
|
||||||
limitReached = currentTotal >= subscription.membersLimit
|
|
||||||
logger.log user_id:user_id, limitReached:limitReached, currentTotal: currentTotal, membersLimit: subscription.membersLimit, "checking if subscription members limit has been reached"
|
|
||||||
callback(err, limitReached, subscription)
|
|
||||||
|
|
||||||
getOwnerIdOfProject = (project_id, callback)->
|
limitReached = LimitationsManager.teamHasReachedMemberLimit(subscription)
|
||||||
|
callback(err, limitReached, subscription)
|
||||||
|
|
|
@ -15,7 +15,7 @@ module.exports = SubscriptionDomainHandler =
|
||||||
getDomainLicencePage: (user)->
|
getDomainLicencePage: (user)->
|
||||||
licence = SubscriptionDomainHandler._findDomainLicence(user.email)
|
licence = SubscriptionDomainHandler._findDomainLicence(user.email)
|
||||||
if licence?.verifyEmail
|
if licence?.verifyEmail
|
||||||
return "/user/subscription/#{licence.subscription_id}/group/invited"
|
return "/user/subscription/domain/join"
|
||||||
else
|
else
|
||||||
return undefined
|
return undefined
|
||||||
|
|
||||||
|
|
|
@ -33,14 +33,6 @@ module.exports =
|
||||||
return res.sendStatus 500
|
return res.sendStatus 500
|
||||||
res.send()
|
res.send()
|
||||||
|
|
||||||
removeEmailInviteFromGroup: (req, res)->
|
|
||||||
adminUserId = AuthenticationController.getLoggedInUserId(req)
|
|
||||||
email = req.params.email
|
|
||||||
logger.log {adminUserId, email}, "removing email invite from group subscription"
|
|
||||||
SubscriptionGroupHandler.removeEmailInviteFromGroup adminUserId, email, (err)->
|
|
||||||
return next(error) if error?
|
|
||||||
res.send()
|
|
||||||
|
|
||||||
removeSelfFromGroup: (req, res)->
|
removeSelfFromGroup: (req, res)->
|
||||||
adminUserId = req.query.admin_user_id
|
adminUserId = req.query.admin_user_id
|
||||||
userToRemove_id = AuthenticationController.getLoggedInUserId(req)
|
userToRemove_id = AuthenticationController.getLoggedInUserId(req)
|
||||||
|
@ -62,68 +54,6 @@ module.exports =
|
||||||
users: users
|
users: users
|
||||||
subscription: subscription
|
subscription: subscription
|
||||||
|
|
||||||
renderGroupInvitePage: (req, res)->
|
|
||||||
group_subscription_id = req.params.subscription_id
|
|
||||||
user_id = AuthenticationController.getLoggedInUserId(req)
|
|
||||||
licence = SubscriptionDomainHandler.findDomainLicenceBySubscriptionId(group_subscription_id)
|
|
||||||
if !licence?
|
|
||||||
return ErrorsController.notFound(req, res)
|
|
||||||
jobs =
|
|
||||||
partOfGroup: (cb)->
|
|
||||||
SubscriptionGroupHandler.isUserPartOfGroup user_id, licence.group_subscription_id, cb
|
|
||||||
subscription: (cb)->
|
|
||||||
SubscriptionLocator.getUsersSubscription user_id, cb
|
|
||||||
async.series jobs, (err, results)->
|
|
||||||
{partOfGroup, subscription} = results
|
|
||||||
if partOfGroup
|
|
||||||
return res.redirect("/user/subscription/custom_account")
|
|
||||||
else
|
|
||||||
res.render "subscriptions/group/invite",
|
|
||||||
title: "Group Invitation"
|
|
||||||
group_subscription_id:group_subscription_id
|
|
||||||
licenceName:licence.name
|
|
||||||
has_personal_subscription: subscription?
|
|
||||||
|
|
||||||
beginJoinGroup: (req, res)->
|
|
||||||
subscription_id = req.params.subscription_id
|
|
||||||
currentUser = AuthenticationController.getSessionUser(req)
|
|
||||||
if !currentUser?
|
|
||||||
logger.err {subscription_id}, "error getting current user"
|
|
||||||
return res.sendStatus 500
|
|
||||||
licence = SubscriptionDomainHandler.findDomainLicenceBySubscriptionId(subscription_id)
|
|
||||||
if !licence?
|
|
||||||
return ErrorsController.notFound(req, res)
|
|
||||||
SubscriptionGroupHandler.sendVerificationEmail subscription_id, licence.name, currentUser.email, (err)->
|
|
||||||
if err?
|
|
||||||
res.sendStatus 500
|
|
||||||
else
|
|
||||||
res.sendStatus 200
|
|
||||||
|
|
||||||
completeJoin: (req, res)->
|
|
||||||
currentUser = AuthenticationController.getSessionUser(req)
|
|
||||||
subscription_id = req.params.subscription_id
|
|
||||||
if !SubscriptionDomainHandler.findDomainLicenceBySubscriptionId(subscription_id)?
|
|
||||||
return ErrorsController.notFound(req, res)
|
|
||||||
email = currentUser?.email
|
|
||||||
logger.log subscription_id:subscription_id, user_id:currentUser?._id, email:email, "starting the completion of joining group"
|
|
||||||
SubscriptionGroupHandler.processGroupVerification email, subscription_id, req.query?.token, (err)->
|
|
||||||
if err? and err == "token_not_found"
|
|
||||||
return res.redirect "/user/subscription/#{subscription_id}/group/invited?expired=true"
|
|
||||||
else if err?
|
|
||||||
return res.sendStatus 500
|
|
||||||
else
|
|
||||||
logger.log subscription_id:subscription_id, email:email, "user successful completed join of group subscription"
|
|
||||||
return res.redirect "/user/subscription/#{subscription_id}/group/successful-join"
|
|
||||||
|
|
||||||
renderSuccessfulJoinPage: (req, res)->
|
|
||||||
subscription_id = req.params.subscription_id
|
|
||||||
licence = SubscriptionDomainHandler.findDomainLicenceBySubscriptionId(subscription_id)
|
|
||||||
if !SubscriptionDomainHandler.findDomainLicenceBySubscriptionId(subscription_id)?
|
|
||||||
return ErrorsController.notFound(req, res)
|
|
||||||
res.render "subscriptions/group/successful_join",
|
|
||||||
title: "Sucessfully joined group"
|
|
||||||
licenceName:licence.name
|
|
||||||
|
|
||||||
exportGroupCsv: (req, res)->
|
exportGroupCsv: (req, res)->
|
||||||
user_id = AuthenticationController.getLoggedInUserId(req)
|
user_id = AuthenticationController.getLoggedInUserId(req)
|
||||||
logger.log user_id: user_id, "exporting group csv"
|
logger.log user_id: user_id, "exporting group csv"
|
||||||
|
|
|
@ -7,6 +7,7 @@ Subscription = require("../../models/Subscription").Subscription
|
||||||
LimitationsManager = require("./LimitationsManager")
|
LimitationsManager = require("./LimitationsManager")
|
||||||
logger = require("logger-sharelatex")
|
logger = require("logger-sharelatex")
|
||||||
OneTimeTokenHandler = require("../Security/OneTimeTokenHandler")
|
OneTimeTokenHandler = require("../Security/OneTimeTokenHandler")
|
||||||
|
TeamInvitesHandler = require("./TeamInvitesHandler")
|
||||||
EmailHandler = require("../Email/EmailHandler")
|
EmailHandler = require("../Email/EmailHandler")
|
||||||
settings = require("settings-sharelatex")
|
settings = require("settings-sharelatex")
|
||||||
NotificationsBuilder = require("../Notifications/NotificationsBuilder")
|
NotificationsBuilder = require("../Notifications/NotificationsBuilder")
|
||||||
|
@ -33,7 +34,7 @@ module.exports = SubscriptionGroupHandler =
|
||||||
userViewModel = buildUserViewModel(user)
|
userViewModel = buildUserViewModel(user)
|
||||||
callback(err, userViewModel)
|
callback(err, userViewModel)
|
||||||
else
|
else
|
||||||
SubscriptionUpdater.addEmailInviteToGroup adminUserId, newEmail, (err) ->
|
TeamInvitesHandler.createInvite adminUserId, newEmail, (err) ->
|
||||||
return callback(err) if err?
|
return callback(err) if err?
|
||||||
userViewModel = buildEmailInviteViewModel(newEmail)
|
userViewModel = buildEmailInviteViewModel(newEmail)
|
||||||
callback(err, userViewModel)
|
callback(err, userViewModel)
|
||||||
|
@ -41,10 +42,6 @@ module.exports = SubscriptionGroupHandler =
|
||||||
removeUserFromGroup: (adminUser_id, userToRemove_id, callback)->
|
removeUserFromGroup: (adminUser_id, userToRemove_id, callback)->
|
||||||
SubscriptionUpdater.removeUserFromGroup adminUser_id, userToRemove_id, callback
|
SubscriptionUpdater.removeUserFromGroup adminUser_id, userToRemove_id, callback
|
||||||
|
|
||||||
removeEmailInviteFromGroup: (adminUser_id, email, callback) ->
|
|
||||||
SubscriptionUpdater.removeEmailInviteFromGroup adminUser_id, email, callback
|
|
||||||
|
|
||||||
|
|
||||||
replaceUserReferencesInGroups: (oldId, newId, callback) ->
|
replaceUserReferencesInGroups: (oldId, newId, callback) ->
|
||||||
Subscription.update {admin_id: oldId}, {admin_id: newId}, (error) ->
|
Subscription.update {admin_id: oldId}, {admin_id: newId}, (error) ->
|
||||||
callback(error) if error?
|
callback(error) if error?
|
||||||
|
@ -62,8 +59,13 @@ module.exports = SubscriptionGroupHandler =
|
||||||
getPopulatedListOfMembers: (adminUser_id, callback)->
|
getPopulatedListOfMembers: (adminUser_id, callback)->
|
||||||
SubscriptionLocator.getUsersSubscription adminUser_id, (err, subscription)->
|
SubscriptionLocator.getUsersSubscription adminUser_id, (err, subscription)->
|
||||||
users = []
|
users = []
|
||||||
|
|
||||||
for email in subscription.invited_emails or []
|
for email in subscription.invited_emails or []
|
||||||
users.push buildEmailInviteViewModel(email)
|
users.push buildEmailInviteViewModel(email)
|
||||||
|
|
||||||
|
for teamInvite in subscription.teamInvites or []
|
||||||
|
users.push buildEmailInviteViewModel(teamInvite.email)
|
||||||
|
|
||||||
jobs = _.map subscription.member_ids, (user_id)->
|
jobs = _.map subscription.member_ids, (user_id)->
|
||||||
return (cb)->
|
return (cb)->
|
||||||
UserGetter.getUser user_id, (err, user)->
|
UserGetter.getUser user_id, (err, user)->
|
||||||
|
@ -85,58 +87,17 @@ module.exports = SubscriptionGroupHandler =
|
||||||
logger.log user_id:user_id, subscription_id:subscription_id, partOfGroup:partOfGroup, "checking if user is part of a group"
|
logger.log user_id:user_id, subscription_id:subscription_id, partOfGroup:partOfGroup, "checking if user is part of a group"
|
||||||
callback(err, partOfGroup)
|
callback(err, partOfGroup)
|
||||||
|
|
||||||
|
|
||||||
sendVerificationEmail: (subscription_id, licenceName, email, callback)->
|
|
||||||
ONE_DAY_IN_S = 1000 * 60 * 60 * 24
|
|
||||||
OneTimeTokenHandler.getNewToken subscription_id, {expiresIn:ONE_DAY_IN_S}, (err, token)->
|
|
||||||
opts =
|
|
||||||
to : email
|
|
||||||
group_name: licenceName
|
|
||||||
completeJoinUrl: "#{settings.siteUrl}/user/subscription/#{subscription_id}/group/complete-join?token=#{token}"
|
|
||||||
EmailHandler.sendEmail "completeJoinGroupAccount", opts, callback
|
|
||||||
|
|
||||||
processGroupVerification: (userEmail, subscription_id, token, callback)->
|
|
||||||
logger.log userEmail:userEmail, subscription_id:subscription_id, "processing group verification for user"
|
|
||||||
OneTimeTokenHandler.getValueFromTokenAndExpire token, (err, token_subscription_id)->
|
|
||||||
if err? or subscription_id != token_subscription_id
|
|
||||||
logger.err userEmail:userEmail, token:token, "token value not found for processing group verification"
|
|
||||||
return callback("token_not_found")
|
|
||||||
SubscriptionLocator.getSubscription subscription_id, (err, subscription)->
|
|
||||||
if err?
|
|
||||||
logger.err err:err, subscription:subscription, userEmail:userEmail, subscription_id:subscription_id, "error getting subscription"
|
|
||||||
return callback(err)
|
|
||||||
if !subscription?
|
|
||||||
logger.warn subscription_id:subscription_id, userEmail:userEmail, "no subscription found"
|
|
||||||
return callback()
|
|
||||||
SubscriptionGroupHandler.addUserToGroup subscription?.admin_id, userEmail, callback
|
|
||||||
|
|
||||||
convertEmailInvitesToMemberships: (email, user_id, callback = (err) ->) ->
|
|
||||||
SubscriptionLocator.getGroupsWithEmailInvite email, (err, groups = []) ->
|
|
||||||
return callback(err) if err?
|
|
||||||
logger.log {email, user_id, groups}, "found groups to convert from email invite to member"
|
|
||||||
jobs = []
|
|
||||||
for group in groups
|
|
||||||
do (group) ->
|
|
||||||
jobs.push (cb) ->
|
|
||||||
SubscriptionUpdater.removeEmailInviteFromGroup group.admin_id, email, (err) ->
|
|
||||||
return cb(err) if err?
|
|
||||||
SubscriptionUpdater.addUserToGroup group.admin_id, user_id, (err) ->
|
|
||||||
return cb(err) if err?
|
|
||||||
logger.log {group_id: group._id, user_id, email}, "converted email invite to group membership"
|
|
||||||
return cb()
|
|
||||||
async.series jobs, callback
|
|
||||||
|
|
||||||
buildUserViewModel = (user)->
|
buildUserViewModel = (user)->
|
||||||
u =
|
u =
|
||||||
email: user.email
|
email: user.email
|
||||||
first_name: user.first_name
|
first_name: user.first_name
|
||||||
last_name: user.last_name
|
last_name: user.last_name
|
||||||
holdingAccount: user.holdingAccount
|
invite: user.holdingAccount
|
||||||
_id: user._id
|
_id: user._id
|
||||||
return u
|
return u
|
||||||
|
|
||||||
buildEmailInviteViewModel = (email) ->
|
buildEmailInviteViewModel = (email) ->
|
||||||
return {
|
return {
|
||||||
email: email
|
email: email
|
||||||
holdingAccount: true
|
invite: true
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
AuthenticationController = require('../Authentication/AuthenticationController')
|
AuthenticationController = require('../Authentication/AuthenticationController')
|
||||||
SubscriptionController = require('./SubscriptionController')
|
SubscriptionController = require('./SubscriptionController')
|
||||||
SubscriptionGroupController = require './SubscriptionGroupController'
|
SubscriptionGroupController = require './SubscriptionGroupController'
|
||||||
|
DomainLicenceController = require './DomainLicenceController'
|
||||||
|
TeamInvitesController = require './TeamInvitesController'
|
||||||
Settings = require "settings-sharelatex"
|
Settings = require "settings-sharelatex"
|
||||||
|
|
||||||
module.exports =
|
module.exports =
|
||||||
|
@ -13,7 +15,6 @@ module.exports =
|
||||||
|
|
||||||
webRouter.get '/user/subscription/custom_account', AuthenticationController.requireLogin(), SubscriptionController.userCustomSubscriptionPage
|
webRouter.get '/user/subscription/custom_account', AuthenticationController.requireLogin(), SubscriptionController.userCustomSubscriptionPage
|
||||||
|
|
||||||
|
|
||||||
webRouter.get '/user/subscription/new', AuthenticationController.requireLogin(), SubscriptionController.paymentPage
|
webRouter.get '/user/subscription/new', AuthenticationController.requireLogin(), SubscriptionController.paymentPage
|
||||||
|
|
||||||
webRouter.get '/user/subscription/thank-you', AuthenticationController.requireLogin(), SubscriptionController.successful_subscription
|
webRouter.get '/user/subscription/thank-you', AuthenticationController.requireLogin(), SubscriptionController.successful_subscription
|
||||||
|
@ -23,14 +24,21 @@ module.exports =
|
||||||
webRouter.post '/subscription/group/user', AuthenticationController.requireLogin(), SubscriptionGroupController.addUserToGroup
|
webRouter.post '/subscription/group/user', AuthenticationController.requireLogin(), SubscriptionGroupController.addUserToGroup
|
||||||
webRouter.get '/subscription/group/export', AuthenticationController.requireLogin(), SubscriptionGroupController.exportGroupCsv
|
webRouter.get '/subscription/group/export', AuthenticationController.requireLogin(), SubscriptionGroupController.exportGroupCsv
|
||||||
webRouter.delete '/subscription/group/user/:user_id', AuthenticationController.requireLogin(), SubscriptionGroupController.removeUserFromGroup
|
webRouter.delete '/subscription/group/user/:user_id', AuthenticationController.requireLogin(), SubscriptionGroupController.removeUserFromGroup
|
||||||
webRouter.delete '/subscription/group/email/:email', AuthenticationController.requireLogin(), SubscriptionGroupController.removeEmailInviteFromGroup
|
|
||||||
webRouter.delete '/subscription/group/user', AuthenticationController.requireLogin(), SubscriptionGroupController.removeSelfFromGroup
|
webRouter.delete '/subscription/group/user', AuthenticationController.requireLogin(), SubscriptionGroupController.removeSelfFromGroup
|
||||||
|
|
||||||
|
# Team invites
|
||||||
|
webRouter.post '/subscription/invites', AuthenticationController.requireLogin(),
|
||||||
|
TeamInvitesController.createInvite
|
||||||
|
webRouter.get '/subscription/invites/:token/', AuthenticationController.requireLogin(),
|
||||||
|
TeamInvitesController.viewInvite
|
||||||
|
webRouter.put '/subscription/invites/:token/', AuthenticationController.requireLogin(),
|
||||||
|
TeamInvitesController.acceptInvite
|
||||||
|
webRouter.delete '/subscription/invites/:email/', AuthenticationController.requireLogin(),
|
||||||
|
TeamInvitesController.revokeInvite
|
||||||
|
|
||||||
webRouter.get '/user/subscription/:subscription_id/group/invited', AuthenticationController.requireLogin(), SubscriptionGroupController.renderGroupInvitePage
|
# Routes to join a domain licence team
|
||||||
webRouter.post '/user/subscription/:subscription_id/group/begin-join', AuthenticationController.requireLogin(), SubscriptionGroupController.beginJoinGroup
|
webRouter.get '/user/subscription/domain/join', AuthenticationController.requireLogin(), DomainLicenceController.join
|
||||||
webRouter.get '/user/subscription/:subscription_id/group/complete-join', AuthenticationController.requireLogin(), SubscriptionGroupController.completeJoin
|
webRouter.post '/user/subscription/domain/join', AuthenticationController.requireLogin(), DomainLicenceController.createInvite
|
||||||
webRouter.get '/user/subscription/:subscription_id/group/successful-join', AuthenticationController.requireLogin(), SubscriptionGroupController.renderSuccessfulJoinPage
|
|
||||||
|
|
||||||
#recurly callback
|
#recurly callback
|
||||||
publicApiRouter.post '/user/subscription/callback', SubscriptionController.recurlyNotificationParser, SubscriptionController.recurlyCallback
|
publicApiRouter.post '/user/subscription/callback', SubscriptionController.recurlyNotificationParser, SubscriptionController.recurlyCallback
|
||||||
|
@ -48,4 +56,3 @@ module.exports =
|
||||||
|
|
||||||
# Currently used in acceptance tests only, as a way to trigger the syncing logic
|
# Currently used in acceptance tests only, as a way to trigger the syncing logic
|
||||||
publicApiRouter.post "/user/:user_id/features/sync", AuthenticationController.httpAuth, SubscriptionController.refreshUserFeatures
|
publicApiRouter.post "/user/:user_id/features/sync", AuthenticationController.httpAuth, SubscriptionController.refreshUserFeatures
|
||||||
|
|
||||||
|
|
|
@ -36,14 +36,6 @@ module.exports = SubscriptionUpdater =
|
||||||
return callback(err)
|
return callback(err)
|
||||||
FeaturesUpdater.refreshFeatures user_id, callback
|
FeaturesUpdater.refreshFeatures user_id, callback
|
||||||
|
|
||||||
addEmailInviteToGroup: (adminUser_id, email, callback) ->
|
|
||||||
logger.log {adminUser_id, email}, "adding email into mongo subscription"
|
|
||||||
searchOps =
|
|
||||||
admin_id: adminUser_id
|
|
||||||
insertOperation =
|
|
||||||
"$addToSet": {invited_emails: email}
|
|
||||||
Subscription.findAndModify searchOps, insertOperation, callback
|
|
||||||
|
|
||||||
removeUserFromGroup: (adminUser_id, user_id, callback)->
|
removeUserFromGroup: (adminUser_id, user_id, callback)->
|
||||||
searchOps =
|
searchOps =
|
||||||
admin_id: adminUser_id
|
admin_id: adminUser_id
|
||||||
|
@ -55,13 +47,6 @@ module.exports = SubscriptionUpdater =
|
||||||
return callback(err)
|
return callback(err)
|
||||||
FeaturesUpdater.refreshFeatures user_id, callback
|
FeaturesUpdater.refreshFeatures user_id, callback
|
||||||
|
|
||||||
removeEmailInviteFromGroup: (adminUser_id, email, callback)->
|
|
||||||
Subscription.update {
|
|
||||||
admin_id: adminUser_id
|
|
||||||
}, "$pull": {
|
|
||||||
invited_emails: email
|
|
||||||
}, callback
|
|
||||||
|
|
||||||
deleteSubscription: (subscription_id, callback = (error) ->) ->
|
deleteSubscription: (subscription_id, callback = (error) ->) ->
|
||||||
SubscriptionLocator.getSubscription subscription_id, (err, subscription) ->
|
SubscriptionLocator.getSubscription subscription_id, (err, subscription) ->
|
||||||
return callback(err) if err?
|
return callback(err) if err?
|
||||||
|
|
|
@ -0,0 +1,58 @@
|
||||||
|
settings = require "settings-sharelatex"
|
||||||
|
logger = require("logger-sharelatex")
|
||||||
|
TeamInvitesHandler = require('./TeamInvitesHandler')
|
||||||
|
AuthenticationController = require("../Authentication/AuthenticationController")
|
||||||
|
SubscriptionLocator = require("./SubscriptionLocator")
|
||||||
|
ErrorController = require("../Errors/ErrorController")
|
||||||
|
EmailHelper = require("../Helpers/EmailHelper")
|
||||||
|
|
||||||
|
module.exports =
|
||||||
|
createInvite: (req, res, next) ->
|
||||||
|
teamManagerId = AuthenticationController.getLoggedInUserId(req)
|
||||||
|
email = EmailHelper.parseEmail(req.body.email)
|
||||||
|
if !email?
|
||||||
|
return res.sendStatus(400)
|
||||||
|
|
||||||
|
TeamInvitesHandler.createInvite teamManagerId, email, (err, invite) ->
|
||||||
|
return next(err) if err?
|
||||||
|
inviteView = { user:
|
||||||
|
{ email: invite.email, sentAt: invite.sentAt, invite: true }
|
||||||
|
}
|
||||||
|
res.json inviteView
|
||||||
|
|
||||||
|
viewInvite: (req, res, next) ->
|
||||||
|
token = req.params.token
|
||||||
|
userId = AuthenticationController.getLoggedInUserId(req)
|
||||||
|
|
||||||
|
TeamInvitesHandler.getInvite token, (err, invite, teamSubscription) ->
|
||||||
|
return next(err) if err?
|
||||||
|
|
||||||
|
if !invite
|
||||||
|
return ErrorController.notFound(req, res, next)
|
||||||
|
|
||||||
|
SubscriptionLocator.getUsersSubscription userId, (err, personalSubscription) ->
|
||||||
|
return next(err) if err?
|
||||||
|
|
||||||
|
res.render "subscriptions/team/invite",
|
||||||
|
inviterName: invite.inviterName
|
||||||
|
inviteToken: invite.token
|
||||||
|
hasPersonalSubscription: personalSubscription?
|
||||||
|
appName: settings.appName
|
||||||
|
|
||||||
|
acceptInvite: (req, res, next) ->
|
||||||
|
token = req.params.token
|
||||||
|
userId = AuthenticationController.getLoggedInUserId(req)
|
||||||
|
|
||||||
|
TeamInvitesHandler.acceptInvite token, userId, (err, results) ->
|
||||||
|
return next(err) if err?
|
||||||
|
res.sendStatus 204
|
||||||
|
|
||||||
|
revokeInvite: (req, res) ->
|
||||||
|
email = EmailHelper.parseEmail(req.params.email)
|
||||||
|
teamManagerId = AuthenticationController.getLoggedInUserId(req)
|
||||||
|
if !email?
|
||||||
|
return res.sendStatus(400)
|
||||||
|
|
||||||
|
TeamInvitesHandler.revokeInvite teamManagerId, email, (err, results) ->
|
||||||
|
return next(err) if err?
|
||||||
|
res.sendStatus 204
|
|
@ -0,0 +1,161 @@
|
||||||
|
logger = require("logger-sharelatex")
|
||||||
|
crypto = require("crypto")
|
||||||
|
async = require("async")
|
||||||
|
|
||||||
|
settings = require("settings-sharelatex")
|
||||||
|
ObjectId = require("mongojs").ObjectId
|
||||||
|
|
||||||
|
TeamInvite = require("../../models/TeamInvite").TeamInvite
|
||||||
|
Subscription = require("../../models/Subscription").Subscription
|
||||||
|
|
||||||
|
UserGetter = require("../User/UserGetter")
|
||||||
|
SubscriptionLocator = require("./SubscriptionLocator")
|
||||||
|
SubscriptionUpdater = require("./SubscriptionUpdater")
|
||||||
|
LimitationsManager = require("./LimitationsManager")
|
||||||
|
|
||||||
|
EmailHandler = require("../Email/EmailHandler")
|
||||||
|
EmailHelper = require("../Helpers/EmailHelper")
|
||||||
|
|
||||||
|
Errors = require "../Errors/Errors"
|
||||||
|
|
||||||
|
module.exports = TeamInvitesHandler =
|
||||||
|
getInvite: (token, callback) ->
|
||||||
|
Subscription.findOne 'teamInvites.token': token, (err, subscription) ->
|
||||||
|
return callback(err) if err?
|
||||||
|
return callback(new Errors.NotFoundError('team not found')) unless subscription?
|
||||||
|
|
||||||
|
invite = subscription.teamInvites.find (i) -> i.token == token
|
||||||
|
return callback(null, invite, subscription)
|
||||||
|
|
||||||
|
createInvite: (teamManagerId, email, callback) ->
|
||||||
|
email = EmailHelper.parseEmail(email)
|
||||||
|
return callback(new Error('invalid email')) if !email?
|
||||||
|
logger.log {teamManagerId, email}, "Creating manager team invite"
|
||||||
|
UserGetter.getUser teamManagerId, (error, teamManager) ->
|
||||||
|
return callback(error) if error?
|
||||||
|
|
||||||
|
SubscriptionLocator.getUsersSubscription teamManagerId, (error, subscription) ->
|
||||||
|
return callback(error) if error?
|
||||||
|
|
||||||
|
if teamManager.first_name and teamManager.last_name
|
||||||
|
inviterName = "#{teamManager.first_name} #{teamManager.last_name} (#{teamManager.email})"
|
||||||
|
else
|
||||||
|
inviterName = teamManager.email
|
||||||
|
|
||||||
|
removeLegacyInvite subscription.id, email, (error) ->
|
||||||
|
return callback(error) if error?
|
||||||
|
createInvite(subscription, email, inviterName, callback)
|
||||||
|
|
||||||
|
createDomainInvite: (user, licence, callback) ->
|
||||||
|
email = EmailHelper.parseEmail(user.email)
|
||||||
|
return callback(new Error('invalid email')) if !email?
|
||||||
|
logger.log {licence, email: email}, "Creating domain team invite"
|
||||||
|
inviterName = licence.name.replace(/\s+licence$/i, licence.name)
|
||||||
|
|
||||||
|
SubscriptionLocator.getSubscription licence.subscription_id, (error, subscription) ->
|
||||||
|
return callback(error) if error?
|
||||||
|
createInvite(subscription, email, inviterName, callback)
|
||||||
|
|
||||||
|
acceptInvite: (token, userId, callback) ->
|
||||||
|
logger.log {userId}, "Accepting invite"
|
||||||
|
TeamInvitesHandler.getInvite token, (err, invite, subscription) ->
|
||||||
|
return callback(err) if err?
|
||||||
|
return callback(new Errors.NotFoundError('invite not found')) unless invite?
|
||||||
|
|
||||||
|
SubscriptionUpdater.addUserToGroup subscription.admin_id, userId, (err) ->
|
||||||
|
return callback(err) if err?
|
||||||
|
|
||||||
|
removeInviteFromTeam(subscription.id, invite.email, callback)
|
||||||
|
|
||||||
|
revokeInvite: (teamManagerId, email, callback) ->
|
||||||
|
email = EmailHelper.parseEmail(email)
|
||||||
|
return callback(new Error('invalid email')) if !email?
|
||||||
|
logger.log {teamManagerId, email}, "Revoking invite"
|
||||||
|
SubscriptionLocator.getUsersSubscription teamManagerId, (err, teamSubscription) ->
|
||||||
|
return callback(err) if err?
|
||||||
|
|
||||||
|
removeInviteFromTeam(teamSubscription.id, email, callback)
|
||||||
|
|
||||||
|
# Legacy method to allow a user to receive a confirmation email if their
|
||||||
|
# email is in Subscription.invited_emails when they join. We'll remove this
|
||||||
|
# after a short while.
|
||||||
|
createTeamInvitesForLegacyInvitedEmail: (email, callback) ->
|
||||||
|
SubscriptionLocator.getGroupsWithEmailInvite email, (err, teams) ->
|
||||||
|
return callback(err) if err?
|
||||||
|
|
||||||
|
async.map teams,
|
||||||
|
(team, cb) -> TeamInvitesHandler.createInvite(team.admin_id, email, cb)
|
||||||
|
, callback
|
||||||
|
|
||||||
|
createInvite = (subscription, email, inviterName, callback) ->
|
||||||
|
logger.log {subscriptionId: subscription.id, email, inviterName}, "Creating invite"
|
||||||
|
checkIfInviteIsPossible subscription, email, (error, possible, reason) ->
|
||||||
|
return callback(error) if error?
|
||||||
|
return callback(reason) unless possible
|
||||||
|
|
||||||
|
|
||||||
|
invite = subscription.teamInvites.find (invite) -> invite.email == email
|
||||||
|
|
||||||
|
if !invite?
|
||||||
|
invite = {
|
||||||
|
email: email
|
||||||
|
inviterName: inviterName
|
||||||
|
token: crypto.randomBytes(32).toString("hex")
|
||||||
|
sentAt: new Date()
|
||||||
|
}
|
||||||
|
subscription.teamInvites.push(invite)
|
||||||
|
else
|
||||||
|
invite.sentAt = new Date()
|
||||||
|
|
||||||
|
subscription.save (error) ->
|
||||||
|
return callback(error) if error?
|
||||||
|
|
||||||
|
opts =
|
||||||
|
to: email
|
||||||
|
inviterName: inviterName
|
||||||
|
acceptInviteUrl: "#{settings.siteUrl}/subscription/invites/#{invite.token}/"
|
||||||
|
appName: settings.appName
|
||||||
|
EmailHandler.sendEmail "verifyEmailToJoinTeam", opts, (error) ->
|
||||||
|
return callback(error, invite)
|
||||||
|
|
||||||
|
removeInviteFromTeam = (subscriptionId, email, callback) ->
|
||||||
|
searchConditions = { _id: new ObjectId(subscriptionId.toString()) }
|
||||||
|
removeInvite = { $pull: { teamInvites: { email: email } } }
|
||||||
|
logger.log {subscriptionId, email, searchConditions, removeInvite}, 'removeInviteFromTeam'
|
||||||
|
|
||||||
|
async.series [
|
||||||
|
(cb) -> Subscription.update(searchConditions, removeInvite, cb),
|
||||||
|
(cb) -> removeLegacyInvite(subscriptionId, email, cb),
|
||||||
|
], callback
|
||||||
|
|
||||||
|
removeLegacyInvite = (subscriptionId, email, callback) ->
|
||||||
|
Subscription.update({
|
||||||
|
_id: new ObjectId(subscriptionId.toString())
|
||||||
|
}, {
|
||||||
|
$pull: {
|
||||||
|
invited_emails: email
|
||||||
|
}
|
||||||
|
}, callback)
|
||||||
|
|
||||||
|
checkIfInviteIsPossible = (subscription, email, callback = (error, possible, reason) -> ) ->
|
||||||
|
unless subscription.groupPlan
|
||||||
|
logger.log {subscriptionId: subscription.id},
|
||||||
|
"can not add members to a subscription that is not in a group plan"
|
||||||
|
return callback(null, false, wrongPlan: true)
|
||||||
|
|
||||||
|
if LimitationsManager.teamHasReachedMemberLimit(subscription)
|
||||||
|
logger.log {subscriptionId: subscription.id}, "team has reached member limit"
|
||||||
|
return callback(null, false, limitReached: true)
|
||||||
|
|
||||||
|
UserGetter.getUserByAnyEmail email, (error, existingUser) ->
|
||||||
|
return callback(error) if error?
|
||||||
|
return callback(null, true) unless existingUser?
|
||||||
|
|
||||||
|
existingMember = subscription.member_ids.find (memberId) ->
|
||||||
|
memberId.toString() == existingUser._id.toString()
|
||||||
|
|
||||||
|
if existingMember
|
||||||
|
logger.log {subscriptionId: subscription.id, email}, "user already in team"
|
||||||
|
return callback(null, false, alreadyInTeam: true)
|
||||||
|
else
|
||||||
|
return callback(null, true)
|
|
@ -110,9 +110,9 @@ module.exports = UserController =
|
||||||
logger.err err:err, user_id:user_id, "error getting user for email update"
|
logger.err err:err, user_id:user_id, "error getting user for email update"
|
||||||
return res.send 500
|
return res.send 500
|
||||||
AuthenticationController.setInSessionUser(req, {email: user.email, first_name: user.first_name, last_name: user.last_name})
|
AuthenticationController.setInSessionUser(req, {email: user.email, first_name: user.first_name, last_name: user.last_name})
|
||||||
UserHandler.populateGroupLicenceInvite user, (err)-> #need to refresh this in the background
|
UserHandler.populateTeamInvites user, (err)-> #need to refresh this in the background
|
||||||
if err?
|
if err?
|
||||||
logger.err err:err, "error populateGroupLicenceInvite"
|
logger.err err:err, "error populateTeamInvites"
|
||||||
res.sendStatus(200)
|
res.sendStatus(200)
|
||||||
|
|
||||||
logout : (req, res)->
|
logout : (req, res)->
|
||||||
|
|
|
@ -1,16 +1,19 @@
|
||||||
SubscriptionDomainHandler = require("../Subscription/SubscriptionDomainHandler")
|
SubscriptionDomainHandler = require("../Subscription/SubscriptionDomainHandler")
|
||||||
NotificationsBuilder = require("../Notifications/NotificationsBuilder")
|
NotificationsBuilder = require("../Notifications/NotificationsBuilder")
|
||||||
SubscriptionGroupHandler = require("../Subscription/SubscriptionGroupHandler")
|
SubscriptionGroupHandler = require("../Subscription/SubscriptionGroupHandler")
|
||||||
|
TeamInvitesHandler = require("../Subscription/TeamInvitesHandler")
|
||||||
logger = require("logger-sharelatex")
|
logger = require("logger-sharelatex")
|
||||||
|
|
||||||
|
|
||||||
module.exports = UserHandler =
|
module.exports = UserHandler =
|
||||||
|
|
||||||
populateGroupLicenceInvite: (user, callback = ->)->
|
populateTeamInvites: (user, callback) ->
|
||||||
SubscriptionGroupHandler.convertEmailInvitesToMemberships user.email, user._id, (err) ->
|
UserHandler.notifyDomainLicence user, (err) ->
|
||||||
return callback(err) if err?
|
return callback(err) if err?
|
||||||
|
TeamInvitesHandler.createTeamInvitesForLegacyInvitedEmail(user.email, callback)
|
||||||
|
|
||||||
logger.log user_id:user._id, "populating any potential group licence invites"
|
notifyDomainLicence: (user, callback = ->)->
|
||||||
|
logger.log user_id:user._id, "notiying user about a potential domain licence"
|
||||||
licence = SubscriptionDomainHandler.getLicenceUserCanJoin user
|
licence = SubscriptionDomainHandler.getLicenceUserCanJoin user
|
||||||
if !licence?
|
if !licence?
|
||||||
return callback()
|
return callback()
|
||||||
|
@ -19,11 +22,10 @@ module.exports = UserHandler =
|
||||||
if err?
|
if err?
|
||||||
return callback(err)
|
return callback(err)
|
||||||
else if alreadyPartOfGroup
|
else if alreadyPartOfGroup
|
||||||
logger.log user_id:user._id, "user already part of group, not creating notifcation for them"
|
logger.log user_id:user._id, "user already part of team, not creating notifcation for them"
|
||||||
return callback()
|
return callback()
|
||||||
else
|
else
|
||||||
NotificationsBuilder.groupPlan(user, licence).create(callback)
|
NotificationsBuilder.groupPlan(user, licence).create(callback)
|
||||||
|
|
||||||
setupLoginData: (user, callback = ->)->
|
setupLoginData: (user, callback = ->)->
|
||||||
@populateGroupLicenceInvite user, callback
|
@populateTeamInvites user, callback
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
mongoose = require 'mongoose'
|
mongoose = require 'mongoose'
|
||||||
Settings = require 'settings-sharelatex'
|
Settings = require 'settings-sharelatex'
|
||||||
|
TeamInviteSchema = require('./TeamInvite').TeamInviteSchema
|
||||||
|
|
||||||
Schema = mongoose.Schema
|
Schema = mongoose.Schema
|
||||||
ObjectId = Schema.ObjectId
|
ObjectId = Schema.ObjectId
|
||||||
|
@ -8,6 +9,7 @@ SubscriptionSchema = new Schema
|
||||||
admin_id : {type:ObjectId, ref:'User', index: {unique: true, dropDups: true}}
|
admin_id : {type:ObjectId, ref:'User', index: {unique: true, dropDups: true}}
|
||||||
member_ids : [ type:ObjectId, ref:'User' ]
|
member_ids : [ type:ObjectId, ref:'User' ]
|
||||||
invited_emails: [ String ]
|
invited_emails: [ String ]
|
||||||
|
teamInvites : [ TeamInviteSchema ]
|
||||||
recurlySubscription_id : String
|
recurlySubscription_id : String
|
||||||
planCode : {type: String}
|
planCode : {type: String}
|
||||||
groupPlan : {type: Boolean, default: false}
|
groupPlan : {type: Boolean, default: false}
|
||||||
|
|
15
services/web/app/coffee/models/TeamInvite.coffee
Normal file
15
services/web/app/coffee/models/TeamInvite.coffee
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
mongoose = require 'mongoose'
|
||||||
|
Settings = require 'settings-sharelatex'
|
||||||
|
|
||||||
|
Schema = mongoose.Schema
|
||||||
|
ObjectId = Schema.ObjectId
|
||||||
|
|
||||||
|
TeamInviteSchema = new Schema
|
||||||
|
email : { type: String, required: true }
|
||||||
|
token : { type: String }
|
||||||
|
inviterName : { type: String }
|
||||||
|
sentAt : { type: Date }
|
||||||
|
|
||||||
|
mongoose.model 'TeamInvite', TeamInviteSchema
|
||||||
|
exports.TeamInvite = mongoose.model 'TeamInvite'
|
||||||
|
exports.TeamInviteSchema = TeamInviteSchema
|
39
services/web/app/views/subscriptions/domain/join.pug
Normal file
39
services/web/app/views/subscriptions/domain/join.pug
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
extends ../../layout
|
||||||
|
|
||||||
|
block scripts
|
||||||
|
script(type='text/javascript').
|
||||||
|
window.group_subscription_id = '#{group_subscription_id}'
|
||||||
|
window.has_personal_subscription = #{has_personal_subscription}
|
||||||
|
|
||||||
|
block content
|
||||||
|
.content.content-alt
|
||||||
|
.container
|
||||||
|
.row
|
||||||
|
.col-md-8.col-md-offset-2
|
||||||
|
-if (query.expired)
|
||||||
|
.alert.alert-warning #{translate("email_link_expired")}
|
||||||
|
|
||||||
|
.row.row-spaced
|
||||||
|
.col-md-8.col-md-offset-2.text-center(ng-cloak)
|
||||||
|
.card(ng-controller="DomainSubscriptionJoinController")
|
||||||
|
.page-header
|
||||||
|
h1.text-centered #{translate("you_are_invited_to_group", {groupName:licenceName})}
|
||||||
|
|
||||||
|
div(ng-show="view =='personalSubscription'")
|
||||||
|
p #{translate("cancel_personal_subscription_first")}
|
||||||
|
p
|
||||||
|
a.btn.btn.btn-default(ng-click="keepPersonalSubscription()", ng-disabled="inflight") #{translate("not_now")}
|
||||||
|
|
|
||||||
|
a.btn.btn.btn-primary(ng-click="cancelSubscription()", ng-disabled="inflight") #{translate("cancel_your_subscription")}
|
||||||
|
|
||||||
|
div(ng-show="view =='domainSubscriptionJoin'")
|
||||||
|
p #{translate("group_provides_you_with_premium_account", {groupName:licenceName})}
|
||||||
|
p
|
||||||
|
a.btn.btn-default(href="/project") #{translate("not_now")}
|
||||||
|
|
|
||||||
|
a.btn.btn.btn-primary(ng-click="joinGroup()", ng-disabled="inflight") #{translate("verify_email_address")}
|
||||||
|
|
||||||
|
div(ng-show="view =='requestSent'")
|
||||||
|
p #{translate("check_email_to_complete_the_upgrade")}
|
||||||
|
p
|
||||||
|
a.btn.btn.btn-primary(href="/project") #{translate("done")}
|
|
@ -1,54 +0,0 @@
|
||||||
extends ../../layout
|
|
||||||
|
|
||||||
block scripts
|
|
||||||
script(type='text/javascript').
|
|
||||||
window.group_subscription_id = '#{group_subscription_id}'
|
|
||||||
window.has_personal_subscription = #{has_personal_subscription}
|
|
||||||
|
|
||||||
block content
|
|
||||||
.content.content-alt
|
|
||||||
.container
|
|
||||||
.row
|
|
||||||
.col-md-8.col-md-offset-2
|
|
||||||
-if (query.expired)
|
|
||||||
.alert.alert-warning #{translate("email_link_expired")}
|
|
||||||
|
|
||||||
.row
|
|
||||||
div
|
|
||||||
.row
|
|
||||||
.col-md-8.col-md-offset-2(ng-cloak)
|
|
||||||
.card(ng-controller="GroupSubscriptionInviteController")
|
|
||||||
.page-header
|
|
||||||
h1.text-centered #{translate("you_are_invited_to_group", {groupName:licenceName})}
|
|
||||||
|
|
||||||
div(ng-show="view =='personalSubscription'").row.text-centered
|
|
||||||
div #{translate("cancel_personal_subscription_first")}
|
|
||||||
.row
|
|
||||||
.col-md-12
|
|
||||||
.row
|
|
||||||
.col-md-12
|
|
||||||
a.btn.btn.btn-default(ng-click="keepPersonalSubscription()", ng-disabled="inflight") #{translate("not_now")}
|
|
||||||
span
|
|
||||||
a.btn.btn.btn-primary(ng-click="cancelSubscription()", ng-disabled="inflight") #{translate("cancel_your_subscription")}
|
|
||||||
|
|
||||||
div(ng-show="view =='groupSubscriptionInvite'").row.text-centered
|
|
||||||
.row
|
|
||||||
.col-md-12 #{translate("group_provides_you_with_premium_account", {groupName:licenceName})}
|
|
||||||
.row
|
|
||||||
.col-md-12
|
|
||||||
.row
|
|
||||||
.col-md-12
|
|
||||||
.text-center
|
|
||||||
a.btn.btn-default(href="/project") #{translate("not_now")}
|
|
||||||
span
|
|
||||||
a.btn.btn.btn-primary(ng-click="joinGroup()", ng-disabled="inflight") #{translate("verify_email_address")}
|
|
||||||
|
|
||||||
|
|
||||||
span(ng-show="view =='requestSent'").row.text-centered.text-center
|
|
||||||
.row
|
|
||||||
.col-md-12 #{translate("check_email_to_complete_the_upgrade")}
|
|
||||||
.row
|
|
||||||
.col-md-12
|
|
||||||
.row
|
|
||||||
.col-md-12
|
|
||||||
a.btn.btn.btn-primary(href="/project") #{translate("done")}
|
|
|
@ -1,25 +0,0 @@
|
||||||
extends ../../layout
|
|
||||||
|
|
||||||
block scripts
|
|
||||||
script(type='text/javascript').
|
|
||||||
window.subscription_id = '#{subscription_id}'
|
|
||||||
|
|
||||||
block content
|
|
||||||
.content.content-alt
|
|
||||||
.container
|
|
||||||
.row
|
|
||||||
.col-md-8.col-md-offset-2(ng-cloak)
|
|
||||||
.card
|
|
||||||
.page-header.row.text-centered
|
|
||||||
h1 #{translate("you_have_joined", {groupName:licenceName})}
|
|
||||||
div(ng-show="!requestSent").row.text-centered
|
|
||||||
.row
|
|
||||||
.span-md-12 #{translate("claim_premium_account", {groupName:licenceName})}
|
|
||||||
div
|
|
||||||
.row
|
|
||||||
.col-md-12
|
|
||||||
.row
|
|
||||||
.span-md-12
|
|
||||||
a.btn.btn-success(href="/project") #{translate("done")}
|
|
||||||
|
|
||||||
|
|
|
@ -32,7 +32,7 @@ block content
|
||||||
.col-md-5
|
.col-md-5
|
||||||
span.header #{translate("name")}
|
span.header #{translate("name")}
|
||||||
.col-md-2
|
.col-md-2
|
||||||
span.header #{translate("registered")}
|
span.header #{translate("accepted_invite")}
|
||||||
li.container-fluid(
|
li.container-fluid(
|
||||||
ng-repeat="user in users | orderBy:'email':true",
|
ng-repeat="user in users | orderBy:'email':true",
|
||||||
ng-controller="SubscriptionGroupMemberListItemController"
|
ng-controller="SubscriptionGroupMemberListItemController"
|
||||||
|
@ -49,8 +49,8 @@ block content
|
||||||
span.name {{ user.first_name }} {{ user.last_name }}
|
span.name {{ user.first_name }} {{ user.last_name }}
|
||||||
.col-md-2
|
.col-md-2
|
||||||
span.registered
|
span.registered
|
||||||
i.fa.fa-check.text-success(ng-show="!user.holdingAccount")
|
i.fa.fa-check.text-success(ng-show="!user.invite")
|
||||||
i.fa.fa-times(ng-show="user.holdingAccount")
|
i.fa.fa-times(ng-show="user.invite")
|
||||||
li(
|
li(
|
||||||
ng-if="users.length == 0",
|
ng-if="users.length == 0",
|
||||||
ng-cloak
|
ng-cloak
|
||||||
|
|
40
services/web/app/views/subscriptions/team/invite.pug
Normal file
40
services/web/app/views/subscriptions/team/invite.pug
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
extends ../../layout
|
||||||
|
|
||||||
|
block scripts
|
||||||
|
script(type='text/javascript').
|
||||||
|
window.teamId = '#{teamId}'
|
||||||
|
window.hasPersonalSubscription = #{hasPersonalSubscription}
|
||||||
|
window.inviteToken = '#{inviteToken}'
|
||||||
|
|
||||||
|
block content
|
||||||
|
.content.content-alt.team-invite
|
||||||
|
.container
|
||||||
|
.row
|
||||||
|
.col-md-8.col-md-offset-2
|
||||||
|
-if (query.expired)
|
||||||
|
.alert.alert-warning #{translate("email_link_expired")}
|
||||||
|
|
||||||
|
.row.row-spaced
|
||||||
|
.col-md-8.col-md-offset-2.text-center(ng-cloak)
|
||||||
|
.card(ng-controller="TeamInviteController")
|
||||||
|
.page-header
|
||||||
|
h1.text-centered #{translate("invited_to_group", {inviterName: inviterName, appName: appName})}
|
||||||
|
|
||||||
|
div(ng-show="view =='personalSubscription'")
|
||||||
|
p #{translate("cancel_personal_subscription_first")}
|
||||||
|
p
|
||||||
|
a.btn.btn.btn-default(ng-click="keepPersonalSubscription()", ng-disabled="inflight") #{translate("not_now")}
|
||||||
|
|
|
||||||
|
a.btn.btn.btn-primary(ng-click="cancelPersonalSubscription()", ng-disabled="inflight") #{translate("cancel_your_subscription")}
|
||||||
|
|
||||||
|
div(ng-show="view =='teamInvite'")
|
||||||
|
p #{translate("join_team_explanation", {appName: appName})}
|
||||||
|
p
|
||||||
|
a.btn.btn-default(href="/project") #{translate("not_now")}
|
||||||
|
|
|
||||||
|
a.btn.btn.btn-primary(ng-click="joinTeam()", ng-disabled="inflight") #{translate("accept_invitation")}
|
||||||
|
|
||||||
|
div(ng-show="view =='inviteAccepted'")
|
||||||
|
p #{translate("joined_team", {inviterName: inviterName})}
|
||||||
|
p
|
||||||
|
a.btn.btn.btn-primary(href="/project") #{translate("done")}
|
|
@ -16,7 +16,8 @@ define [
|
||||||
"main/annual-upgrade"
|
"main/annual-upgrade"
|
||||||
"main/announcements"
|
"main/announcements"
|
||||||
"main/register-users"
|
"main/register-users"
|
||||||
"main/subscription/group-subscription-invite-controller"
|
"main/subscription/domain-subscription-join-controller"
|
||||||
|
"main/subscription/team-invite-controller"
|
||||||
"main/contact-us"
|
"main/contact-us"
|
||||||
"main/learn"
|
"main/learn"
|
||||||
"analytics/AbTestingManager"
|
"analytics/AbTestingManager"
|
||||||
|
|
|
@ -22,7 +22,7 @@ define [
|
||||||
emails = parseEmails($scope.inputs.emails)
|
emails = parseEmails($scope.inputs.emails)
|
||||||
for email in emails
|
for email in emails
|
||||||
queuedHttp
|
queuedHttp
|
||||||
.post("/subscription/group/user", {
|
.post("/subscription/invites", {
|
||||||
email: email,
|
email: email,
|
||||||
_csrf: window.csrfToken
|
_csrf: window.csrfToken
|
||||||
})
|
})
|
||||||
|
@ -34,8 +34,8 @@ define [
|
||||||
$scope.removeMembers = () ->
|
$scope.removeMembers = () ->
|
||||||
for user in $scope.selectedUsers
|
for user in $scope.selectedUsers
|
||||||
do (user) ->
|
do (user) ->
|
||||||
if user.holdingAccount and !user._id?
|
if user.invite and !user._id?
|
||||||
url = "/subscription/group/email/#{encodeURIComponent(user.email)}"
|
url = "/subscription/invites/#{encodeURIComponent(user.email)}"
|
||||||
else
|
else
|
||||||
url = "/subscription/group/user/#{user._id}"
|
url = "/subscription/group/user/#{user._id}"
|
||||||
queuedHttp({
|
queuedHttp({
|
||||||
|
|
|
@ -1,31 +1,31 @@
|
||||||
define [
|
define [
|
||||||
"base"
|
"base"
|
||||||
], (App) ->
|
], (App) ->
|
||||||
App.controller "GroupSubscriptionInviteController", ($scope, $http) ->
|
App.controller "DomainSubscriptionJoinController", ($scope, $http) ->
|
||||||
|
|
||||||
$scope.inflight = false
|
$scope.inflight = false
|
||||||
|
|
||||||
if has_personal_subscription
|
if has_personal_subscription
|
||||||
$scope.view = "personalSubscription"
|
$scope.view = "personalSubscription"
|
||||||
else
|
else
|
||||||
$scope.view = "groupSubscriptionInvite"
|
$scope.view = "domainSubscriptionJoin"
|
||||||
|
|
||||||
$scope.keepPersonalSubscription = ->
|
$scope.keepPersonalSubscription = ->
|
||||||
$scope.view = "groupSubscriptionInvite"
|
$scope.view = "domainSubscriptionJoin"
|
||||||
|
|
||||||
$scope.cancelSubscription = ->
|
$scope.cancelSubscription = ->
|
||||||
$scope.inflight = true
|
$scope.inflight = true
|
||||||
request = $http.post "/user/subscription/cancel", {_csrf:window.csrfToken}
|
request = $http.post "/user/subscription/cancel", {_csrf:window.csrfToken}
|
||||||
request.then ()->
|
request.then ()->
|
||||||
$scope.inflight = false
|
$scope.inflight = false
|
||||||
$scope.view = "groupSubscriptionInvite"
|
$scope.view = "domainSubscriptionJoin"
|
||||||
request.catch ()->
|
request.catch ()->
|
||||||
console.log "the request failed"
|
console.log "the request failed"
|
||||||
|
|
||||||
$scope.joinGroup = ->
|
$scope.joinGroup = ->
|
||||||
$scope.view = "requestSent"
|
$scope.view = "requestSent"
|
||||||
$scope.inflight = true
|
$scope.inflight = true
|
||||||
request = $http.post "/user/subscription/#{group_subscription_id}/group/begin-join", {_csrf:window.csrfToken}
|
request = $http.post "/user/subscription/domain/join", {_csrf:window.csrfToken}
|
||||||
request.then (response)->
|
request.then (response)->
|
||||||
{ status } = response
|
{ status } = response
|
||||||
$scope.inflight = false
|
$scope.inflight = false
|
|
@ -0,0 +1,35 @@
|
||||||
|
define [
|
||||||
|
"base"
|
||||||
|
], (App) ->
|
||||||
|
App.controller "TeamInviteController", ($scope, $http) ->
|
||||||
|
|
||||||
|
$scope.inflight = false
|
||||||
|
|
||||||
|
if hasPersonalSubscription
|
||||||
|
$scope.view = "personalSubscription"
|
||||||
|
else
|
||||||
|
$scope.view = "teamInvite"
|
||||||
|
|
||||||
|
$scope.keepPersonalSubscription = ->
|
||||||
|
$scope.view = "teamInvite"
|
||||||
|
|
||||||
|
$scope.cancelPersonalSubscription = ->
|
||||||
|
$scope.inflight = true
|
||||||
|
request = $http.post "/user/subscription/cancel", {_csrf:window.csrfToken}
|
||||||
|
request.then ()->
|
||||||
|
$scope.inflight = false
|
||||||
|
$scope.view = "teamInvite"
|
||||||
|
request.catch ()->
|
||||||
|
console.log "the request failed"
|
||||||
|
|
||||||
|
$scope.joinTeam = ->
|
||||||
|
$scope.inflight = true
|
||||||
|
request = $http.put "/subscription/invites/#{window.inviteToken}/", {_csrf:window.csrfToken}
|
||||||
|
request.then (response)->
|
||||||
|
{ status } = response
|
||||||
|
$scope.inflight = false
|
||||||
|
$scope.view = "inviteAccepted"
|
||||||
|
if status != 200 # assume request worked
|
||||||
|
$scope.requestSent = false
|
||||||
|
request.catch ()->
|
||||||
|
console.log "the request failed"
|
|
@ -73,6 +73,10 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.team-invite .message {
|
||||||
|
margin: 3em 0;
|
||||||
|
}
|
||||||
|
|
||||||
.capitalised {
|
.capitalised {
|
||||||
text-transform:capitalize;
|
text-transform:capitalize;
|
||||||
}
|
}
|
|
@ -286,7 +286,9 @@ describe "LimitationsManager", ->
|
||||||
@subscription =
|
@subscription =
|
||||||
membersLimit: 3
|
membersLimit: 3
|
||||||
member_ids: ["", ""]
|
member_ids: ["", ""]
|
||||||
invited_emails: ["bob@example.com"]
|
teamInvites: [
|
||||||
|
{ email: "bob@example.com", sentAt: new Date(), token: "hey" }
|
||||||
|
]
|
||||||
|
|
||||||
it "should return true if the limit is hit (including members and invites)", (done)->
|
it "should return true if the limit is hit (including members and invites)", (done)->
|
||||||
@SubscriptionLocator.getUsersSubscription.callsArgWith(1, null, @subscription)
|
@SubscriptionLocator.getUsersSubscription.callsArgWith(1, null, @subscription)
|
||||||
|
|
|
@ -24,10 +24,7 @@ describe "SubscriptionGroupController", ->
|
||||||
@GroupHandler =
|
@GroupHandler =
|
||||||
addUserToGroup: sinon.stub().callsArgWith(2, null, @user)
|
addUserToGroup: sinon.stub().callsArgWith(2, null, @user)
|
||||||
removeUserFromGroup: sinon.stub().callsArgWith(2)
|
removeUserFromGroup: sinon.stub().callsArgWith(2)
|
||||||
removeEmailInviteFromGroup: sinon.stub().callsArgWith(2)
|
|
||||||
isUserPartOfGroup: sinon.stub()
|
isUserPartOfGroup: sinon.stub()
|
||||||
sendVerificationEmail:sinon.stub()
|
|
||||||
processGroupVerification:sinon.stub()
|
|
||||||
getPopulatedListOfMembers: sinon.stub().callsArgWith(1, null, [@user])
|
getPopulatedListOfMembers: sinon.stub().callsArgWith(1, null, [@user])
|
||||||
@SubscriptionLocator = getUsersSubscription: sinon.stub().callsArgWith(1, null, @subscription)
|
@SubscriptionLocator = getUsersSubscription: sinon.stub().callsArgWith(1, null, @subscription)
|
||||||
@AuthenticationController =
|
@AuthenticationController =
|
||||||
|
@ -80,16 +77,6 @@ describe "SubscriptionGroupController", ->
|
||||||
done()
|
done()
|
||||||
@Controller.removeUserFromGroup @req, res
|
@Controller.removeUserFromGroup @req, res
|
||||||
|
|
||||||
describe "removeEmailInviteFromGroup", ->
|
|
||||||
it "should use the admin id for the logged in user and take the email from the params", (done)->
|
|
||||||
email = "jo@example.com"
|
|
||||||
@req.params = email: email
|
|
||||||
res =
|
|
||||||
send : =>
|
|
||||||
@GroupHandler.removeEmailInviteFromGroup.calledWith(@adminUserId, email).should.equal true
|
|
||||||
done()
|
|
||||||
@Controller.removeEmailInviteFromGroup @req, res
|
|
||||||
|
|
||||||
describe "renderSubscriptionGroupAdminPage", ->
|
describe "renderSubscriptionGroupAdminPage", ->
|
||||||
it "should redirect you if you don't have a group account", (done)->
|
it "should redirect you if you don't have a group account", (done)->
|
||||||
@subscription.groupPlan = false
|
@subscription.groupPlan = false
|
||||||
|
@ -109,97 +96,6 @@ describe "SubscriptionGroupController", ->
|
||||||
done()
|
done()
|
||||||
@Controller.renderSubscriptionGroupAdminPage @req, res
|
@Controller.renderSubscriptionGroupAdminPage @req, res
|
||||||
|
|
||||||
describe "renderGroupInvitePage", ->
|
|
||||||
describe "with a valid licence", ->
|
|
||||||
beforeEach ->
|
|
||||||
@SubscriptionDomainHandler.findDomainLicenceBySubscriptionId.returns({subscription_id:@subscription_id, adminUser_id:@adminUserId})
|
|
||||||
|
|
||||||
it "should render subscriptions/group/invite if not part of group", (done)->
|
|
||||||
@GroupHandler.isUserPartOfGroup.callsArgWith(2, null, false)
|
|
||||||
res =
|
|
||||||
render : (pageName)=>
|
|
||||||
pageName.should.equal "subscriptions/group/invite"
|
|
||||||
done()
|
|
||||||
@Controller.renderGroupInvitePage @req, res
|
|
||||||
|
|
||||||
it "should redirect to custom page if is already part of group", (done)->
|
|
||||||
@GroupHandler.isUserPartOfGroup.callsArgWith(2, null, true)
|
|
||||||
res =
|
|
||||||
redirect : (location)=>
|
|
||||||
location.should.equal "/user/subscription/custom_account"
|
|
||||||
done()
|
|
||||||
@Controller.renderGroupInvitePage @req, res
|
|
||||||
|
|
||||||
describe "without a valid licence", ->
|
|
||||||
beforeEach ->
|
|
||||||
@SubscriptionDomainHandler.findDomainLicenceBySubscriptionId.returns(undefined)
|
|
||||||
|
|
||||||
it "should send a 500", (done)->
|
|
||||||
@Controller.renderGroupInvitePage @req, {}
|
|
||||||
@ErrorsController.notFound.called.should.equal true
|
|
||||||
done()
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
describe "beginJoinGroup", ->
|
|
||||||
describe "with a valid licence", ->
|
|
||||||
beforeEach ->
|
|
||||||
@licenceName = "get amazing licence"
|
|
||||||
@SubscriptionDomainHandler.findDomainLicenceBySubscriptionId.returns({name:@licenceName})
|
|
||||||
@GroupHandler.sendVerificationEmail.callsArgWith(3)
|
|
||||||
|
|
||||||
it "should ask the SubscriptionGroupHandler to send the verification email", (done)->
|
|
||||||
res =
|
|
||||||
sendStatus : (statusCode)=>
|
|
||||||
statusCode.should.equal 200
|
|
||||||
@GroupHandler.sendVerificationEmail.calledWith(@subscription_id, @licenceName, @user_email).should.equal true
|
|
||||||
done()
|
|
||||||
@Controller.beginJoinGroup @req, res
|
|
||||||
|
|
||||||
describe "without a valid licence", ->
|
|
||||||
beforeEach ->
|
|
||||||
@SubscriptionDomainHandler.findDomainLicenceBySubscriptionId.returns(undefined)
|
|
||||||
|
|
||||||
it "should send a 500", (done)->
|
|
||||||
@Controller.beginJoinGroup @req, {}
|
|
||||||
@ErrorsController.notFound.called.should.equal true
|
|
||||||
done()
|
|
||||||
|
|
||||||
|
|
||||||
describe "completeJoin", ->
|
|
||||||
describe "with a valid licence", ->
|
|
||||||
beforeEach ->
|
|
||||||
@GroupHandler.processGroupVerification.callsArgWith(3)
|
|
||||||
@SubscriptionDomainHandler.findDomainLicenceBySubscriptionId.returns({name:@licenceName})
|
|
||||||
|
|
||||||
it "should redirect to the success page upon processGroupVerification", (done)->
|
|
||||||
@req.query.token = @token
|
|
||||||
res =
|
|
||||||
redirect : (location)=>
|
|
||||||
@GroupHandler.processGroupVerification.calledWith(@user_email, @subscription_id, @token).should.equal true
|
|
||||||
location.should.equal "/user/subscription/#{@subscription_id}/group/successful-join"
|
|
||||||
done()
|
|
||||||
@Controller.completeJoin @req, res
|
|
||||||
|
|
||||||
describe "without a valid licence", ->
|
|
||||||
|
|
||||||
it "should send a 500", (done)->
|
|
||||||
@SubscriptionDomainHandler.findDomainLicenceBySubscriptionId.returns(undefined)
|
|
||||||
@Controller.completeJoin @req, {}
|
|
||||||
@ErrorsController.notFound.called.should.equal true
|
|
||||||
done()
|
|
||||||
|
|
||||||
it "should redirect to the invited page with querystring if token was not found", (done)->
|
|
||||||
@SubscriptionDomainHandler.findDomainLicenceBySubscriptionId.returns({name:@licenceName})
|
|
||||||
@req.query.token = @token
|
|
||||||
@GroupHandler.processGroupVerification.callsArgWith(3, "token_not_found")
|
|
||||||
res =
|
|
||||||
redirect : (location)=>
|
|
||||||
location.should.equal "/user/subscription/#{@subscription_id}/group/invited?expired=true"
|
|
||||||
done()
|
|
||||||
@Controller.completeJoin @req, res
|
|
||||||
|
|
||||||
|
|
||||||
describe "exportGroupCsv", ->
|
describe "exportGroupCsv", ->
|
||||||
|
|
||||||
beforeEach ->
|
beforeEach ->
|
||||||
|
|
|
@ -30,8 +30,9 @@ describe "SubscriptionGroupHandler", ->
|
||||||
@SubscriptionUpdater =
|
@SubscriptionUpdater =
|
||||||
addUserToGroup: sinon.stub().callsArgWith(2)
|
addUserToGroup: sinon.stub().callsArgWith(2)
|
||||||
removeUserFromGroup: sinon.stub().callsArgWith(2)
|
removeUserFromGroup: sinon.stub().callsArgWith(2)
|
||||||
addEmailInviteToGroup: sinon.stub().callsArgWith(2)
|
|
||||||
removeEmailInviteFromGroup: sinon.stub().callsArgWith(2)
|
@TeamInvitesHandler =
|
||||||
|
createInvite: sinon.stub().callsArgWith(2)
|
||||||
|
|
||||||
@UserGetter =
|
@UserGetter =
|
||||||
getUser: sinon.stub()
|
getUser: sinon.stub()
|
||||||
|
@ -61,6 +62,7 @@ describe "SubscriptionGroupHandler", ->
|
||||||
"logger-sharelatex": log:->
|
"logger-sharelatex": log:->
|
||||||
"../User/UserCreator": @UserCreator
|
"../User/UserCreator": @UserCreator
|
||||||
"./SubscriptionUpdater": @SubscriptionUpdater
|
"./SubscriptionUpdater": @SubscriptionUpdater
|
||||||
|
"./TeamInvitesHandler": @TeamInvitesHandler
|
||||||
"./SubscriptionLocator": @SubscriptionLocator
|
"./SubscriptionLocator": @SubscriptionLocator
|
||||||
"../../models/Subscription": Subscription: @Subscription
|
"../../models/Subscription": Subscription: @Subscription
|
||||||
"../User/UserGetter": @UserGetter
|
"../User/UserGetter": @UserGetter
|
||||||
|
@ -111,7 +113,7 @@ describe "SubscriptionGroupHandler", ->
|
||||||
it "should add an email invite if no user is found", (done) ->
|
it "should add an email invite if no user is found", (done) ->
|
||||||
@UserGetter.getUserByAnyEmail.callsArgWith(1, null, null)
|
@UserGetter.getUserByAnyEmail.callsArgWith(1, null, null)
|
||||||
@Handler.addUserToGroup @adminUser_id, @newEmail, (err)=>
|
@Handler.addUserToGroup @adminUser_id, @newEmail, (err)=>
|
||||||
@SubscriptionUpdater.addEmailInviteToGroup.calledWith(@adminUser_id, @newEmail).should.equal true
|
@TeamInvitesHandler.createInvite.calledWith(@adminUser_id, @newEmail).should.equal true
|
||||||
done()
|
done()
|
||||||
|
|
||||||
describe "removeUserFromGroup", ->
|
describe "removeUserFromGroup", ->
|
||||||
|
@ -183,13 +185,18 @@ describe "SubscriptionGroupHandler", ->
|
||||||
done()
|
done()
|
||||||
|
|
||||||
it "should return any invited users", (done) ->
|
it "should return any invited users", (done) ->
|
||||||
@subscription.invited_emails = ["jo@example.com", "charlie@example.com"]
|
@subscription.invited_emails = [ "jo@example.com" ]
|
||||||
|
|
||||||
|
@subscription.teamInvites = [
|
||||||
|
{ email: "charlie@example.com" }
|
||||||
|
]
|
||||||
|
|
||||||
@Handler.getPopulatedListOfMembers @adminUser_id, (err, users)=>
|
@Handler.getPopulatedListOfMembers @adminUser_id, (err, users)=>
|
||||||
users[0].email.should.equal "jo@example.com"
|
users[0].email.should.equal "jo@example.com"
|
||||||
users[0].holdingAccount.should.equal true
|
users[0].invite.should.equal true
|
||||||
users[1].email.should.equal "charlie@example.com"
|
users[1].email.should.equal "charlie@example.com"
|
||||||
users[1].holdingAccount.should.equal true
|
users[1].invite.should.equal true
|
||||||
users.length.should.equal @subscription.invited_emails.length
|
users.length.should.equal @subscription.teamInvites.length + @subscription.invited_emails.length
|
||||||
done()
|
done()
|
||||||
|
|
||||||
describe "isUserPartOfGroup", ->
|
describe "isUserPartOfGroup", ->
|
||||||
|
@ -207,64 +214,3 @@ describe "SubscriptionGroupHandler", ->
|
||||||
@Handler.isUserPartOfGroup @user_id, @subscription_id, (err, partOfGroup)->
|
@Handler.isUserPartOfGroup @user_id, @subscription_id, (err, partOfGroup)->
|
||||||
partOfGroup.should.equal false
|
partOfGroup.should.equal false
|
||||||
done()
|
done()
|
||||||
|
|
||||||
|
|
||||||
describe "sendVerificationEmail", ->
|
|
||||||
beforeEach ->
|
|
||||||
@token = "secret token"
|
|
||||||
@subscription_id = "123ed13123"
|
|
||||||
@licenceName = "great licnece"
|
|
||||||
@email = "bob@smith.com"
|
|
||||||
@OneTimeTokenHandler.getNewToken.callsArgWith(2, null, @token)
|
|
||||||
@EmailHandler.sendEmail.callsArgWith(2)
|
|
||||||
|
|
||||||
it "should put a one time token into the email", (done)->
|
|
||||||
@Handler.sendVerificationEmail @subscription_id, @licenceName, @email, (err)=>
|
|
||||||
emailOpts = @EmailHandler.sendEmail.args[0][1]
|
|
||||||
emailOpts.completeJoinUrl.should.equal "#{@settings.siteUrl}/user/subscription/#{@subscription_id}/group/complete-join?token=#{@token}"
|
|
||||||
emailOpts.to.should.equal @email
|
|
||||||
emailOpts.group_name.should.equal @licenceName
|
|
||||||
done()
|
|
||||||
|
|
||||||
describe "processGroupVerification", ->
|
|
||||||
beforeEach ->
|
|
||||||
@token = "31dDAd2Da"
|
|
||||||
@SubscriptionLocator.getSubscription.callsArgWith(1, null, @subscription)
|
|
||||||
@Handler.addUserToGroup = sinon.stub().callsArgWith(2)
|
|
||||||
|
|
||||||
it "should addUserToGroup", (done)->
|
|
||||||
@OneTimeTokenHandler.getValueFromTokenAndExpire.callsArgWith(1, null, @subscription_id)
|
|
||||||
@Handler.processGroupVerification @email, @subscription_id, @token, (err)=>
|
|
||||||
@Handler.addUserToGroup.calledWith(@adminUser_id, @email).should.equal true
|
|
||||||
done()
|
|
||||||
|
|
||||||
it "should return token_not_found error if it couldn't get the token", (done)->
|
|
||||||
@OneTimeTokenHandler.getValueFromTokenAndExpire.callsArgWith(1)
|
|
||||||
@Handler.processGroupVerification @email, @subscription_id, @token, (err)=>
|
|
||||||
err.should.equal "token_not_found"
|
|
||||||
done()
|
|
||||||
|
|
||||||
describe "convertEmailInvitesToMemberships", ->
|
|
||||||
beforeEach ->
|
|
||||||
@SubscriptionLocator.getGroupsWithEmailInvite = sinon.stub().yields(null, @groups = [{ admin_id: "group-1" }, { admin_id: "group-2" }])
|
|
||||||
|
|
||||||
it "should get groups with the email address invited to", (done) ->
|
|
||||||
@Handler.convertEmailInvitesToMemberships @email, @user_id, (err) =>
|
|
||||||
@SubscriptionLocator.getGroupsWithEmailInvite.calledWith(@email).should.equal true
|
|
||||||
done()
|
|
||||||
|
|
||||||
it "should remove the email from each group", (done) ->
|
|
||||||
@Handler.convertEmailInvitesToMemberships @email, @user_id, (err) =>
|
|
||||||
for group in @groups
|
|
||||||
@SubscriptionUpdater.removeEmailInviteFromGroup
|
|
||||||
.calledWith(group.admin_id, @email)
|
|
||||||
.should.equal true
|
|
||||||
done()
|
|
||||||
|
|
||||||
it "should add the user to each group", (done) ->
|
|
||||||
@Handler.convertEmailInvitesToMemberships @email, @user_id, (err) =>
|
|
||||||
for group in @groups
|
|
||||||
@SubscriptionUpdater.addUserToGroup
|
|
||||||
.calledWith(group.admin_id, @user_id)
|
|
||||||
.should.equal true
|
|
||||||
done()
|
|
||||||
|
|
|
@ -0,0 +1,267 @@
|
||||||
|
SandboxedModule = require('sandboxed-module')
|
||||||
|
should = require('chai').should()
|
||||||
|
sinon = require 'sinon'
|
||||||
|
expect = require("chai").expect
|
||||||
|
querystring = require 'querystring'
|
||||||
|
modulePath = "../../../../app/js/Features/Subscription/TeamInvitesHandler"
|
||||||
|
|
||||||
|
ObjectId = require("mongojs").ObjectId
|
||||||
|
Errors = require("../../../../app/js/Features/Errors/Errors")
|
||||||
|
|
||||||
|
describe "TeamInvitesHandler", ->
|
||||||
|
beforeEach ->
|
||||||
|
@manager = {
|
||||||
|
id: "666666",
|
||||||
|
first_name: "Daenerys"
|
||||||
|
last_name: "Targaryen"
|
||||||
|
email: "daenerys@example.com"
|
||||||
|
}
|
||||||
|
|
||||||
|
@token = "aaaaaaaaaaaaaaaaaaaaaa"
|
||||||
|
|
||||||
|
@teamInvite = {
|
||||||
|
email: "jorah@example.com",
|
||||||
|
token: @token,
|
||||||
|
}
|
||||||
|
|
||||||
|
@subscription = {
|
||||||
|
id: "55153a8014829a865bbf700d",
|
||||||
|
admin_id: @manager.id,
|
||||||
|
groupPlan: true,
|
||||||
|
member_ids: [],
|
||||||
|
teamInvites: [ @teamInvite ],
|
||||||
|
save: sinon.stub().yields(null),
|
||||||
|
}
|
||||||
|
|
||||||
|
@SubscriptionLocator = {
|
||||||
|
getUsersSubscription: sinon.stub(),
|
||||||
|
getSubscription: sinon.stub().yields(null, @subscription)
|
||||||
|
}
|
||||||
|
|
||||||
|
@UserGetter = {
|
||||||
|
getUser: sinon.stub().yields(),
|
||||||
|
getUserByAnyEmail: sinon.stub().yields()
|
||||||
|
}
|
||||||
|
|
||||||
|
@SubscriptionUpdater = {
|
||||||
|
addUserToGroup: sinon.stub().yields()
|
||||||
|
}
|
||||||
|
|
||||||
|
@LimitationsManager = {
|
||||||
|
teamHasReachedMemberLimit: sinon.stub().returns(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Subscription = {
|
||||||
|
findOne: sinon.stub().yields()
|
||||||
|
update: sinon.stub().yields()
|
||||||
|
}
|
||||||
|
|
||||||
|
@EmailHandler = {
|
||||||
|
sendEmail: sinon.stub().yields(null)
|
||||||
|
}
|
||||||
|
|
||||||
|
@newToken = "bbbbbbbbb"
|
||||||
|
|
||||||
|
@crypto = {
|
||||||
|
randomBytes: =>
|
||||||
|
toString: sinon.stub().returns(@newToken)
|
||||||
|
}
|
||||||
|
|
||||||
|
@UserGetter.getUser.withArgs(@manager.id).yields(null, @manager)
|
||||||
|
@UserGetter.getUserByAnyEmail.withArgs(@manager.email).yields(null, @manager)
|
||||||
|
|
||||||
|
@SubscriptionLocator.getUsersSubscription.yields(null, @subscription)
|
||||||
|
@Subscription.findOne.yields(null, @subscription)
|
||||||
|
|
||||||
|
@TeamInvitesHandler = SandboxedModule.require modulePath, requires:
|
||||||
|
"logger-sharelatex": { log: -> }
|
||||||
|
"crypto": @crypto
|
||||||
|
"settings-sharelatex": { siteUrl: "http://example.com" }
|
||||||
|
"../../models/TeamInvite": { TeamInvite: @TeamInvite = {} }
|
||||||
|
"../../models/Subscription": { Subscription: @Subscription }
|
||||||
|
"../User/UserGetter": @UserGetter
|
||||||
|
"./SubscriptionLocator": @SubscriptionLocator
|
||||||
|
"./SubscriptionUpdater": @SubscriptionUpdater
|
||||||
|
"./LimitationsManager": @LimitationsManager
|
||||||
|
"../Email/EmailHandler": @EmailHandler
|
||||||
|
"../Errors/Errors": Errors
|
||||||
|
|
||||||
|
describe "getInvite", ->
|
||||||
|
it "returns the invite if there's one", (done) ->
|
||||||
|
@TeamInvitesHandler.getInvite @token, (err, invite, subscription) =>
|
||||||
|
expect(err).to.eq(null)
|
||||||
|
expect(invite).to.deep.eq(@teamInvite)
|
||||||
|
expect(subscription).to.deep.eq(@subscription)
|
||||||
|
done()
|
||||||
|
|
||||||
|
it "returns teamNotFound if there's none", (done) ->
|
||||||
|
@Subscription.findOne = sinon.stub().yields(null, null)
|
||||||
|
|
||||||
|
@TeamInvitesHandler.getInvite @token, (err, invite, subscription) ->
|
||||||
|
expect(err).to.be.instanceof(Errors.NotFoundError)
|
||||||
|
done()
|
||||||
|
|
||||||
|
describe "createInvite", ->
|
||||||
|
it "adds the team invite to the subscription", (done) ->
|
||||||
|
@TeamInvitesHandler.createInvite @manager.id, "John.Snow@example.com", (err, invite) =>
|
||||||
|
expect(err).to.eq(null)
|
||||||
|
expect(invite.token).to.eq(@newToken)
|
||||||
|
expect(invite.email).to.eq("john.snow@example.com")
|
||||||
|
expect(invite.inviterName).to.eq("Daenerys Targaryen (daenerys@example.com)")
|
||||||
|
expect(@subscription.teamInvites).to.deep.include(invite)
|
||||||
|
done()
|
||||||
|
|
||||||
|
it "sends an email", (done) ->
|
||||||
|
@TeamInvitesHandler.createInvite @manager.id, "John.Snow@example.com", (err, invite) =>
|
||||||
|
@EmailHandler.sendEmail.calledWith("verifyEmailToJoinTeam",
|
||||||
|
sinon.match({
|
||||||
|
to: "john.snow@example.com",
|
||||||
|
inviterName: "Daenerys Targaryen (daenerys@example.com)",
|
||||||
|
acceptInviteUrl: "http://example.com/subscription/invites/#{@newToken}/"
|
||||||
|
})
|
||||||
|
).should.equal true
|
||||||
|
done()
|
||||||
|
|
||||||
|
it "refreshes the existing invite if the email has already been invited", (done) ->
|
||||||
|
originalInvite = Object.assign({}, @teamInvite)
|
||||||
|
|
||||||
|
@TeamInvitesHandler.createInvite @manager.id, originalInvite.email, (err, invite) =>
|
||||||
|
expect(err).to.eq(null)
|
||||||
|
expect(invite).to.exist
|
||||||
|
|
||||||
|
expect(@subscription.teamInvites.length).to.eq 1
|
||||||
|
expect(@subscription.teamInvites).to.deep.include invite
|
||||||
|
|
||||||
|
expect(invite.email).to.eq originalInvite.email
|
||||||
|
|
||||||
|
@subscription.save.calledOnce.should.eq true
|
||||||
|
|
||||||
|
done()
|
||||||
|
|
||||||
|
it "removes any legacy invite from the subscription", (done) ->
|
||||||
|
@TeamInvitesHandler.createInvite @manager.id, "John.Snow@example.com", (err, invite) =>
|
||||||
|
@Subscription.update.calledWith(
|
||||||
|
{ _id: new ObjectId("55153a8014829a865bbf700d") },
|
||||||
|
{ '$pull': { invited_emails: "john.snow@example.com" } }
|
||||||
|
).should.eq true
|
||||||
|
done()
|
||||||
|
|
||||||
|
describe "createDomainInvite", ->
|
||||||
|
beforeEach ->
|
||||||
|
@licence =
|
||||||
|
subscription_id: @subscription.id
|
||||||
|
name: "Team Daenerys"
|
||||||
|
|
||||||
|
@user =
|
||||||
|
email: "John.Snow@example.com"
|
||||||
|
|
||||||
|
it "adds the team invite to the subscription", (done) ->
|
||||||
|
@TeamInvitesHandler.createDomainInvite @user, @licence, (err, invite) =>
|
||||||
|
expect(err).to.eq(null)
|
||||||
|
expect(invite.token).to.eq(@newToken)
|
||||||
|
expect(invite.email).to.eq("john.snow@example.com")
|
||||||
|
expect(invite.inviterName).to.eq("Team Daenerys")
|
||||||
|
expect(@subscription.teamInvites).to.deep.include(invite)
|
||||||
|
done()
|
||||||
|
|
||||||
|
it "sends an email", (done) ->
|
||||||
|
@TeamInvitesHandler.createDomainInvite @user, @licence, (err, invite) =>
|
||||||
|
@EmailHandler.sendEmail.calledWith("verifyEmailToJoinTeam",
|
||||||
|
sinon.match({
|
||||||
|
to: "john.snow@example.com"
|
||||||
|
inviterName: "Team Daenerys"
|
||||||
|
acceptInviteUrl: "http://example.com/subscription/invites/#{@newToken}/"
|
||||||
|
})
|
||||||
|
).should.equal true
|
||||||
|
done()
|
||||||
|
|
||||||
|
describe "acceptInvite", ->
|
||||||
|
beforeEach ->
|
||||||
|
@user = {
|
||||||
|
id: "123456789",
|
||||||
|
first_name: "Tyrion",
|
||||||
|
last_name: "Lannister",
|
||||||
|
email: "tyrion@example.com"
|
||||||
|
}
|
||||||
|
|
||||||
|
@UserGetter.getUserByAnyEmail.withArgs(@user.email).yields(null, @user)
|
||||||
|
|
||||||
|
@subscription.teamInvites.push({
|
||||||
|
email: "john.snow@example.com",
|
||||||
|
token: "dddddddd",
|
||||||
|
inviterName: "Daenerys Targaryen (daenerys@example.com)"
|
||||||
|
})
|
||||||
|
|
||||||
|
it "adds the user to the team", (done) ->
|
||||||
|
@TeamInvitesHandler.acceptInvite "dddddddd", @user.id, =>
|
||||||
|
@SubscriptionUpdater.addUserToGroup.calledWith(@manager.id, @user.id).should.eq true
|
||||||
|
done()
|
||||||
|
|
||||||
|
it "removes the invite from the subscription", (done) ->
|
||||||
|
@TeamInvitesHandler.acceptInvite "dddddddd", @user.id, =>
|
||||||
|
@Subscription.update.calledWith(
|
||||||
|
{ _id: new ObjectId("55153a8014829a865bbf700d") },
|
||||||
|
{ '$pull': { teamInvites: { email: 'john.snow@example.com' } } }
|
||||||
|
).should.eq true
|
||||||
|
done()
|
||||||
|
|
||||||
|
describe "revokeInvite", ->
|
||||||
|
it "removes the team invite from the subscription", (done) ->
|
||||||
|
@TeamInvitesHandler.revokeInvite @manager.id, "jorah@example.com", =>
|
||||||
|
@Subscription.update.calledWith(
|
||||||
|
{ _id: new ObjectId("55153a8014829a865bbf700d") },
|
||||||
|
{ '$pull': { teamInvites: { email: "jorah@example.com" } } }
|
||||||
|
).should.eq true
|
||||||
|
|
||||||
|
@Subscription.update.calledWith(
|
||||||
|
{ _id: new ObjectId("55153a8014829a865bbf700d") },
|
||||||
|
{ '$pull': { invited_emails: "jorah@example.com" } }
|
||||||
|
).should.eq true
|
||||||
|
done()
|
||||||
|
|
||||||
|
describe "createTeamInvitesForLegacyInvitedEmail", (done) ->
|
||||||
|
beforeEach ->
|
||||||
|
@subscription.invited_emails = ["eddard@example.com", "robert@example.com"]
|
||||||
|
@TeamInvitesHandler.createInvite = sinon.stub().yields(null)
|
||||||
|
@SubscriptionLocator.getGroupsWithEmailInvite = sinon.stub().yields(null, [@subscription])
|
||||||
|
|
||||||
|
it "sends an invitation email to addresses in the legacy invited_emails field", (done) ->
|
||||||
|
@TeamInvitesHandler.createTeamInvitesForLegacyInvitedEmail "eddard@example.com", (err, invite) =>
|
||||||
|
expect(err).not.to.exist
|
||||||
|
|
||||||
|
@TeamInvitesHandler.createInvite.calledWith(
|
||||||
|
@subscription.admin_id,
|
||||||
|
"eddard@example.com"
|
||||||
|
).should.eq true
|
||||||
|
|
||||||
|
@TeamInvitesHandler.createInvite.callCount.should.eq 1
|
||||||
|
|
||||||
|
done()
|
||||||
|
|
||||||
|
describe "validation", ->
|
||||||
|
it "doesn't create an invite if the team limit has been reached", (done) ->
|
||||||
|
@LimitationsManager.teamHasReachedMemberLimit = sinon.stub().returns(true)
|
||||||
|
@TeamInvitesHandler.createInvite @manager.id, "John.Snow@example.com", (err, invite) =>
|
||||||
|
expect(err).to.deep.equal(limitReached: true)
|
||||||
|
done()
|
||||||
|
|
||||||
|
it "doesn't create an invite if the subscription is not in a group plan", (done) ->
|
||||||
|
@subscription.groupPlan = false
|
||||||
|
@TeamInvitesHandler.createInvite @manager.id, "John.Snow@example.com", (err, invite) =>
|
||||||
|
expect(err).to.deep.equal(wrongPlan: true)
|
||||||
|
done()
|
||||||
|
|
||||||
|
it "doesn't create an invite if the user is already part of the team", (done) ->
|
||||||
|
member = {
|
||||||
|
id: "1a2b",
|
||||||
|
_id: "1a2b",
|
||||||
|
email: "tyrion@example.com"
|
||||||
|
}
|
||||||
|
|
||||||
|
@subscription.member_ids = [member.id]
|
||||||
|
@UserGetter.getUserByAnyEmail.withArgs(member.email).yields(null, member)
|
||||||
|
|
||||||
|
@TeamInvitesHandler.createInvite @manager.id, "tyrion@example.com", (err, invite) =>
|
||||||
|
expect(err).to.deep.equal(alreadyInTeam: true)
|
||||||
|
expect(invite).not.to.exist
|
||||||
|
done()
|
|
@ -55,7 +55,7 @@ describe "UserController", ->
|
||||||
@settings =
|
@settings =
|
||||||
siteUrl: "sharelatex.example.com"
|
siteUrl: "sharelatex.example.com"
|
||||||
@UserHandler =
|
@UserHandler =
|
||||||
populateGroupLicenceInvite:sinon.stub().callsArgWith(1)
|
populateTeamInvites: sinon.stub().callsArgWith(1)
|
||||||
@UserSessionsManager =
|
@UserSessionsManager =
|
||||||
trackSession: sinon.stub()
|
trackSession: sinon.stub()
|
||||||
untrackSession: sinon.stub()
|
untrackSession: sinon.stub()
|
||||||
|
@ -267,12 +267,12 @@ describe "UserController", ->
|
||||||
done()
|
done()
|
||||||
@UserController.updateUserSettings @req, @res
|
@UserController.updateUserSettings @req, @res
|
||||||
|
|
||||||
it "should call populateGroupLicenceInvite", (done)->
|
it "should call populateTeamInvites", (done)->
|
||||||
@req.body.email = @newEmail.toUpperCase()
|
@req.body.email = @newEmail.toUpperCase()
|
||||||
@UserUpdater.changeEmailAddress.callsArgWith(2)
|
@UserUpdater.changeEmailAddress.callsArgWith(2)
|
||||||
@res.sendStatus = (code)=>
|
@res.sendStatus = (code)=>
|
||||||
code.should.equal 200
|
code.should.equal 200
|
||||||
@UserHandler.populateGroupLicenceInvite.calledWith(@user).should.equal true
|
@UserHandler.populateTeamInvites.calledWith(@user).should.equal true
|
||||||
done()
|
done()
|
||||||
@UserController.updateUserSettings @req, @res
|
@UserController.updateUserSettings @req, @res
|
||||||
|
|
||||||
|
|
|
@ -19,27 +19,37 @@ describe "UserHandler", ->
|
||||||
|
|
||||||
@SubscriptionGroupHandler =
|
@SubscriptionGroupHandler =
|
||||||
isUserPartOfGroup:sinon.stub()
|
isUserPartOfGroup:sinon.stub()
|
||||||
convertEmailInvitesToMemberships: sinon.stub().callsArgWith(2)
|
|
||||||
@createStub = sinon.stub().callsArgWith(0)
|
@createStub = sinon.stub().callsArgWith(0)
|
||||||
@NotificationsBuilder =
|
@NotificationsBuilder =
|
||||||
groupPlan:sinon.stub().returns({create:@createStub})
|
groupPlan:sinon.stub().returns({create:@createStub})
|
||||||
|
|
||||||
|
@TeamInvitesHandler =
|
||||||
|
createTeamInvitesForLegacyInvitedEmail: sinon.stub().yields()
|
||||||
|
|
||||||
@UserHandler = SandboxedModule.require modulePath, requires:
|
@UserHandler = SandboxedModule.require modulePath, requires:
|
||||||
"logger-sharelatex": @logger = { log: sinon.stub() }
|
"logger-sharelatex": @logger = { log: sinon.stub() }
|
||||||
"../Notifications/NotificationsBuilder":@NotificationsBuilder
|
"../Notifications/NotificationsBuilder":@NotificationsBuilder
|
||||||
"../Subscription/SubscriptionDomainHandler":@SubscriptionDomainHandler
|
"../Subscription/SubscriptionDomainHandler":@SubscriptionDomainHandler
|
||||||
"../Subscription/SubscriptionGroupHandler":@SubscriptionGroupHandler
|
"../Subscription/SubscriptionGroupHandler":@SubscriptionGroupHandler
|
||||||
|
"../Subscription/TeamInvitesHandler": @TeamInvitesHandler
|
||||||
|
|
||||||
describe "populateGroupLicenceInvite", ->
|
describe "populateTeamInvites", ->
|
||||||
|
beforeEach (done)->
|
||||||
|
@UserHandler.notifyDomainLicence = sinon.stub().yields()
|
||||||
|
@UserHandler.populateTeamInvites @user, done
|
||||||
|
|
||||||
|
it "notifies the user about domain licences zzzzz", ->
|
||||||
|
@UserHandler.notifyDomainLicence.calledWith(@user).should.eq true
|
||||||
|
|
||||||
|
it "notifies the user about legacy team invites", ->
|
||||||
|
@TeamInvitesHandler.createTeamInvitesForLegacyInvitedEmail
|
||||||
|
.calledWith(@user.email).should.eq true
|
||||||
|
|
||||||
|
describe "notifyDomainLicence", ->
|
||||||
describe "no licence", ->
|
describe "no licence", ->
|
||||||
beforeEach (done)->
|
beforeEach (done)->
|
||||||
@SubscriptionDomainHandler.getLicenceUserCanJoin.returns()
|
@SubscriptionDomainHandler.getLicenceUserCanJoin.returns()
|
||||||
@UserHandler.populateGroupLicenceInvite @user, done
|
@UserHandler.populateTeamInvites @user, done
|
||||||
|
|
||||||
it "should call convertEmailInvitesToMemberships", ->
|
|
||||||
@SubscriptionGroupHandler.convertEmailInvitesToMemberships
|
|
||||||
.calledWith(@user.email, @user._id)
|
|
||||||
.should.equal true
|
|
||||||
|
|
||||||
it "should not call NotificationsBuilder", (done)->
|
it "should not call NotificationsBuilder", (done)->
|
||||||
@NotificationsBuilder.groupPlan.called.should.equal false
|
@NotificationsBuilder.groupPlan.called.should.equal false
|
||||||
|
@ -53,28 +63,18 @@ describe "UserHandler", ->
|
||||||
beforeEach (done)->
|
beforeEach (done)->
|
||||||
@SubscriptionDomainHandler.getLicenceUserCanJoin.returns(@licence)
|
@SubscriptionDomainHandler.getLicenceUserCanJoin.returns(@licence)
|
||||||
@SubscriptionGroupHandler.isUserPartOfGroup.callsArgWith(2, null, false)
|
@SubscriptionGroupHandler.isUserPartOfGroup.callsArgWith(2, null, false)
|
||||||
@UserHandler.populateGroupLicenceInvite @user, done
|
@UserHandler.populateTeamInvites @user, done
|
||||||
|
|
||||||
it "should create notifcation", (done)->
|
it "should create notifcation", (done)->
|
||||||
@NotificationsBuilder.groupPlan.calledWith(@user, @licence).should.equal true
|
@NotificationsBuilder.groupPlan.calledWith(@user, @licence).should.equal true
|
||||||
done()
|
done()
|
||||||
|
|
||||||
it "should call convertEmailInvitesToMemberships", ->
|
|
||||||
@SubscriptionGroupHandler.convertEmailInvitesToMemberships
|
|
||||||
.calledWith(@user.email, @user._id)
|
|
||||||
.should.equal true
|
|
||||||
|
|
||||||
describe "with matching licence user is already in", ->
|
describe "with matching licence user is already in", ->
|
||||||
beforeEach (done)->
|
beforeEach (done)->
|
||||||
@SubscriptionDomainHandler.getLicenceUserCanJoin.returns(@licence)
|
@SubscriptionDomainHandler.getLicenceUserCanJoin.returns(@licence)
|
||||||
@SubscriptionGroupHandler.isUserPartOfGroup.callsArgWith(2, null, true)
|
@SubscriptionGroupHandler.isUserPartOfGroup.callsArgWith(2, null, true)
|
||||||
@UserHandler.populateGroupLicenceInvite @user, done
|
@UserHandler.populateTeamInvites @user, done
|
||||||
|
|
||||||
it "should create notifcation", (done)->
|
it "should create notifcation", (done)->
|
||||||
@NotificationsBuilder.groupPlan.called.should.equal false
|
@NotificationsBuilder.groupPlan.called.should.equal false
|
||||||
done()
|
done()
|
||||||
|
|
||||||
it "should call convertEmailInvitesToMemberships", ->
|
|
||||||
@SubscriptionGroupHandler.convertEmailInvitesToMemberships
|
|
||||||
.calledWith(@user.email, @user._id)
|
|
||||||
.should.equal true
|
|
Loading…
Reference in a new issue