diff --git a/services/web/app/src/Features/Subscription/SubscriptionLocator.js b/services/web/app/src/Features/Subscription/SubscriptionLocator.js index 6f28796ffd..4df3d3b9b0 100644 --- a/services/web/app/src/Features/Subscription/SubscriptionLocator.js +++ b/services/web/app/src/Features/Subscription/SubscriptionLocator.js @@ -40,6 +40,17 @@ const SubscriptionLocator = { .exec(callback) }, + getAdminEmail(subscriptionId, callback) { + Subscription.findById(subscriptionId) + .populate('admin_id', 'email') + .exec((err, subscription) => { + if (err) { + return callback(err) + } + callback(err, subscription?.admin_id?.email) + }) + }, + getAdminEmailAndName(subscriptionId, callback) { Subscription.findById(subscriptionId) .populate('admin_id', ['email', 'first_name', 'last_name']) @@ -122,6 +133,7 @@ SubscriptionLocator.promises = { SubscriptionLocator.getManagedGroupSubscriptions ), getMemberSubscriptions: promisify(SubscriptionLocator.getMemberSubscriptions), + getAdminEmail: promisify(SubscriptionLocator.getAdminEmail), getAdminEmailAndName: promisify(SubscriptionLocator.getAdminEmailAndName), getSubscription: promisify(SubscriptionLocator.getSubscription), getSubscriptionByMemberIdAndId: promisify( diff --git a/services/web/app/src/Features/Subscription/TeamInvitesController.js b/services/web/app/src/Features/Subscription/TeamInvitesController.js index 9b66d9709c..7c25d17de5 100644 --- a/services/web/app/src/Features/Subscription/TeamInvitesController.js +++ b/services/web/app/src/Features/Subscription/TeamInvitesController.js @@ -1,4 +1,5 @@ const settings = require('@overleaf/settings') +const logger = require('@overleaf/logger') const TeamInvitesHandler = require('./TeamInvitesHandler') const SessionManager = require('../Authentication/SessionManager') const SubscriptionLocator = require('./SubscriptionLocator') @@ -106,6 +107,14 @@ async function viewInvite(req, res, next) { validationStatus: Object.fromEntries(validationStatus), }) } else { + let currentManagedUserAdminEmail + try { + currentManagedUserAdminEmail = + await SubscriptionLocator.promises.getAdminEmail(req.managedBy) + } catch (err) { + logger.error({ err }, 'error getting subscription admin email') + } + return res.render('subscriptions/team/invite', { inviterName: invite.inviterName, inviteToken: invite.token, @@ -113,6 +122,7 @@ async function viewInvite(req, res, next) { appName: settings.appName, expired: req.query.expired, userRestrictions: Array.from(req.userRestrictions || []), + currentManagedUserAdminEmail, }) } } else { diff --git a/services/web/app/src/Features/User/UserPagesController.js b/services/web/app/src/Features/User/UserPagesController.js index b44bd6e583..7f96fe837a 100644 --- a/services/web/app/src/Features/User/UserPagesController.js +++ b/services/web/app/src/Features/User/UserPagesController.js @@ -6,6 +6,7 @@ const Settings = require('@overleaf/settings') const AuthenticationController = require('../Authentication/AuthenticationController') const SessionManager = require('../Authentication/SessionManager') const NewsletterManager = require('../Newsletter/NewsletterManager') +const SubscriptionLocator = require('../Subscription/SubscriptionLocator') const _ = require('lodash') const { expressify } = require('../../util/promises') const Features = require('../../infrastructure/Features') @@ -81,6 +82,14 @@ async function settingsPage(req, res) { } } + let currentManagedUserAdminEmail + try { + currentManagedUserAdminEmail = + await SubscriptionLocator.promises.getAdminEmail(req.managedBy) + } catch (err) { + logger.error({ err }, 'error getting subscription admin email') + } + res.render('user/settings', { title: 'account_settings', user: { @@ -128,6 +137,7 @@ async function settingsPage(req, res) { emailAddressLimit: Settings.emailAddressLimit, isManagedAccount: !!req.managedBy, userRestrictions: Array.from(req.userRestrictions || []), + currentManagedUserAdminEmail, }) } diff --git a/services/web/app/views/subscriptions/team/invite.pug b/services/web/app/views/subscriptions/team/invite.pug index 90a6e33aae..01dc6e9173 100644 --- a/services/web/app/views/subscriptions/team/invite.pug +++ b/services/web/app/views/subscriptions/team/invite.pug @@ -20,7 +20,7 @@ block content div(ng-show="view =='restrictedByManagedGroup'") .alert.alert-info #{translate("restricted")} - p #{translate("account_managed_by_group_administrator")} + p #{translate("account_managed_by_group_administrator", {admin: currentManagedUserAdminEmail})} div(ng-show="view =='hasIndividualRecurlySubscription'") p #{translate("cancel_personal_subscription_first")} diff --git a/services/web/app/views/user/settings.pug b/services/web/app/views/user/settings.pug index a6c265c05e..65e6a8b5dc 100644 --- a/services/web/app/views/user/settings.pug +++ b/services/web/app/views/user/settings.pug @@ -25,6 +25,7 @@ block append meta meta(name="ol-showPersonalAccessToken", data-type="boolean" content=showPersonalAccessToken) meta(name="ol-personalAccessTokens", data-type="json" content=personalAccessTokens) meta(name="ol-emailAddressLimit", data-type="json", content=emailAddressLimit) + meta(name="ol-currentManagedUserAdminEmail" data-type="string" content=currentManagedUserAdminEmail) block content main.content.content-alt#settings-page-root diff --git a/services/web/frontend/js/features/settings/components/managed-account-alert.tsx b/services/web/frontend/js/features/settings/components/managed-account-alert.tsx index 446542bb4b..5df0694fcd 100644 --- a/services/web/frontend/js/features/settings/components/managed-account-alert.tsx +++ b/services/web/frontend/js/features/settings/components/managed-account-alert.tsx @@ -1,9 +1,12 @@ -import { Trans, useTranslation } from 'react-i18next' +import { Trans } from 'react-i18next' import getMeta from '../../../utils/meta' export default function ManagedAccountAlert() { - const { t } = useTranslation() const isManaged = getMeta('ol-isManagedAccount', false) + const currentManagedUserAdminEmail: string = getMeta( + 'ol-currentManagedUserAdminEmail', + '' + ) if (!isManaged) { return null @@ -16,7 +19,14 @@ export default function ManagedAccountAlert() {
- {t('account_managed_by_group_administrator')} + + +
__email__ has been linked to your __institutionName__ institutional account.", "account_has_past_due_invoice_change_plan_warning": "Your account currently has a past due invoice. You will not be able to change your plan until this is resolved.", "account_linking": "Account Linking", - "account_managed_by_group_administrator": "Your account is managed by your group administrator", + "account_managed_by_group_administrator": "Your account is managed by your group administrator (__admin__)", "account_not_linked_to_dropbox": "Your account is not linked to Dropbox", "account_settings": "Account Settings", "account_with_email_exists": "It looks like an __appName__ account with the email __email__ already exists.", diff --git a/services/web/test/unit/src/User/UserPagesControllerTests.js b/services/web/test/unit/src/User/UserPagesControllerTests.js index b688de4334..36f7cbc8ee 100644 --- a/services/web/test/unit/src/User/UserPagesControllerTests.js +++ b/services/web/test/unit/src/User/UserPagesControllerTests.js @@ -48,6 +48,10 @@ describe('UserPagesController', function () { zotero: { encrypted: 'bbbb' }, }, } + this.adminEmail = 'group-admin-email@overleaf.com' + this.subscriptionViewModel = { + memberGroupSubscriptions: [], + } this.UserGetter = { getUser: sinon.stub(), @@ -73,6 +77,11 @@ describe('UserPagesController', function () { this.PersonalAccessTokenManager = { listTokens: sinon.stub().returns([]), } + this.SubscriptionLocator = { + promises: { + getAdminEmail: sinon.stub().returns(this.adminEmail), + }, + } this.UserPagesController = SandboxedModule.require(modulePath, { requires: { '@overleaf/settings': this.settings, @@ -82,6 +91,7 @@ describe('UserPagesController', function () { '../Errors/ErrorController': this.ErrorController, '../Authentication/AuthenticationController': this.AuthenticationController, + '../Subscription/SubscriptionLocator': this.SubscriptionLocator, '../../infrastructure/Features': this.Features, '../../../../modules/oauth2-server/app/src/OAuthPersonalAccessTokenManager': this.PersonalAccessTokenManager, @@ -325,6 +335,14 @@ describe('UserPagesController', function () { return this.UserPagesController.settingsPage(this.req, this.res) }) + it('should send the correct managed user admin email', function (done) { + this.res.render = (page, opts) => { + expect(opts.currentManagedUserAdminEmail).to.equal(this.adminEmail) + return done() + } + return this.UserPagesController.settingsPage(this.req, this.res) + }) + describe('when ldap.updateUserDetailsOnLogin is true', function () { beforeEach(function () { return (this.settings.ldap = { updateUserDetailsOnLogin: true })