Skip to content

Commit e7cf869

Browse files
wip inherited access
1 parent 36e4365 commit e7cf869

File tree

17 files changed

+411
-83
lines changed

17 files changed

+411
-83
lines changed

Makefile

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -93,8 +93,7 @@ build: cache ?= --no-cache
9393
build: ## build the project containers
9494
@$(MAKE) build-backend cache=$(cache)
9595
@$(MAKE) build-yjs-provider cache=$(cache)
96-
@$(MAKE) build-frontend cache=$(cache)
97-
.PHONY: build
96+
.PHONY: buildD
9897

9998
build-backend: cache ?=
10099
build-backend: ## build the app-dev container
@@ -128,7 +127,6 @@ run-backend: ## Start only the backend application and all needed services
128127
run: ## start the wsgi (production) and development server
129128
run:
130129
@$(MAKE) run-backend
131-
@$(COMPOSE) up --force-recreate -d frontend
132130
.PHONY: run
133131

134132
status: ## an alias for "docker compose ps"

src/backend/core/migrations/0021_remove_document_is_public_and_more.py renamed to src/backend/core/migrations/0022_remove_document_is_public_and_more.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55

66
class Migration(migrations.Migration):
77
dependencies = [
8-
("core", "0020_remove_is_public_add_field_attachments_and_duplicated_from"),
8+
("core", "0021_activate_unaccent_extension"),
99
]
1010

