diff --git a/services/web/app/src/Features/Notifications/NotificationsBuilder.js b/services/web/app/src/Features/Notifications/NotificationsBuilder.js index 30ef8b97d5..58c4f68a1a 100644 --- a/services/web/app/src/Features/Notifications/NotificationsBuilder.js +++ b/services/web/app/src/Features/Notifications/NotificationsBuilder.js @@ -281,29 +281,6 @@ function personalAndGroupSubscriptions(userId) { } } -/** - * @param {string} [userId] - */ -function ieeeCollabratecRetirement(userId) { - return { - key: 'notification-ieee-collabratec-retirement', - create(callback) { - NotificationsHandler.createNotification( - userId, - this.key, - 'notification_ieee_collabratec_retirement', - {}, - new Date('2024-08-01'), - false, - callback - ) - }, - deleteAllUnread(callback) { - NotificationsHandler.markAsReadByKeyOnlyBulk(this.key, callback) - }, - } -} - const NotificationsBuilder = { // Note: notification keys should be url-safe dropboxUnlinkedDueToLapsedReconfirmation, @@ -315,7 +292,6 @@ const NotificationsBuilder = { tpdsFileLimit, groupInvitation, personalAndGroupSubscriptions, - ieeeCollabratecRetirement, } NotificationsBuilder.promises = { @@ -343,9 +319,6 @@ NotificationsBuilder.promises = { personalAndGroupSubscriptions(userId) { return promisifyAll(personalAndGroupSubscriptions(userId)) }, - ieeeCollabratecRetirement(userId) { - return promisifyAll(ieeeCollabratecRetirement(userId)) - }, } module.exports = NotificationsBuilder diff --git a/services/web/frontend/extracted-translations.json b/services/web/frontend/extracted-translations.json index 5d0e543e17..841188a7a0 100644 --- a/services/web/frontend/extracted-translations.json +++ b/services/web/frontend/extracted-translations.json @@ -879,7 +879,6 @@ "normally_x_price_per_year": "", "not_managed": "", "not_now": "", - "notification_ieee_collabratec_retirement_message": "", "notification_personal_and_group_subscriptions": "", "notification_project_invite_accepted_message": "", "notification_project_invite_message": "", diff --git a/services/web/frontend/js/features/project-list/components/notifications/groups/common.tsx b/services/web/frontend/js/features/project-list/components/notifications/groups/common.tsx index 7b305af905..328504fbda 100644 --- a/services/web/frontend/js/features/project-list/components/notifications/groups/common.tsx +++ b/services/web/frontend/js/features/project-list/components/notifications/groups/common.tsx @@ -10,7 +10,6 @@ import { Notification as NotificationType, } from '../../../../../../../types/project/dashboard/notification' import GroupInvitationNotification from './group-invitation/group-invitation' -import IEEERetirementBanner from '../ieee-retirement-banner' import { debugConsole } from '@/utils/debugging' import OLButton from '@/features/ui/components/ol/ol-button' @@ -279,8 +278,6 @@ function CommonNotification({ notification }: CommonNotificationProps) { /> ) : templateKey === 'notification_group_invitation' ? ( - ) : templateKey === 'notification_ieee_collabratec_retirement' ? ( - ) : templateKey === 'notification_personal_and_group_subscriptions' ? ( { - eventTracking.sendMB('promo-dismiss', { - name: 'ieee - retirement', - }) - if (id) { - handleDismiss(id) - } - }, [id, handleDismiss]) - - const handleClickPlans = useCallback(() => { - eventTracking.sendMB('promo-click', { - name: 'ieee - retirement', - content: 'plans', - }) - }, []) - - const handleClickEmail = useCallback(() => { - eventTracking.sendMB('promo-click', { - name: 'ieee - retirement', - content: 'email', - }) - }, []) - - useEffect(() => { - if (!viewEventSent) { - eventTracking.sendMB('promo-prompt', { - name: 'ieee - retirement', - }) - viewEventSent = true - } - }, []) - - return ( - , - // eslint-disable-next-line jsx-a11y/anchor-has-content,react/jsx-key - , - ]} - /> - } - /> - ) -} diff --git a/services/web/locales/en.json b/services/web/locales/en.json index b83c626d15..eb52e60f40 100644 --- a/services/web/locales/en.json +++ b/services/web/locales/en.json @@ -1291,7 +1291,6 @@ "not_registered": "Not registered", "note_features_under_development": "<0>Please note that features in this program are still being tested and actively developed. This means that they might <0>change, be <0>removed or <0>become part of a premium plan", "notification_features_upgraded_by_affiliation": "Good news! Your affiliated organization __institutionName__ has an Overleaf subscription, and you now have access to all of Overleaf’s Professional features.", - "notification_ieee_collabratec_retirement_message": "As of April 2, 2024, free access to Overleaf Professional subscriptions via Collabratec or IEEE accounts has ended. Need to upgrade? <0>View our plans. Contact <1>authors@ieee.org with any questions.", "notification_personal_and_group_subscriptions": "We’ve spotted that you’ve got <0>more than one active __appName__ subscription. To avoid paying more than you need to, <1>review your subscriptions.", "notification_personal_subscription_not_required_due_to_affiliation": " Good news! Your affiliated organization __institutionName__ has an Overleaf subscription, and you now have access to Overleaf’s Professional features through your affiliation. You can cancel your individual subscription without losing access to any features.", "notification_project_invite": "__userName__ would like you to join __projectName__ Join Project", diff --git a/services/web/locales/zh-CN.json b/services/web/locales/zh-CN.json index 1c751c7330..114d6768ba 100644 --- a/services/web/locales/zh-CN.json +++ b/services/web/locales/zh-CN.json @@ -1262,7 +1262,6 @@ "not_registered": "未注册", "note_features_under_development": "<0>请注意此计划中的功能仍在测试和快速开发中。 这意味着它们可能<0>改变、<0>被删除或<0>成为高级计划的一部分", "notification_features_upgraded_by_affiliation": "好消息!您的组织__institutionName__已有 Overleaf 订阅,并且您现在可以访问 Overleaf 的所有专业功能。", - "notification_ieee_collabratec_retirement_message": "从 2024年 1 月 31日起, IEEE 不再提供 Overleaf premium 功能给协作者。有问题请联系 <0>authors@ieee.org。需要升级吗? <1>查看我们的订阅计划", "notification_personal_and_group_subscriptions": "我们发现您有<0>多个活跃的 __appName__ 订阅。 为避免支付超出您需要的费用,请<1>检查您的订阅。", "notification_personal_subscription_not_required_due_to_affiliation": " 好消息!您的组织 __institutionName__ 与 Overleaf 有合作关系。您可以取消您的个人订阅,而不会失去访问您的任何利益。", "notification_project_invite": "__userName__ 想让您加入 __projectName__ 加入项目", diff --git a/services/web/scripts/add_notification_ieee_collabratec_users.js b/services/web/scripts/add_notification_ieee_collabratec_users.js deleted file mode 100644 index 1e31c3bb38..0000000000 --- a/services/web/scripts/add_notification_ieee_collabratec_users.js +++ /dev/null @@ -1,165 +0,0 @@ -const path = require('path') -const fs = require('fs') -const NotificationsBuilder = require('../app/src/Features/Notifications/NotificationsBuilder') -const { waitForDb } = require('../app/src/infrastructure/mongodb') -const { Subscription } = require('../app/src/models/Subscription') -const minimist = require('minimist') -const { db } = require('../app/src/infrastructure/mongodb') -const { promiseMapWithLimit } = require('@overleaf/promise-utils') - -/** - * This script is used to notify some users in the IEEEPublications group that - * they will lose access to Overleaf. - * - * Parameters: - * --filename: the filename of the JSON file containing emails of users that - * should **not** be notified. - * --commit: if present, the script will commit the changes to the database. - * - * Usage: - * - dry run: - * node add_notification_ieee_collabratec_users.js --filename=emails.json - * - commit: - * node add_notification_ieee_collabratec_users.js --filename=emails.json --commit - */ - -let COMMIT = false -let EMAILS_FILENAME - -/** - * The IEEE have provided us with a list of active users that should not be removed - * (and therefore not notified). This method retrives those users. - */ -function getActiveUserEmails(filename) { - const data = fs.readFileSync(path.join(__dirname, filename), 'utf8') - const emailsArray = JSON.parse(data) - const emailsSet = new Set(emailsArray) - console.log( - `Read ${emailsSet.size} (${emailsArray.length} in array) emails from ${filename}` - ) - return emailsSet -} - -async function getIEEEUsers() { - return await db.subscriptions - .aggregate([ - { $match: { teamName: 'IEEEPublications' } }, - { $unwind: '$member_ids' }, - { - $lookup: { - from: 'users', - localField: 'member_ids', - foreignField: '_id', - as: 'member_details', - }, - }, - { - $project: { - _id: 1, - teamName: 1, - 'member_details._id': 1, - 'member_details.email': 1, - 'member_details.emails.email': 1, - }, - }, - ]) - .toArray() -} - -async function main() { - const start = performance.now() - - if (!EMAILS_FILENAME) { - throw new Error('No email filename provided') - } - - await waitForDb() - const subscription = await Subscription.findOne({ - teamName: 'IEEEPublications', - }) - - if (!subscription) { - console.error(`No IEEEPublications group subscription found so quitting`) - return - } - - // First we remove all existing Collabratec retirement notifications - if (COMMIT) { - await db.notifications.deleteMany({ - key: 'notification-ieee-collabratec-retirement', - }) - } - - let totalUsers = 0 - let totalUsersNotified = 0 - - const usersArray = await getIEEEUsers() - const activeUsers = getActiveUserEmails(EMAILS_FILENAME) - - const activeUsersFound = new Set() - - // Then go through each collabratec user to see if we need to notify them - await promiseMapWithLimit(10, usersArray, async member => { - if (totalUsers % 5000 === 0) - console.log( - `notified: ${totalUsersNotified} - progress: ${totalUsers} / ${usersArray.length}` - ) - - totalUsers = totalUsers + 1 - - const userDetails = member.member_details[0] - - for (const email of userDetails.emails) { - if (activeUsers.has(email.email)) { - activeUsersFound.add(email.email) - return - } - } - - if (COMMIT) { - await NotificationsBuilder.promises - .ieeeCollabratecRetirement(userDetails._id.toString()) - .create() - } - - totalUsersNotified += 1 - }) - - console.log(`Found ${totalUsers} users in IEEEPublications group`) - - console.log( - `Found ${totalUsersNotified} users in IEEEPublications group to notify` - ) - - console.log(`Found ${activeUsersFound.size} active users`) - - const activeUsersNotFound = Array.from(activeUsers).filter( - user => !activeUsersFound.has(user) - ) - - console.log(`${activeUsersNotFound.length} IEEE active users not found:`) - console.log(activeUsersNotFound) - - const end = performance.now() - console.log(`Took ${end - start} ms`) -} - -const setup = () => { - const argv = minimist(process.argv.slice(2)) - COMMIT = argv.commit !== undefined - EMAILS_FILENAME = argv.filename - if (!COMMIT) { - console.warn('Doing dry run. Add --commit to commit changes') - } -} - -setup() - -main() - .then(() => { - process.exit(0) - }) - .catch(err => { - console.error(err) - process.exit(1) - }) diff --git a/services/web/scripts/remove_unwanted_ieee_collabratec_users.js b/services/web/scripts/remove_unwanted_ieee_collabratec_users.js deleted file mode 100644 index 64d0d1a373..0000000000 --- a/services/web/scripts/remove_unwanted_ieee_collabratec_users.js +++ /dev/null @@ -1,196 +0,0 @@ -const path = require('path') -const fs = require('fs') -const AnalyticsManager = require('../app/src/Features/Analytics/AnalyticsManager') -const { waitForDb } = require('../app/src/infrastructure/mongodb') -const { Subscription } = require('../app/src/models/Subscription') -const minimist = require('minimist') -const { db } = require('../app/src/infrastructure/mongodb') -const { promiseMapWithLimit } = require('@overleaf/promise-utils') -const _ = require('lodash') - -/** - * This script is used to remove some users from the IEEEPublications group. - * - * Parameters: - * --filename: the filename of the JSON file containing emails of users that - * should **not** be removed - * --commit: if present, the script will commit the changes to the database. - * - * Usage: - * - dry run: - * node scripts/remove_unwanted_ieee_collabratec_users.js --filename=emails-to-keep.json - * - commit: - * node scripts/remove_unwanted_ieee_collabratec_users.js --filename=emails-to-keep.json --commit - */ - -let COMMIT = false -let EMAILS_FILENAME - -/** - * The IEEE have provided us with a list of active users that should not be removed - * This method retrieves those users. - */ -function getActiveUserEmails(filename) { - const data = fs.readFileSync(path.join(__dirname, filename), 'utf8') - const emailsArray = JSON.parse(data) - const emailsSet = new Set(emailsArray) - console.log( - `Read ${emailsSet.size} (${emailsArray.length} in array) emails from ${filename}` - ) - return emailsSet -} - -async function getIEEEUsers() { - const results = await db.subscriptions - .aggregate([ - { $match: { teamName: 'IEEEPublications' } }, - { $unwind: '$member_ids' }, - { - $lookup: { - from: 'users', - localField: 'member_ids', - foreignField: '_id', - as: 'member_details', - }, - }, - { - $project: { - _id: 1, - teamName: 1, - 'member_details._id': 1, - 'member_details.email': 1, - 'member_details.emails.email': 1, - }, - }, - ]) - .toArray() - - return results - .map(subscription => subscription.member_details[0]) - .filter(Boolean) -} - -async function main() { - const start = performance.now() - - if (!EMAILS_FILENAME) { - throw new Error('No email filename provided') - } - - await waitForDb() - const subscription = await Subscription.findOne({ - teamName: 'IEEEPublications', - }) - - if (!subscription) { - console.error(`No IEEEPublications group subscription found so quitting`) - return - } - - /** - * @type {string[]} - */ - const oldMemberIds = subscription.member_ids.map(id => id.toString()) - - console.log( - `Found ${oldMemberIds.length} members_ids in IEEEPublications group` - ) - - const usersArray = await getIEEEUsers() - console.log( - `Found ${usersArray.length} users in IEEEPublications group. (${oldMemberIds.length - usersArray.length} missing)` - ) - - const activeUsers = getActiveUserEmails(EMAILS_FILENAME) - - const activeUsersFound = new Set() - - /** - * @type {string[]} - */ - const memberIdsToRemove = [] - - let index = 0 - - // Then go through each collabratec user to see if we need to remove them - await promiseMapWithLimit(10, usersArray, async userDetails => { - if (index % 1000 === 0) - console.log( - `progress: ${index} / ${usersArray.length} (${memberIdsToRemove.length} to remove)` - ) - - index = index + 1 - - if (COMMIT) { - await AnalyticsManager.setUserPropertyForUser( - userDetails._id.toString(), - 'ieee-retirement', - true - ) - } - - for (const email of userDetails.emails) { - if (activeUsers.has(email.email)) { - activeUsersFound.add(email.email) - return - } - } - - memberIdsToRemove.push(userDetails._id.toString()) - }) - - console.log(`Found ${memberIdsToRemove.length} users to remove`) - - /** - * @type {string[]} - */ - const memberIdsToKeep = _.difference(oldMemberIds, memberIdsToRemove) - - console.log(`Keeping ${memberIdsToKeep.length} users`) - - if (COMMIT) { - await Subscription.updateOne( - { teamName: 'IEEEPublications' }, - { member_ids: memberIdsToKeep } - ) - } - - console.log(`Found ${activeUsersFound.size} active users`) - - const activeUsersNotFound = Array.from(activeUsers).filter( - user => !activeUsersFound.has(user) - ) - - console.log(`${activeUsersNotFound.length} IEEE active users not found:`) - console.log(activeUsersNotFound) - - const subscriptionAfter = await Subscription.findOne({ - teamName: 'IEEEPublications', - }) - console.log( - `There are now ${subscriptionAfter?.member_ids?.length} members_ids in IEEEPublications group` - ) - - const end = performance.now() - console.log(`Took ${end - start} ms`) -} - -const setup = () => { - const argv = minimist(process.argv.slice(2)) - COMMIT = argv.commit !== undefined - EMAILS_FILENAME = argv.filename - if (!COMMIT) { - console.warn('Doing dry run. Add --commit to commit changes') - } -} - -setup() - -main() - .then(() => { - process.exit(0) - }) - .catch(err => { - console.error(err) - process.exit(1) - }) diff --git a/services/web/types/project/dashboard/notification.ts b/services/web/types/project/dashboard/notification.ts index 7a19eec4a6..6fc868e6ff 100644 --- a/services/web/types/project/dashboard/notification.ts +++ b/services/web/types/project/dashboard/notification.ts @@ -7,7 +7,6 @@ type TemplateKey = | 'notification_dropbox_unlinked_due_to_lapsed_reconfirmation' | 'notification_group_invitation' | 'notification_personal_and_group_subscriptions' - | 'notification_ieee_collabratec_retirement' type NotificationBase = { _id?: number @@ -74,11 +73,6 @@ export interface NotificationGroupInvitation extends NotificationBase { } } -export interface NotificationIeeeCollabratecRetirement - extends NotificationBase { - templateKey: Extract -} - export type Notification = | NotificationProjectInvite | NotificationWFH2020UpgradeOffer @@ -87,7 +81,6 @@ export type Notification = | NotificationDropboxDuplicateProjectNames | NotificationDropboxUnlinkedDueToLapsedReconfirmation | NotificationGroupInvitation - | NotificationIeeeCollabratecRetirement export type Institution = { _id?: number