Merge branch 'master' into w2p-68346_Bundles-in-edit-item-Updates

Conflicts:
	package.json
	src/app/+item-page/item-page-routing.module.ts
	src/app/core/cache/builders/remote-data-build.service.ts
	src/app/core/cache/server-sync-buffer.effects.ts
	src/app/core/core.module.ts
	src/app/core/data/bitstream-data.service.ts
	src/app/core/data/bundle-data.service.ts
	src/app/core/data/data.service.ts
	src/app/core/data/dso-change-analyzer.service.ts
	src/app/core/data/item-data.service.ts
	src/app/core/data/object-updates/object-updates.actions.ts
	src/app/core/shared/bitstream.model.ts
	src/app/core/shared/dspace-object.model.ts
	src/app/shared/mocks/mock-request.service.ts
	src/app/shared/shared.module.ts
	yarn.lock
This commit is contained in:
Kristof De Langhe
2020-03-05 17:21:38 +01:00
526 changed files with 11384 additions and 9370 deletions

View File

@@ -1,21 +1,28 @@
.git .git
node-modules .idea
__build__ .vscode
__server_build__ .DS_Store
*.iml
# Build folders
node_modules
build
dist
typings typings
tsd_typings tsd_typings
npm-debug.log
dist
coverage coverage
.idea __build__
*.iml __server_build__
# Node
*.log
npm-debug.log.*
# Angular files
*.ngfactory.ts *.ngfactory.ts
*.css.shim.ts *.css.shim.ts
*.scss.shim.ts *.scss.shim.ts
.DS_Store
# Webpack files
webpack.records.json webpack.records.json
npm-debug.log.*
morgan.log
yarn-error.log
*.css
package-lock.json package-lock.json

View File

@@ -44,8 +44,8 @@ after_script:
language: node_js language: node_js
node_js: node_js:
- "8"
- "10" - "10"
- "12"
cache: cache:
yarn: true yarn: true

View File

@@ -1,10 +1,12 @@
# This image will be published as dspace/dspace-angular # This image will be published as dspace/dspace-angular
# See https://dspace-labs.github.io/DSpace-Docker-Images/ for usage details # See https://dspace-labs.github.io/DSpace-Docker-Images/ for usage details
FROM node:8-alpine FROM node:12-alpine
WORKDIR /app WORKDIR /app
ADD . /app/ ADD . /app/
EXPOSE 3000 EXPOSE 3000
RUN yarn install # We run yarn install with an increased network timeout (5min) to avoid "ESOCKETTIMEDOUT" errors from hub.docker.com
# See, for example https://github.com/yarnpkg/yarn/issues/5540
RUN yarn install --network-timeout 300000
CMD yarn run watch CMD yarn run watch

View File

