Skip to content

Commit

Permalink
Merge main into next
Browse files Browse the repository at this point in the history
  • Loading branch information
github-actions committed Feb 18, 2025
2 parents e943b3e + 6b75f20 commit 5676343
Show file tree
Hide file tree
Showing 64 changed files with 1,252 additions and 87 deletions.
7 changes: 7 additions & 0 deletions .changeset/large-moons-kiss.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
"@comet/admin": minor
---

Deprecate `density` prop of `DataGridToolbar`

The density setting of the surrounding Data Grid now controls the styling of the toolbar.
5 changes: 5 additions & 0 deletions .changeset/little-avocados-nail.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@comet/cms-api": minor
---

Allow passing a language to `generateAltText` and `generateImageTitle`
17 changes: 17 additions & 0 deletions .changeset/proud-panthers-buy.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
---
"@comet/blocks-admin": minor
"@comet/admin-rte": minor
"@comet/cms-admin": minor
"@comet/cms-api": minor
---

Add content generation capabilities to `createSeoBlock`

The SEO block (when created using the `createSeoBlock` factory) now supports automatic generation of:

- HTML title
- Meta description
- Open Graph title
- Open Graph description

See the [docs](https://docs.comet-dxp.com/docs/features-modules/content-generation/) for instructions on enabling this feature.
15 changes: 15 additions & 0 deletions .changeset/quiet-glasses-tan.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
---
"@comet/blocks-admin": minor
"@comet/admin-rte": minor
"@comet/cms-admin": minor
"@comet/cms-api": minor
---

Add `extractTextContents` method to blocks

`extractTextContents` can be used to extract plain text from blocks. This functionality is particularly useful for operations such as search indexing or using the content for LLM-based tasks. The option `includeInvisibleContent` can be set to include the content of invisible blocks in the extracted text.

The method is optional for now, but it is recommended to implement it for all blocks and documents. The default behavior is to return

- if the state is a string: the string itself
- otherwise: an empty array
3 changes: 2 additions & 1 deletion demo/admin/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import {
import { css, Global } from "@emotion/react";
import { createApolloClient } from "@src/common/apollo/createApolloClient";
import { ConfigProvider, createConfig } from "@src/config";
import { ContentScope } from "@src/site-configs";
import { theme } from "@src/theme";
import { HTML5toTouch } from "rdndmb-html5-to-touch";
import { DndProvider } from "react-dnd-multi-backend";
Expand Down Expand Up @@ -99,7 +100,7 @@ export function App() {
}}
>
<IntlProvider locale="en" messages={getMessages()}>
<LocaleProvider resolveLocaleForScope={(scope) => scope.domain}>
<LocaleProvider resolveLocaleForScope={(scope: ContentScope) => scope.language}>
<MuiThemeProvider theme={theme}>
<DndProvider options={HTML5toTouch}>
<SnackbarProvider>
Expand Down
1 change: 1 addition & 0 deletions demo/admin/src/common/blocks/LayoutBlock.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,7 @@ export const LayoutBlock = createCompositeBlock(
<Field name="layout" component={FinalFormLayoutSelect} layouts={layoutOptions} fullWidth />
</BlocksFinalForm>
),
extractTextContents: () => [],
}),
title: <FormattedMessage id="layoutBlock.layout" defaultMessage="Layout" />,
},
Expand Down
103 changes: 103 additions & 0 deletions demo/admin/src/documents/pages/EditPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
BlockAdminComponentRoot,
BlockAdminTabLabel,
BlockPreviewWithTabs,
ContentGenerationConfigProvider,
ContentScopeIndicator,
createUsePage,
openSitePreviewWindow,
Expand Down Expand Up @@ -119,6 +120,7 @@ export const EditPage = ({ id }: Props) => {

return (
<AzureAiTranslatorProvider showApplyTranslationDialog={true} enabled={true}>
<<<<<<< HEAD
{hasChanges && (
<RouterPrompt
message={(location) => {
Expand Down Expand Up @@ -208,6 +210,107 @@ export const EditPage = ({ id }: Props) => {
</BlockPreviewWithTabs>
</MainContent>
{dialogs}
=======
<ContentGenerationConfigProvider
seo={{
getRelevantContent: () => {
if (!pageState || !pageState.document) return [];

return PageContentBlock.extractTextContents?.(pageState.document.content, { includeInvisibleContent: false }) ?? [];
},
}}
>
{hasChanges && (
<RouterPrompt
message={(location) => {
if (location.pathname.startsWith(match.url)) return true; //we navigated within our self
return intl.formatMessage({
id: "editPage.discardChanges",
defaultMessage: "Discard unsaved changes?",
});
}}
saveAction={async () => {
try {
await handleSavePage();
return true;
} catch {
return false;
}
}}
/>
)}
<Toolbar scopeIndicator={<ContentScopeIndicator />}>
<ToolbarItem>
<IconButton onClick={stackApi?.goBack} size="large">
<ArrowLeft />
</IconButton>
</ToolbarItem>
<PageName pageId={id} />
<FillSpace />
<ToolbarActions>
<Stack direction="row" spacing={1}>
<Button
startIcon={<Preview />}
variant="textDark"
disabled={!pageState}
onClick={() => {
openSitePreviewWindow(pageState.path, contentScopeMatch.url);
}}
>
<FormattedMessage id="pages.pages.page.edit.preview" defaultMessage="Web preview" />
</Button>
{pageSaveButton}
</Stack>
</ToolbarActions>
</Toolbar>
<MainContent disablePaddingBottom>
<BlockPreviewWithTabs previewUrl={previewUrl} previewState={previewState} previewApi={previewApi}>
{[
{
key: "content",
label: (
<AdminTabLabel isValid={rootBlocksApi.content.isValid}>
<FormattedMessage id="generic.blocks" defaultMessage="Blocks" />
</AdminTabLabel>
),
content: (
<AdminComponentRoot
title={intl.formatMessage({ id: "pages.pages.page.edit.pageBlocks.title", defaultMessage: "Page" })}
>
{rootBlocksApi.content.adminUI}
</AdminComponentRoot>
),
},
{
key: "stage",
label: (
<AdminTabLabel isValid={rootBlocksApi.stage.isValid}>
<FormattedMessage id="pages.page.edit.stage" defaultMessage="Stage" />
</AdminTabLabel>
),
content: (
<AdminComponentRoot
title={intl.formatMessage({ id: "pages.pages.page.edit.stage.title", defaultMessage: "Stage" })}
>
{rootBlocksApi.stage.adminUI}
</AdminComponentRoot>
),
},
{
key: "config",
label: (
<AdminTabLabel isValid={rootBlocksApi.seo.isValid}>
<FormattedMessage id="pages.pages.page.edit.config" defaultMessage="Config" />
</AdminTabLabel>
),
content: rootBlocksApi.seo.adminUI,
},
]}
</BlockPreviewWithTabs>
</MainContent>
{dialogs}
</ContentGenerationConfigProvider>
>>>>>>> main
</AzureAiTranslatorProvider>
);
};
10 changes: 10 additions & 0 deletions demo/api/block-meta.json
Original file line number Diff line number Diff line change
Expand Up @@ -747,6 +747,16 @@
"name": "scope",
"kind": "Json",
"nullable": true
},
{
"name": "altText",
"kind": "String",
"nullable": true
},
{
"name": "title",
"kind": "String",
"nullable": true
}
]
},
Expand Down
12 changes: 8 additions & 4 deletions demo/api/src/content-generation/content-generation.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,15 @@ import { Injectable } from "@nestjs/common";
export class ContentGenerationService implements ContentGenerationServiceInterface {
constructor(private readonly openAiContentGenerationService: AzureOpenAiContentGenerationService) {}

async generateAltText(fileId: string) {
return this.openAiContentGenerationService.generateAltText(fileId);
async generateAltText(fileId: string, options?: { language: string }) {
return this.openAiContentGenerationService.generateAltText(fileId, options);
}

async generateImageTitle(fileId: string) {
return this.openAiContentGenerationService.generateImageTitle(fileId);
async generateImageTitle(fileId: string, options?: { language: string }) {
return this.openAiContentGenerationService.generateImageTitle(fileId, options);
}

async generateSeoTags(content: string, options: { language: string }) {
return this.openAiContentGenerationService.generateSeoTags(content, options);
}
}
5 changes: 5 additions & 0 deletions demo/site/cache-handler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -164,4 +164,9 @@ export default class CacheHandler {
}
fallbackCache.set(key, value, { size: stringData.length });
}

async revalidateTag(tags: string | string[]): Promise<void> {
if (tags.length === 0) return;
console.warn("CacheHandler.revalidateTag", tags);
}
}
28 changes: 28 additions & 0 deletions docs/docs/2-core-concepts/7-dependencies/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,34 @@ You must recreate the block index views after
You can automate this process by following the steps in the [migration guide](/docs/migration-guide/migration-from-v5-to-v6/#block-index).
For new projects, it should already be automated.

### Providing dependency information

If your block depends on certain entities, you should provide dependency information.
You can do that using the `indexData()` method in your block data class like this:

```ts
class MyCustomBlockData extends BlockData {
// ...

indexData(): BlockIndexData {
if (this.damFileId === undefined) {
return {};
}

return {
dependencies: [
{
targetEntityName: DamFile.name,
id: this.damFileId,
},
],
};
}
}
```

This works for all entities - not just `DamFile`.

### Displaying dependencies in the admin interface

Next, you probably want to display the dependencies or dependents (usages) of an entity in the admin interface.
Expand Down
Loading

0 comments on commit 5676343

Please sign in to comment.