Skip to content

Commit 696f42a

Browse files
committed
feat: Add Edit Site Properties form and integrate with site management
1 parent 353801c commit 696f42a

2 files changed

Lines changed: 316 additions & 0 deletions

File tree

Lines changed: 260 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,260 @@
1+
import { useEffect } from 'react'
2+
import { Alert, Divider, Skeleton, Typography } from '@mui/material'
3+
import { Stack } from '@mui/system'
4+
import CippFormComponent from './CippFormComponent'
5+
import { CippFormCondition } from './CippFormCondition'
6+
import { ApiGetCall } from '../../api/ApiCall'
7+
8+
// Option lists for the Edit Site dialog; values match Invoke-ExecSetSiteProperties' whitelist.
9+
const SHARING_CAPABILITY_OPTIONS = [
10+
{ label: 'Disabled — internal only', value: 'Disabled' },
11+
{ label: 'Existing guests only', value: 'ExistingExternalUserSharingOnly' },
12+
{ label: 'New and existing guests (sign-in required)', value: 'ExternalUserSharingOnly' },
13+
{ label: 'Anyone — including anonymous links', value: 'ExternalUserAndGuestSharing' },
14+
]
15+
const LINK_TYPE_OPTIONS = [
16+
{ label: 'Tenant default', value: 'None' },
17+
{ label: 'Specific people (Direct)', value: 'Direct' },
18+
{ label: 'Only people in your organization', value: 'Internal' },
19+
{ label: 'Anyone with the link (Anonymous)', value: 'AnonymousAccess' },
20+
]
21+
const LINK_PERMISSION_OPTIONS = [
22+
{ label: 'Tenant default', value: 'None' },
23+
{ label: 'View', value: 'View' },
24+
{ label: 'Edit', value: 'Edit' },
25+
]
26+
const DOMAIN_MODE_OPTIONS = [
27+
{ label: 'No restriction', value: 'None' },
28+
{ label: 'Allow only specific domains', value: 'AllowList' },
29+
{ label: 'Block specific domains', value: 'BlockList' },
30+
]
31+
const LOCK_STATE_OPTIONS = [
32+
{ label: 'Unlocked', value: 'Unlock' },
33+
{ label: 'Read Only', value: 'ReadOnly' },
34+
{ label: 'No Access (site blocked)', value: 'NoAccess' },
35+
]
36+
37+
// Form body of the Edit Site action. Loads the site's current admin properties and
38+
// prefills the form so submitting without changes is a no-op write of current values.
39+
export const CippEditSitePropertiesForm = ({ formHook, row, tenantFilter }) => {
40+
const siteRow = Array.isArray(row) ? row[0] : row
41+
const propsApi = ApiGetCall({
42+
url: '/api/ListSiteProperties',
43+
data: {
44+
tenantFilter: siteRow?.Tenant ?? tenantFilter,
45+
SiteUrl: siteRow?.webUrl,
46+
},
47+
queryKey: `SiteProperties-${siteRow?.webUrl}`,
48+
})
49+
50+
useEffect(() => {
51+
const p = propsApi.data?.Results
52+
// autoComplete fields hold { label, value } objects; map raw values to their option.
53+
const toOption = (options, value) =>
54+
options.find((o) => o.value === value) ?? (value ? { label: value, value } : null)
55+
if (p && typeof p === 'object' && p.Url) {
56+
formHook.reset({
57+
Title: p.Title ?? '',
58+
SharingCapability: toOption(SHARING_CAPABILITY_OPTIONS, p.SharingCapability),
59+
DefaultSharingLinkType: toOption(LINK_TYPE_OPTIONS, p.DefaultSharingLinkType),
60+
DefaultLinkPermission: toOption(LINK_PERMISSION_OPTIONS, p.DefaultLinkPermission),
61+
SharingDomainRestrictionMode: toOption(
62+
DOMAIN_MODE_OPTIONS,
63+
p.SharingDomainRestrictionMode,
64+
),
65+
SharingAllowedDomainList: p.SharingAllowedDomainList ?? '',
66+
SharingBlockedDomainList: p.SharingBlockedDomainList ?? '',
67+
OverrideTenantAnonymousLinkExpirationPolicy:
68+
!!p.OverrideTenantAnonymousLinkExpirationPolicy,
69+
AnonymousLinkExpirationInDays: p.AnonymousLinkExpirationInDays ?? 0,
70+
LockState: toOption(LOCK_STATE_OPTIONS, p.LockState),
71+
StorageMaximumLevel: p.StorageMaximumLevel,
72+
StorageWarningLevel: p.StorageWarningLevel,
73+
InheritVersionPolicyFromTenant: !!p.InheritVersionPolicyFromTenant,
74+
EnableAutoExpirationVersionTrim: !!p.EnableAutoExpirationVersionTrim,
75+
MajorVersionLimit: p.MajorVersionLimit ?? 0,
76+
ExpireVersionsAfterDays: p.ExpireVersionsAfterDays ?? 0,
77+
})
78+
}
79+
// eslint-disable-next-line react-hooks/exhaustive-deps
80+
}, [propsApi.isSuccess, propsApi.data])
81+
82+
if (propsApi.isFetching) {
83+
return (
84+
<Stack spacing={1}>
85+
<Skeleton variant="rounded" height={40} />
86+
<Skeleton variant="rounded" height={40} />
87+
<Skeleton variant="rounded" height={40} />
88+
</Stack>
89+
)
90+
}
91+
if (propsApi.isError || typeof propsApi.data?.Results === 'string') {
92+
return (
93+
<Alert severity="error">
94+
Failed to load site properties.{' '}
95+
{typeof propsApi.data?.Results === 'string' ? propsApi.data.Results : ''}
96+
</Alert>
97+
)
98+
}
99+
100+
return (
101+
<Stack spacing={1.5}>
102+
<Typography variant="subtitle2">General</Typography>
103+
<CippFormComponent type="textField" name="Title" label="Site Name" formControl={formHook} />
104+
105+
<Divider />
106+
<Typography variant="subtitle2">External Sharing</Typography>
107+
<CippFormComponent
108+
type="autoComplete"
109+
name="SharingCapability"
110+
label="Sharing Capability"
111+
multiple={false}
112+
creatable={false}
113+
options={SHARING_CAPABILITY_OPTIONS}
114+
formControl={formHook}
115+
/>
116+
<CippFormComponent
117+
type="autoComplete"
118+
name="DefaultSharingLinkType"
119+
label="Default Sharing Link Type"
120+
multiple={false}
121+
creatable={false}
122+
options={LINK_TYPE_OPTIONS}
123+
formControl={formHook}
124+
/>
125+
<CippFormComponent
126+
type="autoComplete"
127+
name="DefaultLinkPermission"
128+
label="Default Link Permission"
129+
multiple={false}
130+
creatable={false}
131+
options={LINK_PERMISSION_OPTIONS}
132+
formControl={formHook}
133+
/>
134+
<CippFormComponent
135+
type="autoComplete"
136+
name="SharingDomainRestrictionMode"
137+
label="Domain Restriction"
138+
multiple={false}
139+
creatable={false}
140+
options={DOMAIN_MODE_OPTIONS}
141+
formControl={formHook}
142+
/>
143+
<CippFormCondition
144+
field="SharingDomainRestrictionMode"
145+
compareType="valueEq"
146+
compareValue="AllowList"
147+
formControl={formHook}
148+
>
149+
<CippFormComponent
150+
type="textField"
151+
name="SharingAllowedDomainList"
152+
label="Allowed Domains (space separated)"
153+
formControl={formHook}
154+
/>
155+
</CippFormCondition>
156+
<CippFormCondition
157+
field="SharingDomainRestrictionMode"
158+
compareType="valueEq"
159+
compareValue="BlockList"
160+
formControl={formHook}
161+
>
162+
<CippFormComponent
163+
type="textField"
164+
name="SharingBlockedDomainList"
165+
label="Blocked Domains (space separated)"
166+
formControl={formHook}
167+
/>
168+
</CippFormCondition>
169+
<CippFormComponent
170+
type="switch"
171+
name="OverrideTenantAnonymousLinkExpirationPolicy"
172+
label="Override tenant anonymous link expiration"
173+
formControl={formHook}
174+
/>
175+
<CippFormCondition
176+
field="OverrideTenantAnonymousLinkExpirationPolicy"
177+
compareType="is"
178+
compareValue={true}
179+
formControl={formHook}
180+
>
181+
<CippFormComponent
182+
type="number"
183+
name="AnonymousLinkExpirationInDays"
184+
label="Anonymous Link Expiration (days, 0 = never)"
185+
formControl={formHook}
186+
/>
187+
</CippFormCondition>
188+
189+
<Divider />
190+
<Typography variant="subtitle2">Lock State &amp; Storage</Typography>
191+
<CippFormComponent
192+
type="autoComplete"
193+
name="LockState"
194+
label="Lock State"
195+
multiple={false}
196+
creatable={false}
197+
options={LOCK_STATE_OPTIONS}
198+
formControl={formHook}
199+
/>
200+
<Alert severity="info">
201+
Storage limits only apply when the tenant uses manual site storage limits. Lock state
202+
changes can take a few minutes to fully propagate.
203+
</Alert>
204+
<CippFormComponent
205+
type="number"
206+
name="StorageMaximumLevel"
207+
label="Storage Limit (MB)"
208+
formControl={formHook}
209+
/>
210+
<CippFormComponent
211+
type="number"
212+
name="StorageWarningLevel"
213+
label="Storage Warning Level (MB)"
214+
formControl={formHook}
215+
/>
216+
217+
<Divider />
218+
<Typography variant="subtitle2">File Version Policy</Typography>
219+
<CippFormComponent
220+
type="switch"
221+
name="InheritVersionPolicyFromTenant"
222+
label="Inherit version policy from tenant"
223+
formControl={formHook}
224+
/>
225+
<CippFormCondition
226+
field="InheritVersionPolicyFromTenant"
227+
compareType="is"
228+
compareValue={false}
229+
formControl={formHook}
230+
>
231+
<CippFormComponent
232+
type="switch"
233+
name="EnableAutoExpirationVersionTrim"
234+
label="Automatic version trimming"
235+
formControl={formHook}
236+
/>
237+
<CippFormCondition
238+
field="EnableAutoExpirationVersionTrim"
239+
compareType="is"
240+
compareValue={false}
241+
formControl={formHook}
242+
>
243+
<CippFormComponent
244+
type="number"
245+
name="MajorVersionLimit"
246+
label="Major Version Limit"
247+
formControl={formHook}
248+
/>
249+
<CippFormComponent
250+
type="number"
251+
name="ExpireVersionsAfterDays"
252+
label="Expire Versions After (days, 0 = never)"
253+
formControl={formHook}
254+
/>
255+
</CippFormCondition>
256+
</CippFormCondition>
257+
</Stack>
258+
)
259+
}
260+

