Merge pull request #3543 from overleaf/jel-em-ip-matcher

IP matcher notifications

GitOrigin-RevId: 50811fb683961f4d021b37b1d97c24da48c59720
This commit is contained in:
Jessica Lawshe 2021-01-19 09:52:20 -06:00 committed by Copybot
parent b44aed56dd
commit d612c03f8e
4 changed files with 137 additions and 69 deletions

View file

@ -127,9 +127,15 @@ function ipMatcherAffiliation(userId) {
} }
const key = `ip-matched-affiliation-${body.id}` const key = `ip-matched-affiliation-${body.id}`
const portalPath = body.portal_slug
? `/${body.is_university ? 'edu' : 'org'}/${body.portal_slug}`
: undefined
const messageOpts = { const messageOpts = {
university_name: body.name, university_name: body.name,
content: body.enrolment_ad_html institutionId: body.id,
content: body.enrolment_ad_html,
portalPath,
ssoEnabled: body.sso_enabled
} }
return NotificationsHandler.createNotification( return NotificationsHandler.createNotification(
userId, userId,

View file

@ -51,17 +51,40 @@ include ../../_mixins/saml
span.sr-only #{translate("close")} span.sr-only #{translate("close")}
.alert.alert-info( .alert.alert-info(
ng-switch-when="notification_ip_matched_affiliation" ng-switch-when="notification_ip_matched_affiliation"
ng-if="notification.messageOpts.ssoEnabled"
) )
.notification-body .notification-body
| It looks like you're at  | !{translate("looks_like_youre_at", {institutionName: '{{notification.messageOpts.university_name}}'}, ['strong'])}
strong {{ notification.messageOpts.university_name }}! <br/> br
| Did you know that {{notification.messageOpts.university_name}} is providing | !{translate("you_can_now_log_in_sso", {}, ['strong'])}
strong free Overleaf Professional accounts br
| to everyone at {{notification.messageOpts.university_name}}? <br/> | #{translate("link_institutional_email_get_started", {}, ['strong'])}&nbsp;
| Add an institutional email address to claim your account. a(
ng-href="{{notification.messageOpts.portalPath || 'https://www.overleaf.com/learn/how-to/Institutional_Login'}}"
) #{translate("find_out_more_nt")}
.notification-action .notification-action
a.pull-right.btn.btn-sm.btn-info(href="/user/settings") a.pull-right.btn.btn-sm.btn-info(
| Add Affiliation href=`{{samlInitPath}}?university_id={{notification.messageOpts.institutionId}}`
)
| #{translate("link_account")}
.notification-close
button.btn-sm(ng-click="dismiss(notification)").close.pull-right
span(aria-hidden="true") &times;
span.sr-only #{translate("close")}
.alert.alert-info(
ng-switch-when="notification_ip_matched_affiliation"
ng-if="!notification.messageOpts.ssoEnabled"
)
.notification-body
| !{translate("looks_like_youre_at", {institutionName: '{{notification.messageOpts.university_name}}'}, ['strong'])}
br
| !{translate("did_you_know_institution_providing_professional", {institutionName: '{{notification.messageOpts.university_name}}'}, ['strong'])}
br
| #{translate("add_email_to_claim_features")}
.notification-action
a.pull-right.btn.btn-sm.btn-info(
href="/user/settings"
) #{translate("add_affiliation")}
.notification-close .notification-close
button.btn-sm(ng-click="dismiss(notification)").close.pull-right button.btn-sm(ng-click="dismiss(notification)").close.pull-right
span(aria-hidden="true") &times; span(aria-hidden="true") &times;

View file

@ -1330,5 +1330,11 @@
"github_is_premium": "GitHub sync is a premium feature", "github_is_premium": "GitHub sync is a premium feature",
"remote_service_error": "The remote service produced an error", "remote_service_error": "The remote service produced an error",
"linked_file": "Imported file", "linked_file": "Imported file",
"n_items": "__count__ items" "n_items": "__count__ items",
"you_can_now_log_in_sso": "You can now log in through your institution and may receive <0>free __appName__ Professional features</0>!",
"link_institutional_email_get_started": "Link an institutional email address to your account to get started.",
"looks_like_youre_at": "It looks like you're at <0>__institutionName__</0>!",
"add_affiliation": "Add Affiliation",
"did_you_know_institution_providing_professional": "Did you know that __institutionName__ is providing <0>free __appName__ Professional features</0> to everyone at __institutionName__?",
"add_email_to_claim_features": "Add an institutional email address to claim your features."
} }

