Replace portal title with entity name
Store a short entity name instead of the full home heading so orgs can brand portals for sub-entities. Restore hero i18n composition and keep the English document title composed from the entity name. Signed-off-by: Émile Ré <emile@probo.com>
This commit is contained in:
@@ -45,6 +45,7 @@
|
||||
}
|
||||
},
|
||||
"home": {
|
||||
"heroTitle": "Vertrauen bei {{name}}.",
|
||||
"heroDescription": "Willkommen auf unserem Compliance-Portal. Hier finden Sie unsere Sicherheitsdokumentation und Zertifizierungen.",
|
||||
"sections": {
|
||||
"compliance": "Compliance",
|
||||
|
||||
@@ -55,6 +55,7 @@
|
||||
}
|
||||
},
|
||||
"home": {
|
||||
"heroTitle": "Compliance at {{name}}.",
|
||||
"heroDescription": "Welcome to our Compliance Portal. Find our security documentation and certifications here.",
|
||||
"sections": {
|
||||
"compliance": "Compliance",
|
||||
@@ -104,4 +105,4 @@
|
||||
"previous": "Previous page",
|
||||
"next": "Next page"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -45,6 +45,7 @@
|
||||
}
|
||||
},
|
||||
"home": {
|
||||
"heroTitle": "Confianza en {{name}}.",
|
||||
"heroDescription": "Bienvenido a nuestro Portal de cumplimiento. Aquí encontrará nuestra documentación de seguridad y nuestras certificaciones.",
|
||||
"sections": {
|
||||
"compliance": "Cumplimiento",
|
||||
|
||||
@@ -45,6 +45,7 @@
|
||||
}
|
||||
},
|
||||
"home": {
|
||||
"heroTitle": "La confiance chez {{name}}.",
|
||||
"heroDescription": "Bienvenue dans notre Portail de conformité. Retrouvez ici notre documentation et nos certifications de sécurité.",
|
||||
"sections": {
|
||||
"compliance": "Conformité",
|
||||
|
||||
@@ -45,6 +45,7 @@
|
||||
}
|
||||
},
|
||||
"home": {
|
||||
"heroTitle": "Kepercayaan di {{name}}.",
|
||||
"heroDescription": "Selamat datang di Portal Kepatuhan kami. Temukan dokumentasi keamanan dan sertifikasi kami di sini.",
|
||||
"sections": {
|
||||
"compliance": "Kepatuhan",
|
||||
|
||||
@@ -45,6 +45,7 @@
|
||||
}
|
||||
},
|
||||
"home": {
|
||||
"heroTitle": "La fiducia in {{name}}.",
|
||||
"heroDescription": "Benvenuti nel nostro Portale di conformità. Qui potete trovare la nostra documentazione di sicurezza e le nostre certificazioni.",
|
||||
"sections": {
|
||||
"compliance": "Conformità",
|
||||
|
||||
@@ -45,6 +45,7 @@
|
||||
}
|
||||
},
|
||||
"home": {
|
||||
"heroTitle": "{{name}}における信頼。",
|
||||
"heroDescription": "コンプライアンスポータルへようこそ。セキュリティに関する文書や認証はこちらからご確認いただけます。",
|
||||
"sections": {
|
||||
"compliance": "コンプライアンス",
|
||||
|
||||
@@ -45,6 +45,7 @@
|
||||
}
|
||||
},
|
||||
"home": {
|
||||
"heroTitle": "{{name}}의 신뢰.",
|
||||
"heroDescription": "컴플라이언스 포털에 오신 것을 환영합니다. 이곳에서 보안 문서와 인증 정보를 확인하실 수 있습니다.",
|
||||
"sections": {
|
||||
"compliance": "컴플라이언스",
|
||||
|
||||
@@ -45,6 +45,7 @@
|
||||
}
|
||||
},
|
||||
"home": {
|
||||
"heroTitle": "Zaufanie w {{name}}.",
|
||||
"heroDescription": "Witamy w naszym Portalu Zgodności. Znajdziesz tu naszą dokumentację bezpieczeństwa i certyfikaty.",
|
||||
"sections": {
|
||||
"compliance": "Zgodność",
|
||||
|
||||
@@ -45,6 +45,7 @@
|
||||
}
|
||||
},
|
||||
"home": {
|
||||
"heroTitle": "A confiança na {{name}}.",
|
||||
"heroDescription": "Bem-vindo ao nosso Portal de Conformidade. Encontre aqui a nossa documentação de segurança e certificações.",
|
||||
"sections": {
|
||||
"compliance": "Conformidade",
|
||||
|
||||
@@ -45,6 +45,7 @@
|
||||
}
|
||||
},
|
||||
"home": {
|
||||
"heroTitle": "{{name}}'de Güven.",
|
||||
"heroDescription": "Uyumluluk Portalımıza hoş geldiniz. Güvenlik belgelerimizi ve sertifikalarımızı burada bulabilirsiniz.",
|
||||
"sections": {
|
||||
"compliance": "Uyumluluk",
|
||||
|
||||
@@ -45,6 +45,7 @@
|
||||
}
|
||||
},
|
||||
"home": {
|
||||
"heroTitle": "Довіра в {{name}}.",
|
||||
"heroDescription": "Вітаємо у нашому Порталі відповідності. Тут ви знайдете нашу документацію з безпеки та сертифікати.",
|
||||
"sections": {
|
||||
"compliance": "Відповідність",
|
||||
|
||||
@@ -45,6 +45,7 @@
|
||||
}
|
||||
},
|
||||
"home": {
|
||||
"heroTitle": "信赖 {{name}}。",
|
||||
"heroDescription": "欢迎访问我们的合规门户。您可以在此查看我们的安全文档和认证。",
|
||||
"sections": {
|
||||
"compliance": "合规",
|
||||
|
||||
@@ -45,7 +45,7 @@ const topBarFragment = graphql`
|
||||
}
|
||||
currentCompliancePortal @required(action: THROW) {
|
||||
themedLogoUrl
|
||||
title
|
||||
entityName
|
||||
}
|
||||
}
|
||||
`;
|
||||
@@ -66,7 +66,7 @@ export function TopBar({ queryKey }: TopBarProps) {
|
||||
const localizedPath = useLocalizedPath();
|
||||
|
||||
const { currentCompliancePortal } = data;
|
||||
const title = currentCompliancePortal.title;
|
||||
const entityName = currentCompliancePortal.entityName;
|
||||
const logoUrl = currentCompliancePortal.themedLogoUrl ?? undefined;
|
||||
|
||||
const slots = topBar();
|
||||
@@ -82,11 +82,11 @@ export function TopBar({ queryKey }: TopBarProps) {
|
||||
color="neutral"
|
||||
radius="small"
|
||||
src={logoUrl}
|
||||
fallback={title.charAt(0) || "?"}
|
||||
fallback={entityName.charAt(0) || "?"}
|
||||
className={slots.logo()}
|
||||
/>
|
||||
<Text size={2} weight="medium" color="neutral" highContrast className={slots.brandName()}>
|
||||
{title}
|
||||
{entityName}
|
||||
</Text>
|
||||
<Text size={2} color="neutral" className={slots.tagline()}>
|
||||
{t("topBar.tagline")}
|
||||
|
||||
@@ -45,7 +45,7 @@ export const subscribeDialogProviderFragment = graphql`
|
||||
}
|
||||
currentCompliancePortal @required(action: THROW) {
|
||||
id
|
||||
title
|
||||
entityName
|
||||
viewerSubscription {
|
||||
id
|
||||
}
|
||||
@@ -71,7 +71,7 @@ export function SubscribeDialogProvider({
|
||||
const [unsubscribeFromMailingList, isUnsubscribing] = useUnsubscribeFromMailingList();
|
||||
|
||||
const viewer = data.viewer;
|
||||
const { id: trustCenterId, title, viewerSubscription } = data.currentCompliancePortal;
|
||||
const { id: trustCenterId, entityName, viewerSubscription } = data.currentCompliancePortal;
|
||||
const isSubscribed = viewerSubscription != null;
|
||||
|
||||
const openSubscribe = useCallback(() => {
|
||||
@@ -127,7 +127,7 @@ export function SubscribeDialogProvider({
|
||||
onOpenChange={setDialogOpen}
|
||||
trustCenterId={trustCenterId}
|
||||
viewerEmail={viewer.email}
|
||||
organizationName={title}
|
||||
organizationName={entityName}
|
||||
/>
|
||||
)}
|
||||
</SubscribeDialogContextProvider>
|
||||
|
||||
@@ -34,7 +34,7 @@ import type { HomePageQuery } from "./__generated__/HomePageQuery.graphql";
|
||||
export const homePageQuery = graphql`
|
||||
query HomePageQuery @throwOnFieldError {
|
||||
currentCompliancePortal @required(action: THROW) {
|
||||
title
|
||||
entityName
|
||||
...CompliancePortalContactInfo_compliancePortal
|
||||
...ComplianceFrameworksSection_compliancePortal
|
||||
...SecurityCommitmentsSection_compliancePortal
|
||||
@@ -52,12 +52,12 @@ export function HomePage({ queryRef }: HomePageProps) {
|
||||
const { t } = useTranslation();
|
||||
const data = usePreloadedQuery<HomePageQuery>(homePageQuery, queryRef);
|
||||
const { currentCompliancePortal } = data;
|
||||
const { title } = currentCompliancePortal;
|
||||
const { entityName } = currentCompliancePortal;
|
||||
|
||||
return (
|
||||
<>
|
||||
<Hero
|
||||
title={title}
|
||||
title={t("home.heroTitle", { name: entityName })}
|
||||
description={t("home.heroDescription")}
|
||||
>
|
||||
<CompliancePortalContactInfo compliancePortalKey={currentCompliancePortal} />
|
||||
|
||||
@@ -63,7 +63,7 @@ export const ndaPageQuery = graphql`
|
||||
id
|
||||
}
|
||||
currentCompliancePortal @required(action: THROW) {
|
||||
title
|
||||
entityName
|
||||
nonDisclosureAgreement {
|
||||
fileUrl
|
||||
}
|
||||
@@ -238,7 +238,7 @@ export function NDAPage({ queryRef }: NDAPageProps) {
|
||||
{t("title")}
|
||||
</Heading>
|
||||
<Text size={2} color="neutral">
|
||||
{t("subtitle", { name: compliancePortal.title })}
|
||||
{t("subtitle", { name: compliancePortal.entityName })}
|
||||
</Text>
|
||||
{signature.consentText != null && (
|
||||
<Text size={1} color="faint" className={slots.consent()}>
|
||||
|
||||
@@ -32,7 +32,7 @@ const updateCompliancePageMutation = graphql`
|
||||
id
|
||||
active
|
||||
searchEngineIndexing
|
||||
title
|
||||
entityName
|
||||
description
|
||||
websiteUrl
|
||||
email
|
||||
|
||||
@@ -25,7 +25,7 @@ import { useFormWithSchema } from "#/hooks/useFormWithSchema";
|
||||
const compliancePageFragment = graphql`
|
||||
fragment CompliancePageProfileSection_compliancePageFragment on CompliancePortal {
|
||||
id
|
||||
title
|
||||
entityName
|
||||
description
|
||||
websiteUrl
|
||||
email
|
||||
@@ -35,7 +35,7 @@ const compliancePageFragment = graphql`
|
||||
`;
|
||||
|
||||
const profileSchema = z.object({
|
||||
title: z.string().min(1),
|
||||
entityName: z.string().min(1),
|
||||
description: z.string().optional(),
|
||||
websiteUrl: z.string().optional(),
|
||||
email: z.string().optional(),
|
||||
@@ -58,7 +58,7 @@ export function CompliancePageProfileSection(props: {
|
||||
|
||||
const { formState, handleSubmit, register } = useFormWithSchema(profileSchema, {
|
||||
defaultValues: {
|
||||
title: compliancePage.title,
|
||||
entityName: compliancePage.entityName,
|
||||
description: compliancePage.description || "",
|
||||
websiteUrl: compliancePage.websiteUrl || "",
|
||||
email: compliancePage.email || "",
|
||||
@@ -73,7 +73,7 @@ export function CompliancePageProfileSection(props: {
|
||||
variables: {
|
||||
input: {
|
||||
compliancePortalId: compliancePage.id,
|
||||
title: data.title,
|
||||
entityName: data.entityName,
|
||||
description: data.description || null,
|
||||
websiteUrl: data.websiteUrl || null,
|
||||
email: data.email || null,
|
||||
@@ -96,11 +96,11 @@ export function CompliancePageProfileSection(props: {
|
||||
</div>
|
||||
<Card padded className="space-y-4">
|
||||
<Field
|
||||
{...register("title")}
|
||||
{...register("entityName")}
|
||||
readOnly={readOnly}
|
||||
name="title"
|
||||
label={__("Title")}
|
||||
placeholder={__("Compliance at Acme.")}
|
||||
name="entityName"
|
||||
label={__("Entity name")}
|
||||
placeholder={__("Acme")}
|
||||
/>
|
||||
<div>
|
||||
<Label>{__("Description")}</Label>
|
||||
|
||||
@@ -233,7 +233,7 @@ export function OrganizationSidebar({
|
||||
: (
|
||||
<div className="size-24 rounded-2xl border border-border-mid shadow-mid bg-level-1" />
|
||||
)}
|
||||
<h1 className="text-2xl mt-6">{compliancePortal.title}</h1>
|
||||
<h1 className="text-2xl mt-6">{compliancePortal.entityName}</h1>
|
||||
<p className="text-sm text-txt-secondary mt-1">
|
||||
{compliancePortal.description}
|
||||
</p>
|
||||
|
||||
@@ -24,7 +24,7 @@ import { CompliancePortalContext } from "#/providers/CompliancePortalProvider";
|
||||
|
||||
export function useCompliancePortal(): {
|
||||
id: string;
|
||||
title: string;
|
||||
entityName: string;
|
||||
} {
|
||||
const context = useContext(CompliancePortalContext);
|
||||
if (!context) {
|
||||
|
||||
@@ -47,7 +47,7 @@ export const ndaPageQuery = graphql`
|
||||
id
|
||||
}
|
||||
currentCompliancePortal @required(action: THROW) {
|
||||
title
|
||||
entityName
|
||||
nonDisclosureAgreement {
|
||||
fileName
|
||||
fileUrl
|
||||
@@ -244,7 +244,7 @@ export function NDAPage(props: {
|
||||
__(
|
||||
"%s requires you to sign an NDA before accessing compliance documents.",
|
||||
),
|
||||
compliancePortal.title,
|
||||
compliancePortal.entityName,
|
||||
)}
|
||||
</p>
|
||||
{isMobile && nda?.fileUrl && (
|
||||
|
||||
@@ -107,7 +107,7 @@ export function OverviewPage() {
|
||||
url={getCompliancePortalUrl("documents")}
|
||||
/>
|
||||
<Subprocessors
|
||||
title={compliancePortal.title}
|
||||
title={compliancePortal.entityName}
|
||||
subprocessors={fragment.subprocessors.edges}
|
||||
url={getCompliancePortalUrl("subprocessors")}
|
||||
/>
|
||||
|
||||
@@ -45,7 +45,7 @@ export function SubprocessorsPage({ queryRef }: Props) {
|
||||
<p className="text-sm text-txt-secondary mb-4">
|
||||
{sprintf(
|
||||
__("Third-party subprocessors %s work with:"),
|
||||
data.currentCompliancePortal?.title ?? "",
|
||||
data.currentCompliancePortal?.entityName ?? "",
|
||||
)}
|
||||
</p>
|
||||
<Rows>
|
||||
|
||||
@@ -31,7 +31,7 @@ import type { ConnectPageQuery } from "./__generated__/ConnectPageQuery.graphql"
|
||||
export const connectPageQuery = graphql`
|
||||
query ConnectPageQuery {
|
||||
currentCompliancePortal @required(action: THROW) {
|
||||
title
|
||||
entityName
|
||||
}
|
||||
}
|
||||
`;
|
||||
@@ -45,10 +45,10 @@ export function ConnectPage(props: {
|
||||
const safeContinueUrl = useSafeContinueUrl();
|
||||
|
||||
const {
|
||||
currentCompliancePortal: { title },
|
||||
currentCompliancePortal: { entityName },
|
||||
} = usePreloadedQuery<ConnectPageQuery>(connectPageQuery, queryRef);
|
||||
|
||||
usePageTitle(__(`Connect to ${title}'s Compliance Page`));
|
||||
usePageTitle(__(`Connect to ${entityName}'s Compliance Page`));
|
||||
|
||||
const initiateURL = new URL("/initiate", window.location.origin);
|
||||
initiateURL.searchParams.set("continue", safeContinueUrl.toString());
|
||||
@@ -57,7 +57,7 @@ export function ConnectPage(props: {
|
||||
<div className="space-y-6 w-full max-w-md mx-auto pt-8">
|
||||
<div className="space-y-2 text-center">
|
||||
<h1 className="text-3xl font-bold">
|
||||
{__(`Connect to ${title}'s Compliance Page`)}
|
||||
{__(`Connect to ${entityName}'s Compliance Page`)}
|
||||
</h1>
|
||||
<p className="text-txt-tertiary">
|
||||
{__(
|
||||
|
||||
@@ -31,7 +31,7 @@ export const currentCompliancePortalGraphQuery = graphql`
|
||||
currentCompliancePortal @required(action: THROW) {
|
||||
id
|
||||
slug
|
||||
title
|
||||
entityName
|
||||
description
|
||||
websiteUrl
|
||||
email
|
||||
@@ -120,6 +120,7 @@ export const currentTrustSubprocessorsQuery = graphql`
|
||||
query CompliancePortalGraphCurrentSubprocessorsQuery {
|
||||
currentCompliancePortal {
|
||||
id
|
||||
entityName
|
||||
subprocessors(first: 50) {
|
||||
edges {
|
||||
node {
|
||||
|
||||
@@ -61,7 +61,7 @@ func TestCompliancePortal_UpdateProfile(t *testing.T) {
|
||||
updateCompliancePortal(input: $input) {
|
||||
compliancePortal {
|
||||
id
|
||||
title
|
||||
entityName
|
||||
description
|
||||
websiteUrl
|
||||
email
|
||||
@@ -75,7 +75,7 @@ func TestCompliancePortal_UpdateProfile(t *testing.T) {
|
||||
UpdateCompliancePortal struct {
|
||||
CompliancePortal struct {
|
||||
ID string `json:"id"`
|
||||
Title string `json:"title"`
|
||||
EntityName string `json:"entityName"`
|
||||
Description *string `json:"description"`
|
||||
WebsiteURL *string `json:"websiteUrl"`
|
||||
Email *string `json:"email"`
|
||||
@@ -87,7 +87,7 @@ func TestCompliancePortal_UpdateProfile(t *testing.T) {
|
||||
err = owner.Execute(updateMutation, map[string]any{
|
||||
"input": map[string]any{
|
||||
"compliancePortalId": compliancePortalID,
|
||||
"title": "Acme Security",
|
||||
"entityName": "Acme Security",
|
||||
"description": "We keep your data safe.",
|
||||
"websiteUrl": "https://example.com",
|
||||
"email": "security@example.com",
|
||||
@@ -98,7 +98,7 @@ func TestCompliancePortal_UpdateProfile(t *testing.T) {
|
||||
|
||||
tc := result.UpdateCompliancePortal.CompliancePortal
|
||||
assert.Equal(t, compliancePortalID, tc.ID)
|
||||
assert.Equal(t, "Acme Security", tc.Title)
|
||||
assert.Equal(t, "Acme Security", tc.EntityName)
|
||||
require.NotNil(t, tc.Description)
|
||||
assert.Equal(t, "We keep your data safe.", *tc.Description)
|
||||
require.NotNil(t, tc.WebsiteURL)
|
||||
|
||||
@@ -35,10 +35,10 @@ type mcpFile struct {
|
||||
}
|
||||
|
||||
type compliancePortal struct {
|
||||
ID string `json:"id"`
|
||||
Title string `json:"title"`
|
||||
Active bool `json:"active"`
|
||||
Logo *mcpFile `json:"logo,omitempty"`
|
||||
ID string `json:"id"`
|
||||
EntityName string `json:"entity_name"`
|
||||
Active bool `json:"active"`
|
||||
Logo *mcpFile `json:"logo,omitempty"`
|
||||
}
|
||||
|
||||
type compliancePortalReference struct {
|
||||
@@ -175,7 +175,7 @@ func TestMCP_UpdateCompliancePortal(t *testing.T) {
|
||||
var updateResult struct {
|
||||
CompliancePortal struct {
|
||||
ID string `json:"id"`
|
||||
Title string `json:"title"`
|
||||
EntityName string `json:"entity_name"`
|
||||
Description *string `json:"description"`
|
||||
WebsiteURL *string `json:"website_url"`
|
||||
Email *string `json:"email"`
|
||||
@@ -184,7 +184,7 @@ func TestMCP_UpdateCompliancePortal(t *testing.T) {
|
||||
}
|
||||
mc.CallToolInto("updateCompliancePortal", map[string]any{
|
||||
"compliance_portal_id": getResult.CompliancePortal.ID,
|
||||
"title": "Acme Security",
|
||||
"entity_name": "Acme Security",
|
||||
"description": "We keep your data safe.",
|
||||
"website_url": "https://example.com",
|
||||
"email": "security@example.com",
|
||||
@@ -192,7 +192,7 @@ func TestMCP_UpdateCompliancePortal(t *testing.T) {
|
||||
}, &updateResult)
|
||||
|
||||
assert.Equal(t, getResult.CompliancePortal.ID, updateResult.CompliancePortal.ID)
|
||||
assert.Equal(t, "Acme Security", updateResult.CompliancePortal.Title)
|
||||
assert.Equal(t, "Acme Security", updateResult.CompliancePortal.EntityName)
|
||||
require.NotNil(t, updateResult.CompliancePortal.Description)
|
||||
assert.Equal(t, "We keep your data safe.", *updateResult.CompliancePortal.Description)
|
||||
require.NotNil(t, updateResult.CompliancePortal.WebsiteURL)
|
||||
|
||||
@@ -83,20 +83,20 @@ func TestCompliancePortal_VisitorConnectViaCIMD(t *testing.T) {
|
||||
const query = `
|
||||
query {
|
||||
currentCompliancePortal {
|
||||
title
|
||||
entityName
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
var result struct {
|
||||
CurrentCompliancePortal struct {
|
||||
Title string `json:"title"`
|
||||
EntityName string `json:"entityName"`
|
||||
} `json:"currentCompliancePortal"`
|
||||
}
|
||||
|
||||
err := visitor.ExecuteTrust(trustHost, query, nil, &result)
|
||||
require.NoError(t, err, "visitor session must authenticate trust GraphQL after CIMD connect")
|
||||
assert.NotEmpty(t, result.CurrentCompliancePortal.Title)
|
||||
assert.NotEmpty(t, result.CurrentCompliancePortal.EntityName)
|
||||
}
|
||||
|
||||
func TestCompliancePortal_UnknownCIMDClientRejected(t *testing.T) {
|
||||
|
||||
@@ -77,8 +77,8 @@ export const description: INodeProperties[] = [
|
||||
description: 'Whether search engines should index the compliance portal',
|
||||
},
|
||||
{
|
||||
displayName: 'Title',
|
||||
name: 'title',
|
||||
displayName: 'Entity Name',
|
||||
name: 'entityName',
|
||||
type: 'string',
|
||||
displayOptions: {
|
||||
show: {
|
||||
@@ -87,7 +87,7 @@ export const description: INodeProperties[] = [
|
||||
},
|
||||
},
|
||||
default: '',
|
||||
description: 'The title shown on the public compliance page',
|
||||
description: 'The entity name shown on the public compliance page',
|
||||
},
|
||||
{
|
||||
displayName: 'Description',
|
||||
@@ -155,7 +155,7 @@ export async function execute(
|
||||
const websiteUrl = this.getNodeParameter('websiteUrl', itemIndex, '') as string;
|
||||
const email = this.getNodeParameter('email', itemIndex, '') as string;
|
||||
const headquarterAddress = this.getNodeParameter('headquarterAddress', itemIndex, '') as string;
|
||||
const title = this.getNodeParameter('title', itemIndex, '') as string;
|
||||
const entityName = this.getNodeParameter('entityName', itemIndex, '') as string;
|
||||
|
||||
const query = `
|
||||
mutation UpdateCompliancePortal($input: UpdateCompliancePortalInput!) {
|
||||
@@ -164,7 +164,7 @@ export async function execute(
|
||||
id
|
||||
active
|
||||
searchEngineIndexing
|
||||
title
|
||||
entityName
|
||||
description
|
||||
websiteUrl
|
||||
email
|
||||
@@ -178,7 +178,7 @@ export async function execute(
|
||||
|
||||
const input: Record<string, unknown> = {
|
||||
compliancePortalId,
|
||||
title,
|
||||
entityName,
|
||||
description,
|
||||
websiteUrl,
|
||||
email,
|
||||
|
||||
@@ -49,7 +49,7 @@ mutation($input: UpdateCompliancePortalInput!) {
|
||||
id
|
||||
active
|
||||
searchEngineIndexing
|
||||
title
|
||||
entityName
|
||||
description
|
||||
websiteUrl
|
||||
email
|
||||
@@ -74,7 +74,7 @@ type updateResponse struct {
|
||||
ID string `json:"id"`
|
||||
Active bool `json:"active"`
|
||||
SearchEngineIndexing string `json:"searchEngineIndexing"`
|
||||
Title string `json:"title"`
|
||||
EntityName string `json:"entityName"`
|
||||
Description *string `json:"description"`
|
||||
WebsiteURL *string `json:"websiteUrl"`
|
||||
Email *string `json:"email"`
|
||||
@@ -92,7 +92,7 @@ func NewCmdUpdate(f *cmdutil.Factory) *cobra.Command {
|
||||
flagWebsiteURL string
|
||||
flagEmail string
|
||||
flagHeadquarterAddress string
|
||||
flagTitle string
|
||||
flagEntityName string
|
||||
)
|
||||
|
||||
cmd := &cobra.Command{
|
||||
@@ -193,8 +193,8 @@ func NewCmdUpdate(f *cmdutil.Factory) *cobra.Command {
|
||||
input["headquarterAddress"] = flagHeadquarterAddress
|
||||
}
|
||||
|
||||
if cmd.Flags().Changed("title") {
|
||||
input["title"] = flagTitle
|
||||
if cmd.Flags().Changed("entity-name") {
|
||||
input["entityName"] = flagEntityName
|
||||
}
|
||||
|
||||
if len(input) == 1 {
|
||||
@@ -232,7 +232,7 @@ func NewCmdUpdate(f *cmdutil.Factory) *cobra.Command {
|
||||
cmd.Flags().StringVar(&flagWebsiteURL, "website-url", "", "Compliance page website URL")
|
||||
cmd.Flags().StringVar(&flagEmail, "email", "", "Compliance page contact email")
|
||||
cmd.Flags().StringVar(&flagHeadquarterAddress, "headquarter-address", "", "Compliance page headquarter address")
|
||||
cmd.Flags().StringVar(&flagTitle, "title", "", "Public compliance page title")
|
||||
cmd.Flags().StringVar(&flagEntityName, "entity-name", "", "Entity name shown on the public compliance page")
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
@@ -46,7 +46,7 @@ type (
|
||||
Slug *string
|
||||
SearchEngineIndexing *coredata.SearchEngineIndexing
|
||||
NonDisclosureAgreementFileID *gid.GID
|
||||
Title *string
|
||||
EntityName *string
|
||||
Description **string
|
||||
WebsiteURL **string
|
||||
Email **string
|
||||
@@ -75,8 +75,8 @@ func (utcr *UpdateRequest) Validate() error {
|
||||
v.Check(utcr.Slug, "slug", validator.SafeText(NameMaxLength))
|
||||
v.Check(utcr.NonDisclosureAgreementFileID, "non_disclosure_agreement_file_id", validator.GID(coredata.FileEntityType))
|
||||
|
||||
if utcr.Title != nil {
|
||||
v.Check(*utcr.Title, "title", validator.Required(), validator.SafeTextNoNewLine(TitleMaxLength))
|
||||
if utcr.EntityName != nil {
|
||||
v.Check(*utcr.EntityName, "entity_name", validator.Required(), validator.SafeTextNoNewLine(TitleMaxLength))
|
||||
}
|
||||
|
||||
if utcr.Description != nil {
|
||||
@@ -214,8 +214,8 @@ func (s *Service) Update(
|
||||
portal.SearchEngineIndexing = *req.SearchEngineIndexing
|
||||
}
|
||||
|
||||
if req.Title != nil {
|
||||
portal.Title = *req.Title
|
||||
if req.EntityName != nil {
|
||||
portal.EntityName = *req.EntityName
|
||||
}
|
||||
|
||||
if req.Description != nil {
|
||||
|
||||
@@ -85,7 +85,7 @@ func BuildClientMetadataDocument(
|
||||
|
||||
doc := oauth2.ClientMetadataDocument{
|
||||
ClientID: clientID,
|
||||
ClientName: portal.Title,
|
||||
ClientName: "Compliance at " + portal.EntityName + ".",
|
||||
ClientURI: portalRootURL,
|
||||
RedirectURIs: []string{redirectURI},
|
||||
TokenEndpointAuthMethod: "none",
|
||||
|
||||
@@ -66,7 +66,7 @@ func TestBuildClientMetadataDocument(t *testing.T) {
|
||||
|
||||
websiteURL := "https://www.acme.com"
|
||||
portal := &coredata.CompliancePortal{
|
||||
Title: "Acme Compliance Page",
|
||||
EntityName: "Acme",
|
||||
WebsiteURL: &websiteURL,
|
||||
}
|
||||
|
||||
@@ -76,7 +76,7 @@ func TestBuildClientMetadataDocument(t *testing.T) {
|
||||
)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, "https://acme.example.com/.well-known/oauth-client-metadata", doc.ClientID)
|
||||
assert.Equal(t, "Acme Compliance Page", doc.ClientName)
|
||||
assert.Equal(t, "Compliance at Acme.", doc.ClientName)
|
||||
assert.Equal(t, []string{"https://acme.example.com/callback"}, doc.RedirectURIs)
|
||||
assert.Equal(t, "https://acme.example.com", doc.ClientURI)
|
||||
assert.Equal(t, VisitorOAuthScope, doc.Scope)
|
||||
@@ -88,7 +88,7 @@ func TestBuildClientMetadataDocument_LogoURIUsesBrandLogoEndpoint(t *testing.T)
|
||||
|
||||
logoFileID := gid.MustParseGID("WR-qMrB5AAEAGQAAAZ9mIO8B8vDFQ-i3")
|
||||
portal := &coredata.CompliancePortal{
|
||||
Title: "Acme Compliance Page",
|
||||
EntityName: "Acme",
|
||||
LogoFileID: &logoFileID,
|
||||
}
|
||||
|
||||
|
||||
@@ -49,7 +49,7 @@ type (
|
||||
NonDisclosureAgreementFileID *gid.GID `db:"non_disclosure_agreement_file_id"`
|
||||
DefaultDomainID *gid.GID `db:"default_domain_id"`
|
||||
CustomDomainID *gid.GID `db:"custom_domain_id"`
|
||||
Title string `db:"title"`
|
||||
EntityName string `db:"entity_name"`
|
||||
Description *string `db:"description"`
|
||||
WebsiteURL *string `db:"website_url"`
|
||||
Email *string `db:"email"`
|
||||
@@ -129,7 +129,7 @@ SELECT
|
||||
non_disclosure_agreement_file_id,
|
||||
default_domain_id,
|
||||
custom_domain_id,
|
||||
title,
|
||||
entity_name,
|
||||
description,
|
||||
website_url,
|
||||
email,
|
||||
@@ -188,7 +188,7 @@ SELECT
|
||||
non_disclosure_agreement_file_id,
|
||||
default_domain_id,
|
||||
custom_domain_id,
|
||||
title,
|
||||
entity_name,
|
||||
description,
|
||||
website_url,
|
||||
email,
|
||||
@@ -247,7 +247,7 @@ SELECT
|
||||
non_disclosure_agreement_file_id,
|
||||
default_domain_id,
|
||||
custom_domain_id,
|
||||
title,
|
||||
entity_name,
|
||||
description,
|
||||
website_url,
|
||||
email,
|
||||
@@ -306,7 +306,7 @@ SELECT
|
||||
non_disclosure_agreement_file_id,
|
||||
default_domain_id,
|
||||
custom_domain_id,
|
||||
title,
|
||||
entity_name,
|
||||
description,
|
||||
website_url,
|
||||
email,
|
||||
@@ -365,7 +365,7 @@ SELECT
|
||||
non_disclosure_agreement_file_id,
|
||||
default_domain_id,
|
||||
custom_domain_id,
|
||||
title,
|
||||
entity_name,
|
||||
description,
|
||||
website_url,
|
||||
email,
|
||||
@@ -420,7 +420,7 @@ INSERT INTO trust_centers (
|
||||
non_disclosure_agreement_file_id,
|
||||
default_domain_id,
|
||||
custom_domain_id,
|
||||
title,
|
||||
entity_name,
|
||||
description,
|
||||
website_url,
|
||||
email,
|
||||
@@ -440,7 +440,7 @@ INSERT INTO trust_centers (
|
||||
@non_disclosure_agreement_file_id,
|
||||
@default_domain_id,
|
||||
@custom_domain_id,
|
||||
@title,
|
||||
@entity_name,
|
||||
@description,
|
||||
@website_url,
|
||||
@email,
|
||||
@@ -463,7 +463,7 @@ INSERT INTO trust_centers (
|
||||
"non_disclosure_agreement_file_id": tc.NonDisclosureAgreementFileID,
|
||||
"default_domain_id": tc.DefaultDomainID,
|
||||
"custom_domain_id": tc.CustomDomainID,
|
||||
"title": tc.Title,
|
||||
"entity_name": tc.EntityName,
|
||||
"description": tc.Description,
|
||||
"website_url": tc.WebsiteURL,
|
||||
"email": tc.Email,
|
||||
@@ -502,7 +502,7 @@ SET
|
||||
non_disclosure_agreement_file_id = @non_disclosure_agreement_file_id,
|
||||
default_domain_id = @default_domain_id,
|
||||
custom_domain_id = @custom_domain_id,
|
||||
title = @title,
|
||||
entity_name = @entity_name,
|
||||
description = @description,
|
||||
website_url = @website_url,
|
||||
email = @email,
|
||||
@@ -525,7 +525,7 @@ WHERE
|
||||
"non_disclosure_agreement_file_id": tc.NonDisclosureAgreementFileID,
|
||||
"default_domain_id": tc.DefaultDomainID,
|
||||
"custom_domain_id": tc.CustomDomainID,
|
||||
"title": tc.Title,
|
||||
"entity_name": tc.EntityName,
|
||||
"description": tc.Description,
|
||||
"website_url": tc.WebsiteURL,
|
||||
"email": tc.Email,
|
||||
|
||||
36
pkg/coredata/migrations/20260721T165706Z.sql
Normal file
36
pkg/coredata/migrations/20260721T165706Z.sql
Normal file
@@ -0,0 +1,36 @@
|
||||
-- Copyright (c) 2026 Probo Inc <hello@probo.com>.
|
||||
--
|
||||
-- Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
-- of this software and associated documentation files (the "Software"), to deal
|
||||
-- in the Software without restriction, including without limitation the rights
|
||||
-- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
-- copies of the Software, and to permit persons to whom the Software is
|
||||
-- furnished to do so, subject to the following conditions:
|
||||
--
|
||||
-- The above copyright notice and this permission notice shall be included in
|
||||
-- all copies or substantial portions of the Software.
|
||||
--
|
||||
-- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
-- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
-- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
-- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
-- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
-- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
-- SOFTWARE.
|
||||
|
||||
-- entity_name is the short name shown in the hero ("Compliance at {{name}}.")
|
||||
-- and TopBar brand. Prefill from the organization so existing portals keep a
|
||||
-- sensible default; customized full titles in the old title column are dropped.
|
||||
ALTER TABLE trust_centers
|
||||
ADD COLUMN entity_name TEXT NOT NULL DEFAULT '';
|
||||
|
||||
UPDATE trust_centers tc
|
||||
SET entity_name = o.name
|
||||
FROM organizations o
|
||||
WHERE tc.organization_id = o.id;
|
||||
|
||||
ALTER TABLE trust_centers
|
||||
ALTER COLUMN entity_name DROP DEFAULT;
|
||||
|
||||
ALTER TABLE trust_centers
|
||||
DROP COLUMN title;
|
||||
@@ -614,7 +614,7 @@ func (s *OrganizationService) CreateOrganization(
|
||||
TenantID: organization.TenantID,
|
||||
Active: false,
|
||||
Slug: slug.MakeWithEntropy(organization.Name),
|
||||
Title: "Compliance at " + organization.Name + ".",
|
||||
EntityName: organization.Name,
|
||||
SearchEngineIndexing: coredata.SearchEngineIndexingNotIndexable,
|
||||
MailingListID: &mailingList.ID,
|
||||
CreatedAt: now,
|
||||
|
||||
@@ -9,7 +9,7 @@ type CompliancePortal implements Node {
|
||||
websiteUrl: String
|
||||
email: String
|
||||
headquarterAddress: String
|
||||
title: String!
|
||||
entityName: String!
|
||||
|
||||
nonDisclosureAgreement: NonDisclosureAgreement @goField(forceResolver: true)
|
||||
|
||||
|
||||
@@ -134,7 +134,8 @@ func compliancePageHeadData() HeadDataFunc {
|
||||
|
||||
compliancePageBaseURL := complianceportal.CompliancePortalBaseURLFromContext(r.Context())
|
||||
|
||||
description := tc.Title
|
||||
pageTitle := "Compliance at " + tc.EntityName + "."
|
||||
description := pageTitle
|
||||
if tc.Description != nil && *tc.Description != "" {
|
||||
description = *tc.Description
|
||||
}
|
||||
@@ -142,7 +143,7 @@ func compliancePageHeadData() HeadDataFunc {
|
||||
htmlLang, canonical, hreflang := SEOFromRequest(r)
|
||||
|
||||
headData := HeadData{
|
||||
Title: tc.Title,
|
||||
Title: pageTitle,
|
||||
Description: description,
|
||||
HTMLLang: htmlLang,
|
||||
OGURL: canonical,
|
||||
|
||||
@@ -29,7 +29,7 @@ func NewCompliancePortal(cp *coredata.CompliancePortal) *CompliancePortal {
|
||||
ID: cp.ID,
|
||||
Active: cp.Active,
|
||||
Slug: cp.Slug,
|
||||
Title: cp.Title,
|
||||
EntityName: cp.EntityName,
|
||||
Description: cp.Description,
|
||||
WebsiteURL: cp.WebsiteURL,
|
||||
Email: cp.Email,
|
||||
|
||||
@@ -804,7 +804,7 @@ func (r *mutationResolver) UpdateCompliancePortal(ctx context.Context, input typ
|
||||
WebsiteURL: gqlutils.UnwrapOmittable(input.WebsiteURL),
|
||||
Email: gqlutils.UnwrapOmittable(input.Email),
|
||||
HeadquarterAddress: gqlutils.UnwrapOmittable(input.HeadquarterAddress),
|
||||
Title: input.Title,
|
||||
EntityName: input.EntityName,
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
|
||||
@@ -344,7 +344,7 @@ type CompliancePortal implements Node
|
||||
websiteUrl: String
|
||||
email: String
|
||||
headquarterAddress: String
|
||||
title: String!
|
||||
entityName: String!
|
||||
createdAt: Datetime!
|
||||
updatedAt: Datetime!
|
||||
organization: Organization! @goField(forceResolver: true)
|
||||
@@ -747,7 +747,7 @@ input UpdateCompliancePortalInput {
|
||||
compliancePortalId: ID!
|
||||
active: Boolean
|
||||
searchEngineIndexing: SearchEngineIndexing
|
||||
title: String
|
||||
entityName: String
|
||||
description: String @goField(omittable: true)
|
||||
websiteUrl: String @goField(omittable: true)
|
||||
email: String @goField(omittable: true)
|
||||
|
||||
@@ -38,7 +38,7 @@ type CompliancePortal struct {
|
||||
WebsiteURL *string `json:"websiteUrl,omitempty"`
|
||||
Email *string `json:"email,omitempty"`
|
||||
HeadquarterAddress *string `json:"headquarterAddress,omitempty"`
|
||||
Title string `json:"title"`
|
||||
EntityName string `json:"entityName"`
|
||||
CreatedAt time.Time `json:"createdAt"`
|
||||
UpdatedAt time.Time `json:"updatedAt"`
|
||||
Organization *Organization `json:"organization"`
|
||||
@@ -67,7 +67,7 @@ func NewCompliancePortal(tc *coredata.CompliancePortal) *CompliancePortal {
|
||||
WebsiteURL: tc.WebsiteURL,
|
||||
Email: tc.Email,
|
||||
HeadquarterAddress: tc.HeadquarterAddress,
|
||||
Title: tc.Title,
|
||||
EntityName: tc.EntityName,
|
||||
CreatedAt: tc.CreatedAt,
|
||||
UpdatedAt: tc.UpdatedAt,
|
||||
}
|
||||
|
||||
@@ -4954,8 +4954,8 @@ func (r *Resolver) UpdateCompliancePortalTool(ctx context.Context, req *mcp.Call
|
||||
updateReq.Email = UnwrapOmittable(input.Email)
|
||||
updateReq.HeadquarterAddress = UnwrapOmittable(input.HeadquarterAddress)
|
||||
|
||||
if title := UnwrapOmittable(input.Title); title != nil {
|
||||
updateReq.Title = *title
|
||||
if entityName := UnwrapOmittable(input.EntityName); entityName != nil {
|
||||
updateReq.EntityName = *entityName
|
||||
}
|
||||
|
||||
compliancePortal, _, err := prb.Update(ctx, scope, updateReq)
|
||||
|
||||
@@ -8832,7 +8832,7 @@ components:
|
||||
- organization_id
|
||||
- active
|
||||
- search_engine_indexing
|
||||
- title
|
||||
- entity_name
|
||||
- created_at
|
||||
- updated_at
|
||||
properties:
|
||||
@@ -8870,9 +8870,9 @@ components:
|
||||
- string
|
||||
- "null"
|
||||
description: Compliance page headquarter address
|
||||
title:
|
||||
entity_name:
|
||||
type: string
|
||||
description: Public compliance page title
|
||||
description: Entity name shown on the public compliance page
|
||||
created_at:
|
||||
type: string
|
||||
format: date-time
|
||||
@@ -9079,11 +9079,11 @@ components:
|
||||
- "null"
|
||||
description: Compliance page headquarter address
|
||||
go.probo.inc/mcpgen/omittable: true
|
||||
title:
|
||||
entity_name:
|
||||
type:
|
||||
- string
|
||||
- "null"
|
||||
description: Public compliance page title
|
||||
description: Entity name shown on the public compliance page
|
||||
go.probo.inc/mcpgen/omittable: true
|
||||
|
||||
UpdateCompliancePortalOutput:
|
||||
|
||||
@@ -31,7 +31,7 @@ func NewCompliancePortal(tc *coredata.CompliancePortal) *CompliancePortal {
|
||||
OrganizationID: tc.OrganizationID,
|
||||
Active: tc.Active,
|
||||
SearchEngineIndexing: tc.SearchEngineIndexing,
|
||||
Title: tc.Title,
|
||||
EntityName: tc.EntityName,
|
||||
Description: tc.Description,
|
||||
WebsiteURL: tc.WebsiteURL,
|
||||
Email: tc.Email,
|
||||
|
||||
Reference in New Issue
Block a user