Merge branch 'master' into w2p-63469_Relations-delete-feedback-2

Conflicts:
	src/app/+item-page/simple/item-types/shared/item-relationships-utils.ts
This commit is contained in:
Kristof De Langhe
2019-07-30 10:34:22 +02:00
218 changed files with 2967 additions and 1144 deletions

2
.gitignore vendored
View File

@@ -5,6 +5,8 @@
/tsd_typings/
npm-debug.log
/build/
/config/environment.dev.js
/config/environment.prod.js

View File

@@ -13,7 +13,7 @@ module.exports = {
host: 'dspace7.4science.cloud',
port: 443,
// NOTE: Space is capitalized because 'namespace' is a reserved string in TypeScript
nameSpace: '/dspace-spring-rest/api'
nameSpace: '/server/api'
},
// Caching settings
cache: {
@@ -149,11 +149,39 @@ module.exports = {
// Limit for years to display using jumps of five years (current year - fiveYearLimit)
fiveYearLimit: 30,
// The absolute lowest year to display in the dropdown (only used when no lowest date can be found for all items)
defaultLowerLimit: 1900
defaultLowerLimit: 1900,
// List of all the active Browse-By types
// Adding a type will activate their Browse-By page and add them to the global navigation menu, as well as community and collection pages
// Allowed fields and their purpose:
// id: The browse id to use for fetching info from the rest api
// type: The type of Browse-By page to display
// metadataField: The metadata-field used to create starts-with options (only necessary when the type is set to 'date')
types: [
{
id: 'title',
type: 'title'
},
{
id: 'dateissued',
type: 'date',
metadataField: 'dc.date.issued'
},
{
id: 'author',
type: 'metadata'
},
{
id: 'subject',
type: 'metadata'
}
]
},
item: {
edit: {
undoTimeout: 10000 // 10 seconds
}
},
theme: {
name: 'default',
}
};

View File

@@ -17,15 +17,16 @@
"clean:doc": "rimraf doc",
"clean:log": "rimraf *.log*",
"clean:json": "rimraf *.records.json",
"clean:bld": "rimraf build",
"clean:node": "rimraf node_modules",
"clean:prod": "yarn run clean:coverage && yarn run clean:doc && yarn run clean:dist && yarn run clean:log && yarn run clean:json",
"clean:prod": "yarn run clean:coverage && yarn run clean:doc && yarn run clean:dist && yarn run clean:log && yarn run clean:json && yarn run clean:bld",
"clean": "yarn run clean:prod && yarn run clean:node",
"prebuild": "yarn run clean:dist",
"prebuild": "yarn run clean:bld && yarn run clean:dist",
"prebuild:aot": "yarn run prebuild",
"prebuild:prod": "yarn run prebuild",
"build": "node ./webpack/run-webpack.js --progress --mode development",
"build:aot": "node ./webpack/run-webpack.js --env.aot --env.server --mode development && node ./webpack/run-webpack.js --env.aot --env.client --mode development",
"build:prod": "node ./webpack/run-webpack.js --env.aot --env.server --mode production && node ./webpack/run-webpack.js --env.aot --env.client --mode production",
"build": "node ./scripts/webpack.js --progress --mode development",
"build:aot": "yarn run syncbuilddir && node ./scripts/webpack.js --env.aot --env.server --mode development && node ./scripts/webpack.js --env.aot --env.client --mode development",
"build:prod": "yarn run syncbuilddir && node ./scripts/webpack.js --env.aot --env.server --mode production && node ./scripts/webpack.js --env.aot --env.client --mode production",
"postbuild:prod": "yarn run rollup",
"rollup": "rollup -c rollup.config.js",
"prestart": "yarn run build:prod",
@@ -40,7 +41,8 @@
"server": "node dist/server.js",
"server:watch": "nodemon dist/server.js",
"server:watch:debug": "nodemon --debug dist/server.js",
"webpack:watch": "node ./webpack/run-webpack.js -w --mode development",
"syncbuilddir": "node ./scripts/sync-build-dir.js",
"webpack:watch": "node ./scripts/webpack.js -w --mode development",
"watch": "yarn run build && npm-run-all -p webpack:watch server:watch",
"watch:debug": "yarn run build && npm-run-all -p webpack:watch server:watch:debug",
"predebug": "yarn run build",
@@ -94,6 +96,7 @@
"compression": "1.7.1",
"cookie-parser": "1.4.3",
"core-js": "^2.5.7",
"debug-loader": "^0.0.1",
"express": "4.16.2",
"express-session": "1.15.6",
"fast-json-patch": "^2.0.7",
@@ -121,6 +124,7 @@
"reflect-metadata": "0.1.12",
"rxjs": "6.2.2",
"rxjs-spy": "^7.5.1",
"sass-resources-loader": "^2.0.0",
"sortablejs": "1.7.0",
"text-mask-core": "5.0.1",
"ts-loader": "^5.2.1",
@@ -163,6 +167,7 @@
"codelyzer": "^4.4.4",
"compression-webpack-plugin": "^1.1.6",
"copy-webpack-plugin": "^4.4.1",
"copyfiles": "^2.1.0",
"coveralls": "3.0.0",
"css-loader": "1.0.0",
"cssnano": "^4.1.10",
@@ -213,7 +218,7 @@
"script-ext-html-webpack-plugin": "2.0.1",
"source-map": "0.7.3",
"source-map-loader": "0.2.4",
"string-replace-loader": "2.1.1",
"string-replace-loader": "^2.1.1",
"to-string-loader": "1.1.5",
"ts-helpers": "1.1.2",
"ts-node": "4.1.0",
@@ -224,6 +229,7 @@
"webpack-bundle-analyzer": "^3.3.2",
"webpack-dev-middleware": "3.2.0",
"webpack-dev-server": "^3.1.5",
"webpack-import-glob-loader": "^1.6.3",
"webpack-merge": "4.1.4",
"webpack-node-externals": "1.7.2"
}

View File

@@ -109,6 +109,16 @@
"link": {
"simple": "Simple item page",
"full": "Full item page"
},
"journal": {
"search": {
"title": "Articles in this journal"
}
},
"person": {
"search": {
"title": "Articles by this author"
}
}
},
"select": {
@@ -418,7 +428,10 @@
"titleprefix": "Publication: ",
"journal-title": "Journal Title",
"journal-issn": "Journal ISSN",
"volume-title": "Volume Title"
"volume-title": "Volume Title",
"publisher": "Publisher",
"description": "Description"
},
"listelement": {
"badge": "Publication"
@@ -641,6 +654,34 @@
"birthDate": {
"placeholder": "Birth Date",
"head": "Birth Date"
},
"creativeWorkPublisher": {
"placeholder": "Publisher",
"head": "Publisher"
},
"creativeWorkEditor": {
"placeholder": "Editor",
"head": "Editor"
},
"creativeWorkKeywords": {
"placeholder": "Subject",
"head": "Subject"
},
"creativeDatePublished": {
"placeholder": "Date Published",
"head": "Date Published"
},
"organizationAddressCountry": {
"placeholder": "Country",
"head": "Country"
},
"organizationAddressLocality": {
"placeholder": "City",
"head": "City"
},
"organizationFoundingDate": {
"placeholder": "Date Founded",
"head": "Date Founded"
}
}
}
@@ -813,7 +854,7 @@
"control_panel": "Control Panel",
"browse_global": "All of DSpace",
"browse_global_communities_and_collections": "Communities & Collections",
"browse_global_by_issue_date": "By Issue Date",
"browse_global_by_dateissued": "By Issue Date",
"browse_global_by_author": "By Author",
"browse_global_by_title": "By Title",
"browse_global_by_subject": "By Subject",

BIN
resources/images/banner.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 MiB

View File

@@ -0,0 +1,37 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
width="231.893px" height="167.458px" viewBox="0 0 231.893 167.458" enable-background="new 0 0 231.893 167.458"
xml:space="preserve">
<path fill="#43515F" d="M51.733,143.32c0-1.94,1.65-3.202,4.562-3.202c3.303,0,6.798,1.115,9.419,3.543l3.835-5.146
c-3.202-2.963-7.476-4.516-12.621-4.516c-7.622,0-12.284,4.467-12.284,9.855c0,12.188,18.644,8.254,18.644,13.887
c0,1.893-1.797,3.592-5.632,3.592c-4.466,0-8.011-2.039-10.292-4.418l-3.787,5.39c3.058,3.059,7.525,5.153,13.788,5.153
c8.691,0,12.964-4.474,12.964-10.396C70.329,144.971,51.733,148.418,51.733,143.32z M100.682,134.484H85.534v32.386h6.895v-11.557
h8.254c6.99,0,10.875-4.759,10.875-10.391C111.558,139.243,107.722,134.484,100.682,134.484z M99.71,149.245h-7.283v-8.69h7.283
c2.72,0,4.808,1.651,4.808,4.368C104.518,147.592,102.43,149.245,99.71,149.245z M180.759,140.067c3.302,0,6.215,2.09,7.573,4.71
l5.923-2.913c-2.28-4.078-6.407-7.914-13.496-7.914c-9.759,0-17.283,6.75-17.283,16.75c0,9.954,7.524,16.76,17.283,16.76
c7.089,0,11.216-3.94,13.496-7.971l-5.923-2.865c-1.358,2.623-4.271,4.711-7.573,4.711c-5.924,0-10.194-4.517-10.194-10.635
C170.564,144.583,174.835,140.067,180.759,140.067z M131.958,134.484l-12.485,32.386h7.823l2.04-5.486h13.887l2.038,5.486h7.816
l-12.479-32.386H131.958z M131.228,155.313l5.05-13.936l5.05,13.936H131.228z M231.892,140.553v-6.069h-22.916v32.386h22.916v-6.07
H215.87v-7.379h15.684v-6.069H215.87v-6.797L231.892,140.553L231.892,140.553z"/>
<path fill="#43515F" d="M29.956,150.652c0-9.71-7.04-16.168-17.187-16.168H0v32.386h12.817
C22.916,166.87,29.956,160.458,29.956,150.652z M12.769,160.799H6.894v-20.246h5.924c6.603,0,10.098,4.418,10.098,10.099
C22.916,156.187,19.177,160.799,12.769,160.799z"/>
<path fill="#43515F" d="M120.726,58.569l0.109-0.006l0.116-0.01l0.106-0.013l0.11-0.01l0.11-0.023l0.109-0.019l0.106-0.023
l0.106-0.029l0.105-0.023l0.106-0.033l0.103-0.034l0.097-0.035l0.104-0.04l0.101-0.042l0.1-0.042v-0.001l0.096-0.045l0,0
l0.095-0.044l0.097-0.049l0.091-0.056v-0.001l0.094-0.05v-0.002l0.09-0.056v-0.001l0.093-0.06l0.083-0.056v-0.001l0.085-0.063
l0.088-0.065v-0.002l0.087-0.062v-0.001c0.816-0.683,1.393-1.646,1.561-2.738l0.013-0.104V54.72l0.014-0.101v-0.011l0.009-0.098
v-0.012l0.009-0.101V54.38l0.005-0.095v-0.016l0.002-0.105v-16.46l-0.002-0.105v-0.016l-0.005-0.095v-0.013l-0.009-0.101v-0.012
l-0.009-0.098v-0.011l-0.014-0.1v-0.01l-0.013-0.104c-0.167-1.092-0.744-2.057-1.561-2.738V34.3l-0.087-0.063v-0.002l-0.088-0.065
l-0.085-0.063v-0.001l-0.083-0.056l-0.093-0.061l0,0l-0.09-0.056V33.93l-0.094-0.05v-0.001l-0.091-0.056l-0.097-0.049l-0.095-0.043
V33.73l-0.096-0.045v-0.001l-0.1-0.043l-0.101-0.042l-0.104-0.04l-0.097-0.035l-0.103-0.031l-0.106-0.036l-0.105-0.023l-0.106-0.028
l-0.106-0.024l-0.109-0.019l-0.11-0.023l-0.11-0.009l-0.106-0.014l-0.116-0.01l-0.109-0.006l-0.114-0.005h-7.89
c-9.716,0-15.858-7.838-15.858-17.15V6.92c0-3.812-3.102-6.915-6.914-6.915H74.085c-3.813,0-6.92,3.106-6.92,6.915v16.682
c0,3.806,3.104,6.909,6.92,6.909h8.414c9.169,0,16.906,5.95,17.146,15.403v0.04c-0.24,9.453-7.978,15.402-17.146,15.402h-8.414
c-3.815,0-6.92,3.103-6.92,6.909v16.682c0,3.81,3.106,6.915,6.92,6.915H89.95c3.812,0,6.914-3.104,6.914-6.915v-9.223
c0-9.312,6.144-17.149,15.858-17.149h7.89L120.726,58.569z M154.772,9.956C148.631,3.814,140.15,0,130.816,0h-15.024v17.424h15.024
c4.526,0,8.647,1.858,11.64,4.849c2.99,2.99,4.849,7.112,4.849,11.639v24.042c0,4.538-1.853,8.665-4.832,11.655l-0.017-0.016
c-2.991,2.991-7.113,4.849-11.64,4.849h-15.024v17.424h15.024c9.333,0,17.814-3.814,23.956-9.956v-0.033
c6.142-6.143,9.955-14.614,9.955-23.923V33.912C164.727,24.578,160.914,16.097,154.772,9.956z"/>
</svg>