@@ -13,7 +13,7 @@ You can find additional information on the DSpace 7 Angular UI on the [wiki](htt
Quick start Quick start
----------- -----------
**Ensure you're running [Node](https://nodejs.org) `v8.0.x` or `v10.0.x`, [npm](https://www.npmjs.com/) >= `v5.x` and [yarn](https://yarnpkg.com) >= `v1.x`** **Ensure you're running [Node](https://nodejs.org) `v10.x` or `v12.x`, [npm](https://www.npmjs.com/) >= `v5.x` and [yarn](https://yarnpkg.com) >= `v1.x`**
```bash ```bash
# clone the repo # clone the repo
@@ -65,7 +65,7 @@ Requirements
------------ ------------
- [Node.js](https://nodejs.org), [npm](https://www.npmjs.com/), and [yarn](https://yarnpkg.com) - [Node.js](https://nodejs.org), [npm](https://www.npmjs.com/), and [yarn](https://yarnpkg.com)
- Ensure you're running node `v8.x` or `v10.x`, npm >= `v5.x` and yarn >= `v1.x` - Ensure you're running node `v10.x` or `v12.x`, npm >= `v5.x` and yarn >= `v1.x`
If you have [`nvm`](https://github.com/creationix/nvm#install-script) or [`nvm-windows`](https://github.com/coreybutler/nvm-windows) installed, which is highly recommended, you can run `nvm install --lts && nvm use` to install and start using the latest Node LTS. If you have [`nvm`](https://github.com/creationix/nvm#install-script) or [`nvm-windows`](https://github.com/coreybutler/nvm-windows) installed, which is highly recommended, you can run `nvm install --lts && nvm use` to install and start using the latest Node LTS.

View File

@@ -140,7 +140,7 @@ module.exports = {
}, { }, {
code: 'nl', code: 'nl',
label: 'Nederlands', label: 'Nederlands',
active: false, active: true,
}, { }, {
code: 'pt', code: 'pt',
label: 'Português', label: 'Português',

View File

@@ -5,7 +5,7 @@ services:
container_name: dspace container_name: dspace
depends_on: depends_on:
- dspacedb - dspacedb
image: dspace/dspace:dspace-7_x-jdk8-test image: dspace/dspace:dspace-7_x-test
networks: networks:
dspacenet: dspacenet:
ports: ports:

View File

@@ -5,7 +5,7 @@ services:
container_name: dspace container_name: dspace
depends_on: depends_on:
- dspacedb - dspacedb
image: dspace/dspace:dspace-7_x-jdk8-test image: dspace/dspace:dspace-7_x-test
networks: networks:
dspacenet: dspacenet:
ports: ports:

View File

@@ -1,6 +1,5 @@
dspace.dir=/dspace dspace.dir=/dspace
db.url=jdbc:postgresql://dspacedb:5432/dspace db.url=jdbc:postgresql://dspacedb:5432/dspace
dspace.hostname=dspace dspace.server.url=http://localhost:8080/server
dspace.baseUrl=http://localhost:8080/server
dspace.name=DSpace Started with Docker Compose dspace.name=DSpace Started with Docker Compose
solr.server=http://dspacesolr:8983/solr solr.server=http://dspacesolr:8983/solr

View File

@@ -1,4 +1,4 @@
import { browser, element, by, protractor } from 'protractor'; import { browser, by, element, protractor } from 'protractor';
import { promise } from 'selenium-webdriver'; import { promise } from 'selenium-webdriver';
export class ProtractorPage { export class ProtractorPage {
@@ -32,9 +32,4 @@ export class ProtractorPage {
submitByPressingEnter() { submitByPressingEnter() {
element(by.css('#search-navbar-container form input[name="query"]')).sendKeys(protractor.Key.ENTER); element(by.css('#search-navbar-container form input[name="query"]')).sendKeys(protractor.Key.ENTER);
} }
submitByPressingEnter() {
element(by.css('#search-navbar-container form input[name="query"]')).sendKeys(protractor.Key.ENTER);
}
} }

View File

@@ -8,7 +8,7 @@
}, },
"license": "BSD-2-Clause", "license": "BSD-2-Clause",
"engines": { "engines": {
"node": "8.* || >= 10.*" "node": "10.* || >= 12.*"
}, },
"resolutions": { "resolutions": {
"serialize-javascript": ">= 2.1.2", "serialize-javascript": ">= 2.1.2",
@@ -75,30 +75,28 @@
"sync-i18n": "node ./scripts/sync-i18n-files.js" "sync-i18n": "node ./scripts/sync-i18n-files.js"
}, },
"dependencies": { "dependencies": {
"@angular/animations": "^7.2.15", "@angular/animations": "^8.2.14",
"@angular/cdk": "^7.3.7", "@angular/cdk": "8.2.3",
"@angular/cli": "^7.3.5", "@angular/cli": "^8.3.25",
"@angular/common": "^7.2.15", "@angular/common": "^8.2.14",
"@angular/core": "^7.2.15", "@angular/core": "^8.2.14",
"@angular/forms": "^7.2.15", "@angular/forms": "^8.2.14",
"@angular/http": "^7.2.15", "@angular/platform-browser": "^8.2.14",
"@angular/platform-browser": "^7.2.15", "@angular/platform-browser-dynamic": "^8.2.14",
"@angular/platform-browser-dynamic": "^7.2.15", "@angular/platform-server": "^8.2.14",
"@angular/platform-server": "^7.2.15", "@angular/router": "^8.2.14",
"@angular/router": "^7.2.15",
"@angularclass/bootloader": "1.0.1", "@angularclass/bootloader": "1.0.1",
"@ng-bootstrap/ng-bootstrap": "^4.1.0", "@ng-bootstrap/ng-bootstrap": "^5.2.1",
"@ng-dynamic-forms/core": "^7.1.0", "@ng-dynamic-forms/core": "8.1.1",
"@ng-dynamic-forms/ui-ng-bootstrap": "^7.1.0", "@ng-dynamic-forms/ui-ng-bootstrap": "8.1.1",
"@ngrx/effects": "^7.3.0", "@ngrx/effects": "^8.6.0",
"@ngrx/router-store": "^7.3.0", "@ngrx/router-store": "^8.6.0",
"@ngrx/store": "^7.3.0", "@ngrx/store": "^8.6.0",
"@nguniversal/express-engine": "^7.1.1", "@nguniversal/express-engine": "^8.2.6",
"@ngx-translate/core": "11.0.1", "@ngx-translate/core": "11.0.1",
"@ngx-translate/http-loader": "4.0.0", "@ngx-translate/http-loader": "4.0.0",
"@nicky-lenaers/ngx-scroll-to": "^1.0.0", "@nicky-lenaers/ngx-scroll-to": "^3.0.1",
"angular-idle-preload": "3.0.0", "angular-idle-preload": "3.0.0",
"angular-sortablejs": "^2.5.0",
"angular2-text-mask": "9.0.0", "angular2-text-mask": "9.0.0",
"angulartics2": "7.5.2", "angulartics2": "7.5.2",
"body-parser": "1.18.2", "body-parser": "1.18.2",
@@ -106,7 +104,7 @@
"cerialize": "0.1.18", "cerialize": "0.1.18",
"compression": "1.7.1", "compression": "1.7.1",
"cookie-parser": "1.4.3", "cookie-parser": "1.4.3",
"core-js": "^2.6.5", "core-js": "^3.6.4",
"debug-loader": "^0.0.1", "debug-loader": "^0.0.1",
"express": "4.16.2", "express": "4.16.2",
"express-session": "1.15.6", "express-session": "1.15.6",
@@ -126,17 +124,18 @@
"moment": "^2.22.1", "moment": "^2.22.1",
"moment-range": "^4.0.2", "moment-range": "^4.0.2",
"morgan": "^1.9.1", "morgan": "^1.9.1",
"ng-mocks": "^7.6.0", "ng-mocks": "^8.1.0",
"ng2-file-upload": "1.2.1", "ng2-file-upload": "1.2.1",
"ng2-nouislider": "^1.8.2", "ng2-nouislider": "^1.8.2",
"ngx-bootstrap": "^3.2.0", "ngx-bootstrap": "^5.3.2",
"ngx-infinite-scroll": "6.0.1", "ngx-infinite-scroll": "6.0.1",
"ngx-moment": "^3.4.0", "ngx-moment": "^3.4.0",
"ngx-pagination": "3.0.3", "ngx-pagination": "3.0.3",
"ngx-sortablejs": "^3.1.4",
"nouislider": "^11.0.0", "nouislider": "^11.0.0",
"pem": "1.13.2", "pem": "1.13.2",
"reflect-metadata": "0.1.12", "reflect-metadata": "^0.1.13",
"rxjs": "6.4.0", "rxjs": "6.5.4",
"rxjs-spy": "^7.5.1", "rxjs-spy": "^7.5.1",
"sass-resources-loader": "^2.0.0", "sass-resources-loader": "^2.0.0",
"sortablejs": "1.7.0", "sortablejs": "1.7.0",
@@ -147,17 +146,17 @@
"uuid": "^3.2.1", "uuid": "^3.2.1",
"webfontloader": "1.6.28", "webfontloader": "1.6.28",
"webpack-cli": "^3.2.0", "webpack-cli": "^3.2.0",
"zone.js": "^0.8.29" "zone.js": "^0.9.1"
}, },
"devDependencies": { "devDependencies": {
"@angular-devkit/build-angular": "^0.13.5", "@angular-devkit/build-angular": "^0.803.25",
"@angular/compiler": "^7.2.15", "@angular/compiler": "^8.2.14",
"@angular/compiler-cli": "^7.2.15", "@angular/compiler-cli": "^8.2.14",
"@fortawesome/fontawesome-free": "^5.5.0", "@fortawesome/fontawesome-free": "^5.5.0",
"@ngrx/entity": "^7.3.0", "@ngrx/entity": "^8.6.0",
"@ngrx/schematics": "^7.3.0", "@ngrx/schematics": "^8.6.0",
"@ngrx/store-devtools": "^7.3.0", "@ngrx/store-devtools": "^8.6.0",
"@ngtools/webpack": "^7.3.9", "@ngtools/webpack": "^8.3.25",
"@schematics/angular": "^0.7.5", "@schematics/angular": "^0.7.5",
"@types/acorn": "^4.0.3", "@types/acorn": "^4.0.3",
"@types/cookie-parser": "1.4.1", "@types/cookie-parser": "1.4.1",
@@ -215,8 +214,6 @@
"karma-webdriver-launcher": "^1.0.7", "karma-webdriver-launcher": "^1.0.7",
"karma-webpack": "3.0.0", "karma-webpack": "3.0.0",
"ncp": "^2.0.0", "ncp": "^2.0.0",
"ngrx-store-freeze": "^0.2.4",
"node-sass": "^4.11.0",
"nodemon": "^1.15.0", "nodemon": "^1.15.0",
"npm-run-all": "4.1.3", "npm-run-all": "4.1.3",
"optimize-css-assets-webpack-plugin": "^5.0.1", "optimize-css-assets-webpack-plugin": "^5.0.1",
@@ -247,7 +244,7 @@
"ts-node": "4.1.0", "ts-node": "4.1.0",
"tslint": "5.11.0", "tslint": "5.11.0",
"typedoc": "^0.9.0", "typedoc": "^0.9.0",
"typescript": "3.1.6", "typescript": "3.5.3",
"webdriver-manager": "^12.1.7", "webdriver-manager": "^12.1.7",
"webpack": "^4.29.6", "webpack": "^4.29.6",
"webpack-bundle-analyzer": "^3.3.2", "webpack-bundle-analyzer": "^3.3.2",

View File

@@ -232,6 +232,14 @@
"browse.metadata.title": "Title", "browse.metadata.title": "Title",
"browse.metadata.author.breadcrumbs": "Browse by Author",
"browse.metadata.dateissued.breadcrumbs": "Browse by Date",
"browse.metadata.subject.breadcrumbs": "Browse by Subject",
"browse.metadata.title.breadcrumbs": "Browse by Title",
"browse.startsWith.choose_start": "(Choose start)", "browse.startsWith.choose_start": "(Choose start)",
"browse.startsWith.choose_year": "(Choose year)", "browse.startsWith.choose_year": "(Choose year)",
@@ -273,7 +281,6 @@
"browse.title": "Browsing {{ collection }} by {{ field }} {{ value }}", "browse.title": "Browsing {{ collection }} by {{ field }} {{ value }}",
"chips.remove": "Remove chip", "chips.remove": "Remove chip",
@@ -302,6 +309,8 @@
"collection.edit.head": "Edit Collection", "collection.edit.head": "Edit Collection",
"collection.edit.breadcrumbs": "Edit Collection",
"collection.edit.item-mapper.cancel": "Cancel", "collection.edit.item-mapper.cancel": "Cancel",
@@ -486,6 +495,7 @@
"community.edit.head": "Edit Community", "community.edit.head": "Edit Community",
"community.edit.breadcrumbs": "Edit Community",
"community.edit.logo.label": "Community logo", "community.edit.logo.label": "Community logo",
@@ -772,6 +782,8 @@
"item.edit.head": "Edit Item", "item.edit.head": "Edit Item",
"item.edit.breadcrumbs": "Edit Item",
"item.edit.item-mapper.buttons.add": "Map item to selected collections", "item.edit.item-mapper.buttons.add": "Map item to selected collections",
@@ -1196,6 +1208,8 @@
"login.title": "Login", "login.title": "Login",
"login.breadcrumbs": "Login",
"logout.form.header": "Log out from DSpace", "logout.form.header": "Log out from DSpace",
@@ -1592,6 +1606,7 @@
"search.title": "DSpace Angular :: Search", "search.title": "DSpace Angular :: Search",
"search.breadcrumbs": "Search",
"search.filters.applied.f.author": "Author", "search.filters.applied.f.author": "Author",

File diff suppressed because it is too large Load Diff

View File

@@ -13,8 +13,8 @@
*/ */
Error.stackTraceLimit = Infinity; Error.stackTraceLimit = Infinity;
require('core-js/es6'); require('core-js/es');
require('core-js/es7/reflect'); require('core-js/features/reflect');
// Typescript emit helpers polyfill // Typescript emit helpers polyfill
require('ts-helpers'); require('ts-helpers');

View File

@@ -1,19 +1,18 @@
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { CommonModule } from '@angular/common'; import { CommonModule } from '@angular/common';
import { RouterTestingModule } from '@angular/router/testing';
import { TranslateModule } from '@ngx-translate/core';
import { NgbModule } from '@ng-bootstrap/ng-bootstrap';
import { Router } from '@angular/router';
import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core'; import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
import { RouterStub } from '../../../../shared/testing/router-stub'; import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { Router } from '@angular/router';
import { RouterTestingModule } from '@angular/router/testing';
import { NgbModule } from '@ng-bootstrap/ng-bootstrap';
import { TranslateModule } from '@ngx-translate/core';
import { of as observableOf } from 'rxjs'; import { of as observableOf } from 'rxjs';
import { RestResponse } from '../../../../core/cache/response.models';
import { BitstreamFormatDataService } from '../../../../core/data/bitstream-format-data.service';
import { BitstreamFormatSupportLevel } from '../../../../core/shared/bitstream-format-support-level';
import { BitstreamFormat } from '../../../../core/shared/bitstream-format.model';
import { NotificationsService } from '../../../../shared/notifications/notifications.service'; import { NotificationsService } from '../../../../shared/notifications/notifications.service';
import { NotificationsServiceStub } from '../../../../shared/testing/notifications-service-stub'; import { NotificationsServiceStub } from '../../../../shared/testing/notifications-service-stub';
import { BitstreamFormatDataService } from '../../../../core/data/bitstream-format-data.service'; import { RouterStub } from '../../../../shared/testing/router-stub';
import { RestResponse } from '../../../../core/cache/response.models';
import { BitstreamFormat } from '../../../../core/shared/bitstream-format.model';
import { BitstreamFormatSupportLevel } from '../../../../core/shared/bitstream-format-support-level';
import { ResourceType } from '../../../../core/shared/resource-type';
import { AddBitstreamFormatComponent } from './add-bitstream-format.component'; import { AddBitstreamFormatComponent } from './add-bitstream-format.component';
describe('AddBitstreamFormatComponent', () => { describe('AddBitstreamFormatComponent', () => {
@@ -43,7 +42,7 @@ describe('AddBitstreamFormatComponent', () => {
}); });
TestBed.configureTestingModule({ TestBed.configureTestingModule({
imports: [CommonModule, RouterTestingModule.withRoutes([]), TranslateModule.forRoot(), NgbModule.forRoot()], imports: [CommonModule, RouterTestingModule.withRoutes([]), TranslateModule.forRoot(), NgbModule],
declarations: [AddBitstreamFormatComponent], declarations: [AddBitstreamFormatComponent],
providers: [ providers: [
{provide: Router, useValue: router}, {provide: Router, useValue: router},
@@ -83,7 +82,7 @@ describe('AddBitstreamFormatComponent', () => {
}); });
TestBed.configureTestingModule({ TestBed.configureTestingModule({
imports: [CommonModule, RouterTestingModule.withRoutes([]), TranslateModule.forRoot(), NgbModule.forRoot()], imports: [CommonModule, RouterTestingModule.withRoutes([]), TranslateModule.forRoot(), NgbModule],
declarations: [AddBitstreamFormatComponent], declarations: [AddBitstreamFormatComponent],
providers: [ providers: [
{provide: Router, useValue: router}, {provide: Router, useValue: router},

View File

@@ -92,7 +92,7 @@ describe('BitstreamFormatsComponent', () => {
}); });
TestBed.configureTestingModule({ TestBed.configureTestingModule({
imports: [CommonModule, RouterTestingModule.withRoutes([]), TranslateModule.forRoot(), NgbModule.forRoot()], imports: [CommonModule, RouterTestingModule.withRoutes([]), TranslateModule.forRoot(), NgbModule],
declarations: [BitstreamFormatsComponent, PaginationComponent, EnumKeysPipe], declarations: [BitstreamFormatsComponent, PaginationComponent, EnumKeysPipe],
providers: [ providers: [
{provide: BitstreamFormatDataService, useValue: bitstreamFormatService}, {provide: BitstreamFormatDataService, useValue: bitstreamFormatService},
@@ -214,7 +214,7 @@ describe('BitstreamFormatsComponent', () => {
}); });
TestBed.configureTestingModule({ TestBed.configureTestingModule({
imports: [CommonModule, RouterTestingModule.withRoutes([]), TranslateModule.forRoot(), NgbModule.forRoot()], imports: [CommonModule, RouterTestingModule.withRoutes([]), TranslateModule.forRoot(), NgbModule],
declarations: [BitstreamFormatsComponent, PaginationComponent, EnumKeysPipe], declarations: [BitstreamFormatsComponent, PaginationComponent, EnumKeysPipe],
providers: [ providers: [
{provide: BitstreamFormatDataService, useValue: bitstreamFormatService}, {provide: BitstreamFormatDataService, useValue: bitstreamFormatService},
@@ -230,10 +230,10 @@ describe('BitstreamFormatsComponent', () => {
comp.deleteFormats(); comp.deleteFormats();
expect(bitstreamFormatService.clearBitStreamFormatRequests).toHaveBeenCalled(); expect(bitstreamFormatService.clearBitStreamFormatRequests).toHaveBeenCalled();
expect(bitstreamFormatService.delete).toHaveBeenCalledWith(bitstreamFormat1); expect(bitstreamFormatService.delete).toHaveBeenCalledWith(bitstreamFormat1.id);
expect(bitstreamFormatService.delete).toHaveBeenCalledWith(bitstreamFormat2); expect(bitstreamFormatService.delete).toHaveBeenCalledWith(bitstreamFormat2.id);
expect(bitstreamFormatService.delete).toHaveBeenCalledWith(bitstreamFormat3); expect(bitstreamFormatService.delete).toHaveBeenCalledWith(bitstreamFormat3.id);
expect(bitstreamFormatService.delete).toHaveBeenCalledWith(bitstreamFormat4); expect(bitstreamFormatService.delete).toHaveBeenCalledWith(bitstreamFormat4.id);
expect(notificationsServiceStub.success).toHaveBeenCalledWith('admin.registries.bitstream-formats.delete.success.head', expect(notificationsServiceStub.success).toHaveBeenCalledWith('admin.registries.bitstream-formats.delete.success.head',
'admin.registries.bitstream-formats.delete.success.amount'); 'admin.registries.bitstream-formats.delete.success.amount');
@@ -260,7 +260,7 @@ describe('BitstreamFormatsComponent', () => {
}); });
TestBed.configureTestingModule({ TestBed.configureTestingModule({
imports: [CommonModule, RouterTestingModule.withRoutes([]), TranslateModule.forRoot(), NgbModule.forRoot()], imports: [CommonModule, RouterTestingModule.withRoutes([]), TranslateModule.forRoot(), NgbModule],
declarations: [BitstreamFormatsComponent, PaginationComponent, EnumKeysPipe], declarations: [BitstreamFormatsComponent, PaginationComponent, EnumKeysPipe],
providers: [ providers: [
{provide: BitstreamFormatDataService, useValue: bitstreamFormatService}, {provide: BitstreamFormatDataService, useValue: bitstreamFormatService},
@@ -276,10 +276,10 @@ describe('BitstreamFormatsComponent', () => {
comp.deleteFormats(); comp.deleteFormats();
expect(bitstreamFormatService.clearBitStreamFormatRequests).toHaveBeenCalled(); expect(bitstreamFormatService.clearBitStreamFormatRequests).toHaveBeenCalled();
expect(bitstreamFormatService.delete).toHaveBeenCalledWith(bitstreamFormat1); expect(bitstreamFormatService.delete).toHaveBeenCalledWith(bitstreamFormat1.id);
expect(bitstreamFormatService.delete).toHaveBeenCalledWith(bitstreamFormat2); expect(bitstreamFormatService.delete).toHaveBeenCalledWith(bitstreamFormat2.id);
expect(bitstreamFormatService.delete).toHaveBeenCalledWith(bitstreamFormat3); expect(bitstreamFormatService.delete).toHaveBeenCalledWith(bitstreamFormat3.id);
expect(bitstreamFormatService.delete).toHaveBeenCalledWith(bitstreamFormat4); expect(bitstreamFormatService.delete).toHaveBeenCalledWith(bitstreamFormat4.id);
expect(notificationsServiceStub.error).toHaveBeenCalledWith('admin.registries.bitstream-formats.delete.failure.head', expect(notificationsServiceStub.error).toHaveBeenCalledWith('admin.registries.bitstream-formats.delete.failure.head',
'admin.registries.bitstream-formats.delete.failure.amount'); 'admin.registries.bitstream-formats.delete.failure.amount');

View File

@@ -64,7 +64,7 @@ export class BitstreamFormatsComponent implements OnInit {
const tasks$ = []; const tasks$ = [];
for (const format of formats) { for (const format of formats) {
if (hasValue(format.id)) { if (hasValue(format.id)) {
tasks$.push(this.bitstreamFormatService.delete(format)); tasks$.push(this.bitstreamFormatService.delete(format.id));
} }
} }
zip(...tasks$).subscribe((results: boolean[]) => { zip(...tasks$).subscribe((results: boolean[]) => {

View File

@@ -1,21 +1,20 @@
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { CommonModule } from '@angular/common'; import { CommonModule } from '@angular/common';
import { RouterTestingModule } from '@angular/router/testing';
import { TranslateModule } from '@ngx-translate/core';
import { NgbModule } from '@ng-bootstrap/ng-bootstrap';
import { ActivatedRoute, Router } from '@angular/router';
import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core'; import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
import { RouterStub } from '../../../../shared/testing/router-stub'; import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { ActivatedRoute, Router } from '@angular/router';
import { RouterTestingModule } from '@angular/router/testing';
import { NgbModule } from '@ng-bootstrap/ng-bootstrap';
import { TranslateModule } from '@ngx-translate/core';
import { of as observableOf } from 'rxjs'; import { of as observableOf } from 'rxjs';
import { RestResponse } from '../../../../core/cache/response.models';
import { BitstreamFormatDataService } from '../../../../core/data/bitstream-format-data.service';
import { RemoteData } from '../../../../core/data/remote-data'; import { RemoteData } from '../../../../core/data/remote-data';
import { EditBitstreamFormatComponent } from './edit-bitstream-format.component'; import { BitstreamFormatSupportLevel } from '../../../../core/shared/bitstream-format-support-level';
import { BitstreamFormat } from '../../../../core/shared/bitstream-format.model';
import { NotificationsService } from '../../../../shared/notifications/notifications.service'; import { NotificationsService } from '../../../../shared/notifications/notifications.service';
import { NotificationsServiceStub } from '../../../../shared/testing/notifications-service-stub'; import { NotificationsServiceStub } from '../../../../shared/testing/notifications-service-stub';
import { BitstreamFormatDataService } from '../../../../core/data/bitstream-format-data.service'; import { RouterStub } from '../../../../shared/testing/router-stub';
import { RestResponse } from '../../../../core/cache/response.models'; import { EditBitstreamFormatComponent } from './edit-bitstream-format.component';
import { BitstreamFormat } from '../../../../core/shared/bitstream-format.model';
import { BitstreamFormatSupportLevel } from '../../../../core/shared/bitstream-format-support-level';
import { ResourceType } from '../../../../core/shared/resource-type';
describe('EditBitstreamFormatComponent', () => { describe('EditBitstreamFormatComponent', () => {
let comp: EditBitstreamFormatComponent; let comp: EditBitstreamFormatComponent;
@@ -49,7 +48,7 @@ describe('EditBitstreamFormatComponent', () => {
}); });
TestBed.configureTestingModule({ TestBed.configureTestingModule({
imports: [CommonModule, RouterTestingModule.withRoutes([]), TranslateModule.forRoot(), NgbModule.forRoot()], imports: [CommonModule, RouterTestingModule.withRoutes([]), TranslateModule.forRoot(), NgbModule],
declarations: [EditBitstreamFormatComponent], declarations: [EditBitstreamFormatComponent],
providers: [ providers: [
{provide: ActivatedRoute, useValue: routeStub}, {provide: ActivatedRoute, useValue: routeStub},
@@ -99,7 +98,7 @@ describe('EditBitstreamFormatComponent', () => {
}); });
TestBed.configureTestingModule({ TestBed.configureTestingModule({
imports: [CommonModule, RouterTestingModule.withRoutes([]), TranslateModule.forRoot(), NgbModule.forRoot()], imports: [CommonModule, RouterTestingModule.withRoutes([]), TranslateModule.forRoot(), NgbModule],
declarations: [EditBitstreamFormatComponent], declarations: [EditBitstreamFormatComponent],
providers: [ providers: [
{provide: ActivatedRoute, useValue: routeStub}, {provide: ActivatedRoute, useValue: routeStub},

View File

@@ -40,7 +40,7 @@ describe('FormatFormComponent', () => {
const initAsync = () => { const initAsync = () => {
TestBed.configureTestingModule({ TestBed.configureTestingModule({
imports: [CommonModule, RouterTestingModule.withRoutes([]), ReactiveFormsModule, FormsModule, TranslateModule.forRoot(), NgbModule.forRoot()], imports: [CommonModule, RouterTestingModule.withRoutes([]), ReactiveFormsModule, FormsModule, TranslateModule.forRoot(), NgbModule],
declarations: [FormatFormComponent], declarations: [FormatFormComponent],
providers: [ providers: [
{provide: Router, useValue: router}, {provide: Router, useValue: router},

View File

@@ -148,4 +148,6 @@ export type MetadataRegistryAction
| MetadataRegistryEditFieldAction | MetadataRegistryEditFieldAction
| MetadataRegistryCancelFieldAction | MetadataRegistryCancelFieldAction
| MetadataRegistrySelectFieldAction | MetadataRegistrySelectFieldAction
| MetadataRegistryDeselectFieldAction; | MetadataRegistryDeselectFieldAction
| MetadataRegistryDeselectAllSchemaAction
| MetadataRegistryDeselectAllFieldAction;

View File

@@ -26,13 +26,21 @@ describe('MetadataRegistryComponent', () => {
const mockSchemasList = [ const mockSchemasList = [
{ {
id: 1, id: 1,
self: 'https://dspace7.4science.it/dspace-spring-rest/api/core/metadataschemas/1', _links: {
self: {
href: 'https://dspace7.4science.it/dspace-spring-rest/api/core/metadataschemas/1'
},
},
prefix: 'dc', prefix: 'dc',
namespace: 'http://dublincore.org/documents/dcmi-terms/' namespace: 'http://dublincore.org/documents/dcmi-terms/'
}, },
{ {
id: 2, id: 2,
self: 'https://dspace7.4science.it/dspace-spring-rest/api/core/metadataschemas/2', _links: {
self: {
href: 'https://dspace7.4science.it/dspace-spring-rest/api/core/metadataschemas/2'
},
},
prefix: 'mock', prefix: 'mock',
namespace: 'http://dspace.org/mockschema' namespace: 'http://dspace.org/mockschema'
} }
@@ -53,7 +61,7 @@ describe('MetadataRegistryComponent', () => {
beforeEach(async(() => { beforeEach(async(() => {
TestBed.configureTestingModule({ TestBed.configureTestingModule({
imports: [CommonModule, RouterTestingModule.withRoutes([]), TranslateModule.forRoot(), NgbModule.forRoot()], imports: [CommonModule, RouterTestingModule.withRoutes([]), TranslateModule.forRoot(), NgbModule],
declarations: [MetadataRegistryComponent, PaginationComponent, EnumKeysPipe], declarations: [MetadataRegistryComponent, PaginationComponent, EnumKeysPipe],
providers: [ providers: [
{ provide: RegistryService, useValue: registryServiceStub }, { provide: RegistryService, useValue: registryServiceStub },

View File

@@ -20,7 +20,11 @@ class NullAction extends MetadataRegistryEditSchemaAction {
const schema: MetadataSchema = Object.assign(new MetadataSchema(), const schema: MetadataSchema = Object.assign(new MetadataSchema(),
{ {
id: 'schema-id', id: 'schema-id',
self: 'http://rest.self/schema/dc', _links: {
self: {
href: 'http://rest.self/schema/dc'
},
},
prefix: 'dc', prefix: 'dc',
namespace: 'http://dublincore.org/documents/dcmi-terms/' namespace: 'http://dublincore.org/documents/dcmi-terms/'
}); });
@@ -28,7 +32,11 @@ const schema: MetadataSchema = Object.assign(new MetadataSchema(),
const schema2: MetadataSchema = Object.assign(new MetadataSchema(), const schema2: MetadataSchema = Object.assign(new MetadataSchema(),
{ {
id: 'another-schema-id', id: 'another-schema-id',
self: 'http://rest.self/schema/dcterms', _links: {
self: {
href: 'http://rest.self/schema/dcterms',
},
},
prefix: 'dcterms', prefix: 'dcterms',
namespace: 'http://purl.org/dc/terms/' namespace: 'http://purl.org/dc/terms/'
}); });
@@ -36,7 +44,11 @@ const schema2: MetadataSchema = Object.assign(new MetadataSchema(),
const field: MetadataField = Object.assign(new MetadataField(), const field: MetadataField = Object.assign(new MetadataField(),
{ {
id: 'author-field-id', id: 'author-field-id',
self: 'http://rest.self/field/author', _links: {
self: {
href: 'http://rest.self/field/author',
},
},
element: 'contributor', element: 'contributor',
qualifier: 'author', qualifier: 'author',
scopeNote: 'Author of an item', scopeNote: 'Author of an item',
@@ -46,7 +58,11 @@ const field: MetadataField = Object.assign(new MetadataField(),
const field2: MetadataField = Object.assign(new MetadataField(), const field2: MetadataField = Object.assign(new MetadataField(),
{ {
id: 'title-field-id', id: 'title-field-id',
self: 'http://rest.self/field/title', _links: {
self: {
href: 'http://rest.self/field/title',
},
},
element: 'title', element: 'title',
qualifier: null, qualifier: null,
scopeNote: 'Title of an item', scopeNote: 'Title of an item',

View File

@@ -34,7 +34,7 @@ describe('MetadataSchemaFormComponent', () => {
beforeEach(async(() => { beforeEach(async(() => {
TestBed.configureTestingModule({ TestBed.configureTestingModule({
imports: [CommonModule, RouterTestingModule.withRoutes([]), TranslateModule.forRoot(), NgbModule.forRoot()], imports: [CommonModule, RouterTestingModule.withRoutes([]), TranslateModule.forRoot(), NgbModule],
declarations: [ MetadataSchemaFormComponent, EnumKeysPipe ], declarations: [ MetadataSchemaFormComponent, EnumKeysPipe ],
providers: [ providers: [
{ provide: RegistryService, useValue: registryServiceStub }, { provide: RegistryService, useValue: registryServiceStub },

View File

@@ -42,7 +42,7 @@ describe('MetadataFieldFormComponent', () => {
beforeEach(async(() => { beforeEach(async(() => {
TestBed.configureTestingModule({ TestBed.configureTestingModule({
imports: [CommonModule, RouterTestingModule.withRoutes([]), TranslateModule.forRoot(), NgbModule.forRoot()], imports: [CommonModule, RouterTestingModule.withRoutes([]), TranslateModule.forRoot(), NgbModule],
declarations: [ MetadataFieldFormComponent, EnumKeysPipe ], declarations: [ MetadataFieldFormComponent, EnumKeysPipe ],
providers: [ providers: [
{ provide: RegistryService, useValue: registryServiceStub }, { provide: RegistryService, useValue: registryServiceStub },

View File

@@ -30,13 +30,21 @@ describe('MetadataSchemaComponent', () => {
const mockSchemasList = [ const mockSchemasList = [
{ {
id: 1, id: 1,
self: 'https://dspace7.4science.it/dspace-spring-rest/api/core/metadataschemas/1', _links: {
self: {
href: 'https://dspace7.4science.it/dspace-spring-rest/api/core/metadataschemas/1',
},
},
prefix: 'dc', prefix: 'dc',
namespace: 'http://dublincore.org/documents/dcmi-terms/' namespace: 'http://dublincore.org/documents/dcmi-terms/'
}, },
{ {
id: 2, id: 2,
self: 'https://dspace7.4science.it/dspace-spring-rest/api/core/metadataschemas/2', _links: {
self: {
href: 'https://dspace7.4science.it/dspace-spring-rest/api/core/metadataschemas/2',
},
},
prefix: 'mock', prefix: 'mock',
namespace: 'http://dspace.org/mockschema' namespace: 'http://dspace.org/mockschema'
} }
@@ -44,7 +52,11 @@ describe('MetadataSchemaComponent', () => {
const mockFieldsList = [ const mockFieldsList = [
{ {
id: 1, id: 1,
self: 'https://dspace7.4science.it/dspace-spring-rest/api/core/metadatafields/8', _links: {
self: {
href: 'https://dspace7.4science.it/dspace-spring-rest/api/core/metadatafields/8',
},
},
element: 'contributor', element: 'contributor',
qualifier: 'advisor', qualifier: 'advisor',
scopeNote: null, scopeNote: null,
@@ -52,7 +64,11 @@ describe('MetadataSchemaComponent', () => {
}, },
{ {
id: 2, id: 2,
self: 'https://dspace7.4science.it/dspace-spring-rest/api/core/metadatafields/9', _links: {
self: {
href: 'https://dspace7.4science.it/dspace-spring-rest/api/core/metadatafields/9',
},
},
element: 'contributor', element: 'contributor',
qualifier: 'author', qualifier: 'author',
scopeNote: null, scopeNote: null,
@@ -60,7 +76,11 @@ describe('MetadataSchemaComponent', () => {
}, },
{ {
id: 3, id: 3,
self: 'https://dspace7.4science.it/dspace-spring-rest/api/core/metadatafields/10', _links: {
self: {
href: 'https://dspace7.4science.it/dspace-spring-rest/api/core/metadatafields/10',
},
},
element: 'contributor', element: 'contributor',
qualifier: 'editor', qualifier: 'editor',
scopeNote: 'test scope note', scopeNote: 'test scope note',
@@ -68,7 +88,11 @@ describe('MetadataSchemaComponent', () => {
}, },
{ {
id: 4, id: 4,
self: 'https://dspace7.4science.it/dspace-spring-rest/api/core/metadatafields/11', _links: {
self: {
href: 'https://dspace7.4science.it/dspace-spring-rest/api/core/metadatafields/11',
},
},
element: 'contributor', element: 'contributor',
qualifier: 'illustrator', qualifier: 'illustrator',
scopeNote: null, scopeNote: null,
@@ -99,7 +123,7 @@ describe('MetadataSchemaComponent', () => {
beforeEach(async(() => { beforeEach(async(() => {
TestBed.configureTestingModule({ TestBed.configureTestingModule({
imports: [CommonModule, RouterTestingModule.withRoutes([]), TranslateModule.forRoot(), NgbModule.forRoot()], imports: [CommonModule, RouterTestingModule.withRoutes([]), TranslateModule.forRoot(), NgbModule],
declarations: [MetadataSchemaComponent, PaginationComponent, EnumKeysPipe], declarations: [MetadataSchemaComponent, PaginationComponent, EnumKeysPipe],
providers: [ providers: [
{ provide: RegistryService, useValue: registryServiceStub }, { provide: RegistryService, useValue: registryServiceStub },

View File

@@ -464,7 +464,7 @@ export class EditBitstreamPageComponent implements OnInit, OnDestroy {
bitstream$.pipe( bitstream$.pipe(
switchMap(() => { switchMap(() => {
if (isNewFormat) { if (isNewFormat) {
const operation = Object.assign({op: 'replace', path: '/format', value: selectedFormat.self}); const operation = Object.assign({op: 'replace', path: '/format', value: selectedFormat._links.self});
this.bitstreamService.patch(this.bitstream.self, [operation]); this.bitstreamService.patch(this.bitstream.self, [operation]);
} }

View File

@@ -68,7 +68,7 @@ describe('BrowseByDatePageComponent', () => {
beforeEach(async(() => { beforeEach(async(() => {
TestBed.configureTestingModule({ TestBed.configureTestingModule({
imports: [CommonModule, RouterTestingModule.withRoutes([]), TranslateModule.forRoot(), NgbModule.forRoot()], imports: [CommonModule, RouterTestingModule.withRoutes([]), TranslateModule.forRoot(), NgbModule],
declarations: [BrowseByDatePageComponent, EnumKeysPipe, VarDirective], declarations: [BrowseByDatePageComponent, EnumKeysPipe, VarDirective],
providers: [ providers: [
{ provide: GLOBAL_CONFIG, useValue: ENV_CONFIG }, { provide: GLOBAL_CONFIG, useValue: ENV_CONFIG },

View File

@@ -20,7 +20,6 @@ import { Item } from '../../core/shared/item.model';
import { DSpaceObjectDataService } from '../../core/data/dspace-object-data.service'; import { DSpaceObjectDataService } from '../../core/data/dspace-object-data.service';
import { Community } from '../../core/shared/community.model'; import { Community } from '../../core/shared/community.model';
import { MockRouter } from '../../shared/mocks/mock-router'; import { MockRouter } from '../../shared/mocks/mock-router';
import { ResourceType } from '../../core/shared/resource-type';
import { createSuccessfulRemoteDataObject$ } from '../../shared/testing/utils'; import { createSuccessfulRemoteDataObject$ } from '../../shared/testing/utils';
import { BrowseEntry } from '../../core/shared/browse-entry.model'; import { BrowseEntry } from '../../core/shared/browse-entry.model';
import { VarDirective } from '../../shared/utils/var.directive'; import { VarDirective } from '../../shared/utils/var.directive';
@@ -86,7 +85,7 @@ describe('BrowseByMetadataPageComponent', () => {
beforeEach(async(() => { beforeEach(async(() => {
TestBed.configureTestingModule({ TestBed.configureTestingModule({
imports: [CommonModule, RouterTestingModule.withRoutes([]), TranslateModule.forRoot(), NgbModule.forRoot()], imports: [CommonModule, RouterTestingModule.withRoutes([]), TranslateModule.forRoot(), NgbModule],
declarations: [BrowseByMetadataPageComponent, EnumKeysPipe, VarDirective], declarations: [BrowseByMetadataPageComponent, EnumKeysPipe, VarDirective],
providers: [ providers: [
{ provide: ActivatedRoute, useValue: activatedRouteStub }, { provide: ActivatedRoute, useValue: activatedRouteStub },

View File

@@ -64,7 +64,7 @@ describe('BrowseByTitlePageComponent', () => {
beforeEach(async(() => { beforeEach(async(() => {
TestBed.configureTestingModule({ TestBed.configureTestingModule({
imports: [CommonModule, RouterTestingModule.withRoutes([]), TranslateModule.forRoot(), NgbModule.forRoot()], imports: [CommonModule, RouterTestingModule.withRoutes([]), TranslateModule.forRoot(), NgbModule],
declarations: [BrowseByTitlePageComponent, EnumKeysPipe, VarDirective], declarations: [BrowseByTitlePageComponent, EnumKeysPipe, VarDirective],
providers: [ providers: [
{ provide: ActivatedRoute, useValue: activatedRouteStub }, { provide: ActivatedRoute, useValue: activatedRouteStub },

View File

@@ -0,0 +1,41 @@
import { Injectable } from '@angular/core';
import { Community } from '../core/shared/community.model';
import { DSpaceObjectDataService } from '../core/data/dspace-object-data.service';
import { DSOBreadcrumbsService } from '../core/breadcrumbs/dso-breadcrumbs.service';
import { Collection } from '../core/shared/collection.model';
import { ActivatedRouteSnapshot, RouterStateSnapshot } from '@angular/router';
import { BreadcrumbConfig } from '../breadcrumbs/breadcrumb/breadcrumb-config.model';
import { Observable } from 'rxjs';
import { getRemoteDataPayload, getSucceededRemoteData } from '../core/shared/operators';
import { map } from 'rxjs/operators';
import { hasValue } from '../shared/empty.util';
import { getDSOPath } from '../app-routing.module';
/**
* The class that resolves the BreadcrumbConfig object for a DSpaceObject on a browse by page
*/
@Injectable()
export class BrowseByDSOBreadcrumbResolver {
constructor(protected breadcrumbService: DSOBreadcrumbsService, protected dataService: DSpaceObjectDataService) {
}
/**
* Method for resolving a breadcrumb config object
* @param {ActivatedRouteSnapshot} route The current ActivatedRouteSnapshot
* @param {RouterStateSnapshot} state The current RouterStateSnapshot
* @returns BreadcrumbConfig object
*/
resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<BreadcrumbConfig<Community | Collection>> {
const uuid = route.queryParams.scope;
if (hasValue(uuid)) {
return this.dataService.findById(uuid).pipe(
getSucceededRemoteData(),
getRemoteDataPayload(),
map((object: Community | Collection) => {
return { provider: this.breadcrumbService, key: object, url: getDSOPath(object) };
})
);
}
return undefined;
}
}

View File

@@ -37,24 +37,24 @@ export class BrowseByGuard implements CanActivate {
return dsoAndMetadata$.pipe( return dsoAndMetadata$.pipe(
map((dsoRD) => { map((dsoRD) => {
const name = dsoRD.payload.name; const name = dsoRD.payload.name;
route.data = this.createData(title, id, metadataField, name, metadataTranslated, value); route.data = this.createData(title, id, metadataField, name, metadataTranslated, value, route);
return true; return true;
}) })
); );
} else { } else {
route.data = this.createData(title, id, metadataField, '', metadataTranslated, value); route.data = this.createData(title, id, metadataField, '', metadataTranslated, value, route);
return observableOf(true); return observableOf(true);
} }
} }
private createData(title, id, metadataField, collection, field, value) { private createData(title, id, metadataField, collection, field, value, route) {
return { return Object.assign({}, route.data, {
title: title, title: title,
id: id, id: id,
metadataField: metadataField, metadataField: metadataField,
collection: collection, collection: collection,
field: field, field: field,
value: hasValue(value) ? `"${value}"` : '' value: hasValue(value) ? `"${value}"` : ''
} });
} }
} }

View File

@@ -0,0 +1,28 @@
import { Injectable } from '@angular/core';
import { ActivatedRouteSnapshot, Resolve, RouterStateSnapshot } from '@angular/router';
import { I18nBreadcrumbsService } from '../core/breadcrumbs/i18n-breadcrumbs.service';
import { BreadcrumbConfig } from '../breadcrumbs/breadcrumb/breadcrumb-config.model';
import { I18nBreadcrumbResolver } from '../core/breadcrumbs/i18n-breadcrumb.resolver';
/**
* This class resolves a BreadcrumbConfig object with an i18n key string for a route
* It adds the metadata field of the current browse-by page
*/
@Injectable()
export class BrowseByI18nBreadcrumbResolver extends I18nBreadcrumbResolver {
constructor(protected breadcrumbService: I18nBreadcrumbsService) {
super(breadcrumbService);
}
/**
* Method for resolving a browse-by i18n breadcrumb configuration object
* @param {ActivatedRouteSnapshot} route The current ActivatedRouteSnapshot
* @param {RouterStateSnapshot} state The current RouterStateSnapshot
* @returns BreadcrumbConfig object for a browse-by page
*/
resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): BreadcrumbConfig<string> {
const extendedBreadcrumbKey = route.data.breadcrumbKey + '.' + route.params.id;
route.data = Object.assign({}, route.data, { breadcrumbKey: extendedBreadcrumbKey });
return super.resolve(route, state);
}
}

View File

@@ -2,12 +2,29 @@ import { RouterModule } from '@angular/router';
import { NgModule } from '@angular/core'; import { NgModule } from '@angular/core';
import { BrowseByGuard } from './browse-by-guard'; import { BrowseByGuard } from './browse-by-guard';
import { BrowseBySwitcherComponent } from './+browse-by-switcher/browse-by-switcher.component'; import { BrowseBySwitcherComponent } from './+browse-by-switcher/browse-by-switcher.component';
import { BrowseByDSOBreadcrumbResolver } from './browse-by-dso-breadcrumb.resolver';
import { BrowseByI18nBreadcrumbResolver } from './browse-by-i18n-breadcrumb.resolver';
@NgModule({ @NgModule({
imports: [ imports: [
RouterModule.forChild([ RouterModule.forChild([
{ path: ':id', component: BrowseBySwitcherComponent, canActivate: [BrowseByGuard], data: { title: 'browse.title' } } {
]) path: '',
resolve: { breadcrumb: BrowseByDSOBreadcrumbResolver },
children: [
{
path: ':id',
component: BrowseBySwitcherComponent,
canActivate: [BrowseByGuard],
resolve: { breadcrumb: BrowseByI18nBreadcrumbResolver },
data: { title: 'browse.title', breadcrumbKey: 'browse.metadata' }
}
]
}])
],
providers: [
BrowseByI18nBreadcrumbResolver,
BrowseByDSOBreadcrumbResolver
] ]
}) })
export class BrowseByRoutingModule { export class BrowseByRoutingModule {

View File

@@ -1,3 +1,4 @@
import { filter, tap } from 'rxjs/operators';
import { CollectionItemMapperComponent } from './collection-item-mapper.component'; import { CollectionItemMapperComponent } from './collection-item-mapper.component';
import { async, ComponentFixture, TestBed } from '@angular/core/testing'; import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { RouterTestingModule } from '@angular/router/testing'; import { RouterTestingModule } from '@angular/router/testing';
@@ -52,7 +53,12 @@ describe('CollectionItemMapperComponent', () => {
const mockCollection: Collection = Object.assign(new Collection(), { const mockCollection: Collection = Object.assign(new Collection(), {
id: 'ce41d451-97ed-4a9c-94a1-7de34f16a9f4', id: 'ce41d451-97ed-4a9c-94a1-7de34f16a9f4',
name: 'test-collection' name: 'test-collection',
_links: {
self: {
href: 'https://rest.api/collections/ce41d451-97ed-4a9c-94a1-7de34f16a9f4'
}
}
}); });
const mockCollectionRD: RemoteData<Collection> = new RemoteData<Collection>(false, false, true, null, mockCollection); const mockCollectionRD: RemoteData<Collection> = new RemoteData<Collection>(false, false, true, null, mockCollection);
const mockSearchOptions = of(new PaginatedSearchOptions({ const mockSearchOptions = of(new PaginatedSearchOptions({
@@ -77,7 +83,7 @@ describe('CollectionItemMapperComponent', () => {
const itemDataServiceStub = { const itemDataServiceStub = {
mapToCollection: () => of(new RestResponse(true, 200, 'OK')) mapToCollection: () => of(new RestResponse(true, 200, 'OK'))
}; };
const activatedRouteStub = new ActivatedRouteStub({}, { collection: mockCollectionRD }); const activatedRouteStub = new ActivatedRouteStub({}, { dso: mockCollectionRD });
const translateServiceStub = { const translateServiceStub = {
get: () => of('test-message of collection ' + mockCollection.name), get: () => of('test-message of collection ' + mockCollection.name),
onLangChange: new EventEmitter(), onLangChange: new EventEmitter(),
@@ -116,7 +122,7 @@ describe('CollectionItemMapperComponent', () => {
beforeEach(async(() => { beforeEach(async(() => {
TestBed.configureTestingModule({ TestBed.configureTestingModule({
imports: [CommonModule, FormsModule, RouterTestingModule.withRoutes([]), TranslateModule.forRoot(), NgbModule.forRoot()], imports: [CommonModule, FormsModule, RouterTestingModule.withRoutes([]), TranslateModule.forRoot(), NgbModule],
declarations: [CollectionItemMapperComponent, ItemSelectComponent, SearchFormComponent, PaginationComponent, EnumKeysPipe, VarDirective, ErrorComponent, LoadingComponent], declarations: [CollectionItemMapperComponent, ItemSelectComponent, SearchFormComponent, PaginationComponent, EnumKeysPipe, VarDirective, ErrorComponent, LoadingComponent],
providers: [ providers: [
{ provide: ActivatedRoute, useValue: activatedRouteStub }, { provide: ActivatedRoute, useValue: activatedRouteStub },

View File

@@ -22,6 +22,7 @@ import { SEARCH_CONFIG_SERVICE } from '../../+my-dspace-page/my-dspace-page.comp
import { SearchConfigurationService } from '../../core/shared/search/search-configuration.service'; import { SearchConfigurationService } from '../../core/shared/search/search-configuration.service';
import { PaginatedSearchOptions } from '../../shared/search/paginated-search-options.model'; import { PaginatedSearchOptions } from '../../shared/search/paginated-search-options.model';
import { SearchService } from '../../core/shared/search/search.service'; import { SearchService } from '../../core/shared/search/search.service';
import { followLink } from '../../shared/utils/follow-link-config.model';
@Component({ @Component({
selector: 'ds-collection-item-mapper', selector: 'ds-collection-item-mapper',
@@ -48,7 +49,7 @@ export class CollectionItemMapperComponent implements OnInit {
* A view on the tabset element * A view on the tabset element
* Used to switch tabs programmatically * Used to switch tabs programmatically
*/ */
@ViewChild('tabs') tabs; @ViewChild('tabs', {static: false}) tabs;
/** /**
* The collection to map items to * The collection to map items to
@@ -101,7 +102,7 @@ export class CollectionItemMapperComponent implements OnInit {
} }
ngOnInit(): void { ngOnInit(): void {
this.collectionRD$ = this.route.data.pipe(map((data) => data.collection)).pipe(getSucceededRemoteData()) as Observable<RemoteData<Collection>>; this.collectionRD$ = this.route.data.pipe(map((data) => data.dso)).pipe(getSucceededRemoteData()) as Observable<RemoteData<Collection>>;
this.searchOptions$ = this.searchConfigService.paginatedSearchOptions; this.searchOptions$ = this.searchConfigService.paginatedSearchOptions;
this.loadItemLists(); this.loadItemLists();
} }
@@ -122,7 +123,7 @@ export class CollectionItemMapperComponent implements OnInit {
if (shouldUpdate) { if (shouldUpdate) {
return this.collectionDataService.getMappedItems(collectionRD.payload.id, Object.assign(options, { return this.collectionDataService.getMappedItems(collectionRD.payload.id, Object.assign(options, {
sort: this.defaultSortOptions sort: this.defaultSortOptions
})) }),followLink('owningCollection'))
} }
}) })
); );
@@ -154,7 +155,7 @@ export class CollectionItemMapperComponent implements OnInit {
map((collectionRD: RemoteData<Collection>) => collectionRD.payload), map((collectionRD: RemoteData<Collection>) => collectionRD.payload),
switchMap((collection: Collection) => switchMap((collection: Collection) =>
observableCombineLatest(ids.map((id: string) => observableCombineLatest(ids.map((id: string) =>
remove ? this.itemDataService.removeMappingFromCollection(id, collection.id) : this.itemDataService.mapToCollection(id, collection.self) remove ? this.itemDataService.removeMappingFromCollection(id, collection.id) : this.itemDataService.mapToCollection(id, collection._links.self.href)
)) ))
) )
); );

View File

@@ -10,6 +10,9 @@ import { DeleteCollectionPageComponent } from './delete-collection-page/delete-c
import { URLCombiner } from '../core/url-combiner/url-combiner'; import { URLCombiner } from '../core/url-combiner/url-combiner';
import { getCollectionModulePath } from '../app-routing.module'; import { getCollectionModulePath } from '../app-routing.module';
import { CollectionItemMapperComponent } from './collection-item-mapper/collection-item-mapper.component'; 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';
export const COLLECTION_PARENT_PARAMETER = 'parent'; export const COLLECTION_PARENT_PARAMETER = 'parent';
@@ -18,7 +21,7 @@ export function getCollectionPageRoute(collectionId: string) {
} }
export function getCollectionEditPath(id: string) { export function getCollectionEditPath(id: string) {
return new URLCombiner(getCollectionModulePath(), COLLECTION_EDIT_PATH.replace(/:id/, id)).toString() return new URLCombiner(getCollectionModulePath(), id, COLLECTION_EDIT_PATH).toString()
} }
export function getCollectionCreatePath() { export function getCollectionCreatePath() {
@@ -26,51 +29,54 @@ export function getCollectionCreatePath() {
} }
const COLLECTION_CREATE_PATH = 'create'; const COLLECTION_CREATE_PATH = 'create';
const COLLECTION_EDIT_PATH = ':id/edit'; const COLLECTION_EDIT_PATH = 'edit';
@NgModule({ @NgModule({
imports: [ imports: [
RouterModule.forChild([ RouterModule.forChild([
{
path: ':id',
resolve: {
dso: CollectionPageResolver,
breadcrumb: CollectionBreadcrumbResolver
},
children: [
{
path: COLLECTION_EDIT_PATH,
loadChildren: './edit-collection-page/edit-collection-page.module#EditCollectionPageModule',
canActivate: [AuthenticatedGuard]
},
{
path: 'delete',
pathMatch: 'full',
component: DeleteCollectionPageComponent,
canActivate: [AuthenticatedGuard],
},
{
path: '',
component: CollectionPageComponent,
pathMatch: 'full',
},
{
path: '/edit/mapper',
component: CollectionItemMapperComponent,
pathMatch: 'full',
canActivate: [AuthenticatedGuard]
}
]
},
{ {
path: COLLECTION_CREATE_PATH, path: COLLECTION_CREATE_PATH,
component: CreateCollectionPageComponent, component: CreateCollectionPageComponent,
canActivate: [AuthenticatedGuard, CreateCollectionPageGuard] canActivate: [AuthenticatedGuard, CreateCollectionPageGuard]
}, },
{
path: COLLECTION_EDIT_PATH,
loadChildren: './edit-collection-page/edit-collection-page.module#EditCollectionPageModule',
canActivate: [AuthenticatedGuard]
},
{
path: ':id/delete',
pathMatch: 'full',
component: DeleteCollectionPageComponent,
canActivate: [AuthenticatedGuard],
resolve: {
dso: CollectionPageResolver
}
},
{
path: ':id',
component: CollectionPageComponent,
pathMatch: 'full',
resolve: {
collection: CollectionPageResolver
}
},
{
path: ':id/edit/mapper',
component: CollectionItemMapperComponent,
pathMatch: 'full',
resolve: {
collection: CollectionPageResolver
},
canActivate: [AuthenticatedGuard]
}
]) ])
], ],
providers: [ providers: [
CollectionPageResolver, CollectionPageResolver,
CollectionBreadcrumbResolver,
DSOBreadcrumbsService,
LinkService,
CreateCollectionPageGuard CreateCollectionPageGuard
] ]
}) })

View File

@@ -62,7 +62,7 @@ export class CollectionPageComponent implements OnInit {
ngOnInit(): void { ngOnInit(): void {
this.collectionRD$ = this.route.data.pipe( this.collectionRD$ = this.route.data.pipe(
map((data) => data.collection as RemoteData<Collection>), map((data) => data.dso as RemoteData<Collection>),
redirectToPageNotFoundOn404(this.router), redirectToPageNotFoundOn404(this.router),
take(1) take(1)
); );

View File

@@ -6,6 +6,7 @@ import { CollectionDataService } from '../core/data/collection-data.service';
import { RemoteData } from '../core/data/remote-data'; import { RemoteData } from '../core/data/remote-data';
import { find } from 'rxjs/operators'; import { find } from 'rxjs/operators';
import { hasValue } from '../shared/empty.util'; 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 before the route is activated * This class represents a resolver that requests a specific collection before the route is activated
@@ -23,7 +24,7 @@ export class CollectionPageResolver implements Resolve<RemoteData<Collection>> {
* or an error if something went wrong * or an error if something went wrong
*/ */
resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<RemoteData<Collection>> { resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<RemoteData<Collection>> {
return this.collectionService.findById(route.params.id).pipe( return this.collectionService.findById(route.params.id, followLink('logo')).pipe(
find((RD) => hasValue(RD.error) || RD.hasSucceeded), find((RD) => hasValue(RD.error) || RD.hasSucceeded),
); );
} }

View File

@@ -1,11 +1,11 @@
import { RouterModule } from '@angular/router'; import { RouterModule } from '@angular/router';
import { NgModule } from '@angular/core'; import { NgModule } from '@angular/core';
import { EditCollectionPageComponent } from './edit-collection-page.component'; import { EditCollectionPageComponent } from './edit-collection-page.component';
import { CollectionPageResolver } from '../collection-page.resolver';
import { CollectionMetadataComponent } from './collection-metadata/collection-metadata.component'; import { CollectionMetadataComponent } from './collection-metadata/collection-metadata.component';
import { CollectionRolesComponent } from './collection-roles/collection-roles.component'; import { CollectionRolesComponent } from './collection-roles/collection-roles.component';
import { CollectionSourceComponent } from './collection-source/collection-source.component'; import { CollectionSourceComponent } from './collection-source/collection-source.component';
import { CollectionCurateComponent } from './collection-curate/collection-curate.component'; import { CollectionCurateComponent } from './collection-curate/collection-curate.component';
import { I18nBreadcrumbResolver } from '../../core/breadcrumbs/i18n-breadcrumb.resolver';
/** /**
* Routing module that handles the routing for the Edit Collection page administrator functionality * Routing module that handles the routing for the Edit Collection page administrator functionality
@@ -15,10 +15,11 @@ import { CollectionCurateComponent } from './collection-curate/collection-curate
RouterModule.forChild([ RouterModule.forChild([
{ {
path: '', path: '',
component: EditCollectionPageComponent,
resolve: { resolve: {
dso: CollectionPageResolver breadcrumb: I18nBreadcrumbResolver
}, },
data: { breadcrumbKey: 'collection.edit' },
component: EditCollectionPageComponent,
children: [ children: [
{ {
path: '', path: '',
@@ -30,30 +31,28 @@ import { CollectionCurateComponent } from './collection-curate/collection-curate
component: CollectionMetadataComponent, component: CollectionMetadataComponent,
data: { data: {
title: 'collection.edit.tabs.metadata.title', title: 'collection.edit.tabs.metadata.title',
hideReturnButton: true hideReturnButton: true,
showBreadcrumbs: true
} }
}, },
{ {
path: 'roles', path: 'roles',
component: CollectionRolesComponent, component: CollectionRolesComponent,
data: { title: 'collection.edit.tabs.roles.title' } data: { title: 'collection.edit.tabs.roles.title', showBreadcrumbs: true }
}, },
{ {
path: 'source', path: 'source',
component: CollectionSourceComponent, component: CollectionSourceComponent,
data: { title: 'collection.edit.tabs.source.title' } data: { title: 'collection.edit.tabs.source.title', showBreadcrumbs: true }
}, },
{ {
path: 'curate', path: 'curate',
component: CollectionCurateComponent, component: CollectionCurateComponent,
data: { title: 'collection.edit.tabs.curate.title' } data: { title: 'collection.edit.tabs.curate.title', showBreadcrumbs: true }
} }
] ]
} }
]) ])
],
providers: [
CollectionPageResolver,
] ]
}) })
export class EditCollectionPageRoutingModule { export class EditCollectionPageRoutingModule {

View File

@@ -9,6 +9,9 @@ import { CreateCommunityPageGuard } from './create-community-page/create-communi
import { DeleteCommunityPageComponent } from './delete-community-page/delete-community-page.component'; import { DeleteCommunityPageComponent } from './delete-community-page/delete-community-page.component';
import { URLCombiner } from '../core/url-combiner/url-combiner'; import { URLCombiner } from '../core/url-combiner/url-combiner';
import { getCommunityModulePath } from '../app-routing.module'; import { getCommunityModulePath } from '../app-routing.module';
import { CommunityBreadcrumbResolver } from '../core/breadcrumbs/community-breadcrumb.resolver';
import { DSOBreadcrumbsService } from '../core/breadcrumbs/dso-breadcrumbs.service';
import { LinkService } from '../core/cache/builders/link.service';
export const COMMUNITY_PARENT_PARAMETER = 'parent'; export const COMMUNITY_PARENT_PARAMETER = 'parent';
@@ -17,7 +20,7 @@ export function getCommunityPageRoute(communityId: string) {
} }
export function getCommunityEditPath(id: string) { export function getCommunityEditPath(id: string) {
return new URLCombiner(getCommunityModulePath(), COMMUNITY_EDIT_PATH.replace(/:id/, id)).toString() return new URLCombiner(getCommunityModulePath(), id, COMMUNITY_EDIT_PATH).toString()
} }
export function getCommunityCreatePath() { export function getCommunityCreatePath() {
@@ -25,42 +28,48 @@ export function getCommunityCreatePath() {
} }
const COMMUNITY_CREATE_PATH = 'create'; const COMMUNITY_CREATE_PATH = 'create';
const COMMUNITY_EDIT_PATH = ':id/edit'; const COMMUNITY_EDIT_PATH = 'edit';
@NgModule({ @NgModule({
imports: [ imports: [
RouterModule.forChild([ RouterModule.forChild([
{
path: ':id',
resolve: {
dso: CommunityPageResolver,
breadcrumb: CommunityBreadcrumbResolver
},
children: [
{
path: COMMUNITY_EDIT_PATH,
loadChildren: './edit-community-page/edit-community-page.module#EditCommunityPageModule',
canActivate: [AuthenticatedGuard]
},
{
path: 'delete',
pathMatch: 'full',
component: DeleteCommunityPageComponent,
canActivate: [AuthenticatedGuard],
},
{
path: '',
component: CommunityPageComponent,
pathMatch: 'full',
}
]
},
{ {
path: COMMUNITY_CREATE_PATH, path: COMMUNITY_CREATE_PATH,
component: CreateCommunityPageComponent, component: CreateCommunityPageComponent,
canActivate: [AuthenticatedGuard, CreateCommunityPageGuard] canActivate: [AuthenticatedGuard, CreateCommunityPageGuard]
}, },
{
path: COMMUNITY_EDIT_PATH,
loadChildren: './edit-community-page/edit-community-page.module#EditCommunityPageModule',
canActivate: [AuthenticatedGuard]
},
{
path: ':id/delete',
pathMatch: 'full',
component: DeleteCommunityPageComponent,
canActivate: [AuthenticatedGuard],
resolve: {
dso: CommunityPageResolver
}
},
{
path: ':id',
component: CommunityPageComponent,
pathMatch: 'full',
resolve: {
community: CommunityPageResolver
}
}
]) ])
], ],
providers: [ providers: [
CommunityPageResolver, CommunityPageResolver,
CommunityBreadcrumbResolver,
DSOBreadcrumbsService,
LinkService,
CreateCommunityPageGuard CreateCommunityPageGuard
] ]
}) })

View File

@@ -46,7 +46,7 @@ export class CommunityPageComponent implements OnInit {
ngOnInit(): void { ngOnInit(): void {
this.communityRD$ = this.route.data.pipe( this.communityRD$ = this.route.data.pipe(
map((data) => data.community as RemoteData<Community>), map((data) => data.dso as RemoteData<Community>),
redirectToPageNotFoundOn404(this.router) redirectToPageNotFoundOn404(this.router)
); );
this.logoRD$ = this.communityRD$.pipe( this.logoRD$ = this.communityRD$.pipe(

View File

@@ -0,0 +1,28 @@
import { of as observableOf } from 'rxjs';
import { first } from 'rxjs/operators';
import { CommunityPageResolver } from './community-page.resolver';
describe('CommunityPageResolver', () => {
describe('resolve', () => {
let resolver: CommunityPageResolver;
let communityService: any;
const uuid = '1234-65487-12354-1235';
beforeEach(() => {
communityService = {
findById: (id: string) => observableOf({ payload: { id }, hasSucceeded: true })
};
resolver = new CommunityPageResolver(communityService);
});
it('should resolve a community with the correct id', () => {
resolver.resolve({ params: { id: uuid } } as any, undefined)
.pipe(first())
.subscribe(
(resolved) => {
expect(resolved.payload.id).toEqual(uuid);
}
);
});
});
});

View File

@@ -6,6 +6,7 @@ import { Community } from '../core/shared/community.model';
import { CommunityDataService } from '../core/data/community-data.service'; import { CommunityDataService } from '../core/data/community-data.service';
import { find } from 'rxjs/operators'; import { find } from 'rxjs/operators';
import { hasValue } from '../shared/empty.util'; import { hasValue } from '../shared/empty.util';
import { followLink } from '../shared/utils/follow-link-config.model';
/** /**
* This class represents a resolver that requests a specific community before the route is activated * This class represents a resolver that requests a specific community before the route is activated
@@ -23,7 +24,12 @@ export class CommunityPageResolver implements Resolve<RemoteData<Community>> {
* or an error if something went wrong * or an error if something went wrong
*/ */
resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<RemoteData<Community>> { resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<RemoteData<Community>> {
return this.communityService.findById(route.params.id).pipe( return this.communityService.findById(
route.params.id,
followLink('logo'),
followLink('subcommunities'),
followLink('collections')
).pipe(
find((RD) => hasValue(RD.error) || RD.hasSucceeded) find((RD) => hasValue(RD.error) || RD.hasSucceeded)
); );
} }

View File

@@ -1,15 +1,14 @@
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { ActivatedRoute, Router } from '@angular/router';
import { TranslateModule } from '@ngx-translate/core';
import { CommonModule } from '@angular/common'; import { CommonModule } from '@angular/common';
import { RouterTestingModule } from '@angular/router/testing';
import { NO_ERRORS_SCHEMA } from '@angular/core'; import { NO_ERRORS_SCHEMA } from '@angular/core';
import { RouteService } from '../../core/services/route.service'; import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { SharedModule } from '../../shared/shared.module'; import { ActivatedRoute } from '@angular/router';
import { RouterTestingModule } from '@angular/router/testing';
import { TranslateModule } from '@ngx-translate/core';
import { of as observableOf } from 'rxjs'; import { of as observableOf } from 'rxjs';
import { NotificationsService } from '../../shared/notifications/notifications.service';
import { DeleteCommunityPageComponent } from './delete-community-page.component';
import { CommunityDataService } from '../../core/data/community-data.service'; import { CommunityDataService } from '../../core/data/community-data.service';
import { NotificationsService } from '../../shared/notifications/notifications.service';
import { SharedModule } from '../../shared/shared.module';
import { DeleteCommunityPageComponent } from './delete-community-page.component';
describe('DeleteCommunityPageComponent', () => { describe('DeleteCommunityPageComponent', () => {
let comp: DeleteCommunityPageComponent; let comp: DeleteCommunityPageComponent;

View File

@@ -5,6 +5,7 @@ import { NgModule } from '@angular/core';
import { CommunityMetadataComponent } from './community-metadata/community-metadata.component'; import { CommunityMetadataComponent } from './community-metadata/community-metadata.component';
import { CommunityRolesComponent } from './community-roles/community-roles.component'; import { CommunityRolesComponent } from './community-roles/community-roles.component';
import { CommunityCurateComponent } from './community-curate/community-curate.component'; import { CommunityCurateComponent } from './community-curate/community-curate.component';
import { I18nBreadcrumbResolver } from '../../core/breadcrumbs/i18n-breadcrumb.resolver';
/** /**
* Routing module that handles the routing for the Edit Community page administrator functionality * Routing module that handles the routing for the Edit Community page administrator functionality
@@ -14,10 +15,11 @@ import { CommunityCurateComponent } from './community-curate/community-curate.co
RouterModule.forChild([ RouterModule.forChild([
{ {
path: '', path: '',
component: EditCommunityPageComponent,
resolve: { resolve: {
dso: CommunityPageResolver breadcrumb: I18nBreadcrumbResolver
}, },
data: { breadcrumbKey: 'community.edit' },
component: EditCommunityPageComponent,
children: [ children: [
{ {
path: '', path: '',
@@ -29,26 +31,24 @@ import { CommunityCurateComponent } from './community-curate/community-curate.co
component: CommunityMetadataComponent, component: CommunityMetadataComponent,
data: { data: {
title: 'community.edit.tabs.metadata.title', title: 'community.edit.tabs.metadata.title',
hideReturnButton: true hideReturnButton: true,
showBreadcrumbs: true
} }
}, },
{ {
path: 'roles', path: 'roles',
component: CommunityRolesComponent, component: CommunityRolesComponent,
data: { title: 'community.edit.tabs.roles.title' } data: { title: 'community.edit.tabs.roles.title', showBreadcrumbs: true }
}, },
{ {
path: 'curate', path: 'curate',
component: CommunityCurateComponent, component: CommunityCurateComponent,
data: { title: 'community.edit.tabs.curate.title' } data: { title: 'community.edit.tabs.curate.title', showBreadcrumbs: true }
} }
] ]
} }
]) ])
], ],
providers: [
CommunityPageResolver,
]
}) })
export class EditCommunityPageRoutingModule { export class EditCommunityPageRoutingModule {

View File

@@ -116,7 +116,7 @@ describe('CommunityPageSubCollectionList Component', () => {
TranslateModule.forRoot(), TranslateModule.forRoot(),
SharedModule, SharedModule,
RouterTestingModule.withRoutes([]), RouterTestingModule.withRoutes([]),
NgbModule.forRoot(), NgbModule,
NoopAnimationsModule NoopAnimationsModule
], ],
declarations: [CommunityPageSubCollectionListComponent], declarations: [CommunityPageSubCollectionListComponent],

View File

@@ -117,7 +117,7 @@ describe('CommunityPageSubCommunityListComponent Component', () => {
TranslateModule.forRoot(), TranslateModule.forRoot(),
SharedModule, SharedModule,
RouterTestingModule.withRoutes([]), RouterTestingModule.withRoutes([]),
NgbModule.forRoot(), NgbModule,
NoopAnimationsModule NoopAnimationsModule
], ],
declarations: [CommunityPageSubCommunityListComponent], declarations: [CommunityPageSubCommunityListComponent],

View File

@@ -107,7 +107,7 @@ describe('TopLevelCommunityList Component', () => {
TranslateModule.forRoot(), TranslateModule.forRoot(),
SharedModule, SharedModule,
RouterTestingModule.withRoutes([]), RouterTestingModule.withRoutes([]),
NgbModule.forRoot(), NgbModule,
NoopAnimationsModule NoopAnimationsModule
], ],
declarations: [TopLevelCommunityListComponent], declarations: [TopLevelCommunityListComponent],

View File

@@ -13,6 +13,7 @@ import { ItemBitstreamsComponent } from './item-bitstreams/item-bitstreams.compo
import { ItemCollectionMapperComponent } from './item-collection-mapper/item-collection-mapper.component'; import { ItemCollectionMapperComponent } from './item-collection-mapper/item-collection-mapper.component';
import { ItemMoveComponent } from './item-move/item-move.component'; import { ItemMoveComponent } from './item-move/item-move.component';
import { ItemRelationshipsComponent } from './item-relationships/item-relationships.component'; import { ItemRelationshipsComponent } from './item-relationships/item-relationships.component';
import { I18nBreadcrumbResolver } from '../../core/breadcrumbs/i18n-breadcrumb.resolver';
const ITEM_EDIT_WITHDRAW_PATH = 'withdraw'; const ITEM_EDIT_WITHDRAW_PATH = 'withdraw';
const ITEM_EDIT_REINSTATE_PATH = 'reinstate'; const ITEM_EDIT_REINSTATE_PATH = 'reinstate';
@@ -29,104 +30,88 @@ const ITEM_EDIT_MOVE_PATH = 'move';
RouterModule.forChild([ RouterModule.forChild([
{ {
path: '', path: '',
component: EditItemPageComponent,
resolve: { resolve: {
item: ItemPageResolver breadcrumb: I18nBreadcrumbResolver
}, },
data: { breadcrumbKey: 'item.edit' },
children: [ children: [
{ {
path: '', path: '',
redirectTo: 'status', component: EditItemPageComponent,
pathMatch: 'full' children: [
{
path: '',
redirectTo: 'status',
pathMatch: 'full'
},
{
path: 'status',
component: ItemStatusComponent,
data: { title: 'item.edit.tabs.status.title', showBreadcrumbs: true }
},
{
path: 'bitstreams',
component: ItemBitstreamsComponent,
data: { title: 'item.edit.tabs.bitstreams.title', showBreadcrumbs: true }
},
{
path: 'metadata',
component: ItemMetadataComponent,
data: { title: 'item.edit.tabs.metadata.title', showBreadcrumbs: true }
},
{
path: 'relationships',
component: ItemRelationshipsComponent,
data: { title: 'item.edit.tabs.relationships.title', showBreadcrumbs: true }
},
{
path: 'view',
/* TODO - change when view page exists */
component: ItemBitstreamsComponent,
data: { title: 'item.edit.tabs.view.title', showBreadcrumbs: true }
},
{
path: 'curate',
/* TODO - change when curate page exists */
component: ItemBitstreamsComponent,
data: { title: 'item.edit.tabs.curate.title', showBreadcrumbs: true }
}
]
}, },
{ {
path: 'status', path: 'mapper',
component: ItemStatusComponent, component: ItemCollectionMapperComponent,
data: { title: 'item.edit.tabs.status.title' }
}, },
{ {
path: 'bitstreams', path: ITEM_EDIT_WITHDRAW_PATH,
component: ItemBitstreamsComponent, component: ItemWithdrawComponent,
data: { title: 'item.edit.tabs.bitstreams.title' }
}, },
{ {
path: 'metadata', path: ITEM_EDIT_REINSTATE_PATH,
component: ItemMetadataComponent, component: ItemReinstateComponent,
data: { title: 'item.edit.tabs.metadata.title' }
}, },
{ {
path: 'relationships', path: ITEM_EDIT_PRIVATE_PATH,
component: ItemRelationshipsComponent, component: ItemPrivateComponent,
data: { title: 'item.edit.tabs.relationships.title' }
}, },
{ {
path: 'view', path: ITEM_EDIT_PUBLIC_PATH,
/* TODO - change when view page exists */ component: ItemPublicComponent,
component: ItemBitstreamsComponent,
data: { title: 'item.edit.tabs.view.title' }
}, },
{ {
path: 'curate', path: ITEM_EDIT_DELETE_PATH,
/* TODO - change when curate page exists */ component: ItemDeleteComponent,
component: ItemBitstreamsComponent,
data: { title: 'item.edit.tabs.curate.title' }
}, },
{
path: ITEM_EDIT_MOVE_PATH,
component: ItemMoveComponent,
data: { title: 'item.edit.move.title' },
}
] ]
}, }
{ ])
path: 'mapper',
component: ItemCollectionMapperComponent,
resolve: {
item: ItemPageResolver
}
},
{
path: ITEM_EDIT_WITHDRAW_PATH,
component: ItemWithdrawComponent,
resolve: {
item: ItemPageResolver
}
},
{
path: ITEM_EDIT_REINSTATE_PATH,
component: ItemReinstateComponent,
resolve: {
item: ItemPageResolver
}
},
{
path: ITEM_EDIT_PRIVATE_PATH,
component: ItemPrivateComponent,
resolve: {
item: ItemPageResolver
}
},
{
path: ITEM_EDIT_PUBLIC_PATH,
component: ItemPublicComponent,
resolve: {
item: ItemPageResolver
}
},
{
path: ITEM_EDIT_DELETE_PATH,
component: ItemDeleteComponent,
resolve: {
item: ItemPageResolver
}
},
{
path: ITEM_EDIT_MOVE_PATH,
component: ItemMoveComponent,
data: { title: 'item.edit.move.title' },
resolve: {
item: ItemPageResolver
}
}])
], ],
providers: [ providers: []
ItemPageResolver,
]
}) })
export class EditItemPageRoutingModule { export class EditItemPageRoutingModule {

View File

@@ -195,7 +195,7 @@ export class ItemBitstreamsComponent extends AbstractItemUpdateComponent impleme
take(1), take(1),
switchMap((removedBistreams: Bitstream[]) => { switchMap((removedBistreams: Bitstream[]) => {
if (isNotEmpty(removedBistreams)) { if (isNotEmpty(removedBistreams)) {
return observableZip(...removedBistreams.map((bitstream: Bitstream) => this.bitstreamService.deleteAndReturnResponse(bitstream))); return observableZip(...removedBistreams.map((bitstream: Bitstream) => this.bitstreamService.deleteAndReturnResponse(bitstream.id)));
} else { } else {
return observableOf(undefined); return observableOf(undefined);
} }

View File

@@ -1,21 +1,6 @@
import { Component, EventEmitter, Input, OnInit, Output, ViewChild, ViewContainerRef } from '@angular/core'; import { Component, Input, OnInit, ViewChild, ViewContainerRef } from '@angular/core';
import { Bundle } from '../../../../core/shared/bundle.model'; import { Bundle } from '../../../../core/shared/bundle.model';
import { ObjectUpdatesService } from '../../../../core/data/object-updates/object-updates.service';
import { Observable } from 'rxjs/internal/Observable';
import { FieldUpdates } from '../../../../core/data/object-updates/object-updates.reducer';
import { toBitstreamsArray } from '../../../../core/shared/item-bitstreams-utils';
import { map, switchMap, take, tap } from 'rxjs/operators';
import { Bitstream } from '../../../../core/shared/bitstream.model';
import { Item } from '../../../../core/shared/item.model'; import { Item } from '../../../../core/shared/item.model';
import { CdkDragDrop, CdkDragStart } from '@angular/cdk/drag-drop';
import { RemoteData } from '../../../../core/data/remote-data';
import { PaginatedList } from '../../../../core/data/paginated-list';
import { BundleDataService } from '../../../../core/data/bundle-data.service';
import { BehaviorSubject } from 'rxjs/internal/BehaviorSubject';
import { combineLatest as observableCombineLatest } from 'rxjs';
import { hasNoValue, isEmpty } from '../../../../shared/empty.util';
import { PaginatedSearchOptions } from '../../../../shared/search/paginated-search-options.model';
import { PaginationComponentOptions } from '../../../../shared/pagination/pagination-component-options.model';
import { ResponsiveColumnSizes } from '../../../../shared/responsive-table-sizes/responsive-column-sizes'; import { ResponsiveColumnSizes } from '../../../../shared/responsive-table-sizes/responsive-column-sizes';
import { ResponsiveTableSizes } from '../../../../shared/responsive-table-sizes/responsive-table-sizes'; import { ResponsiveTableSizes } from '../../../../shared/responsive-table-sizes/responsive-table-sizes';
@@ -32,7 +17,7 @@ export class ItemEditBitstreamBundleComponent implements OnInit {
/** /**
* The view on the bundle information and bitstreams * The view on the bundle information and bitstreams
*/ */
@ViewChild('bundleView') bundleView; @ViewChild('bundleView', {static: false}) bundleView;
/** /**
* The bundle to display bitstreams for * The bundle to display bitstreams for

View File

@@ -9,7 +9,7 @@ export class ItemEditBitstreamDragHandleComponent implements OnInit {
/** /**
* The view on the drag-handle * The view on the drag-handle
*/ */
@ViewChild('handleView') handleView; @ViewChild('handleView', {static: false}) handleView;
constructor(private viewContainerRef: ViewContainerRef) { constructor(private viewContainerRef: ViewContainerRef) {
} }

View File

@@ -22,7 +22,7 @@ export class ItemEditBitstreamComponent implements OnChanges, OnInit {
/** /**
* The view on the bitstream * The view on the bitstream
*/ */
@ViewChild('bitstreamView') bitstreamView; @ViewChild('bitstreamView', {static: false}) bitstreamView;
/** /**
* The current field, value and state of the bitstream * The current field, value and state of the bitstream

View File

@@ -1,42 +1,43 @@
import { CommonModule } from '@angular/common';
import { EventEmitter } from '@angular/core';
import { async, ComponentFixture, TestBed } from '@angular/core/testing'; import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { FormsModule } from '@angular/forms';
import { By } from '@angular/platform-browser';
import { ActivatedRoute, Router } from '@angular/router';
import { RouterTestingModule } from '@angular/router/testing'; import { RouterTestingModule } from '@angular/router/testing';
import { NgbModule } from '@ng-bootstrap/ng-bootstrap'; import { NgbModule } from '@ng-bootstrap/ng-bootstrap';
import { CommonModule } from '@angular/common';
import { ItemCollectionMapperComponent } from './item-collection-mapper.component';
import { ActivatedRoute, Router } from '@angular/router';
import { NotificationsService } from '../../../shared/notifications/notifications.service';
import { ItemDataService } from '../../../core/data/item-data.service';
import { RemoteData } from '../../../core/data/remote-data';
import { PaginationComponentOptions } from '../../../shared/pagination/pagination-component-options.model';
import { SortDirection, SortOptions } from '../../../core/cache/models/sort-options.model';
import { RouterStub } from '../../../shared/testing/router-stub';
import { ActivatedRouteStub } from '../../../shared/testing/active-router-stub';
import { EventEmitter } from '@angular/core';
import { SearchServiceStub } from '../../../shared/testing/search-service-stub';
import { PaginatedList } from '../../../core/data/paginated-list';
import { PageInfo } from '../../../core/shared/page-info.model';
import { FormsModule } from '@angular/forms';
import { TranslateModule, TranslateService } from '@ngx-translate/core'; import { TranslateModule, TranslateService } from '@ngx-translate/core';
import { NotificationsServiceStub } from '../../../shared/testing/notifications-service-stub';
import { HostWindowService } from '../../../shared/host-window.service';
import { HostWindowServiceStub } from '../../../shared/testing/host-window-service-stub';
import { By } from '@angular/platform-browser';
import { Item } from '../../../core/shared/item.model';
import { ObjectSelectService } from '../../../shared/object-select/object-select.service';
import { ObjectSelectServiceStub } from '../../../shared/testing/object-select-service-stub';
import { of } from 'rxjs/internal/observable/of'; import { of } from 'rxjs/internal/observable/of';
import { SortDirection, SortOptions } from '../../../core/cache/models/sort-options.model';
import { RestResponse } from '../../../core/cache/response.models'; import { RestResponse } from '../../../core/cache/response.models';
import { CollectionSelectComponent } from '../../../shared/object-select/collection-select/collection-select.component'; import { CollectionDataService } from '../../../core/data/collection-data.service';
import { PaginationComponent } from '../../../shared/pagination/pagination.component'; import { ItemDataService } from '../../../core/data/item-data.service';
import { EnumKeysPipe } from '../../../shared/utils/enum-keys-pipe'; import { PaginatedList } from '../../../core/data/paginated-list';
import { VarDirective } from '../../../shared/utils/var.directive'; import { RemoteData } from '../../../core/data/remote-data';
import { SearchFormComponent } from '../../../shared/search-form/search-form.component';
import { Collection } from '../../../core/shared/collection.model'; import { Collection } from '../../../core/shared/collection.model';
import { ErrorComponent } from '../../../shared/error/error.component'; import { Item } from '../../../core/shared/item.model';
import { LoadingComponent } from '../../../shared/loading/loading.component'; import { PageInfo } from '../../../core/shared/page-info.model';
import { SearchConfigurationService } from '../../../core/shared/search/search-configuration.service'; import { SearchConfigurationService } from '../../../core/shared/search/search-configuration.service';
import { SearchService } from '../../../core/shared/search/search.service'; import { SearchService } from '../../../core/shared/search/search.service';
import { ErrorComponent } from '../../../shared/error/error.component';
import { HostWindowService } from '../../../shared/host-window.service';
import { LoadingComponent } from '../../../shared/loading/loading.component';
import { NotificationsService } from '../../../shared/notifications/notifications.service';
import { CollectionSelectComponent } from '../../../shared/object-select/collection-select/collection-select.component';
import { ObjectSelectService } from '../../../shared/object-select/object-select.service';
import { PaginationComponentOptions } from '../../../shared/pagination/pagination-component-options.model';
import { PaginationComponent } from '../../../shared/pagination/pagination.component';
import { SearchFormComponent } from '../../../shared/search-form/search-form.component';
import { PaginatedSearchOptions } from '../../../shared/search/paginated-search-options.model'; import { PaginatedSearchOptions } from '../../../shared/search/paginated-search-options.model';
import { ActivatedRouteStub } from '../../../shared/testing/active-router-stub';
import { HostWindowServiceStub } from '../../../shared/testing/host-window-service-stub';
import { NotificationsServiceStub } from '../../../shared/testing/notifications-service-stub';
import { ObjectSelectServiceStub } from '../../../shared/testing/object-select-service-stub';
import { RouterStub } from '../../../shared/testing/router-stub';
import { SearchServiceStub } from '../../../shared/testing/search-service-stub';
import { EnumKeysPipe } from '../../../shared/utils/enum-keys-pipe';
import { VarDirective } from '../../../shared/utils/var.directive';
import { ItemCollectionMapperComponent } from './item-collection-mapper.component';
describe('ItemCollectionMapperComponent', () => { describe('ItemCollectionMapperComponent', () => {
let comp: ItemCollectionMapperComponent; let comp: ItemCollectionMapperComponent;
@@ -98,7 +99,7 @@ describe('ItemCollectionMapperComponent', () => {
beforeEach(async(() => { beforeEach(async(() => {
TestBed.configureTestingModule({ TestBed.configureTestingModule({
imports: [CommonModule, FormsModule, RouterTestingModule.withRoutes([]), TranslateModule.forRoot(), NgbModule.forRoot()], imports: [CommonModule, FormsModule, RouterTestingModule.withRoutes([]), TranslateModule.forRoot(), NgbModule],
declarations: [ItemCollectionMapperComponent, CollectionSelectComponent, SearchFormComponent, PaginationComponent, EnumKeysPipe, VarDirective, ErrorComponent, LoadingComponent], declarations: [ItemCollectionMapperComponent, CollectionSelectComponent, SearchFormComponent, PaginationComponent, EnumKeysPipe, VarDirective, ErrorComponent, LoadingComponent],
providers: [ providers: [
{ provide: ActivatedRoute, useValue: activatedRouteStub }, { provide: ActivatedRoute, useValue: activatedRouteStub },
@@ -109,7 +110,8 @@ describe('ItemCollectionMapperComponent', () => {
{ provide: SearchService, useValue: searchServiceStub }, { provide: SearchService, useValue: searchServiceStub },
{ provide: ObjectSelectService, useValue: new ObjectSelectServiceStub() }, { provide: ObjectSelectService, useValue: new ObjectSelectServiceStub() },
{ provide: TranslateService, useValue: translateServiceStub }, { provide: TranslateService, useValue: translateServiceStub },
{ provide: HostWindowService, useValue: new HostWindowServiceStub(0) } { provide: HostWindowService, useValue: new HostWindowServiceStub(0) },
{ provide: CollectionDataService, useValue: {} }
] ]
}).compileComponents(); }).compileComponents();
})); }));

View File

@@ -1,12 +1,18 @@
import { combineLatest as observableCombineLatest, Observable } from 'rxjs'; import { combineLatest as observableCombineLatest, Observable } from 'rxjs';
import { ChangeDetectionStrategy, Component, OnInit, ViewChild } from '@angular/core'; import { ChangeDetectionStrategy, Component, OnInit, ViewChild } from '@angular/core';
import { CollectionDataService } from '../../../core/data/collection-data.service';
import { fadeIn, fadeInOut } from '../../../shared/animations/fade'; import { fadeIn, fadeInOut } from '../../../shared/animations/fade';
import { RemoteData } from '../../../core/data/remote-data'; import { RemoteData } from '../../../core/data/remote-data';
import { PaginatedList } from '../../../core/data/paginated-list'; import { PaginatedList } from '../../../core/data/paginated-list';
import { Collection } from '../../../core/shared/collection.model'; import { Collection } from '../../../core/shared/collection.model';
import { Item } from '../../../core/shared/item.model'; import { Item } from '../../../core/shared/item.model';
import { getRemoteDataPayload, getSucceededRemoteData, toDSpaceObjectListRD } from '../../../core/shared/operators'; import {
getFirstSucceededRemoteDataPayload,
getRemoteDataPayload,
getSucceededRemoteData,
toDSpaceObjectListRD
} from '../../../core/shared/operators';
import { ActivatedRoute, Router } from '@angular/router'; import { ActivatedRoute, Router } from '@angular/router';
import { map, startWith, switchMap, take } from 'rxjs/operators'; import { map, startWith, switchMap, take } from 'rxjs/operators';
import { ItemDataService } from '../../../core/data/item-data.service'; import { ItemDataService } from '../../../core/data/item-data.service';
@@ -39,7 +45,7 @@ export class ItemCollectionMapperComponent implements OnInit {
* A view on the tabset element * A view on the tabset element
* Used to switch tabs programmatically * Used to switch tabs programmatically
*/ */
@ViewChild('tabs') tabs; @ViewChild('tabs', {static: false}) tabs;
/** /**
* The item to map to collections * The item to map to collections
@@ -81,6 +87,7 @@ export class ItemCollectionMapperComponent implements OnInit {
private searchService: SearchService, private searchService: SearchService,
private notificationsService: NotificationsService, private notificationsService: NotificationsService,
private itemDataService: ItemDataService, private itemDataService: ItemDataService,
private collectionDataService: CollectionDataService,
private translateService: TranslateService) { private translateService: TranslateService) {
} }
@@ -106,7 +113,8 @@ export class ItemCollectionMapperComponent implements OnInit {
); );
const owningCollectionRD$ = this.itemRD$.pipe( const owningCollectionRD$ = this.itemRD$.pipe(
switchMap((itemRD: RemoteData<Item>) => itemRD.payload.owningCollection) getFirstSucceededRemoteDataPayload(),
switchMap((item: Item) => this.collectionDataService.findOwningCollectionFor(item))
); );
const itemCollectionsAndOptions$ = observableCombineLatest( const itemCollectionsAndOptions$ = observableCombineLatest(
this.itemCollectionsRD$, this.itemCollectionsRD$,

View File

@@ -182,7 +182,7 @@ describe('ItemDeleteComponent', () => {
notificationsServiceStub = new NotificationsServiceStub(); notificationsServiceStub = new NotificationsServiceStub();
TestBed.configureTestingModule({ TestBed.configureTestingModule({
imports: [CommonModule, FormsModule, RouterTestingModule.withRoutes([]), TranslateModule.forRoot(), NgbModule.forRoot()], imports: [CommonModule, FormsModule, RouterTestingModule.withRoutes([]), TranslateModule.forRoot(), NgbModule],
declarations: [ItemDeleteComponent, VarDirective], declarations: [ItemDeleteComponent, VarDirective],
providers: [ providers: [
{ provide: ActivatedRoute, useValue: routeStub }, { provide: ActivatedRoute, useValue: routeStub },
@@ -220,7 +220,7 @@ describe('ItemDeleteComponent', () => {
spyOn(comp, 'notify'); spyOn(comp, 'notify');
comp.performAction(); comp.performAction();
expect(mockItemDataService.delete) expect(mockItemDataService.delete)
.toHaveBeenCalledWith(mockItem, types.filter((type) => typesSelection[type]).map((type) => type.id)); .toHaveBeenCalledWith(mockItem.id, types.filter((type) => typesSelection[type]).map((type) => type.id));
expect(comp.notify).toHaveBeenCalled(); expect(comp.notify).toHaveBeenCalled();
}); });
}); });

View File

@@ -312,7 +312,7 @@ export class ItemDeleteComponent
) )
), ),
).subscribe((types) => { ).subscribe((types) => {
this.itemDataService.delete(this.item, types).pipe(first()).subscribe( this.itemDataService.delete(this.item.id, types).pipe(first()).subscribe(
(succeeded: boolean) => { (succeeded: boolean) => {
this.notify(succeeded); this.notify(succeeded);
} }
@@ -322,7 +322,7 @@ export class ItemDeleteComponent
/** /**
* When the item is successfully delete, navigate to the homepage, otherwise navigate back to the item edit page * When the item is successfully delete, navigate to the homepage, otherwise navigate back to the item edit page
* @param response * @param succeeded
*/ */
notify(succeeded: boolean) { notify(succeeded: boolean) {
if (succeeded) { if (succeeded) {

View File

@@ -1,23 +1,22 @@
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { CUSTOM_ELEMENTS_SCHEMA, DebugElement } from '@angular/core'; import { CUSTOM_ELEMENTS_SCHEMA, DebugElement } from '@angular/core';
import { EditInPlaceFieldComponent } from './edit-in-place-field.component'; import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { RegistryService } from '../../../../core/registry/registry.service';
import { ObjectUpdatesService } from '../../../../core/data/object-updates/object-updates.service';
import { of as observableOf } from 'rxjs';
import { RemoteData } from '../../../../core/data/remote-data';
import { PaginatedList } from '../../../../core/data/paginated-list';
import { By } from '@angular/platform-browser';
import { FormsModule } from '@angular/forms'; import { FormsModule } from '@angular/forms';
import { SharedModule } from '../../../../shared/shared.module'; import { By } from '@angular/platform-browser';
import { TranslateModule } from '@ngx-translate/core';
import { getTestScheduler } from 'jasmine-marbles'; import { getTestScheduler } from 'jasmine-marbles';
import { of as observableOf } from 'rxjs';
import { TestScheduler } from 'rxjs/testing'; import { TestScheduler } from 'rxjs/testing';
import { FieldChangeType } from '../../../../core/data/object-updates/object-updates.actions'; import { FieldChangeType } from '../../../../core/data/object-updates/object-updates.actions';
import { TranslateModule } from '@ngx-translate/core'; import { ObjectUpdatesService } from '../../../../core/data/object-updates/object-updates.service';
import { MetadatumViewModel } from '../../../../core/shared/metadata.models'; import { PaginatedList } from '../../../../core/data/paginated-list';
import { MetadataSchema } from '../../../../core/metadata/metadata-schema.model';
import { MetadataField } from '../../../../core/metadata/metadata-field.model'; import { MetadataField } from '../../../../core/metadata/metadata-field.model';
import { createSuccessfulRemoteDataObject$ } from '../../../../shared/testing/utils'; import { MetadataSchema } from '../../../../core/metadata/metadata-schema.model';
import { RegistryService } from '../../../../core/registry/registry.service';
import { MetadatumViewModel } from '../../../../core/shared/metadata.models';
import { InputSuggestion } from '../../../../shared/input-suggestions/input-suggestions.model'; import { InputSuggestion } from '../../../../shared/input-suggestions/input-suggestions.model';
import { SharedModule } from '../../../../shared/shared.module';
import { createSuccessfulRemoteDataObject$ } from '../../../../shared/testing/utils';
import { EditInPlaceFieldComponent } from './edit-in-place-field.component';
let comp: EditInPlaceFieldComponent; let comp: EditInPlaceFieldComponent;
let fixture: ComponentFixture<EditInPlaceFieldComponent>; let fixture: ComponentFixture<EditInPlaceFieldComponent>;

View File

@@ -1,4 +1,5 @@
import { Component, Inject } from '@angular/core'; import { Component, Inject } from '@angular/core';
import { LinkService } from '../../../core/cache/builders/link.service';
import { Item } from '../../../core/shared/item.model'; import { Item } from '../../../core/shared/item.model';
import { ItemDataService } from '../../../core/data/item-data.service'; import { ItemDataService } from '../../../core/data/item-data.service';
import { ObjectUpdatesService } from '../../../core/data/object-updates/object-updates.service'; import { ObjectUpdatesService } from '../../../core/data/object-updates/object-updates.service';

View File

@@ -1,23 +1,23 @@
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { Item } from '../../../core/shared/item.model';
import { RouterStub } from '../../../shared/testing/router-stub';
import { CommonModule } from '@angular/common'; import { CommonModule } from '@angular/common';
import { RouterTestingModule } from '@angular/router/testing';
import { TranslateModule } from '@ngx-translate/core';
import { NgbModule } from '@ng-bootstrap/ng-bootstrap';
import { ActivatedRoute, Router } from '@angular/router';
import { ItemMoveComponent } from './item-move.component';
import { NotificationsServiceStub } from '../../../shared/testing/notifications-service-stub';
import { NotificationsService } from '../../../shared/notifications/notifications.service';
import { of as observableOf } from 'rxjs';
import { FormsModule } from '@angular/forms';
import { ItemDataService } from '../../../core/data/item-data.service';
import { RemoteData } from '../../../core/data/remote-data';
import { PaginatedList } from '../../../core/data/paginated-list';
import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core'; import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { FormsModule } from '@angular/forms';
import { ActivatedRoute, Router } from '@angular/router';
import { RouterTestingModule } from '@angular/router/testing';
import { NgbModule } from '@ng-bootstrap/ng-bootstrap';
import { TranslateModule } from '@ngx-translate/core';
import { of as observableOf } from 'rxjs';
import { RestResponse } from '../../../core/cache/response.models'; import { RestResponse } from '../../../core/cache/response.models';
import { ItemDataService } from '../../../core/data/item-data.service';
import { PaginatedList } from '../../../core/data/paginated-list';
import { RemoteData } from '../../../core/data/remote-data';
import { Collection } from '../../../core/shared/collection.model'; import { Collection } from '../../../core/shared/collection.model';
import { Item } from '../../../core/shared/item.model';
import { SearchService } from '../../../core/shared/search/search.service'; import { SearchService } from '../../../core/shared/search/search.service';
import { NotificationsService } from '../../../shared/notifications/notifications.service';
import { NotificationsServiceStub } from '../../../shared/testing/notifications-service-stub';
import { RouterStub } from '../../../shared/testing/router-stub';
import { ItemMoveComponent } from './item-move.component';
describe('ItemMoveComponent', () => { describe('ItemMoveComponent', () => {
let comp: ItemMoveComponent; let comp: ItemMoveComponent;
@@ -50,16 +50,14 @@ describe('ItemMoveComponent', () => {
}) })
}; };
const collection1 = Object.assign(new Collection(),{ const collection1 = Object.assign(new Collection(), {
uuid: 'collection-uuid-1', uuid: 'collection-uuid-1',
name: 'Test collection 1', name: 'Test collection 1'
self: 'self-link-1',
}); });
const collection2 = Object.assign(new Collection(),{ const collection2 = Object.assign(new Collection(), {
uuid: 'collection-uuid-2', uuid: 'collection-uuid-2',
name: 'Test collection 2', name: 'Test collection 2'
self: 'self-link-2',
}); });
const mockSearchService = { const mockSearchService = {
@@ -80,23 +78,20 @@ describe('ItemMoveComponent', () => {
const notificationsServiceStub = new NotificationsServiceStub(); const notificationsServiceStub = new NotificationsServiceStub();
describe('ItemMoveComponent success', () => { describe('ItemMoveComponent success', () => {
beforeEach(async(() => { beforeEach(() => {
TestBed.configureTestingModule({ TestBed.configureTestingModule({
imports: [CommonModule, FormsModule, RouterTestingModule.withRoutes([]), TranslateModule.forRoot(), NgbModule.forRoot()], imports: [CommonModule, FormsModule, RouterTestingModule.withRoutes([]), TranslateModule.forRoot(), NgbModule],
declarations: [ItemMoveComponent], declarations: [ItemMoveComponent],
providers: [ providers: [
{provide: ActivatedRoute, useValue: routeStub}, { provide: ActivatedRoute, useValue: routeStub },
{provide: Router, useValue: routerStub}, { provide: Router, useValue: routerStub },
{provide: ItemDataService, useValue: mockItemDataService}, { provide: ItemDataService, useValue: mockItemDataService },
{provide: NotificationsService, useValue: notificationsServiceStub}, { provide: NotificationsService, useValue: notificationsServiceStub },
{provide: SearchService, useValue: mockSearchService}, { provide: SearchService, useValue: mockSearchService },
], schemas: [ ], schemas: [
CUSTOM_ELEMENTS_SCHEMA CUSTOM_ELEMENTS_SCHEMA
] ]
}).compileComponents(); }).compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(ItemMoveComponent); fixture = TestBed.createComponent(ItemMoveComponent);
comp = fixture.componentInstance; comp = fixture.componentInstance;
fixture.detectChanges(); fixture.detectChanges();
@@ -141,23 +136,20 @@ describe('ItemMoveComponent', () => {
}); });
describe('ItemMoveComponent fail', () => { describe('ItemMoveComponent fail', () => {
beforeEach(async(() => { beforeEach(() => {
TestBed.configureTestingModule({ TestBed.configureTestingModule({
imports: [CommonModule, FormsModule, RouterTestingModule.withRoutes([]), TranslateModule.forRoot(), NgbModule.forRoot()], imports: [CommonModule, FormsModule, RouterTestingModule.withRoutes([]), TranslateModule.forRoot(), NgbModule],
declarations: [ItemMoveComponent], declarations: [ItemMoveComponent],
providers: [ providers: [
{provide: ActivatedRoute, useValue: routeStub}, { provide: ActivatedRoute, useValue: routeStub },
{provide: Router, useValue: routerStub}, { provide: Router, useValue: routerStub },
{provide: ItemDataService, useValue: mockItemDataServiceFail}, { provide: ItemDataService, useValue: mockItemDataServiceFail },
{provide: NotificationsService, useValue: notificationsServiceStub}, { provide: NotificationsService, useValue: notificationsServiceStub },
{provide: SearchService, useValue: mockSearchService}, { provide: SearchService, useValue: mockSearchService },
], schemas: [ ], schemas: [
CUSTOM_ELEMENTS_SCHEMA CUSTOM_ELEMENTS_SCHEMA
] ]
}).compileComponents(); }).compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(ItemMoveComponent); fixture = TestBed.createComponent(ItemMoveComponent);
comp = fixture.componentInstance; comp = fixture.componentInstance;
fixture.detectChanges(); fixture.detectChanges();

View File

@@ -60,7 +60,7 @@ describe('ItemPrivateComponent', () => {
notificationsServiceStub = new NotificationsServiceStub(); notificationsServiceStub = new NotificationsServiceStub();
TestBed.configureTestingModule({ TestBed.configureTestingModule({
imports: [CommonModule, FormsModule, RouterTestingModule.withRoutes([]), TranslateModule.forRoot(), NgbModule.forRoot()], imports: [CommonModule, FormsModule, RouterTestingModule.withRoutes([]), TranslateModule.forRoot(), NgbModule],
declarations: [ItemPrivateComponent], declarations: [ItemPrivateComponent],
providers: [ providers: [
{ provide: ActivatedRoute, useValue: routeStub }, { provide: ActivatedRoute, useValue: routeStub },

View File

@@ -60,7 +60,7 @@ describe('ItemPublicComponent', () => {
notificationsServiceStub = new NotificationsServiceStub(); notificationsServiceStub = new NotificationsServiceStub();
TestBed.configureTestingModule({ TestBed.configureTestingModule({
imports: [CommonModule, FormsModule, RouterTestingModule.withRoutes([]), TranslateModule.forRoot(), NgbModule.forRoot()], imports: [CommonModule, FormsModule, RouterTestingModule.withRoutes([]), TranslateModule.forRoot(), NgbModule],
declarations: [ItemPublicComponent], declarations: [ItemPublicComponent],
providers: [ providers: [
{ provide: ActivatedRoute, useValue: routeStub }, { provide: ActivatedRoute, useValue: routeStub },

View File

@@ -60,7 +60,7 @@ describe('ItemReinstateComponent', () => {
notificationsServiceStub = new NotificationsServiceStub(); notificationsServiceStub = new NotificationsServiceStub();
TestBed.configureTestingModule({ TestBed.configureTestingModule({
imports: [CommonModule, FormsModule, RouterTestingModule.withRoutes([]), TranslateModule.forRoot(), NgbModule.forRoot()], imports: [CommonModule, FormsModule, RouterTestingModule.withRoutes([]), TranslateModule.forRoot(), NgbModule],
declarations: [ItemReinstateComponent], declarations: [ItemReinstateComponent],
providers: [ providers: [
{ provide: ActivatedRoute, useValue: routeStub }, { provide: ActivatedRoute, useValue: routeStub },

View File

@@ -1,19 +1,22 @@
import {EditRelationshipListComponent} from './edit-relationship-list.component'; import { DebugElement, NO_ERRORS_SCHEMA } from '@angular/core';
import {async, ComponentFixture, TestBed} from '@angular/core/testing'; import { ComponentFixture, TestBed } from '@angular/core/testing';
import {RelationshipType} from '../../../../core/shared/item-relationships/relationship-type.model'; import { By } from '@angular/platform-browser';
import {Relationship} from '../../../../core/shared/item-relationships/relationship.model'; import { TranslateModule } from '@ngx-translate/core';
import {of as observableOf} from 'rxjs/internal/observable/of'; import { of as observableOf } from 'rxjs/internal/observable/of';
import {RemoteData} from '../../../../core/data/remote-data'; import { LinkService } from '../../../../core/cache/builders/link.service';
import {Item} from '../../../../core/shared/item.model'; import { FieldChangeType } from '../../../../core/data/object-updates/object-updates.actions';
import {PaginatedList} from '../../../../core/data/paginated-list'; import { ObjectUpdatesService } from '../../../../core/data/object-updates/object-updates.service';
import {PageInfo} from '../../../../core/shared/page-info.model'; import { PaginatedList } from '../../../../core/data/paginated-list';
import {FieldChangeType} from '../../../../core/data/object-updates/object-updates.actions'; import { RelationshipTypeService } from '../../../../core/data/relationship-type.service';
import {SharedModule} from '../../../../shared/shared.module'; import { RemoteData } from '../../../../core/data/remote-data';
import {TranslateModule} from '@ngx-translate/core'; import { ItemType } from '../../../../core/shared/item-relationships/item-type.model';
import {ObjectUpdatesService} from '../../../../core/data/object-updates/object-updates.service'; import { RelationshipType } from '../../../../core/shared/item-relationships/relationship-type.model';
import {DebugElement, NO_ERRORS_SCHEMA} from '@angular/core'; import { Relationship } from '../../../../core/shared/item-relationships/relationship.model';
import {By} from '@angular/platform-browser'; import { Item } from '../../../../core/shared/item.model';
import {ItemType} from '../../../../core/shared/item-relationships/item-type.model'; import { PageInfo } from '../../../../core/shared/page-info.model';
import { getMockLinkService } from '../../../../shared/mocks/mock-link-service';
import { SharedModule } from '../../../../shared/shared.module';
import { EditRelationshipListComponent } from './edit-relationship-list.component';
let comp: EditRelationshipListComponent; let comp: EditRelationshipListComponent;
let fixture: ComponentFixture<EditRelationshipListComponent>; let fixture: ComponentFixture<EditRelationshipListComponent>;
@@ -57,7 +60,11 @@ describe('EditRelationshipListComponent', () => {
}); });
relationship1 = Object.assign(new Relationship(), { relationship1 = Object.assign(new Relationship(), {
self: url + '/2', _links: {
self: {
href: url + '/2'
}
},
id: '2', id: '2',
uuid: '2', uuid: '2',
leftId: 'author1', leftId: 'author1',
@@ -68,7 +75,11 @@ describe('EditRelationshipListComponent', () => {
}); });
relationship2 = Object.assign(new Relationship(), { relationship2 = Object.assign(new Relationship(), {
self: url + '/3', _links: {
self: {
href: url + '/3'
}
},
id: '3', id: '3',
uuid: '3', uuid: '3',
leftId: 'author2', leftId: 'author2',
@@ -79,7 +90,9 @@ describe('EditRelationshipListComponent', () => {
}); });
item = Object.assign(new Item(), { item = Object.assign(new Item(), {
self: 'fake-item-url/publication', _links: {
self: { href: 'fake-item-url/publication' }
},
id: 'publication', id: 'publication',
uuid: 'publication', uuid: 'publication',
relationships: observableOf(new RemoteData( relationships: observableOf(new RemoteData(
@@ -142,6 +155,8 @@ describe('EditRelationshipListComponent', () => {
declarations: [EditRelationshipListComponent], declarations: [EditRelationshipListComponent],
providers: [ providers: [
{ provide: ObjectUpdatesService, useValue: objectUpdatesService }, { provide: ObjectUpdatesService, useValue: objectUpdatesService },
{ provide: RelationshipTypeService, useValue: {} },
{ provide: LinkService, useValue: getMockLinkService() },
], schemas: [ ], schemas: [
NO_ERRORS_SCHEMA NO_ERRORS_SCHEMA
] ]

View File

@@ -1,15 +1,21 @@
import { Component, Input, OnInit } from '@angular/core'; import { Component, Input, OnInit } from '@angular/core';
import { LinkService } from '../../../../core/cache/builders/link.service';
import { ObjectUpdatesService } from '../../../../core/data/object-updates/object-updates.service'; import { ObjectUpdatesService } from '../../../../core/data/object-updates/object-updates.service';
import { Observable } from 'rxjs/internal/Observable'; import { Observable } from 'rxjs/internal/Observable';
import {FieldUpdate, FieldUpdates} from '../../../../core/data/object-updates/object-updates.reducer'; import {FieldUpdate, FieldUpdates} from '../../../../core/data/object-updates/object-updates.reducer';
import {Item} from '../../../../core/shared/item.model'; import {Item} from '../../../../core/shared/item.model';
import {map, switchMap} from 'rxjs/operators'; import { map, switchMap, tap } from 'rxjs/operators';
import {hasValue} from '../../../../shared/empty.util'; import {hasValue} from '../../../../shared/empty.util';
import {Relationship} from '../../../../core/shared/item-relationships/relationship.model'; import {Relationship} from '../../../../core/shared/item-relationships/relationship.model';
import {RelationshipType} from '../../../../core/shared/item-relationships/relationship-type.model'; import {RelationshipType} from '../../../../core/shared/item-relationships/relationship-type.model';
import {getRemoteDataPayload, getSucceededRemoteData} from '../../../../core/shared/operators'; import {
import {combineLatest as observableCombineLatest, combineLatest} from 'rxjs'; getAllSucceededRemoteData,
import {ItemType} from '../../../../core/shared/item-relationships/item-type.model'; getRemoteDataPayload,
getSucceededRemoteData
} from '../../../../core/shared/operators';
import { combineLatest as observableCombineLatest } from 'rxjs';
import { ItemType } from '../../../../core/shared/item-relationships/item-type.model';
import { followLink } from '../../../../shared/utils/follow-link-config.model';
@Component({ @Component({
selector: 'ds-edit-relationship-list', selector: 'ds-edit-relationship-list',
@@ -47,6 +53,7 @@ export class EditRelationshipListComponent implements OnInit {
constructor( constructor(
protected objectUpdatesService: ObjectUpdatesService, protected objectUpdatesService: ObjectUpdatesService,
protected linkService: LinkService
) { ) {
} }
@@ -71,7 +78,7 @@ export class EditRelationshipListComponent implements OnInit {
*/ */
private getLabel(): Observable<string> { private getLabel(): Observable<string> {
return combineLatest([ return observableCombineLatest([
this.relationshipType.leftType, this.relationshipType.leftType,
this.relationshipType.rightType, this.relationshipType.rightType,
].map((itemTypeRD) => itemTypeRD.pipe( ].map((itemTypeRD) => itemTypeRD.pipe(
@@ -94,8 +101,20 @@ export class EditRelationshipListComponent implements OnInit {
ngOnInit(): void { ngOnInit(): void {
this.updates$ = this.item.relationships.pipe( this.updates$ = this.item.relationships.pipe(
getAllSucceededRemoteData(),
map((relationships) => relationships.payload.page.filter((relationship) => relationship)), map((relationships) => relationships.payload.page.filter((relationship) => relationship)),
switchMap((itemRelationships) => map((relationships: Relationship[]) =>
relationships.map((relationship: Relationship) => {
this.linkService.resolveLinks(
relationship,
followLink('relationshipType'),
followLink('leftItem'),
followLink('rightItem'),
);
return relationship;
})
),
switchMap((itemRelationships: Relationship[]) =>
observableCombineLatest( observableCombineLatest(
itemRelationships itemRelationships
.map((relationship) => relationship.relationshipType.pipe( .map((relationship) => relationship.relationshipType.pipe(

View File

@@ -1,16 +1,16 @@
import { async, TestBed } from '@angular/core/testing';
import { of as observableOf } from 'rxjs/internal/observable/of';
import { TranslateModule } from '@ngx-translate/core';
import { ObjectUpdatesService } from '../../../../core/data/object-updates/object-updates.service';
import { NO_ERRORS_SCHEMA } from '@angular/core'; import { NO_ERRORS_SCHEMA } from '@angular/core';
import { EditRelationshipComponent } from './edit-relationship.component'; import { async, TestBed } from '@angular/core/testing';
import { TranslateModule } from '@ngx-translate/core';
import { of as observableOf } from 'rxjs/internal/observable/of';
import { FieldChangeType } from '../../../../core/data/object-updates/object-updates.actions';
import { ObjectUpdatesService } from '../../../../core/data/object-updates/object-updates.service';
import { PaginatedList } from '../../../../core/data/paginated-list';
import { RemoteData } from '../../../../core/data/remote-data';
import { RelationshipType } from '../../../../core/shared/item-relationships/relationship-type.model'; import { RelationshipType } from '../../../../core/shared/item-relationships/relationship-type.model';
import { Relationship } from '../../../../core/shared/item-relationships/relationship.model'; import { Relationship } from '../../../../core/shared/item-relationships/relationship.model';
import { RemoteData } from '../../../../core/data/remote-data';
import { Item } from '../../../../core/shared/item.model'; import { Item } from '../../../../core/shared/item.model';
import { PaginatedList } from '../../../../core/data/paginated-list';
import { PageInfo } from '../../../../core/shared/page-info.model'; import { PageInfo } from '../../../../core/shared/page-info.model';
import { FieldChangeType } from '../../../../core/data/object-updates/object-updates.actions'; import { EditRelationshipComponent } from './edit-relationship.component';
import { NgbModal } from '@ng-bootstrap/ng-bootstrap'; import { NgbModal } from '@ng-bootstrap/ng-bootstrap';
let objectUpdatesService; let objectUpdatesService;
@@ -42,7 +42,11 @@ describe('EditRelationshipComponent', () => {
}); });
item = Object.assign(new Item(), { item = Object.assign(new Item(), {
self: 'fake-item-url/publication', _links: {
self: {
href: 'fake-item-url/publication'
}
},
id: 'publication', id: 'publication',
uuid: 'publication', uuid: 'publication',
relationships: observableOf(new RemoteData(false, false, true, undefined, new PaginatedList(new PageInfo(), relationships))) relationships: observableOf(new RemoteData(false, false, true, undefined, new PaginatedList(new PageInfo(), relationships)))
@@ -54,7 +58,9 @@ describe('EditRelationshipComponent', () => {
relationships = [ relationships = [
Object.assign(new Relationship(), { Object.assign(new Relationship(), {
self: url + '/2', _links: {
self: { href: url + '/2' }
},
id: '2', id: '2',
uuid: '2', uuid: '2',
leftId: 'author1', leftId: 'author1',
@@ -64,7 +70,9 @@ describe('EditRelationshipComponent', () => {
rightItem: observableOf(new RemoteData(false, false, true, undefined, item)), rightItem: observableOf(new RemoteData(false, false, true, undefined, item)),
}), }),
Object.assign(new Relationship(), { Object.assign(new Relationship(), {
self: url + '/3', _links: {
self: { href: url + '/3' }
},
id: '3', id: '3',
uuid: '3', uuid: '3',
leftId: 'author2', leftId: 'author2',

View File

@@ -1,32 +1,35 @@
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { ItemRelationshipsComponent } from './item-relationships.component';
import { ChangeDetectorRef, DebugElement, NO_ERRORS_SCHEMA } from '@angular/core'; import { ChangeDetectorRef, DebugElement, NO_ERRORS_SCHEMA } from '@angular/core';
import { INotification, Notification } from '../../../shared/notifications/models/notification.model'; import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { NotificationType } from '../../../shared/notifications/models/notification-type';
import { RouterStub } from '../../../shared/testing/router-stub';
import { TestScheduler } from 'rxjs/testing';
import { SharedModule } from '../../../shared/shared.module';
import { TranslateModule } from '@ngx-translate/core';
import { ItemDataService } from '../../../core/data/item-data.service';
import { ObjectUpdatesService } from '../../../core/data/object-updates/object-updates.service';
import { ActivatedRoute, Router } from '@angular/router'; import { ActivatedRoute, Router } from '@angular/router';
import { NotificationsService } from '../../../shared/notifications/notifications.service'; import { TranslateModule } from '@ngx-translate/core';
import { getTestScheduler } from 'jasmine-marbles';
import { combineLatest as observableCombineLatest, of as observableOf } from 'rxjs';
import { TestScheduler } from 'rxjs/testing';
import { GLOBAL_CONFIG } from '../../../../config'; import { GLOBAL_CONFIG } from '../../../../config';
import { ObjectCacheService } from '../../../core/cache/object-cache.service';
import { RestResponse } from '../../../core/cache/response.models';
import { EntityTypeService } from '../../../core/data/entity-type.service';
import { ItemDataService } from '../../../core/data/item-data.service';
import { FieldChangeType } from '../../../core/data/object-updates/object-updates.actions';
import { ObjectUpdatesService } from '../../../core/data/object-updates/object-updates.service';
import { PaginatedList } from '../../../core/data/paginated-list';
import { RelationshipService } from '../../../core/data/relationship.service';
import { RemoteData } from '../../../core/data/remote-data';
import { RequestService } from '../../../core/data/request.service';
import { ItemType } from '../../../core/shared/item-relationships/item-type.model';
import { RelationshipType } from '../../../core/shared/item-relationships/relationship-type.model'; import { RelationshipType } from '../../../core/shared/item-relationships/relationship-type.model';
import { Relationship } from '../../../core/shared/item-relationships/relationship.model'; import { Relationship } from '../../../core/shared/item-relationships/relationship.model';
import { combineLatest as observableCombineLatest, of as observableOf } from 'rxjs';
import { RemoteData } from '../../../core/data/remote-data';
import { Item } from '../../../core/shared/item.model'; import { Item } from '../../../core/shared/item.model';
import { PaginatedList } from '../../../core/data/paginated-list';
import { PageInfo } from '../../../core/shared/page-info.model'; import { PageInfo } from '../../../core/shared/page-info.model';
import { FieldChangeType } from '../../../core/data/object-updates/object-updates.actions'; import { NotificationType } from '../../../shared/notifications/models/notification-type';
import { RelationshipService } from '../../../core/data/relationship.service'; import {
import { ObjectCacheService } from '../../../core/cache/object-cache.service'; INotification,
import { getTestScheduler } from 'jasmine-marbles'; Notification
import { RestResponse } from '../../../core/cache/response.models'; } from '../../../shared/notifications/models/notification.model';
import { RequestService } from '../../../core/data/request.service'; import { NotificationsService } from '../../../shared/notifications/notifications.service';
import { EntityTypeService } from '../../../core/data/entity-type.service'; import { SharedModule } from '../../../shared/shared.module';
import { ItemType } from '../../../core/shared/item-relationships/item-type.model'; import { RouterStub } from '../../../shared/testing/router-stub';
import { ItemRelationshipsComponent } from './item-relationships.component';
let comp: any; let comp: any;
let fixture: ComponentFixture<ItemRelationshipsComponent>; let fixture: ComponentFixture<ItemRelationshipsComponent>;
@@ -77,13 +80,17 @@ describe('ItemRelationshipsComponent', () => {
relationships = [ relationships = [
Object.assign(new Relationship(), { Object.assign(new Relationship(), {
self: url + '/2', _links: {
self: { href: url + '/2' }
},
id: '2', id: '2',
uuid: '2', uuid: '2',
relationshipType: observableOf(new RemoteData(false, false, true, undefined, relationshipType)) relationshipType: observableOf(new RemoteData(false, false, true, undefined, relationshipType))
}), }),
Object.assign(new Relationship(), { Object.assign(new Relationship(), {
self: url + '/3', _links: {
self: { href: url + '/3' }
},
id: '3', id: '3',
uuid: '3', uuid: '3',
relationshipType: observableOf(new RemoteData(false, false, true, undefined, relationshipType)) relationshipType: observableOf(new RemoteData(false, false, true, undefined, relationshipType))
@@ -91,7 +98,9 @@ describe('ItemRelationshipsComponent', () => {
]; ];
item = Object.assign(new Item(), { item = Object.assign(new Item(), {
self: 'fake-item-url/publication', _links: {
self: { href: 'fake-item-url/publication' }
},
id: 'publication', id: 'publication',
uuid: 'publication', uuid: 'publication',
relationships: observableOf(new RemoteData(false, false, true, undefined, new PaginatedList(new PageInfo(), relationships))), relationships: observableOf(new RemoteData(false, false, true, undefined, new PaginatedList(new PageInfo(), relationships))),

View File

@@ -4,6 +4,7 @@ import { DeleteRelationship, FieldUpdate, FieldUpdates } from '../../../core/dat
import { Observable } from 'rxjs/internal/Observable'; import { Observable } from 'rxjs/internal/Observable';
import { filter, map, switchMap, take } from 'rxjs/operators'; import { filter, map, switchMap, take } from 'rxjs/operators';
import { zip as observableZip } from 'rxjs'; import { zip as observableZip } from 'rxjs';
import { followLink } from '../../../shared/utils/follow-link-config.model';
import { AbstractItemUpdateComponent } from '../abstract-item-update/abstract-item-update.component'; import { AbstractItemUpdateComponent } from '../abstract-item-update/abstract-item-update.component';
import { ItemDataService } from '../../../core/data/item-data.service'; import { ItemDataService } from '../../../core/data/item-data.service';
import { ObjectUpdatesService } from '../../../core/data/object-updates/object-updates.service'; import { ObjectUpdatesService } from '../../../core/data/object-updates/object-updates.service';
@@ -71,7 +72,10 @@ export class ItemRelationshipsComponent extends AbstractItemUpdateComponent impl
super.ngOnInit(); super.ngOnInit();
this.itemUpdateSubscription = this.requestService.hasByHrefObservable(this.item.self).pipe( this.itemUpdateSubscription = this.requestService.hasByHrefObservable(this.item.self).pipe(
filter((exists: boolean) => !exists), filter((exists: boolean) => !exists),
switchMap(() => this.itemService.findById(this.item.uuid)), switchMap(() => this.itemService.findById(this.item.uuid,
followLink('owningCollection'),
followLink('bundles'),
followLink('relationships'))),
getSucceededRemoteData(), getSucceededRemoteData(),
).subscribe((itemRD: RemoteData<Item>) => { ).subscribe((itemRD: RemoteData<Item>) => {
this.item = itemRD.payload; this.item = itemRD.payload;
@@ -94,7 +98,11 @@ export class ItemRelationshipsComponent extends AbstractItemUpdateComponent impl
this.relationshipTypes$ = this.entityType$.pipe( this.relationshipTypes$ = this.entityType$.pipe(
switchMap((entityType) => switchMap((entityType) =>
this.entityTypeService.getEntityTypeRelationships(entityType.id).pipe( this.entityTypeService.getEntityTypeRelationships(
entityType.id,
followLink('leftType'),
followLink('rightType'))
.pipe(
getSucceededRemoteData(), getSucceededRemoteData(),
getRemoteDataPayload(), getRemoteDataPayload(),
map((relationshipTypes) => relationshipTypes.page), map((relationshipTypes) => relationshipTypes.page),

View File

@@ -33,7 +33,7 @@ describe('ItemStatusComponent', () => {
beforeEach(async(() => { beforeEach(async(() => {
TestBed.configureTestingModule({ TestBed.configureTestingModule({
imports: [CommonModule, RouterTestingModule.withRoutes([]), TranslateModule.forRoot(), NgbModule.forRoot()], imports: [CommonModule, RouterTestingModule.withRoutes([]), TranslateModule.forRoot(), NgbModule],
declarations: [ItemStatusComponent], declarations: [ItemStatusComponent],
providers: [ providers: [
{ provide: ActivatedRoute, useValue: routeStub }, { provide: ActivatedRoute, useValue: routeStub },

View File

@@ -60,7 +60,7 @@ describe('ItemWithdrawComponent', () => {
notificationsServiceStub = new NotificationsServiceStub(); notificationsServiceStub = new NotificationsServiceStub();
TestBed.configureTestingModule({ TestBed.configureTestingModule({
imports: [CommonModule, FormsModule, RouterTestingModule.withRoutes([]), TranslateModule.forRoot(), NgbModule.forRoot(),], imports: [CommonModule, FormsModule, RouterTestingModule.withRoutes([]), TranslateModule.forRoot(), NgbModule,],
declarations: [ItemWithdrawComponent], declarations: [ItemWithdrawComponent],
providers: [ providers: [
{ provide: ActivatedRoute, useValue: routeStub }, { provide: ActivatedRoute, useValue: routeStub },

View File

@@ -83,7 +83,7 @@ describe('AbstractSimpleItemActionComponent', () => {
notificationsServiceStub = new NotificationsServiceStub(); notificationsServiceStub = new NotificationsServiceStub();
TestBed.configureTestingModule({ TestBed.configureTestingModule({
imports: [CommonModule, FormsModule, RouterTestingModule.withRoutes([]), TranslateModule.forRoot(), NgbModule.forRoot()], imports: [CommonModule, FormsModule, RouterTestingModule.withRoutes([]), TranslateModule.forRoot(), NgbModule],
declarations: [MySimpleItemActionComponent], declarations: [MySimpleItemActionComponent],
providers: [ providers: [
{ provide: ActivatedRoute, useValue: routeStub }, { provide: ActivatedRoute, useValue: routeStub },

View File

@@ -1,6 +1,6 @@
<ds-metadata-field-wrapper *ngIf="hasSucceeded() | async" [label]="label | translate"> <ds-metadata-field-wrapper *ngIf="(this.collectionsRD$ | async)?.hasSucceeded" [label]="label | translate">
<div class="collections"> <div class="collections">
<a *ngFor="let collection of (collections | async); let last=last;" [routerLink]="['/collections', collection.id]"> <a *ngFor="let collection of (this.collectionsRD$ | async)?.payload?.page; let last=last;" [routerLink]="['/collections', collection.id]">
<span>{{collection?.name}}</span><span *ngIf="!last" [innerHTML]="separator"></span> <span>{{collection?.name}}</span><span *ngIf="!last" [innerHTML]="separator"></span>
</a> </a>
</div> </div>

View File

@@ -1,22 +1,20 @@
import { CollectionsComponent } from './collections.component';
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { ChangeDetectionStrategy, NO_ERRORS_SCHEMA } from '@angular/core'; import { ChangeDetectionStrategy, NO_ERRORS_SCHEMA } from '@angular/core';
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { By } from '@angular/platform-browser'; import { By } from '@angular/platform-browser';
import { Collection } from '../../../core/shared/collection.model';
import { RemoteDataBuildService } from '../../../core/cache/builders/remote-data-build.service';
import { getMockRemoteDataBuildService } from '../../../shared/mocks/mock-remote-data-build.service';
import { Item } from '../../../core/shared/item.model';
import { of as observableOf } from 'rxjs';
import { RemoteData } from '../../../core/data/remote-data';
import { TranslateModule } from '@ngx-translate/core'; import { TranslateModule } from '@ngx-translate/core';
import { import { RemoteDataBuildService } from '../../../core/cache/builders/remote-data-build.service';
createFailedRemoteDataObject$, import { CollectionDataService } from '../../../core/data/collection-data.service';
createSuccessfulRemoteDataObject$ import { Collection } from '../../../core/shared/collection.model';
} from '../../../shared/testing/utils'; import { Item } from '../../../core/shared/item.model';
import { getMockRemoteDataBuildService } from '../../../shared/mocks/mock-remote-data-build.service';
import { createFailedRemoteDataObject$, createSuccessfulRemoteDataObject$ } from '../../../shared/testing/utils';
import { CollectionsComponent } from './collections.component';
let collectionsComponent: CollectionsComponent; let collectionsComponent: CollectionsComponent;
let fixture: ComponentFixture<CollectionsComponent>; let fixture: ComponentFixture<CollectionsComponent>;
let collectionDataServiceStub;
const mockCollection1: Collection = Object.assign(new Collection(), { const mockCollection1: Collection = Object.assign(new Collection(), {
metadata: { metadata: {
'dc.description.abstract': [ 'dc.description.abstract': [
@@ -32,12 +30,22 @@ const succeededMockItem: Item = Object.assign(new Item(), {owningCollection: cre
const failedMockItem: Item = Object.assign(new Item(), {owningCollection: createFailedRemoteDataObject$(mockCollection1)}); const failedMockItem: Item = Object.assign(new Item(), {owningCollection: createFailedRemoteDataObject$(mockCollection1)});
describe('CollectionsComponent', () => { describe('CollectionsComponent', () => {
collectionDataServiceStub = {
findOwningCollectionFor(item: Item) {
if (item === succeededMockItem) {
return createSuccessfulRemoteDataObject$(mockCollection1);
} else {
return createFailedRemoteDataObject$(mockCollection1);
}
}
};
beforeEach(async(() => { beforeEach(async(() => {
TestBed.configureTestingModule({ TestBed.configureTestingModule({
imports: [TranslateModule.forRoot()], imports: [TranslateModule.forRoot()],
declarations: [ CollectionsComponent ], declarations: [ CollectionsComponent ],
providers: [ providers: [
{ provide: RemoteDataBuildService, useValue: getMockRemoteDataBuildService()} { provide: RemoteDataBuildService, useValue: getMockRemoteDataBuildService()},
{ provide: CollectionDataService, useValue: collectionDataServiceStub },
], ],
schemas: [ NO_ERRORS_SCHEMA ] schemas: [ NO_ERRORS_SCHEMA ]

View File

@@ -1,12 +1,13 @@
import {map} from 'rxjs/operators';
import { Component, Input, OnInit } from '@angular/core'; import { Component, Input, OnInit } from '@angular/core';
import { Observable } from 'rxjs'; import { Observable } from 'rxjs';
import { map } from 'rxjs/operators';
import { CollectionDataService } from '../../../core/data/collection-data.service';
import { PaginatedList } from '../../../core/data/paginated-list';
import { RemoteData } from '../../../core/data/remote-data';
import { Collection } from '../../../core/shared/collection.model'; import { Collection } from '../../../core/shared/collection.model';
import { Item } from '../../../core/shared/item.model'; import { Item } from '../../../core/shared/item.model';
import { RemoteDataBuildService } from '../../../core/cache/builders/remote-data-build.service'; import { PageInfo } from '../../../core/shared/page-info.model';
import { RemoteData } from '../../../core/data/remote-data';
/** /**
* This component renders the parent collections section of the item * This component renders the parent collections section of the item
@@ -25,9 +26,9 @@ export class CollectionsComponent implements OnInit {
separator = '<br/>'; separator = '<br/>';
collections: Observable<Collection[]>; collectionsRD$: Observable<RemoteData<PaginatedList<Collection>>>;
constructor(private rdbs: RemoteDataBuildService) { constructor(private cds: CollectionDataService) {
} }
@@ -37,11 +38,25 @@ export class CollectionsComponent implements OnInit {
// TODO: this should use parents, but the collections // TODO: this should use parents, but the collections
// for an Item aren't returned by the REST API yet, // for an Item aren't returned by the REST API yet,
// only the owning collection // only the owning collection
this.collections = this.item.owner.pipe(map((rd: RemoteData<Collection>) => [rd.payload])); this.collectionsRD$ = this.cds.findOwningCollectionFor(this.item).pipe(
map((rd: RemoteData<Collection>) => {
if (rd.hasSucceeded) {
return new RemoteData(
false,
false,
true,
undefined,
new PaginatedList({
elementsPerPage: 10,
totalPages: 1,
currentPage: 1,
totalElements: 1
} as PageInfo, [rd.payload])
);
} else {
return rd as any;
}
})
);
} }
hasSucceeded() {
return this.item.owner.pipe(map((rd: RemoteData<Collection>) => rd.hasSucceeded));
}
} }

View File

@@ -1,7 +1,7 @@
<ds-metadata-field-wrapper [label]="label | translate"> <ds-metadata-field-wrapper [label]="label | translate">
<div class="file-section row" *ngFor="let file of (bitstreamsObs | async); let last=last;"> <div class="file-section row" *ngFor="let file of (bitstreams$ | async); let last=last;">
<div class="col-3"> <div class="col-3">
<ds-thumbnail [thumbnail]="thumbnails.get(file.id) | async"></ds-thumbnail> <ds-thumbnail [thumbnail]="(file.thumbnail | async)?.payload"></ds-thumbnail>
</div> </div>
<div class="col-7"> <div class="col-7">
<dl class="row"> <dl class="row">
@@ -21,7 +21,7 @@
</dl> </dl>
</div> </div>
<div class="col-2"> <div class="col-2">
<a [href]="file.content" [download]="file.name"> <a [href]="file._links.content.href" [download]="file.name">
{{"item.page.filesection.download" | translate}} {{"item.page.filesection.download" | translate}}
</a> </a>
</div> </div>

View File

@@ -1,10 +1,13 @@
import { Component, Injector, Input, OnInit } from '@angular/core';
import { combineLatest as observableCombineLatest, Observable } from 'rxjs'; import { combineLatest as observableCombineLatest, Observable } from 'rxjs';
import { Component, Input, OnInit } from '@angular/core'; import { map, startWith } from 'rxjs/operators';
import { BitstreamDataService } from '../../../../core/data/bitstream-data.service';
import { Bitstream } from '../../../../core/shared/bitstream.model'; import { Bitstream } from '../../../../core/shared/bitstream.model';
import { Item } from '../../../../core/shared/item.model'; import { Item } from '../../../../core/shared/item.model';
import { getFirstSucceededRemoteListPayload } from '../../../../core/shared/operators';
import { followLink } from '../../../../shared/utils/follow-link-config.model';
import { FileSectionComponent } from '../../../simple/field-components/file-section/file-section.component'; import { FileSectionComponent } from '../../../simple/field-components/file-section/file-section.component';
import { map } from 'rxjs/operators';
/** /**
* This component renders the file section of the item * This component renders the file section of the item
@@ -22,27 +25,49 @@ export class FullFileSectionComponent extends FileSectionComponent implements On
label: string; label: string;
bitstreamsObs: Observable<Bitstream[]>; bitstreams$: Observable<Bitstream[]>;
thumbnails: Map<string, Observable<Bitstream>> = new Map(); constructor(
bitstreamDataService: BitstreamDataService
) {
super(bitstreamDataService);
}
ngOnInit(): void { ngOnInit(): void {
super.ngOnInit(); super.ngOnInit();
} }
initialize(): void { initialize(): void {
const originals = this.item.getFiles(); // TODO pagination
const licenses = this.item.getBitstreamsByBundleName('LICENSE'); const originals$ = this.bitstreamDataService.findAllByItemAndBundleName(
this.bitstreamsObs = observableCombineLatest(originals, licenses).pipe(map(([o, l]) => [...o, ...l])); this.item,
this.bitstreamsObs.subscribe( 'ORIGINAL',
(files) => { elementsPerPage: Number.MAX_SAFE_INTEGER },
files.forEach( followLink( 'format')
).pipe(
getFirstSucceededRemoteListPayload(),
startWith([])
);
const licenses$ = this.bitstreamDataService.findAllByItemAndBundleName(
this.item,
'LICENSE',
{ elementsPerPage: Number.MAX_SAFE_INTEGER },
followLink( 'format')
).pipe(
getFirstSucceededRemoteListPayload(),
startWith([])
);
this.bitstreams$ = observableCombineLatest(originals$, licenses$).pipe(
map(([o, l]) => [...o, ...l]),
map((files: Bitstream[]) =>
files.map(
(original) => { (original) => {
const thumbnail: Observable<Bitstream> = this.item.getThumbnailForOriginal(original); original.thumbnail = this.bitstreamDataService.getMatchingThumbnail(this.item, original);
this.thumbnails.set(original.id, thumbnail); return original;
} }
) )
) )
);
} }
} }

View File

@@ -7,54 +7,62 @@ import { ItemPageResolver } from './item-page.resolver';
import { URLCombiner } from '../core/url-combiner/url-combiner'; import { URLCombiner } from '../core/url-combiner/url-combiner';
import { getItemModulePath } from '../app-routing.module'; import { getItemModulePath } from '../app-routing.module';
import { AuthenticatedGuard } from '../core/auth/authenticated.guard'; import { AuthenticatedGuard } from '../core/auth/authenticated.guard';
import { ItemBreadcrumbResolver } from '../core/breadcrumbs/item-breadcrumb.resolver';
import { DSOBreadcrumbsService } from '../core/breadcrumbs/dso-breadcrumbs.service';
import { LinkService } from '../core/cache/builders/link.service';
import { UploadBitstreamComponent } from './bitstreams/upload/upload-bitstream.component'; import { UploadBitstreamComponent } from './bitstreams/upload/upload-bitstream.component';
export function getItemPageRoute(itemId: string) { export function getItemPageRoute(itemId: string) {
return new URLCombiner(getItemModulePath(), itemId).toString(); return new URLCombiner(getItemModulePath(), itemId).toString();
} }
export function getItemEditPath(id: string) { export function getItemEditPath(id: string) {
return new URLCombiner(getItemModulePath(),ITEM_EDIT_PATH.replace(/:id/, id)).toString() return new URLCombiner(getItemModulePath(), id, ITEM_EDIT_PATH).toString()
} }
const ITEM_EDIT_PATH = ':id/edit'; const ITEM_EDIT_PATH = 'edit';
const UPLOAD_BITSTREAM_PATH = ':id/bitstreams/new'; const UPLOAD_BITSTREAM_PATH = 'bitstreams/new';
@NgModule({ @NgModule({
imports: [ imports: [
RouterModule.forChild([ RouterModule.forChild([
{ {
path: ':id', path: ':id',
component: ItemPageComponent,
pathMatch: 'full',
resolve: { resolve: {
item: ItemPageResolver item: ItemPageResolver,
} breadcrumb: ItemBreadcrumbResolver
},
{
path: ':id/full',
component: FullItemPageComponent,
resolve: {
item: ItemPageResolver
}
},
{
path: ITEM_EDIT_PATH,
loadChildren: './edit-item-page/edit-item-page.module#EditItemPageModule',
canActivate: [AuthenticatedGuard]
},
{
path: UPLOAD_BITSTREAM_PATH,
component: UploadBitstreamComponent,
resolve: {
item: ItemPageResolver
}, },
canActivate: [AuthenticatedGuard] children: [
{
path: '',
component: ItemPageComponent,
pathMatch: 'full',
},
{
path: 'full',
component: FullItemPageComponent,
},
{
path: ITEM_EDIT_PATH,
loadChildren: './edit-item-page/edit-item-page.module#EditItemPageModule',
canActivate: [AuthenticatedGuard]
},
{
path: UPLOAD_BITSTREAM_PATH,
component: UploadBitstreamComponent,
canActivate: [AuthenticatedGuard]
}
],
} }
]) ])
], ],
providers: [ providers: [
ItemPageResolver, ItemPageResolver,
ItemBreadcrumbResolver,
DSOBreadcrumbsService,
LinkService
] ]
}) })
export class ItemPageRoutingModule { export class ItemPageRoutingModule {

View File

@@ -6,6 +6,7 @@ import { ItemDataService } from '../core/data/item-data.service';
import { Item } from '../core/shared/item.model'; import { Item } from '../core/shared/item.model';
import { hasValue } from '../shared/empty.util'; import { hasValue } from '../shared/empty.util';
import { find } from 'rxjs/operators'; import { find } from 'rxjs/operators';
import { followLink } from '../shared/utils/follow-link-config.model';
/** /**
* This class represents a resolver that requests a specific item before the route is activated * This class represents a resolver that requests a specific item before the route is activated
@@ -23,9 +24,12 @@ export class ItemPageResolver implements Resolve<RemoteData<Item>> {
* or an error if something went wrong * or an error if something went wrong
*/ */
resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<RemoteData<Item>> { resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<RemoteData<Item>> {
return this.itemService.findById(route.params.id) return this.itemService.findById(route.params.id,
.pipe( followLink('owningCollection'),
find((RD) => hasValue(RD.error) || RD.hasSucceeded), followLink('bundles'),
); followLink('relationships'),
).pipe(
find((RD) => hasValue(RD.error) || RD.hasSucceeded),
);
} }
} }

View File

@@ -1,7 +1,7 @@
<ng-container *ngVar="(bitstreamsObs | async) as bitstreams"> <ng-container *ngVar="(bitstreams$ | async) as bitstreams">
<ds-metadata-field-wrapper *ngIf="bitstreams?.length > 0" [label]="label | translate"> <ds-metadata-field-wrapper *ngIf="bitstreams?.length > 0" [label]="label | translate">
<div class="file-section"> <div class="file-section">
<a *ngFor="let file of bitstreams; let last=last;" [href]="file?.content" [download]="file?.name"> <a *ngFor="let file of bitstreams; let last=last;" [href]="file?._links.content.href" [download]="file?.name">
<span>{{file?.name}}</span> <span>{{file?.name}}</span>
<span>({{(file?.sizeBytes) | dsFileSize }})</span> <span>({{(file?.sizeBytes) | dsFileSize }})</span>
<span *ngIf="!last" innerHTML="{{separator}}"></span> <span *ngIf="!last" innerHTML="{{separator}}"></span>

View File

@@ -1,8 +1,10 @@
import { Component, Input, OnInit } from '@angular/core'; import { Component, Input, OnInit } from '@angular/core';
import { Observable } from 'rxjs'; import { Observable } from 'rxjs';
import { BitstreamDataService } from '../../../../core/data/bitstream-data.service';
import { Bitstream } from '../../../../core/shared/bitstream.model'; import { Bitstream } from '../../../../core/shared/bitstream.model';
import { Item } from '../../../../core/shared/item.model'; import { Item } from '../../../../core/shared/item.model';
import { getFirstSucceededRemoteListPayload } from '../../../../core/shared/operators';
/** /**
* This component renders the file section of the item * This component renders the file section of the item
@@ -20,14 +22,21 @@ export class FileSectionComponent implements OnInit {
separator = '<br/>'; separator = '<br/>';
bitstreamsObs: Observable<Bitstream[]>; bitstreams$: Observable<Bitstream[]>;
constructor(
protected bitstreamDataService: BitstreamDataService
) {
}
ngOnInit(): void { ngOnInit(): void {
this.initialize(); this.initialize();
} }
initialize(): void { initialize(): void {
this.bitstreamsObs = this.item.getFiles(); this.bitstreams$ = this.bitstreamDataService.findAllByItemAndBundleName(this.item, 'ORIGINAL').pipe(
getFirstSucceededRemoteListPayload()
);
} }
} }

View File

@@ -4,7 +4,7 @@
<div class="row"> <div class="row">
<div class="col-xs-12 col-md-4"> <div class="col-xs-12 col-md-4">
<ds-metadata-field-wrapper> <ds-metadata-field-wrapper>
<ds-thumbnail [thumbnail]="object.getThumbnail() | async"></ds-thumbnail> <ds-thumbnail [thumbnail]="getThumbnail() | async"></ds-thumbnail>
</ds-metadata-field-wrapper> </ds-metadata-field-wrapper>
<ds-item-page-file-section [item]="object"></ds-item-page-file-section> <ds-item-page-file-section [item]="object"></ds-item-page-file-section>
<ds-item-page-date-field [item]="object"></ds-item-page-date-field> <ds-item-page-date-field [item]="object"></ds-item-page-date-field>

View File

@@ -1,20 +1,34 @@
import { async, ComponentFixture, TestBed } from '@angular/core/testing'; import { HttpClient } from '@angular/common/http';
import { TranslateLoader, TranslateModule } from '@ngx-translate/core';
import { MockTranslateLoader } from '../../../../shared/mocks/mock-translate-loader';
import { GenericItemPageFieldComponent } from '../../field-components/specific-field/generic/generic-item-page-field.component';
import { TruncatePipe } from '../../../../shared/utils/truncate.pipe';
import { ItemDataService } from '../../../../core/data/item-data.service';
import { TruncatableService } from '../../../../shared/truncatable/truncatable.service';
import { ChangeDetectionStrategy, NO_ERRORS_SCHEMA } from '@angular/core'; import { ChangeDetectionStrategy, NO_ERRORS_SCHEMA } from '@angular/core';
import { Item } from '../../../../core/shared/item.model'; import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { PaginatedList } from '../../../../core/data/paginated-list';
import { PageInfo } from '../../../../core/shared/page-info.model';
import { By } from '@angular/platform-browser'; import { By } from '@angular/platform-browser';
import { Store } from '@ngrx/store';
import { TranslateLoader, TranslateModule } from '@ngx-translate/core';
import { Observable } from 'rxjs/internal/Observable';
import { RemoteDataBuildService } from '../../../../core/cache/builders/remote-data-build.service';
import { ObjectCacheService } from '../../../../core/cache/object-cache.service';
import { BitstreamDataService } from '../../../../core/data/bitstream-data.service';
import { CommunityDataService } from '../../../../core/data/community-data.service';
import { DefaultChangeAnalyzer } from '../../../../core/data/default-change-analyzer.service';
import { DSOChangeAnalyzer } from '../../../../core/data/dso-change-analyzer.service';
import { ItemDataService } from '../../../../core/data/item-data.service';
import { PaginatedList } from '../../../../core/data/paginated-list';
import { RelationshipService } from '../../../../core/data/relationship.service';
import { RemoteData } from '../../../../core/data/remote-data';
import { Bitstream } from '../../../../core/shared/bitstream.model';
import { HALEndpointService } from '../../../../core/shared/hal-endpoint.service';
import { Item } from '../../../../core/shared/item.model';
import { MetadataMap } from '../../../../core/shared/metadata.models';
import { PageInfo } from '../../../../core/shared/page-info.model';
import { UUIDService } from '../../../../core/shared/uuid.service';
import { MockTranslateLoader } from '../../../../shared/mocks/mock-translate-loader';
import { NotificationsService } from '../../../../shared/notifications/notifications.service';
import { createSuccessfulRemoteDataObject$ } from '../../../../shared/testing/utils';
import { TruncatableService } from '../../../../shared/truncatable/truncatable.service';
import { TruncatePipe } from '../../../../shared/utils/truncate.pipe';
import { GenericItemPageFieldComponent } from '../../field-components/specific-field/generic/generic-item-page-field.component';
import { createRelationshipsObservable } from '../shared/item.component.spec'; import { createRelationshipsObservable } from '../shared/item.component.spec';
import { PublicationComponent } from './publication.component'; import { PublicationComponent } from './publication.component';
import { MetadataMap } from '../../../../core/shared/metadata.models';
import { createSuccessfulRemoteDataObject$ } from '../../../../shared/testing/utils';
import { RelationshipService } from '../../../../core/data/relationship.service';
const mockItem: Item = Object.assign(new Item(), { const mockItem: Item = Object.assign(new Item(), {
bundles: createSuccessfulRemoteDataObject$(new PaginatedList(new PageInfo(), [])), bundles: createSuccessfulRemoteDataObject$(new PaginatedList(new PageInfo(), [])),
@@ -27,6 +41,11 @@ describe('PublicationComponent', () => {
let fixture: ComponentFixture<PublicationComponent>; let fixture: ComponentFixture<PublicationComponent>;
beforeEach(async(() => { beforeEach(async(() => {
const mockBitstreamDataService = {
getThumbnailFor(item: Item): Observable<RemoteData<Bitstream>> {
return createSuccessfulRemoteDataObject$(new Bitstream());
}
};
TestBed.configureTestingModule({ TestBed.configureTestingModule({
imports: [TranslateModule.forRoot({ imports: [TranslateModule.forRoot({
loader: { loader: {
@@ -36,14 +55,25 @@ describe('PublicationComponent', () => {
})], })],
declarations: [PublicationComponent, GenericItemPageFieldComponent, TruncatePipe], declarations: [PublicationComponent, GenericItemPageFieldComponent, TruncatePipe],
providers: [ providers: [
{provide: ItemDataService, useValue: {}}, { provide: ItemDataService, useValue: {} },
{provide: TruncatableService, useValue: {}}, { provide: TruncatableService, useValue: {} },
{provide: RelationshipService, useValue: {}} { provide: RelationshipService, useValue: {} },
{ provide: ObjectCacheService, useValue: {} },
{ provide: UUIDService, useValue: {} },
{ provide: Store, useValue: {} },
{ provide: RemoteDataBuildService, useValue: {} },
{ provide: CommunityDataService, useValue: {} },
{ provide: HALEndpointService, useValue: {} },
{ provide: NotificationsService, useValue: {} },
{ provide: HttpClient, useValue: {} },
{ provide: DSOChangeAnalyzer, useValue: {} },
{ provide: DefaultChangeAnalyzer, useValue: {} },
{ provide: BitstreamDataService, useValue: mockBitstreamDataService },
], ],
schemas: [NO_ERRORS_SCHEMA] schemas: [NO_ERRORS_SCHEMA]
}).overrideComponent(PublicationComponent, { }).overrideComponent(PublicationComponent, {
set: {changeDetection: ChangeDetectionStrategy.Default} set: { changeDetection: ChangeDetectionStrategy.Default }
}).compileComponents(); }).compileComponents();
})); }));

View File

@@ -17,4 +17,5 @@ import { listableObjectComponent } from '../../../../shared/object-collection/sh
changeDetection: ChangeDetectionStrategy.OnPush, changeDetection: ChangeDetectionStrategy.OnPush,
}) })
export class PublicationComponent extends ItemComponent { export class PublicationComponent extends ItemComponent {
} }

View File

@@ -1,12 +1,12 @@
import { getSucceededRemoteData } from '../../../../core/shared/operators';
import { hasValue } from '../../../../shared/empty.util';
import { Observable } from 'rxjs/internal/Observable';
import { Relationship } from '../../../../core/shared/item-relationships/relationship.model';
import { distinctUntilChanged, flatMap, map, switchMap } from 'rxjs/operators';
import { combineLatest as observableCombineLatest, zip as observableZip } from 'rxjs'; import { combineLatest as observableCombineLatest, zip as observableZip } from 'rxjs';
import { Item } from '../../../../core/shared/item.model'; import { Observable } from 'rxjs/internal/Observable';
import { distinctUntilChanged, flatMap, map, switchMap } from 'rxjs/operators';
import { PaginatedList } from '../../../../core/data/paginated-list'; import { PaginatedList } from '../../../../core/data/paginated-list';
import { RemoteData } from '../../../../core/data/remote-data'; import { RemoteData } from '../../../../core/data/remote-data';
import { Relationship } from '../../../../core/shared/item-relationships/relationship.model';
import { Item } from '../../../../core/shared/item.model';
import { getFinishedRemoteData, getSucceededRemoteData } from '../../../../core/shared/operators';
import { hasValue } from '../../../../shared/empty.util';
/** /**
* Operator for comparing arrays using a mapping function * Operator for comparing arrays using a mapping function
@@ -74,8 +74,8 @@ export const paginatedRelationsToItems = (thisId: string) =>
source.pipe( source.pipe(
getSucceededRemoteData(), getSucceededRemoteData(),
switchMap((relationshipsRD: RemoteData<PaginatedList<Relationship>>) => { switchMap((relationshipsRD: RemoteData<PaginatedList<Relationship>>) => {
return observableZip( return observableCombineLatest(
...relationshipsRD.payload.page.map((rel: Relationship) => observableCombineLatest(rel.leftItem, rel.rightItem)) ...relationshipsRD.payload.page.map((rel: Relationship) => observableCombineLatest(rel.leftItem.pipe(getFinishedRemoteData()), rel.rightItem.pipe(getFinishedRemoteData())))
).pipe( ).pipe(
map((arr) => map((arr) =>
arr arr

View File

@@ -1,29 +1,36 @@
import { Item } from '../../../../core/shared/item.model'; import { HttpClient } from '@angular/common/http';
import { ChangeDetectionStrategy, DebugElement, NO_ERRORS_SCHEMA } from '@angular/core';
import { async, ComponentFixture, TestBed } from '@angular/core/testing'; import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { By } from '@angular/platform-browser'; import { By } from '@angular/platform-browser';
import { GenericItemPageFieldComponent } from '../../field-components/specific-field/generic/generic-item-page-field.component'; import { Store } from '@ngrx/store';
import { TruncatableService } from '../../../../shared/truncatable/truncatable.service';
import { ItemDataService } from '../../../../core/data/item-data.service';
import { TranslateLoader, TranslateModule } from '@ngx-translate/core'; import { TranslateLoader, TranslateModule } from '@ngx-translate/core';
import { MockTranslateLoader } from '../../../../shared/mocks/mock-translate-loader';
import { ChangeDetectionStrategy, DebugElement, NO_ERRORS_SCHEMA } from '@angular/core';
import { TruncatePipe } from '../../../../shared/utils/truncate.pipe';
import { isNotEmpty } from '../../../../shared/empty.util';
import { RelationshipType } from '../../../../core/shared/item-relationships/relationship-type.model';
import { PaginatedList } from '../../../../core/data/paginated-list';
import { Relationship } from '../../../../core/shared/item-relationships/relationship.model';
import { PageInfo } from '../../../../core/shared/page-info.model';
import { ItemComponent } from './item.component';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { VarDirective } from '../../../../shared/utils/var.directive';
import { Observable } from 'rxjs/internal/Observable'; import { Observable } from 'rxjs/internal/Observable';
import { MetadataRepresentation } from '../../../../core/shared/metadata-representation/metadata-representation.model'; import { RemoteDataBuildService } from '../../../../core/cache/builders/remote-data-build.service';
import { MetadatumRepresentation } from '../../../../core/shared/metadata-representation/metadatum/metadatum-representation.model'; import { ObjectCacheService } from '../../../../core/cache/object-cache.service';
import { ItemMetadataRepresentation } from '../../../../core/shared/metadata-representation/item/item-metadata-representation.model'; import { BitstreamDataService } from '../../../../core/data/bitstream-data.service';
import { MetadataMap, MetadataValue } from '../../../../core/shared/metadata.models'; import { CommunityDataService } from '../../../../core/data/community-data.service';
import { compareArraysUsing, compareArraysUsingIds } from './item-relationships-utils'; import { DefaultChangeAnalyzer } from '../../../../core/data/default-change-analyzer.service';
import { createSuccessfulRemoteDataObject$ } from '../../../../shared/testing/utils'; import { DSOChangeAnalyzer } from '../../../../core/data/dso-change-analyzer.service';
import { ItemDataService } from '../../../../core/data/item-data.service';
import { PaginatedList } from '../../../../core/data/paginated-list';
import { RelationshipService } from '../../../../core/data/relationship.service'; import { RelationshipService } from '../../../../core/data/relationship.service';
import { RemoteData } from '../../../../core/data/remote-data';
import { Bitstream } from '../../../../core/shared/bitstream.model';
import { HALEndpointService } from '../../../../core/shared/hal-endpoint.service';
import { RelationshipType } from '../../../../core/shared/item-relationships/relationship-type.model';
import { Relationship } from '../../../../core/shared/item-relationships/relationship.model';
import { Item } from '../../../../core/shared/item.model';
import { PageInfo } from '../../../../core/shared/page-info.model';
import { UUIDService } from '../../../../core/shared/uuid.service';
import { isNotEmpty } from '../../../../shared/empty.util';
import { MockTranslateLoader } from '../../../../shared/mocks/mock-translate-loader';
import { NotificationsService } from '../../../../shared/notifications/notifications.service';
import { createSuccessfulRemoteDataObject$ } from '../../../../shared/testing/utils';
import { TruncatableService } from '../../../../shared/truncatable/truncatable.service';
import { TruncatePipe } from '../../../../shared/utils/truncate.pipe';
import { GenericItemPageFieldComponent } from '../../field-components/specific-field/generic/generic-item-page-field.component';
import { compareArraysUsing, compareArraysUsingIds } from './item-relationships-utils';
import { ItemComponent } from './item.component';
/** /**
* Create a generic test for an item-page-fields component using a mockItem and the type of component * Create a generic test for an item-page-fields component using a mockItem and the type of component
@@ -38,6 +45,11 @@ export function getItemPageFieldsTest(mockItem: Item, component) {
let fixture: ComponentFixture<any>; let fixture: ComponentFixture<any>;
beforeEach(async(() => { beforeEach(async(() => {
const mockBitstreamDataService = {
getThumbnailFor(item: Item): Observable<RemoteData<Bitstream>> {
return createSuccessfulRemoteDataObject$(new Bitstream());
}
};
TestBed.configureTestingModule({ TestBed.configureTestingModule({
imports: [TranslateModule.forRoot({ imports: [TranslateModule.forRoot({
loader: { loader: {
@@ -47,14 +59,25 @@ export function getItemPageFieldsTest(mockItem: Item, component) {
})], })],
declarations: [component, GenericItemPageFieldComponent, TruncatePipe], declarations: [component, GenericItemPageFieldComponent, TruncatePipe],
providers: [ providers: [
{provide: ItemDataService, useValue: {}}, { provide: ItemDataService, useValue: {} },
{provide: TruncatableService, useValue: {}}, { provide: TruncatableService, useValue: {} },
{provide: RelationshipService, useValue: {}} { provide: RelationshipService, useValue: {} },
{ provide: ObjectCacheService, useValue: {} },
{ provide: UUIDService, useValue: {} },
{ provide: Store, useValue: {} },
{ provide: RemoteDataBuildService, useValue: {} },
{ provide: CommunityDataService, useValue: {} },
{ provide: HALEndpointService, useValue: {} },
{ provide: HttpClient, useValue: {} },
{ provide: DSOChangeAnalyzer, useValue: {} },
{ provide: NotificationsService, useValue: {} },
{ provide: DefaultChangeAnalyzer, useValue: {} },
{ provide: BitstreamDataService, useValue: mockBitstreamDataService },
], ],
schemas: [NO_ERRORS_SCHEMA] schemas: [NO_ERRORS_SCHEMA]
}).overrideComponent(component, { }).overrideComponent(component, {
set: {changeDetection: ChangeDetectionStrategy.Default} set: { changeDetection: ChangeDetectionStrategy.Default }
}).compileComponents(); }).compileComponents();
})); }));
@@ -102,6 +125,7 @@ export function createRelationshipsObservable() {
}) })
])); ]));
} }
describe('ItemComponent', () => { describe('ItemComponent', () => {
const arr1 = [ const arr1 = [
{ {

View File

@@ -1,5 +1,9 @@
import { Component, Input } from '@angular/core'; import { Component, Input } from '@angular/core';
import { Observable } from 'rxjs/internal/Observable';
import { BitstreamDataService } from '../../../../core/data/bitstream-data.service';
import { Bitstream } from '../../../../core/shared/bitstream.model';
import { Item } from '../../../../core/shared/item.model'; import { Item } from '../../../../core/shared/item.model';
import { getFirstSucceededRemoteDataPayload } from '../../../../core/shared/operators';
@Component({ @Component({
selector: 'ds-item', selector: 'ds-item',
@@ -10,4 +14,14 @@ import { Item } from '../../../../core/shared/item.model';
*/ */
export class ItemComponent { export class ItemComponent {
@Input() object: Item; @Input() object: Item;
constructor(protected bitstreamDataService: BitstreamDataService) {
}
// TODO refactor to return RemoteData, and thumbnail template to deal with loading
getThumbnail(): Observable<Bitstream> {
return this.bitstreamDataService.getThumbnailFor(this.object).pipe(
getFirstSucceededRemoteDataPayload()
);
}
} }

View File

@@ -10,6 +10,7 @@ import { Relationship } from '../../../core/shared/item-relationships/relationsh
import { Item } from '../../../core/shared/item.model'; import { Item } from '../../../core/shared/item.model';
import { MetadatumRepresentation } from '../../../core/shared/metadata-representation/metadatum/metadatum-representation.model'; import { MetadatumRepresentation } from '../../../core/shared/metadata-representation/metadatum/metadatum-representation.model';
import { ItemMetadataRepresentation } from '../../../core/shared/metadata-representation/item/item-metadata-representation.model'; import { ItemMetadataRepresentation } from '../../../core/shared/metadata-representation/item/item-metadata-representation.model';
import { followLink } from '../../../shared/utils/follow-link-config.model';
import { AbstractIncrementalListComponent } from '../abstract-incremental-list/abstract-incremental-list.component'; import { AbstractIncrementalListComponent } from '../abstract-incremental-list/abstract-incremental-list.component';
@Component({ @Component({
@@ -81,7 +82,7 @@ export class MetadataRepresentationListComponent extends AbstractIncrementalList
.map((metadatum: any) => Object.assign(new MetadataValue(), metadatum)) .map((metadatum: any) => Object.assign(new MetadataValue(), metadatum))
.map((metadatum: MetadataValue) => { .map((metadatum: MetadataValue) => {
if (metadatum.isVirtual) { if (metadatum.isVirtual) {
return this.relationshipService.findById(metadatum.virtualValue).pipe( return this.relationshipService.findById(metadatum.virtualValue, followLink('leftItem'), followLink('rightItem')).pipe(
getSucceededRemoteData(), getSucceededRemoteData(),
switchMap((relRD: RemoteData<Relationship>) => switchMap((relRD: RemoteData<Relationship>) =>
observableCombineLatest(relRD.payload.leftItem, relRD.payload.rightItem).pipe( observableCombineLatest(relRD.payload.leftItem, relRD.payload.rightItem).pipe(

View File

@@ -29,7 +29,7 @@ describe('TabbedRelatedEntitiesSearchComponent', () => {
beforeEach(async(() => { beforeEach(async(() => {
TestBed.configureTestingModule({ TestBed.configureTestingModule({
imports: [TranslateModule.forRoot(), NoopAnimationsModule, NgbModule.forRoot()], imports: [TranslateModule.forRoot(), NoopAnimationsModule, NgbModule],
declarations: [TabbedRelatedEntitiesSearchComponent, VarDirective], declarations: [TabbedRelatedEntitiesSearchComponent, VarDirective],
providers: [ providers: [
{ {

View File

@@ -2,12 +2,14 @@ import { NgModule } from '@angular/core';
import { RouterModule } from '@angular/router'; import { RouterModule } from '@angular/router';
import { LoginPageComponent } from './login-page.component'; import { LoginPageComponent } from './login-page.component';
import { I18nBreadcrumbResolver } from '../core/breadcrumbs/i18n-breadcrumb.resolver';
@NgModule({ @NgModule({
imports: [ imports: [
RouterModule.forChild([ RouterModule.forChild([
{ path: '', pathMatch: 'full', component: LoginPageComponent, data: { title: 'login.title' } } { path: '', pathMatch: 'full', component: LoginPageComponent, resolve: { breadcrumb: I18nBreadcrumbResolver }, data: { breadcrumbKey: 'login', title: 'login.title' } }
]) ])
] ]
}) })
export class LoginPageRoutingModule { } export class LoginPageRoutingModule {
}

View File

@@ -1,6 +1,6 @@
import { LookupGuard } from './lookup-guard';
import { of as observableOf } from 'rxjs'; import { of as observableOf } from 'rxjs';
import { IdentifierType } from '../core/data/request.models'; import { IdentifierType } from '../core/data/request.models';
import { LookupGuard } from './lookup-guard';
describe('LookupGuard', () => { describe('LookupGuard', () => {
let dsoService: any; let dsoService: any;
@@ -8,13 +8,13 @@ describe('LookupGuard', () => {
beforeEach(() => { beforeEach(() => {
dsoService = { dsoService = {
findById: jasmine.createSpy('findById').and.returnValue(observableOf({ hasFailed: false, findByIdAndIDType: jasmine.createSpy('findByIdAndIDType').and.returnValue(observableOf({ hasFailed: false,
hasSucceeded: true })) hasSucceeded: true }))
}; };
guard = new LookupGuard(dsoService); guard = new LookupGuard(dsoService);
}); });
it('should call findById with handle params', () => { it('should call findByIdAndIDType with handle params', () => {
const scopedRoute = { const scopedRoute = {
params: { params: {
id: '1234', id: '1234',
@@ -22,10 +22,10 @@ describe('LookupGuard', () => {
} }
}; };
guard.canActivate(scopedRoute as any, undefined); guard.canActivate(scopedRoute as any, undefined);
expect(dsoService.findById).toHaveBeenCalledWith('123456789/1234', IdentifierType.HANDLE) expect(dsoService.findByIdAndIDType).toHaveBeenCalledWith('123456789/1234', IdentifierType.HANDLE)
}); });
it('should call findById with handle params', () => { it('should call findByIdAndIDType with handle params', () => {
const scopedRoute = { const scopedRoute = {
params: { params: {
id: '123456789%2F1234', id: '123456789%2F1234',
@@ -33,10 +33,10 @@ describe('LookupGuard', () => {
} }
}; };
guard.canActivate(scopedRoute as any, undefined); guard.canActivate(scopedRoute as any, undefined);
expect(dsoService.findById).toHaveBeenCalledWith('123456789%2F1234', IdentifierType.HANDLE) expect(dsoService.findByIdAndIDType).toHaveBeenCalledWith('123456789%2F1234', IdentifierType.HANDLE)
}); });
it('should call findById with UUID params', () => { it('should call findByIdAndIDType with UUID params', () => {
const scopedRoute = { const scopedRoute = {
params: { params: {
id: '34cfed7c-f597-49ef-9cbe-ea351f0023c2', id: '34cfed7c-f597-49ef-9cbe-ea351f0023c2',
@@ -44,7 +44,7 @@ describe('LookupGuard', () => {
} }
}; };
guard.canActivate(scopedRoute as any, undefined); guard.canActivate(scopedRoute as any, undefined);
expect(dsoService.findById).toHaveBeenCalledWith('34cfed7c-f597-49ef-9cbe-ea351f0023c2', IdentifierType.UUID) expect(dsoService.findByIdAndIDType).toHaveBeenCalledWith('34cfed7c-f597-49ef-9cbe-ea351f0023c2', IdentifierType.UUID)
}); });
}); });

View File

@@ -20,7 +20,7 @@ export class LookupGuard implements CanActivate {
canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean> { canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean> {
const params = this.getLookupParams(route); const params = this.getLookupParams(route);
return this.dsoService.findById(params.id, params.type).pipe( return this.dsoService.findByIdAndIDType(params.id, params.type).pipe(
map((response: RemoteData<FindByIDRequest>) => response.hasFailed) map((response: RemoteData<FindByIDRequest>) => response.hasFailed)
); );
} }

View File

@@ -19,7 +19,7 @@
<ds-search-labels [inPlaceSearch]="inPlaceSearch"></ds-search-labels> <ds-search-labels [inPlaceSearch]="inPlaceSearch"></ds-search-labels>
<div class="row"> <div class="row">
<div id="search-body" <div id="search-body"
class="row-offcanvas row-offcanvas-left" class="row-offcanvas row-offcanvas-left w-100"
[@pushInOut]="(isSidebarCollapsed() | async) ? 'collapsed' : 'expanded'"> [@pushInOut]="(isSidebarCollapsed() | async) ? 'collapsed' : 'expanded'">
<ds-search-sidebar *ngIf="(isXsOrSm$ | async)" class="col-12" <ds-search-sidebar *ngIf="(isXsOrSm$ | async)" class="col-12"
id="search-sidebar-sm" id="search-sidebar-sm"

View File

@@ -84,7 +84,7 @@ describe('MyDSpacePageComponent', () => {
beforeEach(async(() => { beforeEach(async(() => {
TestBed.configureTestingModule({ TestBed.configureTestingModule({
imports: [TranslateModule.forRoot(), RouterTestingModule.withRoutes([]), NoopAnimationsModule, NgbCollapseModule.forRoot()], imports: [TranslateModule.forRoot(), RouterTestingModule.withRoutes([]), NoopAnimationsModule, NgbCollapseModule],
declarations: [MyDSpacePageComponent, RoleDirective], declarations: [MyDSpacePageComponent, RoleDirective],
providers: [ providers: [
{ provide: SearchService, useValue: searchServiceStub }, { provide: SearchService, useValue: searchServiceStub },

View File

@@ -4,13 +4,24 @@ import { RouterModule } from '@angular/router';
import { ConfigurationSearchPageGuard } from './configuration-search-page.guard'; import { ConfigurationSearchPageGuard } from './configuration-search-page.guard';
import { ConfigurationSearchPageComponent } from './configuration-search-page.component'; import { ConfigurationSearchPageComponent } from './configuration-search-page.component';
import { SearchPageComponent } from './search-page.component'; import { SearchPageComponent } from './search-page.component';
import { I18nBreadcrumbResolver } from '../core/breadcrumbs/i18n-breadcrumb.resolver';
import { I18nBreadcrumbsService } from '../core/breadcrumbs/i18n-breadcrumbs.service';
@NgModule({ @NgModule({
imports: [ imports: [
RouterModule.forChild([ RouterModule.forChild([{
{ path: '', component: SearchPageComponent, data: { title: 'search.title' } }, path: '',
{ path: ':configuration', component: ConfigurationSearchPageComponent, canActivate: [ConfigurationSearchPageGuard]} resolve: { breadcrumb: I18nBreadcrumbResolver }, data: { title: 'search.title', breadcrumbKey: 'search' },
]) children: [
{ path: '', component: SearchPageComponent },
{ path: ':configuration', component: ConfigurationSearchPageComponent, canActivate: [ConfigurationSearchPageGuard] }
]
}]
)
],
providers: [
I18nBreadcrumbResolver,
I18nBreadcrumbsService
] ]
}) })
export class SearchPageRoutingModule { export class SearchPageRoutingModule {

View File

@@ -5,6 +5,6 @@
} }
} }
/deep/ .search-controls { ::ng-deep .search-controls {
margin-bottom: $spacer; margin-bottom: $spacer;
} }

View File

@@ -90,7 +90,7 @@ const routeServiceStub = {
export function configureSearchComponentTestingModule(compType) { export function configureSearchComponentTestingModule(compType) {
TestBed.configureTestingModule({ TestBed.configureTestingModule({
imports: [TranslateModule.forRoot(), RouterTestingModule.withRoutes([]), NoopAnimationsModule, NgbCollapseModule.forRoot()], imports: [TranslateModule.forRoot(), RouterTestingModule.withRoutes([]), NoopAnimationsModule, NgbCollapseModule],
declarations: [compType], declarations: [compType],
providers: [ providers: [
{ provide: SearchService, useValue: searchServiceStub }, { provide: SearchService, useValue: searchServiceStub },

View File

@@ -3,16 +3,30 @@ import { RouterModule } from '@angular/router';
import { PageNotFoundComponent } from './pagenotfound/pagenotfound.component'; import { PageNotFoundComponent } from './pagenotfound/pagenotfound.component';
import { AuthenticatedGuard } from './core/auth/authenticated.guard'; import { AuthenticatedGuard } from './core/auth/authenticated.guard';
import { Breadcrumb } from './breadcrumbs/breadcrumb/breadcrumb.model';
import { DSpaceObject } from './core/shared/dspace-object.model';
import { Community } from './core/shared/community.model';
import { getCommunityPageRoute } from './+community-page/community-page-routing.module';
import { Collection } from './core/shared/collection.model';
import { Item } from './core/shared/item.model';
import { getItemPageRoute } from './+item-page/item-page-routing.module';
import { getCollectionPageRoute } from './+collection-page/collection-page-routing.module';
import { BrowseByDSOBreadcrumbResolver } from './+browse-by/browse-by-dso-breadcrumb.resolver';
const ITEM_MODULE_PATH = 'items'; const ITEM_MODULE_PATH = 'items';
export function getItemModulePath() { export function getItemModulePath() {
return `/${ITEM_MODULE_PATH}`; return `/${ITEM_MODULE_PATH}`;
} }
const COLLECTION_MODULE_PATH = 'collections'; const COLLECTION_MODULE_PATH = 'collections';
export function getCollectionModulePath() { export function getCollectionModulePath() {
return `/${COLLECTION_MODULE_PATH}`; return `/${COLLECTION_MODULE_PATH}`;
} }
const COMMUNITY_MODULE_PATH = 'communities'; const COMMUNITY_MODULE_PATH = 'communities';
export function getCommunityModulePath() { export function getCommunityModulePath() {
return `/${COMMUNITY_MODULE_PATH}`; return `/${COMMUNITY_MODULE_PATH}`;
} }
@@ -21,16 +35,28 @@ export function getBitstreamModulePath() {
return `/${BITSTREAM_MODULE_PATH}`; return `/${BITSTREAM_MODULE_PATH}`;
} }
const ADMIN_MODULE_PATH = 'admin'; const ADMIN_MODULE_PATH = 'admin';
export function getAdminModulePath() { export function getAdminModulePath() {
return `/${ADMIN_MODULE_PATH}`; return `/${ADMIN_MODULE_PATH}`;
} }
export function getDSOPath(dso: DSpaceObject): string {
switch ((dso as any).type) {
case Community.type.value:
return getCommunityPageRoute(dso.uuid);
case Collection.type.value:
return getCollectionPageRoute(dso.uuid);
case Item.type.value:
return getItemPageRoute(dso.uuid);
}
}
@NgModule({ @NgModule({
imports: [ imports: [
RouterModule.forRoot([ RouterModule.forRoot([
{ path: '', redirectTo: '/home', pathMatch: 'full' }, { path: '', redirectTo: '/home', pathMatch: 'full' },
{ path: 'home', loadChildren: './+home-page/home-page.module#HomePageModule' }, { path: 'home', loadChildren: './+home-page/home-page.module#HomePageModule', data: { showBreadcrumbs: false } },
{ path: 'community-list', loadChildren: './community-list-page/community-list-page.module#CommunityListPageModule' }, { path: 'community-list', loadChildren: './community-list-page/community-list-page.module#CommunityListPageModule' },
{ path: 'id', loadChildren: './+lookup-by-id/lookup-by-id.module#LookupIdModule' }, { path: 'id', loadChildren: './+lookup-by-id/lookup-by-id.module#LookupIdModule' },
{ path: 'handle', loadChildren: './+lookup-by-id/lookup-by-id.module#LookupIdModule' }, { path: 'handle', loadChildren: './+lookup-by-id/lookup-by-id.module#LookupIdModule' },
@@ -40,7 +66,7 @@ export function getAdminModulePath() {
{ path: BITSTREAM_MODULE_PATH, loadChildren: './+bitstream-page/bitstream-page.module#BitstreamPageModule' }, { path: BITSTREAM_MODULE_PATH, loadChildren: './+bitstream-page/bitstream-page.module#BitstreamPageModule' },
{ path: 'mydspace', loadChildren: './+my-dspace-page/my-dspace-page.module#MyDSpacePageModule', canActivate: [AuthenticatedGuard] }, { path: 'mydspace', loadChildren: './+my-dspace-page/my-dspace-page.module#MyDSpacePageModule', canActivate: [AuthenticatedGuard] },
{ path: 'search', loadChildren: './+search-page/search-page.module#SearchPageModule' }, { path: 'search', loadChildren: './+search-page/search-page.module#SearchPageModule' },
{ path: 'browse', loadChildren: './+browse-by/browse-by.module#BrowseByModule' }, { path: 'browse', loadChildren: './+browse-by/browse-by.module#BrowseByModule'},
{ path: ADMIN_MODULE_PATH, loadChildren: './+admin/admin.module#AdminModule', canActivate: [AuthenticatedGuard] }, { path: ADMIN_MODULE_PATH, loadChildren: './+admin/admin.module#AdminModule', canActivate: [AuthenticatedGuard] },
{ path: 'login', loadChildren: './+login-page/login-page.module#LoginPageModule' }, { path: 'login', loadChildren: './+login-page/login-page.module#LoginPageModule' },
{ path: 'logout', loadChildren: './+logout-page/logout-page.module#LogoutPageModule' }, { path: 'logout', loadChildren: './+logout-page/logout-page.module#LogoutPageModule' },
@@ -50,7 +76,7 @@ export function getAdminModulePath() {
{ path: '**', pathMatch: 'full', component: PageNotFoundComponent }, { path: '**', pathMatch: 'full', component: PageNotFoundComponent },
]) ])
], ],
exports: [RouterModule] exports: [RouterModule],
}) })
export class AppRoutingModule { export class AppRoutingModule {

View File

@@ -10,7 +10,11 @@
[options]="config.notifications"> [options]="config.notifications">
</ds-notifications-board> </ds-notifications-board>
<main class="main-content"> <main class="main-content">
<div class="container" *ngIf="isLoading"> <div class="container">
<ds-breadcrumbs></ds-breadcrumbs>
</div>
<div class="container" *ngIf="isLoading$ | async">
<ds-loading message="{{'loading.default' | translate}}"></ds-loading> <ds-loading message="{{'loading.default' | translate}}"></ds-loading>
</div> </div>
<router-outlet></router-outlet> <router-outlet></router-outlet>

View File

@@ -19,7 +19,7 @@ import variables from '../styles/_exposed_variables.scss';
import { CSSVariableService } from './shared/sass-helper/sass-helper.service'; import { CSSVariableService } from './shared/sass-helper/sass-helper.service';
import { MenuService } from './shared/menu/menu.service'; import { MenuService } from './shared/menu/menu.service';
import { MenuID } from './shared/menu/initial-menus-state'; import { MenuID } from './shared/menu/initial-menus-state';
import { combineLatest as combineLatestObservable, Observable, of } from 'rxjs'; import { BehaviorSubject, combineLatest as combineLatestObservable, Observable, of } from 'rxjs';
import { slideSidebarPadding } from './shared/animations/slide'; import { slideSidebarPadding } from './shared/animations/slide';
import { HostWindowService } from './shared/host-window.service'; import { HostWindowService } from './shared/host-window.service';
import { Theme } from '../config/theme.inferface'; import { Theme } from '../config/theme.inferface';
@@ -38,7 +38,7 @@ export const LANG_COOKIE = 'language_cookie';
animations: [slideSidebarPadding] animations: [slideSidebarPadding]
}) })
export class AppComponent implements OnInit, AfterViewInit { export class AppComponent implements OnInit, AfterViewInit {
isLoading = true; isLoading$: BehaviorSubject<boolean> = new BehaviorSubject(true);
sidebarVisible: Observable<boolean>; sidebarVisible: Observable<boolean>;
slideSidebarOver: Observable<boolean>; slideSidebarOver: Observable<boolean>;
collapsedSidebarWidth: Observable<string>; collapsedSidebarWidth: Observable<string>;
@@ -131,12 +131,12 @@ export class AppComponent implements OnInit, AfterViewInit {
delay(0) delay(0)
).subscribe((event) => { ).subscribe((event) => {
if (event instanceof NavigationStart) { if (event instanceof NavigationStart) {
this.isLoading = true; this.isLoading$.next(true);
} else if ( } else if (
event instanceof NavigationEnd || event instanceof NavigationEnd ||
event instanceof NavigationCancel event instanceof NavigationCancel
) { ) {
this.isLoading = false; this.isLoading$.next(false);
} }
}); });
} }

View File

@@ -1,4 +1,3 @@
import { isNotEmpty } from './shared/empty.util';
import { StoreActionTypes } from './store.actions'; import { StoreActionTypes } from './store.actions';
// fallback ngrx debugger // fallback ngrx debugger

View File

@@ -3,17 +3,14 @@ import { HttpClientModule } from '@angular/common/http';
import { NgModule } from '@angular/core'; import { NgModule } from '@angular/core';
import { NgbModule } from '@ng-bootstrap/ng-bootstrap'; import { NgbModule } from '@ng-bootstrap/ng-bootstrap';
import { EffectsModule } from '@ngrx/effects'; import { EffectsModule } from '@ngrx/effects';
import { RouterStateSerializer, StoreRouterConnectingModule } from '@ngrx/router-store'; import { RouterStateSerializer, StoreRouterConnectingModule } from '@ngrx/router-store';
import { META_REDUCERS, MetaReducer, StoreModule } from '@ngrx/store'; import { MetaReducer, StoreModule, USER_PROVIDED_META_REDUCERS } from '@ngrx/store';
import { StoreDevtoolsModule } from '@ngrx/store-devtools'; import { StoreDevtoolsModule } from '@ngrx/store-devtools';
import { DYNAMIC_MATCHER_PROVIDERS } from '@ng-dynamic-forms/core';
import { TranslateModule } from '@ngx-translate/core'; import { TranslateModule } from '@ngx-translate/core';
import { ScrollToModule } from '@nicky-lenaers/ngx-scroll-to'; import { ScrollToModule } from '@nicky-lenaers/ngx-scroll-to';
import { storeFreeze } from 'ngrx-store-freeze';
import { ENV_CONFIG, GLOBAL_CONFIG, GlobalConfig } from '../config'; import { ENV_CONFIG, GLOBAL_CONFIG, GlobalConfig } from '../config';
import { AdminSidebarSectionComponent } from './+admin/admin-sidebar/admin-sidebar-section/admin-sidebar-section.component'; import { AdminSidebarSectionComponent } from './+admin/admin-sidebar/admin-sidebar-section/admin-sidebar-section.component';
import { AdminSidebarComponent } from './+admin/admin-sidebar/admin-sidebar.component'; import { AdminSidebarComponent } from './+admin/admin-sidebar/admin-sidebar.component';
@@ -41,6 +38,7 @@ import { DSpaceRouterStateSerializer } from './shared/ngrx/dspace-router-state-s
import { NotificationComponent } from './shared/notifications/notification/notification.component'; import { NotificationComponent } from './shared/notifications/notification/notification.component';
import { NotificationsBoardComponent } from './shared/notifications/notifications-board/notifications-board.component'; import { NotificationsBoardComponent } from './shared/notifications/notifications-board/notifications-board.component';
import { SharedModule } from './shared/shared.module'; import { SharedModule } from './shared/shared.module';
import { BreadcrumbsComponent } from './breadcrumbs/breadcrumbs.component';
export function getConfig() { export function getConfig() {
return ENV_CONFIG; return ENV_CONFIG;
@@ -51,8 +49,7 @@ export function getBase() {
} }
export function getMetaReducers(config: GlobalConfig): Array<MetaReducer<AppState>> { export function getMetaReducers(config: GlobalConfig): Array<MetaReducer<AppState>> {
const metaReducers: Array<MetaReducer<AppState>> = config.production ? appMetaReducers : [...appMetaReducers, storeFreeze]; return config.debug ? [...appMetaReducers, ...debugMetaReducers] : appMetaReducers;
return config.debug ? [...metaReducers, ...debugMetaReducers] : metaReducers;
} }
const IMPORTS = [ const IMPORTS = [
@@ -63,11 +60,11 @@ const IMPORTS = [
AppRoutingModule, AppRoutingModule,
CoreModule.forRoot(), CoreModule.forRoot(),
ScrollToModule.forRoot(), ScrollToModule.forRoot(),
NgbModule.forRoot(), NgbModule,
TranslateModule.forRoot(), TranslateModule.forRoot(),
EffectsModule.forRoot(appEffects), EffectsModule.forRoot(appEffects),
StoreModule.forRoot(appReducers), StoreModule.forRoot(appReducers),
StoreRouterConnectingModule, StoreRouterConnectingModule.forRoot(),
]; ];
const ENTITY_IMPORTS = [ const ENTITY_IMPORTS = [
@@ -92,7 +89,7 @@ const PROVIDERS = [
useFactory: (getBase) useFactory: (getBase)
}, },
{ {
provide: META_REDUCERS, provide: USER_PROVIDED_META_REDUCERS,
useFactory: getMetaReducers, useFactory: getMetaReducers,
deps: [GLOBAL_CONFIG] deps: [GLOBAL_CONFIG]
}, },
@@ -100,7 +97,8 @@ const PROVIDERS = [
provide: RouterStateSerializer, provide: RouterStateSerializer,
useClass: DSpaceRouterStateSerializer useClass: DSpaceRouterStateSerializer
}, },
ClientCookieService ClientCookieService,
...DYNAMIC_MATCHER_PROVIDERS,
]; ];
const DECLARATIONS = [ const DECLARATIONS = [
@@ -131,6 +129,7 @@ const EXPORTS = [
], ],
declarations: [ declarations: [
...DECLARATIONS, ...DECLARATIONS,
BreadcrumbsComponent,
], ],
exports: [ exports: [
...EXPORTS ...EXPORTS

Some files were not shown because too many files have changed in this diff Show More