From 468f0d5f12babe8df234377a902da5a01b987f0a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eike=20Martin=20L=C3=B6hden?= Date: Sat, 13 Jun 2026 10:04:36 +0200 Subject: [PATCH 01/18] Created new menu providers for collection and community page. --- .../menu/providers/add-sub-collection.menu.ts | 56 +++++++++++++++++++ .../menu/providers/add-sub-community.menu.ts | 56 +++++++++++++++++++ .../menu/providers/submit-new-item.menu.ts | 56 +++++++++++++++++++ src/assets/i18n/en.json5 | 6 ++ 4 files changed, 174 insertions(+) create mode 100644 src/app/shared/menu/providers/add-sub-collection.menu.ts create mode 100644 src/app/shared/menu/providers/add-sub-community.menu.ts create mode 100644 src/app/shared/menu/providers/submit-new-item.menu.ts diff --git a/src/app/shared/menu/providers/add-sub-collection.menu.ts b/src/app/shared/menu/providers/add-sub-collection.menu.ts new file mode 100644 index 00000000000..eb4879e9204 --- /dev/null +++ b/src/app/shared/menu/providers/add-sub-collection.menu.ts @@ -0,0 +1,56 @@ +/** + * The contents of this file are subject to the license and copyright + * detailed in the LICENSE and NOTICE files at the root of the source + * tree and available online at + * + * http://www.dspace.org/license/ + */ +import { Injectable } from '@angular/core'; +import { AuthorizationDataService } from '@dspace/core/data/feature-authorization/authorization-data.service'; +import { FeatureID } from '@dspace/core/data/feature-authorization/feature-id'; +import { DSpaceObject } from '@dspace/core/shared/dspace-object.model'; +import { + combineLatest, + Observable, +} from 'rxjs'; +import { map } from 'rxjs/operators'; + +import { LinkMenuItemModel } from '../menu-item/models/link.model'; +import { MenuItemType } from '../menu-item-type.model'; +import { PartialMenuSection } from '../menu-provider.model'; +import { DSpaceObjectPageMenuProvider } from './helper-providers/dso.menu'; + +/** + * Menu provider to create the "Edit" option in the DSO edit menu + */ +@Injectable() +export class AddSubCollectionMenu extends DSpaceObjectPageMenuProvider { + constructor( + protected authorizationDataService: AuthorizationDataService, + ) { + super(); + } + + public getSectionsForContext(dso: DSpaceObject): Observable { + return combineLatest([ + this.authorizationDataService.isAuthorized(FeatureID.CanEditMetadata, dso.self), + ]).pipe( + map(([canEditCommunity]) => { + return [ + { + visible: canEditCommunity, + model: { + type: MenuItemType.LINK, + text: 'community.add.sub-collection', + link: '/collections/create', + queryParams: { + parent: dso.uuid, + }, + } as LinkMenuItemModel, + icon: 'plus', + }, + ] as PartialMenuSection[]; + }), + ); + } +} diff --git a/src/app/shared/menu/providers/add-sub-community.menu.ts b/src/app/shared/menu/providers/add-sub-community.menu.ts new file mode 100644 index 00000000000..f483ce8ada7 --- /dev/null +++ b/src/app/shared/menu/providers/add-sub-community.menu.ts @@ -0,0 +1,56 @@ +/** + * The contents of this file are subject to the license and copyright + * detailed in the LICENSE and NOTICE files at the root of the source + * tree and available online at + * + * http://www.dspace.org/license/ + */ +import { Injectable } from '@angular/core'; +import { AuthorizationDataService } from '@dspace/core/data/feature-authorization/authorization-data.service'; +import { FeatureID } from '@dspace/core/data/feature-authorization/feature-id'; +import { DSpaceObject } from '@dspace/core/shared/dspace-object.model'; +import { + combineLatest, + Observable, +} from 'rxjs'; +import { map } from 'rxjs/operators'; + +import { LinkMenuItemModel } from '../menu-item/models/link.model'; +import { MenuItemType } from '../menu-item-type.model'; +import { PartialMenuSection } from '../menu-provider.model'; +import { DSpaceObjectPageMenuProvider } from './helper-providers/dso.menu'; + +/** + * Menu provider to create the "Edit" option in the DSO edit menu + */ +@Injectable() +export class AddSubCommunityMenu extends DSpaceObjectPageMenuProvider { + constructor( + protected authorizationDataService: AuthorizationDataService, + ) { + super(); + } + + public getSectionsForContext(dso: DSpaceObject): Observable { + return combineLatest([ + this.authorizationDataService.isAuthorized(FeatureID.CanEditMetadata, dso.self), + ]).pipe( + map(([canEditCommunity]) => { + return [ + { + visible: canEditCommunity, + model: { + type: MenuItemType.LINK, + text: 'community.add.sub-community', + link: '/communities/create', + queryParams: { + parent: dso.uuid, + }, + } as LinkMenuItemModel, + icon: 'plus', + }, + ] as PartialMenuSection[]; + }), + ); + } +} diff --git a/src/app/shared/menu/providers/submit-new-item.menu.ts b/src/app/shared/menu/providers/submit-new-item.menu.ts new file mode 100644 index 00000000000..1c794509004 --- /dev/null +++ b/src/app/shared/menu/providers/submit-new-item.menu.ts @@ -0,0 +1,56 @@ +/** + * The contents of this file are subject to the license and copyright + * detailed in the LICENSE and NOTICE files at the root of the source + * tree and available online at + * + * http://www.dspace.org/license/ + */ +import { Injectable } from '@angular/core'; +import { AuthorizationDataService } from '@dspace/core/data/feature-authorization/authorization-data.service'; +import { FeatureID } from '@dspace/core/data/feature-authorization/feature-id'; +import { DSpaceObject } from '@dspace/core/shared/dspace-object.model'; +import { + combineLatest, + Observable, +} from 'rxjs'; +import { map } from 'rxjs/operators'; + +import { LinkMenuItemModel } from '../menu-item/models/link.model'; +import { MenuItemType } from '../menu-item-type.model'; +import { PartialMenuSection } from '../menu-provider.model'; +import { DSpaceObjectPageMenuProvider } from './helper-providers/dso.menu'; + +/** + * Menu provider to create the "Edit" option in the DSO edit menu + */ +@Injectable() +export class SubmitNewItemMenu extends DSpaceObjectPageMenuProvider { + constructor( + protected authorizationDataService: AuthorizationDataService, + ) { + super(); + } + + public getSectionsForContext(dso: DSpaceObject): Observable { + return combineLatest([ + this.authorizationDataService.isAuthorized(FeatureID.CanSubmit, dso.self), + ]).pipe( + map(([canSubmitItem]) => { + return [ + { + visible: canSubmitItem, + model: { + type: MenuItemType.LINK, + text: 'collection.submit.item', + link: '/submit', + queryParams: { + collection: dso.uuid, + }, + } as LinkMenuItemModel, + icon: 'plus', + }, + ] as PartialMenuSection[]; + }), + ); + } +} diff --git a/src/assets/i18n/en.json5 b/src/assets/i18n/en.json5 index 6551143ff13..8c8002411e7 100644 --- a/src/assets/i18n/en.json5 +++ b/src/assets/i18n/en.json5 @@ -7913,4 +7913,10 @@ "bitstream.related.isReplacedBy": "Is replaced by", "bitstream.related.deleted": "deleted", + + "community.add.sub-community": "Add Community", + + "community.add.sub-collection": "Add Collection", + + "collection.submit.item": "Submit item", } From 040cb839db5ace4eb58b96b5761099ca4b4af064 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eike=20Martin=20L=C3=B6hden?= Date: Sat, 13 Jun 2026 10:04:49 +0200 Subject: [PATCH 02/18] Added appMenu entries. --- src/app/app.menus.ts | 12 ++++++++++++ .../shared/menu/providers/submit-new-item.menu.ts | 2 +- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/src/app/app.menus.ts b/src/app/app.menus.ts index 73190c57caf..7b444579684 100644 --- a/src/app/app.menus.ts +++ b/src/app/app.menus.ts @@ -9,6 +9,8 @@ import { buildMenuStructure } from './shared/menu/menu.structure'; import { MenuID } from './shared/menu/menu-id.model'; import { MenuRoute } from './shared/menu/menu-route.model'; import { AccessControlMenuProvider } from './shared/menu/providers/access-control.menu'; +import { AddSubCollectionMenu } from './shared/menu/providers/add-sub-collection.menu'; +import { AddSubCommunityMenu } from './shared/menu/providers/add-sub-community.menu'; import { AdminSearchMenuProvider } from './shared/menu/providers/admin-search.menu'; import { AuditLogsMenuProvider } from './shared/menu/providers/audit-item.menu'; import { AuditOverviewMenuProvider } from './shared/menu/providers/audit-overview.menu'; @@ -36,6 +38,7 @@ import { NotificationsMenuProvider } from './shared/menu/providers/notifications import { ProcessesMenuProvider } from './shared/menu/providers/processes.menu'; import { RegistriesMenuProvider } from './shared/menu/providers/registries.menu'; import { StatisticsMenuProvider } from './shared/menu/providers/statistics.menu'; +import { SubmitNewItemMenu } from './shared/menu/providers/submit-new-item.menu'; import { SystemWideAlertMenuProvider } from './shared/menu/providers/system-wide-alert.menu'; import { WithdrawnReinstateItemMenuProvider } from './shared/menu/providers/withdrawn-reinstate-item.menu'; import { WorkflowMenuProvider } from './shared/menu/providers/workflow.menu'; @@ -96,6 +99,15 @@ export const MENUS = buildMenuStructure({ MenuRoute.COLLECTION_PAGE, MenuRoute.ITEM_PAGE, ), + AddSubCommunityMenu.onRoute( + MenuRoute.COMMUNITY_PAGE, + ), + AddSubCollectionMenu.onRoute( + MenuRoute.COMMUNITY_PAGE, + ), + SubmitNewItemMenu.onRoute( + MenuRoute.COLLECTION_PAGE, + ), WithdrawnReinstateItemMenuProvider.onRoute( MenuRoute.ITEM_PAGE, ), diff --git a/src/app/shared/menu/providers/submit-new-item.menu.ts b/src/app/shared/menu/providers/submit-new-item.menu.ts index 1c794509004..a1c4d1bdf9a 100644 --- a/src/app/shared/menu/providers/submit-new-item.menu.ts +++ b/src/app/shared/menu/providers/submit-new-item.menu.ts @@ -47,7 +47,7 @@ export class SubmitNewItemMenu extends DSpaceObjectPageMenuProvider { collection: dso.uuid, }, } as LinkMenuItemModel, - icon: 'plus', + icon: 'down-to-bracket', }, ] as PartialMenuSection[]; }), From 6d498fb59603513117fd341301b5e0ba5a0dceb3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eike=20Martin=20L=C3=B6hden?= Date: Sun, 14 Jun 2026 06:48:49 +0200 Subject: [PATCH 03/18] Combine sub-objects menu providers. --- src/app/app.menus.ts | 8 +-- .../menu/providers/add-sub-community.menu.ts | 56 ------------------- ...ection.menu.ts => add-sub-objects.menu.ts} | 18 +++++- .../menu/providers/submit-new-item.menu.ts | 2 +- 4 files changed, 18 insertions(+), 66 deletions(-) delete mode 100644 src/app/shared/menu/providers/add-sub-community.menu.ts rename src/app/shared/menu/providers/{add-sub-collection.menu.ts => add-sub-objects.menu.ts} (77%) diff --git a/src/app/app.menus.ts b/src/app/app.menus.ts index 7b444579684..cabb7275f42 100644 --- a/src/app/app.menus.ts +++ b/src/app/app.menus.ts @@ -9,8 +9,6 @@ import { buildMenuStructure } from './shared/menu/menu.structure'; import { MenuID } from './shared/menu/menu-id.model'; import { MenuRoute } from './shared/menu/menu-route.model'; import { AccessControlMenuProvider } from './shared/menu/providers/access-control.menu'; -import { AddSubCollectionMenu } from './shared/menu/providers/add-sub-collection.menu'; -import { AddSubCommunityMenu } from './shared/menu/providers/add-sub-community.menu'; import { AdminSearchMenuProvider } from './shared/menu/providers/admin-search.menu'; import { AuditLogsMenuProvider } from './shared/menu/providers/audit-item.menu'; import { AuditOverviewMenuProvider } from './shared/menu/providers/audit-overview.menu'; @@ -42,6 +40,7 @@ import { SubmitNewItemMenu } from './shared/menu/providers/submit-new-item.menu' import { SystemWideAlertMenuProvider } from './shared/menu/providers/system-wide-alert.menu'; import { WithdrawnReinstateItemMenuProvider } from './shared/menu/providers/withdrawn-reinstate-item.menu'; import { WorkflowMenuProvider } from './shared/menu/providers/workflow.menu'; +import {AddSubObjectsMenu} from "./shared/menu/providers/add-sub-objects.menu"; /** * Represents and builds the menu structure for the three available menus (public navbar, admin sidebar and the dso edit @@ -99,10 +98,7 @@ export const MENUS = buildMenuStructure({ MenuRoute.COLLECTION_PAGE, MenuRoute.ITEM_PAGE, ), - AddSubCommunityMenu.onRoute( - MenuRoute.COMMUNITY_PAGE, - ), - AddSubCollectionMenu.onRoute( + AddSubObjectsMenu.onRoute( MenuRoute.COMMUNITY_PAGE, ), SubmitNewItemMenu.onRoute( diff --git a/src/app/shared/menu/providers/add-sub-community.menu.ts b/src/app/shared/menu/providers/add-sub-community.menu.ts deleted file mode 100644 index f483ce8ada7..00000000000 --- a/src/app/shared/menu/providers/add-sub-community.menu.ts +++ /dev/null @@ -1,56 +0,0 @@ -/** - * The contents of this file are subject to the license and copyright - * detailed in the LICENSE and NOTICE files at the root of the source - * tree and available online at - * - * http://www.dspace.org/license/ - */ -import { Injectable } from '@angular/core'; -import { AuthorizationDataService } from '@dspace/core/data/feature-authorization/authorization-data.service'; -import { FeatureID } from '@dspace/core/data/feature-authorization/feature-id'; -import { DSpaceObject } from '@dspace/core/shared/dspace-object.model'; -import { - combineLatest, - Observable, -} from 'rxjs'; -import { map } from 'rxjs/operators'; - -import { LinkMenuItemModel } from '../menu-item/models/link.model'; -import { MenuItemType } from '../menu-item-type.model'; -import { PartialMenuSection } from '../menu-provider.model'; -import { DSpaceObjectPageMenuProvider } from './helper-providers/dso.menu'; - -/** - * Menu provider to create the "Edit" option in the DSO edit menu - */ -@Injectable() -export class AddSubCommunityMenu extends DSpaceObjectPageMenuProvider { - constructor( - protected authorizationDataService: AuthorizationDataService, - ) { - super(); - } - - public getSectionsForContext(dso: DSpaceObject): Observable { - return combineLatest([ - this.authorizationDataService.isAuthorized(FeatureID.CanEditMetadata, dso.self), - ]).pipe( - map(([canEditCommunity]) => { - return [ - { - visible: canEditCommunity, - model: { - type: MenuItemType.LINK, - text: 'community.add.sub-community', - link: '/communities/create', - queryParams: { - parent: dso.uuid, - }, - } as LinkMenuItemModel, - icon: 'plus', - }, - ] as PartialMenuSection[]; - }), - ); - } -} diff --git a/src/app/shared/menu/providers/add-sub-collection.menu.ts b/src/app/shared/menu/providers/add-sub-objects.menu.ts similarity index 77% rename from src/app/shared/menu/providers/add-sub-collection.menu.ts rename to src/app/shared/menu/providers/add-sub-objects.menu.ts index eb4879e9204..9ceee8e6001 100644 --- a/src/app/shared/menu/providers/add-sub-collection.menu.ts +++ b/src/app/shared/menu/providers/add-sub-objects.menu.ts @@ -24,7 +24,7 @@ import { DSpaceObjectPageMenuProvider } from './helper-providers/dso.menu'; * Menu provider to create the "Edit" option in the DSO edit menu */ @Injectable() -export class AddSubCollectionMenu extends DSpaceObjectPageMenuProvider { +export class AddSubObjectsMenu extends DSpaceObjectPageMenuProvider { constructor( protected authorizationDataService: AuthorizationDataService, ) { @@ -35,10 +35,22 @@ export class AddSubCollectionMenu extends DSpaceObjectPageMenuProvider { return combineLatest([ this.authorizationDataService.isAuthorized(FeatureID.CanEditMetadata, dso.self), ]).pipe( - map(([canEditCommunity]) => { + map(([canEditObject]) => { return [ { - visible: canEditCommunity, + visible: canEditObject, + model: { + type: MenuItemType.LINK, + text: 'community.add.sub-community', + link: '/communities/create', + queryParams: { + parent: dso.uuid, + }, + } as LinkMenuItemModel, + icon: 'plus', + }, + { + visible: canEditObject, model: { type: MenuItemType.LINK, text: 'community.add.sub-collection', diff --git a/src/app/shared/menu/providers/submit-new-item.menu.ts b/src/app/shared/menu/providers/submit-new-item.menu.ts index a1c4d1bdf9a..1c794509004 100644 --- a/src/app/shared/menu/providers/submit-new-item.menu.ts +++ b/src/app/shared/menu/providers/submit-new-item.menu.ts @@ -47,7 +47,7 @@ export class SubmitNewItemMenu extends DSpaceObjectPageMenuProvider { collection: dso.uuid, }, } as LinkMenuItemModel, - icon: 'down-to-bracket', + icon: 'plus', }, ] as PartialMenuSection[]; }), From 1ff5c3e85d8767ef8f1a527f0f2d1d93d2d61418 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eike=20Martin=20L=C3=B6hden?= Date: Tue, 23 Jun 2026 06:47:27 +0200 Subject: [PATCH 04/18] Created spec and e2e tests. --- cypress/e2e/collection-page.cy.ts | 17 ++++ .../providers/add-sub-objects.menu.spec.ts | 79 +++++++++++++++++++ .../providers/submit-new-item.menu.spec.ts | 67 ++++++++++++++++ 3 files changed, 163 insertions(+) create mode 100644 src/app/shared/menu/providers/add-sub-objects.menu.spec.ts create mode 100644 src/app/shared/menu/providers/submit-new-item.menu.spec.ts diff --git a/cypress/e2e/collection-page.cy.ts b/cypress/e2e/collection-page.cy.ts index fae53da0414..d7eed8005c4 100644 --- a/cypress/e2e/collection-page.cy.ts +++ b/cypress/e2e/collection-page.cy.ts @@ -1,5 +1,7 @@ import { testA11y } from 'cypress/support/utils'; +const COLLECTION_PAGE = '/collections/'.concat(Cypress.expose('DSPACE_TEST_COLLECTION')); + describe('Collection Page', () => { it('should pass accessibility tests', () => { @@ -12,3 +14,18 @@ describe('Collection Page', () => { testA11y('ds-collection-page'); }); }); + +describe('Collection Page -> Collection-edit menu', () => { + beforeEach(() => { + // All tests start with visiting the Collection Page + cy.visit(COLLECTION_PAGE); + + // These page elements are restricted, so we will be shown the login form. Fill it out & submit. + cy.loginViaForm(Cypress.expose('DSPACE_TEST_ADMIN_USER'), Cypress.expose('DSPACE_TEST_ADMIN_PASSWORD')); + }); + + it('Edit menu should exist for admins.', () => { + // tag must be loaded + cy.get('ds-dso-edit-menu').should('be.visible'); + }); +}); diff --git a/src/app/shared/menu/providers/add-sub-objects.menu.spec.ts b/src/app/shared/menu/providers/add-sub-objects.menu.spec.ts new file mode 100644 index 00000000000..f783fd07b1c --- /dev/null +++ b/src/app/shared/menu/providers/add-sub-objects.menu.spec.ts @@ -0,0 +1,79 @@ +import { TestBed } from '@angular/core/testing'; +import { AuthorizationDataService } from '@dspace/core/data/feature-authorization/authorization-data.service'; +import { Community } from '@dspace/core/shared/community.model'; +import { COMMUNITY } from '@dspace/core/shared/community.resource-type'; +import { of } from 'rxjs'; + +import { MenuItemType } from '../menu-item-type.model'; +import { PartialMenuSection } from '../menu-provider.model'; +import { DSpaceObjectEditMenuProvider } from './dso-edit.menu'; + +describe('DSpaceObjectEditMenuProvider', () => { + + const expectedSections: PartialMenuSection[] = [ + { + visible: true, + model: { + type: MenuItemType.LINK, + text: 'community.add.sub-community', + link: '/communities/create', + queryParams: { + parent: 'test-uuid', + }, + }, + icon: 'plus', + }, + { + visible: true, + model: { + type: MenuItemType.LINK, + text: 'community.add.sub-collection', + link: '/collections/create', + queryParams: { + parent: 'test-uuid', + }, + }, + icon: 'plus', + }, + ]; + + let provider: DSpaceObjectEditMenuProvider; + + const dso: Community = Object.assign(new Community(), { + type: COMMUNITY.value, + uuid: 'test-uuid', + _links: { self: { href: 'self-link' } }, + }); + + + let authorizationService; + + beforeEach(() => { + + authorizationService = jasmine.createSpyObj('authorizationService', { + 'isAuthorized': of(true), + }); + + TestBed.configureTestingModule({ + providers: [ + DSpaceObjectEditMenuProvider, + { provide: AuthorizationDataService, useValue: authorizationService }, + ], + }); + provider = TestBed.inject(DSpaceObjectEditMenuProvider); + }); + + it('should be created', () => { + expect(provider).toBeTruthy(); + }); + + describe('getSectionsForContext', () => { + it('should return the expected sections', (done) => { + provider.getSectionsForContext(dso).subscribe((sections) => { + expect(sections).toEqual(expectedSections); + done(); + }); + }); + }); + +}); diff --git a/src/app/shared/menu/providers/submit-new-item.menu.spec.ts b/src/app/shared/menu/providers/submit-new-item.menu.spec.ts new file mode 100644 index 00000000000..25e23cc812f --- /dev/null +++ b/src/app/shared/menu/providers/submit-new-item.menu.spec.ts @@ -0,0 +1,67 @@ +import { TestBed } from '@angular/core/testing'; +import { AuthorizationDataService } from '@dspace/core/data/feature-authorization/authorization-data.service'; +import { Collection } from '@dspace/core/shared/collection.model'; +import { COLLECTION } from '@dspace/core/shared/collection.resource-type'; +import { of } from 'rxjs'; + +import { MenuItemType } from '../menu-item-type.model'; +import { PartialMenuSection } from '../menu-provider.model'; +import { DSpaceObjectEditMenuProvider } from './dso-edit.menu'; + +describe('DSpaceObjectEditMenuProvider', () => { + + const expectedSections: PartialMenuSection[] = [ + { + visible: true, + model: { + type: MenuItemType.LINK, + text: 'collection.submit.item', + link: '/submit', + queryParams: { + collection: 'test-uuid', + }, + }, + icon: 'plus', + }, + ]; + + let provider: DSpaceObjectEditMenuProvider; + + const dso: Collection = Object.assign(new Collection(), { + type: COLLECTION.value, + uuid: 'test-uuid', + _links: { self: { href: 'self-link' } }, + }); + + + let authorizationService; + + beforeEach(() => { + + authorizationService = jasmine.createSpyObj('authorizationService', { + 'isAuthorized': of(true), + }); + + TestBed.configureTestingModule({ + providers: [ + DSpaceObjectEditMenuProvider, + { provide: AuthorizationDataService, useValue: authorizationService }, + ], + }); + provider = TestBed.inject(DSpaceObjectEditMenuProvider); + }); + + it('should be created', () => { + expect(provider).toBeTruthy(); + }); + + describe('getSectionsForContext', () => { + it('should return the expected sections', (done) => { + provider.getSectionsForContext(dso).subscribe((sections) => { + expect(sections).toEqual(expectedSections); + done(); + }); + }); + }); + +}); From 06ec8e26da96e6852f7e36df1813ba682515cf00 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eike=20Martin=20L=C3=B6hden?= Date: Thu, 25 Jun 2026 16:00:16 +0200 Subject: [PATCH 05/18] Fixed names and spec tests. --- src/app/app.menus.ts | 8 ++++---- .../shared/menu/providers/add-sub-objects.menu.spec.ts | 10 +++++----- src/app/shared/menu/providers/add-sub-objects.menu.ts | 2 +- .../shared/menu/providers/submit-new-item.menu.spec.ts | 10 +++++----- src/app/shared/menu/providers/submit-new-item.menu.ts | 2 +- 5 files changed, 16 insertions(+), 16 deletions(-) diff --git a/src/app/app.menus.ts b/src/app/app.menus.ts index cabb7275f42..605068cd804 100644 --- a/src/app/app.menus.ts +++ b/src/app/app.menus.ts @@ -9,6 +9,7 @@ import { buildMenuStructure } from './shared/menu/menu.structure'; import { MenuID } from './shared/menu/menu-id.model'; import { MenuRoute } from './shared/menu/menu-route.model'; import { AccessControlMenuProvider } from './shared/menu/providers/access-control.menu'; +import { AddSubObjectsMenuProvider } from './shared/menu/providers/add-sub-objects.menu'; import { AdminSearchMenuProvider } from './shared/menu/providers/admin-search.menu'; import { AuditLogsMenuProvider } from './shared/menu/providers/audit-item.menu'; import { AuditOverviewMenuProvider } from './shared/menu/providers/audit-overview.menu'; @@ -36,11 +37,10 @@ import { NotificationsMenuProvider } from './shared/menu/providers/notifications import { ProcessesMenuProvider } from './shared/menu/providers/processes.menu'; import { RegistriesMenuProvider } from './shared/menu/providers/registries.menu'; import { StatisticsMenuProvider } from './shared/menu/providers/statistics.menu'; -import { SubmitNewItemMenu } from './shared/menu/providers/submit-new-item.menu'; +import { SubmitNewItemMenuProvider } from './shared/menu/providers/submit-new-item.menu'; import { SystemWideAlertMenuProvider } from './shared/menu/providers/system-wide-alert.menu'; import { WithdrawnReinstateItemMenuProvider } from './shared/menu/providers/withdrawn-reinstate-item.menu'; import { WorkflowMenuProvider } from './shared/menu/providers/workflow.menu'; -import {AddSubObjectsMenu} from "./shared/menu/providers/add-sub-objects.menu"; /** * Represents and builds the menu structure for the three available menus (public navbar, admin sidebar and the dso edit @@ -98,10 +98,10 @@ export const MENUS = buildMenuStructure({ MenuRoute.COLLECTION_PAGE, MenuRoute.ITEM_PAGE, ), - AddSubObjectsMenu.onRoute( + AddSubObjectsMenuProvider.onRoute( MenuRoute.COMMUNITY_PAGE, ), - SubmitNewItemMenu.onRoute( + SubmitNewItemMenuProvider.onRoute( MenuRoute.COLLECTION_PAGE, ), WithdrawnReinstateItemMenuProvider.onRoute( diff --git a/src/app/shared/menu/providers/add-sub-objects.menu.spec.ts b/src/app/shared/menu/providers/add-sub-objects.menu.spec.ts index f783fd07b1c..7419a4289a5 100644 --- a/src/app/shared/menu/providers/add-sub-objects.menu.spec.ts +++ b/src/app/shared/menu/providers/add-sub-objects.menu.spec.ts @@ -6,9 +6,9 @@ import { of } from 'rxjs'; import { MenuItemType } from '../menu-item-type.model'; import { PartialMenuSection } from '../menu-provider.model'; -import { DSpaceObjectEditMenuProvider } from './dso-edit.menu'; +import { AddSubObjectsMenuProvider } from './add-sub-objects.menu'; -describe('DSpaceObjectEditMenuProvider', () => { +describe('AddSubObjectsMenuProvider', () => { const expectedSections: PartialMenuSection[] = [ { @@ -37,7 +37,7 @@ describe('DSpaceObjectEditMenuProvider', () => { }, ]; - let provider: DSpaceObjectEditMenuProvider; + let provider: AddSubObjectsMenuProvider; const dso: Community = Object.assign(new Community(), { type: COMMUNITY.value, @@ -56,11 +56,11 @@ describe('DSpaceObjectEditMenuProvider', () => { TestBed.configureTestingModule({ providers: [ - DSpaceObjectEditMenuProvider, + AddSubObjectsMenuProvider, { provide: AuthorizationDataService, useValue: authorizationService }, ], }); - provider = TestBed.inject(DSpaceObjectEditMenuProvider); + provider = TestBed.inject(AddSubObjectsMenuProvider); }); it('should be created', () => { diff --git a/src/app/shared/menu/providers/add-sub-objects.menu.ts b/src/app/shared/menu/providers/add-sub-objects.menu.ts index 9ceee8e6001..ca69808beac 100644 --- a/src/app/shared/menu/providers/add-sub-objects.menu.ts +++ b/src/app/shared/menu/providers/add-sub-objects.menu.ts @@ -24,7 +24,7 @@ import { DSpaceObjectPageMenuProvider } from './helper-providers/dso.menu'; * Menu provider to create the "Edit" option in the DSO edit menu */ @Injectable() -export class AddSubObjectsMenu extends DSpaceObjectPageMenuProvider { +export class AddSubObjectsMenuProvider extends DSpaceObjectPageMenuProvider { constructor( protected authorizationDataService: AuthorizationDataService, ) { diff --git a/src/app/shared/menu/providers/submit-new-item.menu.spec.ts b/src/app/shared/menu/providers/submit-new-item.menu.spec.ts index 25e23cc812f..b7275ab0f6c 100644 --- a/src/app/shared/menu/providers/submit-new-item.menu.spec.ts +++ b/src/app/shared/menu/providers/submit-new-item.menu.spec.ts @@ -6,9 +6,9 @@ import { of } from 'rxjs'; import { MenuItemType } from '../menu-item-type.model'; import { PartialMenuSection } from '../menu-provider.model'; -import { DSpaceObjectEditMenuProvider } from './dso-edit.menu'; +import { SubmitNewItemMenuProvider } from './submit-new-item.menu'; -describe('DSpaceObjectEditMenuProvider', () => { +describe('SubmitNewItemMenuProvider', () => { const expectedSections: PartialMenuSection[] = [ { @@ -25,7 +25,7 @@ describe('DSpaceObjectEditMenuProvider', () => { }, ]; - let provider: DSpaceObjectEditMenuProvider; + let provider: SubmitNewItemMenuProvider; const dso: Collection = Object.assign(new Collection(), { type: COLLECTION.value, @@ -44,11 +44,11 @@ describe('DSpaceObjectEditMenuProvider', () => { TestBed.configureTestingModule({ providers: [ - DSpaceObjectEditMenuProvider, + SubmitNewItemMenuProvider, { provide: AuthorizationDataService, useValue: authorizationService }, ], }); - provider = TestBed.inject(DSpaceObjectEditMenuProvider); + provider = TestBed.inject(SubmitNewItemMenuProvider); }); it('should be created', () => { diff --git a/src/app/shared/menu/providers/submit-new-item.menu.ts b/src/app/shared/menu/providers/submit-new-item.menu.ts index 1c794509004..803d72c6b4e 100644 --- a/src/app/shared/menu/providers/submit-new-item.menu.ts +++ b/src/app/shared/menu/providers/submit-new-item.menu.ts @@ -24,7 +24,7 @@ import { DSpaceObjectPageMenuProvider } from './helper-providers/dso.menu'; * Menu provider to create the "Edit" option in the DSO edit menu */ @Injectable() -export class SubmitNewItemMenu extends DSpaceObjectPageMenuProvider { +export class SubmitNewItemMenuProvider extends DSpaceObjectPageMenuProvider { constructor( protected authorizationDataService: AuthorizationDataService, ) { From 33a1a4bb2f2bdff7331dd365aa1934b603f102d8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eike=20Martin=20L=C3=B6hden?= Date: Thu, 25 Jun 2026 20:41:35 +0200 Subject: [PATCH 06/18] Create configurable Submit button on collection pages. --- config/config.example.yml | 2 ++ .../collection-page.component.html | 4 ++++ .../collection-page.component.ts | 17 +++++++++++++++++ src/assets/i18n/en.json5 | 2 ++ src/config/collection-page-config.interface.ts | 1 + src/config/default-app-config.ts | 1 + src/environments/environment.test.ts | 1 + .../collection-page.component.ts | 6 +++++- 8 files changed, 33 insertions(+), 1 deletion(-) diff --git a/config/config.example.yml b/config/config.example.yml index 6f6c67d1ae6..3c1b5e48f8c 100644 --- a/config/config.example.yml +++ b/config/config.example.yml @@ -478,6 +478,8 @@ collection: showSidebar: true edit: undoTimeout: 10000 # 10 seconds + # Whether to show a submit button on item pages instead of having an entry in the dso-edit menu. + showSubmitButton: false # Theme Config themes: diff --git a/src/app/collection-page/collection-page.component.html b/src/app/collection-page/collection-page.component.html index 407adeec926..78ee84c27d2 100644 --- a/src/app/collection-page/collection-page.component.html +++ b/src/app/collection-page/collection-page.component.html @@ -34,6 +34,10 @@ [hasInnerHtml]="true" [title]="'collection.page.news'"> + @if (showSubmitButton$ | async) { + + + } diff --git a/src/app/collection-page/collection-page.component.ts b/src/app/collection-page/collection-page.component.ts index f9ec6606999..697cb26ffe7 100644 --- a/src/app/collection-page/collection-page.component.ts +++ b/src/app/collection-page/collection-page.component.ts @@ -2,13 +2,19 @@ import { AsyncPipe } from '@angular/common'; import { ChangeDetectionStrategy, Component, + Inject, OnInit, } from '@angular/core'; import { ActivatedRoute, Router, + RouterLink, RouterOutlet, } from '@angular/router'; +import { + APP_CONFIG, + AppConfig, +} from '@dspace/config/app-config.interface'; import { AuthService } from '@dspace/core/auth/auth.service'; import { DSONameService } from '@dspace/core/breadcrumbs/dso-name.service'; import { SortOptions } from '@dspace/core/cache/models/sort-options.model'; @@ -63,6 +69,7 @@ import { VarDirective } from '../shared/utils/var.directive'; ComcolPageLogoComponent, DsoEditMenuComponent, ErrorComponent, + RouterLink, RouterOutlet, ThemedComcolPageBrowseByComponent, ThemedComcolPageContentComponent, @@ -88,12 +95,18 @@ export class CollectionPageComponent implements OnInit { */ collectionPageRoute$: Observable; + /** + * Whether to show a submit button for users on the collection page. + */ + showSubmitButton$: Observable; + constructor( protected route: ActivatedRoute, protected router: Router, protected authService: AuthService, protected authorizationDataService: AuthorizationDataService, public dsoNameService: DSONameService, + @Inject(APP_CONFIG) protected appConfig: AppConfig, ) { } @@ -114,6 +127,10 @@ export class CollectionPageComponent implements OnInit { getAllSucceededRemoteDataPayload(), map((collection) => getCollectionPageRoute(collection.id)), ); + + this.showSubmitButton$ = this.authorizationDataService.isAuthorized(FeatureID.CanSubmit).pipe( + map(authorized => authorized && this.appConfig.collection.showSubmitButton), + ); } isNotEmpty(object: any) { diff --git a/src/assets/i18n/en.json5 b/src/assets/i18n/en.json5 index 8c8002411e7..2fc04173977 100644 --- a/src/assets/i18n/en.json5 +++ b/src/assets/i18n/en.json5 @@ -7919,4 +7919,6 @@ "community.add.sub-collection": "Add Collection", "collection.submit.item": "Submit item", + + "collection.page.submit-button": "Submit item", } diff --git a/src/config/collection-page-config.interface.ts b/src/config/collection-page-config.interface.ts index 5aec06daea2..c477d1aef71 100644 --- a/src/config/collection-page-config.interface.ts +++ b/src/config/collection-page-config.interface.ts @@ -9,6 +9,7 @@ export interface CollectionPageConfig extends Config { edit: { undoTimeout: number; }; + showSubmitButton: boolean; } /** diff --git a/src/config/default-app-config.ts b/src/config/default-app-config.ts index fd703d4d024..839e9d90090 100644 --- a/src/config/default-app-config.ts +++ b/src/config/default-app-config.ts @@ -456,6 +456,7 @@ export class DefaultAppConfig implements AppConfig { edit: { undoTimeout: 10000, // 10 seconds }, + showSubmitButton: false, }; suggestion: SuggestionConfig[] = [ diff --git a/src/environments/environment.test.ts b/src/environments/environment.test.ts index 0ae82e957a1..1a9c33bbb78 100644 --- a/src/environments/environment.test.ts +++ b/src/environments/environment.test.ts @@ -359,6 +359,7 @@ export const environment: BuildConfig = { edit: { undoTimeout: 10000, // 10 seconds }, + showSubmitButton: false, }, themes: [ { diff --git a/src/themes/custom/app/collection-page/collection-page.component.ts b/src/themes/custom/app/collection-page/collection-page.component.ts index dd9435c3ae1..02b191e3d09 100644 --- a/src/themes/custom/app/collection-page/collection-page.component.ts +++ b/src/themes/custom/app/collection-page/collection-page.component.ts @@ -3,7 +3,10 @@ import { ChangeDetectionStrategy, Component, } from '@angular/core'; -import { RouterOutlet } from '@angular/router'; +import { + RouterLink, + RouterOutlet, +} from '@angular/router'; import { TranslateModule } from '@ngx-translate/core'; import { CollectionPageComponent as BaseComponent } from '../../../../app/collection-page/collection-page.component'; @@ -38,6 +41,7 @@ import { VarDirective } from '../../../../app/shared/utils/var.directive'; ComcolPageLogoComponent, DsoEditMenuComponent, ErrorComponent, + RouterLink, RouterOutlet, ThemedComcolPageBrowseByComponent, ThemedComcolPageContentComponent, From ef058b308d5f49a63a97dc31730ab6fff7ef001e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eike=20Martin=20L=C3=B6hden?= Date: Fri, 26 Jun 2026 07:13:39 +0200 Subject: [PATCH 07/18] Updated SubmitNewItemMenuProvider in order to depend on the showSubmitButton configuration. --- .../providers/submit-new-item.menu.spec.ts | 18 +++++++++++++++--- .../menu/providers/submit-new-item.menu.ts | 12 ++++++++++-- 2 files changed, 25 insertions(+), 5 deletions(-) diff --git a/src/app/shared/menu/providers/submit-new-item.menu.spec.ts b/src/app/shared/menu/providers/submit-new-item.menu.spec.ts index b7275ab0f6c..a8085a3e911 100644 --- a/src/app/shared/menu/providers/submit-new-item.menu.spec.ts +++ b/src/app/shared/menu/providers/submit-new-item.menu.spec.ts @@ -1,9 +1,14 @@ import { TestBed } from '@angular/core/testing'; +import { APP_CONFIG } from '@dspace/config/app-config.interface'; import { AuthorizationDataService } from '@dspace/core/data/feature-authorization/authorization-data.service'; import { Collection } from '@dspace/core/shared/collection.model'; import { COLLECTION } from '@dspace/core/shared/collection.resource-type'; -import { of } from 'rxjs'; +import { + Observable, + of, +} from 'rxjs'; +import { environment } from '../../../../environments/environment'; import { MenuItemType } from '../menu-item-type.model'; import { PartialMenuSection } from '../menu-provider.model'; import { SubmitNewItemMenuProvider } from './submit-new-item.menu'; @@ -34,7 +39,7 @@ describe('SubmitNewItemMenuProvider', () => { }); - let authorizationService; + let authorizationService: { isAuthorized: Observable ; }; beforeEach(() => { @@ -46,6 +51,7 @@ describe('SubmitNewItemMenuProvider', () => { providers: [ SubmitNewItemMenuProvider, { provide: AuthorizationDataService, useValue: authorizationService }, + { provide: APP_CONFIG, useValue: environment }, ], }); provider = TestBed.inject(SubmitNewItemMenuProvider); @@ -62,6 +68,12 @@ describe('SubmitNewItemMenuProvider', () => { done(); }); }); - }); + it('should set visibility depending on authorization and collection.showSubmitButton', done => { + provider.getSectionsForContext(dso).subscribe((sections) => { + expect(sections[0].visible).toEqual(authorizationService.isAuthorized && !environment.collection.showSubmitButton); + done(); + }); + }); + }); }); diff --git a/src/app/shared/menu/providers/submit-new-item.menu.ts b/src/app/shared/menu/providers/submit-new-item.menu.ts index 803d72c6b4e..668096944db 100644 --- a/src/app/shared/menu/providers/submit-new-item.menu.ts +++ b/src/app/shared/menu/providers/submit-new-item.menu.ts @@ -5,7 +5,14 @@ * * http://www.dspace.org/license/ */ -import { Injectable } from '@angular/core'; +import { + Inject, + Injectable, +} from '@angular/core'; +import { + APP_CONFIG, + AppConfig, +} from '@dspace/config/app-config.interface'; import { AuthorizationDataService } from '@dspace/core/data/feature-authorization/authorization-data.service'; import { FeatureID } from '@dspace/core/data/feature-authorization/feature-id'; import { DSpaceObject } from '@dspace/core/shared/dspace-object.model'; @@ -27,6 +34,7 @@ import { DSpaceObjectPageMenuProvider } from './helper-providers/dso.menu'; export class SubmitNewItemMenuProvider extends DSpaceObjectPageMenuProvider { constructor( protected authorizationDataService: AuthorizationDataService, + @Inject(APP_CONFIG) protected appConfig: AppConfig, ) { super(); } @@ -38,7 +46,7 @@ export class SubmitNewItemMenuProvider extends DSpaceObjectPageMenuProvider { map(([canSubmitItem]) => { return [ { - visible: canSubmitItem, + visible: canSubmitItem && !this.appConfig.collection.showSubmitButton, model: { type: MenuItemType.LINK, text: 'collection.submit.item', From 36a6426498e76f7145e0daac4e712fadfe31663c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eike=20Martin=20L=C3=B6hden?= Date: Fri, 26 Jun 2026 08:36:14 +0200 Subject: [PATCH 08/18] Switch to cy.env in order to hide sensitive data. --- cypress/e2e/collection-page.cy.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/cypress/e2e/collection-page.cy.ts b/cypress/e2e/collection-page.cy.ts index d7eed8005c4..a4d938d0756 100644 --- a/cypress/e2e/collection-page.cy.ts +++ b/cypress/e2e/collection-page.cy.ts @@ -5,7 +5,7 @@ const COLLECTION_PAGE = '/collections/'.concat(Cypress.expose('DSPACE_TEST_COLLE describe('Collection Page', () => { it('should pass accessibility tests', () => { - cy.visit('/collections/'.concat(Cypress.expose('DSPACE_TEST_COLLECTION'))); + cy.visit(COLLECTION_PAGE); // tag must be loaded cy.get('ds-collection-page').should('be.visible'); @@ -21,7 +21,9 @@ describe('Collection Page -> Collection-edit menu', () => { cy.visit(COLLECTION_PAGE); // These page elements are restricted, so we will be shown the login form. Fill it out & submit. - cy.loginViaForm(Cypress.expose('DSPACE_TEST_ADMIN_USER'), Cypress.expose('DSPACE_TEST_ADMIN_PASSWORD')); + cy.env(['DSPACE_TEST_ADMIN_USER', 'DSPACE_TEST_ADMIN_PASSWORD']).then(({ DSPACE_TEST_ADMIN_USER, DSPACE_TEST_ADMIN_PASSWORD }) => { + cy.loginViaForm(DSPACE_TEST_ADMIN_USER, DSPACE_TEST_ADMIN_PASSWORD); + }); }); it('Edit menu should exist for admins.', () => { From 5d27ae761d0baae5d2bff5dad1ce199eb700195c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eike=20Martin=20L=C3=B6hden?= Date: Sat, 27 Jun 2026 05:57:39 +0200 Subject: [PATCH 09/18] Fixed cypress e2e test. --- cypress/e2e/collection-page.cy.ts | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/cypress/e2e/collection-page.cy.ts b/cypress/e2e/collection-page.cy.ts index a4d938d0756..6269772532a 100644 --- a/cypress/e2e/collection-page.cy.ts +++ b/cypress/e2e/collection-page.cy.ts @@ -1,6 +1,7 @@ import { testA11y } from 'cypress/support/utils'; -const COLLECTION_PAGE = '/collections/'.concat(Cypress.expose('DSPACE_TEST_COLLECTION')); +const COLLECTION_PAGE = '/collections/'.concat(Cypress.env('DSPACE_TEST_COLLECTION')); +const LOGIN_PAGE = '/login'; describe('Collection Page', () => { @@ -17,13 +18,16 @@ describe('Collection Page', () => { describe('Collection Page -> Collection-edit menu', () => { beforeEach(() => { - // All tests start with visiting the Collection Page - cy.visit(COLLECTION_PAGE); + // All tests start with visiting the Login Page + cy.visit(LOGIN_PAGE); // These page elements are restricted, so we will be shown the login form. Fill it out & submit. cy.env(['DSPACE_TEST_ADMIN_USER', 'DSPACE_TEST_ADMIN_PASSWORD']).then(({ DSPACE_TEST_ADMIN_USER, DSPACE_TEST_ADMIN_PASSWORD }) => { cy.loginViaForm(DSPACE_TEST_ADMIN_USER, DSPACE_TEST_ADMIN_PASSWORD); }); + + // Now we can visit the collection page: + cy.visit(COLLECTION_PAGE); }); it('Edit menu should exist for admins.', () => { From 3947e26aa0bb985f4d59e94f30d378c50043b8f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eike=20Martin=20L=C3=B6hden?= Date: Sat, 11 Jul 2026 10:49:36 +0200 Subject: [PATCH 10/18] Removed deprecated Cypres.env() method. --- cypress/e2e/collection-page.cy.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cypress/e2e/collection-page.cy.ts b/cypress/e2e/collection-page.cy.ts index 6269772532a..9d276520547 100644 --- a/cypress/e2e/collection-page.cy.ts +++ b/cypress/e2e/collection-page.cy.ts @@ -1,6 +1,6 @@ import { testA11y } from 'cypress/support/utils'; -const COLLECTION_PAGE = '/collections/'.concat(Cypress.env('DSPACE_TEST_COLLECTION')); +const COLLECTION_PAGE = '/collections/'.concat(Cypress.expose('DSPACE_TEST_COLLECTION')); const LOGIN_PAGE = '/login'; describe('Collection Page', () => { From 8f72f503e973f7554aca2724c0418d18f653b993 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eike=20Martin=20L=C3=B6hden?= Date: Tue, 14 Jul 2026 07:59:13 +0200 Subject: [PATCH 11/18] Updated e2e tests. --- cypress/e2e/collection-page.cy.ts | 21 ++++++++++++------ cypress/e2e/community-page.cy.ts | 37 ++++++++++++++++++++++++++++++- 2 files changed, 50 insertions(+), 8 deletions(-) diff --git a/cypress/e2e/collection-page.cy.ts b/cypress/e2e/collection-page.cy.ts index 9d276520547..d13808447aa 100644 --- a/cypress/e2e/collection-page.cy.ts +++ b/cypress/e2e/collection-page.cy.ts @@ -1,7 +1,6 @@ import { testA11y } from 'cypress/support/utils'; const COLLECTION_PAGE = '/collections/'.concat(Cypress.expose('DSPACE_TEST_COLLECTION')); -const LOGIN_PAGE = '/login'; describe('Collection Page', () => { @@ -18,20 +17,28 @@ describe('Collection Page', () => { describe('Collection Page -> Collection-edit menu', () => { beforeEach(() => { - // All tests start with visiting the Login Page - cy.visit(LOGIN_PAGE); + cy.visit(COLLECTION_PAGE); + // Open login menu in header & verify tag is visible + cy.get('[data-test="login-menu"]').click(); + cy.get('.form-login').should('be.visible'); - // These page elements are restricted, so we will be shown the login form. Fill it out & submit. + // Login, and the tag should no longer exist cy.env(['DSPACE_TEST_ADMIN_USER', 'DSPACE_TEST_ADMIN_PASSWORD']).then(({ DSPACE_TEST_ADMIN_USER, DSPACE_TEST_ADMIN_PASSWORD }) => { cy.loginViaForm(DSPACE_TEST_ADMIN_USER, DSPACE_TEST_ADMIN_PASSWORD); }); - - // Now we can visit the collection page: - cy.visit(COLLECTION_PAGE); + cy.get('ds-log-in').should('not.exist'); }); it('Edit menu should exist for admins.', () => { // tag must be loaded cy.get('ds-dso-edit-menu').should('be.visible'); }); + + it('Options menu should include submit item on collection pages.', () => { + // Open the Options menu and verify the Submit item entry is available + cy.get('ds-dso-edit-menu button[aria-label="Options"]').click(); + cy.get('[data-test="link-menu-item.collection.submit.item"]') + .should('be.visible') + .and('contain', 'Submit item'); + }); }); diff --git a/cypress/e2e/community-page.cy.ts b/cypress/e2e/community-page.cy.ts index 8ccc97d63cb..be5474d52c5 100644 --- a/cypress/e2e/community-page.cy.ts +++ b/cypress/e2e/community-page.cy.ts @@ -1,9 +1,12 @@ import { testA11y } from 'cypress/support/utils'; +const COMMUNITY_PAGE = '/communities/'.concat(Cypress.expose('DSPACE_TEST_COMMUNITY')); + + describe('Community Page', () => { it('should pass accessibility tests', () => { - cy.visit('/communities/'.concat(Cypress.expose('DSPACE_TEST_COMMUNITY'))); + cy.visit(COMMUNITY_PAGE); // tag must be loaded cy.get('ds-community-page').should('be.visible'); @@ -12,3 +15,35 @@ describe('Community Page', () => { testA11y('ds-community-page'); }); }); + + +describe('Community Page -> Community-edit menu', () => { + beforeEach(() => { + cy.visit(COMMUNITY_PAGE); + // Open login menu in header & verify tag is visible + cy.get('[data-test="login-menu"]').click(); + cy.get('.form-login').should('be.visible'); + + // Login, and the tag should no longer exist + cy.env(['DSPACE_TEST_ADMIN_USER', 'DSPACE_TEST_ADMIN_PASSWORD']).then(({ DSPACE_TEST_ADMIN_USER, DSPACE_TEST_ADMIN_PASSWORD }) => { + cy.loginViaForm(DSPACE_TEST_ADMIN_USER, DSPACE_TEST_ADMIN_PASSWORD); + }); + cy.get('ds-log-in').should('not.exist'); + }); + + it('Edit menu should exist for admins.', () => { + // tag must be loaded + cy.get('ds-dso-edit-menu').should('be.visible'); + }); + + it('Options menu should include Add-Community and Add Collections Buttons.', () => { + // Open the Options menu and verify the Add Community and Add Collection entries are available + cy.get('ds-dso-edit-menu button[aria-label="Options"]').click(); + cy.get('[data-test="link-menu-item.community.add.sub-community"]') + .should('be.visible') + .and('contain', 'Add Community'); + cy.get('[data-test="link-menu-item.community.add.sub-collection"]') + .should('be.visible') + .and('contain', 'Add Collection'); + }); +}); From 0d93321a7140ee30b00a08f2e5c944b8671b7003 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eike=20Martin=20L=C3=B6hden?= Date: Thu, 13 Aug 2026 16:03:41 +0200 Subject: [PATCH 12/18] Renamed menu, fixed doc strings and corrected authorization checks. --- src/app/app.menus.ts | 4 ++-- ...d-sub-objects.menu.ts => add-sub-com-col.menu.ts} | 12 ++++++------ .../shared/menu/providers/submit-new-item.menu.ts | 2 +- 3 files changed, 9 insertions(+), 9 deletions(-) rename src/app/shared/menu/providers/{add-sub-objects.menu.ts => add-sub-com-col.menu.ts} (83%) diff --git a/src/app/app.menus.ts b/src/app/app.menus.ts index 605068cd804..4b4310ae7bb 100644 --- a/src/app/app.menus.ts +++ b/src/app/app.menus.ts @@ -9,7 +9,7 @@ import { buildMenuStructure } from './shared/menu/menu.structure'; import { MenuID } from './shared/menu/menu-id.model'; import { MenuRoute } from './shared/menu/menu-route.model'; import { AccessControlMenuProvider } from './shared/menu/providers/access-control.menu'; -import { AddSubObjectsMenuProvider } from './shared/menu/providers/add-sub-objects.menu'; +import { AddSubComColMenuProvider } from './shared/menu/providers/add-sub-com-col.menu'; import { AdminSearchMenuProvider } from './shared/menu/providers/admin-search.menu'; import { AuditLogsMenuProvider } from './shared/menu/providers/audit-item.menu'; import { AuditOverviewMenuProvider } from './shared/menu/providers/audit-overview.menu'; @@ -98,7 +98,7 @@ export const MENUS = buildMenuStructure({ MenuRoute.COLLECTION_PAGE, MenuRoute.ITEM_PAGE, ), - AddSubObjectsMenuProvider.onRoute( + AddSubComColMenuProvider.onRoute( MenuRoute.COMMUNITY_PAGE, ), SubmitNewItemMenuProvider.onRoute( diff --git a/src/app/shared/menu/providers/add-sub-objects.menu.ts b/src/app/shared/menu/providers/add-sub-com-col.menu.ts similarity index 83% rename from src/app/shared/menu/providers/add-sub-objects.menu.ts rename to src/app/shared/menu/providers/add-sub-com-col.menu.ts index ca69808beac..5bb964c12c6 100644 --- a/src/app/shared/menu/providers/add-sub-objects.menu.ts +++ b/src/app/shared/menu/providers/add-sub-com-col.menu.ts @@ -21,10 +21,10 @@ import { PartialMenuSection } from '../menu-provider.model'; import { DSpaceObjectPageMenuProvider } from './helper-providers/dso.menu'; /** - * Menu provider to create the "Edit" option in the DSO edit menu + * Menu provider to create the "Add sub Community" and "Add sub collection" option in the DSO edit menu */ @Injectable() -export class AddSubObjectsMenuProvider extends DSpaceObjectPageMenuProvider { +export class AddSubComColMenuProvider extends DSpaceObjectPageMenuProvider { constructor( protected authorizationDataService: AuthorizationDataService, ) { @@ -33,12 +33,12 @@ export class AddSubObjectsMenuProvider extends DSpaceObjectPageMenuProvider { public getSectionsForContext(dso: DSpaceObject): Observable { return combineLatest([ - this.authorizationDataService.isAuthorized(FeatureID.CanEditMetadata, dso.self), + this.authorizationDataService.isAuthorized(FeatureID.IsCommunityAdmin), ]).pipe( - map(([canEditObject]) => { + map(([isCommunityAdmin]) => { return [ { - visible: canEditObject, + visible: isCommunityAdmin, model: { type: MenuItemType.LINK, text: 'community.add.sub-community', @@ -50,7 +50,7 @@ export class AddSubObjectsMenuProvider extends DSpaceObjectPageMenuProvider { icon: 'plus', }, { - visible: canEditObject, + visible: isCommunityAdmin, model: { type: MenuItemType.LINK, text: 'community.add.sub-collection', diff --git a/src/app/shared/menu/providers/submit-new-item.menu.ts b/src/app/shared/menu/providers/submit-new-item.menu.ts index 668096944db..77d709764d4 100644 --- a/src/app/shared/menu/providers/submit-new-item.menu.ts +++ b/src/app/shared/menu/providers/submit-new-item.menu.ts @@ -28,7 +28,7 @@ import { PartialMenuSection } from '../menu-provider.model'; import { DSpaceObjectPageMenuProvider } from './helper-providers/dso.menu'; /** - * Menu provider to create the "Edit" option in the DSO edit menu + * Menu provider to create the "Submit item" option in the DSO edit menu */ @Injectable() export class SubmitNewItemMenuProvider extends DSpaceObjectPageMenuProvider { From 964f796d45ba19204881e11e118757aefbbcc538 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eike=20Martin=20L=C3=B6hden?= Date: Thu, 13 Aug 2026 17:14:37 +0200 Subject: [PATCH 13/18] Updated names in spec test. --- ...jects.menu.spec.ts => add-sub-com-col.menu.spec.ts} | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) rename src/app/shared/menu/providers/{add-sub-objects.menu.spec.ts => add-sub-com-col.menu.spec.ts} (88%) diff --git a/src/app/shared/menu/providers/add-sub-objects.menu.spec.ts b/src/app/shared/menu/providers/add-sub-com-col.menu.spec.ts similarity index 88% rename from src/app/shared/menu/providers/add-sub-objects.menu.spec.ts rename to src/app/shared/menu/providers/add-sub-com-col.menu.spec.ts index 7419a4289a5..65a991690ad 100644 --- a/src/app/shared/menu/providers/add-sub-objects.menu.spec.ts +++ b/src/app/shared/menu/providers/add-sub-com-col.menu.spec.ts @@ -6,9 +6,9 @@ import { of } from 'rxjs'; import { MenuItemType } from '../menu-item-type.model'; import { PartialMenuSection } from '../menu-provider.model'; -import { AddSubObjectsMenuProvider } from './add-sub-objects.menu'; +import { AddSubComColMenuProvider } from './add-sub-com-col.menu'; -describe('AddSubObjectsMenuProvider', () => { +describe('AddSubComColMenuProvider', () => { const expectedSections: PartialMenuSection[] = [ { @@ -37,7 +37,7 @@ describe('AddSubObjectsMenuProvider', () => { }, ]; - let provider: AddSubObjectsMenuProvider; + let provider: AddSubComColMenuProvider; const dso: Community = Object.assign(new Community(), { type: COMMUNITY.value, @@ -56,11 +56,11 @@ describe('AddSubObjectsMenuProvider', () => { TestBed.configureTestingModule({ providers: [ - AddSubObjectsMenuProvider, + AddSubComColMenuProvider, { provide: AuthorizationDataService, useValue: authorizationService }, ], }); - provider = TestBed.inject(AddSubObjectsMenuProvider); + provider = TestBed.inject(AddSubComColMenuProvider); }); it('should be created', () => { From 303be422bf6c578322aff79e1ceed80214c230a5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eike=20Martin=20L=C3=B6hden?= Date: Fri, 14 Aug 2026 08:45:15 +0200 Subject: [PATCH 14/18] Removed submit-config and move submit-button to dso-edit-menu. --- config/config.example.yml | 2 -- cypress/e2e/collection-page.cy.ts | 21 ++++++++++++++++--- src/app/app.menus.ts | 6 +++--- .../collection-page.component.html | 4 ---- .../collection-page.component.ts | 17 --------------- .../dso-edit-menu-section.component.html | 6 +++--- .../dso-edit-menu-section.component.scss | 3 +++ .../dso-edit-menu-section.component.ts | 11 ++++++++++ .../menu/providers/submit-new-item.menu.ts | 12 ++--------- .../collection-page-config.interface.ts | 1 - src/config/default-app-config.ts | 1 - src/environments/environment.test.ts | 1 - .../collection-page.component.ts | 6 +----- 13 files changed, 41 insertions(+), 50 deletions(-) diff --git a/config/config.example.yml b/config/config.example.yml index 3c1b5e48f8c..6f6c67d1ae6 100644 --- a/config/config.example.yml +++ b/config/config.example.yml @@ -478,8 +478,6 @@ collection: showSidebar: true edit: undoTimeout: 10000 # 10 seconds - # Whether to show a submit button on item pages instead of having an entry in the dso-edit menu. - showSubmitButton: false # Theme Config themes: diff --git a/cypress/e2e/collection-page.cy.ts b/cypress/e2e/collection-page.cy.ts index d13808447aa..b5b9d06930a 100644 --- a/cypress/e2e/collection-page.cy.ts +++ b/cypress/e2e/collection-page.cy.ts @@ -33,11 +33,26 @@ describe('Collection Page -> Collection-edit menu', () => { // tag must be loaded cy.get('ds-dso-edit-menu').should('be.visible'); }); +}); + +describe('Collection Page -> Submit-item button', () => { + beforeEach(() => { + cy.visit(COLLECTION_PAGE); + // Open login menu in header & verify tag is visible + cy.get('[data-test="login-menu"]').click(); + cy.get('.form-login').should('be.visible'); + + // Login, and the tag should no longer exist + cy.env(['DSPACE_TEST_SUBMIT_USER', 'DSPACE_TEST_SUBMIT_USER_PASSWORD']).then(({ DSPACE_TEST_SUBMIT_USER, DSPACE_TEST_SUBMIT_USER_PASSWORD }) => { + cy.loginViaForm(DSPACE_TEST_SUBMIT_USER, DSPACE_TEST_SUBMIT_USER_PASSWORD); + }); + cy.get('ds-log-in').should('not.exist'); + }); - it('Options menu should include submit item on collection pages.', () => { + it('Submit item button should exist for submitters on collection pages.', () => { // Open the Options menu and verify the Submit item entry is available - cy.get('ds-dso-edit-menu button[aria-label="Options"]').click(); - cy.get('[data-test="link-menu-item.collection.submit.item"]') + //cy.get('ds-dso-edit-menu a[aria-label="Options"]').click(); + cy.get('ds-dso-edit-menu a[href="/submit"]') .should('be.visible') .and('contain', 'Submit item'); }); diff --git a/src/app/app.menus.ts b/src/app/app.menus.ts index 4b4310ae7bb..1fc4c5aebde 100644 --- a/src/app/app.menus.ts +++ b/src/app/app.menus.ts @@ -85,6 +85,9 @@ export const MENUS = buildMenuStructure({ EditUserAgreementMenuProvider, ], [MenuID.DSO_EDIT]: [ + SubmitNewItemMenuProvider.onRoute( + MenuRoute.COLLECTION_PAGE, + ), DsoOptionMenuProvider.withSubs([ EditItemMenuProvider.onRoute( MenuRoute.ITEM_PAGE, @@ -101,9 +104,6 @@ export const MENUS = buildMenuStructure({ AddSubComColMenuProvider.onRoute( MenuRoute.COMMUNITY_PAGE, ), - SubmitNewItemMenuProvider.onRoute( - MenuRoute.COLLECTION_PAGE, - ), WithdrawnReinstateItemMenuProvider.onRoute( MenuRoute.ITEM_PAGE, ), diff --git a/src/app/collection-page/collection-page.component.html b/src/app/collection-page/collection-page.component.html index 78ee84c27d2..407adeec926 100644 --- a/src/app/collection-page/collection-page.component.html +++ b/src/app/collection-page/collection-page.component.html @@ -34,10 +34,6 @@ [hasInnerHtml]="true" [title]="'collection.page.news'"> - @if (showSubmitButton$ | async) { - - - } diff --git a/src/app/collection-page/collection-page.component.ts b/src/app/collection-page/collection-page.component.ts index 697cb26ffe7..f9ec6606999 100644 --- a/src/app/collection-page/collection-page.component.ts +++ b/src/app/collection-page/collection-page.component.ts @@ -2,19 +2,13 @@ import { AsyncPipe } from '@angular/common'; import { ChangeDetectionStrategy, Component, - Inject, OnInit, } from '@angular/core'; import { ActivatedRoute, Router, - RouterLink, RouterOutlet, } from '@angular/router'; -import { - APP_CONFIG, - AppConfig, -} from '@dspace/config/app-config.interface'; import { AuthService } from '@dspace/core/auth/auth.service'; import { DSONameService } from '@dspace/core/breadcrumbs/dso-name.service'; import { SortOptions } from '@dspace/core/cache/models/sort-options.model'; @@ -69,7 +63,6 @@ import { VarDirective } from '../shared/utils/var.directive'; ComcolPageLogoComponent, DsoEditMenuComponent, ErrorComponent, - RouterLink, RouterOutlet, ThemedComcolPageBrowseByComponent, ThemedComcolPageContentComponent, @@ -95,18 +88,12 @@ export class CollectionPageComponent implements OnInit { */ collectionPageRoute$: Observable; - /** - * Whether to show a submit button for users on the collection page. - */ - showSubmitButton$: Observable; - constructor( protected route: ActivatedRoute, protected router: Router, protected authService: AuthService, protected authorizationDataService: AuthorizationDataService, public dsoNameService: DSONameService, - @Inject(APP_CONFIG) protected appConfig: AppConfig, ) { } @@ -127,10 +114,6 @@ export class CollectionPageComponent implements OnInit { getAllSucceededRemoteDataPayload(), map((collection) => getCollectionPageRoute(collection.id)), ); - - this.showSubmitButton$ = this.authorizationDataService.isAuthorized(FeatureID.CanSubmit).pipe( - map(authorized => authorized && this.appConfig.collection.showSubmitButton), - ); } isNotEmpty(object: any) { diff --git a/src/app/shared/dso-page/dso-edit-menu/dso-edit-menu-section/dso-edit-menu-section.component.html b/src/app/shared/dso-page/dso-edit-menu/dso-edit-menu-section/dso-edit-menu-section.component.html index b2c8ca84ce9..5d6b9d31c6c 100644 --- a/src/app/shared/dso-page/dso-edit-menu/dso-edit-menu-section/dso-edit-menu-section.component.html +++ b/src/app/shared/dso-page/dso-edit-menu/dso-edit-menu-section/dso-edit-menu-section.component.html @@ -5,13 +5,13 @@ - {{itemModel.text | translate}} + {{itemModel.text | translate}} } @if (section.model.disabled) { } @@ -23,7 +23,7 @@ } diff --git a/src/app/shared/dso-page/dso-edit-menu/dso-edit-menu-section/dso-edit-menu-section.component.scss b/src/app/shared/dso-page/dso-edit-menu/dso-edit-menu-section/dso-edit-menu-section.component.scss index cf0e81c5538..aed87c714b7 100644 --- a/src/app/shared/dso-page/dso-edit-menu/dso-edit-menu-section/dso-edit-menu-section.component.scss +++ b/src/app/shared/dso-page/dso-edit-menu/dso-edit-menu-section/dso-edit-menu-section.component.scss @@ -1,3 +1,6 @@ .btn-dark { background-color: var(--ds-admin-sidebar-bg); } +.dso-button-menu { + width: max-content; +} diff --git a/src/app/shared/dso-page/dso-edit-menu/dso-edit-menu-section/dso-edit-menu-section.component.ts b/src/app/shared/dso-page/dso-edit-menu/dso-edit-menu-section/dso-edit-menu-section.component.ts index ffcd12ddb0c..2f57a17b5ba 100644 --- a/src/app/shared/dso-page/dso-edit-menu/dso-edit-menu-section/dso-edit-menu-section.component.ts +++ b/src/app/shared/dso-page/dso-edit-menu/dso-edit-menu-section/dso-edit-menu-section.component.ts @@ -1,15 +1,20 @@ + +import { AsyncPipe } from '@angular/common'; import { Component, Injector, OnInit, } from '@angular/core'; import { RouterLink } from '@angular/router'; +import { WidthCategory } from '@dspace/core/shared/host-window-type'; import { isNotEmpty } from '@dspace/shared/utils/empty.util'; import { NgbTooltip } from '@ng-bootstrap/ng-bootstrap'; import { TranslateModule } from '@ngx-translate/core'; +import { Observable } from 'rxjs'; import { AbstractMenuSectionComponent } from 'src/app/shared/menu/menu-section/abstract-menu-section.component'; import { BtnDisabledDirective } from '../../../btn-disabled.directive'; +import { HostWindowService } from '../../../host-window.service'; import { MenuService } from '../../../menu/menu.service'; import { MenuID } from '../../../menu/menu-id.model'; import { rendersSectionForMenu } from '../../../menu/menu-section.decorator'; @@ -23,6 +28,7 @@ import { ThemeService } from '../../../theme-support/theme.service'; templateUrl: './dso-edit-menu-section.component.html', styleUrls: ['./dso-edit-menu-section.component.scss'], imports: [ + AsyncPipe, BtnDisabledDirective, NgbTooltip, RouterLink, @@ -35,17 +41,22 @@ export class DsoEditMenuSectionComponent extends AbstractMenuSectionComponent im menuID: MenuID = MenuID.DSO_EDIT; hasLink: boolean; canActivate: boolean; + public isMobile$: Observable; + + maxMobileWidth = WidthCategory.SM; constructor( protected menuService: MenuService, protected injector: Injector, protected themeService: ThemeService, + protected windowService: HostWindowService, ) { super( menuService, injector, themeService, ); + this.isMobile$ = this.windowService.isUpTo(this.maxMobileWidth); } ngOnInit(): void { diff --git a/src/app/shared/menu/providers/submit-new-item.menu.ts b/src/app/shared/menu/providers/submit-new-item.menu.ts index 77d709764d4..d7c8d0535db 100644 --- a/src/app/shared/menu/providers/submit-new-item.menu.ts +++ b/src/app/shared/menu/providers/submit-new-item.menu.ts @@ -5,14 +5,7 @@ * * http://www.dspace.org/license/ */ -import { - Inject, - Injectable, -} from '@angular/core'; -import { - APP_CONFIG, - AppConfig, -} from '@dspace/config/app-config.interface'; +import { Injectable } from '@angular/core'; import { AuthorizationDataService } from '@dspace/core/data/feature-authorization/authorization-data.service'; import { FeatureID } from '@dspace/core/data/feature-authorization/feature-id'; import { DSpaceObject } from '@dspace/core/shared/dspace-object.model'; @@ -34,7 +27,6 @@ import { DSpaceObjectPageMenuProvider } from './helper-providers/dso.menu'; export class SubmitNewItemMenuProvider extends DSpaceObjectPageMenuProvider { constructor( protected authorizationDataService: AuthorizationDataService, - @Inject(APP_CONFIG) protected appConfig: AppConfig, ) { super(); } @@ -46,7 +38,7 @@ export class SubmitNewItemMenuProvider extends DSpaceObjectPageMenuProvider { map(([canSubmitItem]) => { return [ { - visible: canSubmitItem && !this.appConfig.collection.showSubmitButton, + visible: canSubmitItem, model: { type: MenuItemType.LINK, text: 'collection.submit.item', diff --git a/src/config/collection-page-config.interface.ts b/src/config/collection-page-config.interface.ts index c477d1aef71..5aec06daea2 100644 --- a/src/config/collection-page-config.interface.ts +++ b/src/config/collection-page-config.interface.ts @@ -9,7 +9,6 @@ export interface CollectionPageConfig extends Config { edit: { undoTimeout: number; }; - showSubmitButton: boolean; } /** diff --git a/src/config/default-app-config.ts b/src/config/default-app-config.ts index 839e9d90090..fd703d4d024 100644 --- a/src/config/default-app-config.ts +++ b/src/config/default-app-config.ts @@ -456,7 +456,6 @@ export class DefaultAppConfig implements AppConfig { edit: { undoTimeout: 10000, // 10 seconds }, - showSubmitButton: false, }; suggestion: SuggestionConfig[] = [ diff --git a/src/environments/environment.test.ts b/src/environments/environment.test.ts index 1a9c33bbb78..0ae82e957a1 100644 --- a/src/environments/environment.test.ts +++ b/src/environments/environment.test.ts @@ -359,7 +359,6 @@ export const environment: BuildConfig = { edit: { undoTimeout: 10000, // 10 seconds }, - showSubmitButton: false, }, themes: [ { diff --git a/src/themes/custom/app/collection-page/collection-page.component.ts b/src/themes/custom/app/collection-page/collection-page.component.ts index 02b191e3d09..dd9435c3ae1 100644 --- a/src/themes/custom/app/collection-page/collection-page.component.ts +++ b/src/themes/custom/app/collection-page/collection-page.component.ts @@ -3,10 +3,7 @@ import { ChangeDetectionStrategy, Component, } from '@angular/core'; -import { - RouterLink, - RouterOutlet, -} from '@angular/router'; +import { RouterOutlet } from '@angular/router'; import { TranslateModule } from '@ngx-translate/core'; import { CollectionPageComponent as BaseComponent } from '../../../../app/collection-page/collection-page.component'; @@ -41,7 +38,6 @@ import { VarDirective } from '../../../../app/shared/utils/var.directive'; ComcolPageLogoComponent, DsoEditMenuComponent, ErrorComponent, - RouterLink, RouterOutlet, ThemedComcolPageBrowseByComponent, ThemedComcolPageContentComponent, From 83e50d3ebac14ea20d819cce4031fa38488b9e43 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eike=20Martin=20L=C3=B6hden?= Date: Fri, 14 Aug 2026 10:16:59 +0200 Subject: [PATCH 15/18] Updated spec-tests. --- .../dso-edit-menu-section.component.spec.ts | 4 ++++ src/app/shared/menu/providers/submit-new-item.menu.spec.ts | 7 ------- 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/src/app/shared/dso-page/dso-edit-menu/dso-edit-menu-section/dso-edit-menu-section.component.spec.ts b/src/app/shared/dso-page/dso-edit-menu/dso-edit-menu-section/dso-edit-menu-section.component.spec.ts index 7baf6cba144..8be371b7894 100644 --- a/src/app/shared/dso-page/dso-edit-menu/dso-edit-menu-section/dso-edit-menu-section.component.spec.ts +++ b/src/app/shared/dso-page/dso-edit-menu/dso-edit-menu-section/dso-edit-menu-section.component.spec.ts @@ -9,13 +9,16 @@ import { ActivatedRoute, Router, } from '@angular/router'; +import { WidthCategory } from '@dspace/core/shared/host-window-type'; import { ActivatedRouteStub } from '@dspace/core/testing/active-router.stub'; import { CSSVariableServiceStub } from '@dspace/core/testing/css-variable-service.stub'; +import { HostWindowServiceStub } from '@dspace/core/testing/host-window-service.stub'; import { RouterStub } from '@dspace/core/testing/router.stub'; import { TranslateModule } from '@ngx-translate/core'; import { of } from 'rxjs'; import { MenuItemType } from 'src/app/shared/menu/menu-item-type.model'; +import { HostWindowService } from '../../../host-window.service'; import { MenuService } from '../../../menu/menu.service'; import { OnClickMenuItemModel } from '../../../menu/menu-item/models/onclick.model'; import { MenuServiceStub } from '../../../menu/menu-service.stub'; @@ -38,6 +41,7 @@ function initAsync(menuService: MenuServiceStub) { { provide: Router, useValue: new RouterStub() }, { provide: ActivatedRoute, useValue: new ActivatedRouteStub() }, { provide: ThemeService, useValue: getMockThemeService() }, + { provide: HostWindowService, useValue: new HostWindowServiceStub(WidthCategory.MD) }, ], }).compileComponents(); })); diff --git a/src/app/shared/menu/providers/submit-new-item.menu.spec.ts b/src/app/shared/menu/providers/submit-new-item.menu.spec.ts index a8085a3e911..9791fe4ced9 100644 --- a/src/app/shared/menu/providers/submit-new-item.menu.spec.ts +++ b/src/app/shared/menu/providers/submit-new-item.menu.spec.ts @@ -68,12 +68,5 @@ describe('SubmitNewItemMenuProvider', () => { done(); }); }); - - it('should set visibility depending on authorization and collection.showSubmitButton', done => { - provider.getSectionsForContext(dso).subscribe((sections) => { - expect(sections[0].visible).toEqual(authorizationService.isAuthorized && !environment.collection.showSubmitButton); - done(); - }); - }); }); }); From a3b5fb80a64eac0a879a9bc04d9e3b84f8fe3ad3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eike=20Martin=20L=C3=B6hden?= Date: Fri, 14 Aug 2026 11:29:44 +0200 Subject: [PATCH 16/18] Use renderIconOnly attribute to determine wether to display the text of the buttons. --- .../dso-edit-menu-section.component.html | 8 ++++---- src/app/shared/menu/menu-provider.model.ts | 1 + src/app/shared/menu/menu-provider.service.ts | 1 + src/app/shared/menu/menu-section.model.ts | 5 +++++ src/app/shared/menu/providers/submit-new-item.menu.ts | 1 + 5 files changed, 12 insertions(+), 4 deletions(-) diff --git a/src/app/shared/dso-page/dso-edit-menu/dso-edit-menu-section/dso-edit-menu-section.component.html b/src/app/shared/dso-page/dso-edit-menu/dso-edit-menu-section/dso-edit-menu-section.component.html index 5d6b9d31c6c..3171b1cca0f 100644 --- a/src/app/shared/dso-page/dso-edit-menu/dso-edit-menu-section/dso-edit-menu-section.component.html +++ b/src/app/shared/dso-page/dso-edit-menu/dso-edit-menu-section/dso-edit-menu-section.component.html @@ -3,15 +3,15 @@ [ngbTooltip]="itemModel.text | translate"> @if (!section.model.disabled) { + [routerLink]="itemModel.link" [queryParams]="itemModel.queryParams"> - {{itemModel.text | translate}} + {{itemModel.text | translate}} } @if (section.model.disabled) { } @@ -23,7 +23,7 @@ } diff --git a/src/app/shared/menu/menu-provider.model.ts b/src/app/shared/menu/menu-provider.model.ts index 80ca6c15a4e..8dab52fbd86 100644 --- a/src/app/shared/menu/menu-provider.model.ts +++ b/src/app/shared/menu/menu-provider.model.ts @@ -31,6 +31,7 @@ export interface PartialMenuSection { shouldPersistOnRouteChange?: boolean; icon?: string; alwaysRenderExpandable?: boolean; + renderIconOnly?: boolean; } /** diff --git a/src/app/shared/menu/menu-provider.service.ts b/src/app/shared/menu/menu-provider.service.ts index 1d2b393b69f..0efeae37a62 100644 --- a/src/app/shared/menu/menu-provider.service.ts +++ b/src/app/shared/menu/menu-provider.service.ts @@ -210,6 +210,7 @@ export class MenuProviderService { active: section.active ?? false, shouldPersistOnRouteChange: section.shouldPersistOnRouteChange ?? provider.shouldPersistOnRouteChange, alwaysRenderExpandable: section.alwaysRenderExpandable ?? provider.alwaysRenderExpandable, + renderIconOnly: section.renderIconOnly ?? false, }); } diff --git a/src/app/shared/menu/menu-section.model.ts b/src/app/shared/menu/menu-section.model.ts index 8462e27fd8c..84b978ec304 100644 --- a/src/app/shared/menu/menu-section.model.ts +++ b/src/app/shared/menu/menu-section.model.ts @@ -69,4 +69,9 @@ export interface MenuSection { * This section will not be rendered when it has no visible children */ alwaysRenderExpandable?: boolean; + + /** + * Whether only to render the icon of the menu section not the text. + */ + renderIconOnly?: boolean; } diff --git a/src/app/shared/menu/providers/submit-new-item.menu.ts b/src/app/shared/menu/providers/submit-new-item.menu.ts index d7c8d0535db..36c64c5e548 100644 --- a/src/app/shared/menu/providers/submit-new-item.menu.ts +++ b/src/app/shared/menu/providers/submit-new-item.menu.ts @@ -48,6 +48,7 @@ export class SubmitNewItemMenuProvider extends DSpaceObjectPageMenuProvider { }, } as LinkMenuItemModel, icon: 'plus', + renderIconOnly: false, }, ] as PartialMenuSection[]; }), From 8b050d9b8b350c347bac9f449d649c31a48e8eb2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eike=20Martin=20L=C3=B6hden?= Date: Fri, 14 Aug 2026 12:12:25 +0200 Subject: [PATCH 17/18] Fixed spec tests. --- src/app/shared/menu/menu-provider.service.spec.ts | 2 ++ src/app/shared/menu/providers/submit-new-item.menu.spec.ts | 1 + 2 files changed, 3 insertions(+) diff --git a/src/app/shared/menu/menu-provider.service.spec.ts b/src/app/shared/menu/menu-provider.service.spec.ts index 5f68e76c224..0ed93da48b8 100644 --- a/src/app/shared/menu/menu-provider.service.spec.ts +++ b/src/app/shared/menu/menu-provider.service.spec.ts @@ -32,6 +32,7 @@ describe('MenuProviderService', () => { public alwaysRenderExpandable: boolean, public sections: PartialMenuSection[], public renderBrowserOnly: boolean = false, + public renderIconOnly: boolean = false, ) { super(); } @@ -104,6 +105,7 @@ describe('MenuProviderService', () => { active: false, shouldPersistOnRouteChange: sectionToAdd.shouldPersistOnRouteChange ?? provider.shouldPersistOnRouteChange, alwaysRenderExpandable: sectionToAdd.alwaysRenderExpandable ?? provider.alwaysRenderExpandable, + renderIconOnly: sectionToAdd.renderIconOnly ?? false, }; } diff --git a/src/app/shared/menu/providers/submit-new-item.menu.spec.ts b/src/app/shared/menu/providers/submit-new-item.menu.spec.ts index 9791fe4ced9..e18580bb66a 100644 --- a/src/app/shared/menu/providers/submit-new-item.menu.spec.ts +++ b/src/app/shared/menu/providers/submit-new-item.menu.spec.ts @@ -27,6 +27,7 @@ describe('SubmitNewItemMenuProvider', () => { }, }, icon: 'plus', + renderIconOnly: false, }, ]; From 938f37faaed3e2787d600b5e876e7eb024327f2c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eike=20Martin=20L=C3=B6hden?= Date: Fri, 14 Aug 2026 13:21:38 +0200 Subject: [PATCH 18/18] Fixed href in cypres e2e test. --- cypress/e2e/collection-page.cy.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cypress/e2e/collection-page.cy.ts b/cypress/e2e/collection-page.cy.ts index b5b9d06930a..4843272b6a1 100644 --- a/cypress/e2e/collection-page.cy.ts +++ b/cypress/e2e/collection-page.cy.ts @@ -52,7 +52,7 @@ describe('Collection Page -> Submit-item button', () => { it('Submit item button should exist for submitters on collection pages.', () => { // Open the Options menu and verify the Submit item entry is available //cy.get('ds-dso-edit-menu a[aria-label="Options"]').click(); - cy.get('ds-dso-edit-menu a[href="/submit"]') + cy.get(`ds-dso-edit-menu a[href="/submit?collection=${Cypress.expose('DSPACE_TEST_COLLECTION')}"]`) .should('be.visible') .and('contain', 'Submit item'); });