fix: link signing brand logos (#2881)

This commit is contained in:
Ephraim Duncan
2026-06-09 05:51:25 +00:00
committed by GitHub
parent 3e47f1913c
commit 3c0345f755
34 changed files with 449 additions and 295 deletions
@@ -0,0 +1,144 @@
import fs from 'node:fs/promises';
import path from 'node:path';
import { formatDirectTemplatePath } from '@documenso/lib/utils/templates';
import { prisma } from '@documenso/prisma';
import { seedPendingDocumentWithFullFields } from '@documenso/prisma/seed/documents';
import { seedDirectTemplate } from '@documenso/prisma/seed/templates';
import { seedUser } from '@documenso/prisma/seed/users';
import { expect, type Page, test } from '@playwright/test';
import { DocumentDataType, FieldType } from '@prisma/client';
const BRANDING_URL = 'https://brand.example/signing?source=documenso';
const PDF_PAGE_SELECTOR = 'img[data-page-number]';
const readBrandingLogo = async () => {
const logo = await fs.readFile(path.join(__dirname, '../../assets/logo.png'));
return JSON.stringify({
type: DocumentDataType.BYTES_64,
data: logo.toString('base64'),
});
};
const enableOrganisationBranding = async ({
organisationGlobalSettingsId,
brandingUrl = BRANDING_URL,
}: {
organisationGlobalSettingsId: string;
brandingUrl?: string;
}) => {
await prisma.organisationGlobalSettings.update({
where: { id: organisationGlobalSettingsId },
data: {
brandingEnabled: true,
brandingLogo: await readBrandingLogo(),
brandingUrl,
},
});
};
/**
* On signing surfaces the custom branding logo must render as a plain image.
* It must not be wrapped in any link, and the Brand Website must never appear
* as a link on these pages.
*/
const expectPlainBrandingLogo = async (page: Page, logoName: string) => {
const logo = page.getByRole('img', { name: logoName });
await expect(logo).toBeVisible();
// The custom logo must not be wrapped in a link.
await expect(page.getByRole('link', { name: logoName })).toHaveCount(0);
// The Brand Website must never be rendered as a link on signing pages.
await expect(page.locator(`a[href="${BRANDING_URL}"]`)).toHaveCount(0);
};
test('[SIGNING_BRANDING]: V1 normal signing renders custom logo as a plain image', async ({ page }) => {
const { user, team, organisation } = await seedUser();
await enableOrganisationBranding({
organisationGlobalSettingsId: organisation.organisationGlobalSettingsId,
});
const { recipients } = await seedPendingDocumentWithFullFields({
owner: user,
teamId: team.id,
recipients: ['v1-branding-signer@test.documenso.com'],
fields: [FieldType.SIGNATURE],
});
await page.goto(`/sign/${recipients[0].token}`);
await expectPlainBrandingLogo(page, `${team.name}'s Logo`);
});
test('[SIGNING_BRANDING]: V2 signing renders custom logo as a plain image', async ({ page }) => {
const { user, team, organisation } = await seedUser();
await enableOrganisationBranding({
organisationGlobalSettingsId: organisation.organisationGlobalSettingsId,
});
const { recipients } = await seedPendingDocumentWithFullFields({
owner: user,
teamId: team.id,
recipients: ['v2-branding-signer@test.documenso.com'],
fields: [FieldType.SIGNATURE],
updateDocumentOptions: { internalVersion: 2 },
});
const directTemplate = await seedDirectTemplate({
title: 'V2 Branding Direct Template',
userId: user.id,
teamId: team.id,
internalVersion: 2,
});
await page.goto(`/sign/${recipients[0].token}`);
await expectPlainBrandingLogo(page, `${team.name}'s Logo`);
await page.goto(formatDirectTemplatePath(directTemplate.directLink?.token || ''));
await expectPlainBrandingLogo(page, `${team.name}'s Logo`);
});
test('[SIGNING_BRANDING]: V2 signing keeps internal link for the Documenso fallback logo', async ({ page }) => {
const { user, team } = await seedUser();
const { recipients } = await seedPendingDocumentWithFullFields({
owner: user,
teamId: team.id,
recipients: ['v2-fallback-signer@test.documenso.com'],
fields: [FieldType.SIGNATURE],
updateDocumentOptions: { internalVersion: 2 },
});
await page.goto(`/sign/${recipients[0].token}`);
const fallbackLogoLink = page.locator('a[href="/"]').first();
await expect(fallbackLogoLink).toBeVisible();
});
test('[SIGNING_BRANDING]: embedded signing does not render custom logo Brand Website links', async ({ page }) => {
const { user, team, organisation } = await seedUser();
await enableOrganisationBranding({
organisationGlobalSettingsId: organisation.organisationGlobalSettingsId,
});
const { recipients } = await seedPendingDocumentWithFullFields({
owner: user,
teamId: team.id,
recipients: ['embed-branding-signer@test.documenso.com'],
fields: [FieldType.SIGNATURE],
updateDocumentOptions: { internalVersion: 2 },
});
await page.goto(`/embed/sign/${recipients[0].token}`);
await expect(page.locator(PDF_PAGE_SELECTOR).first()).toBeVisible({ timeout: 30_000 });
await expect(page.locator(`a[href="${BRANDING_URL}"]`)).toHaveCount(0);
await expect(page.getByRole('link', { name: `${team.name}'s Logo` })).toHaveCount(0);
});
@@ -0,0 +1,43 @@
import { Img, Link } from '../components';
import { useBranding } from '../providers/branding';
import { getSafeBrandingUrl } from '../utils/branding-url';
export type TemplateBrandingLogoProps = {
assetBaseUrl: string;
className?: string;
};
/**
* Renders the email logo.
*
* - When custom branding is enabled with a logo, the branding logo is shown.
* If a safe (http/https) Brand Website is configured, the logo links to it.
* - Otherwise the Documenso logo is shown.
*/
export const TemplateBrandingLogo = ({ assetBaseUrl, className = 'mb-4 h-6' }: TemplateBrandingLogoProps) => {
const branding = useBranding();
const hasCustomBrandingLogo = branding.brandingEnabled && Boolean(branding.brandingLogo);
if (!hasCustomBrandingLogo) {
const documensoLogoUrl = new URL('/static/logo.png', assetBaseUrl).toString();
return <Img src={documensoLogoUrl} alt="Documenso Logo" className={className} />;
}
const brandingLogo = <Img src={branding.brandingLogo} alt="Branding Logo" className={className} />;
const safeBrandingUrl = getSafeBrandingUrl(branding.brandingUrl);
if (!safeBrandingUrl) {
return brandingLogo;
}
return (
<Link href={safeBrandingUrl} target="_blank">
{brandingLogo}
</Link>
);
};
export default TemplateBrandingLogo;
@@ -2,6 +2,7 @@ import { Trans } from '@lingui/react/macro';
import { Link, Section, Text } from '../components';
import { useBranding } from '../providers/branding';
import { getSafeBrandingUrl } from '../utils/branding-url';
export type TemplateFooterProps = {
isDocument?: boolean;
@@ -11,6 +12,8 @@ export type TemplateFooterProps = {
export const TemplateFooter = ({ isDocument = true, reportUrl }: TemplateFooterProps) => {
const branding = useBranding();
const safeBrandingUrl = branding.brandingEnabled ? getSafeBrandingUrl(branding.brandingUrl) : null;
return (
<Section>
{reportUrl && (
@@ -50,6 +53,14 @@ export const TemplateFooter = ({ isDocument = true, reportUrl }: TemplateFooterP
</Text>
)}
{branding.brandingEnabled && safeBrandingUrl && (
<Text className="my-8 text-slate-400 text-sm">
<Link href={safeBrandingUrl} target="_blank">
{safeBrandingUrl}
</Link>
</Text>
)}
{!branding.brandingEnabled && (
<Text className="my-8 text-slate-400 text-sm">
Documenso, Inc.
+3 -13
View File
@@ -1,9 +1,9 @@
import { msg } from '@lingui/core/macro';
import { useLingui } from '@lingui/react';
import { Body, Container, Head, Html, Img, Preview, Section } from '../components';
import { useBranding } from '../providers/branding';
import { Body, Container, Head, Html, Preview, Section } from '../components';
import { TemplateAccessAuth2FA } from '../template-components/template-access-auth-2fa';
import { TemplateBrandingLogo } from '../template-components/template-branding-logo';
import { TemplateFooter } from '../template-components/template-footer';
export type AccessAuth2FAEmailTemplateProps = {
@@ -25,14 +25,8 @@ export const AccessAuth2FAEmailTemplate = ({
}: AccessAuth2FAEmailTemplateProps) => {
const { _ } = useLingui();
const branding = useBranding();
const previewText = msg`Your verification code is ${code}`;
const getAssetUrl = (path: string) => {
return new URL(path, assetBaseUrl).toString();
};
return (
<Html>
<Head />
@@ -42,11 +36,7 @@ export const AccessAuth2FAEmailTemplate = ({
<Section>
<Container className="mx-auto mt-8 mb-2 max-w-xl rounded-lg border border-slate-200 border-solid p-4 backdrop-blur-sm">
<Section>
{branding.brandingEnabled && branding.brandingLogo ? (
<Img src={branding.brandingLogo} alt="Branding Logo" className="mb-4 h-6" />
) : (
<Img src={getAssetUrl('/static/logo.png')} alt="Documenso Logo" className="mb-4 h-6" />
)}
<TemplateBrandingLogo assetBaseUrl={assetBaseUrl} className="mb-4 h-6" />
<TemplateAccessAuth2FA
documentTitle={documentTitle}
+3 -12
View File
@@ -1,8 +1,8 @@
import { msg } from '@lingui/core/macro';
import { useLingui } from '@lingui/react';
import { Body, Container, Head, Html, Img, Preview, Section } from '../components';
import { useBranding } from '../providers/branding';
import { Body, Container, Head, Html, Preview, Section } from '../components';
import { TemplateBrandingLogo } from '../template-components/template-branding-logo';
import type { TemplateConfirmationEmailProps } from '../template-components/template-confirmation-email';
import { TemplateConfirmationEmail } from '../template-components/template-confirmation-email';
import { TemplateFooter } from '../template-components/template-footer';
@@ -12,14 +12,9 @@ export const ConfirmEmailTemplate = ({
assetBaseUrl = 'http://localhost:3002',
}: TemplateConfirmationEmailProps) => {
const { _ } = useLingui();
const branding = useBranding();
const previewText = msg`Please confirm your email address`;
const getAssetUrl = (path: string) => {
return new URL(path, assetBaseUrl).toString();
};
return (
<Html>
<Head />
@@ -28,11 +23,7 @@ export const ConfirmEmailTemplate = ({
<Section>
<Container className="mx-auto mt-8 mb-2 max-w-xl rounded-lg border border-slate-200 border-solid p-4 backdrop-blur-sm">
<Section>
{branding.brandingEnabled && branding.brandingLogo ? (
<Img src={branding.brandingLogo} alt="Branding Logo" className="mb-4 h-6" />
) : (
<Img src={getAssetUrl('/static/logo.png')} alt="Documenso Logo" className="mb-4 h-6" />
)}
<TemplateBrandingLogo assetBaseUrl={assetBaseUrl} className="mb-4 h-6" />
<TemplateConfirmationEmail confirmationLink={confirmationLink} assetBaseUrl={assetBaseUrl} />
</Section>
@@ -3,8 +3,8 @@ import { msg } from '@lingui/core/macro';
import { useLingui } from '@lingui/react';
import { Trans } from '@lingui/react/macro';
import { Body, Button, Container, Head, Hr, Html, Img, Link, Preview, Section, Text } from '../components';
import { useBranding } from '../providers/branding';
import { Body, Button, Container, Head, Hr, Html, Link, Preview, Section, Text } from '../components';
import { TemplateBrandingLogo } from '../template-components/template-branding-logo';
import { TemplateFooter } from '../template-components/template-footer';
import TemplateImage from '../template-components/template-image';
@@ -24,7 +24,6 @@ export const ConfirmTeamEmailTemplate = ({
token = '',
}: ConfirmTeamEmailProps) => {
const { _ } = useLingui();
const branding = useBranding();
const previewText = msg`Accept team email request for ${teamName} on Documenso`;
@@ -36,11 +35,7 @@ export const ConfirmTeamEmailTemplate = ({
<Body className="mx-auto my-auto font-sans">
<Section className="bg-white">
<Container className="mx-auto mt-8 mb-2 max-w-xl rounded-lg border border-slate-200 border-solid px-2 pt-2 backdrop-blur-sm">
{branding.brandingEnabled && branding.brandingLogo ? (
<Img src={branding.brandingLogo} alt="Branding Logo" className="mb-4 h-6 p-2" />
) : (
<TemplateImage assetBaseUrl={assetBaseUrl} className="mb-4 h-6 p-2" staticAsset="logo.png" />
)}
<TemplateBrandingLogo assetBaseUrl={assetBaseUrl} className="mb-4 h-6 p-2" />
<Section>
<TemplateImage className="mx-auto" assetBaseUrl={assetBaseUrl} staticAsset="mail-open.png" />
+3 -12
View File
@@ -1,8 +1,8 @@
import { msg } from '@lingui/core/macro';
import { useLingui } from '@lingui/react';
import { Body, Container, Head, Hr, Html, Img, Preview, Section } from '../components';
import { useBranding } from '../providers/branding';
import { Body, Container, Head, Hr, Html, Preview, Section } from '../components';
import { TemplateBrandingLogo } from '../template-components/template-branding-logo';
import type { TemplateDocumentCancelProps } from '../template-components/template-document-cancel';
import { TemplateDocumentCancel } from '../template-components/template-document-cancel';
import { TemplateFooter } from '../template-components/template-footer';
@@ -17,14 +17,9 @@ export const DocumentCancelTemplate = ({
cancellationReason,
}: DocumentCancelEmailTemplateProps) => {
const { _ } = useLingui();
const branding = useBranding();
const previewText = msg`${inviterName} has cancelled the document ${documentName}, you don't need to sign it anymore.`;
const getAssetUrl = (path: string) => {
return new URL(path, assetBaseUrl).toString();
};
return (
<Html>
<Head />
@@ -34,11 +29,7 @@ export const DocumentCancelTemplate = ({
<Section>
<Container className="mx-auto mt-8 mb-2 max-w-xl rounded-lg border border-slate-200 border-solid p-4 backdrop-blur-sm">
<Section>
{branding.brandingEnabled && branding.brandingLogo ? (
<Img src={branding.brandingLogo} alt="Branding Logo" className="mb-4 h-6" />
) : (
<Img src={getAssetUrl('/static/logo.png')} alt="Documenso Logo" className="mb-4 h-6" />
)}
<TemplateBrandingLogo assetBaseUrl={assetBaseUrl} className="mb-4 h-6" />
<TemplateDocumentCancel
inviterName={inviterName}
@@ -1,8 +1,8 @@
import { msg } from '@lingui/core/macro';
import { useLingui } from '@lingui/react';
import { Body, Container, Head, Html, Img, Preview, Section } from '../components';
import { useBranding } from '../providers/branding';
import { Body, Container, Head, Html, Preview, Section } from '../components';
import { TemplateBrandingLogo } from '../template-components/template-branding-logo';
import type { TemplateDocumentCompletedProps } from '../template-components/template-document-completed';
import { TemplateDocumentCompleted } from '../template-components/template-document-completed';
import { TemplateFooter } from '../template-components/template-footer';
@@ -20,14 +20,9 @@ export const DocumentCompletedEmailTemplate = ({
reportUrl,
}: DocumentCompletedEmailTemplateProps) => {
const { _ } = useLingui();
const branding = useBranding();
const previewText = msg`Completed Document`;
const getAssetUrl = (path: string) => {
return new URL(path, assetBaseUrl).toString();
};
return (
<Html>
<Head />
@@ -37,11 +32,7 @@ export const DocumentCompletedEmailTemplate = ({
<Section className="bg-white">
<Container className="mx-auto mt-8 mb-2 max-w-xl rounded-lg border border-slate-200 border-solid p-2 backdrop-blur-sm">
<Section className="p-2">
{branding.brandingEnabled && branding.brandingLogo ? (
<Img src={branding.brandingLogo} alt="Branding Logo" className="mb-4 h-6" />
) : (
<Img src={getAssetUrl('/static/logo.png')} alt="Documenso Logo" className="mb-4 h-6" />
)}
<TemplateBrandingLogo assetBaseUrl={assetBaseUrl} className="mb-4 h-6" />
<TemplateDocumentCompleted
downloadLink={downloadLink}
@@ -4,8 +4,8 @@ import { useLingui } from '@lingui/react';
import { Trans } from '@lingui/react/macro';
import { RecipientRole } from '@prisma/client';
import { Body, Button, Container, Head, Html, Img, Preview, Section, Text } from '../components';
import { useBranding } from '../providers/branding';
import { Body, Button, Container, Head, Html, Preview, Section, Text } from '../components';
import { TemplateBrandingLogo } from '../template-components/template-branding-logo';
import TemplateDocumentImage from '../template-components/template-document-image';
import { TemplateFooter } from '../template-components/template-footer';
@@ -25,16 +25,11 @@ export const DocumentCreatedFromDirectTemplateEmailTemplate = ({
assetBaseUrl = 'http://localhost:3002',
}: DocumentCompletedEmailTemplateProps) => {
const { _ } = useLingui();
const branding = useBranding();
const action = _(RECIPIENT_ROLES_DESCRIPTION[recipientRole].actioned).toLowerCase();
const previewText = msg`Document created from direct template`;
const getAssetUrl = (path: string) => {
return new URL(path, assetBaseUrl).toString();
};
return (
<Html>
<Head />
@@ -44,11 +39,7 @@ export const DocumentCreatedFromDirectTemplateEmailTemplate = ({
<Section className="bg-white">
<Container className="mx-auto mt-8 mb-2 max-w-xl rounded-lg border border-slate-200 border-solid p-2 backdrop-blur-sm">
<Section className="p-2">
{branding.brandingEnabled && branding.brandingLogo ? (
<Img src={branding.brandingLogo} alt="Branding Logo" className="mb-4 h-6" />
) : (
<Img src={getAssetUrl('/static/logo.png')} alt="Documenso Logo" className="mb-4 h-6" />
)}
<TemplateBrandingLogo assetBaseUrl={assetBaseUrl} className="mb-4 h-6" />
<TemplateDocumentImage className="mt-6" assetBaseUrl={assetBaseUrl} />
+3 -12
View File
@@ -5,8 +5,8 @@ import { Trans } from '@lingui/react/macro';
import type { RecipientRole } from '@prisma/client';
import { OrganisationType } from '@prisma/client';
import { Body, Container, Head, Hr, Html, Img, Link, Preview, Section, Text } from '../components';
import { useBranding } from '../providers/branding';
import { Body, Container, Head, Hr, Html, Link, Preview, Section, Text } from '../components';
import { TemplateBrandingLogo } from '../template-components/template-branding-logo';
import { TemplateCustomMessageBody } from '../template-components/template-custom-message-body';
import type { TemplateDocumentInviteProps } from '../template-components/template-document-invite';
import { TemplateDocumentInvite } from '../template-components/template-document-invite';
@@ -38,7 +38,6 @@ export const DocumentInviteEmailTemplate = ({
reportUrl,
}: DocumentInviteEmailTemplateProps) => {
const { _ } = useLingui();
const branding = useBranding();
const action = _(RECIPIENT_ROLES_DESCRIPTION[role].actionVerb).toLowerCase();
@@ -54,10 +53,6 @@ export const DocumentInviteEmailTemplate = ({
previewText = msg`Please ${action} your document ${documentName}`;
}
const getAssetUrl = (path: string) => {
return new URL(path, assetBaseUrl).toString();
};
return (
<Html>
<Head />
@@ -67,11 +62,7 @@ export const DocumentInviteEmailTemplate = ({
<Section>
<Container className="mx-auto mt-8 mb-2 max-w-xl rounded-lg border border-slate-200 border-solid p-4 backdrop-blur-sm">
<Section>
{branding.brandingEnabled && branding.brandingLogo ? (
<Img src={branding.brandingLogo} alt="Branding Logo" className="mb-4 h-6" />
) : (
<Img src={getAssetUrl('/static/logo.png')} alt="Documenso Logo" className="mb-4 h-6" />
)}
<TemplateBrandingLogo assetBaseUrl={assetBaseUrl} className="mb-4 h-6" />
<TemplateDocumentInvite
inviterName={inviterName}
+3 -12
View File
@@ -1,8 +1,8 @@
import { msg } from '@lingui/core/macro';
import { useLingui } from '@lingui/react';
import { Body, Container, Head, Html, Img, Preview, Section } from '../components';
import { useBranding } from '../providers/branding';
import { Body, Container, Head, Html, Preview, Section } from '../components';
import { TemplateBrandingLogo } from '../template-components/template-branding-logo';
import type { TemplateDocumentPendingProps } from '../template-components/template-document-pending';
import { TemplateDocumentPending } from '../template-components/template-document-pending';
import { TemplateFooter } from '../template-components/template-footer';
@@ -14,14 +14,9 @@ export const DocumentPendingEmailTemplate = ({
assetBaseUrl = 'http://localhost:3002',
}: DocumentPendingEmailTemplateProps) => {
const { _ } = useLingui();
const branding = useBranding();
const previewText = msg`Pending Document`;
const getAssetUrl = (path: string) => {
return new URL(path, assetBaseUrl).toString();
};
return (
<Html>
<Head />
@@ -31,11 +26,7 @@ export const DocumentPendingEmailTemplate = ({
<Section className="bg-white">
<Container className="mx-auto mt-8 mb-2 max-w-xl rounded-lg border border-slate-200 border-solid p-4 backdrop-blur-sm">
<Section>
{branding.brandingEnabled && branding.brandingLogo ? (
<Img src={branding.brandingLogo} alt="Branding Logo" className="mb-4 h-6" />
) : (
<Img src={getAssetUrl('/static/logo.png')} alt="Documenso Logo" className="mb-4 h-6" />
)}
<TemplateBrandingLogo assetBaseUrl={assetBaseUrl} className="mb-4 h-6" />
<TemplateDocumentPending documentName={documentName} assetBaseUrl={assetBaseUrl} />
</Section>
@@ -1,8 +1,8 @@
import { msg } from '@lingui/core/macro';
import { useLingui } from '@lingui/react';
import { Body, Container, Head, Html, Img, Preview, Section } from '../components';
import { useBranding } from '../providers/branding';
import { Body, Container, Head, Html, Preview, Section } from '../components';
import { TemplateBrandingLogo } from '../template-components/template-branding-logo';
import { TemplateDocumentRecipientSigned } from '../template-components/template-document-recipient-signed';
import { TemplateFooter } from '../template-components/template-footer';
@@ -20,16 +20,11 @@ export const DocumentRecipientSignedEmailTemplate = ({
assetBaseUrl = 'http://localhost:3002',
}: DocumentRecipientSignedEmailTemplateProps) => {
const { _ } = useLingui();
const branding = useBranding();
const recipientReference = recipientName || recipientEmail;
const previewText = msg`${recipientReference} has signed ${documentName}`;
const getAssetUrl = (path: string) => {
return new URL(path, assetBaseUrl).toString();
};
return (
<Html>
<Head />
@@ -39,11 +34,7 @@ export const DocumentRecipientSignedEmailTemplate = ({
<Section className="bg-white">
<Container className="mx-auto mt-8 mb-2 max-w-xl rounded-lg border border-slate-200 border-solid p-2 backdrop-blur-sm">
<Section className="p-2">
{branding.brandingEnabled && branding.brandingLogo ? (
<Img src={branding.brandingLogo} alt="Branding Logo" className="mb-4 h-6" />
) : (
<Img src={getAssetUrl('/static/logo.png')} alt="Documenso Logo" className="mb-4 h-6" />
)}
<TemplateBrandingLogo assetBaseUrl={assetBaseUrl} className="mb-4 h-6" />
<TemplateDocumentRecipientSigned
documentName={documentName}
+3 -12
View File
@@ -1,8 +1,8 @@
import { msg } from '@lingui/core/macro';
import { useLingui } from '@lingui/react';
import { Body, Container, Head, Html, Img, Preview, Section } from '../components';
import { useBranding } from '../providers/branding';
import { Body, Container, Head, Html, Preview, Section } from '../components';
import { TemplateBrandingLogo } from '../template-components/template-branding-logo';
import { TemplateDocumentRejected } from '../template-components/template-document-rejected';
import { TemplateFooter } from '../template-components/template-footer';
@@ -22,14 +22,9 @@ export function DocumentRejectedEmail({
assetBaseUrl = 'http://localhost:3002',
}: DocumentRejectedEmailProps) {
const { _ } = useLingui();
const branding = useBranding();
const previewText = _(msg`${recipientName} has rejected the document '${documentName}'`);
const getAssetUrl = (path: string) => {
return new URL(path, assetBaseUrl).toString();
};
return (
<Html>
<Head />
@@ -39,11 +34,7 @@ export function DocumentRejectedEmail({
<Section>
<Container className="mx-auto mt-8 mb-2 max-w-xl rounded-lg border border-slate-200 border-solid p-4 backdrop-blur-sm">
<Section>
{branding.brandingEnabled && branding.brandingLogo ? (
<Img src={branding.brandingLogo} alt="Branding Logo" className="mb-4 h-6" />
) : (
<Img src={getAssetUrl('/static/logo.png')} alt="Documenso Logo" className="mb-4 h-6" />
)}
<TemplateBrandingLogo assetBaseUrl={assetBaseUrl} className="mb-4 h-6" />
<TemplateDocumentRejected
recipientName={recipientName}
@@ -1,8 +1,8 @@
import { msg } from '@lingui/core/macro';
import { useLingui } from '@lingui/react';
import { Body, Container, Head, Html, Img, Preview, Section } from '../components';
import { useBranding } from '../providers/branding';
import { Body, Container, Head, Html, Preview, Section } from '../components';
import { TemplateBrandingLogo } from '../template-components/template-branding-logo';
import { TemplateDocumentRejectionConfirmed } from '../template-components/template-document-rejection-confirmed';
import { TemplateFooter } from '../template-components/template-footer';
@@ -22,14 +22,9 @@ export function DocumentRejectionConfirmedEmail({
assetBaseUrl = 'http://localhost:3002',
}: DocumentRejectionConfirmedEmailProps) {
const { _ } = useLingui();
const branding = useBranding();
const previewText = _(msg`You have rejected the document '${documentName}'`);
const getAssetUrl = (path: string) => {
return new URL(path, assetBaseUrl).toString();
};
return (
<Html>
<Head />
@@ -39,11 +34,7 @@ export function DocumentRejectionConfirmedEmail({
<Section>
<Container className="mx-auto mt-8 mb-2 max-w-xl rounded-lg border border-slate-200 border-solid p-4 backdrop-blur-sm">
<Section>
{branding.brandingEnabled && branding.brandingLogo ? (
<Img src={branding.brandingLogo} alt="Branding Logo" className="mb-4 h-6" />
) : (
<Img src={getAssetUrl('/static/logo.png')} alt="Documenso Logo" className="mb-4 h-6" />
)}
<TemplateBrandingLogo assetBaseUrl={assetBaseUrl} className="mb-4 h-6" />
<TemplateDocumentRejectionConfirmed
recipientName={recipientName}
+3 -12
View File
@@ -3,8 +3,8 @@ import { msg } from '@lingui/core/macro';
import { useLingui } from '@lingui/react';
import { RecipientRole } from '@prisma/client';
import { Body, Container, Head, Hr, Html, Img, Preview, Section, Text } from '../components';
import { useBranding } from '../providers/branding';
import { Body, Container, Head, Hr, Html, Preview, Section, Text } from '../components';
import { TemplateBrandingLogo } from '../template-components/template-branding-logo';
import { TemplateCustomMessageBody } from '../template-components/template-custom-message-body';
import { TemplateDocumentReminder } from '../template-components/template-document-reminder';
import { TemplateFooter } from '../template-components/template-footer';
@@ -29,16 +29,11 @@ export const DocumentReminderEmailTemplate = ({
reportUrl,
}: DocumentReminderEmailTemplateProps) => {
const { _ } = useLingui();
const branding = useBranding();
const action = _(RECIPIENT_ROLES_DESCRIPTION[role].actionVerb).toLowerCase();
const previewText = msg`Reminder to ${action} ${documentName}`;
const getAssetUrl = (path: string) => {
return new URL(path, assetBaseUrl).toString();
};
return (
<Html>
<Head />
@@ -48,11 +43,7 @@ export const DocumentReminderEmailTemplate = ({
<Section>
<Container className="mx-auto mt-8 mb-2 max-w-xl rounded-lg border border-slate-200 border-solid p-4 backdrop-blur-sm">
<Section>
{branding.brandingEnabled && branding.brandingLogo ? (
<Img src={branding.brandingLogo} alt="Branding Logo" className="mb-4 h-6" />
) : (
<Img src={getAssetUrl('/static/logo.png')} alt="Documenso Logo" className="mb-4 h-6" />
)}
<TemplateBrandingLogo assetBaseUrl={assetBaseUrl} className="mb-4 h-6" />
<TemplateDocumentReminder
recipientName={recipientName}
@@ -1,8 +1,8 @@
import { msg } from '@lingui/core/macro';
import { useLingui } from '@lingui/react';
import { Body, Container, Head, Html, Img, Preview, Section } from '../components';
import { useBranding } from '../providers/branding';
import { Body, Container, Head, Html, Preview, Section } from '../components';
import { TemplateBrandingLogo } from '../template-components/template-branding-logo';
import type { TemplateDocumentSelfSignedProps } from '../template-components/template-document-self-signed';
import { TemplateDocumentSelfSigned } from '../template-components/template-document-self-signed';
import { TemplateFooter } from '../template-components/template-footer';
@@ -14,14 +14,9 @@ export const DocumentSelfSignedEmailTemplate = ({
assetBaseUrl = 'http://localhost:3002',
}: DocumentSelfSignedTemplateProps) => {
const { _ } = useLingui();
const branding = useBranding();
const previewText = msg`Completed Document`;
const getAssetUrl = (path: string) => {
return new URL(path, assetBaseUrl).toString();
};
return (
<Html>
<Head />
@@ -31,11 +26,7 @@ export const DocumentSelfSignedEmailTemplate = ({
<Section className="bg-white">
<Container className="mx-auto mt-8 mb-2 max-w-xl rounded-lg border border-slate-200 border-solid p-2 backdrop-blur-sm">
<Section className="p-2">
{branding.brandingEnabled && branding.brandingLogo ? (
<Img src={branding.brandingLogo} alt="Branding Logo" className="mb-4 h-6" />
) : (
<Img src={getAssetUrl('/static/logo.png')} alt="Documenso Logo" className="mb-4 h-6" />
)}
<TemplateBrandingLogo assetBaseUrl={assetBaseUrl} className="mb-4 h-6" />
<TemplateDocumentSelfSigned documentName={documentName} assetBaseUrl={assetBaseUrl} />
</Section>
@@ -1,8 +1,8 @@
import { msg } from '@lingui/core/macro';
import { useLingui } from '@lingui/react';
import { Body, Container, Head, Hr, Html, Img, Preview, Section } from '../components';
import { useBranding } from '../providers/branding';
import { Body, Container, Head, Hr, Html, Preview, Section } from '../components';
import { TemplateBrandingLogo } from '../template-components/template-branding-logo';
import {
TemplateDocumentDelete,
type TemplateDocumentDeleteProps,
@@ -17,14 +17,9 @@ export const DocumentSuperDeleteEmailTemplate = ({
reason = 'Unknown',
}: DocumentDeleteEmailTemplateProps) => {
const { _ } = useLingui();
const branding = useBranding();
const previewText = msg`An admin has deleted your document "${documentName}".`;
const getAssetUrl = (path: string) => {
return new URL(path, assetBaseUrl).toString();
};
return (
<Html>
<Head />
@@ -34,11 +29,7 @@ export const DocumentSuperDeleteEmailTemplate = ({
<Section>
<Container className="mx-auto mt-8 mb-2 max-w-xl rounded-lg border border-slate-200 border-solid p-4 backdrop-blur-sm">
<Section>
{branding.brandingEnabled && branding.brandingLogo ? (
<Img src={branding.brandingLogo} alt="Branding Logo" className="mb-4 h-6" />
) : (
<Img src={getAssetUrl('/static/logo.png')} alt="Documenso Logo" className="mb-4 h-6" />
)}
<TemplateBrandingLogo assetBaseUrl={assetBaseUrl} className="mb-4 h-6" />
<TemplateDocumentDelete reason={reason} documentName={documentName} assetBaseUrl={assetBaseUrl} />
</Section>
+3 -12
View File
@@ -1,8 +1,8 @@
import { msg } from '@lingui/core/macro';
import { useLingui } from '@lingui/react';
import { Body, Container, Head, Html, Img, Preview, Section } from '../components';
import { useBranding } from '../providers/branding';
import { Body, Container, Head, Html, Preview, Section } from '../components';
import { TemplateBrandingLogo } from '../template-components/template-branding-logo';
import { TemplateFooter } from '../template-components/template-footer';
import type { TemplateForgotPasswordProps } from '../template-components/template-forgot-password';
import { TemplateForgotPassword } from '../template-components/template-forgot-password';
@@ -14,14 +14,9 @@ export const ForgotPasswordTemplate = ({
assetBaseUrl = 'http://localhost:3002',
}: ForgotPasswordTemplateProps) => {
const { _ } = useLingui();
const branding = useBranding();
const previewText = msg`Password Reset Requested`;
const getAssetUrl = (path: string) => {
return new URL(path, assetBaseUrl).toString();
};
return (
<Html>
<Head />
@@ -31,11 +26,7 @@ export const ForgotPasswordTemplate = ({
<Section>
<Container className="mx-auto mt-8 mb-2 max-w-xl rounded-lg border border-slate-200 border-solid p-4 backdrop-blur-sm">
<Section>
{branding.brandingEnabled && branding.brandingLogo ? (
<Img src={branding.brandingLogo} alt="Branding Logo" className="mb-4 h-6" />
) : (
<Img src={getAssetUrl('/static/logo.png')} alt="Documenso Logo" className="mb-4 h-6" />
)}
<TemplateBrandingLogo assetBaseUrl={assetBaseUrl} className="mb-4 h-6" />
<TemplateForgotPassword resetPasswordLink={resetPasswordLink} assetBaseUrl={assetBaseUrl} />
</Section>
@@ -2,8 +2,8 @@ import { msg } from '@lingui/core/macro';
import { useLingui } from '@lingui/react';
import { Trans } from '@lingui/react/macro';
import { Body, Button, Container, Head, Hr, Html, Img, Preview, Section, Text } from '../components';
import { useBranding } from '../providers/branding';
import { Body, Button, Container, Head, Hr, Html, Preview, Section, Text } from '../components';
import { TemplateBrandingLogo } from '../template-components/template-branding-logo';
import { TemplateFooter } from '../template-components/template-footer';
import TemplateImage from '../template-components/template-image';
@@ -21,7 +21,6 @@ export const OrganisationAccountLinkConfirmationTemplate = ({
assetBaseUrl = 'http://localhost:3002',
}: OrganisationAccountLinkConfirmationTemplateProps) => {
const { _ } = useLingui();
const branding = useBranding();
const previewText =
type === 'create'
@@ -35,11 +34,7 @@ export const OrganisationAccountLinkConfirmationTemplate = ({
<Body className="mx-auto my-auto font-sans">
<Section className="bg-white">
<Container className="mx-auto mt-8 mb-2 max-w-xl rounded-lg border border-slate-200 border-solid px-2 pt-2 backdrop-blur-sm">
{branding.brandingEnabled && branding.brandingLogo ? (
<Img src={branding.brandingLogo} alt="Branding Logo" className="mb-4 h-6 p-2" />
) : (
<TemplateImage assetBaseUrl={assetBaseUrl} className="mb-4 h-6 p-2" staticAsset="logo.png" />
)}
<TemplateBrandingLogo assetBaseUrl={assetBaseUrl} className="mb-4 h-6 p-2" />
<Section>
<TemplateImage className="mx-auto h-12 w-12" assetBaseUrl={assetBaseUrl} staticAsset="building-2.png" />
@@ -1,8 +1,8 @@
import { msg } from '@lingui/core/macro';
import { useLingui } from '@lingui/react';
import { Body, Container, Head, Hr, Html, Img, Preview, Section, Text } from '../components';
import { useBranding } from '../providers/branding';
import { Body, Container, Head, Hr, Html, Preview, Section, Text } from '../components';
import { TemplateBrandingLogo } from '../template-components/template-branding-logo';
import { TemplateFooter } from '../template-components/template-footer';
import TemplateImage from '../template-components/template-image';
@@ -22,7 +22,6 @@ export const OrganisationDeleteEmailTemplate = ({
deletedByAdmin = false,
}: OrganisationDeleteEmailProps) => {
const { _ } = useLingui();
const branding = useBranding();
const previewText = msg`Your organisation has been deleted`;
@@ -40,11 +39,7 @@ export const OrganisationDeleteEmailTemplate = ({
<Body className="mx-auto my-auto font-sans">
<Section className="bg-white text-slate-500">
<Container className="mx-auto mt-8 mb-2 max-w-xl rounded-lg border border-slate-200 border-solid p-2 backdrop-blur-sm">
{branding.brandingEnabled && branding.brandingLogo ? (
<Img src={branding.brandingLogo} alt="Branding Logo" className="mb-4 h-6 p-2" />
) : (
<TemplateImage assetBaseUrl={assetBaseUrl} className="mb-4 h-6 p-2" staticAsset="logo.png" />
)}
<TemplateBrandingLogo assetBaseUrl={assetBaseUrl} className="mb-4 h-6 p-2" />
<Section>
<TemplateImage className="mx-auto" assetBaseUrl={assetBaseUrl} staticAsset="delete-team.png" />
@@ -2,8 +2,8 @@ import { msg } from '@lingui/core/macro';
import { useLingui } from '@lingui/react';
import { Trans } from '@lingui/react/macro';
import { Body, Button, Container, Head, Hr, Html, Img, Preview, Section, Text } from '../components';
import { useBranding } from '../providers/branding';
import { Body, Button, Container, Head, Hr, Html, Preview, Section, Text } from '../components';
import { TemplateBrandingLogo } from '../template-components/template-branding-logo';
import { TemplateFooter } from '../template-components/template-footer';
import TemplateImage from '../template-components/template-image';
@@ -23,7 +23,6 @@ export const OrganisationInviteEmailTemplate = ({
token = '',
}: OrganisationInviteEmailProps) => {
const { _ } = useLingui();
const branding = useBranding();
const previewText = msg`Accept invitation to join an organisation on Documenso`;
@@ -35,11 +34,7 @@ export const OrganisationInviteEmailTemplate = ({
<Body className="mx-auto my-auto font-sans">
<Section className="bg-white text-slate-500">
<Container className="mx-auto mt-8 mb-2 max-w-xl rounded-lg border border-slate-200 border-solid p-2 backdrop-blur-sm">
{branding.brandingEnabled && branding.brandingLogo ? (
<Img src={branding.brandingLogo} alt="Branding Logo" className="mb-4 h-6 p-2" />
) : (
<TemplateImage assetBaseUrl={assetBaseUrl} className="mb-4 h-6 p-2" staticAsset="logo.png" />
)}
<TemplateBrandingLogo assetBaseUrl={assetBaseUrl} className="mb-4 h-6 p-2" />
<Section>
<TemplateImage className="mx-auto" assetBaseUrl={assetBaseUrl} staticAsset="add-user.png" />
@@ -2,8 +2,8 @@ import { msg } from '@lingui/core/macro';
import { useLingui } from '@lingui/react';
import { Trans } from '@lingui/react/macro';
import { Body, Container, Head, Hr, Html, Img, Preview, Section, Text } from '../components';
import { useBranding } from '../providers/branding';
import { Body, Container, Head, Hr, Html, Preview, Section, Text } from '../components';
import { TemplateBrandingLogo } from '../template-components/template-branding-logo';
import { TemplateFooter } from '../template-components/template-footer';
import TemplateImage from '../template-components/template-image';
@@ -25,7 +25,6 @@ export const OrganisationJoinEmailTemplate = ({
organisationUrl = 'demo',
}: OrganisationJoinEmailProps) => {
const { _ } = useLingui();
const branding = useBranding();
const previewText = msg`A member has joined your organisation on Documenso`;
@@ -37,11 +36,7 @@ export const OrganisationJoinEmailTemplate = ({
<Body className="mx-auto my-auto font-sans">
<Section className="bg-white text-slate-500">
<Container className="mx-auto mt-8 mb-2 max-w-xl rounded-lg border border-slate-200 border-solid p-2 backdrop-blur-sm">
{branding.brandingEnabled && branding.brandingLogo ? (
<Img src={branding.brandingLogo} alt="Branding Logo" className="mb-4 h-6 p-2" />
) : (
<TemplateImage assetBaseUrl={assetBaseUrl} className="mb-4 h-6 p-2" staticAsset="logo.png" />
)}
<TemplateBrandingLogo assetBaseUrl={assetBaseUrl} className="mb-4 h-6 p-2" />
<Section>
<TemplateImage className="mx-auto" assetBaseUrl={assetBaseUrl} staticAsset="add-user.png" />
@@ -2,8 +2,8 @@ import { msg } from '@lingui/core/macro';
import { useLingui } from '@lingui/react';
import { Trans } from '@lingui/react/macro';
import { Body, Container, Head, Hr, Html, Img, Preview, Section, Text } from '../components';
import { useBranding } from '../providers/branding';
import { Body, Container, Head, Hr, Html, Preview, Section, Text } from '../components';
import { TemplateBrandingLogo } from '../template-components/template-branding-logo';
import { TemplateFooter } from '../template-components/template-footer';
import TemplateImage from '../template-components/template-image';
@@ -25,7 +25,6 @@ export const OrganisationLeaveEmailTemplate = ({
organisationUrl = 'demo',
}: OrganisationLeaveEmailProps) => {
const { _ } = useLingui();
const branding = useBranding();
const previewText = msg`A member has left your organisation on Documenso`;
@@ -37,11 +36,7 @@ export const OrganisationLeaveEmailTemplate = ({
<Body className="mx-auto my-auto font-sans">
<Section className="bg-white text-slate-500">
<Container className="mx-auto mt-8 mb-2 max-w-xl rounded-lg border border-slate-200 border-solid p-2 backdrop-blur-sm">
{branding.brandingEnabled && branding.brandingLogo ? (
<Img src={branding.brandingLogo} alt="Branding Logo" className="mb-4 h-6 p-2" />
) : (
<TemplateImage assetBaseUrl={assetBaseUrl} className="mb-4 h-6 p-2" staticAsset="logo.png" />
)}
<TemplateBrandingLogo assetBaseUrl={assetBaseUrl} className="mb-4 h-6 p-2" />
<Section>
<TemplateImage className="mx-auto" assetBaseUrl={assetBaseUrl} staticAsset="delete-user.png" />
@@ -3,10 +3,9 @@ import { msg } from '@lingui/core/macro';
import { useLingui } from '@lingui/react';
import { Trans } from '@lingui/react/macro';
import { match } from 'ts-pattern';
import { Body, Container, Head, Hr, Html, Img, Preview, Section, Text } from '../components';
import { useBranding } from '../providers/branding';
import { Body, Container, Head, Hr, Html, Preview, Section, Text } from '../components';
import { TemplateBrandingLogo } from '../template-components/template-branding-logo';
import { TemplateFooter } from '../template-components/template-footer';
import TemplateImage from '../template-components/template-image';
export type OrganisationLimitExceededEmailProps = {
assetBaseUrl: string;
@@ -24,7 +23,6 @@ export const OrganisationLimitExceededEmailTemplate = ({
period = '2026-05',
}: OrganisationLimitExceededEmailProps) => {
const { _ } = useLingui();
const branding = useBranding();
const previewText = msg`Organisation Review Required`;
@@ -36,11 +34,7 @@ export const OrganisationLimitExceededEmailTemplate = ({
<Body className="mx-auto my-auto font-sans">
<Section className="bg-white text-slate-500">
<Container className="mx-auto mt-8 mb-2 max-w-xl rounded-lg border border-slate-200 border-solid p-2 backdrop-blur-sm">
{branding.brandingEnabled && branding.brandingLogo ? (
<Img src={branding.brandingLogo} alt="Branding Logo" className="mb-4 h-6 p-2" />
) : (
<TemplateImage assetBaseUrl={assetBaseUrl} className="mb-4 h-6 p-2" staticAsset="logo.png" />
)}
<TemplateBrandingLogo assetBaseUrl={assetBaseUrl} className="mb-4 h-6 p-2" />
<Section className="p-2 text-slate-500">
<Text className="text-center font-medium text-black text-lg">
+3 -12
View File
@@ -1,8 +1,8 @@
import { msg } from '@lingui/core/macro';
import { useLingui } from '@lingui/react';
import { Body, Container, Head, Hr, Html, Img, Preview, Section } from '../components';
import { useBranding } from '../providers/branding';
import { Body, Container, Head, Hr, Html, Preview, Section } from '../components';
import { TemplateBrandingLogo } from '../template-components/template-branding-logo';
import { TemplateFooter } from '../template-components/template-footer';
import type { TemplateRecipientExpiredProps } from '../template-components/template-recipient-expired';
import { TemplateRecipientExpired } from '../template-components/template-recipient-expired';
@@ -17,14 +17,9 @@ export const RecipientExpiredTemplate = ({
assetBaseUrl = 'http://localhost:3002',
}: RecipientExpiredEmailTemplateProps) => {
const { _ } = useLingui();
const branding = useBranding();
const previewText = msg`The signing window for "${recipientName}" on document "${documentName}" has expired.`;
const getAssetUrl = (path: string) => {
return new URL(path, assetBaseUrl).toString();
};
return (
<Html>
<Head />
@@ -34,11 +29,7 @@ export const RecipientExpiredTemplate = ({
<Section>
<Container className="mx-auto mt-8 mb-2 max-w-xl rounded-lg border border-slate-200 border-solid p-4 backdrop-blur-sm">
<Section>
{branding.brandingEnabled && branding.brandingLogo ? (
<Img src={branding.brandingLogo} alt="Branding Logo" className="mb-4 h-6" />
) : (
<Img src={getAssetUrl('/static/logo.png')} alt="Documenso Logo" className="mb-4 h-6" />
)}
<TemplateBrandingLogo assetBaseUrl={assetBaseUrl} className="mb-4 h-6" />
<TemplateRecipientExpired
documentName={documentName}
@@ -2,8 +2,8 @@ import { msg } from '@lingui/core/macro';
import { useLingui } from '@lingui/react';
import { Trans } from '@lingui/react/macro';
import { Body, Container, Head, Hr, Html, Img, Preview, Section, Text } from '../components';
import { useBranding } from '../providers/branding';
import { Body, Container, Head, Hr, Html, Preview, Section, Text } from '../components';
import { TemplateBrandingLogo } from '../template-components/template-branding-logo';
import type { TemplateDocumentCancelProps } from '../template-components/template-document-cancel';
import TemplateDocumentImage from '../template-components/template-document-image';
import { TemplateFooter } from '../template-components/template-footer';
@@ -16,14 +16,9 @@ export const RecipientRemovedFromDocumentTemplate = ({
assetBaseUrl = 'http://localhost:3002',
}: DocumentCancelEmailTemplateProps) => {
const { _ } = useLingui();
const branding = useBranding();
const previewText = msg`${inviterName} has removed you from the document ${documentName}.`;
const getAssetUrl = (path: string) => {
return new URL(path, assetBaseUrl).toString();
};
return (
<Html>
<Head />
@@ -33,11 +28,7 @@ export const RecipientRemovedFromDocumentTemplate = ({
<Section>
<Container className="mx-auto mt-8 mb-2 max-w-xl rounded-lg border border-slate-200 border-solid p-4 backdrop-blur-sm">
<Section>
{branding.brandingEnabled && branding.brandingLogo ? (
<Img src={branding.brandingLogo} alt="Branding Logo" className="mb-4 h-6" />
) : (
<Img src={getAssetUrl('/static/logo.png')} alt="Documenso Logo" className="mb-4 h-6" />
)}
<TemplateBrandingLogo assetBaseUrl={assetBaseUrl} className="mb-4 h-6" />
<TemplateDocumentImage className="mt-6" assetBaseUrl={assetBaseUrl} />
+3 -12
View File
@@ -2,8 +2,8 @@ import { msg } from '@lingui/core/macro';
import { useLingui } from '@lingui/react';
import { Trans } from '@lingui/react/macro';
import { Body, Container, Head, Hr, Html, Img, Link, Preview, Section, Text } from '../components';
import { useBranding } from '../providers/branding';
import { Body, Container, Head, Hr, Html, Link, Preview, Section, Text } from '../components';
import { TemplateBrandingLogo } from '../template-components/template-branding-logo';
import { TemplateFooter } from '../template-components/template-footer';
import type { TemplateResetPasswordProps } from '../template-components/template-reset-password';
import { TemplateResetPassword } from '../template-components/template-reset-password';
@@ -16,14 +16,9 @@ export const ResetPasswordTemplate = ({
assetBaseUrl = 'http://localhost:3002',
}: ResetPasswordTemplateProps) => {
const { _ } = useLingui();
const branding = useBranding();
const previewText = msg`Password Reset Successful`;
const getAssetUrl = (path: string) => {
return new URL(path, assetBaseUrl).toString();
};
return (
<Html>
<Head />
@@ -33,11 +28,7 @@ export const ResetPasswordTemplate = ({
<Section>
<Container className="mx-auto mt-8 mb-2 max-w-xl rounded-lg border border-slate-200 border-solid p-4 backdrop-blur-sm">
<Section>
{branding.brandingEnabled && branding.brandingLogo ? (
<Img src={branding.brandingLogo} alt="Branding Logo" className="mb-4 h-6" />
) : (
<Img src={getAssetUrl('/static/logo.png')} alt="Documenso Logo" className="mb-4 h-6" />
)}
<TemplateBrandingLogo assetBaseUrl={assetBaseUrl} className="mb-4 h-6" />
<TemplateResetPassword userName={userName} userEmail={userEmail} assetBaseUrl={assetBaseUrl} />
</Section>
+3 -8
View File
@@ -2,8 +2,8 @@ import { formatTeamUrl } from '@documenso/lib/utils/teams';
import { msg } from '@lingui/core/macro';
import { useLingui } from '@lingui/react';
import { Body, Container, Head, Hr, Html, Img, Preview, Section, Text } from '../components';
import { useBranding } from '../providers/branding';
import { Body, Container, Head, Hr, Html, Preview, Section, Text } from '../components';
import { TemplateBrandingLogo } from '../template-components/template-branding-logo';
import { TemplateFooter } from '../template-components/template-footer';
import TemplateImage from '../template-components/template-image';
@@ -19,7 +19,6 @@ export const TeamDeleteEmailTemplate = ({
teamUrl = 'demo',
}: TeamDeleteEmailProps) => {
const { _ } = useLingui();
const branding = useBranding();
const previewText = msg`A team you were a part of has been deleted`;
@@ -35,11 +34,7 @@ export const TeamDeleteEmailTemplate = ({
<Body className="mx-auto my-auto font-sans">
<Section className="bg-white text-slate-500">
<Container className="mx-auto mt-8 mb-2 max-w-xl rounded-lg border border-slate-200 border-solid p-2 backdrop-blur-sm">
{branding.brandingEnabled && branding.brandingLogo ? (
<Img src={branding.brandingLogo} alt="Branding Logo" className="mb-4 h-6 p-2" />
) : (
<TemplateImage assetBaseUrl={assetBaseUrl} className="mb-4 h-6 p-2" staticAsset="logo.png" />
)}
<TemplateBrandingLogo assetBaseUrl={assetBaseUrl} className="mb-4 h-6 p-2" />
<Section>
<TemplateImage className="mx-auto" assetBaseUrl={assetBaseUrl} staticAsset="delete-team.png" />
@@ -3,8 +3,8 @@ import { msg } from '@lingui/core/macro';
import { useLingui } from '@lingui/react';
import { Trans } from '@lingui/react/macro';
import { Body, Container, Head, Hr, Html, Img, Preview, Section, Text } from '../components';
import { useBranding } from '../providers/branding';
import { Body, Container, Head, Hr, Html, Preview, Section, Text } from '../components';
import { TemplateBrandingLogo } from '../template-components/template-branding-logo';
import { TemplateFooter } from '../template-components/template-footer';
import TemplateImage from '../template-components/template-image';
@@ -24,7 +24,6 @@ export const TeamEmailRemovedTemplate = ({
teamUrl = 'demo',
}: TeamEmailRemovedTemplateProps) => {
const { _ } = useLingui();
const branding = useBranding();
const previewText = msg`Team email removed for ${teamName} on Documenso`;
@@ -36,11 +35,7 @@ export const TeamEmailRemovedTemplate = ({
<Body className="mx-auto my-auto font-sans">
<Section className="bg-white text-slate-500">
<Container className="mx-auto mt-8 mb-2 max-w-xl rounded-lg border border-slate-200 border-solid px-2 pt-2 backdrop-blur-sm">
{branding.brandingEnabled && branding.brandingLogo ? (
<Img src={branding.brandingLogo} alt="Branding Logo" className="mb-4 h-6 p-2" />
) : (
<TemplateImage assetBaseUrl={assetBaseUrl} className="mb-4 h-6 p-2" staticAsset="logo.png" />
)}
<TemplateBrandingLogo assetBaseUrl={assetBaseUrl} className="mb-4 h-6 p-2" />
<Section>
<TemplateImage className="mx-auto" assetBaseUrl={assetBaseUrl} staticAsset="mail-open-alert.png" />
+21
View File
@@ -0,0 +1,21 @@
/**
* Returns the normalized Brand Website URL only when it is a safe absolute
* http(s) URL.
*
* Rendering must be defensive because old/imported data can bypass the branding
* form validation. Empty, missing, invalid, relative, or non-http(s) values are
* treated as no Brand Website.
*/
export const getSafeBrandingUrl = (brandingUrl: string | null | undefined): string | null => {
if (!brandingUrl) {
return null;
}
const parsed = URL.parse(brandingUrl);
if (parsed?.protocol !== 'http:' && parsed?.protocol !== 'https:') {
return null;
}
return parsed.href;
};