diff --git a/src/app/+admin/admin-access-control/admin-access-control-routing.module.ts b/src/app/+admin/admin-access-control/admin-access-control-routing.module.ts
index 5af18c778f..f61a3c2f71 100644
--- a/src/app/+admin/admin-access-control/admin-access-control-routing.module.ts
+++ b/src/app/+admin/admin-access-control/admin-access-control-routing.module.ts
@@ -6,7 +6,7 @@ import { GroupsRegistryComponent } from './group-registry/groups-registry.compon
import { URLCombiner } from '../../core/url-combiner/url-combiner';
import { getAccessControlModulePath } from '../admin-routing.module';
-const GROUP_EDIT_PATH = 'groups';
+export const GROUP_EDIT_PATH = 'groups';
export function getGroupEditPath(id: string) {
return new URLCombiner(getAccessControlModulePath(), GROUP_EDIT_PATH, id).toString();
diff --git a/src/app/+admin/admin-routing.module.ts b/src/app/+admin/admin-routing.module.ts
index 3d910761b8..b199129c4e 100644
--- a/src/app/+admin/admin-routing.module.ts
+++ b/src/app/+admin/admin-routing.module.ts
@@ -8,7 +8,7 @@ import { I18nBreadcrumbsService } from '../core/breadcrumbs/i18n-breadcrumbs.ser
import { URLCombiner } from '../core/url-combiner/url-combiner';
const REGISTRIES_MODULE_PATH = 'registries';
-const ACCESS_CONTROL_MODULE_PATH = 'access-control';
+export const ACCESS_CONTROL_MODULE_PATH = 'access-control';
export function getRegistriesModulePath() {
return new URLCombiner(getAdminModulePath(), REGISTRIES_MODULE_PATH).toString();
diff --git a/src/app/+item-page/edit-item-page/edit-item-page.module.ts b/src/app/+item-page/edit-item-page/edit-item-page.module.ts
index ac6125fb1c..acb23fe592 100644
--- a/src/app/+item-page/edit-item-page/edit-item-page.module.ts
+++ b/src/app/+item-page/edit-item-page/edit-item-page.module.ts
@@ -29,6 +29,9 @@ import { ItemEditBitstreamDragHandleComponent } from './item-bitstreams/item-edi
import { PaginatedDragAndDropBitstreamListComponent } from './item-bitstreams/item-edit-bitstream-bundle/paginated-drag-and-drop-bitstream-list/paginated-drag-and-drop-bitstream-list.component';
import { VirtualMetadataComponent } from './virtual-metadata/virtual-metadata.component';
import { ItemVersionHistoryComponent } from './item-version-history/item-version-history.component';
+import { ItemAuthorizationsComponent } from './item-authorizations/item-authorizations.component';
+import { ResourcePolicyEditComponent } from '../../shared/resource-policies/edit/resource-policy-edit.component';
+import { ResourcePolicyCreateComponent } from '../../shared/resource-policies/create/resource-policy-create.component';
/**
* Module that contains all components related to the Edit Item page administrator functionality
@@ -67,6 +70,9 @@ import { ItemVersionHistoryComponent } from './item-version-history/item-version
ItemMoveComponent,
ItemEditBitstreamDragHandleComponent,
VirtualMetadataComponent,
+ ItemAuthorizationsComponent,
+ ResourcePolicyEditComponent,
+ ResourcePolicyCreateComponent,
],
providers: [
BundleDataService
diff --git a/src/app/+item-page/edit-item-page/edit-item-page.routing.module.ts b/src/app/+item-page/edit-item-page/edit-item-page.routing.module.ts
index e4b1b06730..87b4b7a592 100644
--- a/src/app/+item-page/edit-item-page/edit-item-page.routing.module.ts
+++ b/src/app/+item-page/edit-item-page/edit-item-page.routing.module.ts
@@ -14,6 +14,12 @@ import { ItemMoveComponent } from './item-move/item-move.component';
import { ItemRelationshipsComponent } from './item-relationships/item-relationships.component';
import { I18nBreadcrumbResolver } from '../../core/breadcrumbs/i18n-breadcrumb.resolver';
import { ItemVersionHistoryComponent } from './item-version-history/item-version-history.component';
+import { ItemAuthorizationsComponent } from './item-authorizations/item-authorizations.component';
+import { ResourcePolicyTargetResolver } from '../../shared/resource-policies/resolvers/resource-policy-target.resolver';
+import { ResourcePolicyResolver } from '../../shared/resource-policies/resolvers/resource-policy.resolver';
+import { ResourcePolicyCreateComponent } from '../../shared/resource-policies/create/resource-policy-create.component';
+import { ResourcePolicyEditComponent } from '../../shared/resource-policies/edit/resource-policy-edit.component';
+import { I18nBreadcrumbsService } from '../../core/breadcrumbs/i18n-breadcrumbs.service';
export const ITEM_EDIT_WITHDRAW_PATH = 'withdraw';
export const ITEM_EDIT_REINSTATE_PATH = 'reinstate';
@@ -21,6 +27,7 @@ export const ITEM_EDIT_PRIVATE_PATH = 'private';
export const ITEM_EDIT_PUBLIC_PATH = 'public';
export const ITEM_EDIT_DELETE_PATH = 'delete';
export const ITEM_EDIT_MOVE_PATH = 'move';
+export const ITEM_EDIT_AUTHORIZATIONS_PATH = 'authorizations';
/**
* Routing module that handles the routing for the Edit Item page administrator functionality
@@ -111,12 +118,43 @@ export const ITEM_EDIT_MOVE_PATH = 'move';
path: ITEM_EDIT_MOVE_PATH,
component: ItemMoveComponent,
data: { title: 'item.edit.move.title' },
+ },
+ {
+ path: ITEM_EDIT_AUTHORIZATIONS_PATH,
+ children: [
+ {
+ path: 'create',
+ resolve: {
+ resourcePolicyTarget: ResourcePolicyTargetResolver
+ },
+ component: ResourcePolicyCreateComponent,
+ data: { title: 'resource-policies.create.page.title' }
+ },
+ {
+ path: 'edit',
+ resolve: {
+ resourcePolicy: ResourcePolicyResolver
+ },
+ component: ResourcePolicyEditComponent,
+ data: { title: 'resource-policies.edit.page.title' }
+ },
+ {
+ path: '',
+ component: ItemAuthorizationsComponent,
+ data: { title: 'item.edit.authorizations.title' }
+ }
+ ]
}
]
}
])
],
- providers: []
+ providers: [
+ I18nBreadcrumbResolver,
+ I18nBreadcrumbsService,
+ ResourcePolicyResolver,
+ ResourcePolicyTargetResolver
+ ]
})
export class EditItemPageRoutingModule {
diff --git a/src/app/+item-page/edit-item-page/item-authorizations/item-authorizations.component.html b/src/app/+item-page/edit-item-page/item-authorizations/item-authorizations.component.html
new file mode 100644
index 0000000000..71aa7b44de
--- /dev/null
+++ b/src/app/+item-page/edit-item-page/item-authorizations/item-authorizations.component.html
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/app/+item-page/edit-item-page/item-authorizations/item-authorizations.component.spec.ts b/src/app/+item-page/edit-item-page/item-authorizations/item-authorizations.component.spec.ts
new file mode 100644
index 0000000000..c687c829eb
--- /dev/null
+++ b/src/app/+item-page/edit-item-page/item-authorizations/item-authorizations.component.spec.ts
@@ -0,0 +1,183 @@
+import { async, ComponentFixture, inject, TestBed } from '@angular/core/testing';
+import { Component, NO_ERRORS_SCHEMA } from '@angular/core';
+import { ActivatedRoute } from '@angular/router';
+import { NoopAnimationsModule } from '@angular/platform-browser/animations';
+
+import { of as observableOf } from 'rxjs';
+import { TranslateModule } from '@ngx-translate/core';
+import { cold } from 'jasmine-marbles';
+
+import { ItemAuthorizationsComponent } from './item-authorizations.component';
+import { Bitstream } from '../../../core/shared/bitstream.model';
+import { Bundle } from '../../../core/shared/bundle.model';
+import { createMockRDPaginatedObs } from '../item-bitstreams/item-bitstreams.component.spec';
+import { Item } from '../../../core/shared/item.model';
+import { LinkService } from '../../../core/cache/builders/link.service';
+import { getMockLinkService } from '../../../shared/mocks/link-service.mock';
+import { createSuccessfulRemoteDataObject } from '../../../shared/remote-data.utils';
+import { createTestComponent } from '../../../shared/testing/utils.test';
+import { PaginatedList } from '../../../core/data/paginated-list';
+import { PageInfo } from '../../../core/shared/page-info.model';
+
+describe('ItemAuthorizationsComponent test suite', () => {
+ let comp: ItemAuthorizationsComponent;
+ let compAsAny: any;
+ let fixture: ComponentFixture;
+ let de;
+
+ const linkService: any = getMockLinkService();
+
+ const bitstream1 = Object.assign(new Bitstream(), {
+ id: 'bitstream1',
+ uuid: 'bitstream1'
+ });
+ const bitstream2 = Object.assign(new Bitstream(), {
+ id: 'bitstream2',
+ uuid: 'bitstream2'
+ });
+ const bitstream3 = Object.assign(new Bitstream(), {
+ id: 'bitstream3',
+ uuid: 'bitstream3'
+ });
+ const bitstream4 = Object.assign(new Bitstream(), {
+ id: 'bitstream4',
+ uuid: 'bitstream4'
+ });
+ const bundle1 = Object.assign(new Bundle(), {
+ id: 'bundle1',
+ uuid: 'bundle1',
+ _links: {
+ self: { href: 'bundle1-selflink' }
+ },
+ bitstreams: createMockRDPaginatedObs([bitstream1, bitstream2])
+ });
+ const bundle2 = Object.assign(new Bundle(), {
+ id: 'bundle2',
+ uuid: 'bundle2',
+ _links: {
+ self: { href: 'bundle2-selflink' }
+ },
+ bitstreams: createMockRDPaginatedObs([bitstream3, bitstream4])
+ });
+ const bundles = [bundle1, bundle2];
+ const bitstreamList1: PaginatedList = new PaginatedList(new PageInfo(), [bitstream1, bitstream2]);
+ const bitstreamList2: PaginatedList = new PaginatedList(new PageInfo(), [bitstream3, bitstream4]);
+
+ const item = Object.assign(new Item(), {
+ uuid: 'item',
+ id: 'item',
+ _links: {
+ self: { href: 'item-selflink' }
+ },
+ bundles: createMockRDPaginatedObs([bundle1, bundle2])
+ });
+
+ const routeStub = {
+ data: observableOf({
+ item: createSuccessfulRemoteDataObject(item)
+ })
+ };
+
+ beforeEach(async(() => {
+ TestBed.configureTestingModule({
+ imports: [
+ NoopAnimationsModule,
+ TranslateModule.forRoot()
+ ],
+ declarations: [
+ ItemAuthorizationsComponent,
+ TestComponent
+ ],
+ providers: [
+ { provide: LinkService, useValue: linkService },
+ { provide: ActivatedRoute, useValue: routeStub },
+ ItemAuthorizationsComponent
+ ],
+ schemas: [
+ NO_ERRORS_SCHEMA
+ ]
+ }).compileComponents();
+ }));
+
+ describe('', () => {
+ let testComp: TestComponent;
+ let testFixture: ComponentFixture;
+
+ // synchronous beforeEach
+ beforeEach(() => {
+ const html = `
+ `;
+
+ testFixture = createTestComponent(html, TestComponent) as ComponentFixture;
+ testComp = testFixture.componentInstance;
+ });
+
+ afterEach(() => {
+ testFixture.destroy();
+ });
+
+ it('should create ItemAuthorizationsComponent', inject([ItemAuthorizationsComponent], (app: ItemAuthorizationsComponent) => {
+
+ expect(app).toBeDefined();
+
+ }));
+ });
+
+ describe('', () => {
+ beforeEach(() => {
+ fixture = TestBed.createComponent(ItemAuthorizationsComponent);
+ comp = fixture.componentInstance;
+ compAsAny = fixture.componentInstance;
+ linkService.resolveLink.and.callFake((object, link) => object);
+ fixture.detectChanges();
+ });
+
+ afterEach(() => {
+ comp = null;
+ compAsAny = null;
+ de = null;
+ fixture.destroy();
+ });
+
+ it('should init bundles and bitstreams map properly', () => {
+ expect(compAsAny.subs.length).toBe(2);
+ expect(compAsAny.bundles$.value).toEqual(bundles);
+ expect(compAsAny.bundleBitstreamsMap.has('bundle1')).toBeTruthy();
+ expect(compAsAny.bundleBitstreamsMap.has('bundle2')).toBeTruthy();
+ let bitstreamList = compAsAny.bundleBitstreamsMap.get('bundle1');
+ expect(bitstreamList).toBeObservable(cold('(a|)', {
+ a: bitstreamList1
+ }));
+
+ bitstreamList = compAsAny.bundleBitstreamsMap.get('bundle2');
+ expect(bitstreamList).toBeObservable(cold('(a|)', {
+ a: bitstreamList2
+ }));
+ });
+
+ it('should get the item UUID', () => {
+
+ expect(comp.getItemUUID()).toBeObservable(cold('(a|)', {
+ a: item.id
+ }));
+
+ });
+
+ it('should get the item\'s bundle', () => {
+
+ expect(comp.getItemBundles()).toBeObservable(cold('a', {
+ a: bundles
+ }));
+
+ });
+ });
+});
+
+// declare a test component
+@Component({
+ selector: 'ds-test-cmp',
+ template: ``
+})
+class TestComponent {
+
+}
diff --git a/src/app/+item-page/edit-item-page/item-authorizations/item-authorizations.component.ts b/src/app/+item-page/edit-item-page/item-authorizations/item-authorizations.component.ts
new file mode 100644
index 0000000000..8153990a02
--- /dev/null
+++ b/src/app/+item-page/edit-item-page/item-authorizations/item-authorizations.component.ts
@@ -0,0 +1,155 @@
+import { Component, OnDestroy, OnInit } from '@angular/core';
+import { ActivatedRoute } from '@angular/router';
+
+import { BehaviorSubject, Observable, of as observableOf, Subscription } from 'rxjs';
+import { catchError, filter, first, flatMap, map, take } from 'rxjs/operators';
+
+import { PaginatedList } from '../../../core/data/paginated-list';
+import {
+ getFirstSucceededRemoteDataPayload,
+ getFirstSucceededRemoteDataWithNotEmptyPayload
+} from '../../../core/shared/operators';
+import { Item } from '../../../core/shared/item.model';
+import { followLink } from '../../../shared/utils/follow-link-config.model';
+import { LinkService } from '../../../core/cache/builders/link.service';
+import { Bundle } from '../../../core/shared/bundle.model';
+import { hasValue, isNotEmpty } from '../../../shared/empty.util';
+import { Bitstream } from '../../../core/shared/bitstream.model';
+import { FindListOptions } from '../../../core/data/request.models';
+
+/**
+ * Interface for a bundle's bitstream map entry
+ */
+interface BundleBitstreamsMapEntry {
+ id: string;
+ bitstreams: Observable>
+}
+
+@Component({
+ selector: 'ds-item-authorizations',
+ templateUrl: './item-authorizations.component.html'
+})
+/**
+ * Component that handles the item Authorizations
+ */
+export class ItemAuthorizationsComponent implements OnInit, OnDestroy {
+
+ /**
+ * A map that contains all bitstream of the item's bundles
+ * @type {Observable