diff --git a/cypress/integration/submission-ui.spec.ts b/cypress/integration/submission-ui.spec.ts index cc8e28dc019..5b819980388 100644 --- a/cypress/integration/submission-ui.spec.ts +++ b/cypress/integration/submission-ui.spec.ts @@ -416,52 +416,6 @@ describe('Create a new submission', () => { createItemProcess.checkAuthorLastnameField(); }); - it('should show warning messages if was selected non-supported license', { - retries: { - runMode: 6, - openMode: 6, - }, - defaultCommandTimeout: 10000 - },() => { - createItemProcess.checkLicenseResourceStep(); - // check default value in the license dropdown selection - createItemProcess.checkLicenseSelectionValue('Select a License ...'); - // check step status - it should be as warning - createItemProcess.checkResourceLicenseStatus('Warnings'); - // select `Select a License ...` from the selection - this license is not supported - createItemProcess.selectValueFromLicenseSelection('Select a License ...'); - // selected value should be seen as selected value in the selection - createItemProcess.checkLicenseSelectionValue('Select a License ...'); - // check step status - it should an error - createItemProcess.checkResourceLicenseStatus('Errors'); - // error messages should be popped up - createItemProcess.showErrorMustChooseLicense(); - createItemProcess.showErrorNotSupportedLicense(); - }); - - it('the submission should have the Notice Step', { - retries: { - runMode: 6, - openMode: 6, - }, - defaultCommandTimeout: 10000 - },() => { - createItemProcess.checkLicenseResourceStep(); - // check default value in the license dropdown selection - createItemProcess.checkLicenseSelectionValue('Select a License ...'); - // check step status - it should be as warning - createItemProcess.checkResourceLicenseStatus('Warnings'); - // select `Select a License ...` from the selection - this license is not supported - createItemProcess.selectValueFromLicenseSelection('Select a License ...'); - // selected value should be seen as selected value in the selection - createItemProcess.checkLicenseSelectionValue('Select a License ...'); - // check step status - it should an error - createItemProcess.checkResourceLicenseStatus('Errors'); - // error messages should be popped up - createItemProcess.showErrorMustChooseLicense(); - createItemProcess.showErrorNotSupportedLicense(); - }); - it('The submission should not have the Notice Step', { retries: { runMode: 6, diff --git a/src/app/item-page/simple/field-components/clarin-collections-item-field/clarin-collections-item-field.component.html b/src/app/item-page/simple/field-components/clarin-collections-item-field/clarin-collections-item-field.component.html index fdb7e3df0b6..c2d26d3506f 100644 --- a/src/app/item-page/simple/field-components/clarin-collections-item-field/clarin-collections-item-field.component.html +++ b/src/app/item-page/simple/field-components/clarin-collections-item-field/clarin-collections-item-field.component.html @@ -1,9 +1,9 @@
-
+
{{'item.page.collections' | translate}}
-
+
{{collection?.name}} diff --git a/src/app/item-page/simple/field-components/clarin-generic-item-field/clarin-generic-item-field.component.html b/src/app/item-page/simple/field-components/clarin-generic-item-field/clarin-generic-item-field.component.html index d993db6ae2c..aa8f11074c9 100644 --- a/src/app/item-page/simple/field-components/clarin-generic-item-field/clarin-generic-item-field.component.html +++ b/src/app/item-page/simple/field-components/clarin-generic-item-field/clarin-generic-item-field.component.html @@ -1,16 +1,27 @@
-
+
{{label | translate}}
-
diff --git a/src/app/item-page/simple/field-components/clarin-generic-item-field/clarin-generic-item-field.component.ts b/src/app/item-page/simple/field-components/clarin-generic-item-field/clarin-generic-item-field.component.ts index ae4120b1b96..089119cf2bb 100644 --- a/src/app/item-page/simple/field-components/clarin-generic-item-field/clarin-generic-item-field.component.ts +++ b/src/app/item-page/simple/field-components/clarin-generic-item-field/clarin-generic-item-field.component.ts @@ -1,6 +1,10 @@ import { Component, Input, OnInit } from '@angular/core'; import { Item } from '../../../../core/shared/item.model'; -import { isNotUndefined } from '../../../../shared/empty.util'; +import { isEmpty, isNotUndefined } from '../../../../shared/empty.util'; +import { ConfigurationProperty } from '../../../../core/shared/configuration-property.model'; +import { DSONameService } from '../../../../core/breadcrumbs/dso-name.service'; +import { convertMetadataFieldIntoSearchType, getBaseUrl } from '../../../../shared/clarin-shared-util'; +import { ConfigurationDataService } from '../../../../core/data/configuration-data.service'; @Component({ selector: 'ds-clarin-generic-item-field', @@ -40,15 +44,73 @@ export class ClarinGenericItemFieldComponent implements OnInit { */ @Input() label: string; + /** + * UI URL loaded from the server. + */ + baseUrl = ''; + // tslint:disable-next-line:no-empty - constructor() { } + constructor(protected dsoNameService: DSONameService, + protected configurationService: ConfigurationDataService) { } // tslint:disable-next-line:no-empty - ngOnInit(): void { + async ngOnInit(): Promise { + await this.assignBaseUrl(); } + /** + * If the metadata fields has some metadata value - show nothing if the field do not have any value. + */ public hasMetadataValue() { return isNotUndefined(this.item.firstMetadataValue(this.fields)); } + /** + * Return current metadata value. The metadata field could have more metadata values, often the metadata + * field has only one metadata value - index is 0, but sometimes it has more values e.g. `Author`. + * @param index + */ + public getLinkToSearch(index, value = '') { + let metadataValue = 'Error: value is empty'; + if (isEmpty(value)) { + // Get metadata value from the Item's metadata field + metadataValue = this.getMetadataValue(index); + } else { + // The metadata value is passed from the parameter. + metadataValue = value; + } + + const searchType = convertMetadataFieldIntoSearchType(this.fields); + return this.baseUrl + '/search/objects?f.' + searchType + '=' + metadataValue + ',equals'; + } + + /** + * If the metadata field has more than 1 value return the value based on the index. + * @param index of the metadata value + */ + public getMetadataValue(index) { + let metadataValue = ''; + if (index === 0) { + // Return first metadata value. + return this.item.firstMetadataValue(this.fields); + } + // The metadata field has more metadata values - get the actual one + this.item.allMetadataValues(this.fields)?.forEach((metadataValueArray, arrayIndex) => { + if (index !== arrayIndex) { + return metadataValue; + } + metadataValue = metadataValueArray; + }); + return metadataValue; + } + + /** + * Load base url from the configuration from the BE. + */ + async assignBaseUrl() { + this.baseUrl = await getBaseUrl(this.configurationService) + .then((baseUrlResponse: ConfigurationProperty) => { + return baseUrlResponse?.values?.[0]; + }); + } } diff --git a/src/app/item-page/simple/item-types/untyped-item/untyped-item.component.html b/src/app/item-page/simple/item-types/untyped-item/untyped-item.component.html index 1524db659fe..3fb68ff17d8 100644 --- a/src/app/item-page/simple/item-types/untyped-item/untyped-item.component.html +++ b/src/app/item-page/simple/item-types/untyped-item/untyped-item.component.html @@ -29,7 +29,8 @@

[fields]="['dc.contributor.author', 'dc.creator']" [label]="'relationships.isAuthorOf'" [iconName]="'fa-pen'" - [separator]="','"> + [separator]="','" + [type]="'author'"> + [iconName]="'fa-link'" + [type]="'hyperlink'"> + [iconName]="'fa-tag'" + [type]="'search'"> [fields]="['dc.language.iso']" [label]="'item.page.language'" [iconName]="'fa-flag'" - [separator]="','"> + [separator]="','" + [type]="'search'"> [iconName]="'fa-file-alt'"> + [iconName]="'fa-copy'" + [type]="'search'"> [fields]="['dc.subject']" [label]="'item.page.subject'" [iconName]="'fa-tags'" - [separator]="','"> + [separator]="','" + [type]="'subject'"> +
+ + ; + and + {{ author.name }} + +
+
+
+ {{ author.name }} ; et al. +
+
+ + + + Show everyone + + +
+ + ; + and + {{author.name}} + +
+
+
+

diff --git a/src/app/shared/clarin-item-author-preview/clarin-item-author-preview.component.scss b/src/app/shared/clarin-item-author-preview/clarin-item-author-preview.component.scss new file mode 100644 index 00000000000..6f28ee1ce79 --- /dev/null +++ b/src/app/shared/clarin-item-author-preview/clarin-item-author-preview.component.scss @@ -0,0 +1,3 @@ +/** +This is a styling file for the clarin-item-author-preview component. + */ diff --git a/src/app/shared/clarin-item-author-preview/clarin-item-author-preview.component.spec.ts b/src/app/shared/clarin-item-author-preview/clarin-item-author-preview.component.spec.ts new file mode 100644 index 00000000000..f75f22ba072 --- /dev/null +++ b/src/app/shared/clarin-item-author-preview/clarin-item-author-preview.component.spec.ts @@ -0,0 +1,34 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { ClarinItemAuthorPreviewComponent } from './clarin-item-author-preview.component'; +import {of} from 'rxjs'; +import {ConfigurationDataService} from '../../core/data/configuration-data.service'; + +describe('ClarinItemAuthorPreviewComponent', () => { + let component: ClarinItemAuthorPreviewComponent; + let fixture: ComponentFixture; + + const configurationServiceSpy = jasmine.createSpyObj('configurationService', { + findByPropertyName: of(true), + }); + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ ClarinItemAuthorPreviewComponent ], + providers: [ + { provide: ConfigurationDataService, useValue: configurationServiceSpy } + ] + }) + .compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(ClarinItemAuthorPreviewComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/shared/clarin-item-author-preview/clarin-item-author-preview.component.ts b/src/app/shared/clarin-item-author-preview/clarin-item-author-preview.component.ts new file mode 100644 index 00000000000..c7a417e9419 --- /dev/null +++ b/src/app/shared/clarin-item-author-preview/clarin-item-author-preview.component.ts @@ -0,0 +1,52 @@ +import { Component, Input, OnInit } from '@angular/core'; +import { BehaviorSubject } from 'rxjs'; +import { AuthorNameLink } from '../clarin-item-box-view/clarin-item-box-view.component'; +import { getBaseUrl, loadItemAuthors } from '../clarin-shared-util'; +import { Item } from '../../core/shared/item.model'; +import { ConfigurationProperty } from '../../core/shared/configuration-property.model'; +import { ConfigurationDataService } from '../../core/data/configuration-data.service'; + +@Component({ + selector: 'ds-clarin-item-author-preview', + templateUrl: './clarin-item-author-preview.component.html', + styleUrls: ['./clarin-item-author-preview.component.scss'] +}) +export class ClarinItemAuthorPreviewComponent implements OnInit { + + @Input() item: Item; + + /** + * Authors of the Item. + */ + itemAuthors: BehaviorSubject = new BehaviorSubject([]); + + /** + * If the Item have a lot of authors do not show them all. + */ + showEveryAuthor: BehaviorSubject = new BehaviorSubject(false); + + /** + * UI URL loaded from the server. + */ + baseUrl = ''; + + constructor(protected configurationService: ConfigurationDataService) { } + + async ngOnInit(): Promise { + await this.assignBaseUrl(); + loadItemAuthors(this.item, this.itemAuthors, this.baseUrl); + } + toggleShowEveryAuthor() { + this.showEveryAuthor.next(!this.showEveryAuthor.value); + } + + /** + * Load base url from the configuration from the BE. + */ + async assignBaseUrl() { + this.baseUrl = await getBaseUrl(this.configurationService) + .then((baseUrlResponse: ConfigurationProperty) => { + return baseUrlResponse?.values?.[0]; + }); + } +} diff --git a/src/app/shared/clarin-item-box-view/clarin-item-box-view.component.html b/src/app/shared/clarin-item-box-view/clarin-item-box-view.component.html index 162406cce0b..b38709fbfbd 100644 --- a/src/app/shared/clarin-item-box-view/clarin-item-box-view.component.html +++ b/src/app/shared/clarin-item-box-view/clarin-item-box-view.component.html @@ -16,33 +16,7 @@
-
- - ; - and - {{ author.name }} - -
-
-
- {{ author.name }} ; et al. -
-
- - - - Show everyone - - -
- - ; - and - {{author.name}} - -
-
-
+
Description:
diff --git a/src/app/shared/clarin-item-box-view/clarin-item-box-view.component.ts b/src/app/shared/clarin-item-box-view/clarin-item-box-view.component.ts index f8670de6711..a7befd62964 100644 --- a/src/app/shared/clarin-item-box-view/clarin-item-box-view.component.ts +++ b/src/app/shared/clarin-item-box-view/clarin-item-box-view.component.ts @@ -19,7 +19,7 @@ import { RemoteData } from '../../core/data/remote-data'; import { PaginatedList } from '../../core/data/paginated-list.model'; import { ClarinLicense } from '../../core/shared/clarin/clarin-license.model'; import { ClarinLicenseDataService } from '../../core/data/clarin/clarin-license-data.service'; -import { secureImageData } from '../clarin-shared-util'; +import { getBaseUrl, secureImageData } from '../clarin-shared-util'; import { DomSanitizer } from '@angular/platform-browser'; import { BundleDataService } from '../../core/data/bundle-data.service'; import { Bundle } from '../../core/shared/bundle.model'; @@ -81,14 +81,7 @@ export class ClarinItemBoxViewComponent implements OnInit { * How many files the Item has. */ itemCountOfFiles: BehaviorSubject = new BehaviorSubject(-1); - /** - * Authors of the Item. - */ - itemAuthors: BehaviorSubject = new BehaviorSubject([]); - /** - * If the Item have a lot of authors do not show them all. - */ - showEveryAuthor: BehaviorSubject = new BehaviorSubject(false); + /** * Current License Label e.g. `PUB` */ @@ -132,33 +125,6 @@ export class ClarinItemBoxViewComponent implements OnInit { this.getItemCommunity(); this.loadItemLicense(); this.getItemFilesSize(); - this.loadItemAuthors(); - } - - private loadItemAuthors() { - if (isNull(this.item)) { - return; - } - - let authorsMV: MetadataValue[] = this.item?.metadata?.['dc.contributor.author']; - // Harvested Items has authors in the metadata field `dc.creator`. - if (isUndefined(authorsMV)) { - authorsMV = this.item?.metadata?.['dc.creator']; - } - - if (isUndefined(authorsMV)) { - return null; - } - const itemAuthorsLocal = []; - authorsMV.forEach((authorMV: MetadataValue) => { - const authorSearchLink = this.baseUrl + '/search/objects?f.author=' + authorMV.value + ',equals'; - const authorNameLink = Object.assign(new AuthorNameLink(), { - name: authorMV.value, - url: authorSearchLink - }); - itemAuthorsLocal.push(authorNameLink); - }); - this.itemAuthors.next(itemAuthorsLocal); } private getItemFilesSize() { @@ -197,15 +163,8 @@ export class ClarinItemBoxViewComponent implements OnInit { }); }); } - - async getBaseUrl(): Promise { - return this.configurationService.findByPropertyName('dspace.ui.url') - .pipe(getFirstSucceededRemoteDataPayload()) - .toPromise(); - } - async assignBaseUrl() { - this.baseUrl = await this.getBaseUrl() + this.baseUrl = await getBaseUrl(this.configurationService) .then((baseUrlResponse: ConfigurationProperty) => { return baseUrlResponse?.values?.[0]; }); @@ -251,17 +210,13 @@ export class ClarinItemBoxViewComponent implements OnInit { secureImageData(imageByteArray) { return secureImageData(this.sanitizer, imageByteArray); } - - toggleShowEveryAuthor() { - this.showEveryAuthor.next(!this.showEveryAuthor.value); - } } /** * Redirect the user after clicking on the `Author`. */ // tslint:disable-next-line:max-classes-per-file -class AuthorNameLink { +export class AuthorNameLink { name: string; url: string; } diff --git a/src/app/shared/clarin-shared-util.ts b/src/app/shared/clarin-shared-util.ts index 673f9afc831..28b6076e22d 100644 --- a/src/app/shared/clarin-shared-util.ts +++ b/src/app/shared/clarin-shared-util.ts @@ -1,4 +1,9 @@ import { DomSanitizer } from '@angular/platform-browser'; +import { getFirstSucceededRemoteDataPayload } from '../core/shared/operators'; +import { ConfigurationDataService } from '../core/data/configuration-data.service'; +import { isNull, isUndefined } from './empty.util'; +import { MetadataValue } from '../core/shared/metadata.models'; +import { AuthorNameLink } from './clarin-item-box-view/clarin-item-box-view.component'; /** * Convert raw byte array to the image is not secure - this function make it secure @@ -8,3 +13,66 @@ export function secureImageData(sanitizer: DomSanitizer,imageByteArray) { const objectURL = 'data:image/png;base64,' + imageByteArray; return sanitizer.bypassSecurityTrustUrl(objectURL); } + +export function getBaseUrl(configurationService: ConfigurationDataService): Promise { + return configurationService.findByPropertyName('dspace.ui.url') + .pipe(getFirstSucceededRemoteDataPayload()) + .toPromise(); +} + +/** + * Some metadata values in the Item View has links to redirect for search, this method decides what is the search field + * based on the metadata field. + * + * @param field metadata field + */ +export function convertMetadataFieldIntoSearchType(field: string[]) { + switch (true) { + case field.includes('dc.contributor.author') || field.includes('dc.creator'): + return 'author'; + case field.includes('dc.type'): + return 'itemtype'; + case field.includes('dc.publisher') || field.includes('creativework.publisher'): + return 'publisher'; + case field.includes('dc.language.iso'): + return 'language'; + case field.includes('dc.subject'): + return 'subject'; + default: + return ''; + } +} + +/** + * Load Authors of the current item into BehaviourSubject - ItemAuthors. This method also compose + * search link for every Author. + * + * @param item current Item + * @param itemAuthors BehaviourSubject (async) of Authors with search links + * @param baseUrl e.g. localhost:8080 + */ +export function loadItemAuthors(item, itemAuthors, baseUrl) { + if (isNull(item) || isNull(itemAuthors) || isNull(baseUrl)) { + return; + } + + let authorsMV: MetadataValue[] = item?.metadata?.['dc.contributor.author']; + // Harvested Items has authors in the metadata field `dc.creator`. + if (isUndefined(authorsMV)) { + authorsMV = item?.metadata?.['dc.creator']; + } + + if (isUndefined(authorsMV)) { + return null; + } + const itemAuthorsLocal = []; + authorsMV.forEach((authorMV: MetadataValue) => { + const authorSearchLink = baseUrl + '/search/objects?f.author=' + authorMV.value + ',equals'; + const authorNameLink = Object.assign(new AuthorNameLink(), { + name: authorMV.value, + url: authorSearchLink + }); + itemAuthorsLocal.push(authorNameLink); + }); + itemAuthors.next(itemAuthorsLocal); +} diff --git a/src/app/shared/shared.module.ts b/src/app/shared/shared.module.ts index 339332bd2f5..97ea23d1ad1 100644 --- a/src/app/shared/shared.module.ts +++ b/src/app/shared/shared.module.ts @@ -183,6 +183,7 @@ import { ClarinLicenseLabelRadioValuePipe } from './utils/clarin-license-label-r import { CharToEndPipe } from './utils/char-to-end.pipe'; import { ClarinLicenseRequiredInfoPipe } from './utils/clarin-license-required-info.pipe'; import { ClarinItemBoxViewComponent } from './clarin-item-box-view/clarin-item-box-view.component'; +import { ClarinItemAuthorPreviewComponent } from './clarin-item-author-preview/clarin-item-author-preview.component'; const MODULES = [ // Do NOT include UniversalModule, HttpModule, or JsonpModule here @@ -357,7 +358,8 @@ const COMPONENTS = [ CommunitySidebarSearchListElementComponent, SearchNavbarComponent, ScopeSelectorModalComponent, - ClarinItemBoxViewComponent + ClarinItemBoxViewComponent, + ClarinItemAuthorPreviewComponent ]; const ENTRY_COMPONENTS = [ diff --git a/src/assets/i18n/en.json5 b/src/assets/i18n/en.json5 index a391f629751..3388f61d113 100644 --- a/src/assets/i18n/en.json5 +++ b/src/assets/i18n/en.json5 @@ -2354,6 +2354,10 @@ "item.preview.oaire.fundingStream": "Funding Stream:", + "item.preview.authors.show.everyone": "Show everyone", + + "item.preview.authors.et.al": " ; et al.", + "item.refbox.modal.copy.instruction": ["Press", "ctrl + c", "to copy"], @@ -3612,6 +3616,8 @@ "search.filters.applied.f.items_owning_community": "Community", + "search.filters.applied.f.publisher": "Publisher", + "search.filters.filter.author.head": "Author", diff --git a/src/styles/_clarin-styles.scss b/src/styles/_clarin-styles.scss index 68f3511cb25..a8dbcb1e5b7 100644 --- a/src/styles/_clarin-styles.scss +++ b/src/styles/_clarin-styles.scss @@ -9,3 +9,7 @@ .label-RES { background-color: #c62d1f; } + +.cursor-pointer { + cursor: pointer; +}