Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
1363ec0
refactor: flatten layout interface structure
FrancescoMolinaro Jul 9, 2026
da6a192
[DURACOM-507] Port dynamic layout functionality and related components
FrancescoMolinaro Jul 9, 2026
f96a169
[DURACOM-507] add tab resolver, fix missing enhancments, add labels, …
FrancescoMolinaro Jul 10, 2026
b41723c
[DURACOM-507] adapt translation prefix, add dso-edit-menu where conte…
FrancescoMolinaro Jul 10, 2026
fc0ca88
[DURACOM-507] remove cris layout references, rename crisRef into dyna…
FrancescoMolinaro Jul 14, 2026
2038167
[DURACOM-507] add autogenerated labels for layout possible customizat…
FrancescoMolinaro Jul 16, 2026
34762b7
[DURACOM-507] add missing layout css variables
FrancescoMolinaro Jul 16, 2026
8291e3e
[DURACOM-507] add inline documentation
FrancescoMolinaro Jul 17, 2026
2fe0335
[DURACOM-507] minor layout fix, add bitstream form options
FrancescoMolinaro Jul 22, 2026
d6e9126
[DURACOM-507] refactor and clean up models and directives
FrancescoMolinaro Aug 4, 2026
1657aca
[DURACOM-507] Add geospatial rendering type, add missing input in the…
FrancescoMolinaro Aug 4, 2026
5c916ab
Merge remote-tracking branch 'gitHub/main' into task/main/DURACOM-507
FrancescoMolinaro Aug 6, 2026
4dc60eb
[DURACOM-507] reintroduce box and rendering decorators
FrancescoMolinaro Aug 6, 2026
6920e9e
[DURACOM-507] add openstreetmap rendering, fix track detection in navbar
FrancescoMolinaro Aug 6, 2026
56f1dfb
[DURACOM-507] refactor, fix layout spacing issues, add dynamic decora…
FrancescoMolinaro Sep 1, 2026
10935af
Merge remote-tracking branch 'gitHub/main' into task/main/DURACOM-507
FrancescoMolinaro Sep 1, 2026
53446fc
[DURACOM-507] fix dynamic layout decorators, clean up
FrancescoMolinaro Sep 2, 2026
443dcca
Merge remote-tracking branch '4Science_gitHub/task/main/DURACOM-507' …
FrancescoMolinaro Sep 2, 2026
39784ed
[DURACOM-507] fix dynamic navbar on mobile
FrancescoMolinaro Sep 2, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 49 additions & 1 deletion config/config.example.yml
Original file line number Diff line number Diff line change
Expand Up @@ -712,7 +712,7 @@ accessibility:
cookieExpirationDuration: 7

# Configuration for layout customization of metadata rendering in Item page
# Currently only the authority reference config is available, more will follow with the integration of the so called CRIS layout.
# Currently only the authority reference config is available, more will follow with the integration of the so called Dynamic layout.
layout:
# Configuration of icons and styles to be used for each authority controlled link
authorityRef:
Expand Down Expand Up @@ -762,6 +762,54 @@ layout:
- name: checksum
type: attribute

# Configuration for URN (Uniform Resource Name) identifier resolution
# Maps identifier types (e.g., DOI, Handle, Scopus) to their base URLs for creating resolvable links
urn:
- name: doi
baseUrl: 'https://doi.org/'
- name: hdl
baseUrl: 'https://hdl.handle.net/'
- name: scopus
baseUrl: 'https://www.scopus.com/authid/detail.uri?authorId='
- name: researcherid
baseUrl: 'http://www.researcherid.com/rid/'
- name: mailto
baseUrl: 'mailto:'

# Item page layout configuration for different entity types
# Defines the visual layout orientation (horizontal/vertical) for various entity types in the item page detail view
itemPage:
OrgUnit:
orientation: vertical
Project:
orientation: vertical
default:
orientation: horizontal

# Metadata box rendering configuration
# Defines default CSS column styles for metadata labels and values in item page detail view
metadataBox:
# CSS classes for metadata label column (default: 'col-3' = 25% width)
defaultMetadataLabelColStyle: col-3
# CSS classes for metadata value column (default: 'col-9' = 75% width)
defaultMetadataValueColStyle: col-9

