diff --git a/app/client/components/AceEditorCompletions.ts b/app/client/components/AceEditorCompletions.ts index 6046cb8b38..3417923c36 100644 --- a/app/client/components/AceEditorCompletions.ts +++ b/app/client/components/AceEditorCompletions.ts @@ -1,6 +1,5 @@ import ace, {Ace} from 'ace-builds'; import {ISuggestionWithValue} from 'app/common/ActiveDocAPI'; -import {commonUrls} from 'app/common/gristUrls'; export interface ICompletionOptions { getSuggestions(prefix: string): Promise; @@ -271,7 +270,7 @@ function retokenizeAceCompleterRow(rowData: AceSuggestion, tokens: Ace.Token[]): // Include into new tokens a special token that will be hidden, but include the link URL. On // click, we find it to know what URL to open. - const href = `${commonUrls.functions}/#` + + const href = `${window.gristConfig.commonUrls.functions}/#` + rowData.funcname.slice(linkStart, linkEnd).toLowerCase(); newTokens.push({value: href, type: 'grist_link_hidden'}); diff --git a/app/client/declarations/global.d.ts b/app/client/declarations/global.d.ts new file mode 100644 index 0000000000..254f7e5f18 --- /dev/null +++ b/app/client/declarations/global.d.ts @@ -0,0 +1,7 @@ +import {GristLoadConfig} from "app/common/gristUrls"; + +declare global { + export interface Window { + gristConfig: GristLoadConfig; + } +} diff --git a/app/client/ui/AccountWidget.ts b/app/client/ui/AccountWidget.ts index 31f9f0fa4d..06e363e915 100644 --- a/app/client/ui/AccountWidget.ts +++ b/app/client/ui/AccountWidget.ts @@ -16,7 +16,7 @@ import { menuItemLink, menuSubHeader, } from 'app/client/ui2018/menus'; -import {commonUrls, isFeatureEnabled} from 'app/common/gristUrls'; +import {isFeatureEnabled} from 'app/common/gristUrls'; import {FullUser} from 'app/common/LoginSessionAPI'; import * as roles from 'app/common/roles'; import {Disposable, dom, DomElementArg, styled} from 'grainjs'; @@ -126,7 +126,7 @@ export class AccountWidget extends Disposable { menuItemLink({href: getLoginOrSignupUrl()}, t("Sign in")), menuDivider(), documentSettingsItem, - menuItemLink({href: commonUrls.plans}, t("Pricing")), + menuItemLink({href: window.gristConfig.commonUrls.plans}, t("Pricing")), mobileModeToggle, ]; } @@ -249,7 +249,7 @@ export class AccountWidget extends Disposable { const isEnabled = (deploymentType === 'core') && isFeatureEnabled("supportGrist"); if (isEnabled) { return menuItemLink(t('Support Grist'), ' ๐Ÿ’›', - {href: commonUrls.githubSponsorGristLabs, target: '_blank'}, + {href: window.gristConfig.commonUrls.githubSponsorGristLabs, target: '_blank'}, testId('usermenu-support-grist'), ); } diff --git a/app/client/ui/AdminLeftPanel.ts b/app/client/ui/AdminLeftPanel.ts index e7a20037d7..ba8ffe0944 100644 --- a/app/client/ui/AdminLeftPanel.ts +++ b/app/client/ui/AdminLeftPanel.ts @@ -10,7 +10,6 @@ import {colors, vars} from 'app/client/ui2018/cssVars'; import {IconName} from 'app/client/ui2018/IconList'; import {cssLink} from 'app/client/ui2018/links'; import {AdminPanelPage} from 'app/common/gristUrls'; -import {commonUrls} from 'app/common/gristUrls'; import {getGristConfig} from "app/common/urlUtils"; import {Computed, dom, DomContents, MultiHolder, Observable, styled} from 'grainjs'; @@ -77,7 +76,7 @@ export function buildAdminLeftPanel(owner: MultiHolder, appModel: AppModel): Pag buildPageEntry('docs', 'Page', adminControlsAvailable), (adminControlsAvailable ? null : cssPanelLink(cssLearnMoreLink( - {href: commonUrls.helpAdminControls, target: "_blank"}, + {href: window.gristConfig.commonUrls.helpAdminControls, target: "_blank"}, t("Learn more"), css.cssPageIcon('FieldLink'), testId('learn-more'), )) diff --git a/app/client/ui/AdminPanel.ts b/app/client/ui/AdminPanel.ts index 9d69424d74..c9a52679f4 100644 --- a/app/client/ui/AdminPanel.ts +++ b/app/client/ui/AdminPanel.ts @@ -23,7 +23,7 @@ import {mediaSmall, testId, theme, vars} from 'app/client/ui2018/cssVars'; import {cssLink, makeLinks} from 'app/client/ui2018/links'; import {toggleSwitch} from 'app/client/ui2018/toggleSwitch'; import {BootProbeInfo, BootProbeResult, SandboxingBootProbeDetails} from 'app/common/BootProbe'; -import {AdminPanelPage, commonUrls, getPageTitleSuffix, LatestVersionAvailable} from 'app/common/gristUrls'; +import {AdminPanelPage, getPageTitleSuffix, LatestVersionAvailable} from 'app/common/gristUrls'; import {InstallAPI, InstallAPIImpl} from 'app/common/InstallAPI'; import {getGristConfig} from 'app/common/urlUtils'; import * as version from 'app/common/version'; @@ -274,7 +274,7 @@ Please log in as an administrator.`)), dom( 'div', {style: 'margin-top: 8px'}, - cssLink({href: commonUrls.helpSandboxing, target: '_blank'}, t('Learn more.')) + cssLink({href: window.gristConfig.commonUrls.helpSandboxing, target: '_blank'}, t('Learn more.')) ), ]; } @@ -675,7 +675,7 @@ system if you enable Grist Enterprise. {{contactUsLink}} to \ learn more.", { contactUsLink: cssLink( - { href: commonUrls.contact, target: "_blank" }, + { href: window.gristConfig.commonUrls.contact, target: "_blank" }, t("Contact us") ), } diff --git a/app/client/ui/AppHeader.ts b/app/client/ui/AppHeader.ts index 80ee3b425f..133c892004 100644 --- a/app/client/ui/AppHeader.ts +++ b/app/client/ui/AppHeader.ts @@ -3,7 +3,6 @@ import {getTheme} from 'app/client/ui/CustomThemes'; import {cssLeftPane} from 'app/client/ui/PagePanels'; import {colors, theme, vars} from 'app/client/ui2018/cssVars'; import {menu, menuItem, menuItemLink, menuSubHeader} from 'app/client/ui2018/menus'; -import {commonUrls} from 'app/common/gristUrls'; import {getOrgName, isTemplatesOrg, Organization} from 'app/common/UserAPI'; import {AppModel} from 'app/client/models/AppModel'; import {icon} from 'app/client/ui2018/icons'; @@ -131,7 +130,7 @@ export class AppHeader extends Disposable { // When signed out and on the templates site (in SaaS Grist), link to the templates page. return cssOrgLink( cssOrgName(dom.text(this._appLogoOrgName), testId('orgname')), - {href: commonUrls.templates}, + {href: window.gristConfig.commonUrls.templates}, testId('org'), ); } else { @@ -228,7 +227,7 @@ export class AppHeader extends Disposable { name: t('Grist Templates'), link: { type: 'href', - href: commonUrls.templates, + href: window.gristConfig.commonUrls.templates, }, }; } diff --git a/app/client/ui/AuditLogStreamingConfig.ts b/app/client/ui/AuditLogStreamingConfig.ts index 752c73b8d4..a6ed4d904b 100644 --- a/app/client/ui/AuditLogStreamingConfig.ts +++ b/app/client/ui/AuditLogStreamingConfig.ts @@ -14,7 +14,6 @@ import { AuditLogStreamingDestinationName, AuditLogStreamingDestinationNameChecker, } from "app/common/Config"; -import { commonUrls } from "app/common/gristUrls"; import { Computed, Disposable, dom, makeTestId, Observable, styled } from "grainjs"; const t = makeT("AuditLogStreamingConfig"); @@ -42,7 +41,7 @@ security information and event management (SIEM) system like \ Splunk. {{learnMoreLink}}.", { learnMoreLink: cssLink( - { href: commonUrls.helpInstallAuditLogs, target: "_blank" }, + { href: window.gristConfig.commonUrls.helpInstallAuditLogs, target: "_blank" }, t("Learn more") ), } diff --git a/app/client/ui/AuditLogsPage.ts b/app/client/ui/AuditLogsPage.ts index 76090c2ea8..1b505ca248 100644 --- a/app/client/ui/AuditLogsPage.ts +++ b/app/client/ui/AuditLogsPage.ts @@ -15,7 +15,7 @@ import { cssBreadcrumbs, separator } from "app/client/ui2018/breadcrumbs"; import { textButton } from "app/client/ui2018/buttons"; import { theme, vars } from "app/client/ui2018/cssVars"; import { cssLink } from "app/client/ui2018/links"; -import { commonUrls, getPageTitleSuffix } from "app/common/gristUrls"; +import { getPageTitleSuffix } from "app/common/gristUrls"; import { getGristConfig } from "app/common/urlUtils"; import { Computed, @@ -115,7 +115,7 @@ SIEM (security information and event management) system if you \ enable Grist Enterprise. {{contactUsLink}} to learn more.", { contactUsLink: cssLink( - { href: commonUrls.contact, target: "_blank" }, + { href: window.gristConfig.commonUrls.contact, target: "_blank" }, t("Contact us") ), } diff --git a/app/client/ui/CustomWidgetGallery.ts b/app/client/ui/CustomWidgetGallery.ts index cc6937c9fe..2ea3c69b72 100644 --- a/app/client/ui/CustomWidgetGallery.ts +++ b/app/client/ui/CustomWidgetGallery.ts @@ -12,7 +12,6 @@ import {loadingSpinner} from 'app/client/ui2018/loaders'; import {IModalControl, modal} from 'app/client/ui2018/modals'; import {AccessLevel, ICustomWidget, matchWidget, WidgetAuthor} from 'app/common/CustomWidget'; import {userTrustsCustomWidget} from 'app/client/ui/userTrustsCustomWidget'; -import {commonUrls} from 'app/common/gristUrls'; import {bundleChanges, Computed, Disposable, dom, makeTestId, Observable, styled} from 'grainjs'; import escapeRegExp from 'lodash/escapeRegExp'; @@ -138,7 +137,7 @@ class CustomWidgetGallery extends Disposable { cssFooter( dom('div', cssHelpLink( - {href: commonUrls.helpCustomWidgets, target: '_blank'}, + {href: window.gristConfig.commonUrls.helpCustomWidgets, target: '_blank'}, cssHelpIcon('Question'), t('Learn more about Custom Widgets'), ), diff --git a/app/client/ui/DocumentSettings.ts b/app/client/ui/DocumentSettings.ts index 176e50eb9c..7ce8309eab 100644 --- a/app/client/ui/DocumentSettings.ts +++ b/app/client/ui/DocumentSettings.ts @@ -29,7 +29,7 @@ import {confirmModal, cssModalButtons, cssModalTitle, cssSpinner, modal} from 'a import {buildCurrencyPicker} from 'app/client/widgets/CurrencyPicker'; import {buildTZAutocomplete} from 'app/client/widgets/TZAutocomplete'; import {EngineCode} from 'app/common/DocumentSettings'; -import {commonUrls, GristLoadConfig, PREFERRED_STORAGE_ANCHOR} from 'app/common/gristUrls'; +import {GristLoadConfig, PREFERRED_STORAGE_ANCHOR} from 'app/common/gristUrls'; import {not, propertyCompare} from 'app/common/gutil'; import {getCurrency, locales} from 'app/common/Locales'; import {isOwner, isOwnerOrEditor} from 'app/common/roles'; @@ -176,7 +176,9 @@ document is first opened, or when a document responds to changes.' expandedContent: dom('div', cssWrap( t('Document ID to use whenever the REST API calls for {{docId}}. See {{apiURL}}', { - apiURL: cssLink({href: commonUrls.helpAPI, target: '_blank'}, t('API documentation.')), + apiURL: cssLink( + {href: window.gristConfig.commonUrls.helpAPI, target: '_blank'}, t('API documentation.') + ), docId: dom('code', 'docId') }) ), @@ -573,7 +575,9 @@ No data will be lost, except possibly currently pending actions.' description: t('Document automatically opens in {{fiddleModeDocUrl}}. \ Anyone may edit, which will create a new unsaved copy.', { - fiddleModeDocUrl: cssLink({href: commonUrls.helpFiddleMode, target: '_blank'}, t('fiddle mode')) + fiddleModeDocUrl: cssLink( + {href: window.gristConfig.commonUrls.helpFiddleMode, target: '_blank'}, t('fiddle mode') + ) } ), itemTestId: testId('doctype-modal-option-template'), @@ -680,13 +684,13 @@ function displayCurrentType( const learnMore = () => t( '[Learn more.]({{learnLink}})', - {learnLink: commonUrls.attachmentStorage} + {learnLink: window.gristConfig.commonUrls.attachmentStorage} ); function stillExternalCopy(inProgress: Observable, ...args: IDomArgs) { const someExternal = () => t( '**Some existing attachments are still [external]({{externalLink}})**.', - {externalLink: commonUrls.attachmentStorage} + {externalLink: window.gristConfig.commonUrls.attachmentStorage} ); const startToInternal = () => t( @@ -713,7 +717,7 @@ function stillExternalCopy(inProgress: Observable, ...args: IDomArgs, ...args: IDomArgs) { const someInternal = () => t( '**Some existing attachments are still [internal]({{internalLink}})** (stored in SQLite file).', - {internalLink: commonUrls.attachmentStorage} + {internalLink: window.gristConfig.commonUrls.attachmentStorage} ); const startToExternal = () => t( diff --git a/app/client/ui/DuplicateTable.ts b/app/client/ui/DuplicateTable.ts index d3437c9ce5..f7b5e17aef 100644 --- a/app/client/ui/DuplicateTable.ts +++ b/app/client/ui/DuplicateTable.ts @@ -7,7 +7,6 @@ import {colors} from 'app/client/ui2018/cssVars'; import {icon} from 'app/client/ui2018/icons'; import {cssLink} from 'app/client/ui2018/links'; import {saveModal} from 'app/client/ui2018/modals'; -import {commonUrls} from 'app/common/gristUrls'; import {Computed, Disposable, dom, input, makeTestId, Observable, styled} from 'grainjs'; const t = makeT('DuplicateTable'); @@ -84,7 +83,7 @@ class DuplicateTableModal extends Disposable { cssWarning( cssWarningIcon('Warning'), dom('div', t("Instead of duplicating tables, it's usually better to segment data using linked views. {{link}}", - {link: cssLink({href: commonUrls.helpLinkingWidgets, target: '_blank'}, 'Read More.')} + {link: cssLink({href: window.gristConfig.commonUrls.helpLinkingWidgets, target: '_blank'}, 'Read More.')} )), ), cssField( diff --git a/app/client/ui/FormContainer.ts b/app/client/ui/FormContainer.ts index 1b4ccb2a31..cd76cf8ae0 100644 --- a/app/client/ui/FormContainer.ts +++ b/app/client/ui/FormContainer.ts @@ -1,7 +1,6 @@ import {makeT} from 'app/client/lib/localization'; import {colors, mediaSmall} from 'app/client/ui2018/cssVars'; import {icon} from 'app/client/ui2018/icons'; -import {commonUrls} from 'app/common/gristUrls'; import {DomContents, DomElementArg, styled} from 'grainjs'; const t = makeT('FormContainer'); @@ -25,7 +24,7 @@ export function buildFormFooter() { cssPoweredByGrist( cssPoweredByGristLink( { - href: commonUrls.forms, + href: window.gristConfig.commonUrls.forms, target: '_blank', 'aria-label': t('Powered by Grist'), }, @@ -35,7 +34,7 @@ export function buildFormFooter() { ), cssBuildForm( cssBuildFormLink( - {href: commonUrls.forms, target: '_blank'}, + {href: window.gristConfig.commonUrls.forms, target: '_blank'}, t('Build your own form'), icon('Expand'), ), diff --git a/app/client/ui/GristTooltips.ts b/app/client/ui/GristTooltips.ts index 597b1fde79..3a8f9c1b4e 100644 --- a/app/client/ui/GristTooltips.ts +++ b/app/client/ui/GristTooltips.ts @@ -6,7 +6,7 @@ import {ShortcutKey, ShortcutKeyContent} from 'app/client/ui/ShortcutKey'; import {basicButtonLink} from 'app/client/ui2018/buttons'; import {icon} from 'app/client/ui2018/icons'; import {cssLink} from 'app/client/ui2018/links'; -import {commonUrls, GristDeploymentType} from 'app/common/gristUrls'; +import {GristDeploymentType} from 'app/common/gristUrls'; import {BehavioralPrompt} from 'app/common/Prefs'; import {getGristConfig} from 'app/common/urlUtils'; import {dom, DomContents, DomElementArg, styled} from 'grainjs'; @@ -91,14 +91,14 @@ export const GristTooltips: Record = { t('Useful for storing the timestamp or author of a new record, data cleaning, and more.') ), dom('div', - cssLink({href: commonUrls.helpTriggerFormulas, target: '_blank'}, t('Learn more.')), + cssLink({href: window.gristConfig.commonUrls.helpTriggerFormulas, target: '_blank'}, t('Learn more.')), ), ...args, ), selectBy: (...args: DomElementArg[]) => cssTooltipContent( dom('div', t('Link your new widget to an existing widget on this page.')), dom('div', - cssLink({href: commonUrls.helpLinkingWidgets, target: '_blank'}, t('Learn more.')), + cssLink({href: window.gristConfig.commonUrls.helpLinkingWidgets, target: '_blank'}, t('Learn more.')), ), ...args, ), @@ -107,7 +107,7 @@ export const GristTooltips: Record = { t('Try out changes in a copy, then decide whether to replace the original with your edits.') ), dom('div', - cssLink({href: commonUrls.helpTryingOutChanges, target: '_blank'}, t('Learn more.')), + cssLink({href: window.gristConfig.commonUrls.helpTryingOutChanges, target: '_blank'}, t('Learn more.')), ), ...args, ), @@ -117,14 +117,14 @@ export const GristTooltips: Record = { see or edit which parts of your document.') ), dom('div', - cssLink({href: commonUrls.helpAccessRules, target: '_blank'}, t('Learn more.')), + cssLink({href: window.gristConfig.commonUrls.helpAccessRules, target: '_blank'}, t('Learn more.')), ), ...args, ), addRowConditionalStyle: (...args: DomElementArg[]) => cssTooltipContent( dom('div', t('Apply conditional formatting to rows based on formulas.')), dom('div', - cssLink({href: commonUrls.helpConditionalFormatting, target: '_blank'}, t('Learn more.')), + cssLink({href: window.gristConfig.commonUrls.helpConditionalFormatting, target: '_blank'}, t('Learn more.')), ), ...args, ), @@ -132,14 +132,14 @@ see or edit which parts of your document.') dom('div', t('Apply conditional formatting to cells in this column when formula conditions are met.')), dom('div', t('Click on โ€œOpen row stylesโ€ to apply conditional formatting to rows.')), dom('div', - cssLink({href: commonUrls.helpConditionalFormatting, target: '_blank'}, t('Learn more.')), + cssLink({href: window.gristConfig.commonUrls.helpConditionalFormatting, target: '_blank'}, t('Learn more.')), ), ...args, ), uuid: (...args: DomElementArg[]) => cssTooltipContent( dom('div', t('A UUID is a randomly-generated string that is useful for unique identifiers and link keys.')), dom('div', - cssLink({href: commonUrls.helpLinkKeys, target: '_blank'}, t('Learn more.')), + cssLink({href: window.gristConfig.commonUrls.helpLinkKeys, target: '_blank'}, t('Learn more.')), ), ...args, ), @@ -147,14 +147,14 @@ see or edit which parts of your document.') dom('div', t('Lookups return data from related tables.')), dom('div', t('Use reference columns to relate data in different tables.')), dom('div', - cssLink({href: commonUrls.helpColRefs, target: '_blank'}, t('Learn more.')), + cssLink({href: window.gristConfig.commonUrls.helpColRefs, target: '_blank'}, t('Learn more.')), ), ...args, ), formulaColumn: (...args: DomElementArg[]) => cssTooltipContent( dom('div', t('Formulas support many Excel functions and full Python syntax.')), dom('div', - cssLink({href: commonUrls.formulas, target: '_blank'}, t('Learn more.')), + cssLink({href: window.gristConfig.commonUrls.formulas, target: '_blank'}, t('Learn more.')), ), ...args, ), @@ -179,7 +179,7 @@ see or edit which parts of your document.') example: dom.create(buildHighlightedCode, 'choice.Role == "Manager"', {}, {style: 'margin-top: 8px;'}), })), dom('div', - cssLink({href: commonUrls.helpFilteringReferenceChoices, target: '_blank'}, t('Learn more.')), + cssLink({href: window.gristConfig.commonUrls.helpFilteringReferenceChoices, target: '_blank'}, t('Learn more.')), ), ...args, ), @@ -188,7 +188,7 @@ see or edit which parts of your document.') t('Community widgets are created and maintained by Grist community members.') ), dom('div', - cssLink({href: commonUrls.helpCustomWidgets, target: '_blank'}, t('Learn more.')), + cssLink({href: window.gristConfig.commonUrls.helpCustomWidgets, target: '_blank'}, t('Learn more.')), ), ...args, ), @@ -228,14 +228,14 @@ external storage." "\n\n" + t( "[Learn more.]({{link}})", { - link: commonUrls.attachmentStorage + link: window.gristConfig.commonUrls.attachmentStorage } )), ...args, ), adminControls: (...args: DomElementArg[]) => cssTooltipContent( dom('div', t('Manage users and resources in a Grist installation.')), - dom('div', cssLink({href: commonUrls.helpAdminControls, target: "_blank"}, t('Learn more.'))), + dom('div', cssLink({href: window.gristConfig.commonUrls.helpAdminControls, target: "_blank"}, t('Learn more.'))), ...args, ), uploadAttachments: (...args: DomElementArg[]) => cssTooltipContent( @@ -272,7 +272,7 @@ export const ErrorTooltips: Record = { dom( "div", cssLink( - {href: commonUrls.helpSummaryFormulas, target: "_blank"}, + {href: window.gristConfig.commonUrls.helpSummaryFormulas, target: "_blank"}, t("Learn more.") ) ), @@ -306,7 +306,7 @@ export const GristBehavioralPrompts: Record cssTooltipContent( dom('div', t('The Raw Data page lists all data tables in your document, \ including summary tables and tables not included in page layouts.')), - dom('div', cssLink({href: commonUrls.helpRawData, target: '_blank'}, t('Learn more.'))), + dom('div', cssLink({href: window.gristConfig.commonUrls.helpRawData, target: '_blank'}, t('Learn more.'))), ...args, ), deploymentTypes: ['saas', 'core', 'enterprise', 'electron'], @@ -345,7 +347,7 @@ including summary tables and tables not included in page layouts.')), content: (...args: DomElementArg[]) => cssTooltipContent( dom('div', t('Access rules give you the power to create nuanced rules \ to determine who can see or edit which parts of your document.')), - dom('div', cssLink({href: commonUrls.helpAccessRules, target: '_blank'}, t('Learn more.'))), + dom('div', cssLink({href: window.gristConfig.commonUrls.helpAccessRules, target: '_blank'}, t('Learn more.'))), ...args, ), deploymentTypes: ['saas', 'core', 'enterprise', 'electron'], @@ -356,7 +358,7 @@ to determine who can see or edit which parts of your document.')), content: (...args: DomElementArg[]) => cssTooltipContent( dom('div', t('Pinned filters are displayed as buttons above the widget.')), dom('div', t('Unpin to hide the the button while keeping the filter.')), - dom('div', cssLink({href: commonUrls.helpFilterButtons, target: '_blank'}, t('Learn more.'))), + dom('div', cssLink({href: window.gristConfig.commonUrls.helpFilterButtons, target: '_blank'}, t('Learn more.'))), ...args, ), deploymentTypes: ['saas', 'core', 'enterprise', 'electron'], @@ -387,7 +389,7 @@ to determine who can see or edit which parts of your document.')), content: (...args: DomElementArg[]) => cssTooltipContent( dom('div', t('Link your new widget to an existing widget on this page.')), dom('div', t('This is the secret to Grist\'s dynamic and productive layouts.')), - dom('div', cssLink({href: commonUrls.helpLinkingWidgets, target: '_blank'}, t('Learn more.'))), + dom('div', cssLink({href: window.gristConfig.commonUrls.helpLinkingWidgets, target: '_blank'}, t('Learn more.'))), ...args, ), deploymentTypes: ['saas', 'core', 'enterprise', 'electron'], @@ -440,7 +442,7 @@ to determine who can see or edit which parts of your document.')), Note each column's type.")), dom('div', t("Can't find the right columns? Click 'Change Widget' to select the table with events \ data.")), - dom('div', cssLink({href: commonUrls.helpCalendarWidget, target: '_blank'}, t('Learn more.'))), + dom('div', cssLink({href: window.gristConfig.commonUrls.helpCalendarWidget, target: '_blank'}, t('Learn more.'))), ...args, ), deploymentTypes: ['saas', 'core', 'enterprise', 'electron'], @@ -458,7 +460,7 @@ data.")), ), dom('div', cssNewsPopupLink(t('Learn more.'), { - href: commonUrls.helpComments, + href: window.gristConfig.commonUrls.helpComments, target: '_blank', }), ), diff --git a/app/client/ui/HomeIntroCards.ts b/app/client/ui/HomeIntroCards.ts index 8705517bdb..7d4b71b822 100644 --- a/app/client/ui/HomeIntroCards.ts +++ b/app/client/ui/HomeIntroCards.ts @@ -7,7 +7,7 @@ import {basicButtonLink, bigPrimaryButton, primaryButtonLink} from 'app/client/u import {colors, theme, vars} from 'app/client/ui2018/cssVars'; import {icon} from 'app/client/ui2018/icons'; import {unstyledButton, unstyledH2} from 'app/client/ui2018/unstyled'; -import {commonUrls, isFeatureEnabled} from 'app/common/gristUrls'; +import {isFeatureEnabled} from 'app/common/gristUrls'; import {getGristConfig} from 'app/common/urlUtils'; import {Computed, dom, IDisposableOwner, makeTestId, styled, subscribeElem} from 'grainjs'; @@ -106,7 +106,7 @@ export function buildHomeIntroCards( unstyledH2(t('Learn more')), cssWebinarsButton( t('Webinars'), - {href: commonUrls.webinars, target: '_blank'}, + {href: window.gristConfig.commonUrls.webinars, target: '_blank'}, testId('webinars'), ) ), @@ -116,7 +116,7 @@ export function buildHomeIntroCards( unstyledH2(t('Find solutions and explore more resources')), cssHelpCenterButton( t('Help center'), - {href: commonUrls.help, target: '_blank'}, + {href: window.gristConfig.commonUrls.help, target: '_blank'}, testId('help-center'), ), ), diff --git a/app/client/ui/HomeLeftPane.ts b/app/client/ui/HomeLeftPane.ts index 9c19c91dd7..b7f558bc0f 100644 --- a/app/client/ui/HomeLeftPane.ts +++ b/app/client/ui/HomeLeftPane.ts @@ -29,7 +29,7 @@ import {newDocMethods} from 'app/client/ui/NewDocMethods'; import {menu, menuIcon, menuItem, upgradableMenuItem, upgradeText} from 'app/client/ui2018/menus'; import {confirmModal} from 'app/client/ui2018/modals'; import * as version from 'app/common/version'; -import {commonUrls, isFeatureEnabled} from 'app/common/gristUrls'; +import {isFeatureEnabled} from 'app/common/gristUrls'; import * as roles from 'app/common/roles'; import {getGristConfig} from 'app/common/urlUtils'; import {Workspace} from 'app/common/UserAPI'; @@ -176,10 +176,10 @@ export function createHomeLeftPane(leftPanelOpen: Observable, home: Hom ) : null ), createHelpTools(home.app), - (commonUrls.termsOfService ? + (window.gristConfig.commonUrls.termsOfService ? cssPageEntry( cssPageLink(cssPageIcon('Memo'), cssLinkText(t("Terms of service")), - { href: commonUrls.termsOfService, target: '_blank' }, + { href: window.gristConfig.commonUrls.termsOfService, target: '_blank' }, testId('dm-tos'), ), ) : null diff --git a/app/client/ui/LeftPanelCommon.ts b/app/client/ui/LeftPanelCommon.ts index 745955bd53..6c70a531aa 100644 --- a/app/client/ui/LeftPanelCommon.ts +++ b/app/client/ui/LeftPanelCommon.ts @@ -19,7 +19,7 @@ import {AppModel} from 'app/client/models/AppModel'; import {testId, theme, vars} from 'app/client/ui2018/cssVars'; import {colorIcon, icon} from 'app/client/ui2018/icons'; import {unstyledButton} from 'app/client/ui2018/unstyled'; -import {commonUrls, isFeatureEnabled} from 'app/common/gristUrls'; +import {isFeatureEnabled} from 'app/common/gristUrls'; import {getGristConfig} from 'app/common/urlUtils'; import {dom, DomContents, Observable, styled} from 'grainjs'; @@ -41,13 +41,13 @@ export function createHelpTools(appModel: AppModel): DomContents { dom.cls('tour-help-center'), deploymentType === 'saas' ? dom.on('click', () => beaconOpenMessage({appModel})) - : {href: commonUrls.help, target: '_blank'}, + : {href: window.gristConfig.commonUrls.help, target: '_blank'}, testId('left-feedback'), ), ), cssPageEntrySmall( cssPageLink(cssPageIcon('FieldLink'), - {href: commonUrls.help, 'aria-label': t("Help Center"), target: '_blank'}, + {href: window.gristConfig.commonUrls.help, 'aria-label': t("Help Center"), target: '_blank'}, ), ), ); diff --git a/app/client/ui/NotifyUI.ts b/app/client/ui/NotifyUI.ts index 9795505d10..22cb6ccc3a 100644 --- a/app/client/ui/NotifyUI.ts +++ b/app/client/ui/NotifyUI.ts @@ -10,7 +10,7 @@ import {theme, vars} from 'app/client/ui2018/cssVars'; import {icon} from 'app/client/ui2018/icons'; import {IconName} from "app/client/ui2018/IconList"; import {menuCssClass} from 'app/client/ui2018/menus'; -import {commonUrls, isFeatureEnabled} from 'app/common/gristUrls'; +import {isFeatureEnabled} from 'app/common/gristUrls'; import {dom, makeTestId, styled} from 'grainjs'; import {cssMenu, defaultMenuOptions, IOpenController, setPopupToCreateDom} from 'popweasel'; @@ -28,7 +28,7 @@ function buildAction(action: NotifyAction, item: Notification, options: IBeaconO appModel.showUpgradeModal())); } else { return dom('a', cssToastAction.cls(''), t("Upgrade Plan"), {target: '_blank'}, - {href: commonUrls.plans}); + {href: window.gristConfig.commonUrls.plans}); } case 'manage': if (urlState().state.get().billing === 'billing') { return null; } diff --git a/app/client/ui/OnboardingPage.ts b/app/client/ui/OnboardingPage.ts index c91fd19181..85cb1ee57d 100644 --- a/app/client/ui/OnboardingPage.ts +++ b/app/client/ui/OnboardingPage.ts @@ -12,7 +12,7 @@ import {icon} from 'app/client/ui2018/icons'; import {IconName} from 'app/client/ui2018/IconList'; import {modal} from 'app/client/ui2018/modals'; import {BaseAPI} from 'app/common/BaseAPI'; -import {commonUrls, getPageTitleSuffix} from 'app/common/gristUrls'; +import {getPageTitleSuffix} from 'app/common/gristUrls'; import {UserPrefs} from 'app/common/Prefs'; import {getGristConfig} from 'app/common/urlUtils'; import { @@ -249,7 +249,7 @@ function buildVideo(_owner: IDisposableOwner, incrementStep: IncrementStep, stat return modal((ctl, modalOwner) => { const youtubePlayer = YouTubePlayer.create(modalOwner, - commonUrls.onboardingTutorialVideoId, + window.gristConfig.commonUrls.onboardingTutorialVideoId, { onPlayerReady: (player) => player.playVideo(), onPlayerStateChange(_player, {data}) { diff --git a/app/client/ui/OpenVideoTour.ts b/app/client/ui/OpenVideoTour.ts index 5444e7d116..55b2e981d4 100644 --- a/app/client/ui/OpenVideoTour.ts +++ b/app/client/ui/OpenVideoTour.ts @@ -6,7 +6,7 @@ import {YouTubePlayer} from 'app/client/ui/YouTubePlayer'; import {theme} from 'app/client/ui2018/cssVars'; import {icon} from 'app/client/ui2018/icons'; import {cssModalCloseButton, modal} from 'app/client/ui2018/modals'; -import {commonUrls, isFeatureEnabled} from 'app/common/gristUrls'; +import {isFeatureEnabled} from 'app/common/gristUrls'; import {dom, keyframes, makeTestId, styled} from 'grainjs'; const t = makeT('OpenVideoTour'); @@ -20,7 +20,7 @@ const testId = makeTestId('test-video-tour-'); return modal( (ctl, owner) => { const youtubePlayer = YouTubePlayer.create(owner, - commonUrls.onboardingTutorialVideoId, + window.gristConfig.commonUrls.onboardingTutorialVideoId, { onPlayerReady: (player) => player.playVideo(), height: '100%', diff --git a/app/client/ui/SupportGristButton.ts b/app/client/ui/SupportGristButton.ts index 75344b4ae1..1f0d55c0a6 100644 --- a/app/client/ui/SupportGristButton.ts +++ b/app/client/ui/SupportGristButton.ts @@ -8,7 +8,7 @@ import {colors, testId, theme, vars} from 'app/client/ui2018/cssVars'; import {icon} from 'app/client/ui2018/icons'; import {cssLink} from 'app/client/ui2018/links'; import {modal} from 'app/client/ui2018/modals'; -import {commonUrls, isFeatureEnabled} from 'app/common/gristUrls'; +import {isFeatureEnabled} from 'app/common/gristUrls'; import {getGristConfig} from 'app/common/urlUtils'; import {Computed, Disposable, dom, DomContents, Observable, styled} from 'grainjs'; @@ -51,7 +51,7 @@ export class SupportGristButton extends Disposable { return cssContributeButton( elemType(cssHeartIcon('๐Ÿ’› '), t('Support Grist'), (which === 'link' ? - {href: commonUrls.githubSponsorGristLabs, target: '_blank'} : + {href: window.gristConfig.commonUrls.githubSponsorGristLabs, target: '_blank'} : dom.on('click', () => this._buildNudgeModal()) ), @@ -152,7 +152,7 @@ document contents. Opt out any time from the {{supportGristLink}} in the user me function helpCenterLink() { return cssLink( t('Help Center'), - {href: commonUrls.helpTelemetryLimited, target: '_blank'}, + {href: window.gristConfig.commonUrls.helpTelemetryLimited, target: '_blank'}, ); } diff --git a/app/client/ui/SupportGristPage.ts b/app/client/ui/SupportGristPage.ts index 0c383a0ba8..0446fe32d4 100644 --- a/app/client/ui/SupportGristPage.ts +++ b/app/client/ui/SupportGristPage.ts @@ -16,7 +16,6 @@ import {basicButtonLink} from 'app/client/ui2018/buttons'; import {icon} from 'app/client/ui2018/icons'; import {cssLink} from 'app/client/ui2018/links'; import {loadingSpinner} from 'app/client/ui2018/loaders'; -import {commonUrls} from 'app/common/gristUrls'; import {TelemetryPrefsWithSources} from 'app/common/InstallAPI'; import {Computed, Disposable, dom, makeTestId} from 'grainjs'; @@ -142,7 +141,7 @@ It also shows to others that there is a determined community behind this product )), cssSponsorButton( cssButtonIconAndText(icon('Heart'), cssButtonText(t('Manage Sponsorship'))), - {href: commonUrls.githubSponsorGristLabs, target: '_blank'}, + {href: window.gristConfig.commonUrls.githubSponsorGristLabs, target: '_blank'}, ), testId('sponsorship-section'), ); @@ -150,27 +149,27 @@ It also shows to others that there is a determined community behind this product public buildSponsorshipSmallButton() { return basicButtonLink('๐Ÿ’› ', t('Sponsor'), - {href: commonUrls.githubSponsorGristLabs, target: '_blank'}); + {href: window.gristConfig.commonUrls.githubSponsorGristLabs, target: '_blank'}); } } function telemetryHelpCenterLink() { return cssLink( t('Help Center'), - {href: commonUrls.helpTelemetryLimited, target: '_blank'}, + {href: window.gristConfig.commonUrls.helpTelemetryLimited, target: '_blank'}, ); } function sponsorGristLink() { return cssLink( t('GitHub Sponsors page'), - {href: commonUrls.githubSponsorGristLabs, target: '_blank'}, + {href: window.gristConfig.commonUrls.githubSponsorGristLabs, target: '_blank'}, ); } function gristCoreLink() { return cssLink( t('GitHub'), - {href: commonUrls.githubGristCore, target: '_blank'}, + {href: window.gristConfig.commonUrls.githubGristCore, target: '_blank'}, ); } diff --git a/app/client/ui/ToggleEnterpriseWidget.ts b/app/client/ui/ToggleEnterpriseWidget.ts index e47dbcf4f2..c966b5188e 100644 --- a/app/client/ui/ToggleEnterpriseWidget.ts +++ b/app/client/ui/ToggleEnterpriseWidget.ts @@ -11,7 +11,7 @@ import {hoverTooltip, showTransientTooltip} from 'app/client/ui/tooltips'; import {bigPrimaryButton} from 'app/client/ui2018/buttons'; import {colors, theme, vars} from 'app/client/ui2018/cssVars'; import {icon} from 'app/client/ui2018/icons'; -import {ActivationState, commonUrls} from 'app/common/gristUrls'; +import {ActivationState} from 'app/common/gristUrls'; import {not} from 'app/common/gutil'; import {getGristConfig} from 'app/common/urlUtils'; import {BindableValue, Computed, Disposable, dom, input, MultiHolder, Observable, styled} from 'grainjs'; @@ -135,8 +135,8 @@ of 30 days has expired. Get an activation key by [contacting us]({{contactLink}} not need an activation key to run Grist Core. Learn more in our [Help Center]({{helpCenter}}).`, { - contactLink: commonUrls.contact, - helpCenter: commonUrls.helpEnterpriseOptIn + contactLink: window.gristConfig.commonUrls.contact, + helpCenter: window.gristConfig.commonUrls.helpEnterpriseOptIn })) ), this._buildPasteYourKey(), @@ -262,7 +262,7 @@ Learn more in our [Help Center]({{helpCenter}}).`, { markdown((txt ? txt + ' ' : '') + t( `To continue using Grist Enterprise, you need to [contact us]({{signupLink}}) to get your activation key.`, { - signupLink: commonUrls.contact, + signupLink: window.gristConfig.commonUrls.contact, })), )), ), @@ -309,7 +309,7 @@ Learn more in our [Help Center]({{helpCenter}}).`, { markdown(t( `Your trial period has expired on **{{expireAt}}**. To continue using Grist Enterprise, you need to [sign up for Grist Enterprise]({{signupLink}}) and paste your activation key below.`, { - signupLink: commonUrls.plans, + signupLink: window.gristConfig.commonUrls.plans, expireAt, })) ), @@ -320,7 +320,7 @@ Learn more in our [Help Center]({{helpCenter}}).`, { markdown(t(`An active subscription is required to continue using Grist Enterprise. You can you activate your subscription by [signing up for Grist Enterprise ]({{signupLink}}) and pasting your activation key below.`, { - signupLink: commonUrls.plans, + signupLink: window.gristConfig.commonUrls.plans, })) ), ]), @@ -348,7 +348,7 @@ function enterpriseNotEnabledCopy() { markdown(t(`An activation key is used to run Grist Enterprise after a trial period of 30 days has expired. Get an activation key by [signing up for Grist Enterprise]({{signupLink}}). You do not need an activation key to run - Grist Core.`, {signupLink: commonUrls.plans})), + Grist Core.`, {signupLink: window.gristConfig.commonUrls.plans})), ), learnMoreLink(), ]; @@ -357,8 +357,8 @@ function enterpriseNotEnabledCopy() { function learnMoreLink() { return cssParagraph( markdown(t(`Learn more in our [Help Center]({{helpCenter}}).`, { - signupLink: commonUrls.plans, - helpCenter: commonUrls.helpEnterpriseOptIn + signupLink: window.gristConfig.commonUrls.plans, + helpCenter: window.gristConfig.commonUrls.helpEnterpriseOptIn }))); } diff --git a/app/client/ui/UserManager.ts b/app/client/ui/UserManager.ts index 4b690f0666..531071175c 100644 --- a/app/client/ui/UserManager.ts +++ b/app/client/ui/UserManager.ts @@ -7,7 +7,7 @@ */ import {makeT} from 'app/client/lib/localization'; import {normalizeEmail} from 'app/common/emails'; -import {commonUrls, isOrgInPathOnly} from 'app/common/gristUrls'; +import {isOrgInPathOnly} from 'app/common/gristUrls'; import {capitalizeFirstWord, isLongerThan} from 'app/common/gutil'; import {getGristConfig} from 'app/common/urlUtils'; import {FullUser} from 'app/common/LoginSessionAPI'; @@ -428,7 +428,7 @@ export class UserManager extends Disposable { }), t(`Add {{member}} to your team`, { member: member.name || t('member') }))); } else if (limit.at >= limit.top) { - elements.push(cssLink({href: commonUrls.plans, target: '_blank'}, + elements.push(cssLink({href: window.gristConfig.commonUrls.plans, target: '_blank'}, t('Create a team to share with more people'))); } return elements; diff --git a/app/client/ui/WelcomeCoachingCall.ts b/app/client/ui/WelcomeCoachingCall.ts index cf1fee47af..8f3321583a 100644 --- a/app/client/ui/WelcomeCoachingCall.ts +++ b/app/client/ui/WelcomeCoachingCall.ts @@ -9,7 +9,6 @@ import {icon} from 'app/client/ui2018/icons'; import {cssLink} from 'app/client/ui2018/links'; import {getGristConfig} from 'app/common/urlUtils'; import {dom, styled} from 'grainjs'; -import {commonUrls} from 'app/common/gristUrls'; const t = makeT('WelcomeCoachingCall'); @@ -98,7 +97,7 @@ We can show you the Grist basics, or start working with your data right away to t("You may also check out {{ourWeeklyWebinars}} to learn more about Grist.", { ourWeeklyWebinars: cssLink( - {href: commonUrls.webinars, target: '_blank'}, + {href: window.gristConfig.commonUrls.webinars, target: '_blank'}, t('our weekly webinars') ) } @@ -116,7 +115,7 @@ We can show you the Grist basics, or start working with your data right away to logTelemetryEvent('clickedScheduleCoachingCall'); }), { - href: commonUrls.freeCoachingCall, + href: window.gristConfig.commonUrls.freeCoachingCall, target: '_blank', }, testId('popup-primary-button'), diff --git a/app/client/ui/errorPages.ts b/app/client/ui/errorPages.ts index 9ad2975468..32cbe141ea 100644 --- a/app/client/ui/errorPages.ts +++ b/app/client/ui/errorPages.ts @@ -7,7 +7,7 @@ import {pagePanels} from 'app/client/ui/PagePanels'; import {createTopBarHome} from 'app/client/ui/TopBar'; import {bigBasicButtonLink, bigPrimaryButtonLink} from 'app/client/ui2018/buttons'; import {theme, vars} from 'app/client/ui2018/cssVars'; -import {commonUrls, getPageTitleSuffix} from 'app/common/gristUrls'; +import {getPageTitleSuffix} from 'app/common/gristUrls'; import {getGristConfig} from 'app/common/urlUtils'; import {dom, DomElementArg, makeTestId, observable, styled} from 'grainjs'; @@ -105,7 +105,7 @@ export function createNotFoundPage(appModel: AppModel, message?: string) { })), cssButtonWrap(bigPrimaryButtonLink(t("Go to main page"), testId('error-primary-btn'), urlState().setLinkUrl({}))), - cssButtonWrap(bigBasicButtonLink(t("Contact support"), {href: commonUrls.contactSupport})), + cssButtonWrap(bigBasicButtonLink(t("Contact support"), {href: window.gristConfig.commonUrls.contactSupport})), ]); } @@ -117,7 +117,7 @@ export function createSigninFailedPage(appModel: AppModel, message?: string) { separator: dom('br') })), signInAgainButton(), - cssButtonWrap(bigBasicButtonLink(t("Contact support"), {href: commonUrls.contactSupport})), + cssButtonWrap(bigBasicButtonLink(t("Contact support"), {href: window.gristConfig.commonUrls.contactSupport})), ]); } @@ -132,7 +132,7 @@ export function createOtherErrorPage(appModel: AppModel, message?: string) { t('There was an unknown error.')), cssButtonWrap(bigPrimaryButtonLink(t("Go to main page"), testId('error-primary-btn'), urlState().setLinkUrl({}))), - cssButtonWrap(bigBasicButtonLink(t("Contact support"), {href: commonUrls.contactSupport})), + cssButtonWrap(bigBasicButtonLink(t("Contact support"), {href: window.gristConfig.commonUrls.contactSupport})), ]); } diff --git a/app/client/widgets/Assistant.ts b/app/client/widgets/Assistant.ts index 68bd268ee8..a5e5993130 100644 --- a/app/client/widgets/Assistant.ts +++ b/app/client/widgets/Assistant.ts @@ -27,7 +27,6 @@ import { AssistanceResponse, DeveloperPromptVersion } from "app/common/Assistanc import { AsyncCreate } from "app/common/AsyncCreate"; import { DocAction } from "app/common/DocActions"; import { isFreePlan } from "app/common/Features"; -import { commonUrls } from "app/common/gristUrls"; import { TelemetryEvent, TelemetryMetadata } from "app/common/Telemetry"; import { getGristConfig } from "app/common/urlUtils"; import { @@ -229,7 +228,7 @@ export class Assistant extends Disposable { content: buildBannerMessage( t("Upgrade to Grist Enterprise to try the new Grist Assistant. {{learnMoreLink}}", { learnMoreLink: cssBannerAnchorLink( - { href: commonUrls.helpAssistant, target: "_blank" }, + { href: window.gristConfig.commonUrls.helpAssistant, target: "_blank" }, t("Learn more.") ) }), diff --git a/app/client/widgets/FormulaAssistant.ts b/app/client/widgets/FormulaAssistant.ts index 02975918f8..1810a2e17a 100644 --- a/app/client/widgets/FormulaAssistant.ts +++ b/app/client/widgets/FormulaAssistant.ts @@ -15,7 +15,6 @@ import {menu, menuItem} from 'app/client/ui2018/menus'; import {Assistant, cssAiImage, cssAiMessage, cssAvatar} from 'app/client/widgets/Assistant'; import {FormulaEditor} from 'app/client/widgets/FormulaEditor'; import {AssistanceState} from 'app/common/Assistance'; -import {commonUrls} from 'app/common/gristUrls'; import {TelemetryEvent, TelemetryMetadata} from 'app/common/Telemetry'; import {getGristConfig} from 'app/common/urlUtils'; import {Disposable, dom, DomElementArg, makeTestId, Observable, styled} from 'grainjs'; @@ -577,7 +576,7 @@ are sent to OpenAI. {{learnMore}}.", urlState().setLinkUrl({ docPage: "code" }) ), learnMore: cssLink(t("Learn more"), { - href: commonUrls.helpFormulaAssistantDataUse, + href: window.gristConfig.commonUrls.helpFormulaAssistantDataUse, target: "_blank", }), } @@ -592,15 +591,15 @@ are sent to OpenAI. {{learnMore}}.", or visit our {{community}} for more help.", { functionList: cssLink(t("Function List"), { - href: commonUrls.functions, + href: window.gristConfig.commonUrls.functions, target: "_blank", }), formulaCheatSheet: cssLink(t("Formula Cheat Sheet"), { - href: commonUrls.formulaSheet, + href: window.gristConfig.commonUrls.formulaSheet, target: "_blank", }), community: cssLink(t("Community"), { - href: commonUrls.community, + href: window.gristConfig.commonUrls.community, target: "_blank", }), } diff --git a/app/common/gristUrls.ts b/app/common/gristUrls.ts index 117c3a0377..02b8780d1b 100644 --- a/app/common/gristUrls.ts +++ b/app/common/gristUrls.ts @@ -1,4 +1,3 @@ -import ICommonUrlsTI from 'app/common/ICommonUrls-ti'; import {AssistantConfig} from 'app/common/Assistant'; import {BillingPage, BillingSubPage, BillingTask} from 'app/common/BillingAPI'; import {Document} from 'app/common/UserAPI'; @@ -18,9 +17,6 @@ import {UIRowId} from 'app/plugin/GristAPI'; import clone from 'lodash/clone'; import pickBy from 'lodash/pickBy'; import slugify from 'slugify'; -import * as t from "ts-interface-checker"; - -const { ICommonUrls: ICommonUrlsChecker } = t.createCheckers(ICommonUrlsTI); export const SpecialDocPage = StringUnion('code', 'acl', 'data', 'GristDocTour', 'settings', 'webhook', 'timing'); type SpecialDocPage = typeof SpecialDocPage.type; @@ -101,62 +97,6 @@ export type FormFraming = 'border' | 'minimal'; * the original. */ -export const getCommonUrls = () => withAdminDefinedUrls({ - help: getHelpCenterUrl(), - helpAccessRules: "https://support.getgrist.com/access-rules", - helpAssistant: "https://support.getgrist.com/assistant", - helpAssistantDataUse: "https://support.getgrist.com/assistant/#data-use-policy", - helpFormulaAssistantDataUse: "https://support.getgrist.com/ai-assistant/#data-use-policy", - helpColRefs: "https://support.getgrist.com/col-refs", - helpConditionalFormatting: "https://support.getgrist.com/conditional-formatting", - helpFilterButtons: "https://support.getgrist.com/search-sort-filter/#filter-buttons", - helpLinkingWidgets: "https://support.getgrist.com/linking-widgets", - helpRawData: "https://support.getgrist.com/raw-data", - helpUnderstandingReferenceColumns: "https://support.getgrist.com/col-refs/#understanding-reference-columns", - helpTriggerFormulas: "https://support.getgrist.com/formulas/#trigger-formulas", - helpTryingOutChanges: "https://support.getgrist.com/copying-docs/#trying-out-changes", - helpCustomWidgets: "https://support.getgrist.com/widget-custom", - helpInstallAuditLogs: "https://support.getgrist.com/install/audit-logs", - helpTeamAuditLogs: "https://support.getgrist.com/teams/audit-logs", - helpTelemetryLimited: "https://support.getgrist.com/telemetry-limited", - helpEnterpriseOptIn: "https://support.getgrist.com/self-managed/#how-do-i-activate-grist-enterprise", - helpCalendarWidget: "https://support.getgrist.com/widget-calendar", - helpLinkKeys: "https://support.getgrist.com/examples/2021-04-link-keys", - helpFilteringReferenceChoices: "https://support.getgrist.com/col-refs/#filtering-reference-choices-in-dropdown", - helpSandboxing: "https://support.getgrist.com/self-managed/#how-do-i-sandbox-documents", - helpAPI: 'https://support.getgrist.com/api', - helpSummaryFormulas: 'https://support.getgrist.com/summary-tables/#summary-formulas', - helpAdminControls: "https://support.getgrist.com/admin-controls", - helpFiddleMode: 'https://support.getgrist.com/glossary/#fiddle-mode', - helpSharing: 'https://support.getgrist.com/sharing', - helpComments: 'https://support.getgrist.com/sharing/#comments', - freeCoachingCall: getFreeCoachingCallUrl(), - contactSupport: getContactSupportUrl(), - termsOfService: getTermsOfServiceUrl(), - onboardingTutorialVideoId: getOnboardingVideoId(), - plans: "https://www.getgrist.com/pricing", - contact: "https://www.getgrist.com/contact", - templates: 'https://www.getgrist.com/templates', - webinars: getWebinarsUrl(), - community: 'https://community.getgrist.com', - functions: 'https://support.getgrist.com/functions', - formulaSheet: 'https://support.getgrist.com/formula-cheat-sheet', - formulas: 'https://support.getgrist.com/formulas', - forms: 'https://www.getgrist.com/forms/?utm_source=grist-forms&utm_medium=grist-forms&utm_campaign=forms-footer', - openGraphPreviewImage: 'https://grist-static.com/icons/opengraph-preview-image.png', - - gristLabsCustomWidgets: 'https://gristlabs.github.io/grist-widget/', - gristLabsWidgetRepository: 'https://github.com/gristlabs/grist-widget/releases/download/latest/manifest.json', - githubGristCore: 'https://github.com/gristlabs/grist-core', - githubSponsorGristLabs: 'https://github.com/sponsors/gristlabs', - - versionCheck: 'https://api.getgrist.com/api/version', - attachmentStorage: 'https://support.getgrist.com/document-settings/#external-attachments', -}); - -export const commonUrls = getCommonUrls(); - - /** * Values representable in a URL. The current state is available as urlState().state observable * in client. Updates to this state are expected by functions such as makeUrl() and setLinkUrl(). @@ -959,7 +899,7 @@ export interface GristLoadConfig { formFraming?: FormFraming; - adminDefinedUrls?: string; + commonUrls: ICommonUrls; // Maximum users to display for user presence features (e.g. active user list) userPresenceMaxUsers?: number; @@ -1029,35 +969,6 @@ export function getSingleOrg(): string|null { return getCustomizableValue('singleOrg', 'GRIST_SINGLE_ORG') || null; } -export function getHelpCenterUrl(): string { - const defaultUrl = 'https://support.getgrist.com'; - return getCustomizableValue('helpCenterUrl', 'GRIST_HELP_CENTER') || defaultUrl; -} - -export function getOnboardingVideoId(): string { - const defaultId = '56AieR9rpww'; - return getCustomizableValue('onboardingTutorialVideoId', 'GRIST_ONBOARDING_VIDEO_ID') || defaultId; -} - -export function getTermsOfServiceUrl(): string|undefined { - return getCustomizableValue('termsOfServiceUrl', 'GRIST_TERMS_OF_SERVICE_URL') || undefined; -} - -export function getFreeCoachingCallUrl(): string { - const defaultUrl = 'https://calendly.com/grist-team/grist-free-coaching-call'; - return getCustomizableValue('freeCoachingCallUrl', 'FREE_COACHING_CALL_URL') || defaultUrl; -} - -export function getContactSupportUrl(): string { - const defaultUrl = 'https://www.getgrist.com/contact'; - return getCustomizableValue('contactSupportUrl', 'GRIST_CONTACT_SUPPORT_URL') || defaultUrl; -} - -export function getWebinarsUrl(): string { - const defaultUrl = 'https://www.getgrist.com/webinars'; - return getCustomizableValue('webinarsUrl', 'GRIST_WEBINARS_URL') || defaultUrl; -} - /** * Returns true if org must be encoded in path, not in domain. Determined from * gristConfig on the client. On the server, returns true if the host is @@ -1294,24 +1205,3 @@ export interface UrlTweaks { url: URL, }): void; } - -function withAdminDefinedUrls(defaultUrls: ICommonUrls): ICommonUrls { - const adminDefinedUrlsStr = getCustomizableValue('adminDefinedUrls', "GRIST_CUSTOM_COMMON_URLS"); - if (!adminDefinedUrlsStr) { - return defaultUrls; - } - - let adminDefinedUrls; - try { - adminDefinedUrls = JSON.parse(adminDefinedUrlsStr); - } catch(e) { - throw new Error("The JSON passed to GRIST_CUSTOM_COMMON_URLS is malformed"); - } - - const merged = { - ...defaultUrls, - ...(adminDefinedUrls) - }; - ICommonUrlsChecker.strictCheck(merged); - return merged; -} diff --git a/app/server/lib/ActiveDoc.ts b/app/server/lib/ActiveDoc.ts index 78cf81810b..b4b1d52539 100644 --- a/app/server/lib/ActiveDoc.ts +++ b/app/server/lib/ActiveDoc.ts @@ -65,7 +65,7 @@ import { import {normalizeEmail} from 'app/common/emails'; import {Features, Product} from 'app/common/Features'; import {isHiddenCol} from 'app/common/gristTypes'; -import {commonUrls, parseUrlId} from 'app/common/gristUrls'; +import {parseUrlId} from 'app/common/gristUrls'; import {byteString, countIf, retryOnce, safeJsonParse, timeoutReached} from 'app/common/gutil'; import {InactivityTimer} from 'app/common/InactivityTimer'; import {Interval} from 'app/common/Interval'; @@ -115,6 +115,7 @@ import {appSettings} from 'app/server/lib/AppSettings'; import {AuditEventAction} from 'app/server/lib/AuditEvent'; import {RequestWithLogin} from 'app/server/lib/Authorizer'; import {Client} from 'app/server/lib/Client'; +import {commonUrls} from 'app/server/lib/commonUrls'; import {getMetaTables} from 'app/server/lib/DocApi'; import {DEFAULT_CACHE_TTL, DocManager} from 'app/server/lib/DocManager'; import {GristServer} from 'app/server/lib/GristServer'; diff --git a/app/server/lib/GristServer.ts b/app/server/lib/GristServer.ts index 2d95aa568d..55cdf892aa 100644 --- a/app/server/lib/GristServer.ts +++ b/app/server/lib/GristServer.ts @@ -13,6 +13,7 @@ import { HomeDBManager, UserChange } from 'app/gen-server/lib/homedb/HomeDBManag import { IAccessTokens } from 'app/server/lib/AccessTokens'; import { RequestWithLogin } from 'app/server/lib/Authorizer'; import { Comm } from 'app/server/lib/Comm'; +import { commonUrls } from 'app/server/lib/commonUrls'; import { create } from 'app/server/lib/create'; import { DocManager } from 'app/server/lib/DocManager'; import { Hosts } from 'app/server/lib/extractOrg'; @@ -179,7 +180,9 @@ export function createDummyGristServer(): GristServer { getOwnUrl() { return 'http://localhost:4242'; }, getPermitStore() { throw new Error('no permit store'); }, getExternalPermitStore() { throw new Error('no external permit store'); }, - getGristConfig() { return { homeUrl: '', timestampMs: 0, serveSameOrigin: true, checkForLatestVersion: false }; }, + getGristConfig() { return { + homeUrl: '', timestampMs: 0, serveSameOrigin: true, checkForLatestVersion: false, commonUrls + }; }, getOrgUrl() { return Promise.resolve(''); }, getResourceUrl() { return Promise.resolve(''); }, getSessions() { throw new Error('no sessions'); }, diff --git a/app/server/lib/commonUrls.ts b/app/server/lib/commonUrls.ts new file mode 100644 index 0000000000..e10dc53504 --- /dev/null +++ b/app/server/lib/commonUrls.ts @@ -0,0 +1,81 @@ +import { ICommonUrls } from "app/common/ICommonUrls"; + +import * as t from "ts-interface-checker"; + +import ICommonUrlsTI from 'app/common/ICommonUrls-ti'; + +const { ICommonUrls: ICommonUrlsChecker } = t.createCheckers(ICommonUrlsTI); + +function withAdminDefinedUrls(defaultUrls: ICommonUrls): ICommonUrls { + const adminDefinedUrlsStr = process.env.GRIST_CUSTOM_COMMON_URLS; + if (!adminDefinedUrlsStr) { + return defaultUrls; + } + + let adminDefinedUrls; + try { + adminDefinedUrls = JSON.parse(adminDefinedUrlsStr); + } catch(e) { + throw new Error("The JSON passed to GRIST_CUSTOM_COMMON_URLS is malformed"); + } + + const merged = { + ...defaultUrls, + ...(adminDefinedUrls) + }; + ICommonUrlsChecker.strictCheck(merged); + return merged; +} + +export const getCommonUrls = () => withAdminDefinedUrls({ + help: process.env.GRIST_HELP_CENTER || 'https://support.getgrist.com', + helpAccessRules: "https://support.getgrist.com/access-rules", + helpAssistant: "https://support.getgrist.com/assistant", + helpAssistantDataUse: "https://support.getgrist.com/assistant/#data-use-policy", + helpFormulaAssistantDataUse: "https://support.getgrist.com/ai-assistant/#data-use-policy", + helpColRefs: "https://support.getgrist.com/col-refs", + helpConditionalFormatting: "https://support.getgrist.com/conditional-formatting", + helpFilterButtons: "https://support.getgrist.com/search-sort-filter/#filter-buttons", + helpLinkingWidgets: "https://support.getgrist.com/linking-widgets", + helpRawData: "https://support.getgrist.com/raw-data", + helpUnderstandingReferenceColumns: "https://support.getgrist.com/col-refs/#understanding-reference-columns", + helpTriggerFormulas: "https://support.getgrist.com/formulas/#trigger-formulas", + helpTryingOutChanges: "https://support.getgrist.com/copying-docs/#trying-out-changes", + helpCustomWidgets: "https://support.getgrist.com/widget-custom", + helpInstallAuditLogs: "https://support.getgrist.com/install/audit-logs", + helpTeamAuditLogs: "https://support.getgrist.com/teams/audit-logs", + helpTelemetryLimited: "https://support.getgrist.com/telemetry-limited", + helpEnterpriseOptIn: "https://support.getgrist.com/self-managed/#how-do-i-activate-grist-enterprise", + helpCalendarWidget: "https://support.getgrist.com/widget-calendar", + helpLinkKeys: "https://support.getgrist.com/examples/2021-04-link-keys", + helpFilteringReferenceChoices: "https://support.getgrist.com/col-refs/#filtering-reference-choices-in-dropdown", + helpSandboxing: "https://support.getgrist.com/self-managed/#how-do-i-sandbox-documents", + helpAPI: 'https://support.getgrist.com/api', + helpSummaryFormulas: 'https://support.getgrist.com/summary-tables/#summary-formulas', + helpAdminControls: "https://support.getgrist.com/admin-controls", + helpFiddleMode: 'https://support.getgrist.com/glossary/#fiddle-mode', + helpSharing: 'https://support.getgrist.com/sharing', + helpComments: 'https://support.getgrist.com/sharing/#comments', + freeCoachingCall: process.env.FREE_COACHING_CALL_URL || 'https://calendly.com/grist-team/grist-free-coaching-call', + contactSupport: process.env.GRIST_CONTACT_SUPPORT_URL || 'https://www.getgrist.com/contact', + termsOfService: process.env.GRIST_TERMS_OF_SERVICE_URL || undefined, + onboardingTutorialVideoId: process.env.GRIST_ONBOARDING_VIDEO_ID || '56AieR9rpww', + plans: "https://www.getgrist.com/pricing", + contact: "https://www.getgrist.com/contact", + templates: 'https://www.getgrist.com/templates', + webinars: process.env.GRIST_WEBINARS_URL || 'https://www.getgrist.com/webinars', + community: 'https://community.getgrist.com', + functions: 'https://support.getgrist.com/functions', + formulaSheet: 'https://support.getgrist.com/formula-cheat-sheet', + formulas: 'https://support.getgrist.com/formulas', + forms: 'https://www.getgrist.com/forms/?utm_source=grist-forms&utm_medium=grist-forms&utm_campaign=forms-footer', + openGraphPreviewImage: 'https://grist-static.com/icons/opengraph-preview-image.png', + gristLabsCustomWidgets: 'https://gristlabs.github.io/grist-widget/', + gristLabsWidgetRepository: 'https://github.com/gristlabs/grist-widget/releases/download/latest/manifest.json', + githubGristCore: 'https://github.com/gristlabs/grist-core', + githubSponsorGristLabs: 'https://github.com/sponsors/gristlabs', + versionCheck: 'https://api.getgrist.com/api/version', + attachmentStorage: 'https://support.getgrist.com/document-settings/#external-attachments', +}); + +export const commonUrls = getCommonUrls(); diff --git a/app/server/lib/sendAppPage.ts b/app/server/lib/sendAppPage.ts index 64159dbfa8..6075acf0c5 100644 --- a/app/server/lib/sendAppPage.ts +++ b/app/server/lib/sendAppPage.ts @@ -1,15 +1,8 @@ import {AssistantConfig} from 'app/common/Assistant'; import { - commonUrls, Features, FormFraming, - getContactSupportUrl, - getFreeCoachingCallUrl, - getHelpCenterUrl, - getOnboardingVideoId, getPageTitleSuffix, - getTermsOfServiceUrl, - getWebinarsUrl, GristLoadConfig, IFeature } from 'app/common/gristUrls'; @@ -18,6 +11,7 @@ import {getTagManagerSnippet} from 'app/common/tagManager'; import {Document} from 'app/common/UserAPI'; import {AttachedCustomWidgets, IAttachedCustomWidget} from "app/common/widgetTypes"; import {appSettings} from "app/server/lib/AppSettings"; +import {commonUrls} from 'app/server/lib/commonUrls'; import {SUPPORT_EMAIL} from 'app/gen-server/lib/homedb/HomeDBManager'; import {isAnonymousUser, isSingleUserMode, RequestWithLogin} from 'app/server/lib/Authorizer'; import {RequestWithOrg} from 'app/server/lib/extractOrg'; @@ -29,6 +23,7 @@ import { } from 'app/server/lib/gristSettings'; import {getSupportedEngineChoices} from 'app/server/lib/serverUtils'; import {readLoadedLngs, readLoadedNamespaces} from 'app/server/localization'; + import * as express from 'express'; import * as fse from 'fs-extra'; import * as handlebars from 'handlebars'; @@ -93,12 +88,6 @@ export function makeGristConfig(options: MakeGristConfigOptions): GristLoadConfi // True if no subdomains or separate servers are defined for the home servers or doc workers. serveSameOrigin: !baseDomain && pathOnly, singleOrg: process.env.GRIST_SINGLE_ORG, - helpCenterUrl: getHelpCenterUrl(), - termsOfServiceUrl: getTermsOfServiceUrl(), - freeCoachingCallUrl: getFreeCoachingCallUrl(), - onboardingTutorialVideoId: getOnboardingVideoId(), - webinarsUrl: getWebinarsUrl(), - contactSupportUrl: getContactSupportUrl(), pathOnly, supportAnon: shouldSupportAnon(), enableAnonPlayground: isAffirmative(process.env.GRIST_ANON_PLAYGROUND ?? true), @@ -136,7 +125,7 @@ export function makeGristConfig(options: MakeGristConfigOptions): GristLoadConfi experimentalPlugins: isAffirmative(process.env.GRIST_EXPERIMENTAL_PLUGINS), notifierEnabled: server?.hasNotifier(), formFraming: GRIST_FEATURE_FORM_FRAMING as FormFraming, - adminDefinedUrls: process.env.GRIST_CUSTOM_COMMON_URLS, + commonUrls, userPresenceMaxUsers: getUserPresenceMaxUsers(), ...extra, }; diff --git a/app/server/lib/updateChecker.ts b/app/server/lib/updateChecker.ts index 993be13761..a489be329e 100644 --- a/app/server/lib/updateChecker.ts +++ b/app/server/lib/updateChecker.ts @@ -1,7 +1,8 @@ -import {commonUrls, LatestVersionAvailable} from "app/common/gristUrls"; +import {LatestVersionAvailable} from "app/common/gristUrls"; import {isAffirmative} from "app/common/gutil"; import {version as installedVersion} from "app/common/version"; import {naturalCompare} from 'app/common/SortFunc'; +import {commonUrls} from "app/server/lib/commonUrls"; import {GristServer} from "app/server/lib/GristServer"; import {ApiError} from "app/common/ApiError"; import {LatestVersion} from "app/server/lib/UpdateManager"; diff --git a/stubs/app/server/server.ts b/stubs/app/server/server.ts index d7f62d606d..36fec1b18b 100644 --- a/stubs/app/server/server.ts +++ b/stubs/app/server/server.ts @@ -4,7 +4,7 @@ * By default, starts up on port 8484. */ -import {commonUrls} from 'app/common/gristUrls'; +import {commonUrls} from 'app/server/lib/commonUrls'; import {isAffirmative} from 'app/common/gutil'; import {HomeDBManager} from 'app/gen-server/lib/homedb/HomeDBManager'; diff --git a/test/common/gristUrls.ts b/test/common/gristUrls.ts index 8f426ff7e2..4f865e5b1d 100644 --- a/test/common/gristUrls.ts +++ b/test/common/gristUrls.ts @@ -1,6 +1,5 @@ import { - decodeUrl, commonUrls as defaultCommonUrls, getCommonUrls, - getHostType, getSlugIfNeeded, IGristUrlState, parseFirstUrlPart + decodeUrl, getHostType, getSlugIfNeeded, IGristUrlState, parseFirstUrlPart } from 'app/common/gristUrls'; import {assert} from 'chai'; import Sinon from 'sinon'; @@ -166,94 +165,4 @@ describe('gristUrls', function() { assert.strictEqual(getSlugIfNeeded({id, urlId, name: "S&P500 is ~$4,894.16"}), 'SandP500-is-dollar489416'); }); }); - - describe('getCommonUrls', function () { - it('should return the default URLs', function () { - const commonUrls = getCommonUrls(); - assert.isObject(commonUrls); - assert.equal(commonUrls.help, "https://support.getgrist.com"); - }); - - describe("with GRIST_CUSTOM_COMMON_URLS env var set", function () { - it('should return the values set by the GRIST_CUSTOM_COMMON_URLS env var', function () { - const customHelpCenterUrl = "http://custom.helpcenter"; - sandbox.define(process.env, 'GRIST_CUSTOM_COMMON_URLS', - `{"help": "${customHelpCenterUrl}"}`); - const commonUrls = getCommonUrls(); - assert.isObject(commonUrls); - assert.equal(commonUrls.help, customHelpCenterUrl); - assert.equal(commonUrls.helpAccessRules, "https://support.getgrist.com/access-rules"); - }); - - it('should throw when keys extraneous to the ICommonUrls interface are added', function () { - const nonExistingKey = 'iDontExist'; - sandbox.define(process.env, 'GRIST_CUSTOM_COMMON_URLS', - `{"${nonExistingKey}": "foo", "help": "https://getgrist.com"}`); - assert.throws(() => getCommonUrls(), `value.${nonExistingKey} is extraneous`); - }); - - it('should throw when the passed JSON is malformed', function () { - sandbox.define(process.env, 'GRIST_CUSTOM_COMMON_URLS', '{"malformed": 42'); - assert.throws(() => getCommonUrls(), 'The JSON passed to GRIST_CUSTOM_COMMON_URLS is malformed'); - }); - - it('should throw when keys has unexpected type', function () { - const regularValueKey = 'help'; - const numberValueKey = 'helpAccessRules'; - const objectValueKey = 'helpAssistant'; - const arrayValueKey = 'helpAssistantDataUse'; - const nullValueKey = 'helpFormulaAssistantDataUse'; - - sandbox.define(process.env, 'GRIST_CUSTOM_COMMON_URLS', - JSON.stringify({ - [regularValueKey]: "https://getgrist.com", - [numberValueKey]: 42, - [objectValueKey]: {"key": "value"}, - [arrayValueKey]: ["foo"], - }) - ); - const buildExpectedErrRegEx = (...keys: string[]) => new RegExp( - keys.map(key => `value\\.${key}`).join('.*'), - 'ms' - ); - assert.throws(() => getCommonUrls(), buildExpectedErrRegEx(numberValueKey, objectValueKey, arrayValueKey)); - sandbox.restore(); - sandbox.define(process.env, 'GRIST_CUSTOM_COMMON_URLS', - JSON.stringify({ - [regularValueKey]: "https://getgrist.com", - [nullValueKey]: null, - }) - ); - assert.throws(() => getCommonUrls(), buildExpectedErrRegEx(nullValueKey)); - }); - - it("should return the default URLs when the parsed value is not an object", function () { - sandbox.define(process.env, "GRIST_CUSTOM_COMMON_URLS", "42"); - assert.deepEqual(getCommonUrls(), defaultCommonUrls); - sandbox.restore(); - sandbox.define(process.env, "GRIST_CUSTOM_COMMON_URLS", "null"); - assert.deepEqual(getCommonUrls(), defaultCommonUrls); - }); - }); - - describe("client-side when customized by the admin", function () { - it("should read the admin-defined values gristConfig", function () { - sandbox.define(globalThis, 'window', { - gristConfig: { - adminDefinedUrls: JSON.stringify({ - help: "https://getgrist.com" - }) - }, - // Fake location to make isClient() believe the code is executed client-side. - location: { - hostname: 'getgrist.com' - }, - }); - const commonUrls = getCommonUrls(); - assert.isObject(commonUrls); - assert.equal(commonUrls.help, "https://getgrist.com"); - assert.equal(commonUrls.helpAccessRules, "https://support.getgrist.com/access-rules"); - }); - }); - }); }); diff --git a/test/fixtures/projects/DocMenu.ts b/test/fixtures/projects/DocMenu.ts index 85e3583ea5..d01f71dc0b 100644 --- a/test/fixtures/projects/DocMenu.ts +++ b/test/fixtures/projects/DocMenu.ts @@ -14,7 +14,7 @@ const globalWindow = { timestampMs : 0, pluginUrl : 'http://localhost:0', plugins: [] - } + } as any }; const mockAppModel = TopAppModelImpl.create(null, globalWindow, mockUserApi); diff --git a/test/server/lib/commonUrls.ts b/test/server/lib/commonUrls.ts new file mode 100644 index 0000000000..2a88e3b55e --- /dev/null +++ b/test/server/lib/commonUrls.ts @@ -0,0 +1,89 @@ +import { + commonUrls as defaultCommonUrls, + getCommonUrls +} from 'app/server/lib/commonUrls'; + +import { assert } from 'chai'; +import Sinon from 'sinon'; + +describe('commonUrls', function () { + let sandbox: Sinon.SinonSandbox; + + beforeEach(function () { + sandbox = Sinon.createSandbox(); + }); + + afterEach(function () { + sandbox.restore(); + }); + + describe('getCommonUrls', function () { + it('should return the default URLs', function () { + const commonUrls = getCommonUrls(); + assert.isObject(commonUrls); + assert.equal(commonUrls.help, "https://support.getgrist.com"); + }); + + describe("with GRIST_CUSTOM_COMMON_URLS env var set", function () { + it('should return the values set by the GRIST_CUSTOM_COMMON_URLS env var', function () { + const customHelpCenterUrl = "http://custom.helpcenter"; + sandbox.define(process.env, 'GRIST_CUSTOM_COMMON_URLS', + `{"help": "${customHelpCenterUrl}"}`); + const commonUrls = getCommonUrls(); + assert.isObject(commonUrls); + assert.equal(commonUrls.help, customHelpCenterUrl); + assert.equal(commonUrls.helpAccessRules, "https://support.getgrist.com/access-rules"); + }); + + it('should throw when keys extraneous to the ICommonUrls interface are added', function () { + const nonExistingKey = 'iDontExist'; + sandbox.define(process.env, 'GRIST_CUSTOM_COMMON_URLS', + `{"${nonExistingKey}": "foo", "help": "https://getgrist.com"}`); + assert.throws(() => getCommonUrls(), `value.${nonExistingKey} is extraneous`); + }); + + it('should throw when the passed JSON is malformed', function () { + sandbox.define(process.env, 'GRIST_CUSTOM_COMMON_URLS', '{"malformed": 42'); + assert.throws(() => getCommonUrls(), 'The JSON passed to GRIST_CUSTOM_COMMON_URLS is malformed'); + }); + + it('should throw when keys has unexpected type', function () { + const regularValueKey = 'help'; + const numberValueKey = 'helpAccessRules'; + const objectValueKey = 'helpAssistant'; + const arrayValueKey = 'helpAssistantDataUse'; + const nullValueKey = 'helpFormulaAssistantDataUse'; + + sandbox.define(process.env, 'GRIST_CUSTOM_COMMON_URLS', + JSON.stringify({ + [regularValueKey]: "https://getgrist.com", + [numberValueKey]: 42, + [objectValueKey]: {"key": "value"}, + [arrayValueKey]: ["foo"], + }) + ); + const buildExpectedErrRegEx = (...keys: string[]) => new RegExp( + keys.map(key => `value\\.${key}`).join('.*'), + 'ms' + ); + assert.throws(() => getCommonUrls(), buildExpectedErrRegEx(numberValueKey, objectValueKey, arrayValueKey)); + sandbox.restore(); + sandbox.define(process.env, 'GRIST_CUSTOM_COMMON_URLS', + JSON.stringify({ + [regularValueKey]: "https://getgrist.com", + [nullValueKey]: null, + }) + ); + assert.throws(() => getCommonUrls(), buildExpectedErrRegEx(nullValueKey)); + }); + + it("should return the default URLs when the parsed value is not an object", function () { + sandbox.define(process.env, "GRIST_CUSTOM_COMMON_URLS", "42"); + assert.deepEqual(getCommonUrls(), defaultCommonUrls); + sandbox.restore(); + sandbox.define(process.env, "GRIST_CUSTOM_COMMON_URLS", "null"); + assert.deepEqual(getCommonUrls(), defaultCommonUrls); + }); + }); + }); +}); diff --git a/test/tsconfig.json b/test/tsconfig.json index 2695a42dfd..860f03214a 100644 --- a/test/tsconfig.json +++ b/test/tsconfig.json @@ -4,6 +4,7 @@ "*", "**/*", "../app/client/declarations.d.ts", + "../app/client/declarations/**/*.d.ts", "../app/common/declarations.d.ts", "../app/server/declarations.d.ts", "../app/server/declarations/**/*.d.ts",