Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
1e9fbee
[DURACOM-453] add edit item menu and data service
FrancescoMolinaro Feb 13, 2026
a3c14c5
[DURACOM-453] finalize improvement for submission and edit mode
FrancescoMolinaro Feb 16, 2026
e0bb5f9
Merge branch 'task/main/DURACOM-444' into task/main/DURACOM-453
FrancescoMolinaro Feb 16, 2026
664e44f
Merge branch 'task/main/DURACOM-426' into task/main/DURACOM-453
FrancescoMolinaro Feb 16, 2026
2edc805
Merge remote-tracking branch 'gitHub/main' into task/main/DURACOM-453
FrancescoMolinaro Feb 16, 2026
850a973
[DURACOM-453] add metadata security and finilize edit mode
FrancescoMolinaro Feb 17, 2026
87cfa6e
[DURACOM-453] fix observable issue, add full projection
FrancescoMolinaro Feb 19, 2026
ab0885e
[DURACOM-453] add shared submission config and labels
FrancescoMolinaro Feb 20, 2026
c352534
[DURACOM-453] port metadata security update, init and administrate
FrancescoMolinaro Mar 10, 2026
31cafeb
[DURACOM-453] port error parsing in edit mode
FrancescoMolinaro Mar 11, 2026
8c656fe
Merge remote-tracking branch 'gitHub/main' into task/main/DURACOM-453
FrancescoMolinaro Mar 16, 2026
6369166
[DURACOM-453] remove collection form required from fields, fix metada…
FrancescoMolinaro Mar 17, 2026
b383541
[DURACOM-453] fix collection form metadata name
FrancescoMolinaro Mar 17, 2026
cb4bc13
[DURACOM-453] fix security level patch update, fix style in admin tab…
FrancescoMolinaro Mar 24, 2026
db7bd44
[DURACOM-453] handle errors on missing security settings endpoint
FrancescoMolinaro Mar 25, 2026
12047a5
Merge remote-tracking branch 'gitHub/main' into task/main/DURACOM-453
FrancescoMolinaro Mar 27, 2026
1faf25a
[DURACOM-453] fix missing security config in tests
FrancescoMolinaro Mar 27, 2026
7746789
Merge remote-tracking branch 'gitHub/main' into task/main/DURACOM-453
FrancescoMolinaro Mar 27, 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
6 changes: 6 additions & 0 deletions src/app/app-routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import { ACCESS_CONTROL_MODULE_PATH } from './access-control/access-control-rout
import { NOTIFICATIONS_MODULE_PATH } from './admin/admin-routing-paths';
import {
ADMIN_MODULE_PATH,
EDIT_ITEM_PATH,
FORGOT_PASSWORD_PATH,
HEALTH_PAGE_PATH,
PROFILE_MODULE_PATH,
Expand Down Expand Up @@ -280,6 +281,11 @@ export const APP_ROUTES: Route[] = [
.then((m) => m.ROUTES),
canActivate: [authenticatedGuard],
},
{
path: EDIT_ITEM_PATH,
loadChildren: () => import('./edit-item/edit-item-routes').then((m) => m.ROUTES),
canActivate: [endUserAgreementCurrentUserGuard],
},
{
path: 'external-login/:token',
loadChildren: () => import('./external-login-page/external-login-routes').then((m) => m.ROUTES),
Expand Down
4 changes: 4 additions & 0 deletions src/app/app.menus.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import { CurationMenuProvider } from './shared/menu/providers/curation.menu';
import { DSpaceObjectEditMenuProvider } from './shared/menu/providers/dso-edit.menu';
import { DsoOptionMenuProvider } from './shared/menu/providers/dso-option.menu';
import { EditMenuProvider } from './shared/menu/providers/edit.menu';
import { EditItemDetailsMenuProvider } from './shared/menu/providers/edit-item-details.menu';
import { ExportMenuProvider } from './shared/menu/providers/export.menu';
import { HealthMenuProvider } from './shared/menu/providers/health.menu';
import { ImportMenuProvider } from './shared/menu/providers/import.menu';
Expand Down Expand Up @@ -104,6 +105,9 @@ export const MENUS = buildMenuStructure({
ClaimMenuProvider.onRoute(
MenuRoute.ITEM_PAGE,
),
EditItemDetailsMenuProvider.onRoute(
MenuRoute.ITEM_PAGE,
),
]),
],
});
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,15 @@ import {
} from '@angular/core';
import { AuthService } from '@dspace/core/auth/auth.service';
import { ObjectCacheService } from '@dspace/core/cache/object-cache.service';
import { ConfigObject } from '@dspace/core/config/models/config.model';
import { SubmissionDefinitionModel } from '@dspace/core/config/models/config-submission-definition.model';
import { SubmissionDefinitionsConfigDataService } from '@dspace/core/config/submission-definitions-config-data.service';
import { CollectionDataService } from '@dspace/core/data/collection-data.service';
import { EntityTypeDataService } from '@dspace/core/data/entity-type-data.service';
import { RequestService } from '@dspace/core/data/request.service';
import { NotificationsService } from '@dspace/core/notification-system/notifications.service';
import { Collection } from '@dspace/core/shared/collection.model';
import { ItemType } from '@dspace/core/shared/item-relationships/item-type.model';
import { NONE_ENTITY_TYPE } from '@dspace/core/shared/item-relationships/item-type.resource-type';
import { MetadataValue } from '@dspace/core/shared/metadata.models';
import { getFirstSucceededRemoteListPayload } from '@dspace/core/shared/operators';
import {
Expand All @@ -25,6 +27,7 @@ import {
} from '@dspace/shared/utils/empty.util';
import { NgbModal } from '@ng-bootstrap/ng-bootstrap';
import {
DynamicCheckboxModel,
DynamicFormControlModel,
DynamicFormOptionConfig,
DynamicFormService,
Expand All @@ -34,7 +37,12 @@ import {
TranslateModule,
TranslateService,
} from '@ngx-translate/core';
import { Observable } from 'rxjs';
import {
catchError,
combineLatest,
Observable,
of,
} from 'rxjs';

import { ComColFormComponent } from '../../shared/comcol/comcol-forms/comcol-form/comcol-form.component';
import { ComcolPageLogoComponent } from '../../shared/comcol/comcol-page-logo/comcol-page-logo.component';
Expand All @@ -44,6 +52,8 @@ import { VarDirective } from '../../shared/utils/var.directive';
import {
collectionFormEntityTypeSelectionConfig,
collectionFormModels,
collectionFormSharedWorkspaceCheckboxConfig,
collectionFormSubmissionDefinitionSelectionConfig,
} from './collection-form.models';

/**
Expand Down Expand Up @@ -79,6 +89,14 @@ export class CollectionFormComponent extends ComColFormComponent<Collection> imp
*/
entityTypeSelection: DynamicSelectModel<string> = new DynamicSelectModel(collectionFormEntityTypeSelectionConfig);

/**
* The dynamic form field used for submission definition selection
* @type {DynamicSelectModel<string>}
*/
submissionDefinitionSelection: DynamicSelectModel<string> = new DynamicSelectModel(collectionFormSubmissionDefinitionSelectionConfig);

sharedWorkspaceChekbox: DynamicCheckboxModel = new DynamicCheckboxModel(collectionFormSharedWorkspaceCheckboxConfig);

/**
* The dynamic form fields used for creating/editing a collection
* @type {DynamicFormControlModel[]}
Expand All @@ -94,6 +112,7 @@ export class CollectionFormComponent extends ComColFormComponent<Collection> imp
protected objectCache: ObjectCacheService,
protected entityTypeService: EntityTypeDataService,
protected chd: ChangeDetectorRef,
protected submissionDefinitionService: SubmissionDefinitionsConfigDataService,
protected modalService: NgbModal) {
super(formService, translate, notificationsService, authService, requestService, objectCache, modalService);
}
Expand All @@ -117,35 +136,65 @@ export class CollectionFormComponent extends ComColFormComponent<Collection> imp

initializeForm() {
let currentRelationshipValue: MetadataValue[];
let currentDefinitionValue: MetadataValue[];
let currentSharedWorkspaceValue: MetadataValue[];
if (this.dso && this.dso.metadata) {
currentRelationshipValue = this.dso.metadata['dspace.entity.type'];
currentDefinitionValue = this.dso.metadata['dspace.submission.definition'];
currentSharedWorkspaceValue = this.dso.metadata['dspace.workspace.shared'];
}

const entities$: Observable<ItemType[]> = this.entityTypeService.findAll({ elementsPerPage: 100, currentPage: 1 }).pipe(
getFirstSucceededRemoteListPayload(),
);

// retrieve all entity types to populate the dropdowns selection
entities$.subscribe((entityTypes: ItemType[]) => {

entityTypes = entityTypes.filter((type: ItemType) => type.label !== NONE_ENTITY_TYPE);
entityTypes.forEach((type: ItemType, index: number) => {
this.entityTypeSelection.add({
disabled: false,
label: type.label,
value: type.label,
} as DynamicFormOptionConfig<string>);
if (currentRelationshipValue && currentRelationshipValue.length > 0 && currentRelationshipValue[0].value === type.label) {
this.entityTypeSelection.select(index);
this.entityTypeSelection.disabled = true;
}
});
const definitions$: Observable<ConfigObject[]> = this.submissionDefinitionService
.findAll({ elementsPerPage: 100, currentPage: 1 }).pipe(
getFirstSucceededRemoteListPayload(),
catchError(() => of([])),
);

this.formModel = entityTypes.length === 0 ? collectionFormModels : [...collectionFormModels, this.entityTypeSelection];
// retrieve all entity types and submission definitions to populate the dropdowns selection
combineLatest([entities$, definitions$])
.subscribe(([entityTypes, definitions]: [ItemType[], SubmissionDefinitionModel[]]) => {

super.ngOnInit();
this.chd.detectChanges();
});
const sortedEntityTypes = entityTypes
.sort((a, b) => a.label.localeCompare(b.label));

sortedEntityTypes.forEach((type: ItemType, index: number) => {
this.entityTypeSelection.add({
disabled: false,
label: type.label,
value: type.label,
} as DynamicFormOptionConfig<string>);
if (currentRelationshipValue && currentRelationshipValue.length > 0 && currentRelationshipValue[0].value === type.label) {
this.entityTypeSelection.select(index);
this.entityTypeSelection.disabled = true;
}
});

definitions.forEach((definition: SubmissionDefinitionModel, index: number) => {
this.submissionDefinitionSelection.add({
disabled: false,
label: definition.name,
value: definition.name,
} as DynamicFormOptionConfig<string>);
if (currentDefinitionValue && currentDefinitionValue.length > 0 && currentDefinitionValue[0].value === definition.name) {
this.submissionDefinitionSelection.select(index);
}
});

this.formModel = entityTypes.length === 0 ?
[...collectionFormModels, this.submissionDefinitionSelection, this.sharedWorkspaceChekbox] :
[...collectionFormModels, this.entityTypeSelection, this.submissionDefinitionSelection, this.sharedWorkspaceChekbox];

super.ngOnInit();

if (currentSharedWorkspaceValue && currentSharedWorkspaceValue.length > 0) {
this.sharedWorkspaceChekbox.value = currentSharedWorkspaceValue[0].value === 'true';
}
this.chd.detectChanges();
});

}
}
20 changes: 20 additions & 0 deletions src/app/collection-page/collection-form/collection-form.models.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import {
DynamicCheckboxModelConfig,
DynamicFormControlModel,
DynamicInputModel,
DynamicSelectModelConfig,
Expand All @@ -11,6 +12,25 @@ export const collectionFormEntityTypeSelectionConfig: DynamicSelectModelConfig<s
id: 'entityType',
name: 'dspace.entity.type',
disabled: false,
errorMessages: {
required: 'collection.form.errors.entityType.required',
},
};

export const collectionFormSubmissionDefinitionSelectionConfig: DynamicSelectModelConfig<string> = {
id: 'submissionDefinition',
name: 'dspace.submission.definition',
disabled: false,
errorMessages: {
required: 'collection.form.errors.submissionDefinition.required',
},
};


export const collectionFormSharedWorkspaceCheckboxConfig: DynamicCheckboxModelConfig = {
id: 'sharedWorkspace',
name: 'dspace.workspace.shared',
disabled: false,
};

/**
Expand Down
1 change: 1 addition & 0 deletions src/app/core/cache/builders/remote-data-build.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -339,6 +339,7 @@ export class RemoteDataBuildService {
response.errorMessage,
payload,
response.statusCode,
response.errors,
);
}),
);
Expand Down
36 changes: 36 additions & 0 deletions src/app/core/config/submission-definitions-config-data.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { Injectable } from '@angular/core';
import { FollowLinkConfig } from '@dspace/core/shared/follow-link-config.model';
import { Observable } from 'rxjs';
import {
mergeMap,
take,
} from 'rxjs/operators';

import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service';
import { ObjectCacheService } from '../cache/object-cache.service';
import { FindListOptions } from '../data/find-list-options.model';
import { PaginatedList } from '../data/paginated-list.model';
import { RemoteData } from '../data/remote-data';
import { RequestService } from '../data/request.service';
import { HALEndpointService } from '../shared/hal-endpoint.service';
import { ConfigDataService } from './config-data.service';
import { ConfigObject } from './models/config.model';

@Injectable({ providedIn: 'root' })
export class SubmissionDefinitionsConfigDataService extends ConfigDataService {
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.

Please add TypeDocs or code comments to describe this new data service, including the findAll() method below

constructor(
protected requestService: RequestService,
protected rdbService: RemoteDataBuildService,
protected objectCache: ObjectCacheService,
protected halService: HALEndpointService,
) {
super('submissiondefinitions', requestService, rdbService, objectCache, halService);
}

findAll(options: FindListOptions = {}, useCachedVersionIfAvailable = true, reRequestOnStale = true, ...linksToFollow: FollowLinkConfig<ConfigObject>[]): Observable<RemoteData<PaginatedList<ConfigObject>>> {
return this.getBrowseEndpoint(options).pipe(
take(1),
mergeMap((href: string) => super.findListByHref(href, options, useCachedVersionIfAvailable, reRequestOnStale, ...linksToFollow)),
);
}
}
4 changes: 4 additions & 0 deletions src/app/core/data-services-map.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,8 @@ import { VERSION } from './shared/version.resource-type';
import { VERSION_HISTORY } from './shared/version-history.resource-type';
import { USAGE_REPORT } from './statistics/models/usage-report.resource-type';
import { CorrectionType } from './submission/models/correctiontype.model';
import { EditItem } from './submission/models/edititem.model';
import { METADATA_SECURITY_TYPE } from './submission/models/metadata-security-config.resource-type';
import { SUBMISSION_CC_LICENSE } from './submission/models/submission-cc-licence.resource-type';
import { SUBMISSION_CC_LICENSE_URL } from './submission/models/submission-cc-licence-link.resource-type';
import {
Expand Down Expand Up @@ -138,4 +140,6 @@ export const LAZY_DATA_SERVICES: LazyDataServicesMap = new Map([
[DUPLICATE.value, () => import('./submission/submission-duplicate-data.service').then(m => m.SubmissionDuplicateDataService)],
[CorrectionType.type.value, () => import('./submission/correctiontype-data.service').then(m => m.CorrectionTypeDataService)],
[AUDIT.value, () => import('./data/audit-data.service').then(m => m.AuditDataService)],
[EditItem.type.value, () => import('./submission/edititem-data.service').then(m => m.EditItemDataService)],
[METADATA_SECURITY_TYPE.value, () => import('./submission/metadatasecurityconfig-data.service').then(m => m.MetadataSecurityConfigurationService)],
]);
Loading
Loading