Skip to content

Commit 3fbb446

Browse files
committed
resolve merge conflicts
2 parents d4f7ab7 + e284755 commit 3fbb446

28 files changed

Lines changed: 938 additions & 217 deletions

File tree

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,9 @@ This changelog follows the principles of [Keep a Changelog](https://keepachangel
1818
- Added Notifications tab in Account Page
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
21+
- Added a message note to the login page
2122
- Download with terms of use and guestbook.
23+
- 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.
2224
- Layout: added a configurable homepage banner for announcements and important messages. (#787)
2325

2426
### Changed

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
"+"

packages/design-system/CHANGELOG.md

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,15 @@ See [Conventional Commits](https://conventionalcommits.org) for commit guideline
55

66
# Non Published Changes
77

8+
### Fixed
9+
10+
- **FormText:** Add `className` prop to allow custom styling.
11+
- **FormInput:** Add `size` prop to allow different input sizes (e.g., 'sm', 'lg'). Defaults to standard size if not specified.
12+
- **Badge:**:
13+
- Add `pill` prop to allow pill-shaped badges.
14+
- Add `dataTestId` prop to facilitate testing.
15+
- Add `className` prop to allow custom styling.
16+
817
# [2.1.0](https://github.com/IQSS/dataverse-frontend/compare/@iqss/dataverse-design-system@2.0.2...@iqss/dataverse-design-system@2.1.0) (2025-10-09)
918

1019
### Added
@@ -28,12 +37,6 @@ See [Conventional Commits](https://conventionalcommits.org) for commit guideline
2837
- Fix word wrapping in options list to prevent overflow and ensure long text is displayed correctly.
2938
- **ButtonGroup:**
3039
- Fix styles for vertical button groups when using tooltips.
31-
- **FormText:** Add `className` prop to allow custom styling.
32-
- **FormInput:** Add `size` prop to allow different input sizes (e.g., 'sm', 'lg'). Defaults to standard size if not specified.
33-
- **Badge:**:
34-
- Add `pill` prop to allow pill-shaped badges.
35-
- Add `dataTestId` prop to facilitate testing.
36-
- Add `className` prop to allow custom styling.
3740

3841
# [2.0.2](https://github.com/IQSS/dataverse-frontend/compare/@iqss/dataverse-design-system@2.0.1...@iqss/dataverse-design-system@2.0.2) (2024-06-23)
3942

src/index.tsx

Lines changed: 23 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -11,31 +11,35 @@ const AppEntrypoint = lazy(() => import('./index.app'))
1111
const container = document.getElementById('root') as HTMLElement
1212
const root = createRoot(container)
1313

14-
const appConfigInit = initAppConfig()
15-
16-
if (!appConfigInit.ok) {
14+
if (window.kcContext) {
1715
root.render(
18-
<ConfigError message={appConfigInit.message} schemaError={appConfigInit.schemaError} />
16+
<StrictMode>
17+
<KcPage kcContext={window.kcContext} />
18+
</StrictMode>
1919
)
2020
} else {
21-
const appConfig = requireAppConfig()
21+
const appConfigInit = initAppConfig()
2222

23-
ApiConfig.init(
24-
`${appConfig.backendUrl}/api/v1`,
25-
DataverseApiAuthMechanism.BEARER_TOKEN,
26-
undefined,
27-
`${appConfig.oidc.localStorageKeyPrefix}token`
28-
)
23+
if (!appConfigInit.ok) {
24+
root.render(
25+
<ConfigError message={appConfigInit.message} schemaError={appConfigInit.schemaError} />
26+
)
27+
} else {
28+
const appConfig = requireAppConfig()
2929

30-
root.render(
31-
<StrictMode>
32-
{window.kcContext ? (
33-
<KcPage kcContext={window.kcContext} />
34-
) : (
30+
ApiConfig.init(
31+
`${appConfig.backendUrl}/api/v1`,
32+
DataverseApiAuthMechanism.BEARER_TOKEN,
33+
undefined,
34+
`${appConfig.oidc.localStorageKeyPrefix}token`
35+
)
36+
37+
root.render(
38+
<StrictMode>
3539
<Suspense>
3640
<AppEntrypoint />
3741
</Suspense>
38-
)}
39-
</StrictMode>
40-
)
42+
</StrictMode>
43+
)
44+
}
4145
}

src/keycloak-theme/login/Template.tsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import type { KcContext } from './KcContext'
99
import { Alert, DropdownButton, DropdownButtonItem, Tooltip } from '@iqss/dataverse-design-system'
1010
import { ArrowUpRightSquareFill } from 'react-bootstrap-icons'
1111
import dataverse_logo from '@/assets/dataverse_brand_icon.svg'
12+
import { SignInNotice } from './components/SignInNotice'
1213
import styles from './template.module.scss'
1314

1415
/*
@@ -64,11 +65,13 @@ export default function Template(props: TemplateProps<KcContext, I18n>) {
6465

6566
return (
6667
<div className={styles.login} id="kc-login-template">
68+
{kcContext.pageId === 'login.ftl' && <SignInNotice i18n={i18n} />}
6769
<div id="kc-header">
6870
<div id="kc-header-wrapper" className={styles['header-wrapper']}>
6971
<img src={dataverse_logo} alt="Brand Logo Image" />
7072
</div>
7173
</div>
74+
7275
<div className={styles['login-card']}>
7376
<header>
7477
{enabledLanguages.length > 1 && (
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
.top-notice {
2+
margin: 0.5rem;
3+
border-radius: 8px;
4+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import { Alert } from '@iqss/dataverse-design-system'
2+
import type { I18n } from '../i18n'
3+
import styles from './SignInNotice.module.scss'
4+
5+
const DATAVERSE_BASE_URL =
6+
(import.meta.env.VITE_DATAVERSE_BASE_URL as string) ?? 'https://dataverse.harvard.edu'
7+
const HARVARD_SIGN_UP_URL = `${DATAVERSE_BASE_URL}/dataverseuser.xhtml?editMode=CREATE&redirectPage=%2Fdataverse_homepage.xhtml`
8+
9+
interface SignInNoticeProps {
10+
i18n: I18n
11+
}
12+
13+
export function SignInNotice({ i18n }: SignInNoticeProps) {
14+
const { msg, msgStr } = i18n
15+
16+
return (
17+
<div className={styles['top-notice']}>
18+
<Alert variant="warning" customHeading={msgStr('signInNoticeTitle')} dismissible={false}>
19+
<>
20+
{msg('signInNoticeBodyPrefix')}
21+
<a href={HARVARD_SIGN_UP_URL}>{msg('signInNoticeSignUpLinkText')}</a>
22+
{msg('signInNoticeBodySuffix')}
23+
</>
24+
</Alert>
25+
</div>
26+
)
27+
}

src/keycloak-theme/login/i18n.ts

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,18 @@ import { i18nBuilder } from 'keycloakify/login'
33
import type { ThemeName } from '../kc.gen'
44

55
/** @see: https://docs.keycloakify.dev/features/i18n */
6-
const { useI18n, ofTypeI18n } = i18nBuilder.withThemeName<ThemeName>().build()
6+
const { useI18n, ofTypeI18n } = i18nBuilder
7+
.withThemeName<ThemeName>()
8+
.withCustomTranslations({
9+
en: {
10+
signInNoticeTitle: 'Note about Federated Login options sign-in',
11+
signInNoticeBodyPrefix:
12+
'Federated Login options are available only to the existing accounts that previously authenticated with these methods. New sign-ups via these options are not supported. Please use your Harvard Login or Username/Email to ',
13+
signInNoticeSignUpLinkText: 'sign up in Harvard Dataverse',
14+
signInNoticeBodySuffix: ', then you may login here.'
15+
}
16+
})
17+
.build()
718

819
type I18n = typeof ofTypeI18n
920

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
</>

0 commit comments

Comments
 (0)