mirror of
https://github.com/yu-i-i/overleaf-cep.git
synced 2026-11-14 06:03:45 +01:00
Merge pull request #19398 from overleaf/rh-rm-ieee-notification
[web] Remove IEEE Collabratec notification GitOrigin-RevId: 14ec3e50ed4913b815620f5215df59b17fc03054
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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": "",
|
||||
|
||||
-3
@@ -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' ? (
|
||||
<GroupInvitationNotification notification={notification} />
|
||||
) : templateKey === 'notification_ieee_collabratec_retirement' ? (
|
||||
<IEEERetirementBanner id={id} />
|
||||
) : templateKey === 'notification_personal_and_group_subscriptions' ? (
|
||||
<Notification
|
||||
type="warning"
|
||||
|
||||
-67
@@ -1,67 +0,0 @@
|
||||
import { useCallback, useEffect } from 'react'
|
||||
import Notification from './notification'
|
||||
import { Trans } from 'react-i18next'
|
||||
import * as eventTracking from '../../../../infrastructure/event-tracking'
|
||||
import useAsyncDismiss from './hooks/useAsyncDismiss'
|
||||
|
||||
let viewEventSent = false
|
||||
|
||||
type IEEERetirementBannerProps = {
|
||||
id: number | undefined
|
||||
}
|
||||
|
||||
export default function IEEERetirementBanner({
|
||||
id,
|
||||
}: IEEERetirementBannerProps) {
|
||||
const { handleDismiss } = useAsyncDismiss()
|
||||
|
||||
const handleClose = useCallback(() => {
|
||||
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 (
|
||||
<Notification
|
||||
type="warning"
|
||||
onDismiss={handleClose}
|
||||
content={
|
||||
<Trans
|
||||
i18nKey="notification_ieee_collabratec_retirement_message"
|
||||
components={[
|
||||
// eslint-disable-next-line jsx-a11y/anchor-has-content,react/jsx-key
|
||||
<a href="/user/subscription" onClick={handleClickPlans} />,
|
||||
// eslint-disable-next-line jsx-a11y/anchor-has-content,react/jsx-key
|
||||
<a href="mailto:authors@ieee.org" onClick={handleClickEmail} />,
|
||||
]}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
)
|
||||
}
|
||||
@@ -1291,7 +1291,6 @@
|
||||
"not_registered": "Not registered",
|
||||
"note_features_under_development": "<0>Please note</0> that features in this program are still being tested and actively developed. This means that they might <0>change</0>, be <0>removed</0> or <0>become part of a premium plan</0>",
|
||||
"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</0>. Contact <1>authors@ieee.org</1> with any questions.",
|
||||
"notification_personal_and_group_subscriptions": "We’ve spotted that you’ve got <0>more than one active __appName__ subscription</0>. To avoid paying more than you need to, <1>review your subscriptions</1>.",
|
||||
"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": "<b>__userName__</b> would like you to join <b>__projectName__</b> <a class=\"btn btn-sm btn-info pull-right\" href=\"/project/__projectId__/invite/token/__token__\">Join Project</a>",
|
||||
|
||||
@@ -1262,7 +1262,6 @@
|
||||
"not_registered": "未注册",
|
||||
"note_features_under_development": "<0>请注意</0>此计划中的功能仍在测试和快速开发中。 这意味着它们可能<0>改变</0>、<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</0>。需要升级吗? <1>查看我们的订阅计划</1>",
|
||||
"notification_personal_and_group_subscriptions": "我们发现您有<0>多个活跃的 __appName__ 订阅</0>。 为避免支付超出您需要的费用,请<1>检查您的订阅</1>。",
|
||||
"notification_personal_subscription_not_required_due_to_affiliation": " 好消息!您的组织 __institutionName__ 与 Overleaf 有合作关系。您可以取消您的个人订阅,而不会失去访问您的任何利益。",
|
||||
"notification_project_invite": "<b>__userName__</b> 想让您加入 <b>__projectName__</b> <a class=\"btn btn-sm btn-info pull-right\" href=\"’/project/__projectId__/invite/token/__token__’\">加入项目</a>",
|
||||
|
||||
@@ -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)
|
||||
})
|
||||
@@ -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)
|
||||
})
|
||||
@@ -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<TemplateKey, 'notification_ieee_collabratec_retirement'>
|
||||
}
|
||||
|
||||
export type Notification =
|
||||
| NotificationProjectInvite
|
||||
| NotificationWFH2020UpgradeOffer
|
||||
@@ -87,7 +81,6 @@ export type Notification =
|
||||
| NotificationDropboxDuplicateProjectNames
|
||||
| NotificationDropboxUnlinkedDueToLapsedReconfirmation
|
||||
| NotificationGroupInvitation
|
||||
| NotificationIeeeCollabratecRetirement
|
||||
|
||||
export type Institution = {
|
||||
_id?: number
|
||||
|
||||
Reference in New Issue
Block a user