🛂(frontend) blocked edition if multiple ancestors

With child documents we need to check the parent
documents to know if the parent doc are collaborative
or not.
This commit is contained in:
Anthony LC
2025-07-09 12:41:10 +02:00
parent 1110ec92d5
commit 8a3dfe0252
3 changed files with 48 additions and 32 deletions

View File

@@ -10,6 +10,7 @@ import {
overrideConfig, overrideConfig,
verifyDocName, verifyDocName,
} from './common'; } from './common';
import { createRootSubPage } from './sub-pages-utils';
test.beforeEach(async ({ page }) => { test.beforeEach(async ({ page }) => {
await page.goto('/'); await page.goto('/');
@@ -524,6 +525,8 @@ test.describe('Doc Editor', () => {
page, page,
browserName, browserName,
}) => { }) => {
test.slow();
/** /**
* The good port is 4444, but we want to simulate a not connected * The good port is 4444, but we want to simulate a not connected
* collaborative server. * collaborative server.
@@ -536,7 +539,12 @@ test.describe('Doc Editor', () => {
await page.goto('/'); await page.goto('/');
const [title] = await createDoc(page, 'editing-blocking', browserName, 1); const [parentTitle] = await createDoc(
page,
'editing-blocking',
browserName,
1,
);
const card = page.getByLabel('It is the card information'); const card = page.getByLabel('It is the card information');
await expect( await expect(
@@ -571,12 +579,20 @@ test.describe('Doc Editor', () => {
// Close the modal // Close the modal
await page.getByRole('button', { name: 'close' }).first().click(); await page.getByRole('button', { name: 'close' }).first().click();
const urlParentDoc = page.url();
const { name: childTitle } = await createRootSubPage(
page,
browserName,
'editing-blocking - child',
);
let responseCanEdit = await responseCanEditPromise; let responseCanEdit = await responseCanEditPromise;
expect(responseCanEdit.ok()).toBeTruthy(); expect(responseCanEdit.ok()).toBeTruthy();
let jsonCanEdit = (await responseCanEdit.json()) as { can_edit: boolean }; let jsonCanEdit = (await responseCanEdit.json()) as { can_edit: boolean };
expect(jsonCanEdit.can_edit).toBeTruthy(); expect(jsonCanEdit.can_edit).toBeTruthy();
const urlDoc = page.url(); const urlChildDoc = page.url();
/** /**
* We open another browser that will connect to the collaborative server * We open another browser that will connect to the collaborative server
@@ -603,14 +619,14 @@ test.describe('Doc Editor', () => {
}, },
); );
await otherPage.goto(urlDoc); await otherPage.goto(urlChildDoc);
const webSocket = await webSocketPromise; const webSocket = await webSocketPromise;
expect(webSocket.url()).toContain( expect(webSocket.url()).toContain(
'ws://localhost:4444/collaboration/ws/?room=', 'ws://localhost:4444/collaboration/ws/?room=',
); );
await verifyDocName(otherPage, title); await verifyDocName(otherPage, childTitle);
await page.reload(); await page.reload();
@@ -633,6 +649,10 @@ test.describe('Doc Editor', () => {
await expect(editor).toHaveAttribute('contenteditable', 'false'); await expect(editor).toHaveAttribute('contenteditable', 'false');
await page.goto(urlParentDoc);
await verifyDocName(page, parentTitle);
await page.getByRole('button', { name: 'Share' }).click(); await page.getByRole('button', { name: 'Share' }).click();
await page.getByLabel('Visibility mode').click(); await page.getByLabel('Visibility mode').click();
@@ -641,18 +661,9 @@ test.describe('Doc Editor', () => {
// Close the modal // Close the modal
await page.getByRole('button', { name: 'close' }).first().click(); await page.getByRole('button', { name: 'close' }).first().click();
await page.reload(); await page.goto(urlChildDoc);
responseCanEditPromise = page.waitForResponse( await expect(editor).toHaveAttribute('contenteditable', 'true');
(response) =>
response.url().includes(`/can-edit/`) && response.status() === 200,
);
responseCanEdit = await responseCanEditPromise;
expect(responseCanEdit.ok()).toBeTruthy();
jsonCanEdit = (await responseCanEdit.json()) as { can_edit: boolean };
expect(jsonCanEdit.can_edit).toBeTruthy();
await expect( await expect(
card.getByText('Others are editing. Your network prevent changes.'), card.getByText('Others are editing. Your network prevent changes.'),

View File

@@ -441,7 +441,7 @@ test.describe('Doc Visibility: Authenticated', () => {
const { name: childTitle } = await createRootSubPage( const { name: childTitle } = await createRootSubPage(
page, page,
browserName, browserName,
'Authenticated read onlyc - child', 'Authenticated read only - child',
); );
const urlChildDoc = page.url(); const urlChildDoc = page.url();

View File

@@ -5,15 +5,20 @@ import { useIsOffline } from '@/features/service-worker';
import { KEY_CAN_EDIT, useDocCanEdit } from '../api/useDocCanEdit'; import { KEY_CAN_EDIT, useDocCanEdit } from '../api/useDocCanEdit';
import { useProviderStore } from '../stores'; import { useProviderStore } from '../stores';
import { Doc, LinkReach } from '../types'; import { Doc, LinkReach, LinkRole } from '../types';
export const useIsCollaborativeEditable = (doc: Doc) => { export const useIsCollaborativeEditable = (doc: Doc) => {
const { isConnected } = useProviderStore(); const { isConnected } = useProviderStore();
const { data: conf } = useConfig(); const { data: conf } = useConfig();
const docIsPublic = doc.link_reach === LinkReach.PUBLIC; const docIsPublic =
const docIsAuth = doc.link_reach === LinkReach.AUTHENTICATED; doc.computed_link_reach === LinkReach.PUBLIC &&
const docHasMember = doc.nb_accesses_direct > 1; doc.computed_link_role === LinkRole.EDITOR;
const docIsAuth =
doc.computed_link_reach === LinkReach.AUTHENTICATED &&
doc.computed_link_role === LinkRole.EDITOR;
const docHasMember =
doc.nb_accesses_direct > 1 || doc.nb_accesses_ancestors > 1;
const isUserReader = !doc.abilities.partial_update; const isUserReader = !doc.abilities.partial_update;
const isShared = docIsPublic || docIsAuth || docHasMember; const isShared = docIsPublic || docIsAuth || docHasMember;
const { isOffline } = useIsOffline(); const { isOffline } = useIsOffline();
@@ -21,23 +26,23 @@ export const useIsCollaborativeEditable = (doc: Doc) => {
const [isEditable, setIsEditable] = useState(true); const [isEditable, setIsEditable] = useState(true);
const [isLoading, setIsLoading] = useState(!_isEditable); const [isLoading, setIsLoading] = useState(!_isEditable);
const timeout = useRef<NodeJS.Timeout | null>(null); const timeout = useRef<NodeJS.Timeout | null>(null);
const { const { data: editingRight, isLoading: isLoadingCanEdit } = useDocCanEdit(
data: { can_edit } = { can_edit: _isEditable }, doc.id,
isLoading: isLoadingCanEdit, {
} = useDocCanEdit(doc.id, { enabled: !_isEditable,
enabled: !_isEditable, queryKey: [KEY_CAN_EDIT, doc.id],
queryKey: [KEY_CAN_EDIT, doc.id], staleTime: 0,
staleTime: 0, },
}); );
useEffect(() => { useEffect(() => {
if (isLoadingCanEdit) { if (isLoadingCanEdit || _isEditable || !editingRight) {
return; return;
} }
// Connection to the WebSocket can take some time, so we set a timeout to ensure the loading state is cleared after a reasonable time. // Connection to the WebSocket can take some time, so we set a timeout to ensure the loading state is cleared after a reasonable time.
timeout.current = setTimeout(() => { timeout.current = setTimeout(() => {
setIsEditable(can_edit); setIsEditable(editingRight.can_edit);
setIsLoading(false); setIsLoading(false);
}, 1500); }, 1500);
@@ -46,7 +51,7 @@ export const useIsCollaborativeEditable = (doc: Doc) => {
clearTimeout(timeout.current); clearTimeout(timeout.current);
} }
}; };
}, [can_edit, isLoadingCanEdit]); }, [editingRight, isLoadingCanEdit, _isEditable]);
useEffect(() => { useEffect(() => {
if (!_isEditable) { if (!_isEditable) {
@@ -59,7 +64,7 @@ export const useIsCollaborativeEditable = (doc: Doc) => {
setIsEditable(true); setIsEditable(true);
setIsLoading(false); setIsLoading(false);
}, [_isEditable, isLoading]); }, [_isEditable]);
if (!conf?.COLLABORATION_WS_NOT_CONNECTED_READY_ONLY) { if (!conf?.COLLABORATION_WS_NOT_CONNECTED_READY_ONLY) {
return { return {