After

Width:  |  Height:  |  Size: 3.9 KiB

22
scripts/sync-build-dir.js Normal file
View File

@@ -0,0 +1,22 @@
const syncBuildDir = require('copyfiles');
const path = require('path');
const {
projectRoot,
theme,
themePath,
} = require('../webpack/helpers');
const projectDepth = projectRoot('./').split(path.sep).length;
let callback;
if (theme !== null && theme !== undefined) {
callback = () => {
syncBuildDir([path.join(themePath, '**/*'), 'build'], { up: projectDepth + 2 }, () => {})
}
}
else {
callback = () => {};
}
syncBuildDir([projectRoot('src/**/*'), 'build'], { up: projectDepth + 1 }, callback);

View File

@@ -1,5 +1,3 @@
@import '../../../../styles/variables.scss';
.selectable-row:hover {
cursor: pointer;
}

View File

@@ -1,5 +1,3 @@
@import '../../../../styles/variables.scss';
.selectable-row:hover {
cursor: pointer;
}

View File

@@ -1 +0,0 @@
@import '../../../../styles/variables.scss';

View File

@@ -1,4 +1,4 @@
<nav @slideHorizontal class="navbar navbar-dark bg-dark p-0"
<nav @slideHorizontal class="navbar navbar-dark p-0"
[ngClass]="{'active': sidebarOpen, 'inactive': sidebarClosed}"
[@slideSidebar]="{
value: (!(sidebarExpanded | async) ? 'collapsed' : 'expanded'),

View File

