From ac997384953d05d9857487de2477f9d41779d2b0 Mon Sep 17 00:00:00 2001 From: Jessica Lawshe Date: Tue, 2 Feb 2021 08:26:31 -0600 Subject: [PATCH] Merge pull request #3609 from overleaf/jel-admin-user-affiliations Add confirmation/reconfirmation to admin user affiliation tab GitOrigin-RevId: d1e065c5e4d6d97814327742db2c4b131d4f0195 --- .../web/app/src/Features/User/UserGetter.js | 35 +++++++++++++------ .../web/frontend/js/filters/formatDate.js | 16 +++++++++ .../web/frontend/stylesheets/core/type.less | 4 +++ .../frontend/stylesheets/core/variables.less | 1 + .../src/SubscriptionDashboardTests.js | 2 ++ .../test/acceptance/src/UserEmailsTests.js | 22 +++++++++--- .../web/test/unit/src/User/UserGetterTests.js | 2 ++ 7 files changed, 68 insertions(+), 14 deletions(-) diff --git a/services/web/app/src/Features/User/UserGetter.js b/services/web/app/src/Features/User/UserGetter.js index e401320ca7..c2554e659a 100644 --- a/services/web/app/src/Features/User/UserGetter.js +++ b/services/web/app/src/Features/User/UserGetter.js @@ -13,14 +13,15 @@ const Errors = require('../Errors/Errors') const Features = require('../../infrastructure/Features') const { normalizeQuery, normalizeMultiQuery } = require('../Helpers/Mongo') -function _emailInReconfirmNotificationPeriod(emailData, institutionData) { +function _lastDayToReconfirm(emailData, institutionData) { const globalReconfirmPeriod = settings.reconfirmNotificationDays - if (!globalReconfirmPeriod) return false + if (!globalReconfirmPeriod) return undefined // only show notification for institutions with reconfirmation enabled - if (!institutionData || !institutionData.maxConfirmationMonths) return false + if (!institutionData || !institutionData.maxConfirmationMonths) + return undefined - if (!emailData.confirmedAt) return false + if (!emailData.confirmedAt) return undefined if (institutionData.ssoEnabled && !emailData.samlProviderId) { // For SSO, only show notification for linked email @@ -30,10 +31,21 @@ function _emailInReconfirmNotificationPeriod(emailData, institutionData) { // reconfirmedAt will not always be set, use confirmedAt as fallback const lastConfirmed = emailData.reconfirmedAt || emailData.confirmedAt - const lastDayToReconfirm = moment(lastConfirmed).add( - institutionData.maxConfirmationMonths, - 'months' - ) + return moment(lastConfirmed) + .add(institutionData.maxConfirmationMonths, 'months') + .toDate() +} + +function _pastReconfirmDate(lastDayToReconfirm) { + if (!lastDayToReconfirm) return false + return moment(lastDayToReconfirm).isBefore() +} + +function _emailInReconfirmNotificationPeriod(lastDayToReconfirm) { + const globalReconfirmPeriod = settings.reconfirmNotificationDays + + if (!globalReconfirmPeriod || !lastDayToReconfirm) return false + const notificationStarts = moment(lastDayToReconfirm).subtract( globalReconfirmPeriod, 'days' @@ -202,14 +214,17 @@ var decorateFullEmails = ( licence, portal } = affiliation + const lastDayToReconfirm = _lastDayToReconfirm(emailData, institution) + const pastReconfirmDate = _pastReconfirmDate(lastDayToReconfirm) const inReconfirmNotificationPeriod = _emailInReconfirmNotificationPeriod( - emailData, - institution + lastDayToReconfirm ) emailData.affiliation = { institution, inferred, inReconfirmNotificationPeriod, + lastDayToReconfirm, + pastReconfirmDate, role, department, licence, diff --git a/services/web/frontend/js/filters/formatDate.js b/services/web/frontend/js/filters/formatDate.js index 0ccd10cb85..e330230b9e 100644 --- a/services/web/frontend/js/filters/formatDate.js +++ b/services/web/frontend/js/filters/formatDate.js @@ -31,6 +31,22 @@ App.filter( } ) +App.filter( + 'utcDate', + () => + function(date, format) { + if (!date) return 'N/A' + if (format == null) { + format = 'D MMM YYYY, HH:mm:ss' + } + return ( + moment(date) + .utc() + .format(format) + ' UTC' + ) + } +) + App.filter('relativeDate', () => date => moment(date).calendar()) App.filter('fromNowDate', () => date => moment(date).fromNow()) diff --git a/services/web/frontend/stylesheets/core/type.less b/services/web/frontend/stylesheets/core/type.less index ea58d6d7f8..8496058fb7 100755 --- a/services/web/frontend/stylesheets/core/type.less +++ b/services/web/frontend/stylesheets/core/type.less @@ -389,3 +389,7 @@ address { font-style: normal; line-height: @line-height-base; } + +.orange-text { + color: @orange-dark; +} diff --git a/services/web/frontend/stylesheets/core/variables.less b/services/web/frontend/stylesheets/core/variables.less index 479fa65b89..68a502214e 100644 --- a/services/web/frontend/stylesheets/core/variables.less +++ b/services/web/frontend/stylesheets/core/variables.less @@ -33,6 +33,7 @@ @red: #a93529; @yellow: #a1a729; @orange: #f89406; +@orange-dark: #9e5e04; @pink: #c3325f; @purple: #7a43b6; diff --git a/services/web/test/acceptance/src/SubscriptionDashboardTests.js b/services/web/test/acceptance/src/SubscriptionDashboardTests.js index cc1591d514..a20cc26df9 100644 --- a/services/web/test/acceptance/src/SubscriptionDashboardTests.js +++ b/services/web/test/acceptance/src/SubscriptionDashboardTests.js @@ -521,6 +521,8 @@ describe('Subscriptions', function() { name: 'Stanford', confirmed: true }, + lastDayToReconfirm: undefined, + pastReconfirmDate: false, portal: undefined } ]) diff --git a/services/web/test/acceptance/src/UserEmailsTests.js b/services/web/test/acceptance/src/UserEmailsTests.js index 6b203e21bd..ad08ab12cb 100644 --- a/services/web/test/acceptance/src/UserEmailsTests.js +++ b/services/web/test/acceptance/src/UserEmailsTests.js @@ -998,6 +998,14 @@ describe('UserEmails', function() { describe('notification period', function() { let defaultEmail, userHelper, email1, email2, email3 const maxConfirmationMonths = 12 + const lastDayToReconfirm = moment() + .subtract(maxConfirmationMonths, 'months') + .toDate() + const oneDayBeforeLastDayToReconfirm = moment(lastDayToReconfirm) + .add(1, 'day') + .toDate() + const daysToBackdate = moment().diff(oneDayBeforeLastDayToReconfirm, 'day') + const daysToBackdateForAfterDate = daysToBackdate + 1 beforeEach(async function() { if (!Features.hasFeature('affiliations')) { @@ -1026,14 +1034,17 @@ describe('UserEmails', function() { beforeEach(async function() { // create a user with 3 affiliations at the institution. // all are within in the notification period - const backdatedDays = maxConfirmationMonths * 2 * 30 const userId = userHelper.user._id await userHelper.addEmailAndConfirm(userId, email1) await userHelper.addEmailAndConfirm(userId, email2) await userHelper.addEmailAndConfirm(userId, email3) - await userHelper.backdateConfirmation(userId, email1, backdatedDays) - await userHelper.backdateConfirmation(userId, email2, backdatedDays) - await userHelper.backdateConfirmation(userId, email3, backdatedDays) + await userHelper.backdateConfirmation(userId, email1, daysToBackdate) + await userHelper.backdateConfirmation(userId, email2, daysToBackdate) + await userHelper.backdateConfirmation( + userId, + email3, + daysToBackdateForAfterDate + ) }) it('should flag inReconfirmNotificationPeriod for all affiliations in period', async function() { @@ -1045,12 +1056,15 @@ describe('UserEmails', function() { expect( fullEmails[1].affiliation.inReconfirmNotificationPeriod ).to.equal(true) + expect(fullEmails[1].affiliation.pastReconfirmDate).to.equal(false) expect( fullEmails[2].affiliation.inReconfirmNotificationPeriod ).to.equal(true) + expect(fullEmails[2].affiliation.pastReconfirmDate).to.equal(false) expect( fullEmails[3].affiliation.inReconfirmNotificationPeriod ).to.equal(true) + expect(fullEmails[3].affiliation.pastReconfirmDate).to.equal(true) }) describe('should flag emails before their confirmation expires, but within the notification period', function() { diff --git a/services/web/test/unit/src/User/UserGetterTests.js b/services/web/test/unit/src/User/UserGetterTests.js index 35fb6f48c6..fb57f22a94 100644 --- a/services/web/test/unit/src/User/UserGetterTests.js +++ b/services/web/test/unit/src/User/UserGetterTests.js @@ -195,8 +195,10 @@ describe('UserGetter', function() { inferred: affiliationsData[0].inferred, department: affiliationsData[0].department, role: affiliationsData[0].role, + lastDayToReconfirm: undefined, licence: affiliationsData[0].licence, inReconfirmNotificationPeriod: false, + pastReconfirmDate: false, portal: undefined } },