src/pages/teams-share/sharepoint/index.js

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import {
1212
CleaningServices,
1313
Assessment,
1414
FolderShared,
15+
Settings,
1516
} from '@mui/icons-material'
1617
import Link from 'next/link'
1718
import { Stack } from '@mui/system'
@@ -22,6 +23,7 @@ import CippFormComponent from '../../../components/CippComponents/CippFormCompon
2223
import { CippFormCondition } from '../../../components/CippComponents/CippFormCondition'
2324
import { CippPropertyList } from '../../../components/CippComponents/CippPropertyList'
2425
import { ApiGetCall } from '../../../api/ApiCall'
26+
import { CippEditSitePropertiesForm } from '../../../components/CippComponents/CippEditSitePropertiesForm'
2527

2628
// Friendly labels for the SharePoint version cleanup (trim) job progress fields.
2729
const VERSION_CLEANUP_LABELS = {
@@ -247,6 +249,60 @@ const Page = () => {
247249
multiPost: false,
248250
allowResubmit: true,
249251
},
252+
{
253+
label: 'Edit Site',
254+
type: 'POST',
255+
icon: <Settings />,
256+
url: '/api/ExecSetSiteProperties',
257+
confirmText:
258+
'Edit site properties for [displayName]. Fields are prefilled with the current values.',
259+
children: ({ formHook, row }) => (
260+
<CippEditSitePropertiesForm formHook={formHook} row={row} tenantFilter={tenantFilter} />
261+
),
262+
customDataformatter: (row, action, formData) => {
263+
const siteRow = Array.isArray(row) ? row[0] : row
264+
const v = (x) => (x && typeof x === 'object' && 'value' in x ? x.value : x)
265+
const payload = {
266+
tenantFilter: siteRow.Tenant ?? tenantFilter,
267+
SiteUrl: siteRow.webUrl,
268+
Title: formData.Title,
269+
SharingCapability: v(formData.SharingCapability),
270+
DefaultSharingLinkType: v(formData.DefaultSharingLinkType),
271+
DefaultLinkPermission: v(formData.DefaultLinkPermission),
272+
SharingDomainRestrictionMode: v(formData.SharingDomainRestrictionMode),
273+
OverrideTenantAnonymousLinkExpirationPolicy:
274+
!!formData.OverrideTenantAnonymousLinkExpirationPolicy,
275+
LockState: v(formData.LockState),
276+
InheritVersionPolicyFromTenant: !!formData.InheritVersionPolicyFromTenant,
277+
}
278+
if (v(formData.SharingDomainRestrictionMode) === 'AllowList') {
279+
payload.SharingAllowedDomainList = formData.SharingAllowedDomainList
280+
}
281+
if (v(formData.SharingDomainRestrictionMode) === 'BlockList') {
282+
payload.SharingBlockedDomainList = formData.SharingBlockedDomainList
283+
}
284+
if (formData.OverrideTenantAnonymousLinkExpirationPolicy) {
285+
payload.AnonymousLinkExpirationInDays = parseInt(
286+
formData.AnonymousLinkExpirationInDays ?? 0,
287+
10
288+
)
289+
}
290+
const storageMax = parseInt(formData.StorageMaximumLevel, 10)
291+
const storageWarn = parseInt(formData.StorageWarningLevel, 10)
292+
if (!isNaN(storageMax) && storageMax > 0) payload.StorageMaximumLevel = storageMax
293+
if (!isNaN(storageWarn) && storageWarn > 0) payload.StorageWarningLevel = storageWarn
294+
if (!formData.InheritVersionPolicyFromTenant) {
295+
payload.EnableAutoExpirationVersionTrim = !!formData.EnableAutoExpirationVersionTrim
296+
if (!formData.EnableAutoExpirationVersionTrim) {
297+
payload.MajorVersionLimit = parseInt(formData.MajorVersionLimit ?? 0, 10)
298+
payload.ExpireVersionsAfterDays = parseInt(formData.ExpireVersionsAfterDays ?? 0, 10)
299+
}
300+
}
301+
return payload
302+
},
303+
multiPost: false,
304+
allowResubmit: true,
305+
},
250306
{
251307
label: 'Add Site Admin',
252308
type: 'POST',

0 commit comments

Comments
 (0)