From b81bea2d058be8b43abf1f4ad4f73fafd480a7f0 Mon Sep 17 00:00:00 2001 From: fflorent Date: Fri, 4 Oct 2024 15:26:16 +0200 Subject: [PATCH] Add description and icon to preview URLs for non-document resources #1242 When pasting a URL in some app or website that allows previewing the link, for other resources than documents, you were offered an irrelevant description. This patches aims to give a generic description of what is Grist. --- app/server/lib/sendAppPage.ts | 44 ++++++++++++++--------------- static/img/icon-grist.png | Bin 0 -> 10103 bytes static/locales/en.server.json | 4 ++- test/nbrowser/HomeIntro.ts | 17 +++++++++++ test/nbrowser/NewDocument.ntest.js | 17 ++++++++++- 5 files changed, 58 insertions(+), 24 deletions(-) create mode 100644 static/img/icon-grist.png diff --git a/app/server/lib/sendAppPage.ts b/app/server/lib/sendAppPage.ts index 2aba9d31ee4..cc086341e34 100644 --- a/app/server/lib/sendAppPage.ts +++ b/app/server/lib/sendAppPage.ts @@ -159,8 +159,8 @@ export function makeSendAppPage({ server, staticDir, tag, testLogin, baseDomain const staticTag = options.tag || tag; // If boot tag is used, serve assets locally, otherwise respect // APP_STATIC_URL. - const staticOrigin = staticTag === 'boot' ? '' : (process.env.APP_STATIC_URL || ''); - const staticBaseUrl = `${staticOrigin}/v/${staticTag}/`; + const staticOrigin = staticTag === 'boot' ? '' : (process.env.APP_STATIC_URL || config.homeUrl || ''); + const staticBaseUrl = `${staticOrigin.replace(/\/*$/, '')}/v/${staticTag}/`; const customHeadHtmlSnippet = server.create.getExtraHeadHtml?.() ?? ""; const warning = testLogin ? "
Authentication is not enforced
" : ""; // Preload all languages that will be used or are requested by client. @@ -174,8 +174,8 @@ export function makeSendAppPage({ server, staticDir, tag, testLogin, baseDomain ).join('\n'); const content = fileContent .replace("", warning) - .replace("", getPageTitle(req, config)) - .replace("", getPageMetadataHtmlSnippet(config)) + .replace("", getPageTitle(config) ?? (translate(req, 'Loading') + "...")) + .replace("", getPageMetadataHtmlSnippet(req, config, staticBaseUrl)) .replace("", getPageTitleSuffix(server.getGristConfig())) .replace("", `` + tagManagerSnippet) .replace("", preloads) @@ -271,11 +271,10 @@ function configuredPageTitleSuffix() { * * Note: The string returned is escaped and safe to insert into HTML. */ -function getPageTitle(req: express.Request, config: GristLoadConfig): string { +function getPageTitle(config: GristLoadConfig): string|null { const maybeDoc = getDocFromConfig(config); - if (!maybeDoc) { return translate(req, 'Loading') + "..."; } - return handlebars.Utils.escapeExpression(maybeDoc.name); + return maybeDoc && handlebars.Utils.escapeExpression(maybeDoc.name); } /** @@ -286,25 +285,26 @@ function getPageTitle(req: express.Request, config: GristLoadConfig): string { * * Note: The string returned is escaped and safe to insert into HTML. */ -function getPageMetadataHtmlSnippet(config: GristLoadConfig): string { +function getPageMetadataHtmlSnippet(req: express.Request, config: GristLoadConfig, staticBaseUrl: string): string { const metadataElements: string[] = []; const maybeDoc = getDocFromConfig(config); - const description = maybeDoc?.options?.description; - if (description) { - const content = handlebars.Utils.escapeExpression(description); - metadataElements.push(``); - metadataElements.push(``); - metadataElements.push(``); - } + const description = maybeDoc?.options?.description ?? String(translate(req, 'gristMetaDescription')); + const escapedDescription = handlebars.Utils.escapeExpression(description); + metadataElements.push(``); + metadataElements.push(``); + metadataElements.push(``); - const icon = maybeDoc?.options?.icon; - if (icon) { - const content = handlebars.Utils.escapeExpression(icon); - metadataElements.push(``); - metadataElements.push(``); - metadataElements.push(``); - } + const icon = maybeDoc?.options?.icon ?? new URL('img/icon-grist.png', staticBaseUrl).href; + const escapedIcon = handlebars.Utils.escapeExpression(icon); + metadataElements.push(``); + metadataElements.push(``); + metadataElements.push(``); + + const title = (getPageTitle(config) ?? translate(req, 'Welcome')) + getPageTitleSuffix(config); + // NB: We don't generate the content of the tag here. + metadataElements.push(`<meta property="og:title" content="${title}">`); + metadataElements.push(`<meta name="twitter:title" content="${title}">`); return metadataElements.join('\n'); } diff --git a/static/img/icon-grist.png b/static/img/icon-grist.png new file mode 100644 index 0000000000000000000000000000000000000000..f4e9f56f93c7ad5e6a14d02b8c31bd9beb83a89f GIT binary patch literal 10103 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 literal 0 HcmV?d00001 diff --git a/static/locales/en.server.json b/static/locales/en.server.json index 3023c47ad28..7be757b677d 100644 --- a/static/locales/en.server.json +++ b/static/locales/en.server.json @@ -1,6 +1,8 @@ { "sendAppPage": { - "Loading": "Loading" + "Loading": "Loading", + "gristMetaDescription": "Grist is the evolution of spreadsheets.", + "Welcome": "Welcome" }, "oidc": { "emailNotVerifiedError": "Please verify your email with the identity provider, and log in again." diff --git a/test/nbrowser/HomeIntro.ts b/test/nbrowser/HomeIntro.ts index e08f77fa4db..14e4cabc57a 100644 --- a/test/nbrowser/HomeIntro.ts +++ b/test/nbrowser/HomeIntro.ts @@ -29,6 +29,7 @@ describe('HomeIntro', function() { }); it('should should intro screen for anon', () => testIntroScreen({anon: true, team: false})); + it('should should set meta tags for URL previews', testMetaTags); it('should not show Other Sites section', testOtherSitesSection); it('should allow create/import from intro screen', testCreateImport.bind(null, false)); it('should link to examples page from the intro', testExamplesPage); @@ -129,6 +130,22 @@ describe('HomeIntro', function() { } } + async function testMetaTags() { + const expectedTitle = 'Welcome - Grist'; + assert.equal(await driver.find('meta[name="twitter:title"]').getAttribute('content'), expectedTitle); + assert.equal(await driver.find('meta[property="og:title"]').getAttribute('content'), expectedTitle); + + const expectedDescription = 'Grist is the evolution of spreadsheets.'; + assert.equal(await driver.find('meta[name="description"]').getAttribute('content'), expectedDescription); + assert.equal(await driver.find('meta[name="twitter:description"]').getAttribute('content'), expectedDescription); + assert.equal(await driver.find('meta[property="og:description"]').getAttribute('content'), expectedDescription); + + const gristIconFileName = 'icon-grist.png'; + assert.include(await driver.find('meta[name="thumbnail"]').getAttribute('content'), gristIconFileName); + assert.include(await driver.find('meta[name="twitter:image"]').getAttribute('content'), gristIconFileName); + assert.include(await driver.find('meta[property="og:image"]').getAttribute('content'), gristIconFileName); + } + async function testCreateImport(isLoggedIn: boolean) { await checkIntroButtons(isLoggedIn); diff --git a/test/nbrowser/NewDocument.ntest.js b/test/nbrowser/NewDocument.ntest.js index c81c3f7262e..5251f2cb5d0 100644 --- a/test/nbrowser/NewDocument.ntest.js +++ b/test/nbrowser/NewDocument.ntest.js @@ -18,7 +18,22 @@ describe('NewDocument.ntest', function() { this.timeout(10000); await gu.actions.createNewDoc('Untitled'); assert.equal(await gu.actions.getDocTitle(), 'Untitled'); - assert.equal(await driver.getTitle(), 'Untitled - Grist'); + + const expectedTitle = 'Untitled - Grist'; + assert.equal(await driver.getTitle(), expectedTitle); + assert.equal(await driver.find('meta[name="twitter:title"]').getAttribute('content'), expectedTitle); + assert.equal(await driver.find('meta[property="og:title"]').getAttribute('content'), expectedTitle); + + const expectedDescription = 'Grist is the evolution of spreadsheets.'; + assert.equal(await driver.find('meta[name="description"]').getAttribute('content'), expectedDescription); + assert.equal(await driver.find('meta[name="twitter:description"]').getAttribute('content'), expectedDescription); + assert.equal(await driver.find('meta[property="og:description"]').getAttribute('content'), expectedDescription); + + const gristIconFileName = 'icon-grist.png'; + assert.include(await driver.find('meta[name="thumbnail"]').getAttribute('content'), gristIconFileName); + assert.include(await driver.find('meta[name="twitter:image"]').getAttribute('content'), gristIconFileName); + assert.include(await driver.find('meta[property="og:image"]').getAttribute('content'), gristIconFileName); + assert.equal(await $('.active_section .test-viewsection-title').wait().text(), 'TABLE1'); await gu.waitForServer(); });