mirror of
https://github.com/overleaf/overleaf.git
synced 2024-11-21 20:47:08 -05:00
Merge pull request #20256 from overleaf/jdt-enable-writefull-unset
Differentiate between unset and disabled Writefull GitOrigin-RevId: 3cf8f12ede851dab5a8067bdbcddba6c69870573
This commit is contained in:
parent
edd8a7211f
commit
707790e51e
5 changed files with 95 additions and 56 deletions
|
@ -446,30 +446,6 @@ const _ProjectController = {
|
||||||
usedLatex,
|
usedLatex,
|
||||||
} = userValues
|
} = userValues
|
||||||
|
|
||||||
// check if a user is not in the writefull-oauth-promotion, in which case they may be part of the auto trial group
|
|
||||||
if (
|
|
||||||
!anonymous &&
|
|
||||||
splitTestAssignments['writefull-oauth-promotion']?.variant === 'default'
|
|
||||||
) {
|
|
||||||
// since we are auto-enrolling users into writefull if they are part of the group, we only want to
|
|
||||||
// auto enroll (set writefull to true) if its the first time they have entered the test
|
|
||||||
// this ensures that they can still turn writefull off (otherwise, we would be setting writefull on every time they access their projects)
|
|
||||||
const { variant, metadata } =
|
|
||||||
await SplitTestHandler.promises.getAssignment(
|
|
||||||
req,
|
|
||||||
res,
|
|
||||||
'writefull-auto-load'
|
|
||||||
)
|
|
||||||
if (variant === 'enabled' && metadata?.isFirstNonDefaultAssignment) {
|
|
||||||
await UserUpdater.promises.updateUser(userId, {
|
|
||||||
$set: {
|
|
||||||
writefull: { enabled: true },
|
|
||||||
},
|
|
||||||
})
|
|
||||||
user.writefull.enabled = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const brandVariation = project?.brandVariationId
|
const brandVariation = project?.brandVariationId
|
||||||
? await BrandVariationsHandler.promises.getBrandVariationById(
|
? await BrandVariationsHandler.promises.getBrandVariationById(
|
||||||
project.brandVariationId
|
project.brandVariationId
|
||||||
|
@ -636,16 +612,15 @@ const _ProjectController = {
|
||||||
!userIsMemberOfGroupSubscription &&
|
!userIsMemberOfGroupSubscription &&
|
||||||
!userHasInstitutionLicence
|
!userHasInstitutionLicence
|
||||||
|
|
||||||
let showAiErrorAssistant = false
|
let aiFeaturesAllowed = false
|
||||||
if (userId && Features.hasFeature('saas')) {
|
if (userId && Features.hasFeature('saas')) {
|
||||||
try {
|
try {
|
||||||
// exit early if the user couldnt use ai anyways, since permissions checks are expensive
|
// exit early if the user couldnt use ai anyways, since permissions checks are expensive
|
||||||
const canUseAiOnProject =
|
const canEditProject =
|
||||||
user.features?.aiErrorAssistant &&
|
privilegeLevel === PrivilegeLevels.READ_AND_WRITE ||
|
||||||
(privilegeLevel === PrivilegeLevels.READ_AND_WRITE ||
|
privilegeLevel === PrivilegeLevels.OWNER
|
||||||
privilegeLevel === PrivilegeLevels.OWNER)
|
|
||||||
|
|
||||||
if (canUseAiOnProject) {
|
if (canEditProject) {
|
||||||
// check permissions for user and project owner, to see if they allow AI on the project
|
// check permissions for user and project owner, to see if they allow AI on the project
|
||||||
const permissionsResults = await Modules.promises.hooks.fire(
|
const permissionsResults = await Modules.promises.hooks.fire(
|
||||||
'projectAllowsCapability',
|
'projectAllowsCapability',
|
||||||
|
@ -657,11 +632,34 @@ const _ProjectController = {
|
||||||
result => result === true
|
result => result === true
|
||||||
)
|
)
|
||||||
|
|
||||||
showAiErrorAssistant = aiAllowed
|
aiFeaturesAllowed = aiAllowed
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
// still allow users to access project if we cant get their permissions, but disable AI feature
|
// still allow users to access project if we cant get their permissions, but disable AI feature
|
||||||
showAiErrorAssistant = false
|
aiFeaturesAllowed = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// check if a user has never tried writefull before (writefull.enabled will be null)
|
||||||
|
// if they previously accepted writefull. user.writefull will be true,
|
||||||
|
// if they explicitly disabled it, user.writefull will be false
|
||||||
|
if (aiFeaturesAllowed && user.writefull?.enabled === null) {
|
||||||
|
// since we are auto-enrolling users into writefull if they are part of the group, we only want to
|
||||||
|
// auto enroll (set writefull to true) if its the first time they have entered the test
|
||||||
|
// this ensures that they can still turn writefull off (otherwise, we would be setting writefull on every time they access their projects)
|
||||||
|
const { variant, metadata } =
|
||||||
|
await SplitTestHandler.promises.getAssignment(
|
||||||
|
req,
|
||||||
|
res,
|
||||||
|
'writefull-auto-load'
|
||||||
|
)
|
||||||
|
if (variant === 'enabled' && metadata?.isFirstNonDefaultAssignment) {
|
||||||
|
await UserUpdater.promises.updateUser(userId, {
|
||||||
|
$set: {
|
||||||
|
writefull: { enabled: true },
|
||||||
|
},
|
||||||
|
})
|
||||||
|
user.writefull.enabled = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -693,7 +691,7 @@ const _ProjectController = {
|
||||||
features: user.features,
|
features: user.features,
|
||||||
refProviders: _.mapValues(user.refProviders, Boolean),
|
refProviders: _.mapValues(user.refProviders, Boolean),
|
||||||
writefull: {
|
writefull: {
|
||||||
enabled: Boolean(user.writefull?.enabled),
|
enabled: Boolean(user.writefull?.enabled && aiFeaturesAllowed),
|
||||||
},
|
},
|
||||||
alphaProgram: user.alphaProgram,
|
alphaProgram: user.alphaProgram,
|
||||||
betaProgram: user.betaProgram,
|
betaProgram: user.betaProgram,
|
||||||
|
@ -741,7 +739,9 @@ const _ProjectController = {
|
||||||
debugPdfDetach,
|
debugPdfDetach,
|
||||||
showSymbolPalette,
|
showSymbolPalette,
|
||||||
symbolPaletteAvailable: Features.hasFeature('symbol-palette'),
|
symbolPaletteAvailable: Features.hasFeature('symbol-palette'),
|
||||||
showAiErrorAssistant,
|
userRestrictions: Array.from(req.userRestrictions || []),
|
||||||
|
showAiErrorAssistant:
|
||||||
|
aiFeaturesAllowed && user.features?.aiErrorAssistant,
|
||||||
detachRole,
|
detachRole,
|
||||||
metadata: { viewport: false },
|
metadata: { viewport: false },
|
||||||
showUpgradePrompt,
|
showUpgradePrompt,
|
||||||
|
|
|
@ -71,13 +71,6 @@ async function settingsPage(req, res) {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// getAssignment sets res.locals, which will pass to the splitTest context
|
|
||||||
await SplitTestHandler.promises.getAssignment(
|
|
||||||
req,
|
|
||||||
res,
|
|
||||||
'writefull-oauth-promotion'
|
|
||||||
)
|
|
||||||
|
|
||||||
let personalAccessTokens
|
let personalAccessTokens
|
||||||
try {
|
try {
|
||||||
const results = await Modules.promises.hooks.fire(
|
const results = await Modules.promises.hooks.fire(
|
||||||
|
|
|
@ -180,7 +180,7 @@ const UserSchema = new Schema(
|
||||||
zotero: Schema.Types.Mixed,
|
zotero: Schema.Types.Mixed,
|
||||||
},
|
},
|
||||||
writefull: {
|
writefull: {
|
||||||
enabled: { type: Boolean, default: false },
|
enabled: { type: Boolean, default: null },
|
||||||
},
|
},
|
||||||
alphaProgram: { type: Boolean, default: false }, // experimental features
|
alphaProgram: { type: Boolean, default: false }, // experimental features
|
||||||
betaProgram: { type: Boolean, default: false },
|
betaProgram: { type: Boolean, default: false },
|
||||||
|
|
|
@ -5,7 +5,6 @@ import { useSSOContext, SSOSubscription } from '../context/sso-context'
|
||||||
import { SSOLinkingWidget } from './linking/sso-widget'
|
import { SSOLinkingWidget } from './linking/sso-widget'
|
||||||
import getMeta from '../../../utils/meta'
|
import getMeta from '../../../utils/meta'
|
||||||
import { useBroadcastUser } from '@/shared/hooks/user-channel/use-broadcast-user'
|
import { useBroadcastUser } from '@/shared/hooks/user-channel/use-broadcast-user'
|
||||||
import { useFeatureFlag } from '@/shared/context/split-test-context'
|
|
||||||
import OLNotification from '@/features/ui/components/ol/ol-notification'
|
import OLNotification from '@/features/ui/components/ol/ol-notification'
|
||||||
|
|
||||||
const availableIntegrationLinkingWidgets = importOverleafModules(
|
const availableIntegrationLinkingWidgets = importOverleafModules(
|
||||||
|
@ -48,19 +47,7 @@ function LinkingSection() {
|
||||||
oauth2ServerComponents
|
oauth2ServerComponents
|
||||||
)
|
)
|
||||||
|
|
||||||
// currently the only thing that is in the langFeedback section is writefull,
|
const haslangFeedbackLinkingWidgets = langFeedbackLinkingWidgets.length
|
||||||
// which is behind a split test. we should hide this section if the user is not in the split test
|
|
||||||
// todo: remove split test check, and split test context after gradual rollout is complete
|
|
||||||
const hasWritefullOauthPromotion = useFeatureFlag('writefull-oauth-promotion')
|
|
||||||
|
|
||||||
// even if they arent in the split test, if they have it enabled let them toggle it off
|
|
||||||
const user = getMeta('ol-user')
|
|
||||||
const shouldLoadWritefull =
|
|
||||||
(hasWritefullOauthPromotion || user.writefull?.enabled === true) &&
|
|
||||||
!window.writefull // check if the writefull extension is installed, in which case we dont handle the integration
|
|
||||||
|
|
||||||
const haslangFeedbackLinkingWidgets =
|
|
||||||
langFeedbackLinkingWidgets.length && shouldLoadWritefull
|
|
||||||
const hasIntegrationLinkingSection =
|
const hasIntegrationLinkingSection =
|
||||||
renderSyncSection && allIntegrationLinkingWidgets.length
|
renderSyncSection && allIntegrationLinkingWidgets.length
|
||||||
const hasReferencesLinkingSection = referenceLinkingWidgets.length
|
const hasReferencesLinkingSection = referenceLinkingWidgets.length
|
||||||
|
|
59
services/web/scripts/split_writefull_disabled_from_unset.js
Normal file
59
services/web/scripts/split_writefull_disabled_from_unset.js
Normal file
|
@ -0,0 +1,59 @@
|
||||||
|
const { db, waitForDb } = require('../app/src/infrastructure/mongodb')
|
||||||
|
const { batchedUpdate } = require('./helpers/batchedUpdate')
|
||||||
|
const { ObjectId } = require('mongodb-legacy')
|
||||||
|
const fs = require('fs')
|
||||||
|
|
||||||
|
const CHUNK_SIZE = 1000
|
||||||
|
|
||||||
|
// Function to chunk the array
|
||||||
|
function chunkArray(array, size) {
|
||||||
|
const result = []
|
||||||
|
for (let i = 0; i < array.length; i += size) {
|
||||||
|
result.push(array.slice(i, i + size))
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
async function main() {
|
||||||
|
// search for file of users who already explicitly opted out first
|
||||||
|
const optOutPath = process.argv[2]
|
||||||
|
const optedOutFile = fs.readFileSync(optOutPath, 'utf8')
|
||||||
|
let optedOutList = optedOutFile
|
||||||
|
|
||||||
|
optedOutList = optedOutFile.split('\n').map(id => new ObjectId(id))
|
||||||
|
|
||||||
|
console.log(`preserving opt-outs of ${optedOutList.length} users`)
|
||||||
|
await waitForDb()
|
||||||
|
// update all applicable user models
|
||||||
|
await batchedUpdate(
|
||||||
|
'users',
|
||||||
|
{ 'writefull.enabled': false }, // and is false
|
||||||
|
{ $set: { 'writefull.enabled': null } }
|
||||||
|
)
|
||||||
|
|
||||||
|
const chunks = chunkArray(optedOutList, CHUNK_SIZE)
|
||||||
|
|
||||||
|
// then reset any explicit false back to being false
|
||||||
|
// Iterate over each chunk and perform the query
|
||||||
|
for (const chunkedIds of chunks) {
|
||||||
|
console.log('batch update started')
|
||||||
|
await db.users.updateMany(
|
||||||
|
{ _id: { $in: chunkedIds } },
|
||||||
|
{ $set: { 'writefull.enabled': false } }
|
||||||
|
)
|
||||||
|
console.log('batch completed')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = main
|
||||||
|
|
||||||
|
if (require.main === module) {
|
||||||
|
main()
|
||||||
|
.then(() => {
|
||||||
|
process.exit(0)
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
console.error({ error })
|
||||||
|
process.exit(1)
|
||||||
|
})
|
||||||
|
}
|
Loading…
Reference in a new issue