From 9e8e124113c67add6fcaa2d11bae5b74f0740de6 Mon Sep 17 00:00:00 2001 From: Davinder Singh Date: Mon, 10 Jul 2023 10:21:49 +0100 Subject: [PATCH] Merge pull request #13591 from overleaf/ab-account-enrollment [web] Managed users - account enrollment GitOrigin-RevId: 6fc9ef8c44394eb92d3afd2022433b0b6d508503 --- .../Authorization/PermissionsManager.js | 12 +++- .../Features/Project/ProjectListController.js | 48 ++++++++----- .../src/Features/User/UserPagesController.js | 1 + .../UserMembershipAuthorization.js | 11 +++ .../UserMembershipEntityConfigs.js | 4 ++ .../UserMembershipMiddleware.js | 9 +++ services/web/app/views/project/list-react.pug | 1 + services/web/app/views/user/settings.pug | 1 + services/web/config/settings.defaults.js | 1 + .../web/frontend/extracted-translations.json | 25 +++++++ .../notifications/user-notifications.tsx | 29 +++++++- .../components/managed-account-alert.tsx | 31 +++++++++ .../js/features/settings/components/root.tsx | 2 + .../stylesheets/app/account-settings.less | 19 +++++ .../frontend/stylesheets/core/variables.less | 3 + .../stylesheets/modules/managed-users.less | 69 ++++++++++++++++++- services/web/locales/en.json | 24 +++++++ 17 files changed, 268 insertions(+), 22 deletions(-) create mode 100644 services/web/frontend/js/features/settings/components/managed-account-alert.tsx diff --git a/services/web/app/src/Features/Authorization/PermissionsManager.js b/services/web/app/src/Features/Authorization/PermissionsManager.js index 1f7bfb99b8..9bf076364e 100644 --- a/services/web/app/src/Features/Authorization/PermissionsManager.js +++ b/services/web/app/src/Features/Authorization/PermissionsManager.js @@ -133,8 +133,16 @@ function registerPolicy(name, capabilities, options = {}) { * @returns {Array} An array of policy names that are enforced. */ function getEnforcedPolicyNames(groupPolicy = {}) { - return Object.keys(groupPolicy).filter( - policyName => groupPolicy[policyName] !== false + if (!groupPolicy) { + return [] + } + return Object.keys( + typeof groupPolicy.toObject === 'function' + ? groupPolicy.toObject() + : groupPolicy + ).filter( + policyName => + !['__v', '_id'].includes(policyName) && groupPolicy[policyName] !== false ) // filter out the policies that are not enforced } diff --git a/services/web/app/src/Features/Project/ProjectListController.js b/services/web/app/src/Features/Project/ProjectListController.js index 8dfcf2c226..0981ed58ef 100644 --- a/services/web/app/src/Features/Project/ProjectListController.js +++ b/services/web/app/src/Features/Project/ProjectListController.js @@ -32,7 +32,6 @@ const SplitTestHandler = require('../SplitTests/SplitTestHandler') /** @typedef {import("../../../../types/project/dashboard/api").Sort} Sort */ /** @typedef {import("./types").AllUsersProjects} AllUsersProjects */ /** @typedef {import("./types").MongoProject} MongoProject */ - /** @typedef {import("../Tags/types").Tag} Tag */ const _ssoAvailable = (affiliation, session, linkedInstitutionIds) => { @@ -90,6 +89,10 @@ async function projectListPage(req, res, next) { // - object - the subscription data object let usersBestSubscription let survey + let userIsMemberOfGroupSubscription = false + let groupSubscriptionsPendingEnrollment = [] + + const isSaas = Features.hasFeature('saas') const userId = SessionManager.getLoggedInUserId(req.session) const projectsBlobPending = _getProjects(userId).catch(err => { @@ -98,7 +101,9 @@ async function projectListPage(req, res, next) { }) const user = await User.findById( userId, - 'email emails features lastPrimaryEmailCheck signUpDate' + `email emails features lastPrimaryEmailCheck signUpDate${ + isSaas ? ' enrollment' : '' + }` ) // Handle case of deleted user @@ -107,7 +112,7 @@ async function projectListPage(req, res, next) { return } - if (Features.hasFeature('saas')) { + if (isSaas) { try { usersBestSubscription = await SubscriptionViewModelBuilder.promises.getBestSubscription({ @@ -119,6 +124,24 @@ async function projectListPage(req, res, next) { "Failed to get user's best subscription" ) } + try { + const { isMember, subscriptions } = + await LimitationsManager.promises.userIsMemberOfGroupSubscription(user) + + userIsMemberOfGroupSubscription = isMember + + // TODO use helper function + if (!user.enrollment?.managedBy) { + groupSubscriptionsPendingEnrollment = subscriptions.filter( + subscription => subscription.groupPlan && subscription.groupPolicy + ) + } + } catch (error) { + logger.error( + { err: error }, + 'Failed to check whether user is a member of group subscription' + ) + } try { survey = await SurveyHandler.promises.getSurvey(userId) @@ -280,20 +303,6 @@ async function projectListPage(req, res, next) { status: prefetchedProjectsBlob ? 'success' : 'too-slow', }) - let userIsMemberOfGroupSubscription = false - try { - const userIsMemberOfGroupSubscriptionPromise = - await LimitationsManager.promises.userIsMemberOfGroupSubscription(user) - - userIsMemberOfGroupSubscription = - userIsMemberOfGroupSubscriptionPromise.isMember - } catch (error) { - logger.error( - { err: error }, - 'Failed to check whether user is a member of group subscription' - ) - } - // in v2 add notifications for matching university IPs if (Settings.overleaf != null && req.ip !== user.lastLoginIp) { try { @@ -395,6 +404,11 @@ async function projectListPage(req, res, next) { showINRBanner, projectDashboardReact: true, // used in navbar welcomePageRedesignVariant: welcomePageRedesignAssignment.variant, + groupSubscriptionsPendingEnrollment: + groupSubscriptionsPendingEnrollment.map(subscription => ({ + groupId: subscription._id, + groupName: subscription.teamName, + })), }) } diff --git a/services/web/app/src/Features/User/UserPagesController.js b/services/web/app/src/Features/User/UserPagesController.js index db91812cb4..56cf51ad76 100644 --- a/services/web/app/src/Features/User/UserPagesController.js +++ b/services/web/app/src/Features/User/UserPagesController.js @@ -126,6 +126,7 @@ async function settingsPage(req, res) { showPersonalAccessToken, personalAccessTokens, emailAddressLimit: Settings.emailAddressLimit, + isManagedAccount: !!user.enrollment?.managedBy, }) } diff --git a/services/web/app/src/Features/UserMembership/UserMembershipAuthorization.js b/services/web/app/src/Features/UserMembership/UserMembershipAuthorization.js index e7cd9caccf..e3c29d32d6 100644 --- a/services/web/app/src/Features/UserMembership/UserMembershipAuthorization.js +++ b/services/web/app/src/Features/UserMembership/UserMembershipAuthorization.js @@ -22,5 +22,16 @@ const UserMembershipAuthorization = { ) } }, + + isEntityMember() { + return req => { + if (!req.entity) { + return false + } + return req.entity[req.entityConfig.fields.membership].some(accessUserId => + accessUserId.equals(req.user._id) + ) + } + }, } module.exports = UserMembershipAuthorization diff --git a/services/web/app/src/Features/UserMembership/UserMembershipEntityConfigs.js b/services/web/app/src/Features/UserMembership/UserMembershipEntityConfigs.js index 7549049a9d..94bc01f7f2 100644 --- a/services/web/app/src/Features/UserMembership/UserMembershipEntityConfigs.js +++ b/services/web/app/src/Features/UserMembership/UserMembershipEntityConfigs.js @@ -8,6 +8,7 @@ module.exports = { read: ['invited_emails', 'teamInvites', 'member_ids'], write: null, access: 'manager_ids', + membership: 'member_ids', name: 'teamName', }, baseQuery: { @@ -47,6 +48,7 @@ module.exports = { read: ['manager_ids'], write: 'manager_ids', access: 'manager_ids', + membership: 'member_ids', name: 'teamName', }, baseQuery: { @@ -72,6 +74,7 @@ module.exports = { read: ['managerIds'], write: 'managerIds', access: 'managerIds', + membership: 'member_ids', name: 'name', }, translations: { @@ -95,6 +98,7 @@ module.exports = { read: ['managerIds'], write: 'managerIds', access: 'managerIds', + membership: 'member_ids', name: 'name', }, translations: { diff --git a/services/web/app/src/Features/UserMembership/UserMembershipMiddleware.js b/services/web/app/src/Features/UserMembership/UserMembershipMiddleware.js index e80c4e8757..fc443cf67a 100644 --- a/services/web/app/src/Features/UserMembership/UserMembershipMiddleware.js +++ b/services/web/app/src/Features/UserMembership/UserMembershipMiddleware.js @@ -22,6 +22,15 @@ const UserMembershipMiddleware = { ]), ], + requireGroup: [fetchEntityConfig('group'), fetchEntity(), requireEntity()], + + requireGroupAccess: [ + AuthenticationController.requireLogin(), + fetchEntityConfig('group'), + fetchEntity(), + requireEntity(), + ], + requireGroupManagementAccess: [ AuthenticationController.requireLogin(), fetchEntityConfig('group'), diff --git a/services/web/app/views/project/list-react.pug b/services/web/app/views/project/list-react.pug index 3cd9f7f250..f3d8565cd3 100644 --- a/services/web/app/views/project/list-react.pug +++ b/services/web/app/views/project/list-react.pug @@ -30,6 +30,7 @@ block append meta meta(name="ol-groupsAndEnterpriseBannerVariant" data-type="string" content=groupsAndEnterpriseBannerVariant) meta(name="ol-showINRBanner" data-type="boolean" content=showINRBanner) meta(name="ol-welcomePageRedesignVariant" data-type="string" content=welcomePageRedesignVariant) + meta(name="ol-groupSubscriptionsPendingEnrollment" data-type="json" content=groupSubscriptionsPendingEnrollment) block content main.content.content-alt.project-list-react#project-list-root diff --git a/services/web/app/views/user/settings.pug b/services/web/app/views/user/settings.pug index a6c265c05e..e29f0d2184 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-isManagedAccount" data-type="boolean" content=isManagedAccount) block content main.content.content-alt#settings-page-root diff --git a/services/web/config/settings.defaults.js b/services/web/config/settings.defaults.js index d9a6916f9a..80c39035f0 100644 --- a/services/web/config/settings.defaults.js +++ b/services/web/config/settings.defaults.js @@ -808,6 +808,7 @@ module.exports = { editorLeftMenuSync: [], editorLeftMenuManageTemplate: [], oauth2Server: [], + managedGroupSubscriptionEnrollmentNotification: [], }, moduleImportSequence: ['launchpad', 'server-ce-scripts', 'user-activate'], diff --git a/services/web/frontend/extracted-translations.json b/services/web/frontend/extracted-translations.json index 52229c81c9..8410af827e 100644 --- a/services/web/frontend/extracted-translations.json +++ b/services/web/frontend/extracted-translations.json @@ -14,10 +14,12 @@ "about_to_leave_projects": "", "about_to_trash_projects": "", "accept": "", + "accept_invitation": "", "accepted_invite": "", "access_denied": "", "account_has_been_link_to_institution_account": "", "account_has_past_due_invoice_change_plan_warning": "", + "account_managed_by_group_administrator": "", "account_not_linked_to_dropbox": "", "account_settings": "", "acct_linked_to_institution_acct_2": "", @@ -117,6 +119,7 @@ "change_plan": "", "change_primary_email_address_instructions": "", "change_project_owner": "", + "change_the_ownership_of_your_personal_projects": "", "change_to_group_plan": "", "change_to_this_plan": "", "changing_the_position_of_your_figure": "", @@ -220,6 +223,7 @@ "discount_of": "", "dismiss": "", "dismiss_error_popup": "", + "do_this_later": "", "do_you_want_to_change_your_primary_email_address_to": "", "do_you_want_to_overwrite_them": "", "documentation": "", @@ -403,6 +407,8 @@ "go_to_pdf_location_in_code": "", "go_to_settings": "", "group_admin": "", + "group_admin_or_managers_can_reassign_projects": "", + "group_managed_by_group_administrator": "", "group_plan_tooltip": "", "group_plan_with_name_tooltip": "", "group_subscription": "", @@ -506,6 +512,7 @@ "join_project": "", "joining": "", "keep_current_plan": "", + "keep_personal_projects_separate": "", "keybindings": "", "labels_help_you_to_easily_reference_your_figures": "", "labs_program_already_participating": "", @@ -525,6 +532,7 @@ "learn_more": "", "learn_more_about_link_sharing": "", "leave": "", + "leave_any_group_subscriptions": "", "leave_group": "", "leave_now": "", "leave_projects": "", @@ -560,6 +568,7 @@ "log_hint_extra_info": "", "log_in_with_primary_email_address": "", "log_viewer_error": "", + "login_to_transfer_account": "", "login_with_service": "", "login_with_service_will_stop_working_soon": "", "logs_and_output_files": "", @@ -616,6 +625,8 @@ "navigate_log_source": "", "navigation": "", "need_anything_contact_us_at": "", + "need_contact_group_admin_to_make_changes": "", + "need_make_changes": "", "need_more_than_x_licenses": "", "need_to_add_new_primary_before_remove": "", "need_to_leave": "", @@ -667,6 +678,7 @@ "ok": "", "on": "", "on_free_plan_upgrade_to_access_features": "", + "only_group_admin_or_managers_can_delete_your_account": "", "open_project": "", "optional": "", "or": "", @@ -804,6 +816,7 @@ "remove_from_group": "", "remove_manager": "", "remove_or_replace_figure": "", + "remove_secondary_email_addresses": "", "remove_tag": "", "removing": "", "rename": "", @@ -896,6 +909,7 @@ "session_expired_redirecting_to_login": "", "sessions": "", "settings": "", + "setup_another_account_under_a_personal_email_address": "", "share": "", "share_project": "", "share_with_your_collabs": "", @@ -999,6 +1013,7 @@ "to_add_email_accounts_need_to_be_linked_2": "", "to_add_more_collaborators": "", "to_change_access_permissions": "", + "to_confirm_transfer_enter_email_address": "", "to_modify_your_subscription_go_to": "", "toggle_compile_options_menu": "", "token": "", @@ -1040,6 +1055,11 @@ "track_changes_is_on": "", "tracked_change_added": "", "tracked_change_deleted": "", + "transfer_account": "", + "transfer_management_of_your_account": "", + "transfer_management_of_your_account_to_x": "", + "transfer_management_resolve_following_issues": "", + "transferring": "", "trash": "", "trash_projects": "", "trashed": "", @@ -1072,6 +1092,8 @@ "unlink_dropbox_warning": "", "unlink_github_repository": "", "unlink_github_warning": "", + "unlink_linked_accounts": "", + "unlink_linked_google_account": "", "unlink_provider_account_title": "", "unlink_provider_account_warning": "", "unlink_reference": "", @@ -1123,6 +1145,7 @@ "we_logged_you_in": "", "wed_love_you_to_stay": "", "welcome_to_sl": "", + "what_does_this_mean_for_you": "", "when_you_tick_the_include_caption_box": "", "wide": "", "with_premium_subscription_you_also_get": "", @@ -1150,6 +1173,8 @@ "you_can_now_log_in_sso": "", "you_dont_have_any_repositories": "", "you_have_added_x_of_group_size_y": "", + "you_have_been_invited_to_transfer_management_of_your_account": "", + "you_have_been_invited_to_transfer_management_of_your_account_to": "", "your_affiliation_is_confirmed": "", "your_browser_does_not_support_this_feature": "", "your_git_access_info": "", diff --git a/services/web/frontend/js/features/project-list/components/notifications/user-notifications.tsx b/services/web/frontend/js/features/project-list/components/notifications/user-notifications.tsx index 5749ad02e0..3bd9da76d4 100644 --- a/services/web/frontend/js/features/project-list/components/notifications/user-notifications.tsx +++ b/services/web/frontend/js/features/project-list/components/notifications/user-notifications.tsx @@ -1,3 +1,4 @@ +import { JSXElementConstructor } from 'react' import Common from './groups/common' import Institution from './groups/institution' import ConfirmEmail from './groups/confirm-email' @@ -6,13 +7,39 @@ import GroupsAndEnterpriseBanner from './groups-and-enterprise-banner' import WritefullPromoBanner from './writefull-promo-banner' import INRBanner from './ads/inr-banner' import getMeta from '../../../../utils/meta' +import importOverleafModules from '../../../../../macros/import-overleaf-module.macro' + +type Subscription = { + groupId: string + groupName: string +} + +const [enrollmentNotificationModule] = importOverleafModules( + 'managedGroupSubscriptionEnrollmentNotification' +) +const EnrollmentNotification: JSXElementConstructor<{ + groupId: string + groupName: string +}> = enrollmentNotificationModule?.import.default function UserNotifications() { - const showIRNBanner = getMeta('ol-showINRBanner') + const showIRNBanner = getMeta('ol-showINRBanner', false) + const groupSubscriptionsPendingEnrollment: Subscription[] = getMeta( + 'ol-groupSubscriptionsPendingEnrollment', + [] + ) return (
+
diff --git a/services/web/frontend/stylesheets/app/account-settings.less b/services/web/frontend/stylesheets/app/account-settings.less index 8b8ee2aa70..3fea13f700 100644 --- a/services/web/frontend/stylesheets/app/account-settings.less +++ b/services/web/frontend/stylesheets/app/account-settings.less @@ -233,3 +233,22 @@ tbody > tr.affiliations-table-warning-row > td { .linking-git-bridge-revoke-button { padding: 2px 4px; } + +.enrollment-alert { + width: 100%; + border-radius: 4px; + padding: 12px; + background-color: @blue-10; + border: 1px solid @blue-20; + display: flex; + flex-direction: row; + + .icon { + flex: 1 1 auto; + padding: 0 16px 0 4px; + } + + a { + text-decoration: underline; + } +} diff --git a/services/web/frontend/stylesheets/core/variables.less b/services/web/frontend/stylesheets/core/variables.less index e45cf19c76..8bd520aa41 100644 --- a/services/web/frontend/stylesheets/core/variables.less +++ b/services/web/frontend/stylesheets/core/variables.less @@ -37,6 +37,7 @@ @blue: #405ebf; @blue-10: #f1f4f9; +@blue-20: #c3d0e3; @blue-30: #97b6e5; @blue-40: #6597e0; @blue-70: #214475; @@ -46,6 +47,8 @@ @green-30: #8cca86; @red: #a93529; @yellow: #a1a729; +@yellow-10: #fcf1e3; +@yellow-50: #8f5514; @orange: #f89406; @orange-dark: #9e5e04; @pink: #c3325f; diff --git a/services/web/frontend/stylesheets/modules/managed-users.less b/services/web/frontend/stylesheets/modules/managed-users.less index 9132b89bf9..3fb6ecb72c 100644 --- a/services/web/frontend/stylesheets/modules/managed-users.less +++ b/services/web/frontend/stylesheets/modules/managed-users.less @@ -1,6 +1,4 @@ .group-settings-title { - min-width: 0; - color: @content-secondary; font-family: Lato, sans-serif; font-size: @font-size-large; line-height: 28px; @@ -9,6 +7,73 @@ text-overflow: ellipsis; } +.enrollment-invite { + color: @neutral-90; + + .inner-card { + background-color: @gray-lightest; + margin: 16px 0; + padding: 16px; + + &.warning { + background-color: @yellow-10; + } + + .list-row { + display: flex; + flex-direction: row; + + .icon { + display: flex; + flex: 1 1 10%; + > span { + font-size: 16px; + } + padding-right: 12px; + font-size: 24px; + + &.error { + color: @yellow-50; + margin-top: 2px; + } + } + } + + a { + text-decoration: underline; + } + } + + .title { + font-family: Lato, sans-serif; + font-size: @font-size-large; + line-height: 28px; + font-weight: bold; + overflow: hidden; + text-overflow: ellipsis; + } + + .subtitle { + font-family: Lato, sans-serif; + font-size: @font-size-base; + line-height: 24px; + font-weight: bold; + overflow: hidden; + text-overflow: ellipsis; + } + + .label { + color: @gray-dark; + font-size: @font-size-small; + padding-left: 0; + } + + .btn { + width: 100%; + margin: 8px 0; + } +} + .managed-users-enabled { font-family: Lato, sans-serif; color: @ol-green; diff --git a/services/web/locales/en.json b/services/web/locales/en.json index e201eb48da..e735bdbc6a 100644 --- a/services/web/locales/en.json +++ b/services/web/locales/en.json @@ -37,6 +37,7 @@ "account_has_been_link_to_institution_account": "Your __appName__ account on __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_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.", @@ -202,6 +203,7 @@ "change_plan": "Change plan", "change_primary_email_address_instructions": "To change your primary email, please add your new primary email address first (by clicking <0>Add another email) and confirm it. Then click the <0>Make Primary button. <1>Learn more about managing your __appName__ emails.", "change_project_owner": "Change Project Owner", + "change_the_ownership_of_your_personal_projects": "Change the ownership of your personal projects to the new account. <0>Find out how to change project owner.", "change_to_annual_billing_and_save": "Get <0>__percentage__ off with annual billing. If you switch now you’ll save <1>__yearlySaving__ per year.", "change_to_group_plan": "Change to a group plan", "change_to_this_plan": "Change to this plan", @@ -359,6 +361,7 @@ "dismiss_error_popup": "Dismiss first error alert", "do_not_have_acct_or_do_not_want_to_link": "If you don’t have an __appName__ account, or if you don’t want to link to your __institutionName__ account, please click __clickText__.", "do_not_link_accounts": "Don’t link accounts", + "do_this_later": "I’ll do this later", "do_you_want_to_change_your_primary_email_address_to": "Do you want to change your primary email address to __email__?", "do_you_want_to_overwrite_them": "Do you want to overwrite them?", "document_history": "Document history", @@ -655,9 +658,11 @@ "go_to_pdf_location_in_code": "Go to PDF location in code (Tip: double click on the PDF for best results)", "go_to_settings": "Go to settings", "group_admin": "Group admin", + "group_admin_or_managers_can_reassign_projects": "Your group admin and group managers will be able to reassign ownership of your projects to another group member.", "group_admins_get_access_to": "Group admins get access to", "group_admins_get_access_to_info": "Special features available only on group plans.", "group_full": "This group is already full", + "group_managed_by_group_administrator": "This group is managed by the group administrator", "group_members_and_collaborators_get_access_to": "Group members and their project collaborators get access to", "group_members_and_collaborators_get_access_to_info": "These features are available to group members and their collaborators (other Overleaf users invited to projects owned a group member).", "group_members_get_access_to": "Group members get access to", @@ -822,6 +827,7 @@ "june": "June", "kb_suggestions_enquiry": "Have you checked our <0>__kbLink__?", "keep_current_plan": "Keep my current plan", + "keep_personal_projects_separate": "Keep personal projects separate", "keep_your_account_safe": "Keep your account safe", "keep_your_email_updated": "Keep your email updated so that you don’t lose access to your account and data.", "keybindings": "Keybindings", @@ -857,6 +863,7 @@ "learn_more_about_link_sharing": "Learn more about Link Sharing", "learn_more_lowercase": "learn more", "leave": "Leave", + "leave_any_group_subscriptions": "Leave any group subscriptions other than the one that will be managing your account. <0>Leave them from the Subscription page.", "leave_group": "Leave group", "leave_now": "Leave now", "leave_projects": "Leave Projects", @@ -919,6 +926,7 @@ "login_register_or": "or", "login_to_accept_invitation": "Login to accept invitation", "login_to_overleaf": "Log in to Overleaf", + "login_to_transfer_account": "Log in to transfer account", "login_with_email": "Log in with your email", "login_with_service": "Log in with __service__", "login_with_service_will_stop_working_soon": "Login with __service__ will stop working soon. If you log in with __service__, please take a moment to set up another SSO provider or a password.", @@ -1000,6 +1008,8 @@ "navigation": "Navigation", "nearly_activated": "You’re one step away from activating your __appName__ account!", "need_anything_contact_us_at": "If there is anything you ever need please feel free to contact us directly at", + "need_contact_group_admin_to_make_changes": "You’ll need to contact your group admin if you wish to make certain changes to your account. <0>Read more about managed users.", + "need_make_changes": "You need to make some changes", "need_more_than_x_licenses": "Need more than __x__ licenses?", "need_to_add_new_primary_before_remove": "You’ll need to add a new primary email address before you can remove this one.", "need_to_leave": "Need to leave?", @@ -1085,6 +1095,7 @@ "one_collaborator": "Only one collaborator", "one_free_collab": "One free collaborator", "online_latex_editor": "Online LaTeX Editor", + "only_group_admin_or_managers_can_delete_your_account": "Only your group admin or group managers will be able to delete your account.", "open_a_file_on_the_left": "Open a file on the left", "open_as_template": "Open as Template", "open_project": "Open Project", @@ -1304,6 +1315,7 @@ "remove_from_group": "Remove from group", "remove_manager": "Remove manager", "remove_or_replace_figure": "Remove or replace figure", + "remove_secondary_email_addresses": "Remove any secondary email addresses associated with your account. <0>Remove them in account settings.", "remove_tag": "Remove tag __tagName__", "removed": "removed", "removing": "Removing", @@ -1432,6 +1444,7 @@ "set_new_password": "Set new password", "set_password": "Set Password", "settings": "Settings", + "setup_another_account_under_a_personal_email_address": "Set up another Overleaf account under a personal email address.", "share": "Share", "share_project": "Share Project", "share_sl_to_get_rewards": "Share __appName__ with your friends and colleagues and unlock the rewards below", @@ -1600,6 +1613,7 @@ "to_add_email_accounts_need_to_be_linked_2": "To add this email, your <0>__appName__ and <0>__institutionName__ accounts will need to be linked.", "to_add_more_collaborators": "To add more collaborators or turn on link sharing, please ask the project owner", "to_change_access_permissions": "To change access permissions, please ask the project owner", + "to_confirm_transfer_enter_email_address": "To confirm the transfer, enter the email address linked to your account.", "to_many_login_requests_2_mins": "This account has had too many login requests. Please wait 2 minutes before trying to log in again", "to_modify_your_subscription_go_to": "To modify your subscription go to", "toggle_compile_options_menu": "Toggle compile options menu", @@ -1651,6 +1665,11 @@ "track_changes_is_on": "Track changes is on", "tracked_change_added": "Added", "tracked_change_deleted": "Deleted", + "transfer_account": "Transfer account", + "transfer_management_of_your_account": "Transfer management of your Overleaf account", + "transfer_management_of_your_account_to_x": "Transfer management of your Overleaf account to __groupName__", + "transfer_management_resolve_following_issues": "To transfer the management of your account, you need to resolve the following issues:", + "transferring": "Transferring", "trash": "Trash", "trash_projects": "Trash Projects", "trashed": "Trashed", @@ -1695,6 +1714,8 @@ "unlink_dropbox_warning": "Any projects that you have synced with Dropbox will be disconnected and no longer kept in sync with Dropbox. Are you sure you want to unlink your Dropbox account?", "unlink_github_repository": "Unlink GitHub Repository", "unlink_github_warning": "Any projects that you have synced with GitHub will be disconnected and no longer kept in sync with GitHub. Are you sure you want to unlink your GitHub account?", + "unlink_linked_accounts": "Unlink any linked accounts (such as ORCID ID, IEEE). <0>Remove them in Account Settings (under Linked Accounts).", + "unlink_linked_google_account": "Unlink your Google account. <0>Remove it in Account Settings (under Linked Accounts).", "unlink_provider_account_title": "Unlink __provider__ Account", "unlink_provider_account_warning": "Warning: When you unlink your account from __provider__ you will not be able to sign in using __provider__ anymore.", "unlink_reference": "Unlink References Provider", @@ -1768,6 +1789,7 @@ "website_status": "Website status", "wed_love_you_to_stay": "We’d love you to stay", "welcome_to_sl": "Welcome to __appName__!", + "what_does_this_mean_for_you": "What does this mean for you?", "when_you_tick_the_include_caption_box": "When you tick the box “Include caption” the image will be inserted into your document with a placeholder caption. To edit it, you simply select the placeholder text and type to replace it with your own.", "wide": "Wide", "will_need_to_log_out_from_and_in_with": "You will need to log out from your __email1__ account and then log in with __email2__.", @@ -1814,6 +1836,8 @@ "you_get_access_to": "You get access to", "you_get_access_to_info": "These features are available only to you (the subscriber).", "you_have_added_x_of_group_size_y": "You have added <0>__addedUsersSize__ of <1>__groupSize__ available members", + "you_have_been_invited_to_transfer_management_of_your_account": "You have been invited to transfer management of your account.", + "you_have_been_invited_to_transfer_management_of_your_account_to": "You have been invited to transfer management of your account to __groupName__.", "you_introed_high_number": " You’ve introduced <0>__numberOfPeople__ people to __appName__. Good job!", "you_introed_small_number": " You’ve introduced <0>__numberOfPeople__ person to __appName__. Good job, but can you get some more?", "you_not_introed_anyone_to_sl": "You’ve not introduced anyone to __appName__ yet. Get sharing!",