diff --git a/services/web/app/src/Features/Project/ProjectController.js b/services/web/app/src/Features/Project/ProjectController.js index fb79bfcdf2..c69fe7cbd4 100644 --- a/services/web/app/src/Features/Project/ProjectController.js +++ b/services/web/app/src/Features/Project/ProjectController.js @@ -552,24 +552,6 @@ const ProjectController = { } ) }, - primaryEmailCheckActive(cb) { - SplitTestHandler.getAssignment( - req, - res, - 'primary-email-check', - (err, assignment) => { - if (err) { - logger.warn( - { err }, - 'failed to get "primary-email-check" split test assignment' - ) - cb(null, false) - } else { - cb(null, assignment.variant === 'active') - } - } - ) - }, survey(cb) { SurveyHandler.getSurvey(userId, (err, survey) => { if (err) { @@ -591,14 +573,12 @@ const ProjectController = { notifications, user, userEmailsData, - primaryEmailCheckActive, groupsAndEnterpriseBannerAssignment, userIsMemberOfGroupSubscription, } = results if ( user && - primaryEmailCheckActive && UserPrimaryEmailCheckHandler.requiresPrimaryEmailCheck(user) ) { return res.redirect('/user/emails/primary-email-check') @@ -997,37 +977,6 @@ const ProjectController = { } ) }, - dictionaryEditorAssignment(cb) { - SplitTestHandler.getAssignment( - req, - res, - 'dictionary-editor', - {}, - (error, assignment) => { - // do not fail editor load if assignment fails - if (error) { - cb(null, { variant: 'default' }) - } else { - cb(null, assignment) - } - } - ) - }, - interstitialPaymentFromPaywallAssignment(cb) { - SplitTestHandler.getAssignment( - req, - res, - 'interstitial-payment-from-paywall', - (error, assignment) => { - // do not fail editor load if assignment fails - if (error) { - cb(null, { variant: 'default' }) - } else { - cb(null, assignment) - } - } - ) - }, latexLogParserAssignment(cb) { SplitTestHandler.getAssignment( req, @@ -1043,25 +992,6 @@ const ProjectController = { } ) }, - compileTimeWarningAssignment: [ - 'user', - (results, cb) => { - if (results.user?.features?.compileTimeout <= 60) { - SplitTestHandler.getAssignment( - req, - res, - 'compile-time-warning', - {}, - () => { - // do not fail editor load if assignment fails - cb() - } - ) - } else { - cb() - } - }, - ], linkSharingUpgradePromptAssignment(cb) { SplitTestHandler.getAssignment( req, @@ -1152,7 +1082,6 @@ const ProjectController = { brandVariation, newSourceEditorAssignment, pdfjsAssignment, - dictionaryEditorAssignment, editorLeftMenuAssignment, } ) => { @@ -1251,10 +1180,6 @@ const ProjectController = { : ['all'] const galileoPromptWords = req.query?.galileoPromptWords || '' - const dictionaryEditorEnabled = - !Features.hasFeature('saas') || - dictionaryEditorAssignment?.variant === 'enabled' - // Persistent upgrade prompts // in header & in share project modal const showUpgradePrompt = @@ -1329,7 +1254,6 @@ const ProjectController = { wsUrl, showSupport: Features.hasFeature('support'), pdfjsVariant: pdfjsAssignment.variant, - dictionaryEditorEnabled, debugPdfDetach, showNewSourceEditorOption, showSymbolPalette, diff --git a/services/web/app/src/Features/Project/ProjectListController.js b/services/web/app/src/Features/Project/ProjectListController.js index 26d4c2e105..a3d2cb8aa6 100644 --- a/services/web/app/src/Features/Project/ProjectListController.js +++ b/services/web/app/src/Features/Project/ProjectListController.js @@ -124,26 +124,8 @@ async function projectListReactPage(req, res, next) { logger.err({ err: error, userId }, 'Failed to load the active survey') } - try { - const assignment = await SplitTestHandler.promises.getAssignment( - req, - res, - 'primary-email-check' - ) - const primaryEmailCheckActive = assignment.variant === 'active' - - if ( - user && - primaryEmailCheckActive && - UserPrimaryEmailCheckHandler.requiresPrimaryEmailCheck(user) - ) { - return res.redirect('/user/emails/primary-email-check') - } - } catch (error) { - logger.warn( - { err: error }, - 'failed to get "primary-email-check" split test assignment' - ) + if (user && UserPrimaryEmailCheckHandler.requiresPrimaryEmailCheck(user)) { + return res.redirect('/user/emails/primary-email-check') } } diff --git a/services/web/app/src/Features/StaticPages/HomeController.js b/services/web/app/src/Features/StaticPages/HomeController.js index fcf2792bf7..4f80645356 100644 --- a/services/web/app/src/Features/StaticPages/HomeController.js +++ b/services/web/app/src/Features/StaticPages/HomeController.js @@ -43,22 +43,7 @@ module.exports = HomeController = { async home(req, res) { if (Features.hasFeature('homepage') && homepageExists) { - try { - const homeRegistration = await SplitTestHandler.promises.getAssignment( - req, - res, - 'home-registration' - ) - const removeRegistration = homeRegistration.variant - return res.render('external/home/v2', { - removeRegistration, - }) - } catch (err) { - logger.error({ err }, err.message) - return res.render('external/home/v2', { - removeRegistration: 'default', - }) - } + return res.render('external/home/v2') } else { return res.redirect('/login') } diff --git a/services/web/app/src/Features/Subscription/SubscriptionController.js b/services/web/app/src/Features/Subscription/SubscriptionController.js index 263cb0ca5b..c231d6f0f3 100644 --- a/services/web/app/src/Features/Subscription/SubscriptionController.js +++ b/services/web/app/src/Features/Subscription/SubscriptionController.js @@ -80,17 +80,6 @@ async function plansPage(req, res) { AnalyticsManager.recordEventForSession(req.session, 'plans-page-view') - const standardPlanNameAssignment = - await SplitTestHandler.promises.getAssignment( - req, - res, - 'standard-plan-name' - ) - - const useNewPlanName = - standardPlanNameAssignment && - standardPlanNameAssignment.variant === 'new-plan-name' - const template = newPlansPageVariantV2 ? 'subscriptions/plans-marketing-v2' : 'subscriptions/plans-marketing' @@ -108,7 +97,6 @@ async function plansPage(req, res) { groupPlanModalOptions, groupPlanModalDefaults, newPlansPageVariantV2, - useNewPlanName, initialLocalizedGroupPrice: SubscriptionHelper.generateInitialLocalizedGroupPrice( recommendedCurrency @@ -151,13 +139,6 @@ async function paymentPage(req, res) { if (recommendedCurrency && currency == null) { currency = recommendedCurrency } - const assignment = await SplitTestHandler.promises.getAssignment( - req, - res, - 'payment-page' - ) - const useUpdatedPaymentPage = - assignment && assignment.variant === 'updated-payment-page' const refreshedPaymentPageAssignment = await SplitTestHandler.promises.getAssignment( @@ -177,9 +158,7 @@ async function paymentPage(req, res) { const template = useRefreshedPaymentPage ? 'subscriptions/new-refreshed' - : useUpdatedPaymentPage - ? 'subscriptions/new-updated' - : 'subscriptions/new' + : 'subscriptions/new-updated' res.render(template, { title: 'subscribe', diff --git a/services/web/app/src/Features/User/UserCreator.js b/services/web/app/src/Features/User/UserCreator.js index 88652ca4bf..8e2ef7e9cd 100644 --- a/services/web/app/src/Features/User/UserCreator.js +++ b/services/web/app/src/Features/User/UserCreator.js @@ -7,7 +7,6 @@ const UserDeleter = require('./UserDeleter') const UserGetter = require('./UserGetter') const UserUpdater = require('./UserUpdater') const Analytics = require('../Analytics/AnalyticsManager') -const SplitTestHandler = require('../SplitTests/SplitTestHandler') const UserOnboardingEmailManager = require('./UserOnboardingEmailManager') const UserPostRegistrationAnalyticsManager = require('./UserPostRegistrationAnalyticsManager') const OError = require('@overleaf/o-error') @@ -38,13 +37,8 @@ async function _addAffiliation(user, affiliationOptions) { async function recordRegistrationEvent(user) { try { - const homeRegistrationAssignment = - await SplitTestHandler.promises.getAssignmentForUser( - user._id, - 'home-registration' - ) const segmentation = { - 'home-registration': homeRegistrationAssignment.variant, + 'home-registration': 'default', } if (user.thirdPartyIdentifiers && user.thirdPartyIdentifiers.length > 0) { segmentation.provider = user.thirdPartyIdentifiers[0].providerId diff --git a/services/web/app/src/Features/User/UserPrimaryEmailCheckHandler.js b/services/web/app/src/Features/User/UserPrimaryEmailCheckHandler.js index 004cb751a5..be1d045d01 100644 --- a/services/web/app/src/Features/User/UserPrimaryEmailCheckHandler.js +++ b/services/web/app/src/Features/User/UserPrimaryEmailCheckHandler.js @@ -6,8 +6,12 @@ function requiresPrimaryEmailCheck({ lastPrimaryEmailCheck, signUpDate, }) { - const hasExpired = date => - Date.now() - date.getTime() > Settings.primary_email_check_expiration + const hasExpired = date => { + if (!date) { + return true + } + return Date.now() - date.getTime() > Settings.primary_email_check_expiration + } const primaryEmailConfirmedAt = emails.find( emailEntry => emailEntry.email === email diff --git a/services/web/app/views/project/editor/left-menu.pug b/services/web/app/views/project/editor/left-menu.pug index 0dda2b9cb0..8fd5c501ce 100644 --- a/services/web/app/views/project/editor/left-menu.pug +++ b/services/web/app/views/project/editor/left-menu.pug @@ -117,15 +117,14 @@ aside#left-menu.full-size( value=language.code )= language.name - if dictionaryEditorEnabled - .form-controls(ng-controller="DictionaryModalController") - label #{translate("dictionary")} - button.btn.btn-secondary.btn-xs(ng-click="openModal()") #{translate("edit")} + .form-controls(ng-controller="DictionaryModalController") + label #{translate("dictionary")} + button.btn.btn-secondary.btn-xs(ng-click="openModal()") #{translate("edit")} - dictionary-modal( - handle-hide="handleHide" - show="show" - ) + dictionary-modal( + handle-hide="handleHide" + show="show" + ) .form-controls label(for="autoComplete") #{translate("auto_complete")} diff --git a/services/web/app/views/project/editor/meta.pug b/services/web/app/views/project/editor/meta.pug index 3589225576..bf86b78d96 100644 --- a/services/web/app/views/project/editor/meta.pug +++ b/services/web/app/views/project/editor/meta.pug @@ -28,7 +28,6 @@ meta(name="ol-galileoFeatures" data-type="json" content=galileoFeatures) meta(name="ol-detachRole" data-type="string" content=detachRole) meta(name="ol-allowedImageNames" data-type="json" content=allowedImageNames) meta(name="ol-languages" data-type="json" content=languages) -meta(name="ol-dictionaryEditorEnabled" data-type="boolean" content=dictionaryEditorEnabled) meta(name="ol-editorThemes" data-type="json" content=editorThemes) meta(name="ol-legacyEditorThemes" data-type="json" content=legacyEditorThemes) meta(name="ol-showUpgradePrompt" data-type="boolean" content=showUpgradePrompt) diff --git a/services/web/app/views/subscriptions/_plans_page_mixins.pug b/services/web/app/views/subscriptions/_plans_page_mixins.pug index 1750a4e983..2fc4f49a3c 100644 --- a/services/web/app/views/subscriptions/_plans_page_mixins.pug +++ b/services/web/app/views/subscriptions/_plans_page_mixins.pug @@ -206,10 +206,7 @@ mixin allCardsAndControls(controlsRowSpaced, listLocation) .best-value strong #{translate('best_value')} .card-header - if (useNewPlanName) - h2 #{translate("standard")} - else - h2 #{translate("collaborator")} + h2 #{translate("standard")} h5.tagline #{translate("tagline_collaborator")} .circle +price_collaborator diff --git a/services/web/app/views/subscriptions/new.pug b/services/web/app/views/subscriptions/new.pug deleted file mode 100644 index d6ca25ef65..0000000000 --- a/services/web/app/views/subscriptions/new.pug +++ /dev/null @@ -1,357 +0,0 @@ -extends ../layout - -include ./_new_mixins - -block append meta - meta(name="ol-countryCode" content=countryCode) - meta(name="ol-recurlyApiKey" content=settings.apis.recurly.publicKey) - meta(name="ol-recommendedCurrency" content=String(currency).slice(0,3)) - -block head-scripts - script(type="text/javascript", nonce=scriptNonce, src="https://js.recurly.com/v4/recurly.js") - -block content - main.content.content-alt#main-content - .container(ng-controller="NewSubscriptionController" ng-cloak) - .row.card-group - .col-md-5.col-md-push-4 - .card.card-highlighted.card-border(ng-hide="threeDSecureFlow") - .alert.alert-danger(ng-show="recurlyLoadError") - strong #{translate('payment_provider_unreachable_error')} - .page-header(ng-hide="recurlyLoadError") - .row - .col-xs-9 - h2 {{planName}} - .col-xs-3 - div.dropdown.changePlanButton.pull-right(ng-cloak, dropdown) - a.btn.btn-default.dropdown-toggle( - href="#", - data-toggle="dropdown", - dropdown-toggle - ) - | {{currencyCode}} ({{allCurrencies[currencyCode]['symbol']}}) - span.caret - ul.dropdown-menu(role="menu") - li(ng-repeat="(currency, value) in availableCurrencies") - a( - ng-click="changeCurrency(currency)", - ) {{currency}} ({{value['symbol']}}) - .row(ng-if="planCode == 'student-annual' || planCode == 'student-monthly' || planCode == 'student_free_trial_7_days'") - .col-xs-12 - p.student-disclaimer #{translate('student_disclaimer')} - - hr.thin - .row - .col-md-12.text-center - div(ng-if="trialLength") - span !{translate("first_few_days_free", {trialLen:'{{trialLength}}'})} - span(ng-if="coupon.discountMonths && coupon.discountRate")   - {{coupon.discountMonths}} #{translate("month")}s {{coupon.discountRate}}% Off - - div(ng-if="recurlyPrice") - - var priceVars = { price: "{{ availableCurrencies[currencyCode]['symbol'] }}{{ recurlyPrice.total }}"}; - span(ng-if="!coupon.singleUse && monthlyBilling") - | !{translate("x_price_per_month", priceVars, ['strong'] )} - span(ng-if="!coupon.singleUse && !monthlyBilling") - | !{translate("x_price_per_year", priceVars, ['strong'] )} - span(ng-if="coupon.singleUse && monthlyBilling") - | !{translate("x_price_for_first_month", priceVars, ['strong'] )} - span(ng-if="coupon.singleUse && !monthlyBilling") - | !{translate("x_price_for_first_year", priceVars, ['strong'] )} - - div(ng-if="coupon && coupon.normalPrice") - - var noDiscountPriceAngularExp = "{{ availableCurrencies[currencyCode]['symbol']}}{{coupon.normalPrice | number:2 }}"; - span.small(ng-if="!coupon.singleUse && monthlyBilling") - | !{translate("normally_x_price_per_month", { price: noDiscountPriceAngularExp } )} - span.small(ng-if="!coupon.singleUse && !monthlyBilling") - | !{translate("normally_x_price_per_year", { price: noDiscountPriceAngularExp } )} - span.small(ng-if="coupon.singleUse && monthlyBilling") - | !{translate("then_x_price_per_month", { price: noDiscountPriceAngularExp } )} - span.small(ng-if="coupon.singleUse && !monthlyBilling") - | !{translate("then_x_price_per_year", { price: noDiscountPriceAngularExp } )} - - .row(ng-hide="recurlyLoadError") - div() - .col-md-12() - form( - name="simpleCCForm" - novalidate - ) - - div.payment-method-toggle - a.payment-method-toggle-switch( - href - ng-click="setPaymentMethod('credit_card');" - ng-class="paymentMethod.value === 'credit_card' ? 'payment-method-toggle-switch-selected' : ''" - ) - i.fa.fa-cc-mastercard.fa-2x(aria-hidden="true") - span   - i.fa.fa-cc-visa.fa-2x(aria-hidden="true") - span   - i.fa.fa-cc-amex.fa-2x(aria-hidden="true") - span.sr-only Pay with Mastercard, Visa, or Amex - a.payment-method-toggle-switch( - href - ng-click="setPaymentMethod('paypal');" - ng-class="paymentMethod.value === 'paypal' ? 'payment-method-toggle-switch-selected' : ''" - ) - i.fa.fa-cc-paypal.fa-2x(aria-hidden="true") - span.sr-only Pay with PayPal - - .alert.alert-warning.small(ng-show="genericError") - strong {{genericError}} - - .alert.alert-warning.small(ng-show="couponError") - strong {{couponError}} - - div(ng-show="paymentMethod.value === 'credit_card'") - .row - .col-xs-6 - .form-group(ng-class="validation.errorFields.first_name || inputHasError(simpleCCForm.firstName) ? 'has-error' : ''") - label(for="first-name") #{translate('first_name')} - input#first-name.form-control( - type="text" - maxlength='255' - data-recurly="first_name" - name="firstName" - ng-model="data.first_name" - required - ) - span.input-feedback-message(ng-if="simpleCCForm.firstName.$error.required") #{translate('this_field_is_required')} - .col-xs-6 - .form-group(ng-class="validation.errorFields.last_name || inputHasError(simpleCCForm.lastName)? 'has-error' : ''") - label(for="last-name") #{translate('last_name')} - input#last-name.form-control( - type="text" - maxlength='255' - data-recurly="last_name" - name="lastName" - ng-model="data.last_name" - required - ) - span.input-feedback-message(ng-if="simpleCCForm.lastName.$error.required") #{translate('this_field_is_required')} - - .form-group(ng-class="validation.errorFields.number ? 'has-error' : ''") - label(for="card-no") #{translate("credit_card_number")} - div#card-no( - type="text" - name="ccNumber" - data-recurly='number' - ) - - .row - .col-xs-3 - .form-group.has-feedback(ng-class="validation.errorFields.month ? 'has-error' : ''") - label(for="month").capitalised #{translate("month")} - div( - type="number" - name="month" - data-recurly="month" - ) - .col-xs-3 - .form-group.has-feedback(ng-class="validation.errorFields.year ? 'has-error' : ''") - label(for="year").capitalised #{translate("year")} - div( - type="number" - name="year" - data-recurly="year" - ) - - .col-xs-6 - .form-group.has-feedback(ng-class="validation.errorFields.cvv ? 'has-error' : ''") - label #{translate("security_code")} - div( - type="number" - ng-model="data.cvv" - data-recurly="cvv" - name="cvv" - cc-format-sec-code - ) - .form-control-feedback - a.form-helper( - href - tabindex="-1" - tooltip-template="'cvv-tooltip-tpl.html'" - tooltip-trigger="mouseenter" - tooltip-append-to-body="true" - ) ? - - div - .row - .col-xs-12 - .form-group(ng-class="validation.errorFields.address1 || inputHasError(simpleCCForm.address1) ? 'has-error' : ''") - label(for="address-line-1") #{translate('address_line_1')} - input#address-line-1.form-control( - type="text" - maxlength="50" - data-recurly="address1" - name="address1" - ng-model="data.address1" - required - ) - span.input-feedback-message(ng-if="simpleCCForm.address1.$error.required") #{translate('this_field_is_required')} - - .row - .col-xs-12 - .form-group.has-feedback(ng-class="validation.errorFields.address2 ? 'has-error' : ''") - label(for="address-line-2") #{translate('address_line_2')} - input#address-line-2.form-control( - type="text" - maxlength="50" - data-recurly="address2" - name="address2" - ng-model="data.address2" - ) - - .row - .col-xs-4 - .form-group(ng-class="validation.errorFields.postal_code || inputHasError(simpleCCForm.postalCode) ? 'has-error' : ''") - label(for="postal-code") #{translate('postal_code')} - input#postal-code.form-control( - type="text" - maxlength="20" - data-recurly="postal_code" - name="postalCode" - ng-model="data.postal_code" - required - ) - span.input-feedback-message(ng-if="simpleCCForm.postalCode.$error.required") #{translate('this_field_is_required')} - - .col-xs-8 - .form-group(ng-class="validation.errorFields.country || inputHasError(simpleCCForm.country) ? 'has-error' : ''") - label(for="country") #{translate('country')} - select#country.form-control( - data-recurly="country" - ng-model="data.country" - name="country" - ng-change="updateCountry()" - ng-selected="{{country.code == data.country}}" - ng-model-options="{ debounce: 200 }" - required - ) - option(value='', disabled) #{translate("country")} - option(value='-', disabled) -------------- - option(ng-repeat="country in countries" ng-bind-html="country.name" value="{{country.code}}") - span.input-feedback-message(ng-if="simpleCCForm.country.$error.required") #{translate('this_field_is_required')} - - .form-group - .checkbox - label - input( - type="checkbox" - ng-model="ui.addCompanyDetails" - ) - | - | #{translate("add_company_details")} - - .form-group(ng-show="ui.addCompanyDetails") - label(for="company-name") #{translate("company_name")} - input#company-name.form-control( - type="text" - name="companyName" - ng-model="data.company" - ) - - .form-group(ng-show="ui.addCompanyDetails && taxes.length") - label(for="vat-number") #{translate("vat_number")} - input#vat-number.form-control( - type="text" - name="vatNumber" - ng-model="data.vat_number" - ng-blur="applyVatNumber()" - ) - - if (showCouponField) - .form-group - label(for="coupon-code") #{translate('coupon_code')} - input#coupon-code.form-control( - type="text" - ng-blur="applyCoupon()" - ng-model="data.coupon" - ) - - p(ng-if="paymentMethod.value === 'paypal'") #{translate("paypal_upgrade")} - - div.price-breakdown( - ng-show="taxes.length" - ) - - var priceBreakdownVars = { total: "{{ availableCurrencies[currencyCode]['symbol'] }}{{ recurlyPrice.total }}", subtotal: "{{availableCurrencies[currencyCode]['symbol']}}{{ recurlyPrice.subtotal }}", tax: "{{availableCurrencies[currencyCode]['symbol']}}{{ recurlyPrice.tax }}" }; - hr.thin - span - | Total: - | - span(ng-if="!coupon.singleUse && monthlyBilling") - | !{translate("x_price_per_month_tax", priceBreakdownVars, ['strong'] )} - span(ng-if="!coupon.singleUse && !monthlyBilling") - | !{translate("x_price_per_year_tax", priceBreakdownVars, ['strong'] )} - span(ng-if="coupon.singleUse && monthlyBilling") - | !{translate("x_price_for_first_month_tax", priceBreakdownVars, ['strong'] )} - span(ng-if="coupon.singleUse && !monthlyBilling") - | !{translate("x_price_for_first_year_tax", priceBreakdownVars, ['strong'] )} - hr.thin - - div.payment-submit - button.btn.btn-primary.btn-block( - ng-click="submit()" - ng-disabled="processing || !isFormValid(simpleCCForm);" - ) - span(ng-show="processing") - i.fa.fa-spinner.fa-spin(aria-hidden="true") - span.sr-only #{translate('processing')} - |   - span(ng-if="paymentMethod.value === 'credit_card'") - | {{ trialLength ? '#{translate("upgrade_cc_btn")}' : '#{translate("upgrade_now")}'}} - span(ng-if="paymentMethod.value !== 'credit_card'") #{translate("upgrade_paypal_btn")} - - p.tos-agreement-notice !{translate("by_subscribing_you_agree_to_our_terms_of_service", {}, [{name: 'a', attrs: {href: '/legal#Terms', target:'_blank', rel:'noopener noreferrer'}}])} - - div.three-d-secure-container.card.card-highlighted.card-border(ng-show="threeDSecureFlow") - .alert.alert-info.small(aria-live="assertive") - strong #{translate('card_must_be_authenticated_by_3dsecure')} - div.three-d-secure-recurly-container - - - - .col-md-3.col-md-pull-4 - .card.card-first - .paymentPageFeatures - h3 #{translate("unlimited_projects")} - p #{translate("create_unlimited_projects")} - - h3 - if plan.features.collaborators == -1 - - var collaboratorCount = 'Unlimited' - else - - var collaboratorCount = plan.features.collaborators - if plan.features.collaborators == 1 - | #{translate("collabs_per_proj_single", {collabcount:collaboratorCount})} - else - | #{translate("collabs_per_proj", {collabcount:collaboratorCount})} - p #{translate("work_on_single_version")}. #{translate("view_collab_edits_in_real_time")} - - h3 #{translate("full_doc_history")} - p.track-changes-example - | #{translate("see_what_has_been")} #[span.added #{translate("added")}] - |  #{translate("and")} #[span.removed #{translate("removed")}]. - p - | #{translate("restore_to_any_older_version")}. - - h3 #{translate("sync_to_dropbox")} - p - | #{translate("acces_work_from_anywhere")}. - | #{translate("work_offline_and_sync_with_dropbox")}. - - hr - - p.small.text-center(ng-non-bindable) !{translate("cancel_anytime", { appName:'{{settings.appName}}' })} - - script(type="text/javascript", nonce=scriptNonce). - ga('send', 'event', 'pageview', 'payment_form', "#{plan_code}") - - script( - type="text/ng-template" - id="cvv-tooltip-tpl.html" - ) - p !{translate("for_visa_mastercard_and_discover", {}, ['strong', 'strong', 'strong'])} - p !{translate("for_american_express", {}, ['strong', 'strong', 'strong'])} - - +studentCheckModal diff --git a/services/web/app/views/subscriptions/plans-marketing/_group_plan_modal.pug b/services/web/app/views/subscriptions/plans-marketing/_group_plan_modal.pug index 180327540b..cb1331be8e 100644 --- a/services/web/app/views/subscriptions/plans-marketing/_group_plan_modal.pug +++ b/services/web/app/views/subscriptions/plans-marketing/_group_plan_modal.pug @@ -51,10 +51,7 @@ div.modal.fade(tabindex="-1" role="dialog" data-ol-group-plan-modal) value=plan_code.code data-ol-group-plan-code=plan_code.code ) - if (useNewPlanName && plan_code.code === 'collaborator') - span Standard - else - span #{plan_code.display} + span #{plan_code.display} .form-group label(for='size') | Number of users diff --git a/services/web/app/views/subscriptions/plans-marketing/_mixins.pug b/services/web/app/views/subscriptions/plans-marketing/_mixins.pug index e49787b814..73032183de 100644 --- a/services/web/app/views/subscriptions/plans-marketing/_mixins.pug +++ b/services/web/app/views/subscriptions/plans-marketing/_mixins.pug @@ -245,10 +245,7 @@ mixin allCardsAndControls(controlsRowSpaced, listLocation) .best-value strong #{translate('best_value')} .card-header - if (useNewPlanName) - h2 #{translate("standard")} - else - h2 #{translate("collaborator")} + h2 #{translate("standard")} .card-byline h5.tagline #{translate("tagline_collaborator")} .circle diff --git a/services/web/app/views/subscriptions/plans-marketing/_tables.pug b/services/web/app/views/subscriptions/plans-marketing/_tables.pug index becd35eba5..91ab1c30ac 100644 --- a/services/web/app/views/subscriptions/plans-marketing/_tables.pug +++ b/services/web/app/views/subscriptions/plans-marketing/_tables.pug @@ -5,11 +5,7 @@ mixin table_premium th th #{translate("free")} th #{translate("personal")} - th - if (useNewPlanName) - | #{translate("standard")} - else - | #{translate("collaborator")} + th #{translate("standard")} .outer.outer-top .outer-content .best-value diff --git a/services/web/frontend/js/features/dictionary/components/dictionary-modal-content.tsx b/services/web/frontend/js/features/dictionary/components/dictionary-modal-content.tsx index 6ba1e18d82..210d163e7e 100644 --- a/services/web/frontend/js/features/dictionary/components/dictionary-modal-content.tsx +++ b/services/web/frontend/js/features/dictionary/components/dictionary-modal-content.tsx @@ -2,7 +2,6 @@ import { useCallback } from 'react' import { useTranslation } from 'react-i18next' import { Alert, Button, Modal } from 'react-bootstrap' import Icon from '../../../shared/components/icon' -import SplitTestBadge from '../../../shared/components/split-test-badge' import Tooltip from '../../../shared/components/tooltip' import useAsync from '../../../shared/hooks/use-async' import { postJSON } from '../../../infrastructure/fetch-json' @@ -38,17 +37,7 @@ export default function DictionaryModalContent({ return ( <> - - {t('edit_dictionary')}{' '} - - + {t('edit_dictionary')} diff --git a/services/web/frontend/js/features/editor-left-menu/components/settings/settings-dictionary.tsx b/services/web/frontend/js/features/editor-left-menu/components/settings/settings-dictionary.tsx index 3cbe57f1f3..0d098b2e6e 100644 --- a/services/web/frontend/js/features/editor-left-menu/components/settings/settings-dictionary.tsx +++ b/services/web/frontend/js/features/editor-left-menu/components/settings/settings-dictionary.tsx @@ -1,19 +1,11 @@ import { useState } from 'react' import { Button } from 'react-bootstrap' import { useTranslation } from 'react-i18next' -import getMeta from '../../../../utils/meta' import DictionaryModal from '../../../dictionary/components/dictionary-modal' export default function SettingsDictionary() { const { t } = useTranslation() const [showModal, setShowModal] = useState(false) - const dictionaryEditorEnabled = getMeta( - 'ol-dictionaryEditorEnabled' - ) as boolean - - if (!dictionaryEditorEnabled) { - return null - } return (
diff --git a/services/web/frontend/js/features/pdf-preview/components/pdf-preview-error.js b/services/web/frontend/js/features/pdf-preview/components/pdf-preview-error.js index 6ffd1c60a1..de58e18017 100644 --- a/services/web/frontend/js/features/pdf-preview/components/pdf-preview-error.js +++ b/services/web/frontend/js/features/pdf-preview/components/pdf-preview-error.js @@ -5,7 +5,6 @@ import { Button } from 'react-bootstrap' import PdfLogEntry from './pdf-log-entry' import { useDetachCompileContext as useCompileContext } from '../../../shared/context/detach-compile-context' import { useStopOnFirstError } from '../../../shared/hooks/use-stop-on-first-error' -import StopOnFirstErrorBadge from '../../../shared/components/stop-on-first-error-badge' function PdfPreviewError({ error }) { const { t } = useTranslation() @@ -254,7 +253,6 @@ function TimedOutLogEntry() { />, ]} />{' '} - )} diff --git a/services/web/frontend/js/features/pdf-preview/components/stop-on-first-error-prompt.js b/services/web/frontend/js/features/pdf-preview/components/stop-on-first-error-prompt.js index 3c7a8cbed4..f29d16f97e 100644 --- a/services/web/frontend/js/features/pdf-preview/components/stop-on-first-error-prompt.js +++ b/services/web/frontend/js/features/pdf-preview/components/stop-on-first-error-prompt.js @@ -4,7 +4,6 @@ import { Button } from 'react-bootstrap' import PdfLogEntry from './pdf-log-entry' import { useDetachCompileContext as useCompileContext } from '../../../shared/context/detach-compile-context' import { useStopOnFirstError } from '../../../shared/hooks/use-stop-on-first-error' -import StopOnFirstErrorBadge from '../../../shared/components/stop-on-first-error-badge' export default function StopOnFirstErrorPrompt() { const { t } = useTranslation() @@ -22,7 +21,6 @@ export default function StopOnFirstErrorPrompt() { return ( } formattedContent={ <> {' '} -

{t('log_entry_maximum_entries_see_full_logs')}

diff --git a/services/web/frontend/js/shared/components/stop-on-first-error-badge.tsx b/services/web/frontend/js/shared/components/stop-on-first-error-badge.tsx deleted file mode 100644 index 3909fe9327..0000000000 --- a/services/web/frontend/js/shared/components/stop-on-first-error-badge.tsx +++ /dev/null @@ -1,33 +0,0 @@ -import Tooltip from './tooltip' - -type Props = { - placement: string -} - -export default function StopOnFirstErrorBadge({ placement }: Props) { - const content = ( - <> - We are testing the “Stop on first error” compilation mode. -
- Click to give feedback - - ) - - return ( - - - {content} - - - ) -} diff --git a/services/web/frontend/js/shared/context/local-compile-context.js b/services/web/frontend/js/shared/context/local-compile-context.js index b336d9f30d..352d2290c3 100644 --- a/services/web/frontend/js/shared/context/local-compile-context.js +++ b/services/web/frontend/js/shared/context/local-compile-context.js @@ -27,8 +27,8 @@ import { useIdeContext } from './ide-context' import { useProjectContext } from './project-context' import { useEditorContext } from './editor-context' import { buildFileList } from '../../features/pdf-preview/util/file-list' -import { useSplitTestContext } from './split-test-context' import { useLayoutContext } from './layout-context' +import { useUserContext } from './user-context' export const LocalCompileContext = createContext() @@ -86,10 +86,10 @@ export function LocalCompileProvider({ children }) { const { _id: projectId, rootDocId } = useProjectContext() - const { splitTestVariants } = useSplitTestContext() - const { pdfPreviewOpen } = useLayoutContext() + const { features } = useUserContext() + // whether a compile is in progress const [compiling, setCompiling] = useState(false) @@ -279,8 +279,7 @@ export function LocalCompileProvider({ children }) { }, [compiledOnce, currentDoc, compiler]) useEffect(() => { - const compileTimeWarningEnabled = - splitTestVariants['compile-time-warning'] === 'show-upgrade-prompt' + const compileTimeWarningEnabled = features?.compileTimeout <= 60 if (compileTimeWarningEnabled && compiling && isProjectOwner) { const timeout = window.setTimeout(() => { @@ -291,7 +290,7 @@ export function LocalCompileProvider({ children }) { window.clearTimeout(timeout) } } - }, [compiling, isProjectOwner, splitTestVariants]) + }, [compiling, isProjectOwner, features]) // handle the data returned from a compile request // note: this should _only_ run when `data` changes, diff --git a/services/web/frontend/js/shared/context/user-context.js b/services/web/frontend/js/shared/context/user-context.js index db0d83d1ba..dba72beaa7 100644 --- a/services/web/frontend/js/shared/context/user-context.js +++ b/services/web/frontend/js/shared/context/user-context.js @@ -22,6 +22,7 @@ UserContext.Provider.propTypes = { mendeley: PropTypes.boolean, zotero: PropTypes.boolean, references: PropTypes.boolean, + compileTimeout: PropTypes.number, }), refProviders: PropTypes.shape({ mendeley: PropTypes.any, diff --git a/services/web/test/acceptance/src/PrimaryEmailCheckTests.js b/services/web/test/acceptance/src/PrimaryEmailCheckTests.js index e6aaf1b78e..8a858eb6e9 100644 --- a/services/web/test/acceptance/src/PrimaryEmailCheckTests.js +++ b/services/web/test/acceptance/src/PrimaryEmailCheckTests.js @@ -1,36 +1,15 @@ const UserHelper = require('./helpers/UserHelper') const Settings = require('@overleaf/settings') const { expect } = require('chai') -const SplitTestManager = require('../../../app/src/Features/SplitTests/SplitTestManager') const Features = require('../../../app/src/infrastructure/Features') -// While the split test is in progress this must be appended to URLs during tests -const SPLIT_TEST_QUERY = '?primary-email-check=active' - describe('PrimaryEmailCheck', function () { let userHelper - // Create the primary-email-check split test because this is now required for the query string override to work. See - // https://github.com/overleaf/internal/pull/7545#discussion_r848575736 before(async function () { if (!Features.hasFeature('saas')) { this.skip() } - - await SplitTestManager.createSplitTest({ - name: 'primary-email-check', - configuration: { - active: true, - analyticsEnabled: true, - phase: 'release', - variants: [ - { - name: 'active', - rolloutPercent: 0, - }, - ], - }, - }) }) beforeEach(async function () { @@ -43,13 +22,13 @@ describe('PrimaryEmailCheck', function () { describe('redirections', function () { describe('when the user has signed up recently', function () { it("shouldn't be redirected from project list to the primary email check page", async function () { - const response = await userHelper.fetch('/project' + SPLIT_TEST_QUERY) + const response = await userHelper.fetch('/project') expect(response.status).to.equal(200) }) it('should be redirected from the primary email check page to the project list', async function () { const response = await userHelper.fetch( - '/user/emails/primary-email-check' + SPLIT_TEST_QUERY + '/user/emails/primary-email-check' ) expect(response.status).to.equal(302) expect(response.headers.get('location')).to.equal( @@ -67,13 +46,13 @@ describe('PrimaryEmailCheck', function () { }) it("shouldn't be redirected from project list to the primary email check page", async function () { - const response = await userHelper.fetch('/project' + SPLIT_TEST_QUERY) + const response = await userHelper.fetch('/project') expect(response.status).to.equal(200) }) it('should be redirected from the primary email check page to the project list', async function () { const response = await userHelper.fetch( - '/user/emails/primary-email-check' + SPLIT_TEST_QUERY + '/user/emails/primary-email-check' ) expect(response.status).to.equal(302) expect(response.headers.get('location')).to.equal( @@ -98,13 +77,13 @@ describe('PrimaryEmailCheck', function () { }) it("shouldn't be redirected from project list to the primary email check page", async function () { - const response = await userHelper.fetch('/project' + SPLIT_TEST_QUERY) + const response = await userHelper.fetch('/project') expect(response.status).to.equal(200) }) it('should be redirected from the primary email check page to the project list', async function () { const response = await userHelper.fetch( - '/user/emails/primary-email-check' + SPLIT_TEST_QUERY + '/user/emails/primary-email-check' ) expect(response.status).to.equal(302) expect(response.headers.get('location')).to.equal( @@ -122,7 +101,7 @@ describe('PrimaryEmailCheck', function () { }) it('should be redirected from project list to the primary email check page', async function () { - const response = await userHelper.fetch('/project' + SPLIT_TEST_QUERY) + const response = await userHelper.fetch('/project') expect(response.status).to.equal(302) expect(response.headers.get('location')).to.equal( UserHelper.url('/user/emails/primary-email-check').toString() @@ -149,7 +128,7 @@ describe('PrimaryEmailCheck', function () { }) checkResponse = await userHelper.fetch( - '/user/emails/primary-email-check' + SPLIT_TEST_QUERY, + '/user/emails/primary-email-check', { method: 'POST' } ) }) @@ -162,7 +141,7 @@ describe('PrimaryEmailCheck', function () { }) it("shouldn't be redirected from project list to the primary email check page any longer", async function () { - const response = await userHelper.fetch('/project' + SPLIT_TEST_QUERY) + const response = await userHelper.fetch('/project') expect(response.status).to.equal(200) }) diff --git a/services/web/test/acceptance/src/SessionTests.js b/services/web/test/acceptance/src/SessionTests.js index bcf538c5c9..2822f784c7 100644 --- a/services/web/test/acceptance/src/SessionTests.js +++ b/services/web/test/acceptance/src/SessionTests.js @@ -73,6 +73,7 @@ describe('Sessions', function () { // set up second session for this user this.user2 = new User() this.user2.email = this.user1.email + this.user2.emails = this.user1.emails this.user2.password = this.user1.password }) @@ -197,9 +198,11 @@ describe('Sessions', function () { // set up second session for this user this.user2 = new User() this.user2.email = this.user1.email + this.user2.emails = this.user1.emails this.user2.password = this.user1.password this.user3 = new User() this.user3.email = this.user1.email + this.user3.emails = this.user1.emails this.user3.password = this.user1.password }) @@ -321,9 +324,11 @@ describe('Sessions', function () { // set up second session for this user this.user2 = new User() this.user2.email = this.user1.email + this.user2.emails = this.user1.emails this.user2.password = this.user1.password this.user3 = new User() this.user3.email = this.user1.email + this.user3.emails = this.user1.emails this.user3.password = this.user1.password async.series([this.user2.login.bind(this.user2)], done) }) diff --git a/services/web/test/acceptance/src/helpers/User.js b/services/web/test/acceptance/src/helpers/User.js index 06ef9cbc62..817053ed6f 100644 --- a/services/web/test/acceptance/src/helpers/User.js +++ b/services/web/test/acceptance/src/helpers/User.js @@ -19,6 +19,7 @@ class User { { email: options.email || `acceptance-test-${count}@example.com`, createdAt: new Date(), + confirmedAt: options.confirmedAt, }, ] this.email = this.emails[0].email diff --git a/services/web/test/frontend/helpers/editor-providers.js b/services/web/test/frontend/helpers/editor-providers.js index 360642732f..a30e400a76 100644 --- a/services/web/test/frontend/helpers/editor-providers.js +++ b/services/web/test/frontend/helpers/editor-providers.js @@ -105,6 +105,9 @@ export function EditorProviders({ metadataManager, } + // Add details for useUserContext + window.metaAttributesCache.set('ol-user', { ...user, features }) + return ( diff --git a/services/web/test/unit/src/Project/ProjectControllerTests.js b/services/web/test/unit/src/Project/ProjectControllerTests.js index d5bc46abc0..15e198200a 100644 --- a/services/web/test/unit/src/Project/ProjectControllerTests.js +++ b/services/web/test/unit/src/Project/ProjectControllerTests.js @@ -71,6 +71,9 @@ describe('ProjectController', function () { this.EditorController = { renameProject: sinon.stub() } this.InactiveProjectManager = { reactivateProjectIfRequired: sinon.stub() } this.ProjectUpdateHandler = { markAsOpened: sinon.stub() } + this.UserPrimaryEmailCheckHandler = { + requiresPrimaryEmailCheck: sinon.stub().returns(false), + } this.ProjectGetter = { findAllUsersProjects: sinon.stub(), getProject: sinon.stub(), @@ -193,6 +196,8 @@ describe('ProjectController', function () { }, '../Institutions/InstitutionsFeatures': this.InstitutionsFeatures, '../Survey/SurveyHandler': this.SurveyHandler, + '../User/UserPrimaryEmailCheckHandler': + this.UserPrimaryEmailCheckHandler, './ProjectAuditLogHandler': this.ProjectAuditLogHandler, }, }) diff --git a/services/web/test/unit/src/Subscription/SubscriptionControllerTests.js b/services/web/test/unit/src/Subscription/SubscriptionControllerTests.js index d1cb7b27fd..aa9fc8369e 100644 --- a/services/web/test/unit/src/Subscription/SubscriptionControllerTests.js +++ b/services/web/test/unit/src/Subscription/SubscriptionControllerTests.js @@ -275,7 +275,7 @@ describe('SubscriptionController', function () { describe('with a valid plan code', function () { it('should render the new subscription page', function (done) { this.res.render = (page, opts) => { - page.should.equal('subscriptions/new') + page.should.equal('subscriptions/new-updated') done() } this.SubscriptionController.paymentPage(this.req, this.res)