diff --git a/services/web/app/src/Features/Subscription/SubscriptionController.js b/services/web/app/src/Features/Subscription/SubscriptionController.js index 882dccc255..4b267a2639 100644 --- a/services/web/app/src/Features/Subscription/SubscriptionController.js +++ b/services/web/app/src/Features/Subscription/SubscriptionController.js @@ -263,6 +263,8 @@ async function interstitialPaymentPage(req, res) { const hasSubscription = await LimitationsManager.promises.userHasV1OrV2Subscription(user) + const showSkipLink = req.query?.skipLink === 'true' + if (hasSubscription) { res.redirect('/user/subscription?hasSubscription=true') } else { @@ -272,6 +274,7 @@ async function interstitialPaymentPage(req, res) { itm_campaign: req.query?.itm_campaign, recommendedCurrency, interstitialPaymentConfig, + showSkipLink, }) } } @@ -339,6 +342,8 @@ async function successfulSubscription(req, res) { const premiumFeaturesDiscoverability = premiumFeaturesDiscoverabilityAssignment?.variant === 'active' + const postCheckoutRedirect = req.session?.postCheckoutRedirect + if (!personalSubscription) { res.redirect('/user/subscription/plans') } else { @@ -346,6 +351,7 @@ async function successfulSubscription(req, res) { title: 'thank_you', personalSubscription, premiumFeaturesDiscoverability, + postCheckoutRedirect, }) } } diff --git a/services/web/app/src/models/User.js b/services/web/app/src/models/User.js index dabaf91c7e..200c19a2aa 100644 --- a/services/web/app/src/models/User.js +++ b/services/web/app/src/models/User.js @@ -181,6 +181,7 @@ const UserSchema = new Schema({ auditLog: [AuditLogEntrySchema], splitTests: Schema.Types.Mixed, analyticsId: { type: String }, + surveyResponses: Schema.Types.Mixed, }) function formatSplitTestsSchema(next) { diff --git a/services/web/app/views/layout/navbar-marketing.pug b/services/web/app/views/layout/navbar-marketing.pug index 4f3100127c..d679b847ab 100644 --- a/services/web/app/views/layout/navbar-marketing.pug +++ b/services/web/app/views/layout/navbar-marketing.pug @@ -1,13 +1,14 @@ nav.navbar.navbar-default.navbar-main .container-fluid .navbar-header - button.navbar-toggle.collapsed( - type="button", - data-toggle="collapse", - data-target="[data-ol-navbar-main-collapse]" - aria-label="Toggle " + translate('navigation') - ) - i.fa.fa-bars(aria-hidden="true") + if (typeof(suppressNavbarRight) == "undefined") + button.navbar-toggle.collapsed( + type="button", + data-toggle="collapse", + data-target="[data-ol-navbar-main-collapse]" + aria-label="Toggle " + translate('navigation') + ) + i.fa.fa-bars(aria-hidden="true") if settings.nav.custom_logo a(href='/', aria-label=settings.appName, style='background-image:url("'+settings.nav.custom_logo+'")').navbar-brand else if (nav.title) @@ -20,72 +21,85 @@ nav.navbar.navbar-default.navbar-main - var canDisplaySplitTestMenu = hasFeature('saas') && (canDisplayAdminMenu || (getSessionUser() && getSessionUser().staffAccess && (getSessionUser().staffAccess.splitTestMetrics || getSessionUser().staffAccess.splitTestManagement))) - var canDisplaySurveyMenu = hasFeature('saas') && canDisplayAdminMenu - .navbar-collapse.collapse(data-ol-navbar-main-collapse) - ul.nav.navbar-nav.navbar-right - if (canDisplayAdminMenu || canDisplayAdminRedirect || canDisplaySplitTestMenu) - li.dropdown.subdued - a.dropdown-toggle( - href="#", - role="button", - aria-haspopup="true", - aria-expanded="false", - data-toggle="dropdown" - ) - | Admin - span.caret - ul.dropdown-menu - if canDisplayAdminMenu - li - a(href="/admin") Manage Site - li - a(href="/admin/user") Manage Users - li - a(href="/admin/project") Project URL Lookup - if canDisplayAdminRedirect - li - a(href=settings.adminUrl) Switch to Admin - if canDisplaySplitTestMenu - li - a(href="/admin/split-test") Manage Split Tests - if canDisplaySurveyMenu - li - a(href="/admin/survey") Manage Surveys + if (typeof(suppressNavbarRight) == "undefined") + .navbar-collapse.collapse(data-ol-navbar-main-collapse) + ul.nav.navbar-nav.navbar-right + if (canDisplayAdminMenu || canDisplayAdminRedirect || canDisplaySplitTestMenu) + li.dropdown.subdued + a.dropdown-toggle( + href="#", + role="button", + aria-haspopup="true", + aria-expanded="false", + data-toggle="dropdown" + ) + | Admin + span.caret + ul.dropdown-menu + if canDisplayAdminMenu + li + a(href="/admin") Manage Site + li + a(href="/admin/user") Manage Users + li + a(href="/admin/project") Project URL Lookup + if canDisplayAdminRedirect + li + a(href=settings.adminUrl) Switch to Admin + if canDisplaySplitTestMenu + li + a(href="/admin/split-test") Manage Split Tests + if canDisplaySurveyMenu + li + a(href="/admin/survey") Manage Surveys - // loop over header_extras - each item in ((splitTestVariants && (splitTestVariants['unified-navigation'] === 'show-unified-navigation')) ? nav.header_extras_unified : nav.header_extras) - - - if ((item.only_when_logged_in && getSessionUser()) - || (item.only_when_logged_out && (!getSessionUser())) - || (!item.only_when_logged_out && !item.only_when_logged_in && !item.only_content_pages) - || (item.only_content_pages && (typeof(suppressNavContentLinks) == "undefined" || !suppressNavContentLinks)) - ){ - var showNavItem = true - } else { - var showNavItem = false - } + // loop over header_extras + each item in ((splitTestVariants && (splitTestVariants['unified-navigation'] === 'show-unified-navigation')) ? nav.header_extras_unified : nav.header_extras) + - + if ((item.only_when_logged_in && getSessionUser()) + || (item.only_when_logged_out && (!getSessionUser())) + || (!item.only_when_logged_out && !item.only_when_logged_in && !item.only_content_pages) + || (item.only_content_pages && (typeof(suppressNavContentLinks) == "undefined" || !suppressNavContentLinks)) + ){ + var showNavItem = true + } else { + var showNavItem = false + } - if showNavItem - if item.dropdown - li.dropdown(class=item.class) - a.dropdown-toggle( - href="#", - role="button", - aria-haspopup="true", - aria-expanded="false", - data-toggle="dropdown" - ) - | !{translate(item.text)} - span.caret - ul.dropdown-menu - each child in item.dropdown - if child.divider - li.divider - if child.splitTest - if (splitTestVariants && (splitTestVariants[child.splitTest.name] === child.splitTest.variant)) + if showNavItem + if item.dropdown + li.dropdown(class=item.class) + a.dropdown-toggle( + href="#", + role="button", + aria-haspopup="true", + aria-expanded="false", + data-toggle="dropdown" + ) + | !{translate(item.text)} + span.caret + ul.dropdown-menu + each child in item.dropdown + if child.divider + li.divider + if child.splitTest + if (splitTestVariants && (splitTestVariants[child.splitTest.name] === child.splitTest.variant)) + li + if child.url + a( + href=child.url, + class=child.class, + event-tracking=child.event + event-tracking-mb="true" + event-tracking-trigger="click" + ) !{translate(child.text)} + else + | !{translate(child.text)} + else li if child.url a( - href=child.url, + href=child.url, class=child.class, event-tracking=child.event event-tracking-mb="true" @@ -93,81 +107,69 @@ nav.navbar.navbar-default.navbar-main ) !{translate(child.text)} else | !{translate(child.text)} - else - li - if child.url - a( - href=child.url, - class=child.class, - event-tracking=child.event - event-tracking-mb="true" - event-tracking-trigger="click" - ) !{translate(child.text)} - else - | !{translate(child.text)} - else - li(class=item.class) - if item.url - a( - href=item.url, - class=item.class, - event-tracking=item.event - event-tracking-mb="true" - event-tracking-trigger="click" - ) !{translate(item.text)} - else - | !{translate(item.text)} + else + li(class=item.class) + if item.url + a( + href=item.url, + class=item.class, + event-tracking=item.event + event-tracking-mb="true" + event-tracking-trigger="click" + ) !{translate(item.text)} + else + | !{translate(item.text)} - // logged out - if !getSessionUser() - // register link - if hasFeature('registration-page') + // logged out + if !getSessionUser() + // register link + if hasFeature('registration-page') + li + a( + href="/register" + event-tracking="menu-clicked-register" + event-tracking-action="clicked" + event-tracking-trigger="click" + event-tracking-mb="true" + event-segmentation={ page: currentUrl } + ) #{translate('register')} + + // login link li a( - href="/register" - event-tracking="menu-clicked-register" + href="/login" + event-tracking="menu-clicked-login" event-tracking-action="clicked" event-tracking-trigger="click" event-tracking-mb="true" event-segmentation={ page: currentUrl } - ) #{translate('register')} + ) #{translate('log_in')} - // login link - li - a( - href="/login" - event-tracking="menu-clicked-login" - event-tracking-action="clicked" - event-tracking-trigger="click" - event-tracking-mb="true" - event-segmentation={ page: currentUrl } - ) #{translate('log_in')} - - // projects link and account menu - if getSessionUser() - li - a(href="/project") #{translate('Projects')} - li.dropdown - a.dropdown-toggle( - href="#", - role="button", - aria-haspopup="true", - aria-expanded="false", - data-toggle="dropdown" - ) - | #{translate('Account')} - span.caret - ul.dropdown-menu - li - div.subdued #{getSessionUser().email} - li.divider.hidden-xs.hidden-sm - li - a(href="/user/settings") #{translate('Account Settings')} - if nav.showSubscriptionLink + // projects link and account menu + if getSessionUser() + li + a(href="/project") #{translate('Projects')} + li.dropdown + a.dropdown-toggle( + href="#", + role="button", + aria-haspopup="true", + aria-expanded="false", + data-toggle="dropdown" + ) + | #{translate('Account')} + span.caret + ul.dropdown-menu li - a(href="/user/subscription") #{translate('subscription')} - li.divider.hidden-xs.hidden-sm - li - form(method="POST" action="/logout") - input(name='_csrf', type='hidden', value=csrfToken) - button.btn-link.text-left.dropdown-menu-button #{translate('log_out')} + div.subdued #{getSessionUser().email} + li.divider.hidden-xs.hidden-sm + li + a(href="/user/settings") #{translate('Account Settings')} + if nav.showSubscriptionLink + li + a(href="/user/subscription") #{translate('subscription')} + li.divider.hidden-xs.hidden-sm + li + form(method="POST" action="/logout") + input(name='_csrf', type='hidden', value=csrfToken) + button.btn-link.text-left.dropdown-menu-button #{translate('log_out')} diff --git a/services/web/app/views/subscriptions/interstitial-payment.pug b/services/web/app/views/subscriptions/interstitial-payment.pug index c9143b42f3..046c7c2856 100644 --- a/services/web/app/views/subscriptions/interstitial-payment.pug +++ b/services/web/app/views/subscriptions/interstitial-payment.pug @@ -41,5 +41,15 @@ block content //- sticky header on mobile will be "hidden" (by removing its sticky position) if it reaches this div .invisible(aria-hidden="true" data-ol-plans-v2-table-sticky-header-stop) - + + if (showSkipLink) + .row.row-spaced-small.text-center + a(href='/project' + event-tracking="skip-button-click" + event-tracking-mb="true" + event-tracking-trigger="click" + event-segmentation='{"paywall-type": "interstitial-page"}' + ) + | #{translate("continue_with_free_plan")} + != moduleIncludes("contactModalGeneral-marketing", locals) diff --git a/services/web/app/views/subscriptions/successful_subscription.pug b/services/web/app/views/subscriptions/successful_subscription.pug index d55cf2af86..5107d3851d 100644 --- a/services/web/app/views/subscriptions/successful_subscription.pug +++ b/services/web/app/views/subscriptions/successful_subscription.pug @@ -30,4 +30,7 @@ block content br(ng-non-bindable) | The #{settings.appName} Team p - a.btn.btn-primary(href="/project") < #{translate("back_to_your_projects")} + if (postCheckoutRedirect) + a.btn.btn-primary(href=postCheckoutRedirect) < #{translate("back_to_your_projects")} + else + a.btn.btn-primary(href="/project") < #{translate("back_to_your_projects")} diff --git a/services/web/frontend/stylesheets/components/lists.less b/services/web/frontend/stylesheets/components/lists.less index f7986b38db..b0e1978d22 100644 --- a/services/web/frontend/stylesheets/components/lists.less +++ b/services/web/frontend/stylesheets/components/lists.less @@ -21,3 +21,7 @@ } } } + +.list-style-check-green { + list-style-image: url('../../../public/img/fa-check-green.svg'); +} diff --git a/services/web/locales/en.json b/services/web/locales/en.json index 2342cc6720..6044994fef 100644 --- a/services/web/locales/en.json +++ b/services/web/locales/en.json @@ -667,6 +667,8 @@ "dropbox_sync_error": "Sorry, there was a problem checking our Dropbox service. Please try again in a few moments.", "send": "Send", "sending": "Sending", + "continue": "Continue", + "skip": "Skip", "invalid_password": "Invalid Password", "invalid_password_not_set": "Password is required", "invalid_password_too_short": "Password too short, minimum __minLength__", @@ -1836,6 +1838,17 @@ "server_pro_license_entitlement_line_1": "<0>__appName__ Server Pro license", "server_pro_license_entitlement_line_2": "You currently have <0>__count__ active users. If you need to increase your license entitlement, please <1>contact Overleaf.", "server_pro_license_entitlement_line_3": "An active user is one who has opened a project in this Server Pro instance in the last 12 months.", + "get_the_most_out_headline": "Get the most out of __appName__ with features such as:", + "more_project_collaborators": "<0>More project <0>collaborators", + "advanced_reference_search": "Advanced <0>reference search", + "longer_compile_timeout": "Longer <0>compile timeout", + "symbol_palette_highlighted": "<0>Symbol palette", + "real_time_track_changes": "Real-time <0>track-changes", + "full_document_history": "Full document <0>history", + "github_git_and_dropbox_integrations": "<0>Github, <0>Git and <0>Dropbox integrations", + "zotero_and_mendeley_integrations": "<0>Zotero and <0>Mendeley integrations", + "skip": "Skip", + "continue_with_free_plan": "Continue with free plan", "history_entry_origin_upload": "upload", "history_entry_origin_git": "via Git", "history_entry_origin_github": "via GitHub", diff --git a/services/web/public/img/fa-check-green.svg b/services/web/public/img/fa-check-green.svg new file mode 100644 index 0000000000..9c6efcca7e --- /dev/null +++ b/services/web/public/img/fa-check-green.svg @@ -0,0 +1,3 @@ + + + diff --git a/services/web/public/img/third-party-icons/dropbox.svg b/services/web/public/img/third-party-icons/dropbox.svg new file mode 100644 index 0000000000..0dc2e21556 --- /dev/null +++ b/services/web/public/img/third-party-icons/dropbox.svg @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/services/web/public/img/third-party-icons/git.svg b/services/web/public/img/third-party-icons/git.svg new file mode 100644 index 0000000000..d0c64d71f5 --- /dev/null +++ b/services/web/public/img/third-party-icons/git.svg @@ -0,0 +1,3 @@ + + + diff --git a/services/web/public/img/third-party-icons/github.svg b/services/web/public/img/third-party-icons/github.svg new file mode 100644 index 0000000000..19191709cb --- /dev/null +++ b/services/web/public/img/third-party-icons/github.svg @@ -0,0 +1,3 @@ + + + diff --git a/services/web/public/img/third-party-icons/mendely.png b/services/web/public/img/third-party-icons/mendely.png new file mode 100644 index 0000000000..5f98fa81b4 Binary files /dev/null and b/services/web/public/img/third-party-icons/mendely.png differ diff --git a/services/web/public/img/third-party-icons/zotero.png b/services/web/public/img/third-party-icons/zotero.png new file mode 100644 index 0000000000..c6c4221605 Binary files /dev/null and b/services/web/public/img/third-party-icons/zotero.png differ diff --git a/services/web/test/unit/src/Subscription/SubscriptionControllerTests.js b/services/web/test/unit/src/Subscription/SubscriptionControllerTests.js index ffc771ee5a..8d46b2335e 100644 --- a/services/web/test/unit/src/Subscription/SubscriptionControllerTests.js +++ b/services/web/test/unit/src/Subscription/SubscriptionControllerTests.js @@ -404,6 +404,7 @@ describe('SubscriptionController', function () { title: 'thank_you', personalSubscription: 'foo', premiumFeaturesDiscoverability: false, + postCheckoutRedirect: undefined, }) done() }