diff --git a/src/frontend/apps/e2e/__tests__/app-impress/doc-editor.spec.ts b/src/frontend/apps/e2e/__tests__/app-impress/doc-editor.spec.ts
index 2f572944..fa67c658 100644
--- a/src/frontend/apps/e2e/__tests__/app-impress/doc-editor.spec.ts
+++ b/src/frontend/apps/e2e/__tests__/app-impress/doc-editor.spec.ts
@@ -696,6 +696,53 @@ test.describe('Doc Editor', () => {
});
});
+ test(`it checks ai_proxy ability`, async ({ page, browserName }) => {
+ await mockedDocument(page, {
+ accesses: [
+ {
+ id: 'b0df4343-c8bd-4c20-9ff6-fbf94fc94egg',
+ role: 'owner',
+ user: {
+ email: 'super@owner.com',
+ full_name: 'Super Owner',
+ },
+ },
+ ],
+ abilities: {
+ destroy: true, // Means owner
+ link_configuration: true,
+ ai_proxy: false,
+ accesses_manage: true,
+ accesses_view: true,
+ update: true,
+ partial_update: true,
+ retrieve: true,
+ },
+ link_reach: 'restricted',
+ link_role: 'editor',
+ created_at: '2021-09-01T09:00:00Z',
+ title: '',
+ });
+
+ const [randomDoc] = await createDoc(
+ page,
+ 'doc-editor-ai-proxy',
+ browserName,
+ 1,
+ );
+
+ await verifyDocName(page, randomDoc);
+
+ await page.locator('.bn-block-outer').last().fill('Hello World');
+
+ const editor = page.locator('.ProseMirror');
+ await editor.getByText('Hello').selectText();
+
+ await expect(page.getByRole('button', { name: 'Ask AI' })).toBeHidden();
+ await page.locator('.bn-block-outer').last().fill('/');
+ await expect(page.getByText('Write with AI')).toBeHidden();
+ });
+
test('it downloads unsafe files', async ({ page, browserName }) => {
const [randomDoc] = await createDoc(page, 'doc-editor', browserName, 1);
diff --git a/src/frontend/apps/impress/src/features/docs/doc-editor/components/AI/useAI.tsx b/src/frontend/apps/impress/src/features/docs/doc-editor/components/AI/useAI.tsx
index a43f4f6a..607b3d65 100644
--- a/src/frontend/apps/impress/src/features/docs/doc-editor/components/AI/useAI.tsx
+++ b/src/frontend/apps/impress/src/features/docs/doc-editor/components/AI/useAI.tsx
@@ -6,10 +6,14 @@ import { fetchAPI } from '@/api';
import { useConfig } from '@/core';
import { Doc } from '@/docs/doc-management';
-export const useAI = (docId: Doc['id']) => {
+export const useAI = (docId: Doc['id'], aiAllowed: boolean) => {
const conf = useConfig().data;
return useMemo(() => {
+ if (!aiAllowed) {
+ return null;
+ }
+
const extension = AIExtension({
transport: new DefaultChatTransport({
fetch: (input, init) => {
@@ -27,5 +31,5 @@ export const useAI = (docId: Doc['id']) => {
});
return extension;
- }, [conf?.AI_BOT, docId]);
+ }, [conf?.AI_BOT, docId, aiAllowed]);
};
diff --git a/src/frontend/apps/impress/src/features/docs/doc-editor/components/BlockNoteEditor.tsx b/src/frontend/apps/impress/src/features/docs/doc-editor/components/BlockNoteEditor.tsx
index d072e3ea..28731c2d 100644
--- a/src/frontend/apps/impress/src/features/docs/doc-editor/components/BlockNoteEditor.tsx
+++ b/src/frontend/apps/impress/src/features/docs/doc-editor/components/BlockNoteEditor.tsx
@@ -20,6 +20,7 @@ import type { Awareness } from 'y-protocols/awareness';
import * as Y from 'yjs';
import { Box, TextErrors } from '@/components';
+import { useConfig } from '@/core';
import { useCunninghamTheme } from '@/cunningham';
import { Doc, useProviderStore } from '@/docs/doc-management';
import { avatarUrlFromName, useAuth } from '@/features/auth';
@@ -46,7 +47,6 @@ import {
PdfBlock,
UploadLoaderBlock,
} from './custom-blocks';
-
const AIMenu = BlockNoteAI?.AIMenu;
const AIMenuController = BlockNoteAI?.AIMenuController;
const useAI = BlockNoteAI?.useAI;
@@ -104,7 +104,9 @@ export const BlockNoteEditor = ({ doc, provider }: BlockNoteEditorProps) => {
}
const { uploadFile, errorAttachment } = useUploadFile(doc.id);
- const aiExtension = useAI?.(doc.id);
+ const conf = useConfig().data;
+ const aiAllowed = !!(conf?.AI_FEATURE_ENABLED && doc.abilities?.ai_proxy);
+ const aiExtension = useAI?.(doc.id, aiAllowed);
const collabName = user?.full_name || user?.email;
const cursorName = collabName || t('Anonymous');
@@ -266,11 +268,11 @@ export const BlockNoteEditor = ({ doc, provider }: BlockNoteEditorProps) => {
comments={showComments}
aria-label={t('Document editor')}
>
- {aiExtension && AIMenuController && AIMenu && (
+ {aiAllowed && AIMenuController && AIMenu && (
)}
-
-
+
+
);
@@ -337,7 +339,7 @@ export const BlockNoteReader = ({
slashMenu={false}
comments={false}
>
-
+
);
diff --git a/src/frontend/apps/impress/src/features/docs/doc-editor/components/BlockNoteSuggestionMenu.tsx b/src/frontend/apps/impress/src/features/docs/doc-editor/components/BlockNoteSuggestionMenu.tsx
index 58f414cc..805377f6 100644
--- a/src/frontend/apps/impress/src/features/docs/doc-editor/components/BlockNoteSuggestionMenu.tsx
+++ b/src/frontend/apps/impress/src/features/docs/doc-editor/components/BlockNoteSuggestionMenu.tsx
@@ -11,8 +11,6 @@ import {
import React, { useMemo } from 'react';
import { useTranslation } from 'react-i18next';
-import { useConfig } from '@/core';
-
import {
DocsBlockSchema,
DocsInlineContentSchema,
@@ -32,7 +30,11 @@ const getMultiColumnSlashMenuItems =
const getAISlashMenuItems = BlockNoteAI?.getAISlashMenuItems;
-export const BlockNoteSuggestionMenu = () => {
+export const BlockNoteSuggestionMenu = ({
+ aiAllowed,
+}: {
+ aiAllowed: boolean;
+}) => {
const editor = useBlockNoteEditor<
DocsBlockSchema,
DocsInlineContentSchema,
@@ -44,7 +46,6 @@ export const BlockNoteSuggestionMenu = () => {
const fileBlocksName = dictionaryDate.slash_menu.file.group;
const getInterlinkingMenuItems = useGetInterlinkingMenuItems();
- const { data: conf } = useConfig();
const getSlashMenuItems = useMemo(() => {
// We insert it after the "Code Block" item to have the interlinking block displayed after the basic blocks
@@ -56,9 +57,7 @@ export const BlockNoteSuggestionMenu = () => {
getMultiColumnSlashMenuItems?.(editor) || [],
getPdfReactSlashMenuItems(editor, t, fileBlocksName),
getCalloutReactSlashMenuItems(editor, t, basicBlocksName),
- conf?.AI_FEATURE_ENABLED && getAISlashMenuItems
- ? getAISlashMenuItems(editor)
- : [],
+ aiAllowed && getAISlashMenuItems ? getAISlashMenuItems(editor) : [],
);
const index = combinedMenu.findIndex(
@@ -80,7 +79,7 @@ export const BlockNoteSuggestionMenu = () => {
t,
fileBlocksName,
basicBlocksName,
- conf?.AI_FEATURE_ENABLED,
+ aiAllowed,
getInterlinkingMenuItems,
]);
diff --git a/src/frontend/apps/impress/src/features/docs/doc-editor/components/BlockNoteToolBar/BlockNoteToolbar.tsx b/src/frontend/apps/impress/src/features/docs/doc-editor/components/BlockNoteToolBar/BlockNoteToolbar.tsx
index a3f29bdf..eec8b33a 100644
--- a/src/frontend/apps/impress/src/features/docs/doc-editor/components/BlockNoteToolBar/BlockNoteToolbar.tsx
+++ b/src/frontend/apps/impress/src/features/docs/doc-editor/components/BlockNoteToolBar/BlockNoteToolbar.tsx
@@ -21,7 +21,7 @@ import { ModalConfirmDownloadUnsafe } from './ModalConfirmDownloadUnsafe';
const AIToolbarButton = BlockNoteAI?.AIToolbarButton;
-export const BlockNoteToolbar = () => {
+export const BlockNoteToolbar = ({ aiAllowed }: { aiAllowed: boolean }) => {
const dict = useDictionary();
const [confirmOpen, setIsConfirmOpen] = useState(false);
const [onConfirm, setOnConfirm] = useState<() => void | Promise>();
@@ -72,7 +72,7 @@ export const BlockNoteToolbar = () => {
const formattingToolbar = useCallback(() => {
return (
- {conf?.AI_FEATURE_ENABLED && AIToolbarButton && }
+ {aiAllowed && AIToolbarButton && }
@@ -87,7 +87,7 @@ export const BlockNoteToolbar = () => {
);
- }, [toolbarItems, conf?.AI_FEATURE_ENABLED]);
+ }, [toolbarItems, aiAllowed, conf?.AI_FEATURE_ENABLED]);
return (
<>