diff --git a/README.md b/README.md
index 78d7816f65..5d8a323461 100644
--- a/README.md
+++ b/README.md
@@ -104,7 +104,7 @@ DSPACE_REST_SSL # Whether the angular REST uses SSL [true/false]
The same settings can also be overwritten by setting system environment variables instead, E.g.:
```bash
-export DSPACE_HOST=https://dspace7.4science.cloud/server
+export DSPACE_HOST=dspace7.4science.cloud
```
The priority works as follows: **environment variable** overrides **variable in `.env` file** overrides **`environment.(prod, dev or test).ts`** overrides **`environment.common.ts`**
diff --git a/package.json b/package.json
index 8d3c936212..21a89400bf 100644
--- a/package.json
+++ b/package.json
@@ -101,7 +101,7 @@
"moment": "^2.22.1",
"morgan": "^1.9.1",
"ng-mocks": "^8.1.0",
- "ng2-file-upload": "1.2.1",
+ "ng2-file-upload": "1.4.0",
"ng2-nouislider": "^1.8.2",
"ngx-bootstrap": "^5.3.2",
"ngx-infinite-scroll": "6.0.1",
diff --git a/src/app/+admin/admin-registries/metadata-schema/metadata-field-form/metadata-field-form.component.spec.ts b/src/app/+admin/admin-registries/metadata-schema/metadata-field-form/metadata-field-form.component.spec.ts
index 98128a6a61..0cb259c6d1 100644
--- a/src/app/+admin/admin-registries/metadata-schema/metadata-field-form/metadata-field-form.component.spec.ts
+++ b/src/app/+admin/admin-registries/metadata-schema/metadata-field-form/metadata-field-form.component.spec.ts
@@ -27,7 +27,8 @@ describe('MetadataFieldFormComponent', () => {
/* tslint:disable:no-empty */
const registryServiceStub = {
getActiveMetadataField: () => observableOf(undefined),
- createOrUpdateMetadataField: (field: MetadataField) => observableOf(field),
+ createMetadataField: (field: MetadataField) => observableOf(field),
+ updateMetadataField: (field: MetadataField) => observableOf(field),
cancelEditMetadataField: () => {},
cancelEditMetadataSchema: () => {},
clearMetadataFieldRequests: () => observableOf(undefined)
@@ -75,7 +76,6 @@ describe('MetadataFieldFormComponent', () => {
const scopeNote = 'fakeScopeNote';
const expected = Object.assign(new MetadataField(), {
- schema: metadataSchema,
element: element,
qualifier: qualifier,
scopeNote: scopeNote
diff --git a/src/app/+admin/admin-registries/metadata-schema/metadata-field-form/metadata-field-form.component.ts b/src/app/+admin/admin-registries/metadata-schema/metadata-field-form/metadata-field-form.component.ts
index 42f6441791..70cdc15b4d 100644
--- a/src/app/+admin/admin-registries/metadata-schema/metadata-field-form/metadata-field-form.component.ts
+++ b/src/app/+admin/admin-registries/metadata-schema/metadata-field-form/metadata-field-form.component.ts
@@ -157,19 +157,17 @@ export class MetadataFieldFormComponent implements OnInit, OnDestroy {
this.registryService.getActiveMetadataField().pipe(take(1)).subscribe(
(field) => {
const values = {
- schema: this.metadataSchema,
element: this.element.value,
qualifier: this.qualifier.value,
scopeNote: this.scopeNote.value
};
if (field == null) {
- this.registryService.createOrUpdateMetadataField(Object.assign(new MetadataField(), values)).subscribe((newField) => {
+ this.registryService.createMetadataField(Object.assign(new MetadataField(), values), this.metadataSchema).subscribe((newField) => {
this.submitForm.emit(newField);
});
} else {
- this.registryService.createOrUpdateMetadataField(Object.assign(new MetadataField(), field, {
+ this.registryService.updateMetadataField(Object.assign(new MetadataField(), field, {
id: field.id,
- schema: this.metadataSchema,
element: (values.element ? values.element : field.element),
qualifier: (values.qualifier ? values.qualifier : field.qualifier),
scopeNote: (values.scopeNote ? values.scopeNote : field.scopeNote)
diff --git a/src/app/+admin/admin-registries/metadata-schema/metadata-schema.component.spec.ts b/src/app/+admin/admin-registries/metadata-schema/metadata-schema.component.spec.ts
index 26f4c90b0c..9532a64e84 100644
--- a/src/app/+admin/admin-registries/metadata-schema/metadata-schema.component.spec.ts
+++ b/src/app/+admin/admin-registries/metadata-schema/metadata-schema.component.spec.ts
@@ -61,7 +61,7 @@ describe('MetadataSchemaComponent', () => {
element: 'contributor',
qualifier: 'advisor',
scopeNote: null,
- schema: mockSchemasList[0]
+ schema: createSuccessfulRemoteDataObject$(mockSchemasList[0])
},
{
id: 2,
@@ -73,7 +73,7 @@ describe('MetadataSchemaComponent', () => {
element: 'contributor',
qualifier: 'author',
scopeNote: null,
- schema: mockSchemasList[0]
+ schema: createSuccessfulRemoteDataObject$(mockSchemasList[0])
},
{
id: 3,
@@ -85,7 +85,7 @@ describe('MetadataSchemaComponent', () => {
element: 'contributor',
qualifier: 'editor',
scopeNote: 'test scope note',
- schema: mockSchemasList[1]
+ schema: createSuccessfulRemoteDataObject$(mockSchemasList[1])
},
{
id: 4,
@@ -97,15 +97,15 @@ describe('MetadataSchemaComponent', () => {
element: 'contributor',
qualifier: 'illustrator',
scopeNote: null,
- schema: mockSchemasList[1]
+ schema: createSuccessfulRemoteDataObject$(mockSchemasList[1])
}
];
const mockSchemas = createSuccessfulRemoteDataObject$(new PaginatedList(null, mockSchemasList));
/* tslint:disable:no-empty */
const registryServiceStub = {
getMetadataSchemas: () => mockSchemas,
- getMetadataFieldsBySchema: (schema: MetadataSchema) => createSuccessfulRemoteDataObject$(new PaginatedList(null, mockFieldsList.filter((value) => value.schema === schema))),
- getMetadataSchemaByName: (schemaName: string) => createSuccessfulRemoteDataObject$(mockSchemasList.filter((value) => value.prefix === schemaName)[0]),
+ getMetadataFieldsBySchema: (schema: MetadataSchema) => createSuccessfulRemoteDataObject$(new PaginatedList(null, mockFieldsList.filter((value) => value.id === 3 || value.id === 4))),
+ getMetadataSchemaByPrefix: (schemaName: string) => createSuccessfulRemoteDataObject$(mockSchemasList.filter((value) => value.prefix === schemaName)[0]),
getActiveMetadataField: () => observableOf(undefined),
getSelectedMetadataFields: () => observableOf([]),
editMetadataField: (schema) => {},
diff --git a/src/app/+admin/admin-registries/metadata-schema/metadata-schema.component.ts b/src/app/+admin/admin-registries/metadata-schema/metadata-schema.component.ts
index 96872573b9..87f4b863c0 100644
--- a/src/app/+admin/admin-registries/metadata-schema/metadata-schema.component.ts
+++ b/src/app/+admin/admin-registries/metadata-schema/metadata-schema.component.ts
@@ -17,6 +17,7 @@ import { getFirstSucceededRemoteDataPayload } from '../../../core/shared/operato
import { toFindListOptions } from '../../../shared/pagination/pagination.utils';
import { BehaviorSubject } from 'rxjs/internal/BehaviorSubject';
import { combineLatest } from 'rxjs/internal/observable/combineLatest';
+import { followLink } from '../../../shared/utils/follow-link-config.model';
@Component({
selector: 'ds-metadata-schema',
@@ -71,7 +72,7 @@ export class MetadataSchemaComponent implements OnInit {
* @param params
*/
initialize(params) {
- this.metadataSchema$ = this.registryService.getMetadataSchemaByName(params.schemaName).pipe(getFirstSucceededRemoteDataPayload());
+ this.metadataSchema$ = this.registryService.getMetadataSchemaByPrefix(params.schemaName).pipe(getFirstSucceededRemoteDataPayload());
this.updateFields();
}
@@ -91,7 +92,7 @@ export class MetadataSchemaComponent implements OnInit {
this.metadataFields$ = combineLatest(this.metadataSchema$, this.needsUpdate$).pipe(
switchMap(([schema, update]: [MetadataSchema, boolean]) => {
if (update) {
- return this.registryService.getMetadataFieldsBySchema(schema, toFindListOptions(this.config));
+ return this.registryService.getMetadataFieldsBySchema(schema, toFindListOptions(this.config), followLink('schema'));
}
})
);
diff --git a/src/app/+admin/admin-sidebar/admin-sidebar.component.ts b/src/app/+admin/admin-sidebar/admin-sidebar.component.ts
index a2818b699a..e5a71aa411 100644
--- a/src/app/+admin/admin-sidebar/admin-sidebar.component.ts
+++ b/src/app/+admin/admin-sidebar/admin-sidebar.component.ts
@@ -149,11 +149,17 @@ export class AdminSidebarComponent extends MenuComponent implements OnInit {
this.modalService.open(CreateItemParentSelectorComponent);
}
} as OnClickMenuItemModel,
- // model: {
- // type: MenuItemType.LINK,
- // text: 'menu.section.new_item',
- // link: '/submit'
- // } as LinkMenuItemModel,
+ },
+ {
+ id: 'new_process',
+ parentID: 'new',
+ active: false,
+ visible: true,
+ model: {
+ type: MenuItemType.LINK,
+ text: 'menu.section.new_process',
+ link: '/processes/new'
+ } as LinkMenuItemModel,
},
{
id: 'new_item_version',
@@ -350,6 +356,20 @@ export class AdminSidebarComponent extends MenuComponent implements OnInit {
icon: 'cogs',
index: 9
},
+
+ /* Processes */
+ {
+ id: 'processes',
+ active: false,
+ visible: true,
+ model: {
+ type: MenuItemType.LINK,
+ text: 'menu.section.processes',
+ link: '/processes'
+ } as LinkMenuItemModel,
+ icon: 'terminal',
+ index: 10
+ },
];
menuList.forEach((menuSection) => this.menuService.addSection(this.menuID, Object.assign(menuSection, {
shouldPersistOnRouteChange: true
@@ -466,7 +486,7 @@ export class AdminSidebarComponent extends MenuComponent implements OnInit {
link: '/admin/workflow'
} as LinkMenuItemModel,
icon: 'user-check',
- index: 10
+ index: 11
},
];
diff --git a/src/app/+collection-page/collection-page-routing.module.ts b/src/app/+collection-page/collection-page-routing.module.ts
index 589defa331..1ebd9b3630 100644
--- a/src/app/+collection-page/collection-page-routing.module.ts
+++ b/src/app/+collection-page/collection-page-routing.module.ts
@@ -9,10 +9,13 @@ import { CreateCollectionPageGuard } from './create-collection-page/create-colle
import { DeleteCollectionPageComponent } from './delete-collection-page/delete-collection-page.component';
import { URLCombiner } from '../core/url-combiner/url-combiner';
import { getCollectionModulePath } from '../app-routing.module';
+import { EditItemTemplatePageComponent } from './edit-item-template-page/edit-item-template-page.component';
+import { ItemTemplatePageResolver } from './edit-item-template-page/item-template-page.resolver';
import { CollectionItemMapperComponent } from './collection-item-mapper/collection-item-mapper.component';
import { CollectionBreadcrumbResolver } from '../core/breadcrumbs/collection-breadcrumb.resolver';
import { DSOBreadcrumbsService } from '../core/breadcrumbs/dso-breadcrumbs.service';
import { LinkService } from '../core/cache/builders/link.service';
+import { I18nBreadcrumbResolver } from '../core/breadcrumbs/i18n-breadcrumb.resolver';
export const COLLECTION_PARENT_PARAMETER = 'parent';
@@ -30,6 +33,7 @@ export function getCollectionCreatePath() {
const COLLECTION_CREATE_PATH = 'create';
const COLLECTION_EDIT_PATH = 'edit';
+const ITEMTEMPLATE_PATH = 'itemtemplate';
@NgModule({
imports: [
@@ -58,6 +62,16 @@ const COLLECTION_EDIT_PATH = 'edit';
component: DeleteCollectionPageComponent,
canActivate: [AuthenticatedGuard],
},
+ {
+ path: ITEMTEMPLATE_PATH,
+ component: EditItemTemplatePageComponent,
+ canActivate: [AuthenticatedGuard],
+ resolve: {
+ item: ItemTemplatePageResolver,
+ breadcrumb: I18nBreadcrumbResolver
+ },
+ data: { title: 'collection.edit.template.title', breadcrumbKey: 'collection.edit.template' }
+ },
{
path: '',
component: CollectionPageComponent,
@@ -75,6 +89,7 @@ const COLLECTION_EDIT_PATH = 'edit';
],
providers: [
CollectionPageResolver,
+ ItemTemplatePageResolver,
CollectionBreadcrumbResolver,
DSOBreadcrumbsService,
LinkService,
diff --git a/src/app/+collection-page/collection-page.module.ts b/src/app/+collection-page/collection-page.module.ts
index 03daae68ae..554d56815a 100644
--- a/src/app/+collection-page/collection-page.module.ts
+++ b/src/app/+collection-page/collection-page.module.ts
@@ -8,6 +8,8 @@ import { CollectionPageRoutingModule } from './collection-page-routing.module';
import { CreateCollectionPageComponent } from './create-collection-page/create-collection-page.component';
import { CollectionFormComponent } from './collection-form/collection-form.component';
import { DeleteCollectionPageComponent } from './delete-collection-page/delete-collection-page.component';
+import { EditItemTemplatePageComponent } from './edit-item-template-page/edit-item-template-page.component';
+import { EditItemPageModule } from '../+item-page/edit-item-page/edit-item-page.module';
import { CollectionItemMapperComponent } from './collection-item-mapper/collection-item-mapper.component';
import { SearchService } from '../core/shared/search/search.service';
import { StatisticsModule } from '../statistics/statistics.module';
@@ -17,13 +19,15 @@ import { StatisticsModule } from '../statistics/statistics.module';
CommonModule,
SharedModule,
CollectionPageRoutingModule,
- StatisticsModule.forRoot()
+ StatisticsModule.forRoot(),
+ EditItemPageModule
],
declarations: [
CollectionPageComponent,
CreateCollectionPageComponent,
DeleteCollectionPageComponent,
CollectionFormComponent,
+ EditItemTemplatePageComponent,
CollectionItemMapperComponent
],
exports: [
diff --git a/src/app/+collection-page/edit-collection-page/collection-metadata/collection-metadata.component.html b/src/app/+collection-page/edit-collection-page/collection-metadata/collection-metadata.component.html
index 6f3a63790d..b4eaf46bfb 100644
--- a/src/app/+collection-page/edit-collection-page/collection-metadata/collection-metadata.component.html
+++ b/src/app/+collection-page/edit-collection-page/collection-metadata/collection-metadata.component.html
@@ -1,3 +1,21 @@
+
+
+
+
+
+
+
+
diff --git a/src/app/+collection-page/edit-collection-page/collection-metadata/collection-metadata.component.spec.ts b/src/app/+collection-page/edit-collection-page/collection-metadata/collection-metadata.component.spec.ts
index 6b480c1a79..aec843afa3 100644
--- a/src/app/+collection-page/edit-collection-page/collection-metadata/collection-metadata.component.spec.ts
+++ b/src/app/+collection-page/edit-collection-page/collection-metadata/collection-metadata.component.spec.ts
@@ -4,16 +4,54 @@ import { SharedModule } from '../../../shared/shared.module';
import { CommonModule } from '@angular/common';
import { RouterTestingModule } from '@angular/router/testing';
import { CollectionDataService } from '../../../core/data/collection-data.service';
-import { ActivatedRoute } from '@angular/router';
+import { ActivatedRoute, Router } from '@angular/router';
import { of as observableOf } from 'rxjs/internal/observable/of';
import { NO_ERRORS_SCHEMA } from '@angular/core';
import { CollectionMetadataComponent } from './collection-metadata.component';
import { NotificationsService } from '../../../shared/notifications/notifications.service';
-import { NotificationsServiceStub } from '../../../shared/testing/notifications-service.stub';
+import { Item } from '../../../core/shared/item.model';
+import { ItemTemplateDataService } from '../../../core/data/item-template-data.service';
+import { Collection } from '../../../core/shared/collection.model';
+import { ObjectCacheService } from '../../../core/cache/object-cache.service';
+import { RequestService } from '../../../core/data/request.service';
+import { createSuccessfulRemoteDataObject, createSuccessfulRemoteDataObject$ } from '../../../shared/remote-data.utils';
describe('CollectionMetadataComponent', () => {
let comp: CollectionMetadataComponent;
let fixture: ComponentFixture;
+ let router: Router;
+ let itemTemplateService: ItemTemplateDataService;
+
+ const template = Object.assign(new Item(), {
+ _links: {
+ self: { href: 'template-selflink' }
+ }
+ });
+ const collection = Object.assign(new Collection(), {
+ uuid: 'collection-id',
+ id: 'collection-id',
+ name: 'Fake Collection',
+ _links: {
+ self: { href: 'collection-selflink' }
+ }
+ });
+
+ const itemTemplateServiceStub = Object.assign({
+ findByCollectionID: () => createSuccessfulRemoteDataObject$(template),
+ create: () => createSuccessfulRemoteDataObject$(template),
+ deleteByCollectionID: () => observableOf(true)
+ });
+
+ const notificationsService = jasmine.createSpyObj('notificationsService', {
+ success: {},
+ error: {}
+ });
+ const objectCache = jasmine.createSpyObj('objectCache', {
+ remove: {}
+ });
+ const requestService = jasmine.createSpyObj('requestService', {
+ removeByHrefSubstring: {}
+ });
beforeEach(async(() => {
TestBed.configureTestingModule({
@@ -21,8 +59,11 @@ describe('CollectionMetadataComponent', () => {
declarations: [CollectionMetadataComponent],
providers: [
{ provide: CollectionDataService, useValue: {} },
- { provide: ActivatedRoute, useValue: { parent: { data: observableOf({ dso: { payload: {} } }) } } },
- { provide: NotificationsService, useValue: new NotificationsServiceStub() }
+ { provide: ItemTemplateDataService, useValue: itemTemplateServiceStub },
+ { provide: ActivatedRoute, useValue: { parent: { data: observableOf({ dso: createSuccessfulRemoteDataObject(collection) }) } } },
+ { provide: NotificationsService, useValue: notificationsService },
+ { provide: ObjectCacheService, useValue: objectCache },
+ { provide: RequestService, useValue: requestService }
],
schemas: [NO_ERRORS_SCHEMA]
}).compileComponents();
@@ -31,12 +72,51 @@ describe('CollectionMetadataComponent', () => {
beforeEach(() => {
fixture = TestBed.createComponent(CollectionMetadataComponent);
comp = fixture.componentInstance;
+ router = (comp as any).router;
+ itemTemplateService = (comp as any).itemTemplateService;
fixture.detectChanges();
});
describe('frontendURL', () => {
it('should have the right frontendURL set', () => {
expect((comp as any).frontendURL).toEqual('/collections/');
- })
+ });
+ });
+
+ describe('addItemTemplate', () => {
+ it('should navigate to the collection\'s itemtemplate page', () => {
+ spyOn(router, 'navigate');
+ comp.addItemTemplate();
+ expect(router.navigate).toHaveBeenCalledWith(['collections', collection.uuid, 'itemtemplate']);
+ });
+ });
+
+ describe('deleteItemTemplate', () => {
+ describe('when delete returns a success', () => {
+ beforeEach(() => {
+ spyOn(itemTemplateService, 'deleteByCollectionID').and.returnValue(observableOf(true));
+ comp.deleteItemTemplate();
+ });
+
+ it('should display a success notification', () => {
+ expect(notificationsService.success).toHaveBeenCalled();
+ });
+
+ it('should reset related object and request cache', () => {
+ expect(objectCache.remove).toHaveBeenCalledWith(template.self);
+ expect(requestService.removeByHrefSubstring).toHaveBeenCalledWith(collection.self);
+ });
+ });
+
+ describe('when delete returns a failure', () => {
+ beforeEach(() => {
+ spyOn(itemTemplateService, 'deleteByCollectionID').and.returnValue(observableOf(false));
+ comp.deleteItemTemplate();
+ });
+
+ it('should display an error notification', () => {
+ expect(notificationsService.error).toHaveBeenCalled();
+ });
+ });
});
});
diff --git a/src/app/+collection-page/edit-collection-page/collection-metadata/collection-metadata.component.ts b/src/app/+collection-page/edit-collection-page/collection-metadata/collection-metadata.component.ts
index af2ab7d0a7..ca2a38aa86 100644
--- a/src/app/+collection-page/edit-collection-page/collection-metadata/collection-metadata.component.ts
+++ b/src/app/+collection-page/edit-collection-page/collection-metadata/collection-metadata.component.ts
@@ -3,8 +3,17 @@ import { ComcolMetadataComponent } from '../../../shared/comcol-forms/edit-comco
import { Collection } from '../../../core/shared/collection.model';
import { CollectionDataService } from '../../../core/data/collection-data.service';
import { ActivatedRoute, Router } from '@angular/router';
+import { ItemTemplateDataService } from '../../../core/data/item-template-data.service';
+import { Observable } from 'rxjs/internal/Observable';
+import { RemoteData } from '../../../core/data/remote-data';
+import { Item } from '../../../core/shared/item.model';
+import { getRemoteDataPayload, getSucceededRemoteData } from '../../../core/shared/operators';
+import { switchMap, take } from 'rxjs/operators';
+import { combineLatest as combineLatestObservable } from 'rxjs';
import { NotificationsService } from '../../../shared/notifications/notifications.service';
import { TranslateService } from '@ngx-translate/core';
+import { ObjectCacheService } from '../../../core/cache/object-cache.service';
+import { RequestService } from '../../../core/data/request.service';
/**
* Component for editing a collection's metadata
@@ -17,13 +26,91 @@ export class CollectionMetadataComponent extends ComcolMetadataComponent>;
+
public constructor(
protected collectionDataService: CollectionDataService,
+ protected itemTemplateService: ItemTemplateDataService,
protected router: Router,
protected route: ActivatedRoute,
protected notificationsService: NotificationsService,
- protected translate: TranslateService
+ protected translate: TranslateService,
+ protected objectCache: ObjectCacheService,
+ protected requestService: RequestService
) {
super(collectionDataService, router, route, notificationsService, translate);
}
+
+ ngOnInit(): void {
+ super.ngOnInit();
+ this.initTemplateItem();
+ }
+
+ /**
+ * Initialize the collection's item template
+ */
+ initTemplateItem() {
+ this.itemTemplateRD$ = this.dsoRD$.pipe(
+ getSucceededRemoteData(),
+ getRemoteDataPayload(),
+ switchMap((collection: Collection) => this.itemTemplateService.findByCollectionID(collection.uuid))
+ );
+ }
+
+ /**
+ * Add a new item template to the collection and redirect to the item template edit page
+ */
+ addItemTemplate() {
+ const collection$ = this.dsoRD$.pipe(
+ getSucceededRemoteData(),
+ getRemoteDataPayload(),
+ take(1)
+ );
+ const template$ = collection$.pipe(
+ switchMap((collection: Collection) => this.itemTemplateService.create(new Item(), collection.uuid)),
+ getSucceededRemoteData(),
+ getRemoteDataPayload(),
+ take(1)
+ );
+
+ combineLatestObservable(collection$, template$).subscribe(([collection, template]) => {
+ this.router.navigate(['collections', collection.uuid, 'itemtemplate']);
+ });
+ }
+
+ /**
+ * Delete the item template from the collection
+ */
+ deleteItemTemplate() {
+ const collection$ = this.dsoRD$.pipe(
+ getSucceededRemoteData(),
+ getRemoteDataPayload(),
+ take(1)
+ );
+ const template$ = collection$.pipe(
+ switchMap((collection: Collection) => this.itemTemplateService.findByCollectionID(collection.uuid)),
+ getSucceededRemoteData(),
+ getRemoteDataPayload(),
+ take(1)
+ );
+
+ combineLatestObservable(collection$, template$).pipe(
+ switchMap(([collection, template]) => {
+ const success$ = this.itemTemplateService.deleteByCollectionID(template, collection.uuid);
+ this.objectCache.remove(template.self);
+ this.requestService.removeByHrefSubstring(collection.self);
+ return success$;
+ })
+ ).subscribe((success: boolean) => {
+ if (success) {
+ this.notificationsService.success(null, this.translate.get('collection.edit.template.notifications.delete.success'));
+ } else {
+ this.notificationsService.error(null, this.translate.get('collection.edit.template.notifications.delete.error'));
+ }
+ this.initTemplateItem();
+ });
+ }
}
diff --git a/src/app/+collection-page/edit-item-template-page/edit-item-template-page.component.html b/src/app/+collection-page/edit-item-template-page/edit-item-template-page.component.html
new file mode 100644
index 0000000000..5b0f83fa22
--- /dev/null
+++ b/src/app/+collection-page/edit-item-template-page/edit-item-template-page.component.html
@@ -0,0 +1,9 @@
+
+
+
+
{{ 'collection.edit.template.head' | translate:{ collection: collection?.name } }}
+
+
+
+
+
diff --git a/src/app/+collection-page/edit-item-template-page/edit-item-template-page.component.spec.ts b/src/app/+collection-page/edit-item-template-page/edit-item-template-page.component.spec.ts
new file mode 100644
index 0000000000..6d5ffc8768
--- /dev/null
+++ b/src/app/+collection-page/edit-item-template-page/edit-item-template-page.component.spec.ts
@@ -0,0 +1,51 @@
+import { EditItemTemplatePageComponent } from './edit-item-template-page.component';
+import { async, ComponentFixture, TestBed } from '@angular/core/testing';
+import { TranslateModule } from '@ngx-translate/core';
+import { SharedModule } from '../../shared/shared.module';
+import { RouterTestingModule } from '@angular/router/testing';
+import { CommonModule } from '@angular/common';
+import { ItemTemplateDataService } from '../../core/data/item-template-data.service';
+import { ActivatedRoute } from '@angular/router';
+import { of as observableOf } from 'rxjs/internal/observable/of';
+import { Collection } from '../../core/shared/collection.model';
+import { NO_ERRORS_SCHEMA } from '@angular/core';
+import { getCollectionEditPath } from '../collection-page-routing.module';
+import { createSuccessfulRemoteDataObject } from '../../shared/remote-data.utils';
+
+describe('EditItemTemplatePageComponent', () => {
+ let comp: EditItemTemplatePageComponent;
+ let fixture: ComponentFixture;
+ let itemTemplateService: ItemTemplateDataService;
+ let collection: Collection;
+
+ beforeEach(async(() => {
+ collection = Object.assign(new Collection(), {
+ uuid: 'collection-id',
+ id: 'collection-id',
+ name: 'Fake Collection'
+ });
+ TestBed.configureTestingModule({
+ imports: [TranslateModule.forRoot(), SharedModule, CommonModule, RouterTestingModule],
+ declarations: [EditItemTemplatePageComponent],
+ providers: [
+ { provide: ItemTemplateDataService, useValue: {} },
+ { provide: ActivatedRoute, useValue: { parent: { data: observableOf({ dso: createSuccessfulRemoteDataObject(collection) }) } } }
+ ],
+ schemas: [NO_ERRORS_SCHEMA]
+ }).compileComponents();
+ }));
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(EditItemTemplatePageComponent);
+ comp = fixture.componentInstance;
+ itemTemplateService = (comp as any).itemTemplateService;
+ fixture.detectChanges();
+ });
+
+ describe('getCollectionEditUrl', () => {
+ it('should return the collection\'s edit url', () => {
+ const url = comp.getCollectionEditUrl(collection);
+ expect(url).toEqual(getCollectionEditPath(collection.uuid));
+ });
+ });
+});
diff --git a/src/app/+collection-page/edit-item-template-page/edit-item-template-page.component.ts b/src/app/+collection-page/edit-item-template-page/edit-item-template-page.component.ts
new file mode 100644
index 0000000000..329c72d683
--- /dev/null
+++ b/src/app/+collection-page/edit-item-template-page/edit-item-template-page.component.ts
@@ -0,0 +1,44 @@
+import { Component, OnInit } from '@angular/core';
+import { Observable } from 'rxjs/internal/Observable';
+import { RemoteData } from '../../core/data/remote-data';
+import { Collection } from '../../core/shared/collection.model';
+import { ActivatedRoute } from '@angular/router';
+import { first, map } from 'rxjs/operators';
+import { ItemTemplateDataService } from '../../core/data/item-template-data.service';
+import { getCollectionEditPath } from '../collection-page-routing.module';
+
+@Component({
+ selector: 'ds-edit-item-template-page',
+ templateUrl: './edit-item-template-page.component.html',
+})
+/**
+ * Component for editing the item template of a collection
+ */
+export class EditItemTemplatePageComponent implements OnInit {
+
+ /**
+ * The collection to edit the item template for
+ */
+ collectionRD$: Observable>;
+
+ constructor(protected route: ActivatedRoute,
+ public itemTemplateService: ItemTemplateDataService) {
+ }
+
+ ngOnInit(): void {
+ this.collectionRD$ = this.route.parent.data.pipe(first(), map((data) => data.dso));
+ }
+
+ /**
+ * Get the URL to the collection's edit page
+ * @param collection
+ */
+ getCollectionEditUrl(collection: Collection): string {
+ if (collection) {
+ return getCollectionEditPath(collection.uuid);
+ } else {
+ return '';
+ }
+ }
+
+}
diff --git a/src/app/+collection-page/edit-item-template-page/item-template-page.resolver.spec.ts b/src/app/+collection-page/edit-item-template-page/item-template-page.resolver.spec.ts
new file mode 100644
index 0000000000..885adadb3f
--- /dev/null
+++ b/src/app/+collection-page/edit-item-template-page/item-template-page.resolver.spec.ts
@@ -0,0 +1,28 @@
+import { of as observableOf } from 'rxjs/internal/observable/of';
+import { first } from 'rxjs/operators';
+import { ItemTemplatePageResolver } from './item-template-page.resolver';
+
+describe('ItemTemplatePageResolver', () => {
+ describe('resolve', () => {
+ let resolver: ItemTemplatePageResolver;
+ let itemTemplateService: any;
+ const uuid = '1234-65487-12354-1235';
+
+ beforeEach(() => {
+ itemTemplateService = {
+ findByCollectionID: (id: string) => observableOf({ payload: { id }, hasSucceeded: true })
+ };
+ resolver = new ItemTemplatePageResolver(itemTemplateService);
+ });
+
+ it('should resolve an item template with the correct id', () => {
+ resolver.resolve({ params: { id: uuid } } as any, undefined)
+ .pipe(first())
+ .subscribe(
+ (resolved) => {
+ expect(resolved.payload.id).toEqual(uuid);
+ }
+ );
+ });
+ });
+});
diff --git a/src/app/+collection-page/edit-item-template-page/item-template-page.resolver.ts b/src/app/+collection-page/edit-item-template-page/item-template-page.resolver.ts
new file mode 100644
index 0000000000..919e131836
--- /dev/null
+++ b/src/app/+collection-page/edit-item-template-page/item-template-page.resolver.ts
@@ -0,0 +1,31 @@
+import { Injectable } from '@angular/core';
+import { ActivatedRouteSnapshot, Resolve, RouterStateSnapshot } from '@angular/router';
+import { RemoteData } from '../../core/data/remote-data';
+import { Item } from '../../core/shared/item.model';
+import { ItemTemplateDataService } from '../../core/data/item-template-data.service';
+import { Observable } from 'rxjs/internal/Observable';
+import { find } from 'rxjs/operators';
+import { hasValue } from '../../shared/empty.util';
+import { followLink } from '../../shared/utils/follow-link-config.model';
+
+/**
+ * This class represents a resolver that requests a specific collection's item template before the route is activated
+ */
+@Injectable()
+export class ItemTemplatePageResolver implements Resolve> {
+ constructor(private itemTemplateService: ItemTemplateDataService) {
+ }
+
+ /**
+ * Method for resolving a collection's item template based on the parameters in the current route
+ * @param {ActivatedRouteSnapshot} route The current ActivatedRouteSnapshot
+ * @param {RouterStateSnapshot} state The current RouterStateSnapshot
+ * @returns Observable<> Emits the found item template based on the parameters in the current route,
+ * or an error if something went wrong
+ */
+ resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable> {
+ return this.itemTemplateService.findByCollectionID(route.params.id, followLink('templateItemOf')).pipe(
+ find((RD) => hasValue(RD.error) || RD.hasSucceeded),
+ );
+ }
+}
diff --git a/src/app/+item-page/edit-item-page/abstract-item-update/abstract-item-update.component.ts b/src/app/+item-page/edit-item-page/abstract-item-update/abstract-item-update.component.ts
index 503759eeff..6f97ec3057 100644
--- a/src/app/+item-page/edit-item-page/abstract-item-update/abstract-item-update.component.ts
+++ b/src/app/+item-page/edit-item-page/abstract-item-update/abstract-item-update.component.ts
@@ -11,6 +11,7 @@ import { first, map } from 'rxjs/operators';
import { RemoteData } from '../../../core/data/remote-data';
import { AbstractTrackableComponent } from '../../../shared/trackable/abstract-trackable.component';
import { environment } from '../../../../environments/environment';
+import { combineLatest as observableCombineLatest } from 'rxjs';
@Component({
selector: 'ds-abstract-item-update',
@@ -45,11 +46,12 @@ export class AbstractItemUpdateComponent extends AbstractTrackableComponent impl
* Initialize common properties between item-update components
*/
ngOnInit(): void {
- this.route.parent.data.pipe(map((data) => data.item))
- .pipe(
- first(),
- map((data: RemoteData- ) => data.payload)
- ).subscribe((item: Item) => {
+ observableCombineLatest(this.route.data, this.route.parent.data).pipe(
+ map(([data, parentData]) => Object.assign({}, data, parentData)),
+ map((data) => data.item),
+ first(),
+ map((data: RemoteData
- ) => data.payload)
+ ).subscribe((item: Item) => {
this.item = item;
this.postItemInit();
});
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 44cb4099f0..c3bd9eb924 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
@@ -78,6 +78,9 @@ import { ObjectValuesPipe } from '../../shared/utils/object-values-pipe';
providers: [
BundleDataService,
ObjectValuesPipe
+ ],
+ exports: [
+ ItemMetadataComponent
]
})
export class EditItemPageModule {
diff --git a/src/app/+item-page/edit-item-page/item-bitstreams/item-bitstreams.component.spec.ts b/src/app/+item-page/edit-item-page/item-bitstreams/item-bitstreams.component.spec.ts
index 125fc1fb0c..0bdb4be6cb 100644
--- a/src/app/+item-page/edit-item-page/item-bitstreams/item-bitstreams.component.spec.ts
+++ b/src/app/+item-page/edit-item-page/item-bitstreams/item-bitstreams.component.spec.ts
@@ -142,6 +142,7 @@ describe('ItemBitstreamsComponent', () => {
parent: {
data: observableOf({ item: createMockRD(item) })
},
+ data: observableOf({}),
url: url
});
bundleService = jasmine.createSpyObj('bundleService', {
diff --git a/src/app/+item-page/edit-item-page/item-metadata/edit-in-place-field/edit-in-place-field.component.html b/src/app/+item-page/edit-item-page/item-metadata/edit-in-place-field/edit-in-place-field.component.html
index e8ffc28920..80c78941c8 100644
--- a/src/app/+item-page/edit-item-page/item-metadata/edit-in-place-field/edit-in-place-field.component.html
+++ b/src/app/+item-page/edit-item-page/item-metadata/edit-in-place-field/edit-in-place-field.component.html
@@ -67,4 +67,4 @@
-
\ No newline at end of file
+
diff --git a/src/app/+item-page/edit-item-page/item-metadata/edit-in-place-field/edit-in-place-field.component.ts b/src/app/+item-page/edit-item-page/item-metadata/edit-in-place-field/edit-in-place-field.component.ts
index 45eecc6e7e..5a905fc7ea 100644
--- a/src/app/+item-page/edit-item-page/item-metadata/edit-in-place-field/edit-in-place-field.component.ts
+++ b/src/app/+item-page/edit-item-page/item-metadata/edit-in-place-field/edit-in-place-field.component.ts
@@ -58,7 +58,7 @@ export class EditInPlaceFieldComponent implements OnInit, OnChanges {
metadataFieldSuggestions: BehaviorSubject = new BehaviorSubject([]);
constructor(
- private metadataFieldService: RegistryService,
+ private registryService: RegistryService,
private objectUpdatesService: ObjectUpdatesService,
) {
}
@@ -75,7 +75,7 @@ export class EditInPlaceFieldComponent implements OnInit, OnChanges {
* Sends a new change update for this field to the object updates service
*/
update(ngModel?: NgModel) {
- this.objectUpdatesService.saveChangeFieldUpdate(this.url, this.metadata);
+ this.objectUpdatesService.saveChangeFieldUpdate(this.url, cloneDeep(this.metadata));
if (hasValue(ngModel)) {
this.checkValidity(ngModel);
}
@@ -103,7 +103,7 @@ export class EditInPlaceFieldComponent implements OnInit, OnChanges {
* Sends a new remove update for this field to the object updates service
*/
remove() {
- this.objectUpdatesService.saveRemoveFieldUpdate(this.url, this.metadata);
+ this.objectUpdatesService.saveRemoveFieldUpdate(this.url, cloneDeep(this.metadata));
}
/**
@@ -123,11 +123,12 @@ export class EditInPlaceFieldComponent implements OnInit, OnChanges {
/**
* Requests all metadata fields that contain the query string in their key
* Then sets all found metadata fields as metadataFieldSuggestions
+ * Ignores fields from metadata schemas "relation" and "relationship"
* @param query The query to look for
*/
findMetadataFieldSuggestions(query: string): void {
if (isNotEmpty(query)) {
- this.metadataFieldService.queryMetadataFields(query).pipe(
+ this.registryService.queryMetadataFields(query).pipe(
// getSucceededRemoteData(),
take(1),
map((data) => data.payload.page)
diff --git a/src/app/+item-page/edit-item-page/item-metadata/item-metadata.component.html b/src/app/+item-page/edit-item-page/item-metadata/item-metadata.component.html
index 496429a3ba..366f6fffe2 100644
--- a/src/app/+item-page/edit-item-page/item-metadata/item-metadata.component.html
+++ b/src/app/+item-page/edit-item-page/item-metadata/item-metadata.component.html
@@ -1,5 +1,5 @@