# Collections box rendering configuration
# Defines default CSS column styles for collections and inline display settings
collectionsBox:
# CSS classes for collection label column (default: 'col-3 fw-bold' = 25% width, bold text)
defaultCollectionsLabelColStyle: col-3 fw-bold
# CSS classes for collection value column (default: 'col-9' = 75% width)
defaultCollectionsValueColStyle: col-9
# CSS classes for collection row styling
# defaultCollectionsRowStyle: ''
# Whether to display collections inline (true) or in a block layout (false)
isInline: true
# Key/Value map to define metadata holding the style information for each entity type
dynamicRefStyleMetadata:
default: 'dspace.entity.style'
Publication: 'example.entity.style'

# Configuration for customization of search results
searchResults:
# Metadata fields to be displayed in the search results under the standard ones
Expand Down
2 changes: 1 addition & 1 deletion scripts/config/decorator-param.interface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ export interface DecoratorParam {
*
* (Optional)
*/
default?: string;
default?: string | boolean;

/**
* The property of the provided parameter value that should be used instead of the value itself.
Expand Down
2 changes: 1 addition & 1 deletion src/app/admin/admin-sidebar/admin-sidebar.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ <h4 class="my-1">{{ 'menu.header.admin' | translate }}</h4>
<div class="sidebar-full-width-container" id="sidebar-top-level-items-container">
<div class="sidebar-full-width-container" id="sidebar-top-level-items" role="menubar"
[attr.aria-label]="'menu.header.admin.description' |translate">
@for (sectionDTO of (sectionDTOs$ | async); track sectionDTO) {
@for (sectionDTO of (sectionDTOs$ | async); track sectionDTO.menuSection.id) {
<ds-menu-component-loader [menuID]="menuID"
[expandable]="sectionDTO.hasSubSections || sectionDTO.menuSection.alwaysRenderExpandable"
[section]="sectionDTO.menuSection">
Expand Down
1 change: 1 addition & 0 deletions src/app/app.component.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ const initialState = {
export function getMockLocaleService(): LocaleService {
return jasmine.createSpyObj('LocaleService', {
setCurrentLanguageCode: jasmine.createSpy('setCurrentLanguageCode'),
getCurrentLanguageCode: jasmine.createSpy('getCurrentLanguageCode'),
});
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -282,7 +282,7 @@ describe('EditBitstreamPageComponent', () => {
});

it('should fill in the media type', () => {
expect(rawForm.mediaInfoContainer.mediaType).toEqual('audio');
expect(rawForm.mediaInfoContainer.fileType).toEqual('audio');
});

it('should fill in the audio transcript', () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -345,26 +345,38 @@ export class EditBitstreamPageComponent implements OnInit, OnDestroy {
/**
* The Dynamic Select Model for the media type
*/
mediaTypeModel = new DynamicSelectModel({
id: 'mediaType',
name: 'mediaType',
fileTypeModel = new DynamicSelectModel({
id: 'fileType',
name: 'fileType',
options: [
{
label: this.translate.instant('bitstream.edit.form.mediaType.option.neither'),
label: this.translate.instant('bitstream.edit.form.fileType.option.neither'),
value: 'neither',
},
{
label: this.translate.instant('bitstream.edit.form.mediaType.option.audio'),
label: this.translate.instant('bitstream.edit.form.fileType.option.audio'),
value: 'audio',
},
{
label: this.translate.instant('bitstream.edit.form.mediaType.option.video'),
label: this.translate.instant('bitstream.edit.form.fileType.option.video'),
value: 'video',
},
{
label: this.translate.instant('bitstream.edit.form.mediaType.option.audio-video'),
label: this.translate.instant('bitstream.edit.form.fileType.option.audio-video'),
value: 'audio+video',
},
{
label: this.translate.instant('bitstream.edit.form.fileType.option.logo'),
value: 'logo',
},
{
label: this.translate.instant('bitstream.edit.form.fileType.option.main-article'),
value: 'main article',
},
{
label: this.translate.instant('bitstream.edit.form.fileType.option.personal-picture'),
value: 'personal picture',
},
Comment thread
alexandrevryghem marked this conversation as resolved.
],
value: 'neither',
});
Expand All @@ -383,11 +395,11 @@ export class EditBitstreamPageComponent implements OnInit, OnDestroy {
operator: OR_OPERATOR,
when: [
{
id: 'mediaType',
id: 'fileType',
value: 'audio',
},
{
id: 'mediaType',
id: 'fileType',
value: 'audio+video',
},
],
Expand All @@ -409,11 +421,11 @@ export class EditBitstreamPageComponent implements OnInit, OnDestroy {
operator: OR_OPERATOR,
when: [
{
id: 'mediaType',
id: 'fileType',
value: 'video',
},
{
id: 'mediaType',
id: 'fileType',
value: 'audio+video',
},
],
Expand All @@ -425,7 +437,7 @@ export class EditBitstreamPageComponent implements OnInit, OnDestroy {
/**
* All input models in a simple array for easier iterations
*/
inputModels = [this.primaryBitstreamModel, this.fileNameModel, this.descriptionModel, this.mediaTypeModel,
inputModels = [this.primaryBitstreamModel, this.fileNameModel, this.descriptionModel, this.fileTypeModel,
this.audioTranscriptModel, this.videoDescriptionModel, this.selectedFormatModel, this.newFormatModel];

/**
Expand Down Expand Up @@ -453,7 +465,7 @@ export class EditBitstreamPageComponent implements OnInit, OnDestroy {
new DynamicFormGroupModel({
id: 'mediaInfoContainer',
group: [
this.mediaTypeModel,
this.fileTypeModel,
this.audioTranscriptModel,
this.videoDescriptionModel,
],
Expand Down Expand Up @@ -513,7 +525,7 @@ export class EditBitstreamPageComponent implements OnInit, OnDestroy {
host: this.newFormatBaseLayout + ' invisible',
},
},
mediaType: {
fileType: {
grid: {
host: 'col-12 d-inline-block',
},
Expand Down Expand Up @@ -739,7 +751,7 @@ export class EditBitstreamPageComponent implements OnInit, OnDestroy {
description: bitstream.firstMetadataValue('dc.description'),
},
mediaInfoContainer: {
mediaType: bitstream.firstMetadataValue('dc.type') ?? 'neither',
fileType: bitstream.firstMetadataValue('dc.type') ?? 'neither',
audioTranscript: bitstream.firstMetadataValue('dspace.bitstream.transcript'),
videoDescription: bitstream.firstMetadataValue('dspace.bitstream.textalternative'),
},
Expand Down Expand Up @@ -921,11 +933,11 @@ export class EditBitstreamPageComponent implements OnInit, OnDestroy {
} else {
Metadata.setFirstValue(newMetadata, 'dc.description', rawForm.descriptionContainer.description);
}
const mediaType = rawForm.mediaInfoContainer?.mediaType;
if (isEmpty(mediaType) || mediaType === 'neither') {
const fileType = rawForm.mediaInfoContainer?.fileType;
if (isEmpty(fileType) || fileType === 'neither') {
delete newMetadata['dc.type'];
} else {
Metadata.setFirstValue(newMetadata, 'dc.type', mediaType);
Metadata.setFirstValue(newMetadata, 'dc.type', fileType);
}
if (isEmpty(rawForm.mediaInfoContainer?.audioTranscript)) {
delete newMetadata['dspace.bitstream.transcript'];
Expand Down

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This file should be removed

Empty file.
155 changes: 155 additions & 0 deletions src/app/core/data/bitstream-data.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,13 @@ import {
} from './request.models';
import { RequestService } from './request.service';

/**
* Filter for metadata value to be used for rest API
*/
export interface MetadataFilter {
metadataName: string;
metadataValue: string;
}
/**
* A service to retrieve {@link Bitstream}s from the REST API
*/
Expand Down Expand Up @@ -382,4 +389,152 @@ export class BitstreamDataService extends IdentifiableDataService<Bitstream> imp
return this.rdbService.buildFromRequestUUIDAndAwait(requestId, () => observableCombineLatest(bitstreams.map((bitstream: Bitstream) => this.invalidateByHref(bitstream._links.self.href))));
}

/**
* Returns an observable of {@link RemoteData} of a {@link Bitstream} that is not marked
* hidden (that hasn't got the metadata `bitstream.hide` or its value is not true/yes).
* resolve {@link HALLink}s of the object
*
* @param uuid The item UUID to retrieve bitstreams from
* @param bundlename Bundle type of the bitstreams
* @param metadataFilters Array of object we want to filter by
* @param options The {@link FindListOptions} for the request
* @param useCachedVersionIfAvailable If this is true, the request will only be sent if there's
* no valid cached version. Defaults to true
* @param reRequestOnStale Whether or not the request should automatically be re-
* requested after the response becomes stale
* @param linksToFollow List of {@link FollowLinkConfig} that indicate which
* {@link HALLink}s should be automatically resolved
*/
findShowableBitstreamsByItem(
uuid: string,
bundlename: string,
metadataFilters: MetadataFilter[],
options: FindListOptions = {},
useCachedVersionIfAvailable = true,
reRequestOnStale = true,
...linksToFollow: FollowLinkConfig<Bitstream>[]
): Observable<RemoteData<PaginatedList<Bitstream>>> {
const searchParams = [];
searchParams.push(new RequestParam('uuid', uuid));
searchParams.push(new RequestParam('name', bundlename));

metadataFilters.forEach((entry: MetadataFilter) => {
searchParams.push(new RequestParam('filterMetadata', entry.metadataName));
searchParams.push(new RequestParam('filterMetadataValue', entry.metadataValue));
});

const hrefObs = this.getSearchByHref(
'showableByItem',
{ searchParams },
...linksToFollow,
);

return this.findListByHref(
hrefObs,
options,
useCachedVersionIfAvailable,
reRequestOnStale,
...linksToFollow,
);
}


/**
* Returns an observable of {@link RemoteData} of a {@link Bitstream}, based on a handle and an
* optional sequenceId or filename, with a list of {@link FollowLinkConfig}, to automatically
* resolve {@link HALLink}s of the object
*
* @param uuid The item UUID to retrieve bitstreams from
* @param bundlename Bundle type of the bitstreams
* @param metadataFilters Array of object we want to filter by
* @param options The {@link FindListOptions} for the request
* @param useCachedVersionIfAvailable If this is true, the request will only be sent if there's
* no valid cached version. Defaults to true
* @param reRequestOnStale Whether or not the request should automatically be re-
* requested after the response becomes stale
* @param linksToFollow List of {@link FollowLinkConfig} that indicate which
* {@link HALLink}s should be automatically resolved
*/
findByItem(
uuid: string,
bundlename: string,
metadataFilters: MetadataFilter[],
options: FindListOptions,
useCachedVersionIfAvailable = true,
reRequestOnStale = true,
...linksToFollow: FollowLinkConfig<Bitstream>[]
): Observable<RemoteData<PaginatedList<Bitstream>>> {
const searchParams = [];
searchParams.push(new RequestParam('uuid', uuid));
searchParams.push(new RequestParam('name', bundlename));

metadataFilters.forEach((entry: MetadataFilter) => {
searchParams.push(new RequestParam('filterMetadata', entry.metadataName));
searchParams.push(new RequestParam('filterMetadataValue', entry.metadataValue));
});

const hrefObs = this.getSearchByHref(
'byItemId',
{ searchParams },
...linksToFollow,
);

return this.findListByHref(
hrefObs,
options,
useCachedVersionIfAvailable,
reRequestOnStale,
...linksToFollow,
);
}

/**
* Returns an observable of {@link RemoteData} of a {@link Bitstream} that is not marked
* hidden (that hasn't got the metadata `bitstream.hide` or its value is not true/yes).
* resolve {@link HALLink}s of the object
*
* @param uuid The item UUID to retrieve bitstreams from
* @param bundlename Bundle type of the bitstreams
* @param metadataFilters Array of object we want to filter by
* @param options The {@link FindListOptions} for the request
* @param useCachedVersionIfAvailable If this is true, the request will only be sent if there's
* no valid cached version. Defaults to true
* @param reRequestOnStale Whether or not the request should automatically be re-
* requested after the response becomes stale
* @param linksToFollow List of {@link FollowLinkConfig} that indicate which
* {@link HALLink}s should be automatically resolved
*/
showableByItem(
uuid: string,
bundlename: string,
metadataFilters: MetadataFilter[],
options: FindListOptions,
useCachedVersionIfAvailable = true,
reRequestOnStale = true,
...linksToFollow: FollowLinkConfig<Bitstream>[]
): Observable<RemoteData<PaginatedList<Bitstream>>> {
const searchParams = [];
searchParams.push(new RequestParam('uuid', uuid));
searchParams.push(new RequestParam('name', bundlename));

metadataFilters.forEach((entry: MetadataFilter) => {
searchParams.push(new RequestParam('filterMetadata', entry.metadataName));
searchParams.push(new RequestParam('filterMetadataValue', entry.metadataValue));
});

const hrefObs = this.getSearchByHref(
'showableByItem',
{ searchParams },
...linksToFollow,
);

return this.findListByHref(
hrefObs,
options,
useCachedVersionIfAvailable,
reRequestOnStale,
...linksToFollow,
);
}

}
Loading
Loading