mirror of
https://github.com/DSpace/dspace-angular.git
synced 2025-10-15 14:03:06 +00:00
94390: Replace DSO page edit buttons with a menu
This commit is contained in:
@@ -1,7 +1,7 @@
|
|||||||
<div class="sidebar-section">
|
<div class="sidebar-section">
|
||||||
<a class="nav-item nav-link d-flex flex-row flex-nowrap"
|
<a class="nav-item nav-link d-flex flex-row flex-nowrap"
|
||||||
[ngClass]="{ disabled: !hasLink }"
|
[ngClass]="{ disabled: isDisabled }"
|
||||||
[attr.aria-disabled]="!hasLink"
|
[attr.aria-disabled]="isDisabled"
|
||||||
[attr.aria-labelledby]="'sidebarName-' + section.id"
|
[attr.aria-labelledby]="'sidebarName-' + section.id"
|
||||||
[title]="('menu.section.icon.' + section.id) | translate"
|
[title]="('menu.section.icon.' + section.id) | translate"
|
||||||
[routerLink]="itemModel.link"
|
[routerLink]="itemModel.link"
|
||||||
|
@@ -17,14 +17,16 @@ describe('AdminSidebarSectionComponent', () => {
|
|||||||
const menuService = new MenuServiceStub();
|
const menuService = new MenuServiceStub();
|
||||||
const iconString = 'test';
|
const iconString = 'test';
|
||||||
|
|
||||||
|
describe('when not disabled', () => {
|
||||||
|
|
||||||
beforeEach(waitForAsync(() => {
|
beforeEach(waitForAsync(() => {
|
||||||
TestBed.configureTestingModule({
|
TestBed.configureTestingModule({
|
||||||
imports: [NoopAnimationsModule, RouterTestingModule, TranslateModule.forRoot()],
|
imports: [NoopAnimationsModule, RouterTestingModule, TranslateModule.forRoot()],
|
||||||
declarations: [AdminSidebarSectionComponent, TestComponent],
|
declarations: [AdminSidebarSectionComponent, TestComponent],
|
||||||
providers: [
|
providers: [
|
||||||
{ provide: 'sectionDataProvider', useValue: { model: { link: 'google.com' }, icon: iconString } },
|
{provide: 'sectionDataProvider', useValue: {model: {link: 'google.com'}, icon: iconString}},
|
||||||
{ provide: MenuService, useValue: menuService },
|
{provide: MenuService, useValue: menuService},
|
||||||
{ provide: CSSVariableService, useClass: CSSVariableServiceStub },
|
{provide: CSSVariableService, useClass: CSSVariableServiceStub},
|
||||||
]
|
]
|
||||||
}).overrideComponent(AdminSidebarSectionComponent, {
|
}).overrideComponent(AdminSidebarSectionComponent, {
|
||||||
set: {
|
set: {
|
||||||
@@ -49,6 +51,52 @@ describe('AdminSidebarSectionComponent', () => {
|
|||||||
const icon = fixture.debugElement.query(By.css('.shortcut-icon')).query(By.css('i.fas'));
|
const icon = fixture.debugElement.query(By.css('.shortcut-icon')).query(By.css('i.fas'));
|
||||||
expect(icon.nativeElement.getAttribute('class')).toContain('fa-' + iconString);
|
expect(icon.nativeElement.getAttribute('class')).toContain('fa-' + iconString);
|
||||||
});
|
});
|
||||||
|
it('should not contain the disabled class', () => {
|
||||||
|
const disabled = fixture.debugElement.query(By.css('.disabled'));
|
||||||
|
expect(disabled).toBeFalsy();
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
describe('when disabled', () => {
|
||||||
|
|
||||||
|
beforeEach(waitForAsync(() => {
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
imports: [NoopAnimationsModule, RouterTestingModule, TranslateModule.forRoot()],
|
||||||
|
declarations: [AdminSidebarSectionComponent, TestComponent],
|
||||||
|
providers: [
|
||||||
|
{provide: 'sectionDataProvider', useValue: {model: {link: 'google.com', disabled: true}, icon: iconString}},
|
||||||
|
{provide: MenuService, useValue: menuService},
|
||||||
|
{provide: CSSVariableService, useClass: CSSVariableServiceStub},
|
||||||
|
]
|
||||||
|
}).overrideComponent(AdminSidebarSectionComponent, {
|
||||||
|
set: {
|
||||||
|
entryComponents: [TestComponent]
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.compileComponents();
|
||||||
|
}));
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
fixture = TestBed.createComponent(AdminSidebarSectionComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
spyOn(component as any, 'getMenuItemComponent').and.returnValue(TestComponent);
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should set the right icon', () => {
|
||||||
|
const icon = fixture.debugElement.query(By.css('.shortcut-icon')).query(By.css('i.fas'));
|
||||||
|
expect(icon.nativeElement.getAttribute('class')).toContain('fa-' + iconString);
|
||||||
|
});
|
||||||
|
it('should contain the disabled class', () => {
|
||||||
|
const disabled = fixture.debugElement.query(By.css('.disabled'));
|
||||||
|
expect(disabled).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// declare a test component
|
// declare a test component
|
||||||
|
@@ -5,7 +5,7 @@ import { MenuService } from '../../../shared/menu/menu.service';
|
|||||||
import { rendersSectionForMenu } from '../../../shared/menu/menu-section.decorator';
|
import { rendersSectionForMenu } from '../../../shared/menu/menu-section.decorator';
|
||||||
import { LinkMenuItemModel } from '../../../shared/menu/menu-item/models/link.model';
|
import { LinkMenuItemModel } from '../../../shared/menu/menu-item/models/link.model';
|
||||||
import { MenuSection } from '../../../shared/menu/menu.reducer';
|
import { MenuSection } from '../../../shared/menu/menu.reducer';
|
||||||
import { isNotEmpty } from '../../../shared/empty.util';
|
import { isEmpty } from '../../../shared/empty.util';
|
||||||
import { Router } from '@angular/router';
|
import { Router } from '@angular/router';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -26,7 +26,12 @@ export class AdminSidebarSectionComponent extends MenuSectionComponent implement
|
|||||||
*/
|
*/
|
||||||
menuID: MenuID = MenuID.ADMIN;
|
menuID: MenuID = MenuID.ADMIN;
|
||||||
itemModel;
|
itemModel;
|
||||||
hasLink: boolean;
|
|
||||||
|
/**
|
||||||
|
* Boolean to indicate whether this section is disabled
|
||||||
|
*/
|
||||||
|
isDisabled: boolean;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
@Inject('sectionDataProvider') menuSection: MenuSection,
|
@Inject('sectionDataProvider') menuSection: MenuSection,
|
||||||
protected menuService: MenuService,
|
protected menuService: MenuService,
|
||||||
@@ -38,13 +43,13 @@ export class AdminSidebarSectionComponent extends MenuSectionComponent implement
|
|||||||
}
|
}
|
||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
this.hasLink = isNotEmpty(this.itemModel?.link);
|
this.isDisabled = this.itemModel?.disabled || isEmpty(this.itemModel?.link);
|
||||||
super.ngOnInit();
|
super.ngOnInit();
|
||||||
}
|
}
|
||||||
|
|
||||||
navigate(event: any): void {
|
navigate(event: any): void {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
if (this.hasLink) {
|
if (this.isDisabled) {
|
||||||
this.router.navigate(this.itemModel.link);
|
this.router.navigate(this.itemModel.link);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -7,6 +7,7 @@
|
|||||||
[attr.aria-labelledby]="'sidebarName-' + section.id"
|
[attr.aria-labelledby]="'sidebarName-' + section.id"
|
||||||
[attr.aria-expanded]="expanded | async"
|
[attr.aria-expanded]="expanded | async"
|
||||||
[title]="('menu.section.icon.' + section.id) | translate"
|
[title]="('menu.section.icon.' + section.id) | translate"
|
||||||
|
[class.disabled]="section.model.disabled"
|
||||||
(click)="toggleSection($event)"
|
(click)="toggleSection($event)"
|
||||||
(keyup.space)="toggleSection($event)"
|
(keyup.space)="toggleSection($event)"
|
||||||
(keyup.enter)="toggleSection($event)"
|
(keyup.enter)="toggleSection($event)"
|
||||||
|
@@ -21,6 +21,7 @@ import { CollectionPageAdministratorGuard } from './collection-page-administrato
|
|||||||
import { MenuItemType } from '../shared/menu/initial-menus-state';
|
import { MenuItemType } from '../shared/menu/initial-menus-state';
|
||||||
import { LinkMenuItemModel } from '../shared/menu/menu-item/models/link.model';
|
import { LinkMenuItemModel } from '../shared/menu/menu-item/models/link.model';
|
||||||
import { ThemedCollectionPageComponent } from './themed-collection-page.component';
|
import { ThemedCollectionPageComponent } from './themed-collection-page.component';
|
||||||
|
import { DSOEditMenuResolver } from '../shared/dso-page/dso-edit-menu.resolver';
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
imports: [
|
imports: [
|
||||||
@@ -34,7 +35,8 @@ import { ThemedCollectionPageComponent } from './themed-collection-page.componen
|
|||||||
path: ':id',
|
path: ':id',
|
||||||
resolve: {
|
resolve: {
|
||||||
dso: CollectionPageResolver,
|
dso: CollectionPageResolver,
|
||||||
breadcrumb: CollectionBreadcrumbResolver
|
breadcrumb: CollectionBreadcrumbResolver,
|
||||||
|
menu: DSOEditMenuResolver
|
||||||
},
|
},
|
||||||
runGuardsAndResolvers: 'always',
|
runGuardsAndResolvers: 'always',
|
||||||
children: [
|
children: [
|
||||||
@@ -90,7 +92,8 @@ import { ThemedCollectionPageComponent } from './themed-collection-page.componen
|
|||||||
DSOBreadcrumbsService,
|
DSOBreadcrumbsService,
|
||||||
LinkService,
|
LinkService,
|
||||||
CreateCollectionPageGuard,
|
CreateCollectionPageGuard,
|
||||||
CollectionPageAdministratorGuard
|
CollectionPageAdministratorGuard,
|
||||||
|
DSOEditMenuResolver
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
export class CollectionPageRoutingModule {
|
export class CollectionPageRoutingModule {
|
||||||
|
@@ -34,9 +34,7 @@
|
|||||||
[title]="'collection.page.news'">
|
[title]="'collection.page.news'">
|
||||||
</ds-comcol-page-content>
|
</ds-comcol-page-content>
|
||||||
</header>
|
</header>
|
||||||
<div class="pl-2">
|
<ds-dso-edit-menu></ds-dso-edit-menu>
|
||||||
<ds-dso-page-edit-button *ngIf="isCollectionAdmin$ | async" [pageRoute]="collectionPageRoute$ | async" [dso]="collection" [tooltipMsg]="'collection.page.edit'"></ds-dso-page-edit-button>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<section class="comcol-page-browse-section">
|
<section class="comcol-page-browse-section">
|
||||||
<!-- Browse-By Links -->
|
<!-- Browse-By Links -->
|
||||||
|
@@ -14,6 +14,7 @@ import { CommunityPageAdministratorGuard } from './community-page-administrator.
|
|||||||
import { MenuItemType } from '../shared/menu/initial-menus-state';
|
import { MenuItemType } from '../shared/menu/initial-menus-state';
|
||||||
import { LinkMenuItemModel } from '../shared/menu/menu-item/models/link.model';
|
import { LinkMenuItemModel } from '../shared/menu/menu-item/models/link.model';
|
||||||
import { ThemedCommunityPageComponent } from './themed-community-page.component';
|
import { ThemedCommunityPageComponent } from './themed-community-page.component';
|
||||||
|
import { DSOEditMenuResolver } from '../shared/dso-page/dso-edit-menu.resolver';
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
imports: [
|
imports: [
|
||||||
@@ -27,7 +28,8 @@ import { ThemedCommunityPageComponent } from './themed-community-page.component'
|
|||||||
path: ':id',
|
path: ':id',
|
||||||
resolve: {
|
resolve: {
|
||||||
dso: CommunityPageResolver,
|
dso: CommunityPageResolver,
|
||||||
breadcrumb: CommunityBreadcrumbResolver
|
breadcrumb: CommunityBreadcrumbResolver,
|
||||||
|
menu: DSOEditMenuResolver
|
||||||
},
|
},
|
||||||
runGuardsAndResolvers: 'always',
|
runGuardsAndResolvers: 'always',
|
||||||
children: [
|
children: [
|
||||||
@@ -72,7 +74,8 @@ import { ThemedCommunityPageComponent } from './themed-community-page.component'
|
|||||||
DSOBreadcrumbsService,
|
DSOBreadcrumbsService,
|
||||||
LinkService,
|
LinkService,
|
||||||
CreateCommunityPageGuard,
|
CreateCommunityPageGuard,
|
||||||
CommunityPageAdministratorGuard
|
CommunityPageAdministratorGuard,
|
||||||
|
DSOEditMenuResolver
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
export class CommunityPageRoutingModule {
|
export class CommunityPageRoutingModule {
|
||||||
|
@@ -20,9 +20,7 @@
|
|||||||
[title]="'community.page.news'">
|
[title]="'community.page.news'">
|
||||||
</ds-comcol-page-content>
|
</ds-comcol-page-content>
|
||||||
</header>
|
</header>
|
||||||
<div class="pl-2">
|
<ds-dso-edit-menu></ds-dso-edit-menu>
|
||||||
<ds-dso-page-edit-button *ngIf="isCommunityAdmin$ | async" [pageRoute]="communityPageRoute$ | async" [dso]="communityPayload" [tooltipMsg]="'community.page.edit'"></ds-dso-page-edit-button>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<section class="comcol-page-browse-section">
|
<section class="comcol-page-browse-section">
|
||||||
<!-- Browse-By Links -->
|
<!-- Browse-By Links -->
|
||||||
|
@@ -2,9 +2,8 @@
|
|||||||
<h2 class="item-page-title-field mr-auto">
|
<h2 class="item-page-title-field mr-auto">
|
||||||
{{'journalissue.page.titleprefix' | translate}}<ds-metadata-values [mdValues]="object?.allMetadata(['dc.title'])"></ds-metadata-values>
|
{{'journalissue.page.titleprefix' | translate}}<ds-metadata-values [mdValues]="object?.allMetadata(['dc.title'])"></ds-metadata-values>
|
||||||
</h2>
|
</h2>
|
||||||
<div class="pl-2">
|
<ds-dso-edit-menu></ds-dso-edit-menu>
|
||||||
<ds-dso-page-edit-button [pageRoute]="itemPageRoute" [dso]="object" [tooltipMsg]="'journalissue.page.edit'"></ds-dso-page-edit-button>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-xs-12 col-md-4">
|
<div class="col-xs-12 col-md-4">
|
||||||
|
@@ -2,9 +2,7 @@
|
|||||||
<h2 class="item-page-title-field mr-auto">
|
<h2 class="item-page-title-field mr-auto">
|
||||||
{{'journalvolume.page.titleprefix' | translate}}<ds-metadata-values [mdValues]="object?.allMetadata(['dc.title'])"></ds-metadata-values>
|
{{'journalvolume.page.titleprefix' | translate}}<ds-metadata-values [mdValues]="object?.allMetadata(['dc.title'])"></ds-metadata-values>
|
||||||
</h2>
|
</h2>
|
||||||
<div class="pl-2">
|
<ds-dso-edit-menu></ds-dso-edit-menu>
|
||||||
<ds-dso-page-edit-button [pageRoute]="itemPageRoute" [dso]="object" [tooltipMsg]="'journalvolume.page.edit'"></ds-dso-page-edit-button>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-xs-12 col-md-4">
|
<div class="col-xs-12 col-md-4">
|
||||||
|
@@ -2,9 +2,8 @@
|
|||||||
<h2 class="item-page-title-field mr-auto">
|
<h2 class="item-page-title-field mr-auto">
|
||||||
{{'journal.page.titleprefix' | translate}}<ds-metadata-values [mdValues]="object?.allMetadata(['dc.title'])"></ds-metadata-values>
|
{{'journal.page.titleprefix' | translate}}<ds-metadata-values [mdValues]="object?.allMetadata(['dc.title'])"></ds-metadata-values>
|
||||||
</h2>
|
</h2>
|
||||||
<div class="pl-2">
|
<ds-dso-edit-menu></ds-dso-edit-menu>
|
||||||
<ds-dso-page-edit-button [pageRoute]="itemPageRoute" [dso]="object" [tooltipMsg]="'journal.page.edit'"></ds-dso-page-edit-button>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-xs-12 col-md-4">
|
<div class="col-xs-12 col-md-4">
|
||||||
|
@@ -2,9 +2,8 @@
|
|||||||
<h2 class="item-page-title-field mr-auto">
|
<h2 class="item-page-title-field mr-auto">
|
||||||
{{'orgunit.page.titleprefix' | translate}}<ds-metadata-values [mdValues]="object?.allMetadata(['organization.legalName'])"></ds-metadata-values>
|
{{'orgunit.page.titleprefix' | translate}}<ds-metadata-values [mdValues]="object?.allMetadata(['organization.legalName'])"></ds-metadata-values>
|
||||||
</h2>
|
</h2>
|
||||||
<div class="pl-2">
|
<ds-dso-edit-menu></ds-dso-edit-menu>
|
||||||
<ds-dso-page-edit-button [pageRoute]="itemPageRoute" [dso]="object" [tooltipMsg]="'orgunit.page.edit'"></ds-dso-page-edit-button>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-xs-12 col-md-4">
|
<div class="col-xs-12 col-md-4">
|
||||||
|
@@ -2,9 +2,8 @@
|
|||||||
<h2 class="item-page-title-field mr-auto">
|
<h2 class="item-page-title-field mr-auto">
|
||||||
{{'person.page.titleprefix' | translate}}<ds-metadata-values [mdValues]="[object?.firstMetadata('person.familyName'), object?.firstMetadata('person.givenName')]" [separator]="', '"></ds-metadata-values>
|
{{'person.page.titleprefix' | translate}}<ds-metadata-values [mdValues]="[object?.firstMetadata('person.familyName'), object?.firstMetadata('person.givenName')]" [separator]="', '"></ds-metadata-values>
|
||||||
</h2>
|
</h2>
|
||||||
<div class="pl-2">
|
<ds-dso-edit-menu></ds-dso-edit-menu>
|
||||||
<ds-dso-page-edit-button [pageRoute]="itemPageRoute" [dso]="object" [tooltipMsg]="'person.page.edit'"></ds-dso-page-edit-button>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-xs-12 col-md-4">
|
<div class="col-xs-12 col-md-4">
|
||||||
|
@@ -2,9 +2,8 @@
|
|||||||
<h2 class="item-page-title-field mr-auto">
|
<h2 class="item-page-title-field mr-auto">
|
||||||
{{'project.page.titleprefix' | translate}}<ds-metadata-values [mdValues]="object?.allMetadata(['dc.title'])"></ds-metadata-values>
|
{{'project.page.titleprefix' | translate}}<ds-metadata-values [mdValues]="object?.allMetadata(['dc.title'])"></ds-metadata-values>
|
||||||
</h2>
|
</h2>
|
||||||
<div class="pl-2">
|
<ds-dso-edit-menu></ds-dso-edit-menu>
|
||||||
<ds-dso-page-edit-button [pageRoute]="itemPageRoute" [dso]="object" [tooltipMsg]="'project.page.edit'"></ds-dso-page-edit-button>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-xs-12 col-md-4">
|
<div class="col-xs-12 col-md-4">
|
||||||
|
@@ -6,9 +6,8 @@
|
|||||||
<ds-view-tracker [object]="item"></ds-view-tracker>
|
<ds-view-tracker [object]="item"></ds-view-tracker>
|
||||||
<div class="d-flex flex-row">
|
<div class="d-flex flex-row">
|
||||||
<ds-item-page-title-field class="mr-auto" [item]="item"></ds-item-page-title-field>
|
<ds-item-page-title-field class="mr-auto" [item]="item"></ds-item-page-title-field>
|
||||||
<div class="pl-2">
|
<ds-dso-edit-menu></ds-dso-edit-menu>
|
||||||
<ds-dso-page-edit-button [pageRoute]="itemPageRoute$ | async" [dso]="item" [tooltipMsg]="'item.page.edit'"></ds-dso-page-edit-button>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="simple-view-link my-3" *ngIf="!fromWfi">
|
<div class="simple-view-link my-3" *ngIf="!fromWfi">
|
||||||
<a class="btn btn-outline-primary" [routerLink]="[(itemPageRoute$ | async)]">
|
<a class="btn btn-outline-primary" [routerLink]="[(itemPageRoute$ | async)]">
|
||||||
|
@@ -16,6 +16,7 @@ import { ThemedFullItemPageComponent } from './full/themed-full-item-page.compon
|
|||||||
import { VersionPageComponent } from './version-page/version-page/version-page.component';
|
import { VersionPageComponent } from './version-page/version-page/version-page.component';
|
||||||
import { BitstreamRequestACopyPageComponent } from '../shared/bitstream-request-a-copy-page/bitstream-request-a-copy-page.component';
|
import { BitstreamRequestACopyPageComponent } from '../shared/bitstream-request-a-copy-page/bitstream-request-a-copy-page.component';
|
||||||
import { REQUEST_COPY_MODULE_PATH } from '../app-routing-paths';
|
import { REQUEST_COPY_MODULE_PATH } from '../app-routing-paths';
|
||||||
|
import { DSOEditMenuResolver } from '../shared/dso-page/dso-edit-menu.resolver';
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
imports: [
|
imports: [
|
||||||
@@ -24,7 +25,8 @@ import { REQUEST_COPY_MODULE_PATH } from '../app-routing-paths';
|
|||||||
path: ':id',
|
path: ':id',
|
||||||
resolve: {
|
resolve: {
|
||||||
dso: ItemPageResolver,
|
dso: ItemPageResolver,
|
||||||
breadcrumb: ItemBreadcrumbResolver
|
breadcrumb: ItemBreadcrumbResolver,
|
||||||
|
menu: DSOEditMenuResolver
|
||||||
},
|
},
|
||||||
runGuardsAndResolvers: 'always',
|
runGuardsAndResolvers: 'always',
|
||||||
children: [
|
children: [
|
||||||
@@ -88,6 +90,7 @@ import { REQUEST_COPY_MODULE_PATH } from '../app-routing-paths';
|
|||||||
LinkService,
|
LinkService,
|
||||||
ItemPageAdministratorGuard,
|
ItemPageAdministratorGuard,
|
||||||
VersionResolver,
|
VersionResolver,
|
||||||
|
DSOEditMenuResolver
|
||||||
]
|
]
|
||||||
|
|
||||||
})
|
})
|
||||||
|
@@ -32,7 +32,6 @@ import { MediaViewerImageComponent } from './media-viewer/media-viewer-image/med
|
|||||||
import { NgxGalleryModule } from '@kolkov/ngx-gallery';
|
import { NgxGalleryModule } from '@kolkov/ngx-gallery';
|
||||||
import { MiradorViewerComponent } from './mirador-viewer/mirador-viewer.component';
|
import { MiradorViewerComponent } from './mirador-viewer/mirador-viewer.component';
|
||||||
import { VersionPageComponent } from './version-page/version-page/version-page.component';
|
import { VersionPageComponent } from './version-page/version-page/version-page.component';
|
||||||
import { VersionedItemComponent } from './simple/item-types/versioned-item/versioned-item.component';
|
|
||||||
import { ThemedFileSectionComponent } from './simple/field-components/file-section/themed-file-section.component';
|
import { ThemedFileSectionComponent } from './simple/field-components/file-section/themed-file-section.component';
|
||||||
|
|
||||||
|
|
||||||
@@ -82,7 +81,6 @@ const DECLARATIONS = [
|
|||||||
],
|
],
|
||||||
declarations: [
|
declarations: [
|
||||||
...DECLARATIONS,
|
...DECLARATIONS,
|
||||||
VersionedItemComponent
|
|
||||||
],
|
],
|
||||||
exports: [
|
exports: [
|
||||||
...DECLARATIONS
|
...DECLARATIONS
|
||||||
|
@@ -11,9 +11,7 @@
|
|||||||
<h2 class="item-page-title-field mr-auto">
|
<h2 class="item-page-title-field mr-auto">
|
||||||
{{'publication.page.titleprefix' | translate}}<ds-metadata-values [mdValues]="object?.allMetadata(['dc.title'])"></ds-metadata-values>
|
{{'publication.page.titleprefix' | translate}}<ds-metadata-values [mdValues]="object?.allMetadata(['dc.title'])"></ds-metadata-values>
|
||||||
</h2>
|
</h2>
|
||||||
<div class="pl-2">
|
<ds-dso-edit-menu></ds-dso-edit-menu>
|
||||||
<ds-dso-page-edit-button [pageRoute]="itemPageRoute" [dso]="object" [tooltipMsg]="'publication.page.edit'"></ds-dso-page-edit-button>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-xs-12 col-md-4">
|
<div class="col-xs-12 col-md-4">
|
||||||
|
@@ -11,12 +11,8 @@
|
|||||||
<h2 class="item-page-title-field mr-auto">
|
<h2 class="item-page-title-field mr-auto">
|
||||||
<ds-metadata-values [mdValues]="object?.allMetadata(['dc.title'])"></ds-metadata-values>
|
<ds-metadata-values [mdValues]="object?.allMetadata(['dc.title'])"></ds-metadata-values>
|
||||||
</h2>
|
</h2>
|
||||||
<div class="pl-2">
|
<ds-dso-edit-menu></ds-dso-edit-menu>
|
||||||
<ds-dso-page-version-button (newVersionEvent)="onCreateNewVersion()" [dso]="object"
|
|
||||||
[tooltipMsgCreate]="'item.page.version.create'"
|
|
||||||
[tooltipMsgHasDraft]="'item.page.version.hasDraft'"></ds-dso-page-version-button>
|
|
||||||
<ds-dso-page-edit-button [pageRoute]="itemPageRoute" [dso]="object" [tooltipMsg]="'item.page.edit'"></ds-dso-page-edit-button>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-xs-12 col-md-4">
|
<div class="col-xs-12 col-md-4">
|
||||||
|
@@ -2,7 +2,7 @@ import { ChangeDetectionStrategy, Component } from '@angular/core';
|
|||||||
import { Item } from '../../../../core/shared/item.model';
|
import { Item } from '../../../../core/shared/item.model';
|
||||||
import { ViewMode } from '../../../../core/shared/view-mode.model';
|
import { ViewMode } from '../../../../core/shared/view-mode.model';
|
||||||
import { listableObjectComponent } from '../../../../shared/object-collection/shared/listable-object/listable-object.decorator';
|
import { listableObjectComponent } from '../../../../shared/object-collection/shared/listable-object/listable-object.decorator';
|
||||||
import { VersionedItemComponent } from '../versioned-item/versioned-item.component';
|
import { ItemComponent } from '../shared/item.component';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Component that represents a publication Item page
|
* Component that represents a publication Item page
|
||||||
@@ -15,6 +15,6 @@ import { VersionedItemComponent } from '../versioned-item/versioned-item.compone
|
|||||||
templateUrl: './untyped-item.component.html',
|
templateUrl: './untyped-item.component.html',
|
||||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||||
})
|
})
|
||||||
export class UntypedItemComponent extends VersionedItemComponent {
|
export class UntypedItemComponent extends ItemComponent {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -1,95 +0,0 @@
|
|||||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
|
||||||
|
|
||||||
import { VersionedItemComponent } from './versioned-item.component';
|
|
||||||
import { VersionHistoryDataService } from '../../../../core/data/version-history-data.service';
|
|
||||||
import { TranslateService } from '@ngx-translate/core';
|
|
||||||
import { VersionDataService } from '../../../../core/data/version-data.service';
|
|
||||||
import { NotificationsService } from '../../../../shared/notifications/notifications.service';
|
|
||||||
import { ItemVersionsSharedService } from '../../../../shared/item/item-versions/item-versions-shared.service';
|
|
||||||
import { Item } from '../../../../core/shared/item.model';
|
|
||||||
import { createSuccessfulRemoteDataObject$ } from '../../../../shared/remote-data.utils';
|
|
||||||
import { buildPaginatedList } from '../../../../core/data/paginated-list.model';
|
|
||||||
import { PageInfo } from '../../../../core/shared/page-info.model';
|
|
||||||
import { MetadataMap } from '../../../../core/shared/metadata.models';
|
|
||||||
import { createRelationshipsObservable, mockRouteService } from '../shared/item.component.spec';
|
|
||||||
import { RouterTestingModule } from '@angular/router/testing';
|
|
||||||
import { Component } from '@angular/core';
|
|
||||||
import { WorkspaceitemDataService } from '../../../../core/submission/workspaceitem-data.service';
|
|
||||||
import { SearchService } from '../../../../core/shared/search/search.service';
|
|
||||||
import { ItemDataService } from '../../../../core/data/item-data.service';
|
|
||||||
import { Version } from '../../../../core/shared/version.model';
|
|
||||||
import { RouteService } from '../../../../core/services/route.service';
|
|
||||||
|
|
||||||
const mockItem: Item = Object.assign(new Item(), {
|
|
||||||
bundles: createSuccessfulRemoteDataObject$(buildPaginatedList(new PageInfo(), [])),
|
|
||||||
metadata: new MetadataMap(),
|
|
||||||
relationships: createRelationshipsObservable(),
|
|
||||||
_links: {
|
|
||||||
self: {
|
|
||||||
href: 'item-href'
|
|
||||||
},
|
|
||||||
version: {
|
|
||||||
href: 'version-href'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
@Component({template: ''})
|
|
||||||
class DummyComponent {
|
|
||||||
}
|
|
||||||
|
|
||||||
describe('VersionedItemComponent', () => {
|
|
||||||
let component: VersionedItemComponent;
|
|
||||||
let fixture: ComponentFixture<VersionedItemComponent>;
|
|
||||||
|
|
||||||
let versionService: VersionDataService;
|
|
||||||
let versionHistoryService: VersionHistoryDataService;
|
|
||||||
|
|
||||||
const versionServiceSpy = jasmine.createSpyObj('versionService', {
|
|
||||||
findByHref: createSuccessfulRemoteDataObject$<Version>(new Version()),
|
|
||||||
});
|
|
||||||
|
|
||||||
const versionHistoryServiceSpy = jasmine.createSpyObj('versionHistoryService', {
|
|
||||||
createVersion: createSuccessfulRemoteDataObject$<Version>(new Version()),
|
|
||||||
});
|
|
||||||
|
|
||||||
beforeEach(async () => {
|
|
||||||
await TestBed.configureTestingModule({
|
|
||||||
declarations: [VersionedItemComponent, DummyComponent],
|
|
||||||
imports: [RouterTestingModule],
|
|
||||||
providers: [
|
|
||||||
{ provide: VersionHistoryDataService, useValue: versionHistoryServiceSpy },
|
|
||||||
{ provide: TranslateService, useValue: {} },
|
|
||||||
{ provide: VersionDataService, useValue: versionServiceSpy },
|
|
||||||
{ provide: NotificationsService, useValue: {} },
|
|
||||||
{ provide: ItemVersionsSharedService, useValue: {} },
|
|
||||||
{ provide: WorkspaceitemDataService, useValue: {} },
|
|
||||||
{ provide: SearchService, useValue: {} },
|
|
||||||
{ provide: ItemDataService, useValue: {} },
|
|
||||||
{ provide: RouteService, useValue: mockRouteService }
|
|
||||||
]
|
|
||||||
}).compileComponents();
|
|
||||||
versionService = TestBed.inject(VersionDataService);
|
|
||||||
versionHistoryService = TestBed.inject(VersionHistoryDataService);
|
|
||||||
});
|
|
||||||
|
|
||||||
beforeEach(() => {
|
|
||||||
fixture = TestBed.createComponent(VersionedItemComponent);
|
|
||||||
component = fixture.componentInstance;
|
|
||||||
component.object = mockItem;
|
|
||||||
fixture.detectChanges();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should create', () => {
|
|
||||||
expect(component).toBeTruthy();
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('when onCreateNewVersion() is called', () => {
|
|
||||||
it('should call versionService.findByHref', () => {
|
|
||||||
component.onCreateNewVersion();
|
|
||||||
expect(versionService.findByHref).toHaveBeenCalledWith('version-href');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
});
|
|
@@ -1,80 +0,0 @@
|
|||||||
import { Component } from '@angular/core';
|
|
||||||
import { ItemComponent } from '../shared/item.component';
|
|
||||||
import { ItemVersionsSummaryModalComponent } from '../../../../shared/item/item-versions/item-versions-summary-modal/item-versions-summary-modal.component';
|
|
||||||
import { getFirstCompletedRemoteData, getFirstSucceededRemoteDataPayload } from '../../../../core/shared/operators';
|
|
||||||
import { RemoteData } from '../../../../core/data/remote-data';
|
|
||||||
import { Version } from '../../../../core/shared/version.model';
|
|
||||||
import { switchMap, tap } from 'rxjs/operators';
|
|
||||||
import { NgbModal } from '@ng-bootstrap/ng-bootstrap';
|
|
||||||
import { VersionHistoryDataService } from '../../../../core/data/version-history-data.service';
|
|
||||||
import { TranslateService } from '@ngx-translate/core';
|
|
||||||
import { VersionDataService } from '../../../../core/data/version-data.service';
|
|
||||||
import { ItemVersionsSharedService } from '../../../../shared/item/item-versions/item-versions-shared.service';
|
|
||||||
import { Router } from '@angular/router';
|
|
||||||
import { WorkspaceitemDataService } from '../../../../core/submission/workspaceitem-data.service';
|
|
||||||
import { SearchService } from '../../../../core/shared/search/search.service';
|
|
||||||
import { Item } from '../../../../core/shared/item.model';
|
|
||||||
import { ItemDataService } from '../../../../core/data/item-data.service';
|
|
||||||
import { WorkspaceItem } from '../../../../core/submission/models/workspaceitem.model';
|
|
||||||
import { RouteService } from '../../../../core/services/route.service';
|
|
||||||
|
|
||||||
@Component({
|
|
||||||
selector: 'ds-versioned-item',
|
|
||||||
templateUrl: './versioned-item.component.html',
|
|
||||||
styleUrls: ['./versioned-item.component.scss']
|
|
||||||
})
|
|
||||||
export class VersionedItemComponent extends ItemComponent {
|
|
||||||
|
|
||||||
constructor(
|
|
||||||
private modalService: NgbModal,
|
|
||||||
private versionHistoryService: VersionHistoryDataService,
|
|
||||||
private translateService: TranslateService,
|
|
||||||
private versionService: VersionDataService,
|
|
||||||
private itemVersionShared: ItemVersionsSharedService,
|
|
||||||
private router: Router,
|
|
||||||
private workspaceItemDataService: WorkspaceitemDataService,
|
|
||||||
private searchService: SearchService,
|
|
||||||
private itemService: ItemDataService,
|
|
||||||
protected routeService: RouteService
|
|
||||||
) {
|
|
||||||
super(routeService);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Open a modal that allows to create a new version starting from the specified item, with optional summary
|
|
||||||
*/
|
|
||||||
onCreateNewVersion(): void {
|
|
||||||
|
|
||||||
const item = this.object;
|
|
||||||
const versionHref = item._links.version.href;
|
|
||||||
|
|
||||||
// Open modal
|
|
||||||
const activeModal = this.modalService.open(ItemVersionsSummaryModalComponent);
|
|
||||||
|
|
||||||
// Show current version in modal
|
|
||||||
this.versionService.findByHref(versionHref).pipe(getFirstCompletedRemoteData()).subscribe((res: RemoteData<Version>) => {
|
|
||||||
// if res.hasNoContent then the item is unversioned
|
|
||||||
activeModal.componentInstance.firstVersion = res.hasNoContent;
|
|
||||||
activeModal.componentInstance.versionNumber = (res.hasNoContent ? undefined : res.payload.version);
|
|
||||||
});
|
|
||||||
|
|
||||||
// On createVersionEvent emitted create new version and notify
|
|
||||||
activeModal.componentInstance.createVersionEvent.pipe(
|
|
||||||
switchMap((summary: string) => this.versionHistoryService.createVersion(item._links.self.href, summary)),
|
|
||||||
getFirstCompletedRemoteData(),
|
|
||||||
// show success/failure notification
|
|
||||||
tap((res: RemoteData<Version>) => { this.itemVersionShared.notifyCreateNewVersion(res); }),
|
|
||||||
// get workspace item
|
|
||||||
getFirstSucceededRemoteDataPayload<Version>(),
|
|
||||||
switchMap((newVersion: Version) => this.itemService.findByHref(newVersion._links.item.href)),
|
|
||||||
getFirstSucceededRemoteDataPayload<Item>(),
|
|
||||||
switchMap((newVersionItem: Item) => this.workspaceItemDataService.findByItem(newVersionItem.uuid, true, false)),
|
|
||||||
getFirstSucceededRemoteDataPayload<WorkspaceItem>(),
|
|
||||||
).subscribe((wsItem) => {
|
|
||||||
const wsiId = wsItem.id;
|
|
||||||
const route = 'workspaceitems/' + wsiId + '/edit';
|
|
||||||
this.router.navigateByUrl(route);
|
|
||||||
});
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
@@ -6,6 +6,7 @@
|
|||||||
(mouseenter)="activateSection($event)"
|
(mouseenter)="activateSection($event)"
|
||||||
(mouseleave)="deactivateSection($event)">
|
(mouseleave)="deactivateSection($event)">
|
||||||
<a href="javascript:void(0);" class="nav-link dropdown-toggle" routerLinkActive="active"
|
<a href="javascript:void(0);" class="nav-link dropdown-toggle" routerLinkActive="active"
|
||||||
|
[class.disabled]="section.model?.disabled"
|
||||||
id="browseDropdown" (click)="toggleSection($event)"
|
id="browseDropdown" (click)="toggleSection($event)"
|
||||||
data-toggle="dropdown">
|
data-toggle="dropdown">
|
||||||
<ng-container
|
<ng-container
|
||||||
|
190
src/app/shared/dso-page/dso-edit-menu.resolver.spec.ts
Normal file
190
src/app/shared/dso-page/dso-edit-menu.resolver.spec.ts
Normal file
@@ -0,0 +1,190 @@
|
|||||||
|
import { TestBed, waitForAsync } from '@angular/core/testing';
|
||||||
|
import { MenuServiceStub } from '../testing/menu-service.stub';
|
||||||
|
import { of as observableOf } from 'rxjs';
|
||||||
|
import { TranslateModule } from '@ngx-translate/core';
|
||||||
|
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
|
||||||
|
import { RouterTestingModule } from '@angular/router/testing';
|
||||||
|
import { AdminSidebarComponent } from '../../admin/admin-sidebar/admin-sidebar.component';
|
||||||
|
import { MenuService } from '../menu/menu.service';
|
||||||
|
import { AuthorizationDataService } from '../../core/data/feature-authorization/authorization-data.service';
|
||||||
|
import { NgbModal } from '@ng-bootstrap/ng-bootstrap';
|
||||||
|
import { NO_ERRORS_SCHEMA } from '@angular/core';
|
||||||
|
import { MenuID, MenuItemType } from '../menu/initial-menus-state';
|
||||||
|
import { DSOEditMenuResolver } from './dso-edit-menu.resolver';
|
||||||
|
import { DsoVersioningModalService } from './dso-versioning-modal-service/dso-versioning-modal.service';
|
||||||
|
import { DSpaceObjectDataService } from '../../core/data/dspace-object-data.service';
|
||||||
|
import { Item } from '../../core/shared/item.model';
|
||||||
|
import { createFailedRemoteDataObject$, createSuccessfulRemoteDataObject$ } from '../remote-data.utils';
|
||||||
|
|
||||||
|
describe('DSOEditMenuResolver', () => {
|
||||||
|
|
||||||
|
const MENU_STATE = {
|
||||||
|
id: 'some menu'
|
||||||
|
};
|
||||||
|
|
||||||
|
let resolver: DSOEditMenuResolver;
|
||||||
|
|
||||||
|
let dSpaceObjectDataService;
|
||||||
|
let menuService;
|
||||||
|
let authorizationService;
|
||||||
|
let dsoVersioningModalService;
|
||||||
|
|
||||||
|
const route = {
|
||||||
|
data: {
|
||||||
|
menu: {
|
||||||
|
'statistics': [{
|
||||||
|
id: 'statistics-dummy-1',
|
||||||
|
active: false,
|
||||||
|
visible: true,
|
||||||
|
model: null
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
params: {id: 'test-uuid'},
|
||||||
|
};
|
||||||
|
|
||||||
|
const state = {
|
||||||
|
url: 'test-url'
|
||||||
|
};
|
||||||
|
|
||||||
|
const testObject = Object.assign(new Item(), {uuid: 'test-uuid', type: 'item', _links: {self: {href: 'self-link'}}});
|
||||||
|
|
||||||
|
const dummySections1 = [{
|
||||||
|
id: 'dummy-1',
|
||||||
|
active: false,
|
||||||
|
visible: true,
|
||||||
|
model: null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'dummy-2',
|
||||||
|
active: false,
|
||||||
|
visible: true,
|
||||||
|
model: null
|
||||||
|
}];
|
||||||
|
|
||||||
|
const dummySections2 = [{
|
||||||
|
id: 'dummy-3',
|
||||||
|
active: false,
|
||||||
|
visible: true,
|
||||||
|
model: null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'dummy-4',
|
||||||
|
active: false,
|
||||||
|
visible: true,
|
||||||
|
model: null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'dummy-5',
|
||||||
|
active: false,
|
||||||
|
visible: true,
|
||||||
|
model: null
|
||||||
|
}];
|
||||||
|
|
||||||
|
beforeEach(waitForAsync(() => {
|
||||||
|
menuService = new MenuServiceStub();
|
||||||
|
spyOn(menuService, 'getMenu').and.returnValue(observableOf(MENU_STATE));
|
||||||
|
|
||||||
|
dSpaceObjectDataService = jasmine.createSpyObj('dSpaceObjectDataService', {
|
||||||
|
findById: createSuccessfulRemoteDataObject$(testObject)
|
||||||
|
});
|
||||||
|
authorizationService = jasmine.createSpyObj('authorizationService', {
|
||||||
|
isAuthorized: observableOf(true)
|
||||||
|
});
|
||||||
|
dsoVersioningModalService = jasmine.createSpyObj('dsoVersioningModalService', {
|
||||||
|
isNewVersionButtonDisabled: observableOf(false),
|
||||||
|
getVersioningTooltipMessage: observableOf('message'),
|
||||||
|
openCreateVersionModal: {}
|
||||||
|
});
|
||||||
|
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
imports: [TranslateModule.forRoot(), NoopAnimationsModule, RouterTestingModule],
|
||||||
|
declarations: [AdminSidebarComponent],
|
||||||
|
providers: [
|
||||||
|
{provide: DSpaceObjectDataService, useValue: dSpaceObjectDataService},
|
||||||
|
{provide: MenuService, useValue: menuService},
|
||||||
|
{provide: AuthorizationDataService, useValue: authorizationService},
|
||||||
|
{provide: DsoVersioningModalService, useValue: dsoVersioningModalService},
|
||||||
|
{
|
||||||
|
provide: NgbModal, useValue: {
|
||||||
|
open: () => {/*comment*/
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
schemas: [NO_ERRORS_SCHEMA]
|
||||||
|
});
|
||||||
|
resolver = TestBed.inject(DSOEditMenuResolver);
|
||||||
|
|
||||||
|
spyOn(menuService, 'addSection');
|
||||||
|
}));
|
||||||
|
|
||||||
|
it('should be created', () => {
|
||||||
|
expect(resolver).toBeTruthy();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('resolve', () => {
|
||||||
|
it('should create all menus when a dso is found', (done) => {
|
||||||
|
spyOn(resolver, 'getDsoMenus').and.returnValue(
|
||||||
|
[observableOf(dummySections1), observableOf(dummySections2)]
|
||||||
|
);
|
||||||
|
resolver.resolve(route as any, null).subscribe(resolved => {
|
||||||
|
expect(resolved).toEqual(
|
||||||
|
{
|
||||||
|
...route.data.menu,
|
||||||
|
[MenuID.DSO_EDIT]: [
|
||||||
|
...dummySections1.map((menu) => Object.assign(menu, {id: menu.id + '-test-uuid'})),
|
||||||
|
...dummySections2.map((menu) => Object.assign(menu, {id: menu.id + '-test-uuid'}))
|
||||||
|
]
|
||||||
|
}
|
||||||
|
);
|
||||||
|
expect(resolver.getDsoMenus).toHaveBeenCalled();
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
it('should return the statistics menu when no dso is found', (done) => {
|
||||||
|
(dSpaceObjectDataService.findById as jasmine.Spy).and.returnValue(createFailedRemoteDataObject$());
|
||||||
|
|
||||||
|
resolver.resolve(route as any, null).subscribe(resolved => {
|
||||||
|
expect(resolved).toEqual(
|
||||||
|
{
|
||||||
|
...route.data.menu
|
||||||
|
}
|
||||||
|
);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
describe('getDsoMenus', () => {
|
||||||
|
it('should return as first part the item version list ', (done) => {
|
||||||
|
const result = resolver.getDsoMenus(testObject, route, state);
|
||||||
|
result[0].subscribe((menuList) => {
|
||||||
|
expect(menuList.length).toEqual(1);
|
||||||
|
expect(menuList[0].id).toEqual('version-dso');
|
||||||
|
expect(menuList[0].active).toEqual(false);
|
||||||
|
expect(menuList[0].visible).toEqual(true);
|
||||||
|
expect(menuList[0].model.type).toEqual(MenuItemType.ONCLICK);
|
||||||
|
expect(menuList[0].model.text).toEqual('message');
|
||||||
|
expect(menuList[0].model.disabled).toEqual(false);
|
||||||
|
expect(menuList[0].icon).toEqual('code-branch');
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
it('should return as second part the common list ', (done) => {
|
||||||
|
const result = resolver.getDsoMenus(testObject, route, state);
|
||||||
|
result[1].subscribe((menuList) => {
|
||||||
|
expect(menuList.length).toEqual(1);
|
||||||
|
expect(menuList[0].id).toEqual('edit-dso');
|
||||||
|
expect(menuList[0].active).toEqual(false);
|
||||||
|
expect(menuList[0].visible).toEqual(true);
|
||||||
|
expect(menuList[0].model.type).toEqual(MenuItemType.LINK);
|
||||||
|
expect(menuList[0].model.text).toEqual('item.page.edit');
|
||||||
|
expect(menuList[0].model.link).toEqual('test-url/edit/metadata');
|
||||||
|
expect(menuList[0].icon).toEqual('pencil-alt');
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
164
src/app/shared/dso-page/dso-edit-menu.resolver.ts
Normal file
164
src/app/shared/dso-page/dso-edit-menu.resolver.ts
Normal file
@@ -0,0 +1,164 @@
|
|||||||
|
import { ActivatedRouteSnapshot, Resolve, RouterStateSnapshot } from '@angular/router';
|
||||||
|
import { combineLatest, Observable, of as observableOf } from 'rxjs';
|
||||||
|
import { FeatureID } from '../../core/data/feature-authorization/feature-id';
|
||||||
|
import { MenuID, MenuItemType } from '../menu/initial-menus-state';
|
||||||
|
import { MenuService } from '../menu/menu.service';
|
||||||
|
import { AuthorizationDataService } from '../../core/data/feature-authorization/authorization-data.service';
|
||||||
|
import { Injectable } from '@angular/core';
|
||||||
|
import { LinkMenuItemModel } from '../menu/menu-item/models/link.model';
|
||||||
|
import { Item } from '../../core/shared/item.model';
|
||||||
|
import { NgbModal } from '@ng-bootstrap/ng-bootstrap';
|
||||||
|
import { OnClickMenuItemModel } from '../menu/menu-item/models/onclick.model';
|
||||||
|
import { getFirstCompletedRemoteData } from '../../core/shared/operators';
|
||||||
|
import { map, switchMap } from 'rxjs/operators';
|
||||||
|
import { DSpaceObjectDataService } from '../../core/data/dspace-object-data.service';
|
||||||
|
import { URLCombiner } from '../../core/url-combiner/url-combiner';
|
||||||
|
import { DsoVersioningModalService } from './dso-versioning-modal-service/dso-versioning-modal.service';
|
||||||
|
import { hasValue } from '../empty.util';
|
||||||
|
import { MenuSection } from '../menu/menu.reducer';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates the menus for the dspace object pages
|
||||||
|
*/
|
||||||
|
@Injectable({
|
||||||
|
providedIn: 'root'
|
||||||
|
})
|
||||||
|
export class DSOEditMenuResolver implements Resolve<{ [key: string]: MenuSection[] }> {
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
protected dSpaceObjectDataService: DSpaceObjectDataService,
|
||||||
|
protected menuService: MenuService,
|
||||||
|
protected authorizationService: AuthorizationDataService,
|
||||||
|
protected modalService: NgbModal,
|
||||||
|
protected dsoVersioningModalService: DsoVersioningModalService,
|
||||||
|
) {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialise all dspace object related menus
|
||||||
|
*/
|
||||||
|
resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<{ [key: string]: MenuSection[] }> {
|
||||||
|
const uuid = route.params.id;
|
||||||
|
return this.dSpaceObjectDataService.findById(uuid, true, false).pipe(
|
||||||
|
getFirstCompletedRemoteData(),
|
||||||
|
switchMap((dsoRD) => {
|
||||||
|
if (dsoRD.hasSucceeded) {
|
||||||
|
const dso = dsoRD.payload;
|
||||||
|
return combineLatest(this.getDsoMenus(dso, route, state)).pipe(
|
||||||
|
map((combinedMenus) => [].concat.apply([], combinedMenus)),
|
||||||
|
map((menus) => this.addDsoUuidToMenuIDs(menus, dso)),
|
||||||
|
map((menus) => {
|
||||||
|
return {
|
||||||
|
...route.data?.menu,
|
||||||
|
[MenuID.DSO_EDIT]: menus
|
||||||
|
};
|
||||||
|
})
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return observableOf({...route.data?.menu});
|
||||||
|
}
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return all the menus for a dso based on the route and state
|
||||||
|
*/
|
||||||
|
getDsoMenus(dso, route, state) {
|
||||||
|
return [
|
||||||
|
this.getItemMenu(dso),
|
||||||
|
this.getCommonMenu(dso, state)
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the common menus between all dspace objects
|
||||||
|
*/
|
||||||
|
protected getCommonMenu(dso, state): Observable<any[]> {
|
||||||
|
return combineLatest([
|
||||||
|
this.authorizationService.isAuthorized(FeatureID.CanEditMetadata, dso.self),
|
||||||
|
]).pipe(
|
||||||
|
map(([canEditItem]) => {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
id: 'edit-dso',
|
||||||
|
active: false,
|
||||||
|
visible: canEditItem,
|
||||||
|
model: {
|
||||||
|
type: MenuItemType.LINK,
|
||||||
|
text: this.getDsoType(dso) + '.page.edit',
|
||||||
|
link: new URLCombiner(state.url, 'edit', 'metadata').toString()
|
||||||
|
} as LinkMenuItemModel,
|
||||||
|
icon: 'pencil-alt',
|
||||||
|
index: 1
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get item sepcific menus
|
||||||
|
*/
|
||||||
|
protected getItemMenu(dso): Observable<any[]> {
|
||||||
|
if (dso instanceof Item) {
|
||||||
|
return combineLatest([
|
||||||
|
this.authorizationService.isAuthorized(FeatureID.CanCreateVersion, dso.self),
|
||||||
|
this.dsoVersioningModalService.isNewVersionButtonDisabled(dso),
|
||||||
|
this.dsoVersioningModalService.getVersioningTooltipMessage(dso, 'item.page.version.hasDraft', 'item.page.version.create')
|
||||||
|
]).pipe(
|
||||||
|
map(([canCreateVersion, disableVersioning, versionTooltip]) => {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
id: 'version-dso',
|
||||||
|
active: false,
|
||||||
|
visible: canCreateVersion,
|
||||||
|
model: {
|
||||||
|
type: MenuItemType.ONCLICK,
|
||||||
|
text: versionTooltip,
|
||||||
|
disabled: disableVersioning,
|
||||||
|
function: () => {
|
||||||
|
this.dsoVersioningModalService.openCreateVersionModal(dso);
|
||||||
|
}
|
||||||
|
} as OnClickMenuItemModel,
|
||||||
|
icon: 'code-branch',
|
||||||
|
index: 0
|
||||||
|
},
|
||||||
|
];
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return observableOf([]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve the dso or entity type for an object to be used in generic messages
|
||||||
|
*/
|
||||||
|
protected getDsoType(dso) {
|
||||||
|
const renderType = dso.getRenderTypes()[0];
|
||||||
|
if (typeof renderType === 'string' || renderType instanceof String) {
|
||||||
|
return renderType.toLowerCase();
|
||||||
|
} else {
|
||||||
|
return dso.type.toString().toLowerCase();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add the dso uuid to all provided menu ids and parent ids
|
||||||
|
*/
|
||||||
|
protected addDsoUuidToMenuIDs(menus, dso) {
|
||||||
|
return menus.map((menu) => {
|
||||||
|
Object.assign(menu, {
|
||||||
|
id: menu.id + '-' + dso.uuid
|
||||||
|
});
|
||||||
|
if (hasValue(menu.parentID)) {
|
||||||
|
Object.assign(menu, {
|
||||||
|
parentID: menu.parentID + '-' + dso.uuid
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return menu;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,18 @@
|
|||||||
|
<div class="dso-button-menu mb-1" ngbDropdown placement="bottom-right">
|
||||||
|
<div class="d-flex flex-row flex-nowrap"
|
||||||
|
[ngbTooltip]="itemModel.text | translate">
|
||||||
|
<button class="btn btn-dark btn-sm" ngbDropdownToggle [disabled]="section.model.disabled">
|
||||||
|
<i class="fas fa-{{section.icon}} fa-fw"></i>
|
||||||
|
</button>
|
||||||
|
<ul ngbDropdownMenu>
|
||||||
|
<ng-container *ngFor="let subSection of (subSections$ | async)">
|
||||||
|
<ng-container
|
||||||
|
*ngComponentOutlet="(sectionMap$ | async).get(subSection.id).component; injector: (sectionMap$ | async).get(subSection.id).injector;"></ng-container>
|
||||||
|
</ng-container>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@@ -0,0 +1,26 @@
|
|||||||
|
.btn-dark {
|
||||||
|
background-color: var(--ds-admin-sidebar-bg);
|
||||||
|
}
|
||||||
|
|
||||||
|
.dso-button-menu {
|
||||||
|
.dropdown-toggle::after {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ul.dropdown-menu {
|
||||||
|
background-color: var(--ds-admin-sidebar-bg);
|
||||||
|
color: white;
|
||||||
|
|
||||||
|
::ng-deep a {
|
||||||
|
color: white;
|
||||||
|
|
||||||
|
&.disabled {
|
||||||
|
color: $btn-link-disabled-color;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.disabled {
|
||||||
|
color: $btn-link-disabled-color;
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,75 @@
|
|||||||
|
import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
|
||||||
|
import { MenuServiceStub } from '../../../testing/menu-service.stub';
|
||||||
|
import { TranslateModule } from '@ngx-translate/core';
|
||||||
|
import { MenuService } from '../../../menu/menu.service';
|
||||||
|
import { CSSVariableService } from '../../../sass-helper/sass-helper.service';
|
||||||
|
import { CSSVariableServiceStub } from '../../../testing/css-variable-service.stub';
|
||||||
|
import { Router } from '@angular/router';
|
||||||
|
import { RouterStub } from '../../../testing/router.stub';
|
||||||
|
import { of as observableOf } from 'rxjs';
|
||||||
|
import { Component } from '@angular/core';
|
||||||
|
import { DsoEditMenuExpandableSectionComponent } from './dso-edit-menu-expandable-section.component';
|
||||||
|
import { MenuItemType } from '../../../menu/initial-menus-state';
|
||||||
|
import { By } from '@angular/platform-browser';
|
||||||
|
|
||||||
|
describe('DsoEditMenuExpandableSectionComponent', () => {
|
||||||
|
let component: DsoEditMenuExpandableSectionComponent;
|
||||||
|
let fixture: ComponentFixture<DsoEditMenuExpandableSectionComponent>;
|
||||||
|
const menuService = new MenuServiceStub();
|
||||||
|
const iconString = 'test';
|
||||||
|
|
||||||
|
const dummySection = {
|
||||||
|
id: 'dummy',
|
||||||
|
active: false,
|
||||||
|
visible: true,
|
||||||
|
model: {
|
||||||
|
type: MenuItemType.TEXT,
|
||||||
|
disabled: false,
|
||||||
|
text: 'text'
|
||||||
|
},
|
||||||
|
icon: iconString
|
||||||
|
};
|
||||||
|
|
||||||
|
beforeEach(waitForAsync(() => {
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
imports: [TranslateModule.forRoot()],
|
||||||
|
declarations: [DsoEditMenuExpandableSectionComponent, TestComponent],
|
||||||
|
providers: [
|
||||||
|
{provide: 'sectionDataProvider', useValue: dummySection},
|
||||||
|
{provide: MenuService, useValue: menuService},
|
||||||
|
{provide: CSSVariableService, useClass: CSSVariableServiceStub},
|
||||||
|
{provide: Router, useValue: new RouterStub()},
|
||||||
|
]
|
||||||
|
}).overrideComponent(DsoEditMenuExpandableSectionComponent, {
|
||||||
|
set: {
|
||||||
|
entryComponents: [TestComponent]
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.compileComponents();
|
||||||
|
}));
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
spyOn(menuService, 'getSubSectionsByParentID').and.returnValue(observableOf([]));
|
||||||
|
fixture = TestBed.createComponent(DsoEditMenuExpandableSectionComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
spyOn(component as any, 'getMenuItemComponent').and.returnValue(TestComponent);
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should show a button with the icon', () => {
|
||||||
|
const button = fixture.debugElement.query(By.css('.btn-dark'));
|
||||||
|
expect(button.nativeElement.innerHTML).toContain('fa-' + iconString);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// declare a test component
|
||||||
|
@Component({
|
||||||
|
selector: 'ds-test-cmp',
|
||||||
|
template: ``
|
||||||
|
})
|
||||||
|
class TestComponent {
|
||||||
|
}
|
@@ -0,0 +1,39 @@
|
|||||||
|
import { Component, Inject, Injector } from '@angular/core';
|
||||||
|
import { MenuID } from 'src/app/shared/menu/initial-menus-state';
|
||||||
|
import { rendersSectionForMenu } from 'src/app/shared/menu/menu-section.decorator';
|
||||||
|
import { MenuSectionComponent } from 'src/app/shared/menu/menu-section/menu-section.component';
|
||||||
|
import { MenuService } from '../../../menu/menu.service';
|
||||||
|
import { MenuSection } from '../../../menu/menu.reducer';
|
||||||
|
import { Router } from '@angular/router';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents an expandable section in the dso edit menus
|
||||||
|
*/
|
||||||
|
@Component({
|
||||||
|
/* tslint:disable:component-selector */
|
||||||
|
selector: 'ds-dso-edit-menu-expandable-section',
|
||||||
|
templateUrl: './dso-edit-menu-expandable-section.component.html',
|
||||||
|
styleUrls: ['./dso-edit-menu-expandable-section.component.scss'],
|
||||||
|
})
|
||||||
|
@rendersSectionForMenu(MenuID.DSO_EDIT, true)
|
||||||
|
export class DsoEditMenuExpandableSectionComponent extends MenuSectionComponent {
|
||||||
|
|
||||||
|
menuID: MenuID = MenuID.DSO_EDIT;
|
||||||
|
itemModel;
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
@Inject('sectionDataProvider') menuSection: MenuSection,
|
||||||
|
protected menuService: MenuService,
|
||||||
|
protected injector: Injector,
|
||||||
|
protected router: Router,
|
||||||
|
) {
|
||||||
|
super(menuSection, menuService, injector);
|
||||||
|
this.itemModel = menuSection.model;
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnInit(): void {
|
||||||
|
this.menuService.activateSection(this.menuID, this.section.id);
|
||||||
|
super.ngOnInit();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -0,0 +1,25 @@
|
|||||||
|
<div *ngIf="!canActivate" class="dso-button-menu mb-1"
|
||||||
|
[title]="itemModel.text | translate"
|
||||||
|
[ngbTooltip]="itemModel.text | translate">
|
||||||
|
<a *ngIf="!section.model.disabled" class="d-flex flex-row flex-nowrap"
|
||||||
|
[routerLink]="itemModel.link"
|
||||||
|
href="javascript:void(0);">
|
||||||
|
<button class="btn btn-dark btn-sm" [disabled]="section.model.disabled">
|
||||||
|
<i class="fas fa-{{section.icon}} fa-fw"></i>
|
||||||
|
</button>
|
||||||
|
</a>
|
||||||
|
<div *ngIf="section.model.disabled" class="d-flex flex-row flex-nowrap">
|
||||||
|
<button class="btn btn-dark btn-sm" [disabled]="section.model.disabled">
|
||||||
|
<i class="fas fa-{{section.icon}} fa-fw"></i>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div *ngIf="canActivate" class="dso-button-menu mb-1"
|
||||||
|
[title]="itemModel.text | translate"
|
||||||
|
[ngbTooltip]="itemModel.text | translate">
|
||||||
|
<button class="btn btn-dark btn-sm" [disabled]="section.model.disabled"
|
||||||
|
(click)="activate($event)">
|
||||||
|
<i class="fas fa-{{section.icon}} fa-fw"></i>
|
||||||
|
</button>
|
||||||
|
</div>
|
@@ -0,0 +1,3 @@
|
|||||||
|
.btn-dark {
|
||||||
|
background-color: var(--ds-admin-sidebar-bg);
|
||||||
|
}
|
@@ -0,0 +1,173 @@
|
|||||||
|
import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
|
||||||
|
import { MenuServiceStub } from '../../../testing/menu-service.stub';
|
||||||
|
import { TranslateModule } from '@ngx-translate/core';
|
||||||
|
import { MenuService } from '../../../menu/menu.service';
|
||||||
|
import { CSSVariableService } from '../../../sass-helper/sass-helper.service';
|
||||||
|
import { CSSVariableServiceStub } from '../../../testing/css-variable-service.stub';
|
||||||
|
import { Router } from '@angular/router';
|
||||||
|
import { RouterStub } from '../../../testing/router.stub';
|
||||||
|
import { of as observableOf } from 'rxjs';
|
||||||
|
import { Component } from '@angular/core';
|
||||||
|
import { MenuItemType } from '../../../menu/initial-menus-state';
|
||||||
|
import { By } from '@angular/platform-browser';
|
||||||
|
import { DsoEditMenuSectionComponent } from './dso-edit-menu-section.component';
|
||||||
|
import { OnClickMenuItemModel } from '../../../menu/menu-item/models/onclick.model';
|
||||||
|
|
||||||
|
function initAsync(dummySectionText: { visible: boolean; icon: string; active: boolean; model: { disabled: boolean; text: string; type: MenuItemType }; id: string }, menuService: MenuServiceStub) {
|
||||||
|
beforeEach(waitForAsync(() => {
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
imports: [TranslateModule.forRoot()],
|
||||||
|
declarations: [DsoEditMenuSectionComponent, TestComponent],
|
||||||
|
providers: [
|
||||||
|
{provide: 'sectionDataProvider', useValue: dummySectionText},
|
||||||
|
{provide: MenuService, useValue: menuService},
|
||||||
|
{provide: CSSVariableService, useClass: CSSVariableServiceStub},
|
||||||
|
{provide: Router, useValue: new RouterStub()},
|
||||||
|
]
|
||||||
|
}).overrideComponent(DsoEditMenuSectionComponent, {
|
||||||
|
set: {
|
||||||
|
entryComponents: [TestComponent]
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.compileComponents();
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
describe('DsoEditMenuSectionComponent', () => {
|
||||||
|
let component: DsoEditMenuSectionComponent;
|
||||||
|
let fixture: ComponentFixture<DsoEditMenuSectionComponent>;
|
||||||
|
const menuService = new MenuServiceStub();
|
||||||
|
const iconString = 'test';
|
||||||
|
|
||||||
|
const dummySectionText = {
|
||||||
|
id: 'dummy',
|
||||||
|
active: false,
|
||||||
|
visible: true,
|
||||||
|
model: {
|
||||||
|
type: MenuItemType.TEXT,
|
||||||
|
disabled: false,
|
||||||
|
text: 'text'
|
||||||
|
},
|
||||||
|
icon: iconString
|
||||||
|
};
|
||||||
|
const dummySectionLink = {
|
||||||
|
id: 'dummy',
|
||||||
|
active: false,
|
||||||
|
visible: true,
|
||||||
|
model: {
|
||||||
|
type: MenuItemType.LINK,
|
||||||
|
disabled: false,
|
||||||
|
text: 'text',
|
||||||
|
link: 'link'
|
||||||
|
},
|
||||||
|
icon: iconString
|
||||||
|
};
|
||||||
|
|
||||||
|
const dummySectionClick = {
|
||||||
|
id: 'dummy',
|
||||||
|
active: false,
|
||||||
|
visible: true,
|
||||||
|
model: {
|
||||||
|
type: MenuItemType.ONCLICK,
|
||||||
|
disabled: false,
|
||||||
|
text: 'text',
|
||||||
|
function: () => 'test'
|
||||||
|
},
|
||||||
|
icon: iconString
|
||||||
|
};
|
||||||
|
|
||||||
|
describe('text model', () => {
|
||||||
|
initAsync(dummySectionText, menuService);
|
||||||
|
beforeEach(() => {
|
||||||
|
spyOn(menuService, 'getSubSectionsByParentID').and.returnValue(observableOf([]));
|
||||||
|
fixture = TestBed.createComponent(DsoEditMenuSectionComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
spyOn(component as any, 'getMenuItemComponent').and.returnValue(TestComponent);
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should show a button with the icon', () => {
|
||||||
|
const button = fixture.debugElement.query(By.css('.btn-dark'));
|
||||||
|
expect(button.nativeElement.innerHTML).toContain('fa-' + iconString);
|
||||||
|
});
|
||||||
|
describe('when the section model in a disabled link or text', () => {
|
||||||
|
it('should show just the button', () => {
|
||||||
|
const textButton = fixture.debugElement.query(By.css('div div button'));
|
||||||
|
expect(textButton.nativeElement.innerHTML).toContain('fa-' + iconString);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
describe('on click model', () => {
|
||||||
|
initAsync(dummySectionClick, menuService);
|
||||||
|
beforeEach(() => {
|
||||||
|
spyOn(menuService, 'getSubSectionsByParentID').and.returnValue(observableOf([]));
|
||||||
|
fixture = TestBed.createComponent(DsoEditMenuSectionComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
spyOn(component as any, 'getMenuItemComponent').and.returnValue(TestComponent);
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('when the section model in an on click menu', () => {
|
||||||
|
it('should call the activate method when clicking the button', () => {
|
||||||
|
spyOn(component, 'activate');
|
||||||
|
|
||||||
|
const button = fixture.debugElement.query(By.css('.btn-dark'));
|
||||||
|
button.triggerEventHandler('click', null);
|
||||||
|
|
||||||
|
expect(component.activate).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
describe('activate', () => {
|
||||||
|
const mockEvent = jasmine.createSpyObj('event', {
|
||||||
|
preventDefault: jasmine.createSpy('preventDefault'),
|
||||||
|
stopPropagation: jasmine.createSpy('stopPropagation'),
|
||||||
|
});
|
||||||
|
it('should call the item model function when not disabled', () => {
|
||||||
|
spyOn(component.section.model as OnClickMenuItemModel, 'function');
|
||||||
|
component.activate(mockEvent);
|
||||||
|
|
||||||
|
expect((component.section.model as OnClickMenuItemModel).function).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
it('should call not the item model function when disabled', () => {
|
||||||
|
spyOn(component.section.model as OnClickMenuItemModel, 'function');
|
||||||
|
component.itemModel.disabled = true;
|
||||||
|
component.activate(mockEvent);
|
||||||
|
|
||||||
|
expect((component.section.model as OnClickMenuItemModel).function).not.toHaveBeenCalled();
|
||||||
|
component.itemModel.disabled = false;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('link model', () => {
|
||||||
|
initAsync(dummySectionLink, menuService);
|
||||||
|
beforeEach(() => {
|
||||||
|
spyOn(menuService, 'getSubSectionsByParentID').and.returnValue(observableOf([]));
|
||||||
|
fixture = TestBed.createComponent(DsoEditMenuSectionComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
spyOn(component as any, 'getMenuItemComponent').and.returnValue(TestComponent);
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('when the section model in a non disabled link', () => {
|
||||||
|
it('should show a link element with the button in it', () => {
|
||||||
|
const link = fixture.debugElement.query(By.css('a'));
|
||||||
|
expect(link.nativeElement.innerHTML).toContain('button');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// declare a test component
|
||||||
|
@Component({
|
||||||
|
selector: 'ds-test-cmp',
|
||||||
|
template: ``
|
||||||
|
})
|
||||||
|
class TestComponent {
|
||||||
|
}
|
@@ -0,0 +1,51 @@
|
|||||||
|
import { Component, Inject, Injector, OnInit } from '@angular/core';
|
||||||
|
import { MenuID } from 'src/app/shared/menu/initial-menus-state';
|
||||||
|
import { rendersSectionForMenu } from 'src/app/shared/menu/menu-section.decorator';
|
||||||
|
import { MenuSectionComponent } from 'src/app/shared/menu/menu-section/menu-section.component';
|
||||||
|
import { MenuService } from '../../../menu/menu.service';
|
||||||
|
import { MenuSection } from '../../../menu/menu.reducer';
|
||||||
|
import { isNotEmpty } from '../../../empty.util';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents a non-expandable section in the dso edit menus
|
||||||
|
*/
|
||||||
|
@Component({
|
||||||
|
/* tslint:disable:component-selector */
|
||||||
|
selector: 'ds-dso-edit-menu-section',
|
||||||
|
templateUrl: './dso-edit-menu-section.component.html',
|
||||||
|
styleUrls: ['./dso-edit-menu-section.component.scss']
|
||||||
|
})
|
||||||
|
@rendersSectionForMenu(MenuID.DSO_EDIT, false)
|
||||||
|
export class DsoEditMenuSectionComponent extends MenuSectionComponent implements OnInit {
|
||||||
|
|
||||||
|
menuID: MenuID = MenuID.DSO_EDIT;
|
||||||
|
itemModel;
|
||||||
|
hasLink: boolean;
|
||||||
|
canActivate: boolean;
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
@Inject('sectionDataProvider') menuSection: MenuSection,
|
||||||
|
protected menuService: MenuService,
|
||||||
|
protected injector: Injector,
|
||||||
|
) {
|
||||||
|
super(menuSection, menuService, injector);
|
||||||
|
this.itemModel = menuSection.model;
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnInit(): void {
|
||||||
|
this.hasLink = isNotEmpty(this.itemModel?.link);
|
||||||
|
this.canActivate = isNotEmpty(this.itemModel?.function);
|
||||||
|
super.ngOnInit();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Activate the section's model funtion
|
||||||
|
*/
|
||||||
|
public activate(event: any) {
|
||||||
|
event.preventDefault();
|
||||||
|
if (!this.itemModel.disabled) {
|
||||||
|
this.itemModel.function();
|
||||||
|
}
|
||||||
|
event.stopPropagation();
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,6 @@
|
|||||||
|
<div class="dso-edit-menu d-flex flex-wrap">
|
||||||
|
<div *ngFor="let section of (sections | async)" class="ml-1">
|
||||||
|
<ng-container
|
||||||
|
*ngComponentOutlet="(sectionMap$ | async).get(section.id)?.component; injector: (sectionMap$ | async).get(section.id)?.injector;"></ng-container>
|
||||||
|
</div>
|
||||||
|
</div>
|
@@ -0,0 +1,78 @@
|
|||||||
|
import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
|
||||||
|
import { TranslateModule } from '@ngx-translate/core';
|
||||||
|
import { ChangeDetectionStrategy, Injector, NO_ERRORS_SCHEMA } from '@angular/core';
|
||||||
|
import { of as observableOf } from 'rxjs';
|
||||||
|
import { RouterTestingModule } from '@angular/router/testing';
|
||||||
|
import { ActivatedRoute } from '@angular/router';
|
||||||
|
import { DsoEditMenuComponent } from './dso-edit-menu.component';
|
||||||
|
import { MenuServiceStub } from '../../testing/menu-service.stub';
|
||||||
|
import { AuthorizationDataService } from '../../../core/data/feature-authorization/authorization-data.service';
|
||||||
|
import { AuthService } from '../../../core/auth/auth.service';
|
||||||
|
import { AuthServiceStub } from '../../testing/auth-service.stub';
|
||||||
|
import { MenuService } from '../../menu/menu.service';
|
||||||
|
import {
|
||||||
|
ExpandableNavbarSectionComponent
|
||||||
|
} from '../../../navbar/expandable-navbar-section/expandable-navbar-section.component';
|
||||||
|
import { MenuItemModel } from '../../menu/menu-item/models/menu-item.model';
|
||||||
|
|
||||||
|
describe('DsoEditMenuComponent', () => {
|
||||||
|
let comp: DsoEditMenuComponent;
|
||||||
|
let fixture: ComponentFixture<DsoEditMenuComponent>;
|
||||||
|
const menuService = new MenuServiceStub();
|
||||||
|
let authorizationService: AuthorizationDataService;
|
||||||
|
|
||||||
|
const routeStub = {
|
||||||
|
children: []
|
||||||
|
};
|
||||||
|
|
||||||
|
const section = {
|
||||||
|
id: 'edit-dso',
|
||||||
|
active: false,
|
||||||
|
visible: true,
|
||||||
|
model: {
|
||||||
|
type: null,
|
||||||
|
disabled: false,
|
||||||
|
} as MenuItemModel,
|
||||||
|
icon: 'pencil-alt',
|
||||||
|
index: 1
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
beforeEach(waitForAsync(() => {
|
||||||
|
authorizationService = jasmine.createSpyObj('authorizationService', {
|
||||||
|
isAuthorized: observableOf(true)
|
||||||
|
});
|
||||||
|
spyOn(menuService, 'getMenuTopSections').and.returnValue(observableOf([section]));
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
imports: [TranslateModule.forRoot(), RouterTestingModule],
|
||||||
|
declarations: [DsoEditMenuComponent],
|
||||||
|
providers: [
|
||||||
|
Injector,
|
||||||
|
{provide: MenuService, useValue: menuService},
|
||||||
|
{provide: AuthService, useClass: AuthServiceStub},
|
||||||
|
{provide: ActivatedRoute, useValue: routeStub},
|
||||||
|
{provide: AuthorizationDataService, useValue: authorizationService},
|
||||||
|
],
|
||||||
|
schemas: [NO_ERRORS_SCHEMA]
|
||||||
|
}).overrideComponent(ExpandableNavbarSectionComponent, {
|
||||||
|
set: {
|
||||||
|
changeDetection: ChangeDetectionStrategy.Default,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.compileComponents();
|
||||||
|
}));
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
fixture = TestBed.createComponent(DsoEditMenuComponent);
|
||||||
|
comp = fixture.componentInstance;
|
||||||
|
comp.sections = observableOf([]);
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('onInit', () => {
|
||||||
|
it('should create', () => {
|
||||||
|
expect(comp).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
@@ -0,0 +1,36 @@
|
|||||||
|
import { Component, Injector } from '@angular/core';
|
||||||
|
import { AuthorizationDataService } from 'src/app/core/data/feature-authorization/authorization-data.service';
|
||||||
|
import { MenuID } from '../../menu/initial-menus-state';
|
||||||
|
import { MenuComponent } from '../../menu/menu.component';
|
||||||
|
import { MenuService } from '../../menu/menu.service';
|
||||||
|
import { ActivatedRoute } from '@angular/router';
|
||||||
|
import { AuthService } from '../../../core/auth/auth.service';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Component representing the edit menu and other menus on the dspace object pages
|
||||||
|
*/
|
||||||
|
@Component({
|
||||||
|
selector: 'ds-dso-edit-menu',
|
||||||
|
styleUrls: ['./dso-edit-menu.component.scss'],
|
||||||
|
templateUrl: './dso-edit-menu.component.html',
|
||||||
|
})
|
||||||
|
export class DsoEditMenuComponent extends MenuComponent {
|
||||||
|
/**
|
||||||
|
* The menu ID of this component is DSO_EDIT
|
||||||
|
* @type {MenuID.DSO_EDIT}
|
||||||
|
*/
|
||||||
|
menuID = MenuID.DSO_EDIT;
|
||||||
|
|
||||||
|
constructor(protected menuService: MenuService,
|
||||||
|
protected injector: Injector,
|
||||||
|
public authorizationService: AuthorizationDataService,
|
||||||
|
public route: ActivatedRoute,
|
||||||
|
private authService: AuthService,
|
||||||
|
) {
|
||||||
|
super(menuService, injector, authorizationService, route);
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnInit(): void {
|
||||||
|
super.ngOnInit();
|
||||||
|
}
|
||||||
|
}
|
@@ -1,7 +0,0 @@
|
|||||||
<a *ngIf="isAuthorized$ | async"
|
|
||||||
[routerLink]="[pageRoute, 'edit', 'metadata']"
|
|
||||||
class="edit-button btn btn-dark btn-sm"
|
|
||||||
[ngbTooltip]="tooltipMsg | translate"
|
|
||||||
role="button" [title]="tooltipMsg |translate" [attr.aria-label]="tooltipMsg |translate">
|
|
||||||
<i class="fas fa-pencil-alt fa-fw"></i>
|
|
||||||
</a>
|
|
@@ -1,3 +0,0 @@
|
|||||||
.btn-dark {
|
|
||||||
background-color: var(--ds-admin-sidebar-bg);
|
|
||||||
}
|
|
@@ -1,76 +0,0 @@
|
|||||||
import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
|
|
||||||
import { DsoPageEditButtonComponent } from './dso-page-edit-button.component';
|
|
||||||
import { DSpaceObject } from '../../../core/shared/dspace-object.model';
|
|
||||||
import { Item } from '../../../core/shared/item.model';
|
|
||||||
import { AuthorizationDataService } from '../../../core/data/feature-authorization/authorization-data.service';
|
|
||||||
import { of as observableOf } from 'rxjs';
|
|
||||||
import { TranslateModule } from '@ngx-translate/core';
|
|
||||||
import { RouterTestingModule } from '@angular/router/testing';
|
|
||||||
import { FeatureID } from '../../../core/data/feature-authorization/feature-id';
|
|
||||||
import { By } from '@angular/platform-browser';
|
|
||||||
import { NgbModule } from '@ng-bootstrap/ng-bootstrap';
|
|
||||||
|
|
||||||
describe('DsoPageEditButtonComponent', () => {
|
|
||||||
let component: DsoPageEditButtonComponent;
|
|
||||||
let fixture: ComponentFixture<DsoPageEditButtonComponent>;
|
|
||||||
|
|
||||||
let authorizationService: AuthorizationDataService;
|
|
||||||
let dso: DSpaceObject;
|
|
||||||
|
|
||||||
beforeEach(waitForAsync(() => {
|
|
||||||
dso = Object.assign(new Item(), {
|
|
||||||
id: 'test-item',
|
|
||||||
_links: {
|
|
||||||
self: { href: 'test-item-selflink' }
|
|
||||||
}
|
|
||||||
});
|
|
||||||
authorizationService = jasmine.createSpyObj('authorizationService', {
|
|
||||||
isAuthorized: observableOf(true)
|
|
||||||
});
|
|
||||||
TestBed.configureTestingModule({
|
|
||||||
declarations: [DsoPageEditButtonComponent],
|
|
||||||
imports: [TranslateModule.forRoot(), RouterTestingModule.withRoutes([]), NgbModule],
|
|
||||||
providers: [
|
|
||||||
{ provide: AuthorizationDataService, useValue: authorizationService }
|
|
||||||
]
|
|
||||||
}).compileComponents();
|
|
||||||
}));
|
|
||||||
|
|
||||||
beforeEach(() => {
|
|
||||||
fixture = TestBed.createComponent(DsoPageEditButtonComponent);
|
|
||||||
component = fixture.componentInstance;
|
|
||||||
component.dso = dso;
|
|
||||||
component.pageRoute = 'test';
|
|
||||||
fixture.detectChanges();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should check the authorization of the current user', () => {
|
|
||||||
expect(authorizationService.isAuthorized).toHaveBeenCalledWith(FeatureID.CanEditMetadata, dso.self);
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('when the user is authorized', () => {
|
|
||||||
beforeEach(() => {
|
|
||||||
(authorizationService.isAuthorized as jasmine.Spy).and.returnValue(observableOf(true));
|
|
||||||
component.ngOnInit();
|
|
||||||
fixture.detectChanges();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should render a link', () => {
|
|
||||||
const link = fixture.debugElement.query(By.css('a'));
|
|
||||||
expect(link).not.toBeNull();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('when the user is not authorized', () => {
|
|
||||||
beforeEach(() => {
|
|
||||||
(authorizationService.isAuthorized as jasmine.Spy).and.returnValue(observableOf(false));
|
|
||||||
component.ngOnInit();
|
|
||||||
fixture.detectChanges();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should not render a link', () => {
|
|
||||||
const link = fixture.debugElement.query(By.css('a'));
|
|
||||||
expect(link).toBeNull();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
@@ -1,43 +0,0 @@
|
|||||||
import { Component, Input, OnInit } from '@angular/core';
|
|
||||||
import { DSpaceObject } from '../../../core/shared/dspace-object.model';
|
|
||||||
import { AuthorizationDataService } from '../../../core/data/feature-authorization/authorization-data.service';
|
|
||||||
import { Observable } from 'rxjs';
|
|
||||||
import { FeatureID } from '../../../core/data/feature-authorization/feature-id';
|
|
||||||
|
|
||||||
@Component({
|
|
||||||
selector: 'ds-dso-page-edit-button',
|
|
||||||
templateUrl: './dso-page-edit-button.component.html',
|
|
||||||
styleUrls: ['./dso-page-edit-button.component.scss']
|
|
||||||
})
|
|
||||||
/**
|
|
||||||
* Display a button linking to the edit page of a DSpaceObject
|
|
||||||
*/
|
|
||||||
export class DsoPageEditButtonComponent implements OnInit {
|
|
||||||
/**
|
|
||||||
* The DSpaceObject to display a button to the edit page for
|
|
||||||
*/
|
|
||||||
@Input() dso: DSpaceObject;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The prefix of the route to the edit page (before the object's UUID, e.g. "items")
|
|
||||||
*/
|
|
||||||
@Input() pageRoute: string;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A message for the tooltip on the button
|
|
||||||
* Supports i18n keys
|
|
||||||
*/
|
|
||||||
@Input() tooltipMsg: string;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Whether or not the current user is authorized to edit the DSpaceObject
|
|
||||||
*/
|
|
||||||
isAuthorized$: Observable<boolean>;
|
|
||||||
|
|
||||||
constructor(protected authorizationService: AuthorizationDataService) { }
|
|
||||||
|
|
||||||
ngOnInit() {
|
|
||||||
this.isAuthorized$ = this.authorizationService.isAuthorized(FeatureID.CanEditMetadata, this.dso.self);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@@ -1,8 +0,0 @@
|
|||||||
<button *ngIf="isAuthorized$ | async"
|
|
||||||
class="edit-button btn btn-dark btn-sm"
|
|
||||||
(click)="createNewVersion()"
|
|
||||||
[disabled]="disableNewVersionButton$ | async"
|
|
||||||
[ngbTooltip]="tooltipMsg$ | async | translate"
|
|
||||||
role="button" [title]="tooltipMsg$ | async |translate" [attr.aria-label]="tooltipMsg$ | async | translate">
|
|
||||||
<i class="fas fa-code-branch fa-fw"></i>
|
|
||||||
</button>
|
|
@@ -1,3 +0,0 @@
|
|||||||
.btn-dark {
|
|
||||||
background-color: var(--ds-admin-sidebar-bg);
|
|
||||||
}
|
|
@@ -1,96 +0,0 @@
|
|||||||
import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
|
|
||||||
import { DsoPageVersionButtonComponent } from './dso-page-version-button.component';
|
|
||||||
import { Item } from '../../../core/shared/item.model';
|
|
||||||
import { AuthorizationDataService } from '../../../core/data/feature-authorization/authorization-data.service';
|
|
||||||
import { Observable, of, of as observableOf } from 'rxjs';
|
|
||||||
import { TranslateModule } from '@ngx-translate/core';
|
|
||||||
import { RouterTestingModule } from '@angular/router/testing';
|
|
||||||
import { FeatureID } from '../../../core/data/feature-authorization/feature-id';
|
|
||||||
import { By } from '@angular/platform-browser';
|
|
||||||
import { NgbModule } from '@ng-bootstrap/ng-bootstrap';
|
|
||||||
import { VersionHistoryDataService } from '../../../core/data/version-history-data.service';
|
|
||||||
|
|
||||||
describe('DsoPageVersionButtonComponent', () => {
|
|
||||||
let component: DsoPageVersionButtonComponent;
|
|
||||||
let fixture: ComponentFixture<DsoPageVersionButtonComponent>;
|
|
||||||
|
|
||||||
let authorizationService: AuthorizationDataService;
|
|
||||||
let versionHistoryService: VersionHistoryDataService;
|
|
||||||
|
|
||||||
let dso: Item;
|
|
||||||
let tooltipMsg: Observable<string>;
|
|
||||||
|
|
||||||
const authorizationServiceSpy = jasmine.createSpyObj('authorizationService', ['isAuthorized']);
|
|
||||||
|
|
||||||
const versionHistoryServiceSpy = jasmine.createSpyObj('versionHistoryService',
|
|
||||||
['getVersions', 'getLatestVersionFromHistory$', 'isLatest$', 'hasDraftVersion$']
|
|
||||||
);
|
|
||||||
|
|
||||||
beforeEach(waitForAsync(() => {
|
|
||||||
dso = Object.assign(new Item(), {
|
|
||||||
id: 'test-item',
|
|
||||||
_links: {
|
|
||||||
self: { href: 'test-item-selflink' },
|
|
||||||
version: { href: 'test-item-version-selflink' },
|
|
||||||
},
|
|
||||||
});
|
|
||||||
tooltipMsg = of('tooltip-msg');
|
|
||||||
|
|
||||||
TestBed.configureTestingModule({
|
|
||||||
declarations: [DsoPageVersionButtonComponent],
|
|
||||||
imports: [TranslateModule.forRoot(), RouterTestingModule.withRoutes([]), NgbModule],
|
|
||||||
providers: [
|
|
||||||
{ provide: AuthorizationDataService, useValue: authorizationServiceSpy },
|
|
||||||
{ provide: VersionHistoryDataService, useValue: versionHistoryServiceSpy },
|
|
||||||
]
|
|
||||||
}).compileComponents();
|
|
||||||
|
|
||||||
authorizationService = TestBed.inject(AuthorizationDataService);
|
|
||||||
versionHistoryService = TestBed.inject(VersionHistoryDataService);
|
|
||||||
|
|
||||||
versionHistoryServiceSpy.hasDraftVersion$.and.returnValue(observableOf(true));
|
|
||||||
}));
|
|
||||||
|
|
||||||
beforeEach(() => {
|
|
||||||
fixture = TestBed.createComponent(DsoPageVersionButtonComponent);
|
|
||||||
component = fixture.componentInstance;
|
|
||||||
component.dso = dso;
|
|
||||||
component.tooltipMsg$ = tooltipMsg;
|
|
||||||
fixture.detectChanges();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should check the authorization of the current user', () => {
|
|
||||||
expect(authorizationService.isAuthorized).toHaveBeenCalledWith(FeatureID.CanCreateVersion, dso.self);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should check if the item has a draft version', () => {
|
|
||||||
expect(versionHistoryServiceSpy.hasDraftVersion$).toHaveBeenCalledWith(dso._links.version.href);
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('when the user is authorized', () => {
|
|
||||||
beforeEach(() => {
|
|
||||||
authorizationServiceSpy.isAuthorized.and.returnValue(observableOf(true));
|
|
||||||
component.ngOnInit();
|
|
||||||
fixture.detectChanges();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should render a button', () => {
|
|
||||||
const button = fixture.debugElement.query(By.css('button'));
|
|
||||||
expect(button).not.toBeNull();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('when the user is not authorized', () => {
|
|
||||||
beforeEach(() => {
|
|
||||||
authorizationServiceSpy.isAuthorized.and.returnValue(observableOf(false));
|
|
||||||
component.ngOnInit();
|
|
||||||
fixture.detectChanges();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should render a button', () => {
|
|
||||||
const button = fixture.debugElement.query(By.css('button'));
|
|
||||||
expect(button).toBeNull();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
});
|
|
@@ -1,77 +0,0 @@
|
|||||||
import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core';
|
|
||||||
import { AuthorizationDataService } from '../../../core/data/feature-authorization/authorization-data.service';
|
|
||||||
import { Observable, of } from 'rxjs';
|
|
||||||
import { FeatureID } from '../../../core/data/feature-authorization/feature-id';
|
|
||||||
import { VersionHistoryDataService } from '../../../core/data/version-history-data.service';
|
|
||||||
import { Item } from '../../../core/shared/item.model';
|
|
||||||
import { map, startWith, switchMap } from 'rxjs/operators';
|
|
||||||
|
|
||||||
@Component({
|
|
||||||
selector: 'ds-dso-page-version-button',
|
|
||||||
templateUrl: './dso-page-version-button.component.html',
|
|
||||||
styleUrls: ['./dso-page-version-button.component.scss']
|
|
||||||
})
|
|
||||||
/**
|
|
||||||
* Display a button linking to the edit page of a DSpaceObject
|
|
||||||
*/
|
|
||||||
export class DsoPageVersionButtonComponent implements OnInit {
|
|
||||||
/**
|
|
||||||
* The item for which display a button to create a new version
|
|
||||||
*/
|
|
||||||
@Input() dso: Item;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A message for the tooltip on the button
|
|
||||||
* Supports i18n keys
|
|
||||||
*/
|
|
||||||
@Input() tooltipMsgCreate: string;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A message for the tooltip on the button (when is disabled)
|
|
||||||
* Supports i18n keys
|
|
||||||
*/
|
|
||||||
@Input() tooltipMsgHasDraft: string;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Emits an event that triggers the creation of the new version
|
|
||||||
*/
|
|
||||||
@Output() newVersionEvent = new EventEmitter();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Whether or not the current user is authorized to create a new version of the DSpaceObject
|
|
||||||
*/
|
|
||||||
isAuthorized$: Observable<boolean>;
|
|
||||||
|
|
||||||
disableNewVersionButton$: Observable<boolean>;
|
|
||||||
|
|
||||||
tooltipMsg$: Observable<string>;
|
|
||||||
|
|
||||||
constructor(
|
|
||||||
protected authorizationService: AuthorizationDataService,
|
|
||||||
protected versionHistoryService: VersionHistoryDataService,
|
|
||||||
) {
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a new version for the current item
|
|
||||||
*/
|
|
||||||
createNewVersion() {
|
|
||||||
this.newVersionEvent.emit();
|
|
||||||
}
|
|
||||||
|
|
||||||
ngOnInit() {
|
|
||||||
this.isAuthorized$ = this.authorizationService.isAuthorized(FeatureID.CanCreateVersion, this.dso.self);
|
|
||||||
|
|
||||||
this.disableNewVersionButton$ = this.versionHistoryService.hasDraftVersion$(this.dso._links.version.href).pipe(
|
|
||||||
// button is disabled if hasDraftVersion = true, and enabled if hasDraftVersion = false or null
|
|
||||||
// (hasDraftVersion is null when a version history does not exist)
|
|
||||||
map((res) => Boolean(res)),
|
|
||||||
startWith(true),
|
|
||||||
);
|
|
||||||
|
|
||||||
this.tooltipMsg$ = this.disableNewVersionButton$.pipe(
|
|
||||||
switchMap((hasDraftVersion) => of(hasDraftVersion ? this.tooltipMsgHasDraft : this.tooltipMsgCreate)),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@@ -0,0 +1,92 @@
|
|||||||
|
import { DsoVersioningModalService } from './dso-versioning-modal.service';
|
||||||
|
import { waitForAsync } from '@angular/core/testing';
|
||||||
|
import { createSuccessfulRemoteDataObject$ } from '../../remote-data.utils';
|
||||||
|
import { Version } from '../../../core/shared/version.model';
|
||||||
|
import { Item } from '../../../core/shared/item.model';
|
||||||
|
import { MetadataMap } from '../../../core/shared/metadata.models';
|
||||||
|
import { createRelationshipsObservable } from '../../../item-page/simple/item-types/shared/item.component.spec';
|
||||||
|
import { buildPaginatedList } from '../../../core/data/paginated-list.model';
|
||||||
|
import { PageInfo } from '../../../core/shared/page-info.model';
|
||||||
|
import { EMPTY, of as observableOf } from 'rxjs';
|
||||||
|
|
||||||
|
fdescribe('DsoVersioningModalService', () => {
|
||||||
|
let service: DsoVersioningModalService;
|
||||||
|
let modalService;
|
||||||
|
let versionService;
|
||||||
|
let versionHistoryService;
|
||||||
|
let itemVersionShared;
|
||||||
|
let router;
|
||||||
|
let workspaceItemDataService;
|
||||||
|
let itemService;
|
||||||
|
|
||||||
|
const mockItem: Item = Object.assign(new Item(), {
|
||||||
|
bundles: createSuccessfulRemoteDataObject$(buildPaginatedList(new PageInfo(), [])),
|
||||||
|
metadata: new MetadataMap(),
|
||||||
|
relationships: createRelationshipsObservable(),
|
||||||
|
_links: {
|
||||||
|
self: {
|
||||||
|
href: 'item-href'
|
||||||
|
},
|
||||||
|
version: {
|
||||||
|
href: 'version-href'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
beforeEach(waitForAsync(() => {
|
||||||
|
modalService = jasmine.createSpyObj('modalService', {
|
||||||
|
open: {componentInstance: {firstVersion: {}, versionNumber: {}, createVersionEvent: EMPTY}}
|
||||||
|
});
|
||||||
|
versionService = jasmine.createSpyObj('versionService', {
|
||||||
|
findByHref: createSuccessfulRemoteDataObject$<Version>(new Version()),
|
||||||
|
});
|
||||||
|
versionHistoryService = jasmine.createSpyObj('versionHistoryService', {
|
||||||
|
createVersion: createSuccessfulRemoteDataObject$<Version>(new Version()),
|
||||||
|
hasDraftVersion$: observableOf(false)
|
||||||
|
});
|
||||||
|
itemVersionShared = jasmine.createSpyObj('itemVersionShared', ['notifyCreateNewVersion']);
|
||||||
|
router = jasmine.createSpyObj('router', ['navigateByUrl']);
|
||||||
|
workspaceItemDataService = jasmine.createSpyObj('workspaceItemDataService', ['findByItem']);
|
||||||
|
itemService = jasmine.createSpyObj('itemService', ['findByHref']);
|
||||||
|
|
||||||
|
service = new DsoVersioningModalService(
|
||||||
|
modalService,
|
||||||
|
versionService,
|
||||||
|
versionHistoryService,
|
||||||
|
itemVersionShared,
|
||||||
|
router,
|
||||||
|
workspaceItemDataService,
|
||||||
|
itemService
|
||||||
|
);
|
||||||
|
}));
|
||||||
|
describe('when onCreateNewVersion() is called', () => {
|
||||||
|
it('should call versionService.findByHref', () => {
|
||||||
|
service.openCreateVersionModal(mockItem);
|
||||||
|
expect(versionService.findByHref).toHaveBeenCalledWith('version-href');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('isNewVersionButtonDisabled', () => {
|
||||||
|
it('should call versionHistoryService.hasDraftVersion$', () => {
|
||||||
|
service.isNewVersionButtonDisabled(mockItem);
|
||||||
|
expect(versionHistoryService.hasDraftVersion$).toHaveBeenCalledWith(mockItem._links.version.href);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('getVersioningTooltipMessage', () => {
|
||||||
|
it('should return the create message when isNewVersionButtonDisabled returns false', (done) => {
|
||||||
|
spyOn(service, 'isNewVersionButtonDisabled').and.returnValue(observableOf(false));
|
||||||
|
service.getVersioningTooltipMessage(mockItem, 'draft-message', 'create-message').subscribe((message) => {
|
||||||
|
expect(message).toEqual('create-message');
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
it('should return the draft message when isNewVersionButtonDisabled returns true', (done) => {
|
||||||
|
spyOn(service, 'isNewVersionButtonDisabled').and.returnValue(observableOf(true));
|
||||||
|
service.getVersioningTooltipMessage(mockItem, 'draft-message', 'create-message').subscribe((message) => {
|
||||||
|
expect(message).toEqual('draft-message');
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
@@ -0,0 +1,98 @@
|
|||||||
|
import {
|
||||||
|
ItemVersionsSummaryModalComponent
|
||||||
|
} from '../../item/item-versions/item-versions-summary-modal/item-versions-summary-modal.component';
|
||||||
|
import { getFirstCompletedRemoteData, getFirstSucceededRemoteDataPayload } from '../../../core/shared/operators';
|
||||||
|
import { RemoteData } from '../../../core/data/remote-data';
|
||||||
|
import { Version } from '../../../core/shared/version.model';
|
||||||
|
import { map, startWith, switchMap, tap } from 'rxjs/operators';
|
||||||
|
import { Item } from '../../../core/shared/item.model';
|
||||||
|
import { WorkspaceItem } from '../../../core/submission/models/workspaceitem.model';
|
||||||
|
import { NgbModal } from '@ng-bootstrap/ng-bootstrap';
|
||||||
|
import { VersionDataService } from '../../../core/data/version-data.service';
|
||||||
|
import { VersionHistoryDataService } from '../../../core/data/version-history-data.service';
|
||||||
|
import { ItemVersionsSharedService } from '../../item/item-versions/item-versions-shared.service';
|
||||||
|
import { Router } from '@angular/router';
|
||||||
|
import { WorkspaceitemDataService } from '../../../core/submission/workspaceitem-data.service';
|
||||||
|
import { ItemDataService } from '../../../core/data/item-data.service';
|
||||||
|
import { Injectable } from '@angular/core';
|
||||||
|
import { Observable, of } from 'rxjs';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Service to take care of all the functionality related to the version creation modal
|
||||||
|
*/
|
||||||
|
@Injectable({
|
||||||
|
providedIn: 'root'
|
||||||
|
})
|
||||||
|
export class DsoVersioningModalService {
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
protected modalService: NgbModal,
|
||||||
|
protected versionService: VersionDataService,
|
||||||
|
protected versionHistoryService: VersionHistoryDataService,
|
||||||
|
protected itemVersionShared: ItemVersionsSharedService,
|
||||||
|
protected router: Router,
|
||||||
|
protected workspaceItemDataService: WorkspaceitemDataService,
|
||||||
|
protected itemService: ItemDataService,
|
||||||
|
) {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Open the create version modal for the provided dso
|
||||||
|
*/
|
||||||
|
openCreateVersionModal(dso): void {
|
||||||
|
|
||||||
|
const item = dso;
|
||||||
|
const versionHref = item._links.version.href;
|
||||||
|
|
||||||
|
// Open modal
|
||||||
|
const activeModal = this.modalService.open(ItemVersionsSummaryModalComponent);
|
||||||
|
|
||||||
|
// Show current version in modal
|
||||||
|
this.versionService.findByHref(versionHref).pipe(getFirstCompletedRemoteData()).subscribe((res: RemoteData<Version>) => {
|
||||||
|
// if res.hasNoContent then the item is unversioned
|
||||||
|
activeModal.componentInstance.firstVersion = res.hasNoContent;
|
||||||
|
activeModal.componentInstance.versionNumber = (res.hasNoContent ? undefined : res.payload.version);
|
||||||
|
});
|
||||||
|
|
||||||
|
// On createVersionEvent emitted create new version and notify
|
||||||
|
activeModal.componentInstance.createVersionEvent.pipe(
|
||||||
|
switchMap((summary: string) => this.versionHistoryService.createVersion(item._links.self.href, summary)),
|
||||||
|
getFirstCompletedRemoteData(),
|
||||||
|
// show success/failure notification
|
||||||
|
tap((res: RemoteData<Version>) => {
|
||||||
|
this.itemVersionShared.notifyCreateNewVersion(res);
|
||||||
|
}),
|
||||||
|
// get workspace item
|
||||||
|
getFirstSucceededRemoteDataPayload<Version>(),
|
||||||
|
switchMap((newVersion: Version) => this.itemService.findByHref(newVersion._links.item.href)),
|
||||||
|
getFirstSucceededRemoteDataPayload<Item>(),
|
||||||
|
switchMap((newVersionItem: Item) => this.workspaceItemDataService.findByItem(newVersionItem.uuid, true, false)),
|
||||||
|
getFirstSucceededRemoteDataPayload<WorkspaceItem>(),
|
||||||
|
).subscribe((wsItem) => {
|
||||||
|
const wsiId = wsItem.id;
|
||||||
|
const route = 'workspaceitems/' + wsiId + '/edit';
|
||||||
|
this.router.navigateByUrl(route);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if the new version button should be disabled for the provided dso
|
||||||
|
*/
|
||||||
|
isNewVersionButtonDisabled(dso): Observable<boolean> {
|
||||||
|
return this.versionHistoryService.hasDraftVersion$(dso._links.version.href).pipe(
|
||||||
|
// button is disabled if hasDraftVersion = true, and enabled if hasDraftVersion = false or null
|
||||||
|
// (hasDraftVersion is null when a version history does not exist)
|
||||||
|
map((res) => Boolean(res)),
|
||||||
|
startWith(true),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks and returns the tooltip that needs to be used for the create version button tooltip
|
||||||
|
*/
|
||||||
|
getVersioningTooltipMessage(dso, tooltipMsgHasDraft, tooltipMsgCreate): Observable<string> {
|
||||||
|
return this.isNewVersionButtonDisabled(dso).pipe(
|
||||||
|
switchMap((hasDraftVersion) => of(hasDraftVersion ? tooltipMsgHasDraft : tooltipMsgCreate)),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@@ -5,7 +5,8 @@ import { MenusState } from './menu.reducer';
|
|||||||
*/
|
*/
|
||||||
export enum MenuID {
|
export enum MenuID {
|
||||||
ADMIN = 'admin-sidebar',
|
ADMIN = 'admin-sidebar',
|
||||||
PUBLIC = 'public'
|
PUBLIC = 'public',
|
||||||
|
DSO_EDIT = 'dso-edit'
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -36,5 +37,14 @@ export const initialMenusState: MenusState = {
|
|||||||
visible: true,
|
visible: true,
|
||||||
sections: {},
|
sections: {},
|
||||||
sectionToSubsectionIndex: {}
|
sectionToSubsectionIndex: {}
|
||||||
}
|
},
|
||||||
|
[MenuID.DSO_EDIT]:
|
||||||
|
{
|
||||||
|
id: MenuID.DSO_EDIT,
|
||||||
|
collapsed: true,
|
||||||
|
previewCollapsed: true,
|
||||||
|
visible: false,
|
||||||
|
sections: {},
|
||||||
|
sectionToSubsectionIndex: {}
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
<a class="nav-item nav-link"
|
<a class="nav-item nav-link"
|
||||||
[ngClass]="{ 'disabled': !hasLink }"
|
[ngClass]="{ 'disabled': !hasLink || item.disabled }"
|
||||||
[attr.aria-disabled]="!hasLink"
|
[attr.aria-disabled]="!hasLink || item.disabled"
|
||||||
[title]="item.text | translate"
|
[title]="item.text | translate"
|
||||||
[routerLink]="getRouterLink()"
|
[routerLink]="getRouterLink()"
|
||||||
(click)="$event.stopPropagation()"
|
(click)="$event.stopPropagation()"
|
||||||
|
@@ -37,7 +37,7 @@ export class LinkMenuItemComponent implements OnInit {
|
|||||||
|
|
||||||
navigate(event: any) {
|
navigate(event: any) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
if (this.getRouterLink()) {
|
if (!this.item.disabled && this.getRouterLink()) {
|
||||||
this.router.navigate([this.getRouterLink()]);
|
this.router.navigate([this.getRouterLink()]);
|
||||||
}
|
}
|
||||||
event.stopPropagation();
|
event.stopPropagation();
|
||||||
|
@@ -6,5 +6,6 @@ import { MenuItemModel } from './menu-item.model';
|
|||||||
*/
|
*/
|
||||||
export class AltmetricMenuItemModel implements MenuItemModel {
|
export class AltmetricMenuItemModel implements MenuItemModel {
|
||||||
type = MenuItemType.ALTMETRIC;
|
type = MenuItemType.ALTMETRIC;
|
||||||
|
disabled: boolean;
|
||||||
url: string;
|
url: string;
|
||||||
}
|
}
|
||||||
|
@@ -6,6 +6,7 @@ import { MenuItemType } from '../../initial-menus-state';
|
|||||||
*/
|
*/
|
||||||
export class LinkMenuItemModel implements MenuItemModel {
|
export class LinkMenuItemModel implements MenuItemModel {
|
||||||
type = MenuItemType.LINK;
|
type = MenuItemType.LINK;
|
||||||
|
disabled: boolean;
|
||||||
text: string;
|
text: string;
|
||||||
link: string;
|
link: string;
|
||||||
}
|
}
|
||||||
|
@@ -5,4 +5,5 @@ import { MenuItemType } from '../../initial-menus-state';
|
|||||||
*/
|
*/
|
||||||
export interface MenuItemModel {
|
export interface MenuItemModel {
|
||||||
type: MenuItemType;
|
type: MenuItemType;
|
||||||
|
disabled: boolean;
|
||||||
}
|
}
|
||||||
|
@@ -6,6 +6,7 @@ import { MenuItemType } from '../../initial-menus-state';
|
|||||||
*/
|
*/
|
||||||
export class OnClickMenuItemModel implements MenuItemModel {
|
export class OnClickMenuItemModel implements MenuItemModel {
|
||||||
type = MenuItemType.ONCLICK;
|
type = MenuItemType.ONCLICK;
|
||||||
|
disabled: boolean;
|
||||||
text: string;
|
text: string;
|
||||||
function: () => {};
|
function: () => {};
|
||||||
}
|
}
|
||||||
|
@@ -6,6 +6,7 @@ import { MenuItemModel } from './menu-item.model';
|
|||||||
*/
|
*/
|
||||||
export class SearchMenuItemModel implements MenuItemModel {
|
export class SearchMenuItemModel implements MenuItemModel {
|
||||||
type = MenuItemType.SEARCH;
|
type = MenuItemType.SEARCH;
|
||||||
|
disabled: boolean;
|
||||||
placeholder: string;
|
placeholder: string;
|
||||||
action: string;
|
action: string;
|
||||||
}
|
}
|
||||||
|
@@ -6,5 +6,6 @@ import { MenuItemModel } from './menu-item.model';
|
|||||||
*/
|
*/
|
||||||
export class TextMenuItemModel implements MenuItemModel {
|
export class TextMenuItemModel implements MenuItemModel {
|
||||||
type = MenuItemType.TEXT;
|
type = MenuItemType.TEXT;
|
||||||
|
disabled: boolean;
|
||||||
text: string;
|
text: string;
|
||||||
}
|
}
|
||||||
|
@@ -1,4 +1,5 @@
|
|||||||
<a href="javascript:void(0);"
|
<a *ngIf="!item.disabled"
|
||||||
|
href="javascript:void(0);"
|
||||||
class="nav-item nav-link"
|
class="nav-item nav-link"
|
||||||
role="button"
|
role="button"
|
||||||
[title]="item.text | translate"
|
[title]="item.text | translate"
|
||||||
@@ -6,3 +7,4 @@
|
|||||||
(keyup.space)="activate($event)"
|
(keyup.space)="activate($event)"
|
||||||
(keyup.enter)="activate($event)"
|
(keyup.enter)="activate($event)"
|
||||||
>{{item.text | translate}}</a>
|
>{{item.text | translate}}</a>
|
||||||
|
<span *ngIf="item.disabled" class="nav-item nav-link disabled">{{item.text | translate}}</span>
|
||||||
|
@@ -14,13 +14,16 @@ import { OnClickMenuItemModel } from './models/onclick.model';
|
|||||||
@rendersMenuItemForType(MenuItemType.ONCLICK)
|
@rendersMenuItemForType(MenuItemType.ONCLICK)
|
||||||
export class OnClickMenuItemComponent {
|
export class OnClickMenuItemComponent {
|
||||||
item: OnClickMenuItemModel;
|
item: OnClickMenuItemModel;
|
||||||
|
|
||||||
constructor(@Inject('itemModelProvider') item: OnClickMenuItemModel) {
|
constructor(@Inject('itemModelProvider') item: OnClickMenuItemModel) {
|
||||||
this.item = item;
|
this.item = item;
|
||||||
}
|
}
|
||||||
|
|
||||||
public activate(event: any) {
|
public activate(event: any) {
|
||||||
|
if (!this.item.disabled) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
this.item.function();
|
this.item.function();
|
||||||
event.stopPropagation();
|
event.stopPropagation();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1 +1 @@
|
|||||||
<span>{{item.text | translate}}</span>
|
<span [class.disabled]="item.disabled">{{item.text | translate}}</span>
|
||||||
|
@@ -64,8 +64,10 @@ export class MenuSectionComponent implements OnInit, OnDestroy {
|
|||||||
*/
|
*/
|
||||||
toggleSection(event: Event) {
|
toggleSection(event: Event) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
|
if (!this.section.model?.disabled) {
|
||||||
this.menuService.toggleActiveSection(this.menuID, this.section.id);
|
this.menuService.toggleActiveSection(this.menuID, this.section.id);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Activate this section
|
* Activate this section
|
||||||
@@ -73,8 +75,10 @@ export class MenuSectionComponent implements OnInit, OnDestroy {
|
|||||||
*/
|
*/
|
||||||
activateSection(event: Event) {
|
activateSection(event: Event) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
|
if (!this.section.model?.disabled) {
|
||||||
this.menuService.activateSection(this.menuID, this.section.id);
|
this.menuService.activateSection(this.menuID, this.section.id);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Deactivate this section
|
* Deactivate this section
|
||||||
|
@@ -157,8 +157,6 @@ import { SidebarSearchListElementComponent } from './object-list/sidebar-search-
|
|||||||
import { CollectionSidebarSearchListElementComponent } from './object-list/sidebar-search-list-element/collection/collection-sidebar-search-list-element.component';
|
import { CollectionSidebarSearchListElementComponent } from './object-list/sidebar-search-list-element/collection/collection-sidebar-search-list-element.component';
|
||||||
import { CommunitySidebarSearchListElementComponent } from './object-list/sidebar-search-list-element/community/community-sidebar-search-list-element.component';
|
import { CommunitySidebarSearchListElementComponent } from './object-list/sidebar-search-list-element/community/community-sidebar-search-list-element.component';
|
||||||
import { AuthorizedCollectionSelectorComponent } from './dso-selector/dso-selector/authorized-collection-selector/authorized-collection-selector.component';
|
import { AuthorizedCollectionSelectorComponent } from './dso-selector/dso-selector/authorized-collection-selector/authorized-collection-selector.component';
|
||||||
import { DsoPageEditButtonComponent } from './dso-page/dso-page-edit-button/dso-page-edit-button.component';
|
|
||||||
import { DsoPageVersionButtonComponent } from './dso-page/dso-page-version-button/dso-page-version-button.component';
|
|
||||||
import { HoverClassDirective } from './hover-class.directive';
|
import { HoverClassDirective } from './hover-class.directive';
|
||||||
import { ValidationSuggestionsComponent } from './input-suggestions/validation-suggestions/validation-suggestions.component';
|
import { ValidationSuggestionsComponent } from './input-suggestions/validation-suggestions/validation-suggestions.component';
|
||||||
import { ItemAlertsComponent } from './item/item-alerts/item-alerts.component';
|
import { ItemAlertsComponent } from './item/item-alerts/item-alerts.component';
|
||||||
@@ -177,6 +175,13 @@ import { ScopeSelectorModalComponent } from './search-form/scope-selector-modal/
|
|||||||
import { BitstreamRequestACopyPageComponent } from './bitstream-request-a-copy-page/bitstream-request-a-copy-page.component';
|
import { BitstreamRequestACopyPageComponent } from './bitstream-request-a-copy-page/bitstream-request-a-copy-page.component';
|
||||||
import { DsSelectComponent } from './ds-select/ds-select.component';
|
import { DsSelectComponent } from './ds-select/ds-select.component';
|
||||||
import { LogInOidcComponent } from './log-in/methods/oidc/log-in-oidc.component';
|
import { LogInOidcComponent } from './log-in/methods/oidc/log-in-oidc.component';
|
||||||
|
import {
|
||||||
|
DsoEditMenuSectionComponent
|
||||||
|
} from './dso-page/dso-edit-menu/dso-edit-menu-section/dso-edit-menu-section.component';
|
||||||
|
import { DsoEditMenuComponent } from './dso-page/dso-edit-menu/dso-edit-menu.component';
|
||||||
|
import {
|
||||||
|
DsoEditMenuExpandableSectionComponent
|
||||||
|
} from './dso-page/dso-edit-menu/dso-edit-expandable-menu-section/dso-edit-menu-expandable-section.component';
|
||||||
|
|
||||||
const MODULES = [
|
const MODULES = [
|
||||||
// Do NOT include UniversalModule, HttpModule, or JsonpModule here
|
// Do NOT include UniversalModule, HttpModule, or JsonpModule here
|
||||||
@@ -402,18 +407,20 @@ const ENTRY_COMPONENTS = [
|
|||||||
OnClickMenuItemComponent,
|
OnClickMenuItemComponent,
|
||||||
TextMenuItemComponent,
|
TextMenuItemComponent,
|
||||||
ScopeSelectorModalComponent,
|
ScopeSelectorModalComponent,
|
||||||
|
DsoEditMenuSectionComponent,
|
||||||
|
DsoEditMenuExpandableSectionComponent,
|
||||||
];
|
];
|
||||||
|
|
||||||
const SHARED_ITEM_PAGE_COMPONENTS = [
|
const SHARED_ITEM_PAGE_COMPONENTS = [
|
||||||
MetadataFieldWrapperComponent,
|
MetadataFieldWrapperComponent,
|
||||||
MetadataValuesComponent,
|
MetadataValuesComponent,
|
||||||
DsoPageEditButtonComponent,
|
|
||||||
DsoPageVersionButtonComponent,
|
|
||||||
ItemAlertsComponent,
|
ItemAlertsComponent,
|
||||||
GenericItemPageFieldComponent,
|
GenericItemPageFieldComponent,
|
||||||
MetadataRepresentationListComponent,
|
MetadataRepresentationListComponent,
|
||||||
RelatedItemsComponent,
|
RelatedItemsComponent,
|
||||||
|
DsoEditMenuSectionComponent,
|
||||||
|
DsoEditMenuComponent,
|
||||||
|
DsoEditMenuExpandableSectionComponent,
|
||||||
];
|
];
|
||||||
|
|
||||||
const PROVIDERS = [
|
const PROVIDERS = [
|
||||||
|
Reference in New Issue
Block a user