@@ -1,5 +1,3 @@
@import '../../../styles/variables.scss';
@import '../../../styles/mixins.scss';
$icon-z-index: 10;
:host {
@@ -8,6 +6,7 @@ $icon-z-index: 10;
height: 100vh;
flex: 1 1 auto;
nav {
background-color: $admin-sidebar-bg;
height: 100%;
flex-direction: column;
> div {
@@ -44,7 +43,7 @@ $icon-z-index: 10;
.sidebar-section {
display: flex;
align-content: stretch;
background-color: $dark;
background-color: $admin-sidebar-bg;
.nav-item {
padding-top: $spacer;
padding-bottom: $spacer;

View File

@@ -1,5 +1,3 @@
@import '../../../../styles/variables.scss';
::ng-deep {
.fa-chevron-right {
padding-left: $spacer/2;

View File

@@ -13,6 +13,7 @@ import { BrowseService } from '../../core/browse/browse.service';
import { DSpaceObjectDataService } from '../../core/data/dspace-object-data.service';
import { GLOBAL_CONFIG, GlobalConfig } from '../../../config';
import { StartsWithType } from '../../shared/starts-with/starts-with-decorator';
import { BrowseByType, rendersBrowseBy } from '../+browse-by-switcher/browse-by-decorator';
@Component({
selector: 'ds-browse-by-date-page',
@@ -21,9 +22,10 @@ import { StartsWithType } from '../../shared/starts-with/starts-with-decorator';
})
/**
* Component for browsing items by metadata definition of type 'date'
* A metadata definition is a short term used to describe one or multiple metadata fields.
* A metadata definition (a.k.a. browse id) is a short term used to describe one or multiple metadata fields.
* An example would be 'dateissued' for 'dc.date.issued'
*/
@rendersBrowseBy(BrowseByType.Date)
export class BrowseByDatePageComponent extends BrowseByMetadataPageComponent {
/**
@@ -53,12 +55,12 @@ export class BrowseByDatePageComponent extends BrowseByMetadataPageComponent {
})
.subscribe((params) => {
const metadataField = params.metadataField || this.defaultMetadataField;
this.metadata = params.metadata || this.defaultMetadata;
this.browseId = params.id || this.defaultBrowseId;
this.startsWith = +params.startsWith || params.startsWith;
const searchOptions = browseParamsToOptions(params, Object.assign({}), this.sortConfig, this.metadata);
const searchOptions = browseParamsToOptions(params, Object.assign({}), this.sortConfig, this.browseId);
this.updatePageWithItems(searchOptions, this.value);
this.updateParent(params.scope);
this.updateStartsWithOptions(this.metadata, metadataField, params.scope);
this.updateStartsWithOptions(this.browseId, metadataField, params.scope);
}));
}
@@ -78,8 +80,9 @@ export class BrowseByDatePageComponent extends BrowseByMetadataPageComponent {
let lowerLimit = this.config.browseBy.defaultLowerLimit;
if (hasValue(firstItemRD.payload)) {
const date = firstItemRD.payload.firstMetadataValue(metadataField);
if (hasValue(date) && hasValue(+date.split('-')[0])) {
lowerLimit = +date.split('-')[0];
if (hasValue(date)) {
const dateObj = new Date(date);
lowerLimit = dateObj.getFullYear();
}
}
const options = [];

View File

@@ -1,7 +1,7 @@
<div class="container">
<div class="browse-by-metadata w-100">
<ds-browse-by *ngIf="startsWithOptions" class="col-xs-12 w-100"
title="{{'browse.title' | translate:{collection: (parent$ | async)?.payload?.name || '', field: 'browse.metadata.' + metadata | translate, value: (value)? '&quot;' + value + '&quot;': ''} }}"
title="{{'browse.title' | translate:{collection: (parent$ | async)?.payload?.name || '', field: 'browse.metadata.' + browseId | translate, value: (value)? '&quot;' + value + '&quot;': ''} }}"
[objects$]="(items$ !== undefined)? items$ : browseEntries$"
[paginationConfig]="paginationConfig"
[sortConfig]="sortConfig"

View File

@@ -15,6 +15,7 @@ import { DSpaceObjectDataService } from '../../core/data/dspace-object-data.serv
import { DSpaceObject } from '../../core/shared/dspace-object.model';
import { take } from 'rxjs/operators';
import { StartsWithType } from '../../shared/starts-with/starts-with-decorator';
import { BrowseByType, rendersBrowseBy } from '../+browse-by-switcher/browse-by-decorator';
@Component({
selector: 'ds-browse-by-metadata-page',
@@ -23,9 +24,10 @@ import { StartsWithType } from '../../shared/starts-with/starts-with-decorator';
})
/**
* Component for browsing (items) by metadata definition
* A metadata definition is a short term used to describe one or multiple metadata fields.
* A metadata definition (a.k.a. browse id) is a short term used to describe one or multiple metadata fields.
* An example would be 'author' for 'dc.contributor.*'
*/
@rendersBrowseBy(BrowseByType.Metadata)
export class BrowseByMetadataPageComponent implements OnInit {
/**
@@ -63,14 +65,14 @@ export class BrowseByMetadataPageComponent implements OnInit {
subs: Subscription[] = [];
/**
* The default metadata definition to resort to when none is provided
* The default browse id to resort to when none is provided
*/
defaultMetadata = 'author';
defaultBrowseId = 'author';
/**
* The current metadata definition
* The current browse id
*/
metadata = this.defaultMetadata;
browseId = this.defaultBrowseId;
/**
* The type of StartsWith options to render
@@ -112,10 +114,10 @@ export class BrowseByMetadataPageComponent implements OnInit {
return Object.assign({}, params, queryParams);
})
.subscribe((params) => {
this.metadata = params.metadata || this.defaultMetadata;
this.browseId = params.id || this.defaultBrowseId;
this.value = +params.value || params.value || '';
this.startsWith = +params.startsWith || params.startsWith;
const searchOptions = browseParamsToOptions(params, this.paginationConfig, this.sortConfig, this.metadata);
const searchOptions = browseParamsToOptions(params, this.paginationConfig, this.sortConfig, this.browseId);
if (isNotEmpty(this.value)) {
this.updatePageWithItems(searchOptions, this.value);
} else {

View File

@@ -0,0 +1,12 @@
import { BrowseByType, rendersBrowseBy } from './browse-by-decorator';
describe('BrowseByDecorator', () => {
const titleDecorator = rendersBrowseBy(BrowseByType.Title);
const dateDecorator = rendersBrowseBy(BrowseByType.Date);
const metadataDecorator = rendersBrowseBy(BrowseByType.Metadata);
it('should have a decorator for all types', () => {
expect(titleDecorator.length).not.toEqual(0);
expect(dateDecorator.length).not.toEqual(0);
expect(metadataDecorator.length).not.toEqual(0);
});
});

View File

@@ -0,0 +1,37 @@
import { hasNoValue } from '../../shared/empty.util';
export enum BrowseByType {
Title = 'title',
Metadata = 'metadata',
Date = 'date'
}
export const DEFAULT_BROWSE_BY_TYPE = BrowseByType.Metadata;
const map = new Map();
/**
* Decorator used for rendering Browse-By pages by type
* @param browseByType The type of page
*/
export function rendersBrowseBy(browseByType: BrowseByType) {
return function decorator(component: any) {
if (hasNoValue(map.get(browseByType))) {
map.set(browseByType, component);
} else {
throw new Error(`There can't be more than one component to render Browse-By of type "${browseByType}"`);
}
};
}
/**
* Get the component used for rendering a Browse-By page by type
* @param browseByType The type of page
*/
export function getComponentByBrowseByType(browseByType) {
const comp = map.get(browseByType);
if (hasNoValue(comp)) {
map.get(DEFAULT_BROWSE_BY_TYPE);
}
return comp;
}

View File

@@ -0,0 +1 @@
<ng-container *ngComponentOutlet="browseByComponent | async"></ng-container>

View File

@@ -0,0 +1,55 @@
import { BrowseBySwitcherComponent } from './browse-by-switcher.component';
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { NO_ERRORS_SCHEMA } from '@angular/core';
import { ENV_CONFIG, GLOBAL_CONFIG } from '../../../config';
import { ActivatedRoute } from '@angular/router';
import * as decorator from './browse-by-decorator';
import createSpy = jasmine.createSpy;
import { BehaviorSubject } from 'rxjs/internal/BehaviorSubject';
describe('BrowseBySwitcherComponent', () => {
let comp: BrowseBySwitcherComponent;
let fixture: ComponentFixture<BrowseBySwitcherComponent>;
const types = ENV_CONFIG.browseBy.types;
const params = new BehaviorSubject(createParamsWithId('initialValue'));
const activatedRouteStub = {
params: params
};
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ BrowseBySwitcherComponent ],
providers: [
{ provide: GLOBAL_CONFIG, useValue: ENV_CONFIG },
{ provide: ActivatedRoute, useValue: activatedRouteStub }
],
schemas: [ NO_ERRORS_SCHEMA ]
}).compileComponents();
}));
beforeEach(async(() => {
fixture = TestBed.createComponent(BrowseBySwitcherComponent);
comp = fixture.componentInstance;
spyOnProperty(decorator, 'getComponentByBrowseByType').and.returnValue(createSpy('getComponentByItemType'));
}));
types.forEach((type) => {
describe(`when switching to a browse-by page for "${type.id}"`, () => {
beforeEach(() => {
params.next(createParamsWithId(type.id));
fixture.detectChanges();
});
it(`should call getComponentByBrowseByType with type "${type.type}"`, () => {
expect(decorator.getComponentByBrowseByType).toHaveBeenCalledWith(type.type);
});
});
});
});
export function createParamsWithId(id) {
return { id: id };
}

View File

@@ -0,0 +1,40 @@
import { Component, Inject, OnInit } from '@angular/core';
import { ActivatedRoute } from '@angular/router';
import { Observable } from 'rxjs/internal/Observable';
import { GLOBAL_CONFIG, GlobalConfig } from '../../../config';
import { BrowseByTypeConfig } from '../../../config/browse-by-type-config.interface';
import { map, tap } from 'rxjs/operators';
import { getComponentByBrowseByType } from './browse-by-decorator';
@Component({
selector: 'ds-browse-by-switcher',
templateUrl: './browse-by-switcher.component.html'
})
/**
* Component for determining what Browse-By component to use depending on the metadata (browse ID) provided
*/
export class BrowseBySwitcherComponent implements OnInit {
/**
* Resolved browse-by component
*/
browseByComponent: Observable<any>;
public constructor(@Inject(GLOBAL_CONFIG) public config: GlobalConfig,
protected route: ActivatedRoute) {
}
/**
* Fetch the correct browse-by component by using the relevant config from environment.js
*/
ngOnInit(): void {
this.browseByComponent = this.route.params.pipe(
map((params) => {
const id = params.id;
return this.config.browseBy.types.find((config: BrowseByTypeConfig) => config.id === id);
}),
map((config: BrowseByTypeConfig) => getComponentByBrowseByType(config.type))
);
}
}

View File

@@ -1,6 +1,5 @@
import { combineLatest as observableCombineLatest } from 'rxjs';
import { Component } from '@angular/core';
import { ItemDataService } from '../../core/data/item-data.service';
import { ActivatedRoute, Router } from '@angular/router';
import { hasValue } from '../../shared/empty.util';
import {
@@ -11,6 +10,7 @@ import { BrowseEntrySearchOptions } from '../../core/browse/browse-entry-search-
import { DSpaceObjectDataService } from '../../core/data/dspace-object-data.service';
import { BrowseService } from '../../core/browse/browse.service';
import { SortDirection, SortOptions } from '../../core/cache/models/sort-options.model';
import { BrowseByType, rendersBrowseBy } from '../+browse-by-switcher/browse-by-decorator';
@Component({
selector: 'ds-browse-by-title-page',
@@ -20,6 +20,7 @@ import { SortDirection, SortOptions } from '../../core/cache/models/sort-options
/**
* Component for browsing items by title (dc.title)
*/
@rendersBrowseBy(BrowseByType.Title)
export class BrowseByTitlePageComponent extends BrowseByMetadataPageComponent {
public constructor(protected route: ActivatedRoute,
@@ -41,8 +42,8 @@ export class BrowseByTitlePageComponent extends BrowseByMetadataPageComponent {
return Object.assign({}, params, queryParams, data);
})
.subscribe((params) => {
this.metadata = params.metadata || this.defaultMetadata;
this.updatePageWithItems(browseParamsToOptions(params, this.paginationConfig, this.sortConfig, this.metadata), undefined);
this.browseId = params.id || this.defaultBrowseId;
this.updatePageWithItems(browseParamsToOptions(params, this.paginationConfig, this.sortConfig, this.browseId), undefined);
this.updateParent(params.scope)
}));
this.updateStartsWithTextOptions();

View File

@@ -1,6 +1,7 @@
import { first } from 'rxjs/operators';
import { BrowseByGuard } from './browse-by-guard';
import { of as observableOf } from 'rxjs';
import { ENV_CONFIG } from '../../config';
describe('BrowseByGuard', () => {
describe('canActivate', () => {
@@ -11,20 +12,20 @@ describe('BrowseByGuard', () => {
const name = 'An interesting DSO';
const title = 'Author';
const field = 'Author';
const metadata = 'author';
const id = 'author';
const metadataField = 'dc.contributor';
const scope = '1234-65487-12354-1235';
const value = 'Filter';
beforeEach(() => {
dsoService = {
findById: (id: string) => observableOf({ payload: { name: name }, hasSucceeded: true })
findById: (dsoId: string) => observableOf({ payload: { name: name }, hasSucceeded: true })
};
translateService = {
instant: () => field
};
guard = new BrowseByGuard(dsoService, translateService);
guard = new BrowseByGuard(ENV_CONFIG, dsoService, translateService);
});
it('should return true, and sets up the data correctly, with a scope and value', () => {
@@ -34,7 +35,7 @@ describe('BrowseByGuard', () => {
metadataField,
},
params: {
metadata,
id,
},
queryParams: {
scope,
@@ -47,7 +48,7 @@ describe('BrowseByGuard', () => {
(canActivate) => {
const result = {
title,
metadata,
id,
metadataField,
collection: name,
field,
@@ -66,7 +67,7 @@ describe('BrowseByGuard', () => {
metadataField,
},
params: {
metadata,
id,
},
queryParams: {
scope
@@ -79,7 +80,7 @@ describe('BrowseByGuard', () => {
(canActivate) => {
const result = {
title,
metadata,
id,
metadataField,
collection: name,
field,
@@ -98,7 +99,7 @@ describe('BrowseByGuard', () => {
metadataField,
},
params: {
metadata,
id,
},
queryParams: {
value
@@ -110,7 +111,7 @@ describe('BrowseByGuard', () => {
(canActivate) => {
const result = {
title,
metadata,
id,
metadataField,
collection: '',
field,

View File

@@ -1,11 +1,12 @@
import { ActivatedRouteSnapshot, CanActivate, RouterStateSnapshot } from '@angular/router';
import { Injectable } from '@angular/core';
import { Inject, Injectable } from '@angular/core';
import { DSpaceObjectDataService } from '../core/data/dspace-object-data.service';
import { hasValue } from '../shared/empty.util';
import { hasNoValue, hasValue } from '../shared/empty.util';
import { map } from 'rxjs/operators';
import { getSucceededRemoteData } from '../core/shared/operators';
import { TranslateService } from '@ngx-translate/core';
import { of as observableOf } from 'rxjs';
import { GLOBAL_CONFIG, GlobalConfig } from '../../config';
@Injectable()
/**
@@ -13,36 +14,43 @@ import { of as observableOf } from 'rxjs';
*/
export class BrowseByGuard implements CanActivate {
constructor(protected dsoService: DSpaceObjectDataService,
constructor(@Inject(GLOBAL_CONFIG) public config: GlobalConfig,
protected dsoService: DSpaceObjectDataService,
protected translate: TranslateService) {
}
canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot) {
const title = route.data.title;
const metadata = route.params.metadata || route.queryParams.metadata || route.data.metadata;
const metadataField = route.data.metadataField;
const id = route.params.id || route.queryParams.id || route.data.id;
let metadataField = route.data.metadataField;
if (hasNoValue(metadataField) && hasValue(id)) {
const config = this.config.browseBy.types.find((conf) => conf.id === id);
if (hasValue(config) && hasValue(config.metadataField)) {
metadataField = config.metadataField;
}
}
const scope = route.queryParams.scope;
const value = route.queryParams.value;
const metadataTranslated = this.translate.instant('browse.metadata.' + metadata);
const metadataTranslated = this.translate.instant('browse.metadata.' + id);
if (hasValue(scope)) {
const dsoAndMetadata$ = this.dsoService.findById(scope).pipe(getSucceededRemoteData());
return dsoAndMetadata$.pipe(
map((dsoRD) => {
const name = dsoRD.payload.name;
route.data = this.createData(title, metadata, metadataField, name, metadataTranslated, value);
route.data = this.createData(title, id, metadataField, name, metadataTranslated, value);
return true;
})
);
} else {
route.data = this.createData(title, metadata, metadataField, '', metadataTranslated, value);
route.data = this.createData(title, id, metadataField, '', metadataTranslated, value);
return observableOf(true);
}
}
private createData(title, metadata, metadataField, collection, field, value) {
private createData(title, id, metadataField, collection, field, value) {
return {
title: title,
metadata: metadata,
id: id,
metadataField: metadataField,
collection: collection,
field: field,

View File

@@ -1,16 +1,12 @@
import { RouterModule } from '@angular/router';
import { NgModule } from '@angular/core';
import { BrowseByTitlePageComponent } from './+browse-by-title-page/browse-by-title-page.component';
import { BrowseByMetadataPageComponent } from './+browse-by-metadata-page/browse-by-metadata-page.component';
import { BrowseByDatePageComponent } from './+browse-by-date-page/browse-by-date-page.component';
import { BrowseByGuard } from './browse-by-guard';
import { BrowseBySwitcherComponent } from './+browse-by-switcher/browse-by-switcher.component';
@NgModule({
imports: [
RouterModule.forChild([
{ path: 'title', component: BrowseByTitlePageComponent, canActivate: [BrowseByGuard], data: { metadata: 'title', title: 'browse.title' } },
{ path: 'dateissued', component: BrowseByDatePageComponent, canActivate: [BrowseByGuard], data: { metadata: 'dateissued', metadataField: 'dc.date.issued', title: 'browse.title' } },
{ path: ':metadata', component: BrowseByMetadataPageComponent, canActivate: [BrowseByGuard], data: { title: 'browse.title' } }
{ path: ':id', component: BrowseBySwitcherComponent, canActivate: [BrowseByGuard], data: { title: 'browse.title' } }
])
]
})

View File

@@ -8,6 +8,7 @@ import { BrowseService } from '../core/browse/browse.service';
import { BrowseByMetadataPageComponent } from './+browse-by-metadata-page/browse-by-metadata-page.component';
import { BrowseByDatePageComponent } from './+browse-by-date-page/browse-by-date-page.component';
import { BrowseByGuard } from './browse-by-guard';
import { BrowseBySwitcherComponent } from './+browse-by-switcher/browse-by-switcher.component';
@NgModule({
imports: [
@@ -18,12 +19,18 @@ import { BrowseByGuard } from './browse-by-guard';
declarations: [
BrowseByTitlePageComponent,
BrowseByMetadataPageComponent,
BrowseByDatePageComponent
BrowseByDatePageComponent,
BrowseBySwitcherComponent
],
providers: [
ItemDataService,
BrowseService,
BrowseByGuard
],
entryComponents: [
BrowseByTitlePageComponent,
BrowseByMetadataPageComponent,
BrowseByDatePageComponent
]
})
export class BrowseByModule {

View File

@@ -1 +0,0 @@
@import '../../styles/variables.scss';

View File

@@ -1 +0,0 @@
@import '../../styles/variables.scss';

View File

@@ -1 +0,0 @@
@import '../../../styles/variables.scss';

View File

@@ -1 +0,0 @@
@import '../../../styles/variables.scss';

View File

@@ -1,16 +1,14 @@
@import '../../../styles/variables.scss';
:host {
display: block;
margin-top: -$content-spacing;
margin-bottom: -$content-spacing;
display: block;
margin-top: -$content-spacing;
margin-bottom: -$content-spacing;
}
.display-3 {
word-break: break-word;
word-break: break-word;
}
.dspace-logo {
height: 110px;
width: 110px;
}
height: 110px;
width: 110px;
}

View File

@@ -1 +1,2 @@
@import '../../styles/variables.scss';
:host {
}

View File

@@ -1,5 +1,3 @@
@import '../../../styles/variables.scss';
.btn {
min-width: $edit-item-button-min-width;
}

View File

@@ -1 +0,0 @@
@import '../../../../styles/variables.scss';

View File

@@ -1,4 +1,3 @@
@import '../../../../../styles/variables.scss';
.btn[disabled] {
color: $gray-600;
border-color: $gray-600;

View File

@@ -1,5 +1,3 @@
@import '../../../../styles/variables.scss';
.button-row {
.btn {
margin-right: 0.5 * $spacer;

View File

@@ -1,5 +1,3 @@
@import '../../../../styles/variables.scss';
:host {
.simple-view-element {
margin-bottom: 15px;

View File

@@ -1 +0,0 @@
@import '../../../../styles/variables.scss';

View File

@@ -1 +0,0 @@
@import '../../../../styles/variables.scss';

View File

@@ -1,5 +1,3 @@
@import '../../../../../styles/variables';
@import '../../../../../styles/mixins';
@media screen and (min-width: map-get($grid-breakpoints, md)) {
dt {
text-align: right;

View File

@@ -1,5 +1,3 @@
@import '../../../styles/variables.scss';
:host {
div.simple-view-link {
text-align: center;

View File

@@ -21,6 +21,10 @@
[fields]="['journalvolume.identifier.name']"
[label]="'publication.page.volume-title'">
</ds-generic-item-page-field>
<ds-generic-item-page-field [item]="item"
[fields]="['dc.publisher']"
[label]="'publication.page.publisher'">
</ds-generic-item-page-field>
</div>
<div class="col-xs-12 col-md-6">
<ds-metadata-representation-list
@@ -40,6 +44,11 @@
[label]="'relationships.isJournalIssueOf' | translate">
</ds-related-items>
<ds-item-page-abstract-field [item]="item"></ds-item-page-abstract-field>
<ds-generic-item-page-field [item]="item"
[fields]="['dc.description']"
[label]="'publication.page.description'">
</ds-generic-item-page-field>
<ds-generic-item-page-field [item]="item"
[fields]="['dc.subject']"
[separator]="','"

View File

@@ -7,7 +7,7 @@ import {
} from '../../../../shared/items/item-type-decorator';
import { ItemComponent } from '../shared/item.component';
import { MetadataRepresentation } from '../../../../core/shared/metadata-representation/metadata-representation.model';
import { filterRelationsByTypeLabel, relationsToItems } from '../shared/item-relationships-utils';
import { getRelatedItemsByTypeLabel } from '../shared/item-relationships-utils';
@rendersItemType('Publication', ItemViewMode.Full)
@rendersItemType(DEFAULT_ITEM_TYPE, ItemViewMode.Full)
@@ -46,18 +46,15 @@ export class PublicationComponent extends ItemComponent implements OnInit {
this.authors$ = this.buildRepresentations('Person', 'dc.contributor.author');
this.projects$ = this.resolvedRelsAndTypes$.pipe(
filterRelationsByTypeLabel('isProjectOfPublication'),
relationsToItems(this.item.id)
getRelatedItemsByTypeLabel(this.item.id, 'isProjectOfPublication')
);
this.orgUnits$ = this.resolvedRelsAndTypes$.pipe(
filterRelationsByTypeLabel('isOrgUnitOfPublication'),
relationsToItems(this.item.id)
getRelatedItemsByTypeLabel(this.item.id, 'isOrgUnitOfPublication')
);
this.journalIssues$ = this.resolvedRelsAndTypes$.pipe(
filterRelationsByTypeLabel('isJournalIssueOfPublication'),
relationsToItems(this.item.id)
getRelatedItemsByTypeLabel(this.item.id, 'isJournalIssueOfPublication')
);
}

View File

@@ -2,12 +2,12 @@ import { ItemMetadataRepresentation } from '../../../../core/shared/metadata-rep
import { MetadataRepresentation } from '../../../../core/shared/metadata-representation/metadata-representation.model';
import { MetadatumRepresentation } from '../../../../core/shared/metadata-representation/metadatum/metadatum-representation.model';
import { MetadataValue } from '../../../../core/shared/metadata.models';
import { getSucceededRemoteData } from '../../../../core/shared/operators';
import { hasValue } from '../../../../shared/empty.util';
import { getRemoteDataPayload, getSucceededRemoteData } from '../../../../core/shared/operators';
import { hasNoValue, hasValue } from '../../../../shared/empty.util';
import { Observable } from 'rxjs/internal/Observable';
import { Relationship } from '../../../../core/shared/item-relationships/relationship.model';
import { RelationshipType } from '../../../../core/shared/item-relationships/relationship-type.model';
import { distinctUntilChanged, filter, flatMap, map, tap } from 'rxjs/operators';
import { distinctUntilChanged, filter, flatMap, map, switchMap, tap } from 'rxjs/operators';
import { of as observableOf, zip as observableZip, combineLatest as observableCombineLatest } from 'rxjs';
import { ItemDataService } from '../../../../core/data/item-data.service';
import { Item } from '../../../../core/shared/item.model';
@@ -44,23 +44,36 @@ export const compareArraysUsingIds = <T extends { id: string }>() =>
/**
* Fetch the relationships which match the type label given
* @param {string} label Type label
* @param thisId The item's id of which the relations belong to
* @returns {(source: Observable<[Relationship[] , RelationshipType[]]>) => Observable<Relationship[]>}
*/
export const filterRelationsByTypeLabel = (label: string) =>
export const filterRelationsByTypeLabel = (label: string, thisId?: string) =>
(source: Observable<[Relationship[], RelationshipType[]]>): Observable<Relationship[]> =>
source.pipe(
map(([relsCurrentPage, relTypesCurrentPage]) =>
relsCurrentPage.filter((rel: Relationship, idx: number) =>
hasValue(relTypesCurrentPage[idx]) && (relTypesCurrentPage[idx].leftLabel === label ||
relTypesCurrentPage[idx].rightLabel === label)
)
),
switchMap(([relsCurrentPage, relTypesCurrentPage]) => {
const relatedItems$ = observableZip(...relsCurrentPage.map((rel: Relationship) =>
observableCombineLatest(
rel.leftItem.pipe(getSucceededRemoteData(), getRemoteDataPayload()),
rel.rightItem.pipe(getSucceededRemoteData(), getRemoteDataPayload()))
)
);
return relatedItems$.pipe(
map((arr) => relsCurrentPage.filter((rel: Relationship, idx: number) =>
hasValue(relTypesCurrentPage[idx]) && (
(hasNoValue(thisId) && (relTypesCurrentPage[idx].leftLabel === label ||
relTypesCurrentPage[idx].rightLabel === label)) ||
(thisId === arr[idx][0].id && relTypesCurrentPage[idx].leftLabel === label) ||
(thisId === arr[idx][1].id && relTypesCurrentPage[idx].rightLabel === label)
)
))
);
}),
distinctUntilChanged(compareArraysUsingIds())
);
/**
* Operator for turning a list of relationships into a list of the relevant items
* @param {string} thisId The item's id of which the relations belong to
* @param {string} thisId The item's id of which the relations belong to
* @returns {(source: Observable<Relationship[]>) => Observable<Item[]>}
*/
export const relationsToItems = (thisId: string) =>
@@ -80,10 +93,25 @@ export const relationsToItems = (thisId: string) =>
} else if (rightItem.payload.id === thisId) {
return leftItem.payload;
}
})),
})
.filter((item: Item) => hasValue(item))
),
distinctUntilChanged(compareArraysUsingIds()),
);
/**
* Operator for turning a list of relationships and their relationship-types into a list of relevant items by relationship label
* @param thisId The item's id of which the relations belong to
* @param label The label of the relationship-type to filter on
* @param side Filter only on one side of the relationship (for example: child-parent relationships)
*/
export const getRelatedItemsByTypeLabel = (thisId: string, label: string) =>
(source: Observable<[Relationship[], RelationshipType[]]>): Observable<Item[]> =>
source.pipe(
filterRelationsByTypeLabel(label, thisId),
relationsToItems(thisId)
);
/**
* Operator for turning a list of relationships into a list of metadatarepresentations given the original metadata
* @param parentId The id of the parent item

View File

@@ -104,7 +104,9 @@ export function containsFieldInput(fields: DebugElement[], metadataKey: string):
export function createRelationshipsObservable() {
return observableOf(new RemoteData(false, false, true, null, new PaginatedList(new PageInfo(), [
Object.assign(new Relationship(), {
relationshipType: observableOf(new RemoteData(false, false, true, null, new RelationshipType()))
relationshipType: observableOf(new RemoteData(false, false, true, null, new RelationshipType())),
leftItem: observableOf(new RemoteData(false, false, true, null, new Item())),
rightItem: observableOf(new RemoteData(false, false, true, null, new Item()))
})
])));
}

View File

@@ -1,5 +1,3 @@
@import '../../styles/variables.scss';
.login-logo {
height: $login-logo-height;
width: $login-logo-width;

View File

@@ -1,5 +1,3 @@
@import '../../../../../styles/variables.scss';
@import '../../../../../styles/mixins.scss';
.filters {
.toggle-more-filters a {

View File

@@ -1,9 +1,9 @@
<a *ngIf="isVisible | async" class="d-flex flex-row"
[routerLink]="[getSearchLink()]"
[queryParams]="addQueryParams" queryParamsHandling="merge">
<input type="checkbox" [checked]="false" class="my-1 align-self-stretch"/>
<span class="filter-value px-1">{{filterValue.value}}</span>
<span class="float-right filter-value-count ml-auto">
[routerLink]="[getSearchLink()]"
[queryParams]="addQueryParams" queryParamsHandling="merge">
<input type="checkbox" [checked]="false" class="my-1 align-self-stretch"/>
<span class="filter-value px-1">{{filterValue.value}}</span>
<span class="float-right filter-value-count ml-auto">
<span class="badge badge-secondary badge-pill">{{filterValue.count}}</span>
</span>
</a>
</a>

View File

@@ -1,5 +1,3 @@
@import '../../../../../../styles/variables.scss';
a {
color: $body-color;
&:hover, &focus {

View File

@@ -1,8 +1,8 @@
<a *ngIf="isVisible | async" class="d-flex flex-row"
[routerLink]="[getSearchLink()]"
[queryParams]="changeQueryParams" queryParamsHandling="merge">
<span class="filter-value px-1">{{filterValue.label}}</span>
<span class="float-right filter-value-count ml-auto">
<span class="filter-value px-1">{{filterValue.label}}</span>
<span class="float-right filter-value-count ml-auto">
<span class="badge badge-secondary badge-pill">{{filterValue.count}}</span>
</span>
</a>

View File

@@ -1,5 +1,3 @@
@import '../../../../../../styles/variables.scss';
a {
color: $link-color;
&:hover {

View File

@@ -18,6 +18,7 @@ const rangeDelimiter = '-';
@Component({
selector: 'ds-search-facet-range-option',
styleUrls: ['./search-facet-range-option.component.scss'],
// templateUrl: './search-facet-range-option.component.html',
templateUrl: './search-facet-range-option.component.html',
})

View File

@@ -1,5 +1,3 @@
@import '../../../../../../styles/variables.scss';
a {
color: $body-color;
&:hover, &focus {

View File

@@ -1,13 +1,10 @@
@import '../../../../styles/variables.scss';
@import '../../../../styles/mixins.scss';
:host .facet-filter {
border: 1px solid map-get($theme-colors, light);
cursor: pointer;
.search-filter-wrapper.closed {
overflow: hidden;
}
.filter-toggle {
line-height: $line-height-base;
}
border: 1px solid map-get($theme-colors, light);
cursor: pointer;
.search-filter-wrapper.closed {
overflow: hidden;
}
.filter-toggle {
line-height: $line-height-base;
}
}

View File

@@ -1,6 +1,3 @@
@import '../../../../../styles/variables.scss';
@import '../../../../../styles/mixins.scss';
.filters {
.toggle-more-filters a {
color: $link-color;

View File

@@ -1,14 +1,10 @@
@import '../../../../../styles/variables.scss';
@import '../../../../../styles/mixins.scss';
.filters {
.toggle-more-filters a {
color: $link-color;
text-decoration: underline;
cursor: pointer;
}
}
.toggle-more-filters a {
color: $link-color;
text-decoration: underline;
cursor: pointer;
}
}
$slider-handle-width: 18px;
::ng-deep

View File

@@ -1,5 +1,3 @@
@import '../../../../../styles/variables.scss';
@import '../../../../../styles/mixins.scss';
.filters {
.toggle-more-filters a {

View File

@@ -1,2 +0,0 @@
@import '../../../styles/variables.scss';
@import '../../../styles/mixins.scss';

View File

@@ -1,5 +1,3 @@
@import '../../styles/variables.scss';
@import '../../styles/mixins.scss';
@include media-breakpoint-down(md) {
.container {

View File

@@ -1,5 +1,3 @@
@import '../../../styles/variables.scss';
.setting-option {
border: 1px solid map-get($theme-colors, light);
border: 1px solid map-get($theme-colors, light);
}

View File

@@ -1,6 +1,3 @@
@import '../../../styles/variables.scss';
@import '../../../styles/mixins.scss';
:host {
.results {
line-height: $button-height;

View File

@@ -9,7 +9,6 @@
<ds-notifications-board
[options]="config.notifications">
</ds-notifications-board>
<main class="main-content">
<div class="container" *ngIf="isLoading">
<ds-loading message="{{'loading.default' | translate}}"></ds-loading>

View File

@@ -1,4 +1,3 @@
@import '../styles/variables.scss';
@import '../styles/helpers/font_awesome_imports.scss';
@import '../../node_modules/bootstrap/scss/bootstrap.scss';
@import '../../node_modules/nouislider/distribute/nouislider.min';
@@ -48,4 +47,3 @@ ds-admin-sidebar {
position: fixed;
z-index: $sidebar-z-index;
}

View File

@@ -28,10 +28,10 @@ import variables from '../styles/_exposed_variables.scss';
import { CSSVariableService } from './shared/sass-helper/sass-helper.service';
import { MenuService } from './shared/menu/menu.service';
import { MenuID } from './shared/menu/initial-menus-state';
import { Observable } from 'rxjs/internal/Observable';
import { combineLatest as combineLatestObservable, Observable, of } from 'rxjs';
import { slideSidebarPadding } from './shared/animations/slide';
import { combineLatest as combineLatestObservable } from 'rxjs';
import { HostWindowService } from './shared/host-window.service';
import { Theme } from '../config/theme.inferface';
@Component({
selector: 'ds-app',
@@ -47,6 +47,7 @@ export class AppComponent implements OnInit, AfterViewInit {
slideSidebarOver: Observable<boolean>;
collapsedSidebarWidth: Observable<string>;
totalSidebarWidth: Observable<string>;
theme: Observable<Theme> = of({} as any);
constructor(
@Inject(GLOBAL_CONFIG) public config: GlobalConfig,
@@ -59,7 +60,7 @@ export class AppComponent implements OnInit, AfterViewInit {
private router: Router,
private cssService: CSSVariableService,
private menuService: MenuService,
private windowService: HostWindowService
private windowService: HostWindowService,
) {
// Load all the languages that are defined as active from the config file
translate.addLangs(config.languages.filter((LangConfig) => LangConfig.active === true).map((a) => a.code));
@@ -83,6 +84,7 @@ export class AppComponent implements OnInit, AfterViewInit {
}
ngOnInit() {
const env: string = this.config.production ? 'Production' : 'Development';
const color: string = this.config.production ? 'red' : 'green';
console.info(`Environment: %c${env}`, `color: ${color}; font-weight: bold;`);

View File

@@ -1,4 +1,3 @@
import { ObjectCacheEffects } from './cache/object-cache.effects';
import { UUIDIndexEffects } from './index/index.effects';
import { RequestEffects } from './data/request.effects';

View File

@@ -60,6 +60,7 @@ import { HALEndpointService } from './shared/hal-endpoint.service';
import { FacetValueResponseParsingService } from './data/facet-value-response-parsing.service';
import { FacetValueMapResponseParsingService } from './data/facet-value-map-response-parsing.service';
import { FacetConfigResponseParsingService } from './data/facet-config-response-parsing.service';
import { ResourcePolicyService } from './data/resource-policy.service';
import { RegistryService } from './registry/registry.service';
import { RegistryMetadataschemasResponseParsingService } from './data/registry-metadataschemas-response-parsing.service';
import { RegistryMetadatafieldsResponseParsingService } from './data/registry-metadatafields-response-parsing.service';
@@ -126,6 +127,7 @@ const PROVIDERS = [
MetadataService,
ObjectCacheService,
PaginationComponentOptions,
ResourcePolicyService,
RegistryService,
NormalizedObjectBuildService,
RemoteDataBuildService,

View File

@@ -89,7 +89,7 @@ describe('ComColDataService', () => {
function initMockCommunityDataService(): CommunityDataService {
return jasmine.createSpyObj('responseCache', {
getEndpoint: hot('--a-', { a: communitiesEndpoint }),
getIDHref: cold('b-', { b: communityEndpoint })
getIDHref: communityEndpoint
});
}

View File

@@ -31,7 +31,7 @@ export abstract class ComColDataService<T extends CacheableObject> extends DataS
return this.halService.getEndpoint(linkPath);
} else {
const scopeCommunityHrefObs = this.cds.getEndpoint().pipe(
mergeMap((endpoint: string) => this.cds.getIDHref(endpoint, options.scopeID)),
map((endpoint: string) => this.cds.getIDHref(endpoint, options.scopeID)),
filter((href: string) => isNotEmpty(href)),
take(1),
tap((href: string) => {

View File

@@ -13,15 +13,38 @@ import { MetadataSchema } from '../metadata/metadataschema.model';
import { NormalizedObjectBuildService } from '../cache/builders/normalized-object-build.service';
import { HttpClient } from '@angular/common/http';
import { NotificationsService } from '../../shared/notifications/notifications.service';
import { ChangeAnalyzer } from './change-analyzer';
import { DefaultChangeAnalyzer } from './default-change-analyzer.service';
/* tslint:disable:max-classes-per-file */
class DataServiceImpl extends DataService<MetadataSchema> {
protected linkPath = 'metadataschemas';
protected forceBypassCache = false;
constructor(
protected requestService: RequestService,
protected rdbService: RemoteDataBuildService,
protected dataBuildService: NormalizedObjectBuildService,
protected store: Store<CoreState>,
protected objectCache: ObjectCacheService,
protected halService: HALEndpointService,
protected notificationsService: NotificationsService,
protected http: HttpClient,
protected comparator: ChangeAnalyzer<MetadataSchema>) {
super();
}
getBrowseEndpoint(options: FindAllOptions = {}, linkPath: string = this.linkPath): Observable<string> {
return this.halService.getEndpoint(linkPath);
}
}
/**
* A service responsible for fetching/sending data from/to the REST API on the metadataschemas endpoint
*/
@Injectable()
export class MetadataSchemaDataService extends DataService<MetadataSchema> {
protected linkPath = 'metadataschemas';
protected forceBypassCache = false;
export class MetadataSchemaDataService {
private dataService: DataServiceImpl;
constructor(
protected requestService: RequestService,
@@ -33,17 +56,6 @@ export class MetadataSchemaDataService extends DataService<MetadataSchema> {
protected dataBuildService: NormalizedObjectBuildService,
protected http: HttpClient,
protected notificationsService: NotificationsService) {
super();
this.dataService = new DataServiceImpl(requestService, rdbService, dataBuildService, null, objectCache, halService, notificationsService, http, comparator);
}
/**
* Get the endpoint for browsing metadataschemas
* @param {FindAllOptions} options
* @returns {Observable<string>}
*/
public getBrowseEndpoint(options: FindAllOptions = {}, linkPath: string = this.linkPath): Observable<string> {
return null;
}
}

View File

@@ -0,0 +1,77 @@
import { cold, getTestScheduler } from 'jasmine-marbles';
import { TestScheduler } from 'rxjs/testing';
import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service';
import { ResourcePolicy } from '../shared/resource-policy.model';
import { HALEndpointService } from '../shared/hal-endpoint.service';
import { GetRequest } from './request.models';
import { RequestService } from './request.service';
import { ResourcePolicyService } from './resource-policy.service';
import { ObjectCacheService } from '../cache/object-cache.service';
import { NotificationsService } from '../../shared/notifications/notifications.service';
import { HttpClient } from '@angular/common/http';
import { NormalizedObjectBuildService } from '../cache/builders/normalized-object-build.service';
describe('ResourcePolicyService', () => {
let scheduler: TestScheduler;
let service: ResourcePolicyService;
let requestService: RequestService;
let rdbService: RemoteDataBuildService;
let objectCache: ObjectCacheService;
const testObject = {
uuid: '664184ee-b254-45e8-970d-220e5ccc060b'
} as ResourcePolicy;
const requestURL = `https://rest.api/rest/api/resourcepolicies/${testObject.uuid}`;
const requestUUID = '8b3c613a-5a4b-438b-9686-be1d5b4a1c5a';
beforeEach(() => {
scheduler = getTestScheduler();
requestService = jasmine.createSpyObj('requestService', {
generateRequestId: requestUUID,
configure: true
});
rdbService = jasmine.createSpyObj('rdbService', {
buildSingle: cold('a', {
a: {
payload: testObject
}
})
});
objectCache = {} as ObjectCacheService;
const halService = {} as HALEndpointService;
const notificationsService = {} as NotificationsService;
const http = {} as HttpClient;
const comparator = {} as any;
const dataBuildService = {} as NormalizedObjectBuildService;
service = new ResourcePolicyService(
requestService,
rdbService,
dataBuildService,
objectCache,
halService,
notificationsService,
http,
comparator
)
});
describe('findByHref', () => {
it('should configure the proper GetRequest', () => {
scheduler.schedule(() => service.findByHref(requestURL));
scheduler.flush();
expect(requestService.configure).toHaveBeenCalledWith(new GetRequest(requestUUID, requestURL, null), false);
});
it('should return a RemoteData<ResourcePolicy> for the object with the given URL', () => {
const result = service.findByHref(requestURL);
const expected = cold('a', {
a: {
payload: testObject
}
});
expect(result).toBeObservable(expected);
});
});
});

View File

@@ -0,0 +1,67 @@
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Store } from '@ngrx/store';
import { Observable } from 'rxjs';
import { DataService } from '../data/data.service';
import { RequestService } from '../data/request.service';
import { FindAllOptions } from '../data/request.models';
import { HALEndpointService } from '../shared/hal-endpoint.service';
import { ResourcePolicy } from '../shared/resource-policy.model';
import { RemoteData } from '../data/remote-data';
import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service';
import { CoreState } from '../core.reducers';
import { ObjectCacheService } from '../cache/object-cache.service';
import { NotificationsService } from '../../shared/notifications/notifications.service';
import { NormalizedObjectBuildService } from '../cache/builders/normalized-object-build.service';
import { ChangeAnalyzer } from './change-analyzer';
import { DefaultChangeAnalyzer } from '../data/default-change-analyzer.service';
import { HttpOptions } from '../dspace-rest-v2/dspace-rest-v2.service';
/* tslint:disable:max-classes-per-file */
class DataServiceImpl extends DataService<ResourcePolicy> {
protected linkPath = 'resourcepolicies';
protected forceBypassCache = false;
constructor(
protected requestService: RequestService,
protected rdbService: RemoteDataBuildService,
protected dataBuildService: NormalizedObjectBuildService,
protected store: Store<CoreState>,
protected objectCache: ObjectCacheService,
protected halService: HALEndpointService,
protected notificationsService: NotificationsService,
protected http: HttpClient,
protected comparator: ChangeAnalyzer<ResourcePolicy>) {
super();
}
getBrowseEndpoint(options: FindAllOptions = {}, linkPath: string = this.linkPath): Observable<string> {
return this.halService.getEndpoint(linkPath);
}
}
/**
* A service responsible for fetching/sending data from/to the REST API on the resourcepolicies endpoint
*/
@Injectable()
export class ResourcePolicyService {
private dataService: DataServiceImpl;
constructor(
protected requestService: RequestService,
protected rdbService: RemoteDataBuildService,
protected dataBuildService: NormalizedObjectBuildService,
protected objectCache: ObjectCacheService,
protected halService: HALEndpointService,
protected notificationsService: NotificationsService,
protected http: HttpClient,
protected comparator: DefaultChangeAnalyzer<ResourcePolicy>) {
this.dataService = new DataServiceImpl(requestService, rdbService, dataBuildService, null, objectCache, halService, notificationsService, http, comparator);
}
findByHref(href: string, options?: HttpOptions): Observable<RemoteData<ResourcePolicy>> {
return this.dataService.findByHref(href, options);
}
}

View File

@@ -4,14 +4,14 @@
[innerHTML]="firstMetadataValue('dc.title')"></a>
<span class="text-muted">
<ds-truncatable-part [id]="item.id" [minLines]="1">
<span *ngIf="item.allMetadata(['journalvolume.identifier.volume']).length > 0"
<span *ngIf="item.allMetadata(['publicationvolume.volumeNumber']).length > 0"
class="item-list-journal-issues">
<span *ngFor="let value of allMetadataValues(['journalvolume.identifier.volume']); let last=last;">
<span *ngFor="let value of allMetadataValues(['publicationvolume.volumeNumber']); let last=last;">
<span [innerHTML]="value"><span [innerHTML]="value"></span></span>
</span>
<span *ngIf="item.allMetadata(['journalissue.identifier.number']).length > 0"
<span *ngIf="item.allMetadata(['publicationissue.issueNumber']).length > 0"
class="item-list-journal-issue-numbers">
<span *ngFor="let value of allMetadataValues(['journalissue.identifier.number']); let last=last;">
<span *ngFor="let value of allMetadataValues(['publicationissue.issueNumber']); let last=last;">
<span> - </span><span [innerHTML]="value"><span [innerHTML]="value"></span></span>
</span>
</span>

View File

@@ -20,13 +20,13 @@ const mockItemWithMetadata: Item = Object.assign(new Item(), {
value: 'This is just another title'
}
],
'journalvolume.identifier.volume': [
'publicationvolume.volumeNumber': [
{
language: 'en_US',
value: '1234'
}
],
'journalissue.identifier.number': [
'publicationissue.issueNumber': [
{
language: 'en_US',
value: '5678'

View File

@@ -10,9 +10,9 @@
<span [innerHTML]="value"><span [innerHTML]="value"></span></span>
</span>
</span>
<span *ngIf="item.allMetadata(['journalvolume.identifier.volume']).length > 0"
<span *ngIf="item.allMetadata(['publicationvolume.volumeNumber']).length > 0"
class="item-list-journal-volume-identifiers">
<span *ngFor="let value of allMetadataValues(['journalvolume.identifier.volume']); let last=last;">
<span *ngFor="let value of allMetadataValues(['publicationvolume.volumeNumber']); let last=last;">
<span> (</span><span [innerHTML]="value"><span [innerHTML]="value"></span></span><span>)</span>
</span>
</span>

View File

@@ -26,7 +26,7 @@ const mockItemWithMetadata: Item = Object.assign(new Item(), {
value: 'This is just another journal title'
}
],
'journalvolume.identifier.volume': [
'publicationvolume.volumeNumber': [
{
language: 'en_US',
value: '1234'

View File

@@ -4,9 +4,9 @@
[innerHTML]="firstMetadataValue('dc.title')"></a>
<span class="text-muted">
<ds-truncatable-part [id]="item.id" [minLines]="1">
<span *ngIf="item.allMetadata(['journal.identifier.issn']).length > 0"
<span *ngIf="item.allMetadata(['creativeworkseries.issn']).length > 0"
class="item-list-journals">
<span *ngFor="let value of allMetadataValues(['journal.identifier.issn']); let last=last;">
<span *ngFor="let value of allMetadataValues(['creativeworkseries.issn']); let last=last;">
<span [innerHTML]="value"><span [innerHTML]="value"></span></span>
</span>
</span>

View File

@@ -20,7 +20,7 @@ const mockItemWithMetadata: Item = Object.assign(new Item(), {
value: 'This is just another title'
}
],
'journal.identifier.issn': [
'creativeworkseries.issn': [
{
language: 'en_US',
value: '1234'

View File

@@ -7,11 +7,15 @@
<ds-thumbnail [thumbnail]="this.item.getThumbnail() | async"></ds-thumbnail>
</ds-metadata-field-wrapper>
<ds-generic-item-page-field [item]="item"
[fields]="['journalissue.identifier.number']"
[fields]="['publicationvolume.volumeNumber']"
[label]="'journalvolume.page.volume'">
</ds-generic-item-page-field>
<ds-generic-item-page-field [item]="item"
[fields]="['publicationissue.issueNumber']"
[label]="'journalissue.page.number'">
</ds-generic-item-page-field>
<ds-generic-item-page-field [item]="item"
[fields]="['journalissue.issuedate']"
[fields]="['creativework.datePublished']"
[label]="'journalissue.page.issuedate'">
</ds-generic-item-page-field>
<ds-generic-item-page-field [item]="item"
@@ -19,7 +23,7 @@
[label]="'journalissue.page.journal-title'">
</ds-generic-item-page-field>
<ds-generic-item-page-field [item]="item"
[fields]="['journal.identifier.issn']"
[fields]="['creativeworkseries.issn']"
[label]="'journalissue.page.journal-issn'">
</ds-generic-item-page-field>
</div>
@@ -34,11 +38,11 @@
[label]="'relationships.isPublicationOfJournalIssue' | translate">
</ds-related-items>
<ds-generic-item-page-field [item]="item"
[fields]="['journalissue.identifier.description']"
[fields]="['dc.description']"
[label]="'journalissue.page.description'">
</ds-generic-item-page-field>
<ds-generic-item-page-field [item]="item"
[fields]="['journalissue.identifier.keyword']"
[fields]="['creativework.keywords']"
[label]="'journalissue.page.keyword'">
</ds-generic-item-page-field>
<div>

View File

@@ -12,25 +12,25 @@ import {
const mockItem: Item = Object.assign(new Item(), {
bitstreams: observableOf(new RemoteData(false, false, true, null, new PaginatedList(new PageInfo(), []))),
metadata: {
'journalissue.identifier.number': [
'publicationissue.issueNumber': [
{
language: 'en_US',
value: '1234'
}
],
'journalissue.issuedate': [
'creativework.datePublished': [
{
language: 'en_US',
value: '2018'
}
],
'journalissue.identifier.description': [
'dc.description': [
{
language: 'en_US',
value: 'desc'
}
],
'journalissue.identifier.keyword': [
'creativework.keywords': [
{
language: 'en_US',
value: 'keyword'

View File

@@ -4,10 +4,7 @@ import { Item } from '../../../../core/shared/item.model';
import { ItemViewMode, rendersItemType } from '../../../../shared/items/item-type-decorator';
import { isNotEmpty } from '../../../../shared/empty.util';
import { ItemComponent } from '../../../../+item-page/simple/item-types/shared/item.component';
import {
filterRelationsByTypeLabel,
relationsToItems
} from '../../../../+item-page/simple/item-types/shared/item-relationships-utils';
import { getRelatedItemsByTypeLabel } from '../../../../+item-page/simple/item-types/shared/item-relationships-utils';
@rendersItemType('JournalIssue', ItemViewMode.Full)
@Component({
@@ -34,12 +31,10 @@ export class JournalIssueComponent extends ItemComponent {
if (isNotEmpty(this.resolvedRelsAndTypes$)) {
this.volumes$ = this.resolvedRelsAndTypes$.pipe(
filterRelationsByTypeLabel('isJournalVolumeOfIssue'),
relationsToItems(this.item.id)
getRelatedItemsByTypeLabel(this.item.id, 'isJournalVolumeOfIssue')
);
this.publications$ = this.resolvedRelsAndTypes$.pipe(
filterRelationsByTypeLabel('isPublicationOfJournalIssue'),
relationsToItems(this.item.id)
getRelatedItemsByTypeLabel(this.item.id, 'isPublicationOfJournalIssue')
);
}
}

View File

@@ -7,11 +7,11 @@
<ds-thumbnail [thumbnail]="this.item.getThumbnail() | async"></ds-thumbnail>
</ds-metadata-field-wrapper>
<ds-generic-item-page-field [item]="item"
[fields]="['journalvolume.identifier.volume']"
[fields]="['publicationvolume.volumeNumber']"
[label]="'journalvolume.page.volume'">
</ds-generic-item-page-field>
<ds-generic-item-page-field [item]="item"
[fields]="['journalvolume.issuedate']"
[fields]="['creativework.datePublished']"
[label]="'journalvolume.page.issuedate'">
</ds-generic-item-page-field>
</div>
@@ -25,7 +25,7 @@
[label]="'relationships.isIssueOf' | translate">
</ds-related-items>
<ds-generic-item-page-field [item]="item"
[fields]="['journalvolume.identifier.description']"
[fields]="['dc.description']"
[label]="'journalvolume.page.description'">
</ds-generic-item-page-field>
<div>

View File

@@ -12,19 +12,19 @@ import {
const mockItem: Item = Object.assign(new Item(), {
bitstreams: observableOf(new RemoteData(false, false, true, null, new PaginatedList(new PageInfo(), []))),
metadata: {
'journalvolume.identifier.volume': [
'publicationvolume.volumeNumber': [
{
language: 'en_US',
value: '1234'
}
],
'journalvolume.issuedate': [
'creativework.datePublished': [
{
language: 'en_US',
value: '2018'
}
],
'journalvolume.identifier.description': [
'dc.description': [
{
language: 'en_US',
value: 'desc'

View File

@@ -4,10 +4,7 @@ import { Item } from '../../../../core/shared/item.model';
import { ItemViewMode, rendersItemType } from '../../../../shared/items/item-type-decorator';
import { isNotEmpty } from '../../../../shared/empty.util';
import { ItemComponent } from '../../../../+item-page/simple/item-types/shared/item.component';
import {
filterRelationsByTypeLabel,
relationsToItems
} from '../../../../+item-page/simple/item-types/shared/item-relationships-utils';
import { getRelatedItemsByTypeLabel } from '../../../../+item-page/simple/item-types/shared/item-relationships-utils';
@rendersItemType('JournalVolume', ItemViewMode.Full)
@Component({
@@ -34,12 +31,10 @@ export class JournalVolumeComponent extends ItemComponent {
if (isNotEmpty(this.resolvedRelsAndTypes$)) {
this.journals$ = this.resolvedRelsAndTypes$.pipe(
filterRelationsByTypeLabel('isJournalOfVolume'),
relationsToItems(this.item.id)
getRelatedItemsByTypeLabel(this.item.id, 'isJournalOfVolume')
);
this.issues$ = this.resolvedRelsAndTypes$.pipe(
filterRelationsByTypeLabel('isIssueOfJournalVolume'),
relationsToItems(this.item.id)
getRelatedItemsByTypeLabel(this.item.id, 'isIssueOfJournalVolume')
);
}
}

View File

@@ -7,15 +7,15 @@
<ds-thumbnail [thumbnail]="this.item.getThumbnail() | async"></ds-thumbnail>
</ds-metadata-field-wrapper>
<ds-generic-item-page-field class="item-page-fields" [item]="item"
[fields]="['journal.identifier.issn']"
[fields]="['creativeworkseries.issn']"
[label]="'journal.page.issn'">
</ds-generic-item-page-field>
<ds-generic-item-page-field class="item-page-fields" [item]="item"
[fields]="['journal.publisher']"
[fields]="['creativework.publisher']"
[label]="'journal.page.publisher'">
</ds-generic-item-page-field>
<ds-generic-item-page-field [item]="item"
[fields]="['journal.contributor.editor']"
[fields]="['creativework.editor']"
[label]="'journal.page.editor'">
</ds-generic-item-page-field>
</div>
@@ -25,7 +25,7 @@
[label]="'relationships.isVolumeOf' | translate">
</ds-related-items>
<ds-generic-item-page-field class="item-page-fields" [item]="item"
[fields]="['journal.identifier.description']"
[fields]="['dc.description']"
[label]="'journal.page.description'">
</ds-generic-item-page-field>
<div>

View File

@@ -22,19 +22,19 @@ let fixture: ComponentFixture<JournalComponent>;
const mockItem: Item = Object.assign(new Item(), {
bitstreams: observableOf(new RemoteData(false, false, true, null, new PaginatedList(new PageInfo(), []))),
metadata: {
'journal.identifier.issn': [
'creativeworkseries.issn': [
{
language: 'en_US',
value: '1234'
}
],
'journal.publisher': [
'creativework.publisher': [
{
language: 'en_US',
value: 'a publisher'
}
],
'journal.identifier.description': [
'dc.description': [
{
language: 'en_US',
value: 'desc'

View File

@@ -4,10 +4,7 @@ import { Item } from '../../../../core/shared/item.model';
import { ItemViewMode, rendersItemType } from '../../../../shared/items/item-type-decorator';
import { isNotEmpty } from '../../../../shared/empty.util';
import { ItemComponent } from '../../../../+item-page/simple/item-types/shared/item.component';
import {
filterRelationsByTypeLabel,
relationsToItems
} from '../../../../+item-page/simple/item-types/shared/item-relationships-utils';
import { getRelatedItemsByTypeLabel } from '../../../../+item-page/simple/item-types/shared/item-relationships-utils';
@rendersItemType('Journal', ItemViewMode.Full)
@Component({
@@ -29,8 +26,7 @@ export class JournalComponent extends ItemComponent {
if (isNotEmpty(this.resolvedRelsAndTypes$)) {
this.volumes$ = this.resolvedRelsAndTypes$.pipe(
filterRelationsByTypeLabel('isVolumeOfJournal'),
relationsToItems(this.item.id)
getRelatedItemsByTypeLabel(this.item.id, 'isVolumeOfJournal')
);
}
}

View File

@@ -1,13 +1,13 @@
<ds-truncatable [id]="item.id">
<a
[routerLink]="['/items/' + item.id]" class="lead"
[innerHTML]="firstMetadataValue('orgunit.identifier.name')"></a>
[innerHTML]="firstMetadataValue('organization.legalName')"></a>
<span class="text-muted">
<ds-truncatable-part [id]="item.id" [minLines]="3">
<span *ngIf="item.allMetadata(['orgunit.identifier.description']).length > 0"
<span *ngIf="item.allMetadata(['dc.description']).length > 0"
class="item-list-orgunit-description">
<ds-truncatable-part [id]="item.id" [minLines]="3"><span
[innerHTML]="firstMetadataValue('orgunit.identifier.description')"></span>
[innerHTML]="firstMetadataValue('dc.description')"></span>
</ds-truncatable-part>
</span>
</ds-truncatable-part>

View File

@@ -20,7 +20,7 @@ const mockItemWithMetadata: Item = Object.assign(new Item(), {
value: 'This is just another title'
}
],
'orgunit.identifier.description': [
'dc.description': [
{
language: 'en_US',
value: 'A description about the OrgUnit'

View File

@@ -1,13 +1,13 @@
<ng-template #descTemplate>
<span class="text-muted">
<span *ngIf="item.allMetadata(['orgunit.identifier.description']).length > 0"
<span *ngIf="item.allMetadata(['dc.description']).length > 0"
class="item-list-job-title">
<span [innerHTML]="firstMetadataValue(['orgunit.identifier.description'])"></span>
<span [innerHTML]="firstMetadataValue(['dc.description'])"></span>
</span>
</span>
</ng-template>
<ds-truncatable [id]="item.id">
<a [routerLink]="['/items/' + item.id]"
[innerHTML]="firstMetadataValue('orgunit.identifier.name')"
[innerHTML]="firstMetadataValue('organization.legalName')"
[tooltip]="descTemplate"></a>
</ds-truncatable>

View File

@@ -1,12 +1,12 @@
<ds-truncatable [id]="item.id">
<a
[routerLink]="['/items/' + item.id]" class="lead"
[innerHTML]="firstMetadataValue('dc.contributor.author')"></a>
[innerHTML]="firstMetadataValue('person.familyName') + ', ' + firstMetadataValue('person.givenName')"></a>
<span class="text-muted">
<ds-truncatable-part [id]="item.id" [minLines]="1">
<span *ngIf="item.allMetadata(['person.identifier.jobtitle']).length > 0"
<span *ngIf="item.allMetadata(['person.jobTitle']).length > 0"
class="item-list-job-title">
<span *ngFor="let value of allMetadataValues(['person.identifier.jobtitle']); let last=last;">
<span *ngFor="let value of allMetadataValues(['person.jobTitle']); let last=last;">
<span [innerHTML]="value"><span [innerHTML]="value"></span></span>
</span>
</span>

View File

@@ -20,7 +20,7 @@ const mockItemWithMetadata: Item = Object.assign(new Item(), {
value: 'This is just another title'
}
],
'person.identifier.jobtitle': [
'person.jobTitle': [
{
language: 'en_US',
value: 'Developer'

View File

@@ -1,8 +1,8 @@
<ng-template #descTemplate>
<span class="text-muted">
<span *ngIf="item.allMetadata(['person.identifier.jobtitle']).length > 0"
<span *ngIf="item.allMetadata(['person.jobTitle']).length > 0"
class="item-list-job-title">
<span *ngFor="let value of allMetadataValues(['person.identifier.jobtitle']); let last=last;">
<span *ngFor="let value of allMetadataValues(['person.jobTitle']); let last=last;">
<span [innerHTML]="value"><span [innerHTML]="value"></span></span>
</span>
</span>
@@ -10,6 +10,6 @@
</ng-template>
<ds-truncatable [id]="item.id">
<a [routerLink]="['/items/' + item.id]"
[innerHTML]="firstMetadataValue('dc.contributor.author')"
[innerHTML]="firstMetadataValue('person.familyName') + ', ' + firstMetadataValue('person.givenName')"
[tooltip]="descTemplate"></a>
</ds-truncatable>

View File

@@ -1,16 +1,16 @@
<ds-truncatable [id]="item.id">
<a
[routerLink]="['/items/' + item.id]" class="lead"
[innerHTML]="firstMetadataValue('project.identifier.name')"></a>
<span class="text-muted">
<ds-truncatable-part [id]="item.id" [minLines]="1">
<span *ngIf="item.allMetadata(['project.identifier.status']).length > 0"
class="item-list-status">
<span *ngFor="let value of allMetadataValues(['project.identifier.status']); let last=last;">
<span [innerHTML]="value"><span [innerHTML]="value"></span></span>
</span>
</span>
</ds-truncatable-part>
</span>
[innerHTML]="firstMetadataValue('dc.title')"></a>
<!--<span class="text-muted">-->
<!--<ds-truncatable-part [id]="item.id" [minLines]="1">-->
<!--<span *ngIf="item.allMetadata(['project.identifier.status']).length > 0"-->
<!--class="item-list-status">-->
<!--<span *ngFor="let value of allMetadataValues(['project.identifier.status']); let last=last;">-->
<!--<span [innerHTML]="value"><span [innerHTML]="value"></span></span>-->
<!--</span>-->
<!--</span>-->
<!--</ds-truncatable-part>-->
<!--</span>-->
</ds-truncatable>

View File

@@ -20,12 +20,12 @@ const mockItemWithMetadata: Item = Object.assign(new Item(), {
value: 'This is just another title'
}
],
'project.identifier.status': [
{
language: 'en_US',
value: 'A status about the project'
}
]
// 'project.identifier.status': [
// {
// language: 'en_US',
// value: 'A status about the project'
// }
// ]
}
});
const mockItemWithoutMetadata: Item = Object.assign(new Item(), {
@@ -61,27 +61,27 @@ describe('ProjectListElementComponent', () => {
}));
describe('When the item has a status', () => {
beforeEach(() => {
projectListElementComponent.item = mockItemWithMetadata;
fixture.detectChanges();
});
it('should show the status span', () => {
const statusField = fixture.debugElement.query(By.css('span.item-list-status'));
expect(statusField).not.toBeNull();
});
});
describe('When the item has no status', () => {
beforeEach(() => {
projectListElementComponent.item = mockItemWithoutMetadata;
fixture.detectChanges();
});
it('should not show the status span', () => {
const statusField = fixture.debugElement.query(By.css('span.item-list-status'));
expect(statusField).toBeNull();
});
});
// describe('When the item has a status', () => {
// beforeEach(() => {
// projectListElementComponent.item = mockItemWithMetadata;
// fixture.detectChanges();
// });
//
// it('should show the status span', () => {
// const statusField = fixture.debugElement.query(By.css('span.item-list-status'));
// expect(statusField).not.toBeNull();
// });
// });
//
// describe('When the item has no status', () => {
// beforeEach(() => {
// projectListElementComponent.item = mockItemWithoutMetadata;
// fixture.detectChanges();
// });
//
// it('should not show the status span', () => {
// const statusField = fixture.debugElement.query(By.css('span.item-list-status'));
// expect(statusField).toBeNull();
// });
// });
});

View File

@@ -1,5 +1,5 @@
<h2 class="item-page-title-field">
{{'orgunit.page.titleprefix' | translate}}<ds-metadata-values [mdValues]="item?.allMetadata(['orgunit.identifier.name'])"></ds-metadata-values>
{{'orgunit.page.titleprefix' | translate}}<ds-metadata-values [mdValues]="item?.allMetadata(['organization.legalName'])"></ds-metadata-values>
</h2>
<div class="row">
<div class="col-xs-12 col-md-4">
@@ -7,19 +7,19 @@
<ds-thumbnail [thumbnail]="this.item.getThumbnail() | async" [defaultImage]="'assets/images/orgunit-placeholder.svg'"></ds-thumbnail>
</ds-metadata-field-wrapper>
<ds-generic-item-page-field [item]="item"
[fields]="['orgunit.identifier.dateestablished']"
[fields]="['organization.foundingDate']"
[label]="'orgunit.page.dateestablished'">
</ds-generic-item-page-field>
<ds-generic-item-page-field [item]="item"
[fields]="['orgunit.identifier.city']"
[fields]="['organization.address.addressLocality']"
[label]="'orgunit.page.city'">
</ds-generic-item-page-field>
<ds-generic-item-page-field [item]="item"
[fields]="['orgunit.identifier.country']"
[fields]="['organization.adress.addressCountry']"
[label]="'orgunit.page.country'">
</ds-generic-item-page-field>
<ds-generic-item-page-field [item]="item"
[fields]="['orgunit.identifier.id']"
[fields]="['dc.identifier']"
[label]="'orgunit.page.id'">
</ds-generic-item-page-field>
</div>
@@ -37,7 +37,7 @@
[label]="'relationships.isPublicationOf' | translate">
</ds-related-items>
<ds-generic-item-page-field [item]="item"
[fields]="['orgunit.identifier.description']"
[fields]="['dc.description']"
[label]="'orgunit.page.description'">
</ds-generic-item-page-field>
<div>

View File

@@ -12,31 +12,31 @@ import {
const mockItem: Item = Object.assign(new Item(), {
bitstreams: observableOf(new RemoteData(false, false, true, null, new PaginatedList(new PageInfo(), []))),
metadata: {
'orgunit.identifier.dateestablished': [
'organization.foundingDate': [
{
language: 'en_US',
value: '2018'
}
],
'orgunit.identifier.city': [
'organization.address.addressLocality': [
{
language: 'en_US',
value: 'New York'
}
],
'orgunit.identifier.country': [
'organization.adress.addressCountry': [
{
language: 'en_US',
value: 'USA'
}
],
'orgunit.identifier.id': [
'dc.identifier': [
{
language: 'en_US',
value: '1'
}
],
'orgunit.identifier.description': [
'dc.description': [
{
language: 'en_US',
value: 'desc'

View File

@@ -4,10 +4,7 @@ import { Item } from '../../../../core/shared/item.model';
import { ItemViewMode, rendersItemType } from '../../../../shared/items/item-type-decorator';
import { isNotEmpty } from '../../../../shared/empty.util';
import { ItemComponent } from '../../../../+item-page/simple/item-types/shared/item.component';
import {
filterRelationsByTypeLabel,
relationsToItems
} from '../../../../+item-page/simple/item-types/shared/item-relationships-utils';
import { getRelatedItemsByTypeLabel } from '../../../../+item-page/simple/item-types/shared/item-relationships-utils';
@rendersItemType('OrgUnit', ItemViewMode.Full)
@Component({
@@ -39,18 +36,15 @@ export class OrgunitComponent extends ItemComponent implements OnInit {
if (isNotEmpty(this.resolvedRelsAndTypes$)) {
this.people$ = this.resolvedRelsAndTypes$.pipe(
filterRelationsByTypeLabel('isPersonOfOrgUnit'),
relationsToItems(this.item.id)
getRelatedItemsByTypeLabel(this.item.id, 'isPersonOfOrgUnit')
);
this.projects$ = this.resolvedRelsAndTypes$.pipe(
filterRelationsByTypeLabel('isProjectOfOrgUnit'),
relationsToItems(this.item.id)
getRelatedItemsByTypeLabel(this.item.id, 'isProjectOfOrgUnit')
);
this.publications$ = this.resolvedRelsAndTypes$.pipe(
filterRelationsByTypeLabel('isPublicationOfOrgUnit'),
relationsToItems(this.item.id)
getRelatedItemsByTypeLabel(this.item.id, 'isPublicationOfOrgUnit')
);
}
}}

View File

@@ -1,5 +1,5 @@
<h2 class="item-page-title-field">
{{'person.page.titleprefix' | translate}}<ds-metadata-values [mdValues]="item?.allMetadata(['dc.contributor.author'])"></ds-metadata-values>
{{'person.page.titleprefix' | translate}}<ds-metadata-values [mdValues]="[item?.firstMetadata('person.familyName'), item?.firstMetadata('person.givenName')]" [separator]="', '"></ds-metadata-values>
</h2>
<div class="row">
<div class="col-xs-12 col-md-4">
@@ -7,21 +7,21 @@
<ds-thumbnail [thumbnail]="this.item.getThumbnail() | async" [defaultImage]="'assets/images/person-placeholder.svg'"></ds-thumbnail>
</ds-metadata-field-wrapper>
<ds-generic-item-page-field [item]="item"
[fields]="['person.identifier.email']"
[fields]="['person.email']"
[label]="'person.page.email'">
</ds-generic-item-page-field>
<!--<ds-generic-item-page-field [item]="item"-->
<!--[fields]="['person.identifier.orcid']"-->
<!--[label]="'person.page.orcid'">-->
<!--</ds-generic-item-page-field>-->
<ds-generic-item-page-field [item]="item"
[fields]="['person.identifier.orcid']"
[label]="'person.page.orcid'">
</ds-generic-item-page-field>
<ds-generic-item-page-field [item]="item"
[fields]="['person.identifier.birthdate']"
[fields]="['person.birthDate']"
[label]="'person.page.birthdate'">
</ds-generic-item-page-field>
<ds-generic-item-page-field [item]="item"
[fields]="['person.identifier.staffid']"
[label]="'person.page.staffid'">
</ds-generic-item-page-field>
<!--<ds-generic-item-page-field [item]="item"-->
<!--[fields]="['person.identifier.staffid']"-->
<!--[label]="'person.page.staffid'">-->
<!--</ds-generic-item-page-field>-->
</div>
<div class="col-xs-12 col-md-6">
<ds-related-items
@@ -33,15 +33,15 @@
[label]="'relationships.isOrgUnitOf' | translate">
</ds-related-items>
<ds-generic-item-page-field [item]="item"
[fields]="['person.identifier.jobtitle']"
[fields]="['person.jobTitle']"
[label]="'person.page.jobtitle'">
</ds-generic-item-page-field>
<ds-generic-item-page-field [item]="item"
[fields]="['person.identifier.lastname']"
[fields]="['person.familyName']"
[label]="'person.page.lastname'">
</ds-generic-item-page-field>
<ds-generic-item-page-field [item]="item"
[fields]="['person.identifier.firstname']"
[fields]="['person.givenName']"
[label]="'person.page.firstname'">
</ds-generic-item-page-field>
<div>

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