1111
operations = [

src/frontend/apps/impress/src/components/quick-search/QuickSearchStyle.tsx

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -65,8 +65,7 @@ export const QuickSearchStyle = createGlobalStyle`
6565
6666
[cmdk-list] {
6767
68-
padding: 0 var(--c--theme--spacings--base) var(--c--theme--spacings--base)
69-
var(--c--theme--spacings--base);
68+
7069
7170
flex:1;
7271
overflow-y: auto;

src/frontend/apps/impress/src/features/docs/doc-header/components/DocToolBox.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -193,7 +193,7 @@ export const DocToolBox = ({ doc }: DocToolBoxProps) => {
193193
}}
194194
size={isSmallMobile ? 'small' : 'medium'}
195195
style={{
196-
color: colors['primary-800'],
196+
color: colorsTokens['primary-800'],
197197
}}
198198
>
199199
{t('Share')}

src/frontend/apps/impress/src/features/docs/doc-management/types.tsx

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ export interface Access {
55
role: Role;
66
team: string;
77
user: User;
8+
document_id: string;
89
abilities: {
910
destroy: boolean;
1011
partial_update: boolean;
@@ -23,8 +24,8 @@ export enum Role {
2324

2425
export enum LinkReach {
2526
RESTRICTED = 'restricted',
26-
PUBLIC = 'public',
2727
AUTHENTICATED = 'authenticated',
28+
PUBLIC = 'public',
2829
}
2930

3031
export enum LinkRole {
@@ -48,8 +49,13 @@ export interface Doc {
4849
link_role: LinkRole;
4950
nb_accesses_direct: number;
5051
nb_accesses_ancestors: number;
52+
computed_link_reach: LinkReach;
53+
computed_link_role?: LinkRole;
54+
ancestors_link_reach: LinkReach;
55+
ancestors_link_role?: LinkRole;
5156
numchild: number;
5257
updated_at: string;
58+
user_role: Role;
5359
user_roles: Role[];
5460
abilities: {
5561
accesses_manage: boolean;
@@ -73,9 +79,16 @@ export interface Doc {
7379
versions_destroy: boolean;
7480
versions_list: boolean;
7581
versions_retrieve: boolean;
82+
link_select_options: LinkSelectOption;
7683
};
7784
}
7885

86+
export interface LinkSelectOption {
87+
public?: LinkRole[];
88+
authenticated?: LinkRole[];
89+
restricted?: LinkRole[];
90+
}
91+
7992
export enum DocDefaultFilter {
8093
ALL_DOCS = 'all_docs',
8194
MY_DOCS = 'my_docs',

src/frontend/apps/impress/src/features/docs/doc-share/api/useDocAccesses.tsx

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ export type DocAccessesParam = {
1616
};
1717

1818
export type DocAccessesAPIParams = DocAccessesParam & {
19-
page: number;
19+
page?: number;
2020
};
2121

2222
type AccessesResponse = APIList<Access>;
@@ -25,7 +25,7 @@ export const getDocAccesses = async ({
2525
page,
2626
docId,
2727
ordering,
28-
}: DocAccessesAPIParams): Promise<AccessesResponse> => {
28+
}: DocAccessesAPIParams): Promise<Access[]> => {
2929
let url = `documents/${docId}/accesses/?page=${page}`;
3030

3131
if (ordering) {
@@ -41,16 +41,16 @@ export const getDocAccesses = async ({
4141
);
4242
}
4343

44-
return response.json() as Promise<AccessesResponse>;
44+
return (await response.json()) as Access[];
4545
};
4646

4747
export const KEY_LIST_DOC_ACCESSES = 'docs-accesses';
4848

4949
export function useDocAccesses(
5050
params: DocAccessesAPIParams,
51-
queryConfig?: UseQueryOptions<AccessesResponse, APIError, AccessesResponse>,
51+
queryConfig?: UseQueryOptions<Access[], APIError, Access[]>,
5252
) {
53-
return useQuery<AccessesResponse, APIError, AccessesResponse>({
53+
return useQuery<Access[], APIError, Access[]>({
5454
queryKey: [KEY_LIST_DOC_ACCESSES, params],
5555
queryFn: () => getDocAccesses(params),
5656
...queryConfig,
Lines changed: 5 additions & 0 deletions
Loading
Lines changed: 5 additions & 0 deletions
Loading
Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
import { Button, Modal, ModalSize, useModal } from '@openfun/cunningham-react';
2+
import { useTranslation } from 'react-i18next';
3+
import { createGlobalStyle } from 'styled-components';
4+
5+
import { Box, Text } from '@/components';
6+
import { useCunninghamTheme } from '@/cunningham';
7+
8+
import { Access, useDoc } from '../../doc-management';
9+
import SimpleFileIcon from '../../docs-grid/assets/simple-document.svg';
10+
11+
import { DocShareMemberItem } from './DocShareMemberItem';
12+
const ShareModalStyle = createGlobalStyle`
13+
.c__modal__title {
14+
padding-bottom: 0 !important;
15+
}
16+
.c__modal__scroller {
17+
padding: 15px 15px !important;
18+
}
19+
`;
20+
21+
type Props = {
22+
accesses: Map<string, Access[]>;
23+
};
24+
25+
export const DocInheritedShareContent = ({ accesses }: Props) => {
26+
const { t } = useTranslation();
27+
const { spacingsTokens } = useCunninghamTheme();
28+
29+
// Check if accesses map is empty
30+
const hasAccesses = accesses.size > 0;
31+
32+
if (!hasAccesses) {
33+
return null;
34+
}
35+
36+
return (
37+
<Box $gap={spacingsTokens.sm}>
38+
<Box
39+
$gap={spacingsTokens.sm}
40+
$padding={{
41+
horizontal: spacingsTokens.base,
42+
vertical: spacingsTokens.sm,
43+
bottom: '0px',
44+
}}
45+
>
46+
<Text $variation="1000" $weight="bold" $size="sm">
47+
{t('Inherited share')}
48+
</Text>
49+
50+
{Array.from(accesses.keys()).map((documentId) => (
51+
<DocInheritedShareContentItem
52+
key={documentId}
53+
accesses={accesses.get(documentId) ?? []}
54+
document_id={documentId}
55+
/>
56+
))}
57+
</Box>
58+
</Box>
59+
);
60+
};
61+
62+
type DocInheritedShareContentItemProps = {
63+
accesses: Access[];
64+
document_id: string;
65+
};
66+
export const DocInheritedShareContentItem = ({
67+
accesses,
68+
document_id,
69+
}: DocInheritedShareContentItemProps) => {
70+
const { t } = useTranslation();
71+
const { spacingsTokens } = useCunninghamTheme();
72+
const { data: doc } = useDoc({ id: document_id });
73+
const accessModal = useModal();
74+
75+
if (!doc) {
76+
return null;
77+
}
78+
79+
return (
80+
<>
81+
<Box
82+
$gap={spacingsTokens.sm}
83+
$width="100%"
84+
$direction="row"
85+
$align="center"
86+
$justify="space-between"
87+
>
88+
<Box $direction="row" $align="center" $gap={spacingsTokens.sm}>
89+
<SimpleFileIcon />
90+
<Box>
91+
<Text $variation="1000" $weight="bold" $size="sm">
92+
{doc.title ?? t('Untitled document')}
93+
</Text>
94+
<Text $variation="600" $weight="400" $size="xs">
95+
{t('Members of this page have access')}
96+
</Text>
97+
</Box>
98+
</Box>
99+
<Button color="primary-text" size="small" onClick={accessModal.open}>
100+
{t('See access')}
101+
</Button>
102+
</Box>
103+
{accessModal.isOpen && (
104+
<Modal
105+
isOpen
106+
closeOnClickOutside
107+
onClose={accessModal.close}
108+
title={
109+
<Box $align="flex-start">
110+
<Text $variation="1000" $weight="bold" $size="sm">
111+
{t('Access inherited from the parent page')}
112+
</Text>
113+
</Box>
114+
}
115+
size={ModalSize.MEDIUM}
116+
>
117+
<ShareModalStyle />
118+
<Box $padding={{ top: spacingsTokens.sm }}>
119+
{accesses.map((access) => (
120+
<DocShareMemberItem
121+
key={access.id}
122+
doc={doc}
123+
access={access}
124+
isInherited
125+
/>
126+
))}
127+
</Box>
128+
</Modal>
129+
)}
130+
</>
131+
);
132+
};

src/frontend/apps/impress/src/features/docs/doc-share/components/DocShareMemberItem.tsx

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,13 @@ import { SearchUserRow } from './SearchUserRow';
2121
type Props = {
2222
doc: Doc;
2323
access: Access;
24+
isInherited?: boolean;
2425
};
25-
export const DocShareMemberItem = ({ doc, access }: Props) => {
26+
export const DocShareMemberItem = ({
27+
doc,
28+
access,
29+
isInherited = false,
30+
}: Props) => {
2631
const { t } = useTranslation();
2732
const queryClient = useQueryClient();
2833
const { isLastOwner, isOtherOwner } = useWhoAmI(access);
@@ -84,6 +89,8 @@ export const DocShareMemberItem = ({ doc, access }: Props) => {
8489
},
8590
];
8691

92+
const canUpdate = isInherited ? false : doc.abilities.accesses_manage;
93+
8794
return (
8895
<Box
8996
$width="100%"
@@ -98,12 +105,12 @@ export const DocShareMemberItem = ({ doc, access }: Props) => {
98105
<DocRoleDropdown
99106
currentRole={access.role}
100107
onSelectRole={onUpdate}
101-
canUpdate={doc.abilities.accesses_manage}
108+
canUpdate={canUpdate}
102109
message={message}
103110
rolesAllowed={access.abilities.set_role_to}
104111
/>
105112

106-
{isDesktop && doc.abilities.accesses_manage && (
113+
{isDesktop && canUpdate && (
107114
<DropdownMenu options={moreActions}>
108115
<IconOptions
109116
isHorizontal

0 commit comments

Comments
 (0)