Skip to content

Commit 5588116

Browse files
authored
Merge pull request #956 from IQSS/938-dataset-terms-modal-should-be-showed-when-download-files-with-custom-terms
Show terms modal before download when dataset has custom terms or non-default license
2 parents 5cacbf6 + 521a4ff commit 5588116

21 files changed

Lines changed: 858 additions & 191 deletions

File tree

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ This changelog follows the principles of [Keep a Changelog](https://keepachangel
1919
- Added runtime configuration options for homepage branding and support link.
2020
- Added an environment variable to docker-compose-dev.yml to hide the OIDC client used in the SPA from the JSF frontend: DATAVERSE_AUTH_OIDC_HIDDEN_JSF: 1
2121
- Download with terms of use and guestbook.
22+
- Show terms modal before download when dataset has custom terms, a non-default license (not CC0 1.0), or a guestbook. Draft datasets and dataset editors bypass the modal.
2223

2324
### Changed
2425

dev-env/shib-dev-env/keycloak/test-realm.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -940,7 +940,7 @@
940940
"alwaysDisplayInConsole": false,
941941
"clientAuthenticatorType": "client-secret",
942942
"redirectUris": [
943-
"https://localhost/spa/*"
943+
"https://localhost/modern/*"
944944
],
945945
"webOrigins": [
946946
"+"

src/sections/dataset/dataset-action-buttons/access-dataset-menu/AccessDatasetMenu.tsx

Lines changed: 21 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,13 @@ import {
88
DatasetLicense,
99
DatasetPermissions,
1010
DatasetPublishingStatus,
11-
DatasetVersion
11+
DatasetVersion,
12+
defaultLicense
1213
} from '../../../../dataset/domain/models/Dataset'
1314
import { FileDownloadSize, FileDownloadMode } from '../../../../files/domain/models/FileMetadata'
1415
import { DatasetExploreOptions } from '../DatasetToolsOptions'
1516
import { useAccessRepository } from '@/sections/access/AccessRepositoryContext'
16-
import { DownloadWithGuestbookModal } from '@/sections/dataset/dataset-files/files-table/file-actions/file-actions-cell/file-action-buttons/file-options-menu/DownloadWithGuestbookModal'
17+
import { DownloadWithTermsAndGuestbookModal } from '@/sections/dataset/dataset-files/files-table/file-actions/file-actions-cell/file-action-buttons/file-options-menu/DownloadWithTermsAndGuestbookModal'
1718
import {
1819
downloadFromSignedUrl,
1920
EMPTY_GUESTBOOK_RESPONSE,
@@ -47,14 +48,18 @@ export function AccessDatasetMenu({
4748
customTerms
4849
}: AccessDatasetMenuProps) {
4950
const { t } = useTranslation('dataset')
50-
const [showDownloadWithGuestbookModal, setShowDownloadWithGuestbookModal] = useState(false)
51+
const [showDownloadWithTermsAndGuestbookModal, setShowDownloadWithTermsAndGuestbookModal] =
52+
useState(false)
5153
const [selectedDownloadFormat, setSelectedDownloadFormat] = useState<FileDownloadMode>(
5254
FileDownloadMode.ORIGINAL
5355
)
54-
const hasGuestbook =
55-
guestbookId !== undefined &&
56-
version.publishingStatus !== DatasetPublishingStatus.DRAFT &&
57-
!permissions.canUpdateDataset
56+
const isDraft = version.publishingStatus === DatasetPublishingStatus.DRAFT
57+
const bypassTermsGuard = isDraft || permissions.canUpdateDataset
58+
const hasGuestbook = guestbookId !== undefined
59+
const hasNonDefaultLicense = license !== undefined && license.name !== defaultLicense.name
60+
const hasCustomTerms = customTerms !== undefined
61+
const shouldShowModal =
62+
!bypassTermsGuard && (hasGuestbook || hasCustomTerms || hasNonDefaultLicense)
5863

5964
const flesToDownloadSizeIsZero =
6065
fileDownloadSizes.map(({ value }) => value).reduce((acc, curr) => acc + curr, 0) === 0
@@ -79,7 +84,7 @@ export function AccessDatasetMenu({
7984
) => {
8085
event.preventDefault()
8186
setSelectedDownloadFormat(mode)
82-
setShowDownloadWithGuestbookModal(true)
87+
setShowDownloadWithTermsAndGuestbookModal(true)
8388
}
8489

8590
return (
@@ -96,15 +101,15 @@ export function AccessDatasetMenu({
96101
datasetNumericId={datasetNumericId}
97102
hasOneTabularFileAtLeast={hasOneTabularFileAtLeast}
98103
fileDownloadSizes={fileDownloadSizes}
99-
hasGuestbook={hasGuestbook}
104+
requiresTermsOrGuestbook={shouldShowModal}
100105
onDownloadWithGuestbook={handleDownloadWithGuestbook}
101106
/>
102107
<DatasetExploreOptions persistentId={persistentId} />
103108
</DropdownButton>
104-
{hasGuestbook && showDownloadWithGuestbookModal && (
105-
<DownloadWithGuestbookModal
106-
show={showDownloadWithGuestbookModal}
107-
handleClose={() => setShowDownloadWithGuestbookModal(false)}
109+
{shouldShowModal && showDownloadWithTermsAndGuestbookModal && (
110+
<DownloadWithTermsAndGuestbookModal
111+
show={showDownloadWithTermsAndGuestbookModal}
112+
handleClose={() => setShowDownloadWithTermsAndGuestbookModal(false)}
108113
datasetId={datasetNumericId} // TODO: we should allow this to pass persistentId when we have the backend support for guestbook submission with persistentId
109114
datasetPersistentId={persistentId}
110115
guestbookId={guestbookId}
@@ -121,15 +126,15 @@ interface DatasetDownloadOptionsProps {
121126
datasetNumericId?: number | string
122127
hasOneTabularFileAtLeast: boolean
123128
fileDownloadSizes: FileDownloadSize[]
124-
hasGuestbook: boolean
129+
requiresTermsOrGuestbook: boolean
125130
onDownloadWithGuestbook: (event: React.MouseEvent<HTMLElement>, mode: FileDownloadMode) => void
126131
}
127132

128133
const DatasetDownloadOptions = ({
129134
datasetNumericId,
130135
hasOneTabularFileAtLeast,
131136
fileDownloadSizes,
132-
hasGuestbook,
137+
requiresTermsOrGuestbook,
133138
onDownloadWithGuestbook
134139
}: DatasetDownloadOptionsProps) => {
135140
const { t } = useTranslation('dataset')
@@ -140,7 +145,7 @@ const DatasetDownloadOptions = ({
140145
event: React.MouseEvent<HTMLElement>,
141146
mode: FileDownloadMode
142147
): void => {
143-
if (hasGuestbook) {
148+
if (requiresTermsOrGuestbook) {
144149
onDownloadWithGuestbook(event, mode)
145150
return
146151
}

src/sections/dataset/dataset-files/files-table/file-actions/download-files/DownloadFilesButton.tsx

Lines changed: 30 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,14 @@ import { useTranslation } from 'react-i18next'
33
import { Button, DropdownButton, DropdownButtonItem } from '@iqss/dataverse-design-system'
44
import { MouseEvent, useState } from 'react'
55
import { toast } from 'react-toastify'
6-
import { DatasetPublishingStatus } from '@/dataset/domain/models/Dataset'
6+
import { DatasetPublishingStatus, defaultLicense } from '@/dataset/domain/models/Dataset'
77
import { FileDownloadMode } from '../../../../../../files/domain/models/FileMetadata'
88
import { useDataset } from '../../../../DatasetContext'
99
import { FileSelection } from '../../row-selection/useFileSelection'
1010
import { NoSelectedFilesModal } from '../no-selected-files-modal/NoSelectedFilesModal'
1111
import { FilePreview } from '../../../../../../files/domain/models/FilePreview'
1212
import { useMediaQuery } from '../../../../../../shared/hooks/useMediaQuery'
13-
import { DownloadWithGuestbookModal } from '../file-actions-cell/file-action-buttons/file-options-menu/DownloadWithGuestbookModal'
13+
import { DownloadWithTermsAndGuestbookModal } from '../file-actions-cell/file-action-buttons/file-options-menu/DownloadWithTermsAndGuestbookModal'
1414
import {
1515
downloadFromSignedUrl,
1616
EMPTY_GUESTBOOK_RESPONSE,
@@ -31,7 +31,8 @@ export function DownloadFilesButton({ files, fileSelection }: DownloadFilesButto
3131
const { t } = useTranslation('files')
3232
const { dataset } = useDataset()
3333
const [showNoFilesSelectedModal, setShowNoFilesSelectedModal] = useState(false)
34-
const [showDownloadWithGuestbookModal, setShowDownloadWithGuestbookModal] = useState(false)
34+
const [showDownloadWithTermsAndGuestbookModal, setShowDownloadWithTermsAndGuestbookModal] =
35+
useState(false)
3536
const [selectedDownloadFormat, setSelectedDownloadFormat] = useState<FileDownloadMode>(
3637
FileDownloadMode.ORIGINAL
3738
)
@@ -43,10 +44,15 @@ export function DownloadFilesButton({ files, fileSelection }: DownloadFilesButto
4344
const fileIdsForGuestbookSubmission = allFilesSelected
4445
? undefined
4546
: getFileIdsFromSelection(fileSelection)
46-
const hasGuestbook =
47-
dataset?.guestbookId !== undefined &&
48-
dataset?.version.publishingStatus !== DatasetPublishingStatus.DRAFT &&
49-
!dataset?.permissions.canUpdateDataset
47+
const isDraftDataset = dataset?.version.publishingStatus === DatasetPublishingStatus.DRAFT
48+
const canEdit = dataset?.permissions.canUpdateDataset ?? false
49+
const bypassTermsGuard = isDraftDataset || canEdit
50+
const hasGuestbook = dataset?.guestbookId !== undefined
51+
const hasNonDefaultLicense =
52+
dataset?.license !== undefined && dataset.license.name !== defaultLicense.name
53+
const hasCustomTerms = dataset?.termsOfUse?.customTerms !== undefined
54+
const shouldShowModal =
55+
!bypassTermsGuard && (hasGuestbook || hasCustomTerms || hasNonDefaultLicense)
5056

5157
const onClick = (event: MouseEvent<HTMLElement>, downloadMode: FileDownloadMode) => {
5258
if (fileSelectionCount === SELECTED_FILES_EMPTY) {
@@ -55,10 +61,10 @@ export function DownloadFilesButton({ files, fileSelection }: DownloadFilesButto
5561
return
5662
}
5763

58-
if (hasGuestbook) {
64+
if (shouldShowModal) {
5965
event.preventDefault()
6066
setSelectedDownloadFormat(downloadMode)
61-
setShowDownloadWithGuestbookModal(true)
67+
setShowDownloadWithTermsAndGuestbookModal(true)
6268
return
6369
}
6470

@@ -101,15 +107,17 @@ export function DownloadFilesButton({ files, fileSelection }: DownloadFilesButto
101107
show={showNoFilesSelectedModal}
102108
handleClose={() => setShowNoFilesSelectedModal(false)}
103109
/>
104-
{hasGuestbook && showDownloadWithGuestbookModal && (
105-
<DownloadWithGuestbookModal
110+
{shouldShowModal && showDownloadWithTermsAndGuestbookModal && (
111+
<DownloadWithTermsAndGuestbookModal
106112
fileIds={fileIdsForGuestbookSubmission}
107113
datasetId={allFilesSelected ? dataset.id : undefined}
108114
guestbookId={dataset.guestbookId}
109115
format={selectedDownloadFormat}
110116
datasetPersistentId={dataset.persistentId}
111-
show={showDownloadWithGuestbookModal}
112-
handleClose={() => setShowDownloadWithGuestbookModal(false)}
117+
datasetLicense={dataset.license}
118+
datasetCustomTerms={dataset.termsOfUse?.customTerms}
119+
show={showDownloadWithTermsAndGuestbookModal}
120+
handleClose={() => setShowDownloadWithTermsAndGuestbookModal(false)}
113121
/>
114122
)}
115123
</>
@@ -140,27 +148,15 @@ export function DownloadFilesButton({ files, fileSelection }: DownloadFilesButto
140148
// no tabular file content
141149
return (
142150
<>
143-
{hasGuestbook ? (
144-
<Button
145-
id="download-files"
146-
variant="secondary"
147-
icon={<Download className={styles.icon} />}
148-
aria-label={t('actions.downloadFiles.title')}
149-
withSpacing
150-
onClick={(event) => onClick(event, FileDownloadMode.ORIGINAL)}>
151-
{dropdownButtonTitle}
152-
</Button>
153-
) : (
154-
<Button
155-
id="download-files"
156-
variant="secondary"
157-
icon={<Download className={styles.icon} />}
158-
aria-label={t('actions.downloadFiles.title')}
159-
withSpacing
160-
onClick={(event) => onClick(event, FileDownloadMode.ORIGINAL)}>
161-
{dropdownButtonTitle}
162-
</Button>
163-
)}
151+
<Button
152+
id="download-files"
153+
variant="secondary"
154+
icon={<Download className={styles.icon} />}
155+
aria-label={t('actions.downloadFiles.title')}
156+
withSpacing
157+
onClick={(event) => onClick(event, FileDownloadMode.ORIGINAL)}>
158+
{dropdownButtonTitle}
159+
</Button>
164160

165161
{downloadFeedbackModals}
166162
</>

src/sections/dataset/dataset-files/files-table/file-actions/file-actions-cell/file-action-buttons/file-options-menu/DownloadWithGuestbookModal.module.scss renamed to src/sections/dataset/dataset-files/files-table/file-actions/file-actions-cell/file-action-buttons/file-options-menu/DownloadWithTermsAndGuestbookModal.module.scss

File renamed without changes.

src/sections/dataset/dataset-files/files-table/file-actions/file-actions-cell/file-action-buttons/file-options-menu/DownloadWithGuestbookModal.tsx renamed to src/sections/dataset/dataset-files/files-table/file-actions/file-actions-cell/file-action-buttons/file-options-menu/DownloadWithTermsAndGuestbookModal.tsx

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,10 @@ import {
1717
GuestbookAnswerDTO,
1818
GuestbookResponseDTO
1919
} from '@/access/domain/repositories/AccessRepository'
20-
import { downloadFromSignedUrl } from '@/shared/helpers/DownloadHelper'
20+
import { downloadFromSignedUrl, EMPTY_GUESTBOOK_RESPONSE } from '@/shared/helpers/DownloadHelper'
2121
import { FileDownloadMode } from '@/files/domain/models/FileMetadata'
2222

23-
interface DownloadWithGuestbookModalProps {
23+
interface DownloadWithTermsAndGuestbookModalProps {
2424
fileId?: number | string
2525
fileIds?: Array<number>
2626
format?: string | FileDownloadMode
@@ -34,7 +34,7 @@ interface DownloadWithGuestbookModalProps {
3434
}
3535

3636
type GuestbookFormValues = Record<string, string>
37-
export function DownloadWithGuestbookModal({
37+
export function DownloadWithTermsAndGuestbookModal({
3838
fileId,
3939
fileIds,
4040
format,
@@ -45,18 +45,19 @@ export function DownloadWithGuestbookModal({
4545
datasetCustomTerms,
4646
show,
4747
handleClose
48-
}: DownloadWithGuestbookModalProps) {
48+
}: DownloadWithTermsAndGuestbookModalProps) {
4949
const { t: tFiles } = useTranslation('files')
5050
const { t: tDataset } = useTranslation('dataset')
5151
const { user } = useSession()
5252
const accessRepository = useAccessRepository()
5353
const guestbookRepository = useGuestbookRepository()
5454

55+
const hasGuestbook = guestbookId !== undefined
5556
const [formValues, setFormValues] = useState<GuestbookFormValues>({})
5657
const { guestbook, isLoadingGuestbook, errorGetGuestbook } = useGetGuestbookById({
5758
guestbookRepository,
5859
guestbookId,
59-
enabled: show
60+
enabled: show && hasGuestbook
6061
})
6162
const accountFieldKeys = useMemo(() => ['name', 'email', 'institution', 'position'], [])
6263

@@ -178,6 +179,10 @@ export function DownloadWithGuestbookModal({
178179
}
179180

180181
const buildGuestbookResponse = (): GuestbookResponseDTO => {
182+
if (!guestbook) {
183+
return EMPTY_GUESTBOOK_RESPONSE
184+
}
185+
181186
const customQuestionAnswers = customQuestions.reduce<GuestbookAnswerDTO[]>(
182187
(answers, question, index) => {
183188
const fieldName = getGuestbookCustomQuestionFieldName(question, index)
@@ -275,12 +280,14 @@ export function DownloadWithGuestbookModal({
275280
onClick={() =>
276281
void handleSubmit({
277282
hasFormErrors: hasAccountFieldErrors || hasCustomQuestionErrors,
278-
guestbook,
279283
guestbookResponse: buildGuestbookResponse()
280284
})
281285
}
282286
disabled={
283-
isLoadingGuestbook || isSubmittingGuestbook || !!errorGetGuestbook || !guestbook
287+
isLoadingGuestbook ||
288+
isSubmittingGuestbook ||
289+
!!errorGetGuestbook ||
290+
(hasGuestbook && !guestbook)
284291
}
285292
aria-label={tFiles('requestAccess.confirmation')}>
286293
{isSubmittingGuestbook ? <Spinner variant="light" animation="border" size="sm" /> : null}{' '}

0 commit comments

Comments
 (0)