View file

@ -1,18 +1,4 @@
/* eslint-disable
camelcase,
max-len,
no-return-assign,
no-unused-vars,
*/
// TODO: This file was created by bulk-decaffeinate.
// Fix any style issues and re-enable lint.
/*
* decaffeinate suggestions:
* DS102: Remove unnecessary code created because of implicit returns
* Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
*/
const SandboxedModule = require('sandboxed-module') const SandboxedModule = require('sandboxed-module')
const { assert } = require('chai')
require('chai').should() require('chai').should()
const sinon = require('sinon') const sinon = require('sinon')
const modulePath = require('path').join( const modulePath = require('path').join(
@ -21,19 +7,14 @@ const modulePath = require('path').join(
) )
describe('NotificationsBuilder', function() { describe('NotificationsBuilder', function() {
const user_id = '123nd3ijdks' const userId = '123nd3ijdks'
describe('ipMatcherAffiliation', function() {
beforeEach(function() { beforeEach(function() {
this.handler = { createNotification: sinon.stub().callsArgWith(6) } this.handler = { createNotification: sinon.stub().callsArgWith(6) }
this.settings = { apis: { v1: { url: 'v1.url', user: '', pass: '' } } } this.settings = { apis: { v1: { url: 'v1.url', user: '', pass: '' } } }
this.body = { id: 1, name: 'stanford', enrolment_ad_html: 'v1 ad content' } this.request = sinon.stub()
const response = { statusCode: 200 } this.controller = SandboxedModule.require(modulePath, {
this.request = sinon
.stub()
.returns(this.stubResponse)
.callsArgWith(1, null, response, this.body)
return (this.controller = SandboxedModule.require(modulePath, {
globals: { globals: {
console: console console: console
}, },
@ -46,28 +27,80 @@ describe('NotificationsBuilder', function() {
err() {} err() {}
} }
} }
})) })
})
describe('with portal and with SSO', function() {
beforeEach(function() {
this.body = {
id: 1,
name: 'stanford',
enrolment_ad_html: 'v1 ad content',
is_university: true,
portal_slug: null,
sso_enabled: false
}
this.request.callsArgWith(1, null, { statusCode: 200 }, this.body)
}) })
it('should call v1 and create affiliation notifications', function(done) { it('should call v1 and create affiliation notifications', function(done) {
const ip = '192.168.0.1' const ip = '192.168.0.1'
return this.controller this.controller.ipMatcherAffiliation(userId).create(ip, callback => {
.ipMatcherAffiliation(user_id)
.create(ip, callback => {
this.request.calledOnce.should.equal(true) this.request.calledOnce.should.equal(true)
const expectedOpts = { const expectedOpts = {
institutionId: this.body.id,
university_name: this.body.name, university_name: this.body.name,
content: this.body.enrolment_ad_html content: this.body.enrolment_ad_html,
ssoEnabled: false,
portalPath: undefined
} }
this.handler.createNotification this.handler.createNotification
.calledWith( .calledWith(
user_id, userId,
`ip-matched-affiliation-${this.body.id}`, `ip-matched-affiliation-${this.body.id}`,
'notification_ip_matched_affiliation', 'notification_ip_matched_affiliation',
expectedOpts expectedOpts
) )
.should.equal(true) .should.equal(true)
return done() done()
})
})
})
describe('without portal and without SSO', function() {
beforeEach(function() {
this.body = {
id: 1,
name: 'stanford',
enrolment_ad_html: 'v1 ad content',
is_university: true,
portal_slug: 'stanford',
sso_enabled: true
}
this.request.callsArgWith(1, null, { statusCode: 200 }, this.body)
})
it('should call v1 and create affiliation notifications', function(done) {
const ip = '192.168.0.1'
this.controller.ipMatcherAffiliation(userId).create(ip, callback => {
this.request.calledOnce.should.equal(true)
const expectedOpts = {
institutionId: this.body.id,
university_name: this.body.name,
content: this.body.enrolment_ad_html,
ssoEnabled: true,
portalPath: '/edu/stanford'
}
this.handler.createNotification
.calledWith(
userId,
`ip-matched-affiliation-${this.body.id}`,
'notification_ip_matched_affiliation',
expectedOpts
)
.should.equal(true)
done()
})
})
}) })
}) })
}) })