diff --git a/services/web/.eslintrc.js b/services/web/.eslintrc.js index 9bc1656822..0aefd86d0c 100644 --- a/services/web/.eslintrc.js +++ b/services/web/.eslintrc.js @@ -195,9 +195,6 @@ module.exports = { $: true, angular: true, ga: true, - // Injected in layout.pug - user_id: true, - ExposedSettings: true, }, rules: { // TODO: remove once https://github.com/standard/eslint-config-standard-react/issues/68 (support eslint@8) is fixed. diff --git a/services/web/.storybook/preview.tsx b/services/web/.storybook/preview.tsx index 39d5913ba3..6975503524 100644 --- a/services/web/.storybook/preview.tsx +++ b/services/web/.storybook/preview.tsx @@ -9,11 +9,82 @@ import i18n from 'i18next' import { initReactI18next } from 'react-i18next' // @ts-ignore import en from '../../../services/web/locales/en.json' -import { ExposedSettings } from '../types/exposed-settings' -import { User } from '../types/user' -window.i18n = window.i18n || {} -window.i18n.currentLangCode = window.i18n.currentLangCode || 'en' +function resetMeta() { + window.metaAttributesCache = new Map() + window.metaAttributesCache.set('ol-i18n', { currentLangCode: 'en' }) + window.metaAttributesCache.set('ol-ExposedSettings', { + adminEmail: 'placeholder@example.com', + appName: 'Overleaf', + cookieDomain: '.overleaf.stories', + dropboxAppName: 'Overleaf-Stories', + emailConfirmationDisabled: false, + enableSubscriptions: true, + hasAffiliationsFeature: false, + hasLinkUrlFeature: true, + hasLinkedProjectFileFeature: true, + hasLinkedProjectOutputFileFeature: true, + hasSamlFeature: true, + ieeeBrandId: 15, + isOverleaf: true, + labsEnabled: true, + maxEntitiesPerProject: 10, + maxUploadSize: 5 * 1024 * 1024, + recaptchaDisabled: { + invite: true, + login: true, + passwordReset: true, + register: true, + addEmail: true, + }, + sentryAllowedOriginRegex: '', + siteUrl: 'http://localhost', + templateLinks: [], + textExtensions: [ + 'tex', + 'latex', + 'sty', + 'cls', + 'bst', + 'bib', + 'bibtex', + 'txt', + 'tikz', + 'mtx', + 'rtex', + 'md', + 'asy', + 'lbx', + 'bbx', + 'cbx', + 'm', + 'lco', + 'dtx', + 'ins', + 'ist', + 'def', + 'clo', + 'ldf', + 'rmd', + 'lua', + 'gv', + 'mf', + 'lhs', + 'mk', + 'xmpdata', + 'cfg', + 'rnw', + 'ltx', + 'inc', + ], + editableFilenames: ['latexmkrc', '.latexmkrc', 'makefile', 'gnumakefile'], + validRootDocExtensions: ['tex', 'Rtex', 'ltx', 'Rnw'], + fileIgnorePattern: + '**/{{__MACOSX,.git,.texpadtmp,.R}{,/**},.!(latexmkrc),*.{dvi,aux,log,toc,out,pdfsync,synctex,synctex(busy),fdb_latexmk,fls,nlo,ind,glo,gls,glg,bbl,blg,doc,docx,gz,swp}}', + projectUploadTimeout: 12000, + }) +} + i18n.use(initReactI18next).init({ lng: 'en', @@ -41,10 +112,6 @@ i18n.use(initReactI18next).init({ }, }) -// avoid some errors by creating these objects in advance -window.user = {} as User -window.ExposedSettings = {} as ExposedSettings - const preview: Preview = { parameters: { // Automatically mark prop-types like onClick, onToggle, etc as Storybook @@ -98,6 +165,8 @@ const preview: Preview = { ? bootstrap5Style : bootstrap3Style + resetMeta() + return ( <> {activeStyle && } @@ -109,3 +178,6 @@ const preview: Preview = { } export default preview + +// Populate meta for top-level access in modules on import +resetMeta() diff --git a/services/web/app/src/infrastructure/ExpressLocals.js b/services/web/app/src/infrastructure/ExpressLocals.js index cdfa08df84..c7dcb9eebb 100644 --- a/services/web/app/src/infrastructure/ExpressLocals.js +++ b/services/web/app/src/infrastructure/ExpressLocals.js @@ -18,6 +18,10 @@ const { const { addOptionalCleanupHandlerAfterDrainingConnections, } = require('./GracefulShutdown') +const { expressify } = require('@overleaf/promise-utils') +const { + loadAssignmentsInLocals, +} = require('../Features/SplitTests/SplitTestMiddleware') const IEEE_BRAND_ID = Settings.ieeeBrandId @@ -76,6 +80,10 @@ function getWebpackAssets(entrypoint, section) { } module.exports = function (webRouter, privateApiRouter, publicApiRouter) { + webRouter.use( + expressify(loadAssignmentsInLocals(['remove-window-attributes'])) + ) + if (process.env.NODE_ENV === 'development') { // In the dev-env, delay requests until we fetched the manifest once. webRouter.use(function (req, res, next) { diff --git a/services/web/app/views/project/editor/meta.pug b/services/web/app/views/project/editor/meta.pug index 3210d65537..5a5ecd8ff8 100644 --- a/services/web/app/views/project/editor/meta.pug +++ b/services/web/app/views/project/editor/meta.pug @@ -39,9 +39,6 @@ meta(name="ol-projectTags" data-type="json" content=projectTags) meta(name="ol-loadingText", data-type="string" content=translate("loading")) meta(name="ol-translationLoadErrorMessage", data-type="string" content=translate("could_not_load_translations")) -- var fileActionI18n = ['edited', 'renamed', 'created', 'deleted'].reduce((acc, i) => {acc[i] = translate('file_action_' + i); return acc}, {}) -meta(name="ol-fileActionI18n" data-type="json" content=fileActionI18n) - if (settings.overleaf != null) meta(name="ol-overallThemes" data-type="json" content=overallThemes) diff --git a/services/web/cypress/support/component.ts b/services/web/cypress/support/component.ts index 704458cb2b..0f246c392a 100644 --- a/services/web/cypress/support/component.ts +++ b/services/web/cypress/support/component.ts @@ -1,19 +1,11 @@ import 'cypress-plugin-tab' import '../../frontend/stylesheets/main-style.less' -import './ct/window' // needs to be before i18n +import { resetMeta } from './ct/window' // needs to be before i18n import '../../frontend/js/i18n' import './shared/commands' import './shared/exceptions' import './ct/commands' beforeEach(function () { - cy.window().then(win => { - win.metaAttributesCache = new Map() - }) -}) - -afterEach(function () { - cy.window().then(win => { - win.metaAttributesCache?.clear() - }) + resetMeta() }) diff --git a/services/web/cypress/support/ct/window.ts b/services/web/cypress/support/ct/window.ts index fcdcc79479..ae2a194bc1 100644 --- a/services/web/cypress/support/ct/window.ts +++ b/services/web/cypress/support/ct/window.ts @@ -1,10 +1,16 @@ -window.i18n = { currentLangCode: 'en' } -window.ExposedSettings = { - appName: 'Overleaf', - validRootDocExtensions: ['tex', 'Rtex', 'ltx', 'Rnw'], - fileIgnorePattern: - '**/{{__MACOSX,.git,.texpadtmp,.R}{,/**},.!(latexmkrc),*.{dvi,aux,log,toc,out,pdfsync,synctex,synctex(busy),fdb_latexmk,fls,nlo,ind,glo,gls,glg,bbl,blg,doc,docx,gz,swp}}', - hasLinkedProjectFileFeature: true, - hasLinkedProjectOutputFileFeature: true, - hasLinkUrlFeature: true, -} as typeof window.ExposedSettings +export function resetMeta() { + window.metaAttributesCache = new Map() + window.metaAttributesCache.set('ol-i18n', { currentLangCode: 'en' }) + window.metaAttributesCache.set('ol-ExposedSettings', { + appName: 'Overleaf', + validRootDocExtensions: ['tex', 'Rtex', 'ltx', 'Rnw'], + fileIgnorePattern: + '**/{{__MACOSX,.git,.texpadtmp,.R}{,/**},.!(latexmkrc),*.{dvi,aux,log,toc,out,pdfsync,synctex,synctex(busy),fdb_latexmk,fls,nlo,ind,glo,gls,glg,bbl,blg,doc,docx,gz,swp}}', + hasLinkedProjectFileFeature: true, + hasLinkedProjectOutputFileFeature: true, + hasLinkUrlFeature: true, + }) +} + +// Populate meta for top-level access in modules on import +resetMeta() diff --git a/services/web/frontend/js/features/cookie-banner/index.js b/services/web/frontend/js/features/cookie-banner/index.js index afd48f2787..a22046a08d 100644 --- a/services/web/frontend/js/features/cookie-banner/index.js +++ b/services/web/frontend/js/features/cookie-banner/index.js @@ -1,3 +1,5 @@ +import getMeta from '@/utils/meta' + function loadGA() { if (window.olLoadGA) { window.olLoadGA() @@ -6,7 +8,7 @@ function loadGA() { function setConsent(value) { document.querySelector('.cookie-banner').classList.add('hidden') - const cookieDomain = window.ExposedSettings.cookieDomain + const cookieDomain = getMeta('ol-ExposedSettings').cookieDomain const oneYearInSeconds = 60 * 60 * 24 * 365 const cookieAttributes = '; path=/' + @@ -23,7 +25,10 @@ function setConsent(value) { } } -if (window.ExposedSettings.gaToken || window.ExposedSettings.gaTokenV4) { +if ( + getMeta('ol-ExposedSettings').gaToken || + getMeta('ol-ExposedSettings').gaTokenV4 +) { document .querySelectorAll('[data-ol-cookie-banner-set-consent]') .forEach(el => { diff --git a/services/web/frontend/js/features/editor-left-menu/components/actions-menu.tsx b/services/web/frontend/js/features/editor-left-menu/components/actions-menu.tsx index b94a11f15b..65d53a557d 100644 --- a/services/web/frontend/js/features/editor-left-menu/components/actions-menu.tsx +++ b/services/web/frontend/js/features/editor-left-menu/components/actions-menu.tsx @@ -12,9 +12,9 @@ const components = importOverleafModules('editorLeftMenuManageTemplate') as { export default function ActionsMenu() { const { t } = useTranslation() - const anonymous = getMeta('ol-anonymous') as boolean | undefined + const anonymous = getMeta('ol-anonymous') - if (anonymous === true || anonymous === undefined) { + if (anonymous) { return null } diff --git a/services/web/frontend/js/features/editor-left-menu/components/help-menu.tsx b/services/web/frontend/js/features/editor-left-menu/components/help-menu.tsx index d5c34e7abd..ceb64d1857 100644 --- a/services/web/frontend/js/features/editor-left-menu/components/help-menu.tsx +++ b/services/web/frontend/js/features/editor-left-menu/components/help-menu.tsx @@ -6,7 +6,7 @@ import HelpShowHotkeys from './help-show-hotkeys' export default function HelpMenu() { const { t } = useTranslation() - const showSupport = getMeta('ol-showSupport') as boolean | undefined + const showSupport = getMeta('ol-showSupport') return ( <> diff --git a/services/web/frontend/js/features/editor-left-menu/components/settings-menu.tsx b/services/web/frontend/js/features/editor-left-menu/components/settings-menu.tsx index 5504419e2f..42c930cb40 100644 --- a/services/web/frontend/js/features/editor-left-menu/components/settings-menu.tsx +++ b/services/web/frontend/js/features/editor-left-menu/components/settings-menu.tsx @@ -19,9 +19,9 @@ import SettingsSyntaxValidation from './settings/settings-syntax-validation' export default function SettingsMenu() { const { t } = useTranslation() - const anonymous = getMeta('ol-anonymous') as boolean | undefined + const anonymous = getMeta('ol-anonymous') - if (anonymous === true || anonymous === undefined) { + if (anonymous) { return null } diff --git a/services/web/frontend/js/features/editor-left-menu/components/settings/settings-editor-theme.tsx b/services/web/frontend/js/features/editor-left-menu/components/settings/settings-editor-theme.tsx index 8244b298b9..dfd9fddd9f 100644 --- a/services/web/frontend/js/features/editor-left-menu/components/settings/settings-editor-theme.tsx +++ b/services/web/frontend/js/features/editor-left-menu/components/settings/settings-editor-theme.tsx @@ -7,10 +7,8 @@ import type { Option } from './settings-menu-select' export default function SettingsEditorTheme() { const { t } = useTranslation() - const editorThemes = getMeta('ol-editorThemes') as string[] | undefined - const legacyEditorThemes = getMeta('ol-legacyEditorThemes') as - | string[] - | undefined + const editorThemes = getMeta('ol-editorThemes') + const legacyEditorThemes = getMeta('ol-legacyEditorThemes') const { editorTheme, setEditorTheme } = useProjectSettingsContext() const options = useMemo(() => { diff --git a/services/web/frontend/js/features/editor-left-menu/components/settings/settings-image-name.tsx b/services/web/frontend/js/features/editor-left-menu/components/settings/settings-image-name.tsx index 78c62ab755..5986999a26 100644 --- a/services/web/frontend/js/features/editor-left-menu/components/settings/settings-image-name.tsx +++ b/services/web/frontend/js/features/editor-left-menu/components/settings/settings-image-name.tsx @@ -4,26 +4,26 @@ import getMeta from '../../../../utils/meta' import SettingsMenuSelect from './settings-menu-select' import type { Option } from './settings-menu-select' import { useProjectSettingsContext } from '../../context/project-settings-context' -import type { AllowedImageName } from '../../../../../../types/project-settings' export default function SettingsImageName() { const { t } = useTranslation() const { imageName, setImageName } = useProjectSettingsContext() - const allowedImageNames = getMeta('ol-allowedImageNames') as - | AllowedImageName[] - | undefined + const allowedImageNames = useMemo( + () => getMeta('ol-allowedImageNames') || [], + [] + ) const options: Array