Skip to content

Commit e1c6a5a

Browse files
authored
fix(alt-text): respect update access in the admin UI (#161)
1 parent 593f791 commit e1c6a5a

5 files changed

Lines changed: 23 additions & 4 deletions

File tree

alt-text/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
- fix: enforce collection access control in the generate and bulk-generate endpoints by running the Local API reads and writes under the requesting user (`overrideAccess: false`)
66
- fix: filter the alt text health report (endpoint and dashboard widget) to the collections the requesting user may read, so the aggregate no longer discloses counts and document IDs for collections their role cannot access
77
- feat: `healthCheck` now accepts an access function that gates the health endpoint and hides the dashboard widget, letting the collection-wide report be restricted (e.g. to admins) separately from the generate endpoints
8+
- fix: respect update access in the admin UI — render the alt text field read-only and hide the single-document and bulk generate buttons for users without update access
89

910
## 0.7.0
1011

alt-text/dev/src/collections/Media.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,11 @@ export const Media: CollectionConfig = {
99
upload: {
1010
mimeTypes: ['image/*', 'video/*'],
1111
},
12+
access: {
13+
// change the email in the next line to test if the 'generate alt text' buttons are correctly hidden
14+
// and the alt text field is readonly
15+
update: ({ req }) => req.user?.email === 'dev@payloadcms.com',
16+
},
1217
fields: [
1318
// The plugin will automatically inject context, alt, and keywords fields
1419

alt-text/src/components/AltTextField.tsx

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import { matchesMimeType } from '../utilities/mimeTypes.js'
88
import { GenerateAltTextButton } from './GenerateAltTextButton.js'
99

1010
export const AltTextField = (clientProps: TextareaFieldClientProps) => {
11-
const { field, path } = clientProps
11+
const { field, path, readOnly } = clientProps
1212

1313
const supportedMimeTypes = field.admin?.custom?.supportedMimeTypes as string[] | undefined
1414
const trackedMimeTypes = field.admin?.custom?.trackedMimeTypes as string[] | undefined
@@ -41,9 +41,12 @@ export const AltTextField = (clientProps: TextareaFieldClientProps) => {
4141

4242
<div className="field-type__wrap">
4343
<TextareaInput
44-
AfterInput={<GenerateAltTextButton supportedMimeTypes={supportedMimeTypes} />}
44+
AfterInput={
45+
readOnly ? undefined : <GenerateAltTextButton supportedMimeTypes={supportedMimeTypes} />
46+
}
4547
onChange={(e: React.ChangeEvent<HTMLTextAreaElement>) => setValue(e.target.value)}
4648
path={path}
49+
readOnly={readOnly}
4750
required={required}
4851
value={value}
4952
/>

alt-text/src/components/BulkGenerateAltTextsButton.tsx

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
'use client'
22

3-
import { Button, toast, useConfig, useSelection, useTranslation } from '@payloadcms/ui'
3+
import { Button, toast, useAuth, useConfig, useSelection, useTranslation } from '@payloadcms/ui'
44
import { useRouter } from 'next/navigation.js'
55
import { useTransition } from 'react'
66

@@ -15,7 +15,10 @@ import { Spinner } from './icons/Spinner.js'
1515
export function BulkGenerateAltTextsButton({ collectionSlug }: { collectionSlug: string }) {
1616
const { t } = useTranslation<PluginAltTextTranslations, PluginAltTextTranslationKeys>()
1717
const [isPending, startTransition] = useTransition()
18+
const { permissions } = useAuth()
1819
const { selected, setSelection } = useSelection()
20+
21+
const canUpdateCollection = Boolean(permissions?.collections?.[collectionSlug]?.update)
1922
const {
2023
config: {
2124
routes: { api: apiRoute },
@@ -97,6 +100,7 @@ export function BulkGenerateAltTextsButton({ collectionSlug }: { collectionSlug:
97100
}
98101

99102
return (
103+
canUpdateCollection &&
100104
selectedIds.length > 0 && (
101105
<div className="m-0" style={{ display: 'flex', justifyContent: 'right' }}>
102106
<Button

alt-text/src/components/GenerateAltTextButton.tsx

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ import { Spinner } from './icons/Spinner.js'
2121

2222
export function GenerateAltTextButton({ supportedMimeTypes }: { supportedMimeTypes?: string[] }) {
2323
const { t } = useTranslation<PluginAltTextTranslations, PluginAltTextTranslationKeys>()
24-
const { id, collectionSlug } = useDocumentInfo()
24+
const { id, collectionSlug, docPermissions } = useDocumentInfo()
2525
const locale = useLocale()
2626
const [isPending, startTransition] = useTransition()
2727
const {
@@ -38,6 +38,12 @@ export function GenerateAltTextButton({ supportedMimeTypes }: { supportedMimeTyp
3838
const isUnsupportedMimeType =
3939
!!mimeType && !!supportedMimeTypes && !supportedMimeTypes.includes(mimeType)
4040

41+
// Hide the generate button from users who cannot update the document — the
42+
// generated alt text would not be persistable by them anyway.
43+
if (!docPermissions?.update) {
44+
return null
45+
}
46+
4147
const handleGenerateAltText = () => {
4248
if (!collectionSlug || !id) {
4349
toast.error(t('@jhb.software/payload-alt-text-plugin:cannotGenerateMissingFields'))

0 commit comments

Comments
 (0)