diff --git a/x-pack/platform/plugins/shared/screenshotting/server/browsers/chromium/templates/index.test.ts b/x-pack/platform/plugins/shared/screenshotting/server/browsers/chromium/templates/index.test.ts new file mode 100644 index 0000000000000..90fdf4576c59e --- /dev/null +++ b/x-pack/platform/plugins/shared/screenshotting/server/browsers/chromium/templates/index.test.ts @@ -0,0 +1,124 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { getHeaderTemplate, getFooterTemplate, getDefaultFooterLogo } from '.'; + +let defaultFooterLogo: string; + +describe('templates/index', () => { + beforeAll(async () => { + defaultFooterLogo = (await getDefaultFooterLogo()).trim(); + }); + + describe('getHeaderTemplate()', () => { + it('works with a plain logo', async () => { + const title = 'plain'; + const result = await getHeaderTemplate({ title }); + expect(result).toBe(getHeader(title)); + }); + it('works with an html title', async () => { + const title = 'html'; + const result = await getHeaderTemplate({ title }); + expect(result).toBe(getHeader('<b>html</b>')); + }); + }); + + describe('getFooterTemplate()', () => { + it('works with no logo', async () => { + const result = await getFooterTemplate({}); + expect(result).toBe(getFooter()); + }); + it('works with a plain logo', async () => { + const logo = 'http://example.com/favico.ico'; + const result = await getFooterTemplate({ logo }); + expect(result).toBe(getFooter(logo)); + }); + it('works with an html logo', async () => { + const logo = '"/>${title} +`.trimStart(); +} + +function getFooter(logo?: string): string { + const hasLogo = !!logo; + + if (!hasLogo) { + logo = defFooterLogo(); + } + return ` + +
+ + +${getPoweredBy(hasLogo)}
+  of  +
+
+`.trimStart(); +} + +function getPoweredBy(hasLogo: boolean): string { + if (!hasLogo) return ''; + return `
Powered by Elastic
\n`; +} + +function defFooterLogo(): string { + return defaultFooterLogo!; +} diff --git a/x-pack/platform/plugins/shared/screenshotting/server/browsers/chromium/templates/index.ts b/x-pack/platform/plugins/shared/screenshotting/server/browsers/chromium/templates/index.ts new file mode 100644 index 0000000000000..179e21ce94265 --- /dev/null +++ b/x-pack/platform/plugins/shared/screenshotting/server/browsers/chromium/templates/index.ts @@ -0,0 +1,78 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { i18n } from '@kbn/i18n'; +import fs from 'fs/promises'; +import path from 'path'; +import Handlebars, { TemplateDelegate } from '@kbn/handlebars'; +import { assetPath } from '../../../constants'; + +// see: https://handlebarsjs.com/guide/builtin-helpers.html +const HBCompileOptions = { + knownHelpersOnly: true, + knownHelpers: { + helperMissing: false, + blockHelperMissing: false, + each: false, + if: true, + unless: false, + with: false, + log: false, + lookup: false, + }, +}; + +async function compileTemplate(pathToTemplate: string): Promise> { + const contentsBuffer = await fs.readFile(pathToTemplate); + return Handlebars.compileAST(contentsBuffer.toString(), HBCompileOptions); +} + +interface HeaderTemplateInput { + title: string; +} +interface GetHeaderArgs { + title: string; +} + +export async function getHeaderTemplate({ title }: GetHeaderArgs): Promise { + const template = await compileTemplate( + path.resolve(__dirname, './header.handlebars.html') + ); + return template({ title }); +} + +export async function getDefaultFooterLogo(): Promise { + const logoBuffer = await fs.readFile(path.resolve(assetPath, 'img', 'logo-grey.png')); + return `data:image/png;base64,${logoBuffer.toString('base64')}`; +} + +interface FooterTemplateInput { + base64FooterLogo: string; + hasCustomLogo: boolean; + poweredByElasticCopy: string; +} + +interface GetFooterArgs { + logo?: string; +} + +export async function getFooterTemplate({ logo }: GetFooterArgs): Promise { + const template = await compileTemplate( + path.resolve(__dirname, './footer.handlebars.html') + ); + const hasCustomLogo = Boolean(logo); + return template({ + base64FooterLogo: hasCustomLogo ? logo! : await getDefaultFooterLogo(), + hasCustomLogo, + poweredByElasticCopy: i18n.translate( + 'xpack.screenshotting.exportTypes.printablePdf.footer.logoDescription', + { + defaultMessage: 'Powered by Elastic', + } + ), + }); +} diff --git a/x-pack/platform/plugins/shared/screenshotting/tsconfig.json b/x-pack/platform/plugins/shared/screenshotting/tsconfig.json new file mode 100644 index 0000000000000..67363ecf6ffde --- /dev/null +++ b/x-pack/platform/plugins/shared/screenshotting/tsconfig.json @@ -0,0 +1,34 @@ +{ + "extends": "../../../../../tsconfig.base.json", + "compilerOptions": { + "outDir": "target/types", + }, + "include": [ + "common/**/*", + "public/**/*", + "server/**/*", + "../../../../../typings/**/*" + ], + "kbn_references": [ + "@kbn/core", + { "path": "../../../../../src/setup_node_env/tsconfig.json" }, + "@kbn/expressions-plugin", + "@kbn/screenshot-mode-plugin", + "@kbn/cloud-plugin", + "@kbn/utility-types", + "@kbn/logging", + "@kbn/std", + "@kbn/i18n", + "@kbn/utils", + "@kbn/core-logging-server-mocks", + "@kbn/logging-mocks", + "@kbn/core-http-server", + "@kbn/core-plugins-server", + "@kbn/task-manager-plugin", + "@kbn/screenshotting-server", + "@kbn/handlebars", + ], + "exclude": [ + "target/**/*", + ] +}