diff --git a/services/web/app/src/Features/Notifications/NotificationsBuilder.js b/services/web/app/src/Features/Notifications/NotificationsBuilder.js index f71d2d54ab..291f58f294 100644 --- a/services/web/app/src/Features/Notifications/NotificationsBuilder.js +++ b/services/web/app/src/Features/Notifications/NotificationsBuilder.js @@ -127,9 +127,15 @@ function ipMatcherAffiliation(userId) { } const key = `ip-matched-affiliation-${body.id}` + const portalPath = body.portal_slug + ? `/${body.is_university ? 'edu' : 'org'}/${body.portal_slug}` + : undefined const messageOpts = { 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( userId, diff --git a/services/web/app/views/project/list/notifications.pug b/services/web/app/views/project/list/notifications.pug index ebd2e88db3..fd6a508d72 100644 --- a/services/web/app/views/project/list/notifications.pug +++ b/services/web/app/views/project/list/notifications.pug @@ -51,17 +51,40 @@ include ../../_mixins/saml span.sr-only #{translate("close")} .alert.alert-info( ng-switch-when="notification_ip_matched_affiliation" + ng-if="notification.messageOpts.ssoEnabled" ) .notification-body - | It looks like you're at  - strong {{ notification.messageOpts.university_name }}!
- | Did you know that {{notification.messageOpts.university_name}} is providing - strong free Overleaf Professional accounts - | to everyone at {{notification.messageOpts.university_name}}?
- | Add an institutional email address to claim your account. + | !{translate("looks_like_youre_at", {institutionName: '{{notification.messageOpts.university_name}}'}, ['strong'])} + br + | !{translate("you_can_now_log_in_sso", {}, ['strong'])} + br + | #{translate("link_institutional_email_get_started", {}, ['strong'])}  + a( + ng-href="{{notification.messageOpts.portalPath || 'https://www.overleaf.com/learn/how-to/Institutional_Login'}}" + ) #{translate("find_out_more_nt")} .notification-action - a.pull-right.btn.btn-sm.btn-info(href="/user/settings") - | Add Affiliation + a.pull-right.btn.btn-sm.btn-info( + 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") × + 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 button.btn-sm(ng-click="dismiss(notification)").close.pull-right span(aria-hidden="true") × diff --git a/services/web/locales/en.json b/services/web/locales/en.json index 123b114b1e..a003014965 100644 --- a/services/web/locales/en.json +++ b/services/web/locales/en.json @@ -1330,5 +1330,11 @@ "github_is_premium": "GitHub sync is a premium feature", "remote_service_error": "The remote service produced an error", "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!", + "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__!", + "add_affiliation": "Add Affiliation", + "did_you_know_institution_providing_professional": "Did you know that __institutionName__ is providing <0>free __appName__ Professional features to everyone at __institutionName__?", + "add_email_to_claim_features": "Add an institutional email address to claim your features." } diff --git a/services/web/test/unit/src/Notifications/NotificationsBuilderTests.js b/services/web/test/unit/src/Notifications/NotificationsBuilderTests.js index 918dad2fb9..77724bf6ee 100644 --- a/services/web/test/unit/src/Notifications/NotificationsBuilderTests.js +++ b/services/web/test/unit/src/Notifications/NotificationsBuilderTests.js @@ -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 { assert } = require('chai') require('chai').should() const sinon = require('sinon') const modulePath = require('path').join( @@ -21,53 +7,100 @@ const modulePath = require('path').join( ) describe('NotificationsBuilder', function() { - const user_id = '123nd3ijdks' + const userId = '123nd3ijdks' - beforeEach(function() { - this.handler = { createNotification: sinon.stub().callsArgWith(6) } - - this.settings = { apis: { v1: { url: 'v1.url', user: '', pass: '' } } } - this.body = { id: 1, name: 'stanford', enrolment_ad_html: 'v1 ad content' } - const response = { statusCode: 200 } - this.request = sinon - .stub() - .returns(this.stubResponse) - .callsArgWith(1, null, response, this.body) - return (this.controller = SandboxedModule.require(modulePath, { - globals: { - console: console - }, - requires: { - './NotificationsHandler': this.handler, - 'settings-sharelatex': this.settings, - request: this.request, - 'logger-sharelatex': { - log() {}, - err() {} + describe('ipMatcherAffiliation', function() { + beforeEach(function() { + this.handler = { createNotification: sinon.stub().callsArgWith(6) } + this.settings = { apis: { v1: { url: 'v1.url', user: '', pass: '' } } } + this.request = sinon.stub() + this.controller = SandboxedModule.require(modulePath, { + globals: { + console: console + }, + requires: { + './NotificationsHandler': this.handler, + 'settings-sharelatex': this.settings, + request: this.request, + 'logger-sharelatex': { + log() {}, + err() {} + } } - } - })) - }) - - it('should call v1 and create affiliation notifications', function(done) { - const ip = '192.168.0.1' - return this.controller - .ipMatcherAffiliation(user_id) - .create(ip, callback => { - this.request.calledOnce.should.equal(true) - const expectedOpts = { - university_name: this.body.name, - content: this.body.enrolment_ad_html - } - this.handler.createNotification - .calledWith( - user_id, - `ip-matched-affiliation-${this.body.id}`, - 'notification_ip_matched_affiliation', - expectedOpts - ) - .should.equal(true) - return done() }) + }) + + 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) { + 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: false, + portalPath: undefined + } + this.handler.createNotification + .calledWith( + userId, + `ip-matched-affiliation-${this.body.id}`, + 'notification_ip_matched_affiliation', + expectedOpts + ) + .should.equal(true) + 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() + }) + }) + }) }) })