diff --git a/services/web/app/src/Features/Project/ProjectController.js b/services/web/app/src/Features/Project/ProjectController.js index 50d49f23f2..884a604d33 100644 --- a/services/web/app/src/Features/Project/ProjectController.js +++ b/services/web/app/src/Features/Project/ProjectController.js @@ -429,6 +429,7 @@ const ProjectController = { (results.v1Projects != null ? results.v1Projects.tags : undefined) || [] const tags = results.tags.concat(v1Tags) + const notificationsInstitution = [] for (const notification of notifications) { notification.html = req.i18n.translate( notification.templateKey, @@ -460,7 +461,7 @@ const ProjectController = { affiliation.institution.id.toString() ) === -1 ) { - notifications.push({ + notificationsInstitution.push({ email: affiliation.email, institutionId: affiliation.institution.id, institutionName: affiliation.institution.name, @@ -473,7 +474,7 @@ const ProjectController = { if (samlSession) { // Notification: After SSO Linked if (samlSession.linked) { - notifications.push({ + notificationsInstitution.push({ email: samlSession.institutionEmail, institutionName: samlSession.linked.universityName, templateKey: 'notification_institution_sso_linked' @@ -484,7 +485,7 @@ const ProjectController = { // The requested email does not match primary email returned from // the institution if (samlSession.emailNonCanonical) { - notifications.push({ + notificationsInstitution.push({ institutionEmail: samlSession.emailNonCanonical, requestedEmail: samlSession.requestedEmail, templateKey: 'notification_institution_sso_non_canonical' @@ -493,7 +494,7 @@ const ProjectController = { // Notification: Tried to register, but account already existed if (samlSession.registerIntercept) { - notifications.push({ + notificationsInstitution.push({ email: samlSession.institutionEmail, templateKey: 'notification_institution_sso_already_registered' }) @@ -529,6 +530,7 @@ const ProjectController = { projects, tags, notifications: notifications || [], + notificationsInstitution, portalTemplates, user, userAffiliations, diff --git a/services/web/app/views/project/list.pug b/services/web/app/views/project/list.pug index 555f534c5c..409b094404 100644 --- a/services/web/app/views/project/list.pug +++ b/services/web/app/views/project/list.pug @@ -5,7 +5,7 @@ block vars block content script#data(type="application/json"). - !{StringHelper.stringifyJsonForScript({ projects, tags, notifications })} + !{StringHelper.stringifyJsonForScript({ projects, tags, notifications, notificationsInstitution })} script(type="text/javascript"). window.data = JSON.parse($("#data").text()); diff --git a/services/web/app/views/project/list/notifications.pug b/services/web/app/views/project/list/notifications.pug index cb3d9abbf6..f7092f01aa 100644 --- a/services/web/app/views/project/list/notifications.pug +++ b/services/web/app/views/project/list/notifications.pug @@ -1,29 +1,43 @@ .user-notifications(ng-controller="NotificationsController") ul.list-unstyled( - ng-if="notifications.length > 0", + ng-if="notifications.length > 0 && projects.length > 0", ng-cloak ) li.notification-entry( - ng-repeat="notification in notifications", + ng-repeat="notification in notifications" ) div(ng-switch="notification.templateKey" ng-hide="notification.hide") - .alert.alert-info(ng-switch-when="notification_project_invite", ng-controller="ProjectInviteNotificationController") - .notification-body(ng-show="!notification.accepted") - | !{translate("notification_project_invite_message", { userName: "{{ userName }}", projectName: "{{ projectName }}" })} - a.pull-right.btn.btn-sm.btn-info(href, ng-click="accept()", ng-disabled="notification.inflight") + .alert.alert-info( + ng-switch-when="notification_project_invite", + ng-controller="ProjectInviteNotificationController" + ) + .notification-body + span(ng-show="!notification.accepted") !{translate("notification_project_invite_message", { userName: "{{ userName }}", projectName: "{{ projectName }}" })} + span(ng-show="notification.accepted") !{translate("notification_project_invite_accepted_message", { projectName: "{{ projectName }}" })} + .notification-action + a.pull-right.btn.btn-sm.btn-info( + ng-show="notification.accepted", + href="/project/{{ notification.messageOpts.projectId }}" + ) #{translate("open_project")} + a.pull-right.btn.btn-sm.btn-info( + href, + ng-click="accept()", + ng-disabled="notification.inflight", + ng-show="!notification.accepted" + ) span(ng-show="!notification.inflight") #{translate("join_project")} span(ng-show="notification.inflight") i.fa.fa-fw.fa-spinner.fa-spin(aria-hidden="true") |   | #{translate("joining")}... - .notification-body(ng-show="notification.accepted") - | !{translate("notification_project_invite_accepted_message", { projectName: "{{ projectName }}" })} - a.pull-right.btn.btn-sm.btn-info(href="/project/{{ notification.messageOpts.projectId }}") #{translate("open_project")} .notification-close button(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") + + .alert.alert-info( + ng-switch-when="notification_ip_matched_affiliation" + ) .notification-body | It looks like you're at strong {{ notification.messageOpts.university_name }}!
@@ -31,31 +45,41 @@ strong free Overleaf Professional accounts | to everyone at {{notification.messageOpts.university_name}}?
| Add an institutional email address to claim your account. + .notification-action a.pull-right.btn.btn-sm.btn-info(href="/user/settings") | Add Affiliation .notification-close - button(ng-click="dismiss(notification)").close.pull-right + 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_institution_sso_available") + + .alert.alert-info( + ng-switch-when="notification_institution_sso_available" + ) .notification-body p !{translate("can_link_institution_email_acct_to_institution_acct", {appName: settings.appName, email: "{{notification.email}}", institutionName: "{{notification.institutionName}}"})} div !{translate("doing_this_allow_log_in_through_institution", {appName: settings.appName})} .notification-action - a.btn.btn-info(href="{{samlInitPath}}?university_id={{notification.institutionId}}&auto=project&email={{notification.email}}") + a.btn.btn-sm.btn-info(href="{{samlInitPath}}?university_id={{notification.institutionId}}&auto=project&email={{notification.email}}") | #{translate('link_account')} .notification-close button(ng-click="dismiss(notification)").close.pull-right span(aria-hidden="true") × span.sr-only #{translate("close")} - .alert.alert-info(ng-switch-when="notification_institution_sso_linked") + + .alert.alert-info( + ng-switch-when="notification_institution_sso_linked" + ) .notification-body div !{translate("account_has_been_link_to_institution_account", {appName: settings.appName, email: "{{notification.email}}", institutionName: "{{notification.institutionName}}"})} .notification-close button(ng-click="dismiss(notification)").close.pull-right span(aria-hidden="true") × span.sr-only #{translate("close")} - .alert.alert-warning(ng-switch-when="notification_institution_sso_non_canonical") + + .alert.alert-warning( + ng-switch-when="notification_institution_sso_non_canonical" + ) .notification-body div i.fa.fa-fw.fa-exclamation-triangle(aria-hidden="true") @@ -64,24 +88,88 @@ button(ng-click="dismiss(notification)").close.pull-right span(aria-hidden="true") × span.sr-only #{translate("close")} - .alert.alert-info(ng-switch-when="notification_institution_sso_already_registered") + + .alert.alert-info( + ng-switch-when="notification_institution_sso_already_registered" + ) .notification-body | !{translate("tried_to_register_with_email", {appName: settings.appName, email: "{{notification.email}}"})} | #{translate("we_logged_you_in")} .notification-action - a.btn.btn-info(href="/") + a.btn.btn-sm.btn-info(href="/") | #{translate("find_out_more")} .notification-close button(ng-click="dismiss(notification)").close.pull-right span(aria-hidden="true") × span.sr-only #{translate("close")} - .alert.alert-info(ng-switch-default) + + .alert.alert-info( + ng-switch-default + ) span(ng-bind-html="notification.html").notification-body .notification-close button(ng-click="dismiss(notification)").close.pull-right span(aria-hidden="true") × span.sr-only #{translate("close")} + ul.list-unstyled( + ng-if="notificationsInstitution.length > 0", + ng-cloak + ) + li.notification-entry( + ng-repeat="notification in notificationsInstitution" + ) + div(ng-switch="notification.templateKey" ng-hide="notification.hide") + .alert.alert-info( + ng-switch-when="notification_institution_sso_available" + ) + .notification-body + p !{translate("can_link_institution_email_acct_to_institution_acct", {appName: settings.appName, email: "{{notification.email}}", institutionName: "{{notification.institutionName}}"})} + div !{translate("doing_this_allow_log_in_through_institution", {appName: settings.appName})} + .notification-action + a.btn.btn-sm.btn-info(href="{{samlInitPath}}?university_id={{notification.institutionId}}&auto=project&email={{notification.email}}") + | #{translate('link_account')} + .notification-close + button(ng-click="dismiss(notification)").close.pull-right + span(aria-hidden="true") × + span.sr-only #{translate("close")} + + .alert.alert-info( + ng-switch-when="notification_institution_sso_linked" + ) + .notification-body + div !{translate("account_has_been_link_to_institution_account", {appName: settings.appName, email: "{{notification.email}}", institutionName: "{{notification.institutionName}}"})} + .notification-close + button(ng-click="dismiss(notification)").close.pull-right + span(aria-hidden="true") × + span.sr-only #{translate("close")} + + .alert.alert-warning( + ng-switch-when="notification_institution_sso_non_canonical" + ) + .notification-body + div + i.fa.fa-fw.fa-exclamation-triangle(aria-hidden="true") + | !{translate("tried_to_log_in_with_email", {email: "{{notification.requestedEmail}}"})} !{translate("in_order_to_match_institutional_metadata_associated", {email: "{{notification.institutionEmail}}"})} + .notification-close + button(ng-click="dismiss(notification)").close.pull-right + span(aria-hidden="true") × + span.sr-only #{translate("close")} + + .alert.alert-info( + ng-switch-when="notification_institution_sso_already_registered" + ) + .notification-body + | !{translate("tried_to_register_with_email", {appName: settings.appName, email: "{{notification.email}}"})} + | #{translate("we_logged_you_in")} + .notification-action + a.btn.btn-sm.btn-info(href="/") + | #{translate("find_out_more")} + .notification-close + button(ng-click="dismiss(notification)").close.pull-right + span(aria-hidden="true") × + span.sr-only #{translate("close")} + ul.list-unstyled( ng-controller="EmailNotificationController", @@ -89,7 +177,7 @@ ) li.notification-entry( ng-repeat="userEmail in userEmails", - ng-if="showConfirmEmail(userEmail)" + ng-if="showConfirmEmail(userEmail) && projects.length > 0" ) .alert.alert-warning(ng-if="!userEmail.confirmationInflight") .notification-body diff --git a/services/web/public/src/main/project-list/project-list.js b/services/web/public/src/main/project-list/project-list.js index 0434d2ae3b..3cf8c395ac 100644 --- a/services/web/public/src/main/project-list/project-list.js +++ b/services/web/public/src/main/project-list/project-list.js @@ -12,6 +12,7 @@ define(['base', 'main/project-list/services/project-list'], function(App) { $scope.projects = window.data.projects $scope.tags = window.data.tags $scope.notifications = window.data.notifications + $scope.notificationsInstitution = window.data.notificationsInstitution $scope.allSelected = false $scope.selectedProjects = [] $scope.isArchiveableProjectSelected = false diff --git a/services/web/test/unit/src/Project/ProjectControllerTests.js b/services/web/test/unit/src/Project/ProjectControllerTests.js index 09daec1e54..e16c903de8 100644 --- a/services/web/test/unit/src/Project/ProjectControllerTests.js +++ b/services/web/test/unit/src/Project/ProjectControllerTests.js @@ -645,7 +645,7 @@ describe('ProjectController', function() { }) it('should show institution SSO available notification', function() { this.res.render = (pageName, opts) => { - expect(opts.notifications).to.deep.include({ + expect(opts.notificationsInstitution).to.deep.include({ email: 'test@overleaf.com', institutionId: 1, institutionName: 'Overleaf', @@ -663,7 +663,7 @@ describe('ProjectController', function() { } } this.res.render = (pageName, opts) => { - expect(opts.notifications).to.deep.include({ + expect(opts.notificationsInstitution).to.deep.include({ email: this.institutionEmail, institutionName: this.institutionName, templateKey: 'notification_institution_sso_linked' @@ -675,7 +675,7 @@ describe('ProjectController', function() { // when they request to link an email but the institution returns // a different email this.res.render = (pageName, opts) => { - expect(opts.notifications).to.deep.include({ + expect(opts.notificationsInstitution).to.deep.include({ institutionEmail: this.institutionEmail, requestedEmail: 'requested@overleaf.com', templateKey: 'notification_institution_sso_non_canonical' @@ -694,7 +694,7 @@ describe('ProjectController', function() { }) it('should show a notification when intent was to register via SSO but account existed', function() { this.res.render = (pageName, opts) => { - expect(opts.notifications).to.deep.include({ + expect(opts.notificationsInstitution).to.deep.include({ email: this.institutionEmail, templateKey: 'notification_institution_sso_already_registered' }) @@ -718,7 +718,7 @@ describe('ProjectController', function() { }) it('should not show institution sso available notification', function() { this.res.render = (pageName, opts) => { - expect(opts.notifications).to.deep.not.include({ + expect(opts.notificationsInstitution).to.deep.not.include({ email: 'test@overleaf.com', institutionId: 1, institutionName: 'Overleaf',