diff --git a/cypress/e2e/search-navbar.cy.ts b/cypress/e2e/search-navbar.cy.ts index 5000dc206c5..648db17fe65 100644 --- a/cypress/e2e/search-navbar.cy.ts +++ b/cypress/e2e/search-navbar.cy.ts @@ -15,7 +15,7 @@ const page = { } }; -xdescribe('Search from Navigation Bar', () => { +describe('Search from Navigation Bar', () => { // NOTE: these tests currently assume this query will return results! const query = TEST_SEARCH_TERM; diff --git a/package.json b/package.json index df15df83b57..8bfdfabec7a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "dspace-angular", - "version": "2024.01.00-SNAPSHOT", + "version": "2023.02.04-SNAPSHOT", "scripts": { "ng": "ng", "config:watch": "nodemon", diff --git a/src/app/admin/admin-edit-user-agreement/admin-edit-user-agreement.component.ts b/src/app/admin/admin-edit-user-agreement/admin-edit-user-agreement.component.ts index d4821d76347..ef46b73a4c6 100644 --- a/src/app/admin/admin-edit-user-agreement/admin-edit-user-agreement.component.ts +++ b/src/app/admin/admin-edit-user-agreement/admin-edit-user-agreement.component.ts @@ -72,7 +72,7 @@ export class AdminEditUserAgreementComponent implements OnInit, OnDestroy { this.subs.push(this.siteService.patch(this.site, operations).pipe( getFirstCompletedRemoteData(), ).subscribe((restResponse) => { - if (restResponse.isSuccess) { + if (restResponse.hasSucceeded) { this.notificationsService.success(this.translateService.get('admin.edit-user-agreement.success')); if ( result === 'edit-with-reset' ) { this.deleteAllUserAgreementMetadataValues(); diff --git a/src/app/core/browse/search-manager.ts b/src/app/core/browse/search-manager.ts index 723fbd0436b..6c3ef6e9962 100644 --- a/src/app/core/browse/search-manager.ts +++ b/src/app/core/browse/search-manager.ts @@ -4,7 +4,7 @@ import { map, switchMap } from 'rxjs/operators'; import { PaginatedList } from '../data/paginated-list.model'; import { RemoteData } from '../data/remote-data'; import { Item } from '../shared/item.model'; -import { getFirstSucceededRemoteData } from '../shared/operators'; +import { getFirstCompletedRemoteData } from '../shared/operators'; import { BrowseEntrySearchOptions } from './browse-entry-search-options.model'; import { FollowLinkConfig } from '../../shared/utils/follow-link-config.model'; import { ItemDataService } from '../data/item-data.service'; @@ -107,8 +107,16 @@ export class SearchManager { .filter((item) => hasValue(item)); const uuidList = this.extractUUID(items, environment.followAuthorityMetadata); - - return uuidList.length > 0 ? this.itemService.findAllById(uuidList).pipe(getFirstSucceededRemoteData()) : of(null); + return uuidList.length > 0 ? this.itemService.findAllById(uuidList).pipe( + getFirstCompletedRemoteData(), + map(data => { + if (data.hasSucceeded) { + return of(data); + } else { + of(null); + } + }) + ) : of(null); } protected extractUUID(items: Item[], metadataToFollow: FollowAuthorityMetadata[]): string[] { diff --git a/src/app/core/core.module.ts b/src/app/core/core.module.ts index bab4d48346d..c8f2fb45185 100644 --- a/src/app/core/core.module.ts +++ b/src/app/core/core.module.ts @@ -162,7 +162,7 @@ import { EditItemMode } from './submission/models/edititem-mode.model'; import { AuditDataService } from './audit/audit-data.service'; import { Audit } from './audit/model/audit.model'; import { ItemExportFormat } from './itemexportformat/model/item-export-format.model'; -import { MetricsComponentsDataService } from './layout/metrics-components-data.service'; +import { MetricsComponentsService } from './layout/metrics-components.service'; import { MetricsComponent } from './layout/models/metrics-component.model'; import { Metric } from './shared/metric.model'; import { MetricsDataService } from './data/metrics-data.service'; @@ -354,7 +354,7 @@ const PROVIDERS = [ FilteredDiscoveryPageResponseParsingService, { provide: NativeWindowService, useFactory: NativeWindowFactory }, TabDataService, - MetricsComponentsDataService, + MetricsComponentsService, MetricsDataService, VocabularyService, VocabularyDataService, diff --git a/src/app/core/json-patch/builder/json-patch-operations-builder.ts b/src/app/core/json-patch/builder/json-patch-operations-builder.ts index a4886d370d3..6bd488c3043 100644 --- a/src/app/core/json-patch/builder/json-patch-operations-builder.ts +++ b/src/app/core/json-patch/builder/json-patch-operations-builder.ts @@ -13,6 +13,8 @@ import { VocabularyEntry } from '../../submission/vocabularies/models/vocabulary import { FormFieldMetadataValueObject } from '../../../shared/form/builder/models/form-field-metadata-value.model'; import { FormFieldLanguageValueObject } from '../../../shared/form/builder/models/form-field-language-value.model'; import { CoreState } from '../../core-state.model'; +import { Metadata } from '../../shared/metadata.utils'; +import { ConfidenceType } from '../../shared/confidence-type'; /** * Provides methods to dispatch JsonPatch Operations Actions @@ -148,6 +150,11 @@ export class JsonPatchOperationsBuilder { } else { operationValue = value; } + //Update confidence if was added once the field was already created, value is set only in constructor of FormFieldMetadataValueObject + if (Metadata.hasValidAuthority(operationValue.authority) && (isEmpty(operationValue.confidence) || operationValue.confidence === -1)) { + operationValue.confidence = ConfidenceType.CF_ACCEPTED; + } + } else if (value instanceof Date) { if (securityLevel != null) { operationValue = new FormFieldMetadataValueObject(dateToISOFormat(value), null, securityLevel); diff --git a/src/app/core/layout/metrics-components-data.service.spec.ts b/src/app/core/layout/metrics-components.service.spec.ts similarity index 88% rename from src/app/core/layout/metrics-components-data.service.spec.ts rename to src/app/core/layout/metrics-components.service.spec.ts index dafcfb1cb2b..199e0dd150d 100644 --- a/src/app/core/layout/metrics-components-data.service.spec.ts +++ b/src/app/core/layout/metrics-components.service.spec.ts @@ -1,12 +1,12 @@ import { Metric } from '../shared/metric.model'; -import { MetricsComponentsDataService } from './metrics-components-data.service'; +import { MetricsComponentsService } from './metrics-components.service'; -describe('MetricsComponentsDataService', () => { +describe('MetricsComponentsService', () => { - let service: MetricsComponentsDataService; + let service: MetricsComponentsService; beforeEach(() => { - service = new MetricsComponentsDataService(); + service = new MetricsComponentsService(); }); @@ -44,8 +44,8 @@ describe('MetricsComponentsDataService', () => { expect(result.length).toBe(1); expect(result[0].metrics.length).toBe(2); - expect(result[0].metrics[0].metricType).toBe('views'); - expect(result[0].metrics[1].metricType).toBe('downloads'); + expect(result[0].metrics[0].metricType).toBe('downloads'); + expect(result[0].metrics[1].metricType).toBe('views'); }); diff --git a/src/app/core/layout/metrics-components-data.service.ts b/src/app/core/layout/metrics-components.service.ts similarity index 74% rename from src/app/core/layout/metrics-components-data.service.ts rename to src/app/core/layout/metrics-components.service.ts index 4a415e242fa..46bbb7d25e0 100644 --- a/src/app/core/layout/metrics-components-data.service.ts +++ b/src/app/core/layout/metrics-components.service.ts @@ -4,7 +4,7 @@ import { CrisLayoutMetricRow } from './models/tab.model'; /** * A service responsible for managing metrics objects */ -export class MetricsComponentsDataService { +export class MetricsComponentsService { /** * Get matching metrics for item. @@ -20,17 +20,13 @@ export class MetricsComponentsDataService { computeMetricsRows(itemMetrics: Metric[], maxColumn, metricTypes: string[]): CrisLayoutMetricRow[] { - // support - const typeMap = {}; - metricTypes.forEach((type) => typeMap[type] = type); - // filter, enrich, order - const metrics = itemMetrics - .filter((metric) => typeMap[metric.metricType]) - .map((metric) => { - return { ...metric, position: typeMap[metric.metricType].position }; + const metrics: Metric[] = itemMetrics + .filter((metric: Metric) => metricTypes.includes(metric.metricType)) + .map((metric: Metric) => { + return { ...metric, position: metricTypes.indexOf(metric.metricType) }; }) - .sort((metric) => metric.position); + .sort((metricA, metricB ) => metricA.position - metricB.position); // chunker const totalRow = Math.ceil(metrics.length / maxColumn); diff --git a/src/app/core/shared/client-math.service.ts b/src/app/core/shared/client-math.service.ts new file mode 100644 index 00000000000..9c7ab784c9f --- /dev/null +++ b/src/app/core/shared/client-math.service.ts @@ -0,0 +1,105 @@ +import { DOCUMENT } from '@angular/common'; +import { + Inject, + Injectable, +} from '@angular/core'; +import { Observable, ReplaySubject, Subject } from 'rxjs'; +import { environment } from 'src/environments/environment'; +import { + NativeWindowRef, + NativeWindowService, +} from '../services/window.service';import { MathJaxConfig, MathService } from './math.service'; + +@Injectable({ + providedIn: 'root' +}) +/** + * Provide the MathService for CSR + */ +export class ClientMathService extends MathService { + + protected isReady$: Subject; + + protected mathJaxOptions = { + tex: { + inlineMath: [['$', '$'], ['\\(', '\\)']] + }, + svg: { + fontCache: 'global' + }, + startup: { + typeset: false + } + }; + + protected mathJax: MathJaxConfig = { + source: 'https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-chtml.js', + id: 'MathJaxScript' + }; + protected mathJaxFallback: MathJaxConfig = { + source: 'https://cdnjs.cloudflare.com/ajax/libs/mathjax/3.2.2/es5/tex-chtml.min.js', + id: 'MathJaxBackupScript' + }; + + constructor( + @Inject(DOCUMENT) private _document: Document, + @Inject(NativeWindowService) protected _window: NativeWindowRef, + ) { + super(); + + this.isReady$ = new ReplaySubject(); + + void this.registerMathJaxAsync(this.mathJax) + .then(() => this.isReady$.next(true)) + .catch(_ => { + void this.registerMathJaxAsync(this.mathJaxFallback) + .then(() => this.isReady$.next(true)); + }); + } + + /** + * Register the specified MathJax script in the document + * + * @param config The configuration object for the script + */ + protected async registerMathJaxAsync(config: MathJaxConfig): Promise { + if (environment.markdown.mathjax) { + return new Promise((resolve, reject) => { + + const optionsScript: HTMLScriptElement = this._document.createElement('script'); + optionsScript.type = 'text/javascript'; + optionsScript.text = `MathJax = ${JSON.stringify(this.mathJaxOptions)};`; + this._document.head.appendChild(optionsScript); + + const script: HTMLScriptElement = this._document.createElement('script'); + script.id = config.id; + script.type = 'text/javascript'; + script.src = config.source; + script.crossOrigin = 'anonymous'; + script.async = true; + script.onload = () => resolve(); + script.onerror = error => reject(error); + this._document.head.appendChild(script); + }); + } + return Promise.resolve(); + } + + /** + * Return the status of the script registration + */ + ready(): Observable { + return this.isReady$; + } + + /** + * Render the specified element using the MathJax JavaScript + * + * @param element The element to render with MathJax + */ + render(element: HTMLElement) { + if (environment.markdown.mathjax) { + this._window.nativeWindow.MathJax.typesetPromise([element]); + } + } +} diff --git a/src/app/core/shared/math.service.spec.ts b/src/app/core/shared/math.service.spec.ts new file mode 100644 index 00000000000..aa632272bd2 --- /dev/null +++ b/src/app/core/shared/math.service.spec.ts @@ -0,0 +1,47 @@ +import { TestBed } from '@angular/core/testing'; +import { Observable, of } from 'rxjs'; +import { MathService, MathJaxConfig } from './math.service'; + +export class MockMathService extends MathService { + protected mathJaxOptions: any = {}; + protected mathJax: MathJaxConfig = { source: '', id: '' }; + protected mathJaxFallback: MathJaxConfig = { source: '', id: '' }; + + protected registerMathJaxAsync(config: MathJaxConfig): Promise { + return Promise.resolve(); + } + + ready(): Observable { + return of(true); + } + + render(element: HTMLElement): void { + return; + } +} + +describe('MathService', () => { + let service: MockMathService; + + beforeEach(() => { + TestBed.configureTestingModule({}); + service = new MockMathService(); + spyOn(service, 'render'); + }); + + it('should be created', () => { + expect(service).toBeTruthy(); + }); + + it('should be ready', (done) => { + service.ready().subscribe(isReady => { + expect(isReady).toBe(true); + done(); + }); + }); + + it('should render', () => { + service.render(document.createElement('div')); + expect(service.render).toHaveBeenCalled(); + }); +}); diff --git a/src/app/core/shared/math.service.ts b/src/app/core/shared/math.service.ts new file mode 100644 index 00000000000..c06ce06220b --- /dev/null +++ b/src/app/core/shared/math.service.ts @@ -0,0 +1,19 @@ +import { Observable } from 'rxjs'; + +export interface MathJaxConfig { + source: string; + id: string; +} + +/** + * This service is used to provide the MathJax library with the ability to render markdown code + */ +export abstract class MathService { + protected abstract mathJaxOptions: any; + protected abstract mathJax: MathJaxConfig; + protected abstract mathJaxFallback: MathJaxConfig; + + protected abstract registerMathJaxAsync(config: MathJaxConfig): Promise; + abstract ready(): Observable; + abstract render(element: HTMLElement): void; +} diff --git a/src/app/core/shared/server-math.service.ts b/src/app/core/shared/server-math.service.ts new file mode 100644 index 00000000000..1484e84537e --- /dev/null +++ b/src/app/core/shared/server-math.service.ts @@ -0,0 +1,44 @@ +import { Injectable } from '@angular/core'; +import { Observable, ReplaySubject, Subject } from 'rxjs'; +import { MathJaxConfig, MathService } from './math.service'; + +@Injectable({ + providedIn: 'root' +}) +/** + * Provide the MathService for SSR + */ +export class ServerMathService extends MathService { + + protected signal: Subject; + + protected mathJaxOptions = {}; + + protected mathJax: MathJaxConfig = { + source: '', + id: '' + }; + protected mathJaxFallback: MathJaxConfig = { + source: '', + id: '' + }; + + constructor() { + super(); + + this.signal = new ReplaySubject(); + this.signal.next(true); + } + + protected async registerMathJaxAsync(config: MathJaxConfig): Promise { + return Promise.resolve(); + } + + ready(): Observable { + return this.signal; + } + + render(element: HTMLElement) { + return; + } +} diff --git a/src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/metadata/rendering-types/advanced-attachment/advanced-attachment.component.spec.ts b/src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/metadata/rendering-types/advanced-attachment/advanced-attachment.component.spec.ts index 5331ef09b2c..137bcea9baf 100644 --- a/src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/metadata/rendering-types/advanced-attachment/advanced-attachment.component.spec.ts +++ b/src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/metadata/rendering-types/advanced-attachment/advanced-attachment.component.spec.ts @@ -152,6 +152,7 @@ describe('AdvancedAttachmentComponent', () => { { provide: 'fieldProvider', useValue: mockField }, { provide: 'itemProvider', useValue: testItem }, { provide: 'renderingSubTypeProvider', useValue: '' }, + { provide: 'tabNameProvider', useValue: '' }, { provide: BitstreamDataService, useValue: mockBitstreamDataService }, { provide: AuthorizationDataService, useValue: mockAuthorizedService }, ], diff --git a/src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/metadata/rendering-types/advanced-attachment/advanced-attachment.component.ts b/src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/metadata/rendering-types/advanced-attachment/advanced-attachment.component.ts index dacefea5e11..d0a221fdc9e 100644 --- a/src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/metadata/rendering-types/advanced-attachment/advanced-attachment.component.ts +++ b/src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/metadata/rendering-types/advanced-attachment/advanced-attachment.component.ts @@ -41,10 +41,11 @@ export class AdvancedAttachmentComponent extends AttachmentComponent implements @Inject('fieldProvider') public fieldProvider: LayoutField, @Inject('itemProvider') public itemProvider: Item, @Inject('renderingSubTypeProvider') public renderingSubTypeProvider: string, + @Inject('tabNameProvider') public tabNameProvider: string, protected bitstreamDataService: BitstreamDataService, protected translateService: TranslateService ) { - super(fieldProvider, itemProvider, renderingSubTypeProvider, bitstreamDataService, translateService); + super(fieldProvider, itemProvider, renderingSubTypeProvider, tabNameProvider, bitstreamDataService, translateService); } getBitstreamsByItem(options?: FindListOptions): Observable> { diff --git a/src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/metadata/rendering-types/advanced-attachment/bitstream-attachment/attachment-render/attachment-render.component.ts b/src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/metadata/rendering-types/advanced-attachment/bitstream-attachment/attachment-render/attachment-render.component.ts index de716630b23..953c004f42c 100644 --- a/src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/metadata/rendering-types/advanced-attachment/bitstream-attachment/attachment-render/attachment-render.component.ts +++ b/src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/metadata/rendering-types/advanced-attachment/bitstream-attachment/attachment-render/attachment-render.component.ts @@ -36,6 +36,10 @@ export class AttachmentRenderComponent implements OnInit { * The bitstream */ @Input() renderingType: AttachmentRenderingType | string; + /** + * The tab name + */ + @Input() tabName: string; /** * Directive hook used to place the dynamic render component @@ -77,6 +81,7 @@ export class AttachmentRenderComponent implements OnInit { attachentComponentRef = this.attachmentValueViewRef.createComponent(factory, 0, this.getComponentInjector()); (attachentComponentRef.instance as any).item = this.item; (attachentComponentRef.instance as any).bitstream = this.bitstream; + (attachentComponentRef.instance as any).tabName = this.tabName; } return attachentComponentRef; } @@ -87,7 +92,8 @@ export class AttachmentRenderComponent implements OnInit { getComponentInjector() { const providers = [ {provide: 'itemProvider', useValue: this.item, deps: []}, - {provide: 'bitstreamProvider', useValue: this.bitstream, deps: []} + {provide: 'bitstreamProvider', useValue: this.bitstream, deps: []}, + {provide: 'tabNameProvider', useValue: this.tabName, deps: []} ]; return Injector.create({ diff --git a/src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/metadata/rendering-types/advanced-attachment/bitstream-attachment/bitstream-attachment.component.html b/src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/metadata/rendering-types/advanced-attachment/bitstream-attachment/bitstream-attachment.component.html index 01dbf22b091..e9e283989db 100644 --- a/src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/metadata/rendering-types/advanced-attachment/bitstream-attachment/bitstream-attachment.component.html +++ b/src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/metadata/rendering-types/advanced-attachment/bitstream-attachment/bitstream-attachment.component.html @@ -9,12 +9,12 @@
- +
- +
@@ -22,13 +22,19 @@
-
+
{{ 'cris-layout.advanced-attachment.'+attachmentConf.name | translate }} -

+

+ {{attachment.firstMetadataValue(attachmentConf.name) | titlecase}} +

+ +

{{attachment.firstMetadataValue(attachmentConf.name)}}

@@ -42,7 +48,7 @@ -

+

{{'cris-layout.advanced-attachment.label.not-present' | translate}}

{{getFormat(attachment) | async}}

@@ -53,7 +59,7 @@
-

+

{{'cris-layout.advanced-attachment.label.not-present' | translate}}

({{getChecksum(attachment).checkSumAlgorithm}}):{{ getChecksum(attachment).value }}

diff --git a/src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/metadata/rendering-types/advanced-attachment/bitstream-attachment/bitstream-attachment.component.spec.ts b/src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/metadata/rendering-types/advanced-attachment/bitstream-attachment/bitstream-attachment.component.spec.ts index 0d0f0790fde..d39347a4636 100644 --- a/src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/metadata/rendering-types/advanced-attachment/bitstream-attachment/bitstream-attachment.component.spec.ts +++ b/src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/metadata/rendering-types/advanced-attachment/bitstream-attachment/bitstream-attachment.component.spec.ts @@ -18,6 +18,7 @@ describe('BitstreamAttachmentComponent', () => { {provide: 'fieldProvider', useValue: {}}, {provide: 'itemProvider', useValue: {}}, {provide: 'renderingSubTypeProvider', useValue: ''}, + {provide: 'tabNameProvider', useValue: '' }, {provide: BitstreamDataService, useValue: {}}, {provide: TranslateService, useValue: {}}, ], diff --git a/src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/metadata/rendering-types/advanced-attachment/bitstream-attachment/bitstream-attachment.component.ts b/src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/metadata/rendering-types/advanced-attachment/bitstream-attachment/bitstream-attachment.component.ts index 4578a1a5d0e..8b4e332fec9 100644 --- a/src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/metadata/rendering-types/advanced-attachment/bitstream-attachment/bitstream-attachment.component.ts +++ b/src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/metadata/rendering-types/advanced-attachment/bitstream-attachment/bitstream-attachment.component.ts @@ -34,6 +34,12 @@ export class BitstreamAttachmentComponent extends BitstreamRenderingModelCompone */ allAttachmentProviders: string[] = []; + /** + * Attachment metadata to be displayed in title case + */ + + attachmentTypeMetadata = 'dc.type'; + @Input() attachment: Bitstream; @@ -41,12 +47,13 @@ export class BitstreamAttachmentComponent extends BitstreamRenderingModelCompone @Inject('fieldProvider') public fieldProvider: LayoutField, @Inject('itemProvider') public itemProvider: Item, @Inject('renderingSubTypeProvider') public renderingSubTypeProvider: string, + @Inject('tabNameProvider') public tabNameProvider: string, protected readonly bitstreamDataService: BitstreamDataService, protected readonly translateService: TranslateService, protected readonly router: Router, protected readonly route: ActivatedRoute, ) { - super(fieldProvider, itemProvider, renderingSubTypeProvider, bitstreamDataService, translateService); + super(fieldProvider, itemProvider, renderingSubTypeProvider, tabNameProvider, bitstreamDataService, translateService); } ngOnInit() { diff --git a/src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/metadata/rendering-types/attachment/attachment.component.html b/src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/metadata/rendering-types/attachment/attachment.component.html index 9a2713867fe..907cb32139d 100644 --- a/src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/metadata/rendering-types/attachment/attachment.component.html +++ b/src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/metadata/rendering-types/attachment/attachment.component.html @@ -2,7 +2,7 @@
- {{getType(attachment)}}: {{fileName(attachment)}} ({{getSize(attachment) | dsFileSize}}) + {{getType(attachment) | titlecase}}: {{fileName(attachment)}} ({{getSize(attachment) | dsFileSize}}) {{getDescription(attachment)}} diff --git a/src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/metadata/rendering-types/attachment/attachment.component.spec.ts b/src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/metadata/rendering-types/attachment/attachment.component.spec.ts index 3c17409f4b6..f2ec04ae99d 100644 --- a/src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/metadata/rendering-types/attachment/attachment.component.spec.ts +++ b/src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/metadata/rendering-types/attachment/attachment.component.spec.ts @@ -152,6 +152,7 @@ describe('AttachmentComponent', () => { { provide: 'fieldProvider', useValue: mockField }, { provide: 'itemProvider', useValue: testItem }, { provide: 'renderingSubTypeProvider', useValue: '' }, + { provide: 'tabNameProvider', useValue: '' }, { provide: BitstreamDataService, useValue: mockBitstreamDataService }, { provide: AuthorizationDataService, useValue: mockAuthorizedService }, ], diff --git a/src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/metadata/rendering-types/attachment/attachment.component.ts b/src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/metadata/rendering-types/attachment/attachment.component.ts index 74e068c0126..3111effe9f5 100644 --- a/src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/metadata/rendering-types/attachment/attachment.component.ts +++ b/src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/metadata/rendering-types/attachment/attachment.component.ts @@ -49,10 +49,11 @@ export class AttachmentComponent extends BitstreamAttachmentRenderingModelCompon @Inject('fieldProvider') public fieldProvider: LayoutField, @Inject('itemProvider') public itemProvider: Item, @Inject('renderingSubTypeProvider') public renderingSubTypeProvider: string, + @Inject('tabNameProvider') public tabNameProvider: string, protected bitstreamDataService: BitstreamDataService, protected translateService: TranslateService ) { - super(fieldProvider, itemProvider, renderingSubTypeProvider, bitstreamDataService, translateService); + super(fieldProvider, itemProvider, renderingSubTypeProvider, tabNameProvider, bitstreamDataService, translateService); } /** diff --git a/src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/metadata/rendering-types/attachment/bitstream-attachment-rendering.model.ts b/src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/metadata/rendering-types/attachment/bitstream-attachment-rendering.model.ts index d6007f725fe..64f141bdf7b 100644 --- a/src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/metadata/rendering-types/attachment/bitstream-attachment-rendering.model.ts +++ b/src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/metadata/rendering-types/attachment/bitstream-attachment-rendering.model.ts @@ -26,10 +26,11 @@ export abstract class BitstreamAttachmentRenderingModelComponent extends Bitstre @Inject('fieldProvider') fieldProvider: LayoutField, @Inject('itemProvider') itemProvider: Item, @Inject('renderingSubTypeProvider') renderingSubTypeProvider: string, + @Inject('tabNameProvider') public tabNameProvider: string, bitstreamDataService: BitstreamDataService, translateService: TranslateService ) { - super(fieldProvider, itemProvider, renderingSubTypeProvider, bitstreamDataService, translateService); + super(fieldProvider, itemProvider, renderingSubTypeProvider, tabNameProvider, bitstreamDataService, translateService); } getBitstreamsByItem(options?: FindListOptions): Observable> { diff --git a/src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/metadata/rendering-types/bitstream-rendering-model.ts b/src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/metadata/rendering-types/bitstream-rendering-model.ts index aee32ecf7b7..876a7c4b38a 100644 --- a/src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/metadata/rendering-types/bitstream-rendering-model.ts +++ b/src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/metadata/rendering-types/bitstream-rendering-model.ts @@ -35,10 +35,11 @@ export abstract class BitstreamRenderingModelComponent extends RenderingTypeStru @Inject('fieldProvider') public fieldProvider: LayoutField, @Inject('itemProvider') public itemProvider: Item, @Inject('renderingSubTypeProvider') public renderingSubTypeProvider: string, + @Inject('tabNameProvider') public tabNameProvider: string, protected bitstreamDataService: BitstreamDataService, protected translateService: TranslateService ) { - super(fieldProvider, itemProvider, renderingSubTypeProvider, translateService); + super(fieldProvider, itemProvider, renderingSubTypeProvider, tabNameProvider, translateService); } /** diff --git a/src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/metadata/rendering-types/crisref/crisref.component.spec.ts b/src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/metadata/rendering-types/crisref/crisref.component.spec.ts index 946b507a5de..67e87b3d17d 100644 --- a/src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/metadata/rendering-types/crisref/crisref.component.spec.ts +++ b/src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/metadata/rendering-types/crisref/crisref.component.spec.ts @@ -95,6 +95,7 @@ describe('CrisrefComponent', () => { { provide: 'itemProvider', useValue: testItem }, { provide: 'metadataValueProvider', useValue: metadataValue }, { provide: 'renderingSubTypeProvider', useValue: '' }, + { provide: 'tabNameProvider', useValue: '' }, { provide: ItemDataService, useValue: itemService }, ], declarations: [CrisrefComponent, MetadataLinkViewComponent] diff --git a/src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/metadata/rendering-types/date/date.component.spec.ts b/src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/metadata/rendering-types/date/date.component.spec.ts index 8cb149367bb..affed15eeda 100644 --- a/src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/metadata/rendering-types/date/date.component.spec.ts +++ b/src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/metadata/rendering-types/date/date.component.spec.ts @@ -65,6 +65,7 @@ describe('DateComponent', () => { { provide: 'itemProvider', useValue: testItem }, { provide: 'metadataValueProvider', useValue: metadataValue }, { provide: 'renderingSubTypeProvider', useValue: '' }, + { provide: 'tabNameProvider', useValue: '' }, { provide: LocaleService, useValue: localeServiceMock }, ], declarations: [DateComponent, DsDatePipe] diff --git a/src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/metadata/rendering-types/identifier/identifier.component.spec.ts b/src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/metadata/rendering-types/identifier/identifier.component.spec.ts index efed64a68eb..3aad5d5ad9a 100644 --- a/src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/metadata/rendering-types/identifier/identifier.component.spec.ts +++ b/src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/metadata/rendering-types/identifier/identifier.component.spec.ts @@ -83,6 +83,7 @@ describe('IdentifierComponent', () => { { provide: 'itemProvider', useValue: testItem }, { provide: 'metadataValueProvider', useValue: doiMetadataValueWithoutSubType }, { provide: 'renderingSubTypeProvider', useValue: '' }, + { provide: 'tabNameProvider', useValue: '' }, { provide: ResolverStrategyService, useClass: ResolverStrategyService } ], declarations: [IdentifierComponent] diff --git a/src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/metadata/rendering-types/identifier/identifier.component.ts b/src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/metadata/rendering-types/identifier/identifier.component.ts index 85b8d33035c..6a83e0c58ac 100644 --- a/src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/metadata/rendering-types/identifier/identifier.component.ts +++ b/src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/metadata/rendering-types/identifier/identifier.component.ts @@ -42,10 +42,11 @@ export class IdentifierComponent extends RenderingTypeValueModelComponent implem @Inject('itemProvider') public itemProvider: Item, @Inject('metadataValueProvider') public metadataValueProvider: MetadataValue, @Inject('renderingSubTypeProvider') public renderingSubTypeProvider: string, + @Inject('tabNameProvider') public tabNameProvider: string, protected resolver: ResolverStrategyService, protected translateService: TranslateService ) { - super(fieldProvider, itemProvider, metadataValueProvider, renderingSubTypeProvider, translateService); + super(fieldProvider, itemProvider, metadataValueProvider, renderingSubTypeProvider, tabNameProvider, translateService); } getIdentifierFromValue() { diff --git a/src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/metadata/rendering-types/link/link.component.spec.ts b/src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/metadata/rendering-types/link/link.component.spec.ts index 603db9adff8..cf630aa13a5 100644 --- a/src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/metadata/rendering-types/link/link.component.spec.ts +++ b/src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/metadata/rendering-types/link/link.component.spec.ts @@ -60,6 +60,7 @@ describe('LinkComponent', () => { { provide: 'itemProvider', useValue: testItem }, { provide: 'metadataValueProvider', useValue: metadataValue }, { provide: 'renderingSubTypeProvider', useValue: '' }, + {provide: 'tabNameProvider', useValue: '' }, ], declarations: [LinkComponent] }) diff --git a/src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/metadata/rendering-types/link/link.component.ts b/src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/metadata/rendering-types/link/link.component.ts index ea61fbabd81..49dbd4659a2 100644 --- a/src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/metadata/rendering-types/link/link.component.ts +++ b/src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/metadata/rendering-types/link/link.component.ts @@ -43,9 +43,10 @@ export class LinkComponent extends RenderingTypeValueModelComponent implements O @Inject('itemProvider') public itemProvider: Item, @Inject('metadataValueProvider') public metadataValueProvider: MetadataValue, @Inject('renderingSubTypeProvider') public renderingSubTypeProvider: string, + @Inject('tabNameProvider') public tabNameProvider: string, protected translateService: TranslateService ) { - super(fieldProvider, itemProvider, metadataValueProvider, renderingSubTypeProvider, translateService); + super(fieldProvider, itemProvider, metadataValueProvider, renderingSubTypeProvider, tabNameProvider, translateService); } ngOnInit(): void { diff --git a/src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/metadata/rendering-types/longtext/longtext.component.spec.ts b/src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/metadata/rendering-types/longtext/longtext.component.spec.ts index c914ab30bcc..7b43a95e5a0 100644 --- a/src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/metadata/rendering-types/longtext/longtext.component.spec.ts +++ b/src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/metadata/rendering-types/longtext/longtext.component.spec.ts @@ -58,6 +58,7 @@ describe('LongtextComponent', () => { { provide: 'itemProvider', useValue: testItem }, { provide: 'metadataValueProvider', useValue: metadataValue }, { provide: 'renderingSubTypeProvider', useValue: '' }, + { provide: 'tabNameProvider', useValue: '' }, ], declarations: [LongtextComponent] }) diff --git a/src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/metadata/rendering-types/metadataGroup/inline/inline.component.html b/src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/metadata/rendering-types/metadataGroup/inline/inline.component.html index 9f028e671cf..b8f736a1429 100644 --- a/src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/metadata/rendering-types/metadataGroup/inline/inline.component.html +++ b/src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/metadata/rendering-types/metadataGroup/inline/inline.component.html @@ -4,8 +4,8 @@ + [metadataValue]="mdg.value">
-
\ No newline at end of file +
diff --git a/src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/metadata/rendering-types/metadataGroup/inline/inline.component.spec.ts b/src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/metadata/rendering-types/metadataGroup/inline/inline.component.spec.ts index b85fe2cdb93..0de292434e3 100644 --- a/src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/metadata/rendering-types/metadataGroup/inline/inline.component.spec.ts +++ b/src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/metadata/rendering-types/metadataGroup/inline/inline.component.spec.ts @@ -84,6 +84,7 @@ describe('InlineComponent', () => { { provide: 'fieldProvider', useValue: mockField }, { provide: 'itemProvider', useValue: testItem }, { provide: 'renderingSubTypeProvider', useValue: '' }, + { provide: 'tabNameProvider', useValue: '' }, ], declarations: [ DsDatePipe, diff --git a/src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/metadata/rendering-types/metadataGroup/inline/inline.component.ts b/src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/metadata/rendering-types/metadataGroup/inline/inline.component.ts index c27b97c85bf..e5a1127f46f 100644 --- a/src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/metadata/rendering-types/metadataGroup/inline/inline.component.ts +++ b/src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/metadata/rendering-types/metadataGroup/inline/inline.component.ts @@ -22,9 +22,10 @@ export class InlineComponent extends MetadataGroupComponent implements OnInit { @Inject('fieldProvider') public fieldProvider: LayoutField, @Inject('itemProvider') public itemProvider: Item, @Inject('renderingSubTypeProvider') public renderingSubTypeProvider: string, + @Inject('tabNameProvider') public tabNameProvider: string, protected translateService: TranslateService ) { - super(fieldProvider, itemProvider, renderingSubTypeProvider, translateService); + super(fieldProvider, itemProvider, renderingSubTypeProvider, tabNameProvider, translateService); } } diff --git a/src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/metadata/rendering-types/metadataGroup/metadata-group.component.ts b/src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/metadata/rendering-types/metadataGroup/metadata-group.component.ts index be0fa271e19..1c244a83fad 100644 --- a/src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/metadata/rendering-types/metadataGroup/metadata-group.component.ts +++ b/src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/metadata/rendering-types/metadataGroup/metadata-group.component.ts @@ -43,9 +43,10 @@ export abstract class MetadataGroupComponent extends RenderingTypeStructuredMode @Inject('fieldProvider') public fieldProvider: LayoutField, @Inject('itemProvider') public itemProvider: Item, @Inject('renderingSubTypeProvider') public renderingSubTypeProvider: string, + @Inject('tabNameProvider') public tabNameProvider: string, protected translateService: TranslateService ) { - super(fieldProvider, itemProvider, renderingSubTypeProvider, translateService); + super(fieldProvider, itemProvider, renderingSubTypeProvider, tabNameProvider, translateService); } ngOnInit() { diff --git a/src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/metadata/rendering-types/metadataGroup/table/table.component.html b/src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/metadata/rendering-types/metadataGroup/table/table.component.html index 3b76c16e322..26a7dd93354 100644 --- a/src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/metadata/rendering-types/metadataGroup/table/table.component.html +++ b/src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/metadata/rendering-types/metadataGroup/table/table.component.html @@ -17,4 +17,3 @@
- diff --git a/src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/metadata/rendering-types/metadataGroup/table/table.component.spec.ts b/src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/metadata/rendering-types/metadataGroup/table/table.component.spec.ts index 8313c724ba2..082a242902b 100644 --- a/src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/metadata/rendering-types/metadataGroup/table/table.component.spec.ts +++ b/src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/metadata/rendering-types/metadataGroup/table/table.component.spec.ts @@ -89,6 +89,7 @@ describe('TableComponent', () => { { provide: 'fieldProvider', useValue: mockField }, { provide: 'itemProvider', useValue: testItem }, { provide: 'renderingSubTypeProvider', useValue: '' }, + { provide: 'tabNameProvider', useValue: '' }, ], declarations: [ DsDatePipe, diff --git a/src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/metadata/rendering-types/orcid/orcid.component.spec.ts b/src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/metadata/rendering-types/orcid/orcid.component.spec.ts index e8637237681..8954483b005 100644 --- a/src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/metadata/rendering-types/orcid/orcid.component.spec.ts +++ b/src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/metadata/rendering-types/orcid/orcid.component.spec.ts @@ -185,6 +185,7 @@ describe('OrcidComponent', () => { { provide: 'itemProvider', useValue: testItem }, { provide: 'metadataValueProvider', useValue: metadataValue }, { provide: 'renderingSubTypeProvider', useValue: '' }, + { provide: 'tabNameProvider', useValue: '' }, { provide: ConfigurationDataService, useValue: configurationDataService} ], declarations: [OrcidComponent, DsDatePipe] diff --git a/src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/metadata/rendering-types/orcid/orcid.component.ts b/src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/metadata/rendering-types/orcid/orcid.component.ts index c3d79b588a7..1d885b0aa5f 100644 --- a/src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/metadata/rendering-types/orcid/orcid.component.ts +++ b/src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/metadata/rendering-types/orcid/orcid.component.ts @@ -32,10 +32,11 @@ export class OrcidComponent extends RenderingTypeValueModelComponent implements @Inject('itemProvider') public itemProvider: Item, @Inject('metadataValueProvider') public metadataValueProvider: MetadataValue, @Inject('renderingSubTypeProvider') public renderingSubTypeProvider: string, + @Inject('tabNameProvider') public tabNameProvider: string, private configurationService: ConfigurationDataService, protected translateService: TranslateService ) { - super(fieldProvider, itemProvider, metadataValueProvider, renderingSubTypeProvider, translateService); + super(fieldProvider, itemProvider, metadataValueProvider, renderingSubTypeProvider, tabNameProvider, translateService); } ngOnInit() { diff --git a/src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/metadata/rendering-types/rendering-type-structured.model.ts b/src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/metadata/rendering-types/rendering-type-structured.model.ts index 344f2c8ee6a..6a4f35d0ace 100644 --- a/src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/metadata/rendering-types/rendering-type-structured.model.ts +++ b/src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/metadata/rendering-types/rendering-type-structured.model.ts @@ -17,12 +17,14 @@ export abstract class RenderingTypeStructuredModelComponent extends RenderingTyp @Inject('fieldProvider') public fieldProvider: LayoutField, @Inject('itemProvider') public itemProvider: Item, @Inject('renderingSubTypeProvider') public renderingSubTypeProvider: string, + @Inject('tabNameProvider') public tabNameProvider: string, protected translateService: TranslateService ) { super(translateService); this.field = fieldProvider; this.item = itemProvider; this.renderingSubType = renderingSubTypeProvider; + this.tabName = tabNameProvider; } } diff --git a/src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/metadata/rendering-types/rendering-type-value.model.ts b/src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/metadata/rendering-types/rendering-type-value.model.ts index 04c53e006a7..8e367296b55 100644 --- a/src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/metadata/rendering-types/rendering-type-value.model.ts +++ b/src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/metadata/rendering-types/rendering-type-value.model.ts @@ -24,6 +24,7 @@ export abstract class RenderingTypeValueModelComponent extends RenderingTypeMode @Inject('itemProvider') public itemProvider: Item, @Inject('metadataValueProvider') public metadataValueProvider: MetadataValue, @Inject('renderingSubTypeProvider') public renderingSubTypeProvider: string, + @Inject('tabNameProvider') public tabNameProvider: string, protected translateService: TranslateService ) { super(translateService); @@ -31,6 +32,7 @@ export abstract class RenderingTypeValueModelComponent extends RenderingTypeMode this.item = itemProvider; this.metadataValue = metadataValueProvider; this.renderingSubType = renderingSubTypeProvider; + this.tabName = tabNameProvider; } /** diff --git a/src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/metadata/rendering-types/rendering-type.model.ts b/src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/metadata/rendering-types/rendering-type.model.ts index 56d08a5a202..dfab55432f8 100644 --- a/src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/metadata/rendering-types/rendering-type.model.ts +++ b/src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/metadata/rendering-types/rendering-type.model.ts @@ -37,6 +37,11 @@ export abstract class RenderingTypeModelComponent { @Input() indexToBeRendered; + /** + * The tab name + */ + @Input() tabName: string; + /** * The prefix used for box field label's i18n key */ diff --git a/src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/metadata/rendering-types/tag/tag.component.ts b/src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/metadata/rendering-types/tag/tag.component.ts index d476dd5904f..db1c424b184 100644 --- a/src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/metadata/rendering-types/tag/tag.component.ts +++ b/src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/metadata/rendering-types/tag/tag.component.ts @@ -29,9 +29,10 @@ export class TagComponent extends RenderingTypeStructuredModelComponent implemen @Inject('fieldProvider') public fieldProvider: LayoutField, @Inject('itemProvider') public itemProvider: Item, @Inject('renderingSubTypeProvider') public renderingSubTypeProvider: string, + @Inject('tabNameProvider') public tabNameProvider: string, protected translateService: TranslateService ) { - super(fieldProvider, itemProvider, renderingSubTypeProvider, translateService); + super(fieldProvider, itemProvider, renderingSubTypeProvider, tabNameProvider, translateService); } /** diff --git a/src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/metadata/rendering-types/text/text.component.spec.ts b/src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/metadata/rendering-types/text/text.component.spec.ts index b406544e4b1..26004bf2b6c 100644 --- a/src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/metadata/rendering-types/text/text.component.spec.ts +++ b/src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/metadata/rendering-types/text/text.component.spec.ts @@ -59,6 +59,7 @@ describe('TextComponent', () => { { provide: 'itemProvider', useValue: testItem }, { provide: 'metadataValueProvider', useValue: metadataValue }, { provide: 'renderingSubTypeProvider', useValue: '' }, + { provide: 'tabNameProvider', useValue: '' }, ], declarations: [TextComponent, DsDatePipe] }) diff --git a/src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/metadata/rendering-types/thumbnail/thumbnail.component.spec.ts b/src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/metadata/rendering-types/thumbnail/thumbnail.component.spec.ts index 5aab0e0dbc5..24b96d9d93f 100644 --- a/src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/metadata/rendering-types/thumbnail/thumbnail.component.spec.ts +++ b/src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/metadata/rendering-types/thumbnail/thumbnail.component.spec.ts @@ -104,6 +104,7 @@ describe('ThumbnailComponent', () => { { provide: 'fieldProvider', useValue: fieldProvider }, { provide: 'itemProvider', useValue: testItem }, { provide: 'renderingSubTypeProvider', useValue: '' }, + { provide: 'tabNameProvider', useValue: '' }, { provide: BitstreamDataService, useValue: mockBitstreamDataService }, { provide: AuthorizationDataService, useValue: mockAuthorizedService }, { provide: ConfigurationDataService, useValue: {} }, diff --git a/src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/metadata/rendering-types/thumbnail/thumbnail.component.ts b/src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/metadata/rendering-types/thumbnail/thumbnail.component.ts index cc034068556..f2a65ec9506 100644 --- a/src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/metadata/rendering-types/thumbnail/thumbnail.component.ts +++ b/src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/metadata/rendering-types/thumbnail/thumbnail.component.ts @@ -50,10 +50,11 @@ export class ThumbnailComponent extends BitstreamRenderingModelComponent impleme @Inject('fieldProvider') public fieldProvider: LayoutField, @Inject('itemProvider') public itemProvider: Item, @Inject('renderingSubTypeProvider') public renderingSubTypeProvider: string, + @Inject('tabNameProvider') public tabNameProvider: string, protected bitstreamDataService: BitstreamDataService, protected translateService: TranslateService ) { - super(fieldProvider, itemProvider, renderingSubTypeProvider, bitstreamDataService, translateService); + super(fieldProvider, itemProvider, renderingSubTypeProvider, tabNameProvider, bitstreamDataService, translateService); } /** diff --git a/src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/metadata/rendering-types/valuepair/valuepair.component.spec.ts b/src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/metadata/rendering-types/valuepair/valuepair.component.spec.ts index 9e7860915f2..26fc90fc4db 100644 --- a/src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/metadata/rendering-types/valuepair/valuepair.component.spec.ts +++ b/src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/metadata/rendering-types/valuepair/valuepair.component.spec.ts @@ -109,6 +109,7 @@ describe('ValuepairComponent', () => { { provide: 'itemProvider', useValue: testItem1 }, { provide: 'metadataValueProvider', useValue: { value: 'it', authority: null } }, { provide: 'renderingSubTypeProvider', useValue: VOCABULARY_NAME_1 }, + { provide: 'tabNameProvider', useValue: '' }, ], }).compileComponents(); @@ -156,6 +157,7 @@ describe('ValuepairComponent', () => { { provide: 'itemProvider', useValue: testItem2 }, { provide: 'metadataValueProvider', useValue: {value: undefined, authority: VOCABULARY_NAME_2 + ':asd' } }, { provide: 'renderingSubTypeProvider', useValue: VOCABULARY_NAME_2 }, + { provide: 'tabNameProvider', useValue: '' }, ], }).compileComponents(); diff --git a/src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/metadata/rendering-types/valuepair/valuepair.component.ts b/src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/metadata/rendering-types/valuepair/valuepair.component.ts index f5c09cf612d..8683375bce1 100644 --- a/src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/metadata/rendering-types/valuepair/valuepair.component.ts +++ b/src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/metadata/rendering-types/valuepair/valuepair.component.ts @@ -39,11 +39,12 @@ export class ValuepairComponent extends RenderingTypeValueModelComponent impleme @Inject('itemProvider') public itemProvider: Item, @Inject('metadataValueProvider') public metadataValueProvider: MetadataValue, @Inject('renderingSubTypeProvider') public renderingSubTypeProvider: string, + @Inject('tabNameProvider') public tabNameProvider: string, protected translateService: TranslateService, protected vocabularyService: VocabularyService, protected authService: AuthService ) { - super(fieldProvider, itemProvider, metadataValueProvider, renderingSubTypeProvider, translateService); + super(fieldProvider, itemProvider, metadataValueProvider, renderingSubTypeProvider, tabNameProvider, translateService); } ngOnInit(): void { diff --git a/src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/metadata/row/metadata-container/metadata-container.component.ts b/src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/metadata/row/metadata-container/metadata-container.component.ts index 790edacc729..e2072a5c0a8 100644 --- a/src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/metadata/row/metadata-container/metadata-container.component.ts +++ b/src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/metadata/row/metadata-container/metadata-container.component.ts @@ -38,6 +38,10 @@ export class MetadataContainerComponent implements OnInit { * The metadata field to render */ @Input() field: LayoutField; + /** + * The tab name + */ + @Input() tabName: string; /** * The prefix used for box field label's i18n key diff --git a/src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/metadata/row/metadata-container/metadata-render/metadata-render.component.spec.ts b/src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/metadata/row/metadata-container/metadata-render/metadata-render.component.spec.ts index 03974fc4a4f..ac9d74b4dc9 100644 --- a/src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/metadata/row/metadata-container/metadata-render/metadata-render.component.spec.ts +++ b/src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/metadata/row/metadata-container/metadata-render/metadata-render.component.spec.ts @@ -121,7 +121,8 @@ describe('MetadataRenderComponent', () => { }) ], providers: [ - Injector + Injector, + { provide: 'tabNameProvider', useValue: '' }, ], declarations: [ DsDatePipe, diff --git a/src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/metadata/row/metadata-container/metadata-render/metadata-render.component.ts b/src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/metadata/row/metadata-container/metadata-render/metadata-render.component.ts index 292555760c8..61deb46312d 100644 --- a/src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/metadata/row/metadata-container/metadata-render/metadata-render.component.ts +++ b/src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/metadata/row/metadata-container/metadata-render/metadata-render.component.ts @@ -102,7 +102,7 @@ export class MetadataRenderComponent implements OnInit { const providers = [ { provide: 'fieldProvider', useValue: this.field, deps: [] }, { provide: 'itemProvider', useValue: this.item, deps: [] }, - { provide: 'renderingSubTypeProvider', useValue: this.renderingSubType, deps: [] } + { provide: 'renderingSubTypeProvider', useValue: this.renderingSubType, deps: [] }, ]; if (isNotEmpty(this.metadataValue)) { this.metadataValue = this.normalizeMetadataValue(this.metadataValue); diff --git a/src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/metrics/cris-layout-metrics-box.component.spec.ts b/src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/metrics/cris-layout-metrics-box.component.spec.ts index f69017b6342..565972f6a15 100644 --- a/src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/metrics/cris-layout-metrics-box.component.spec.ts +++ b/src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/metrics/cris-layout-metrics-box.component.spec.ts @@ -1,5 +1,5 @@ /* eslint-disable max-classes-per-file */ -import { async, ComponentFixture, TestBed } from '@angular/core/testing'; +import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; import { Observable, of } from 'rxjs'; import { RemoteData } from '../../../../../core/data/remote-data'; @@ -16,7 +16,7 @@ import { SharedModule } from '../../../../../shared/shared.module'; import { CrisLayoutMetricsBoxComponent } from './cris-layout-metrics-box.component'; import { metricsComponent } from '../../../../../shared/testing/metrics-components.mock'; import { MetricsComponent } from '../../../../../core/layout/models/metrics-component.model'; -import { MetricsComponentsDataService } from '../../../../../core/layout/metrics-components-data.service'; +import { MetricsComponentsService } from '../../../../../core/layout/metrics-components.service'; import { Metric } from '../../../../../core/shared/metric.model'; import { ItemDataService } from '../../../../../core/data/item-data.service'; import { CrisLayoutMetricRow } from '../../../../../core/layout/models/tab.model'; @@ -78,7 +78,7 @@ describe('CrisLayoutMetricsBoxComponent', () => { let component: CrisLayoutMetricsBoxComponent; let fixture: ComponentFixture; - beforeEach(async(() => { + beforeEach(waitForAsync(() => { itemDataService = jasmine.createSpyObj('ItemDataService', { getMetrics: jasmine.createSpy('getMetrics') @@ -94,7 +94,7 @@ describe('CrisLayoutMetricsBoxComponent', () => { BrowserAnimationsModule, SharedModule], providers: [ - { provide: MetricsComponentsDataService, useClass: MetricsComponentsDataServiceMock }, + { provide: MetricsComponentsService, useClass: MetricsComponentsDataServiceMock }, { provide: ItemDataService, useValue: itemDataService }, { provide: 'boxProvider', useClass: boxMetrics }, { provide: 'itemProvider', useClass: { metrics: [metric1Mock, metric2Mock] } }, diff --git a/src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/metrics/cris-layout-metrics-box.component.ts b/src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/metrics/cris-layout-metrics-box.component.ts index a6b771a8ad8..05de62cfe76 100644 --- a/src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/metrics/cris-layout-metrics-box.component.ts +++ b/src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/metrics/cris-layout-metrics-box.component.ts @@ -8,7 +8,7 @@ import { RenderCrisLayoutBoxFor } from '../../../../decorators/cris-layout-box.d import { LayoutBox } from '../../../../enums/layout-box.enum'; import { getFirstSucceededRemoteDataPayload } from '../../../../../core/shared/operators'; import { hasValue } from '../../../../../shared/empty.util'; -import { MetricsComponentsDataService } from '../../../../../core/layout/metrics-components-data.service'; +import { MetricsComponentsService } from '../../../../../core/layout/metrics-components.service'; import { ItemDataService } from '../../../../../core/data/item-data.service'; import { CrisLayoutBox, MetricsBoxConfiguration, } from '../../../../../core/layout/models/box.model'; import { Item } from '../../../../../core/shared/item.model'; @@ -51,7 +51,7 @@ export class CrisLayoutMetricsBoxComponent extends CrisLayoutBoxModelComponent i subs: Subscription[] = []; constructor( - protected metricsComponentService: MetricsComponentsDataService, + protected metricsComponentService: MetricsComponentsService, protected itemService: ItemDataService, protected translateService: TranslateService, @Inject('boxProvider') public boxProvider: CrisLayoutBox, diff --git a/src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/cris-layout-box-container.component.ts b/src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/cris-layout-box-container.component.ts index 7a0c5fd708d..53f9061306f 100644 --- a/src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/cris-layout-box-container.component.ts +++ b/src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/cris-layout-box-container.component.ts @@ -22,6 +22,11 @@ export class CrisLayoutBoxContainerComponent implements OnInit { */ @Input() item: Item; + /** + * The tab name + */ + @Input() tabName: string; + /** * CrisLayoutBoxRenderOptions reference of the box that will be created */ @@ -64,6 +69,7 @@ export class CrisLayoutBoxContainerComponent implements OnInit { providers: [ { provide: 'boxProvider', useFactory: () => (this.box), deps: [] }, { provide: 'itemProvider', useFactory: () => (this.item), deps: [] }, + { provide: 'tabNameProvider', useFactory: () => (this.tabName), deps: []} ], parent: this.injector }); diff --git a/src/app/cris-layout/cris-layout-matrix/cris-layout-matrix.component.html b/src/app/cris-layout/cris-layout-matrix/cris-layout-matrix.component.html index 116e653e046..836f9688fad 100644 --- a/src/app/cris-layout/cris-layout-matrix/cris-layout-matrix.component.html +++ b/src/app/cris-layout/cris-layout-matrix/cris-layout-matrix.component.html @@ -5,7 +5,7 @@
- +
diff --git a/src/app/cris-layout/enums/layout-box.enum.ts b/src/app/cris-layout/enums/layout-box.enum.ts index 143c14ab535..d0dfbf15d96 100644 --- a/src/app/cris-layout/enums/layout-box.enum.ts +++ b/src/app/cris-layout/enums/layout-box.enum.ts @@ -13,4 +13,5 @@ export enum LayoutBox { ORCID_SYNC_QUEUE = 'ORCID_SYNC_QUEUE', IIIFVIEWER = 'IIIFVIEWER', COLLECTIONS = 'COLLECTIONS', + VERSIONING = 'VERSIONING', } diff --git a/src/app/info/end-user-agreement/end-user-agreement-content/end-user-agreement-content.component.html b/src/app/info/end-user-agreement/end-user-agreement-content/end-user-agreement-content.component.html index c545a833530..91f6de4e1a2 100644 --- a/src/app/info/end-user-agreement/end-user-agreement-content/end-user-agreement-content.component.html +++ b/src/app/info/end-user-agreement/end-user-agreement-content/end-user-agreement-content.component.html @@ -1,3 +1,3 @@

{{ 'info.end-user-agreement.head' | translate }}

- + diff --git a/src/app/item-page/cris-item-page-tab.resolver.spec.ts b/src/app/item-page/cris-item-page-tab.resolver.spec.ts index c7a422ccec2..5101bcfa9cc 100644 --- a/src/app/item-page/cris-item-page-tab.resolver.spec.ts +++ b/src/app/item-page/cris-item-page-tab.resolver.spec.ts @@ -10,6 +10,7 @@ import { CrisItemPageTabResolver } from './cris-item-page-tab.resolver'; import { TabDataService } from '../core/layout/tab-data.service'; import { createPaginatedList } from '../shared/testing/utils.test'; import { tabDetailsTest, tabPublicationsTest } from '../shared/testing/layout-tab.mocks'; +import { PLATFORM_ID } from '@angular/core'; describe('CrisItemPageTabResolver', () => { beforeEach(() => { @@ -32,6 +33,7 @@ describe('CrisItemPageTabResolver', () => { let hardRedirectService: HardRedirectService; let router; + let platformId; const uuid = '1234-65487-12354-1235'; const item = Object.assign(new Item(), { @@ -54,6 +56,7 @@ describe('CrisItemPageTabResolver', () => { beforeEach(() => { router = TestBed.inject(Router); + platformId = TestBed.inject(PLATFORM_ID); itemService.findById.and.returnValue(createSuccessfulRemoteDataObject$(item)); @@ -63,65 +66,131 @@ describe('CrisItemPageTabResolver', () => { }); describe('and there tabs', () => { - beforeEach(() => { - - (tabService as any).findByItem.and.returnValue(tabsRD$); - - spyOn(router, 'navigateByUrl'); - resolver = new CrisItemPageTabResolver(hardRedirectService, tabService, itemService, router); + describe('when platform is browser', () => { + beforeEach(() => { + + (tabService as any).findByItem.and.returnValue(tabsRD$); + + spyOn(router, 'navigateByUrl'); + + resolver = new CrisItemPageTabResolver(platformId, hardRedirectService, tabService, itemService, router); + }); + + it('should redirect to root route if given tab is the first one', (done) => { + resolver.resolve({ params: { id: uuid } } as any, { url: '/entities/publication/1234-65487-12354-1235/publications' } as any) + .pipe(take(1)) + .subscribe( + (resolved) => { + expect(router.navigateByUrl).toHaveBeenCalledWith('/entities/publication/1234-65487-12354-1235'); + expect(hardRedirectService.redirect).not.toHaveBeenCalled(); + expect(resolved).toEqual(tabsRD); + done(); + } + ); + }); + + it('should not redirect to root route if tab different than the main one is given', (done) => { + resolver.resolve({ params: { id: uuid } } as any, { url: '/entities/publication/1234-65487-12354-1235/details' } as any) + .pipe(take(1)) + .subscribe( + (resolved) => { + expect(router.navigateByUrl).not.toHaveBeenCalled(); + expect(hardRedirectService.redirect).not.toHaveBeenCalled(); + expect(resolved).toEqual(tabsRD); + done(); + } + ); + }); + + it('should not redirect to root route if no tab is given', (done) => { + resolver.resolve({ params: { id: uuid } } as any, { url: '/entities/publication/1234-65487-12354-1235' } as any) + .pipe(take(1)) + .subscribe( + (resolved) => { + expect(router.navigateByUrl).not.toHaveBeenCalled(); + expect(hardRedirectService.redirect).not.toHaveBeenCalled(); + expect(resolved).toEqual(tabsRD); + done(); + } + ); + }); + + it('should navigate to 404 if a wrong tab is given', (done) => { + resolver.resolve({ params: { id: uuid } } as any, { url: '/entities/publication/1234-65487-12354-1235/test' } as any) + .pipe(take(1)) + .subscribe( + (resolved) => { + expect(router.navigateByUrl).toHaveBeenCalled(); + expect(hardRedirectService.redirect).not.toHaveBeenCalled(); + expect(resolved).toEqual(tabsRD); + done(); + } + ); + }); }); - it('should redirect to root route if given tab is the first one', (done) => { - resolver.resolve({ params: { id: uuid } } as any, { url: '/entities/publication/1234-65487-12354-1235/publications' } as any) - .pipe(take(1)) - .subscribe( - (resolved) => { - expect(router.navigateByUrl).not.toHaveBeenCalled(); - expect(hardRedirectService.redirect).toHaveBeenCalledWith('/entities/publication/1234-65487-12354-1235', 302); - expect(resolved).toEqual(tabsRD); - done(); - } - ); - }); - - it('should not redirect to root route if tab different than the main one is given', (done) => { - resolver.resolve({ params: { id: uuid } } as any, { url: '/entities/publication/1234-65487-12354-1235/details' } as any) - .pipe(take(1)) - .subscribe( - (resolved) => { - expect(router.navigateByUrl).not.toHaveBeenCalled(); - expect(hardRedirectService.redirect).not.toHaveBeenCalled(); - expect(resolved).toEqual(tabsRD); - done(); - } - ); - }); - - it('should not redirect to root route if no tab is given', (done) => { - resolver.resolve({ params: { id: uuid } } as any, { url: '/entities/publication/1234-65487-12354-1235' } as any) - .pipe(take(1)) - .subscribe( - (resolved) => { - expect(router.navigateByUrl).not.toHaveBeenCalled(); - expect(hardRedirectService.redirect).not.toHaveBeenCalled(); - expect(resolved).toEqual(tabsRD); - done(); - } - ); - }); - - it('should navigate to 404 if a wrong tab is given', (done) => { - resolver.resolve({ params: { id: uuid } } as any, { url: '/entities/publication/1234-65487-12354-1235/test' } as any) - .pipe(take(1)) - .subscribe( - (resolved) => { - expect(router.navigateByUrl).toHaveBeenCalled(); - expect(hardRedirectService.redirect).not.toHaveBeenCalled(); - expect(resolved).toEqual(tabsRD); - done(); - } - ); + describe('when platform is server', () => { + beforeEach(() => { + platformId = 'server'; + (tabService as any).findByItem.and.returnValue(tabsRD$); + + spyOn(router, 'navigateByUrl'); + + resolver = new CrisItemPageTabResolver(platformId, hardRedirectService, tabService, itemService, router); + }); + + it('should redirect to root route if given tab is the first one', (done) => { + resolver.resolve({ params: { id: uuid } } as any, { url: '/entities/publication/1234-65487-12354-1235/publications' } as any) + .pipe(take(1)) + .subscribe( + (resolved) => { + expect(router.navigateByUrl).not.toHaveBeenCalled(); + expect(hardRedirectService.redirect).toHaveBeenCalledWith('/entities/publication/1234-65487-12354-1235', 302); + expect(resolved).toEqual(tabsRD); + done(); + } + ); + }); + + it('should not redirect to root route if tab different than the main one is given', (done) => { + resolver.resolve({ params: { id: uuid } } as any, { url: '/entities/publication/1234-65487-12354-1235/details' } as any) + .pipe(take(1)) + .subscribe( + (resolved) => { + expect(router.navigateByUrl).not.toHaveBeenCalled(); + expect(hardRedirectService.redirect).not.toHaveBeenCalled(); + expect(resolved).toEqual(tabsRD); + done(); + } + ); + }); + + it('should not redirect to root route if no tab is given', (done) => { + resolver.resolve({ params: { id: uuid } } as any, { url: '/entities/publication/1234-65487-12354-1235' } as any) + .pipe(take(1)) + .subscribe( + (resolved) => { + expect(router.navigateByUrl).not.toHaveBeenCalled(); + expect(hardRedirectService.redirect).not.toHaveBeenCalled(); + expect(resolved).toEqual(tabsRD); + done(); + } + ); + }); + + it('should navigate to 404 if a wrong tab is given', (done) => { + resolver.resolve({ params: { id: uuid } } as any, { url: '/entities/publication/1234-65487-12354-1235/test' } as any) + .pipe(take(1)) + .subscribe( + (resolved) => { + expect(router.navigateByUrl).toHaveBeenCalled(); + expect(hardRedirectService.redirect).not.toHaveBeenCalled(); + expect(resolved).toEqual(tabsRD); + done(); + } + ); + }); }); }); @@ -132,7 +201,7 @@ describe('CrisItemPageTabResolver', () => { spyOn(router, 'navigateByUrl'); - resolver = new CrisItemPageTabResolver(hardRedirectService, tabService, itemService, router); + resolver = new CrisItemPageTabResolver(platformId, hardRedirectService, tabService, itemService, router); }); it('should not redirect nor navigate', (done) => { diff --git a/src/app/item-page/cris-item-page-tab.resolver.ts b/src/app/item-page/cris-item-page-tab.resolver.ts index b381a993664..7def5960f28 100644 --- a/src/app/item-page/cris-item-page-tab.resolver.ts +++ b/src/app/item-page/cris-item-page-tab.resolver.ts @@ -1,4 +1,4 @@ -import { Injectable} from '@angular/core'; +import { Inject, Injectable, PLATFORM_ID } from '@angular/core'; import { ActivatedRouteSnapshot, Resolve, Router, RouterStateSnapshot } from '@angular/router'; import { Observable } from 'rxjs'; @@ -15,6 +15,7 @@ import { getItemPageRoute } from './item-page-routing-paths'; import { createFailedRemoteDataObject$ } from '../shared/remote-data.utils'; import { HardRedirectService } from '../core/services/hard-redirect.service'; import { getPageNotFoundRoute } from '../app-routing-paths'; +import { isPlatformServer } from '@angular/common'; /** * This class represents a resolver that requests the tabs of specific @@ -24,6 +25,7 @@ import { getPageNotFoundRoute } from '../app-routing-paths'; export class CrisItemPageTabResolver implements Resolve>> { constructor( + @Inject(PLATFORM_ID) protected platformId: any, private hardRedirectService: HardRedirectService, private tabService: TabDataService, private itemDataService: ItemDataService, @@ -63,8 +65,13 @@ export class CrisItemPageTabResolver implements Resolve - + diff --git a/src/app/item-page/field-components/metadata-values/metadata-values.component.ts b/src/app/item-page/field-components/metadata-values/metadata-values.component.ts index cbbae9006da..befed0bc14a 100644 --- a/src/app/item-page/field-components/metadata-values/metadata-values.component.ts +++ b/src/app/item-page/field-components/metadata-values/metadata-values.component.ts @@ -37,7 +37,7 @@ export class MetadataValuesComponent implements OnChanges { @Input() label: string; /** - * Whether the {@link MarkdownPipe} should be used to render these metadata values. + * Whether the {@link MarkdownDirective} should be used to render these metadata values. * This will only have effect if {@link MarkdownConfig#enabled} is true. * Mathjax will only be rendered if {@link MarkdownConfig#mathjax} is true. */ diff --git a/src/app/item-page/full/field-components/file-section/full-file-section.component.html b/src/app/item-page/full/field-components/file-section/full-file-section.component.html index 50d84fe22df..463ef32aedf 100644 --- a/src/app/item-page/full/field-components/file-section/full-file-section.component.html +++ b/src/app/item-page/full/field-components/file-section/full-file-section.component.html @@ -34,8 +34,8 @@
{{"item.page.filesection.original.bundle"
- - {{"item.page.filesection.download" | translate}} + + {{"item.page.filesection.download" | translate}}
diff --git a/src/app/item-page/full/field-components/file-section/full-file-section.component.spec.ts b/src/app/item-page/full/field-components/file-section/full-file-section.component.spec.ts index b2e956efafb..37e75d30aa0 100644 --- a/src/app/item-page/full/field-components/file-section/full-file-section.component.spec.ts +++ b/src/app/item-page/full/field-components/file-section/full-file-section.component.spec.ts @@ -22,6 +22,7 @@ import { APP_CONFIG } from 'src/config/app-config.interface'; import { environment } from 'src/environments/environment'; import { UUIDService } from '../../../../core/shared/uuid.service'; import { getMockUUIDService } from '../../../../shared/mocks/uuid.service.mock'; +import { AuthorizationDataService } from '../../../../core/data/feature-authorization/authorization-data.service'; describe('FullFileSectionComponent', () => { let comp: FullFileSectionComponent; @@ -58,6 +59,10 @@ describe('FullFileSectionComponent', () => { findAllByItemAndBundleName: createSuccessfulRemoteDataObject$(createPaginatedList([mockBitstream, mockBitstream, mockBitstream])) }); + const authorizedDataService = jasmine.createSpyObj('authorizedDataService',{ + isAuthorized: observableOf(false), + }); + const paginationService = new PaginationServiceStub(); beforeEach(waitForAsync(() => { @@ -75,7 +80,8 @@ describe('FullFileSectionComponent', () => { { provide: NotificationsService, useValue: new NotificationsServiceStub() }, { provide: PaginationService, useValue: paginationService }, { provide: APP_CONFIG, useValue: environment }, - { provide: UUIDService, useValue: getMockUUIDService() } + { provide: UUIDService, useValue: getMockUUIDService() }, + { provide: AuthorizationDataService, useValue: authorizedDataService }, ], schemas: [NO_ERRORS_SCHEMA] @@ -98,5 +104,14 @@ describe('FullFileSectionComponent', () => { const fileNameElement = fixture.debugElement.query(By.css('[data-test="file-name"]')).nativeElement; expect(fileNameElement.classList).toContain('text-break'); }); + + it('canDownload should return an observable with false value, if user is not authorized to download bitstream', waitForAsync(() => { + authorizedDataService.isAuthorized.and.returnValue(observableOf(false)); + comp.canDownload(mockBitstream).subscribe(canDownload => expect(canDownload).toBeFalse()); + })); + it('canDownload should return an observable with true value, if user is authorized to download bitstream', waitForAsync(() => { + authorizedDataService.isAuthorized.and.returnValue(observableOf(true)); + comp.canDownload(mockBitstream).subscribe(canDownload => expect(canDownload).toBeTrue()); + })); }); }); diff --git a/src/app/item-page/full/field-components/file-section/full-file-section.component.ts b/src/app/item-page/full/field-components/file-section/full-file-section.component.ts index 6b66b48a8c6..6590f340e40 100644 --- a/src/app/item-page/full/field-components/file-section/full-file-section.component.ts +++ b/src/app/item-page/full/field-components/file-section/full-file-section.component.ts @@ -17,6 +17,8 @@ import { PaginationService } from '../../../../core/pagination/pagination.servic import { DSONameService } from '../../../../core/breadcrumbs/dso-name.service'; import { AppConfig, APP_CONFIG } from 'src/config/app-config.interface'; import { UUIDService } from '../../../../core/shared/uuid.service'; +import { AuthorizationDataService } from '../../../../core/data/feature-authorization/authorization-data.service'; +import { FeatureID } from '../../../../core/data/feature-authorization/feature-id'; /** * This component renders the file section of the item @@ -56,6 +58,7 @@ export class FullFileSectionComponent extends FileSectionComponent implements On protected paginationService: PaginationService, public dsoNameService: DSONameService, protected uuidService: UUIDService, + public authorizationService: AuthorizationDataService, @Inject(APP_CONFIG) protected appConfig: AppConfig ) { super(bitstreamDataService, notificationsService, translateService, dsoNameService, appConfig); @@ -112,6 +115,10 @@ export class FullFileSectionComponent extends FileSectionComponent implements On return bitstream?.allMetadataValues('bitstream.viewer.provider').includes('nodownload'); } + canDownload(file: Bitstream): Observable { + return this.authorizationService.isAuthorized(FeatureID.CanDownload, file.self); + } + ngOnDestroy(): void { this.paginationService.clearPagination(this.originalOptions.id); this.paginationService.clearPagination(this.licenseOptions.id); diff --git a/src/app/item-page/item-page-routing-paths.ts b/src/app/item-page/item-page-routing-paths.ts index 8433a75c4ca..4ec25421187 100644 --- a/src/app/item-page/item-page-routing-paths.ts +++ b/src/app/item-page/item-page-routing-paths.ts @@ -53,7 +53,7 @@ export function getEntityEditRoute(entityType: string, itemId: string) { * @param versionId the ID of the version for which the route will be retrieved */ export function getItemVersionRoute(versionId: string) { - return new URLCombiner(getItemModuleRoute(), ITEM_VERSION_PATH, versionId).toString(); + return new URLCombiner(getItemModuleRoute(), versionId).toString(); } diff --git a/src/app/item-page/item-page.module.ts b/src/app/item-page/item-page.module.ts index c490a41782e..d104852480d 100644 --- a/src/app/item-page/item-page.module.ts +++ b/src/app/item-page/item-page.module.ts @@ -61,6 +61,8 @@ import { ThemedItemAlertsComponent } from './alerts/themed-item-alerts.component import { ThemedFullFileSectionComponent } from './full/field-components/file-section/themed-full-file-section.component'; +import { MarkdownViewerModule } from '../shared/markdown-viewer/markdown-viewer.module'; + const ENTRY_COMPONENTS = [ // put only entry components that use custom decorator @@ -123,6 +125,7 @@ const DECLARATIONS = [ CrisItemPageModule, ContextMenuModule.withEntryComponents(), MiradorViewerModule, + MarkdownViewerModule, ], declarations: [ ...DECLARATIONS, diff --git a/src/app/item-page/simple/field-components/specific-field/abstract/item-page-abstract-field.component.ts b/src/app/item-page/simple/field-components/specific-field/abstract/item-page-abstract-field.component.ts index f29bef31a76..1e85ab743be 100644 --- a/src/app/item-page/simple/field-components/specific-field/abstract/item-page-abstract-field.component.ts +++ b/src/app/item-page/simple/field-components/specific-field/abstract/item-page-abstract-field.component.ts @@ -37,7 +37,7 @@ export class ItemPageAbstractFieldComponent extends ItemPageFieldComponent { label = 'item.page.abstract'; /** - * Use the {@link MarkdownPipe} to render dc.description.abstract values + * Use the {@link MarkdownDirective} to render dc.description.abstract values */ enableMarkdown = true; } diff --git a/src/app/item-page/simple/field-components/specific-field/generic/generic-item-page-field.component.ts b/src/app/item-page/simple/field-components/specific-field/generic/generic-item-page-field.component.ts index 53d2f6aa20d..5e32fa62f4b 100644 --- a/src/app/item-page/simple/field-components/specific-field/generic/generic-item-page-field.component.ts +++ b/src/app/item-page/simple/field-components/specific-field/generic/generic-item-page-field.component.ts @@ -36,7 +36,7 @@ export class GenericItemPageFieldComponent extends ItemPageFieldComponent { @Input() label: string; /** - * Whether the {@link MarkdownPipe} should be used to render this metadata. + * Whether the {@link MarkdownDirective} should be used to render this metadata. */ @Input() enableMarkdown = false; diff --git a/src/app/item-page/simple/field-components/specific-field/item-page-field.component.spec.ts b/src/app/item-page/simple/field-components/specific-field/item-page-field.component.spec.ts index 15b7a9df212..f3afdcb43d4 100644 --- a/src/app/item-page/simple/field-components/specific-field/item-page-field.component.spec.ts +++ b/src/app/item-page/simple/field-components/specific-field/item-page-field.component.spec.ts @@ -9,13 +9,14 @@ import { MetadataMap, MetadataValue } from '../../../../core/shared/metadata.mod import { createSuccessfulRemoteDataObject$ } from '../../../../shared/remote-data.utils'; import { createPaginatedList } from '../../../../shared/testing/utils.test'; import { environment } from '../../../../../environments/environment'; -import { MarkdownPipe } from '../../../../shared/utils/markdown.pipe'; import { SharedModule } from '../../../../shared/shared.module'; import { APP_CONFIG } from '../../../../../config/app-config.interface'; import { By } from '@angular/platform-browser'; import { BrowseDefinitionDataService } from '../../../../core/browse/browse-definition-data.service'; import { BrowseDefinitionDataServiceStub } from '../../../../shared/testing/browse-definition-data-service.stub'; import { RouterTestingModule } from '@angular/router/testing'; +import { MarkdownDirective } from '../../../../shared/utils/markdown.directive'; +import { MathService } from '../../../../core/shared/math.service'; let comp: ItemPageFieldComponent; let fixture: ComponentFixture; @@ -51,14 +52,15 @@ describe('ItemPageFieldComponent', () => { ], providers: [ { provide: APP_CONFIG, useValue: appConfig }, - { provide: BrowseDefinitionDataService, useValue: BrowseDefinitionDataServiceStub } + { provide: BrowseDefinitionDataService, useValue: BrowseDefinitionDataServiceStub }, + { provide: MathService, useValue: {} } ], declarations: [ItemPageFieldComponent, MetadataValuesComponent], schemas: [NO_ERRORS_SCHEMA] }).overrideComponent(ItemPageFieldComponent, { set: { changeDetection: ChangeDetectionStrategy.Default } }).compileComponents(); - markdownSpy = spyOn(MarkdownPipe.prototype, 'transform'); + markdownSpy = spyOn(MarkdownDirective.prototype, 'render'); fixture = TestBed.createComponent(ItemPageFieldComponent); comp = fixture.componentInstance; comp.item = mockItemWithMetadataFieldsAndValue(mockFields, mockValue); diff --git a/src/app/item-page/simple/field-components/specific-field/item-page-field.component.ts b/src/app/item-page/simple/field-components/specific-field/item-page-field.component.ts index fc526dabcc5..a715dd97250 100644 --- a/src/app/item-page/simple/field-components/specific-field/item-page-field.component.ts +++ b/src/app/item-page/simple/field-components/specific-field/item-page-field.component.ts @@ -26,7 +26,7 @@ export class ItemPageFieldComponent { @Input() item: Item; /** - * Whether the {@link MarkdownPipe} should be used to render this metadata. + * Whether the {@link MarkdownDirective} should be used to render this metadata. */ enableMarkdown = false; diff --git a/src/app/item-page/simple/item-page.component.html b/src/app/item-page/simple/item-page.component.html index a7263a43be8..d1537e75cb5 100644 --- a/src/app/item-page/simple/item-page.component.html +++ b/src/app/item-page/simple/item-page.component.html @@ -2,6 +2,9 @@
+
+ +
diff --git a/src/app/item-page/versions/item-versions.component.html b/src/app/item-page/versions/item-versions.component.html index 176c1f0cde6..fbe357102c0 100644 --- a/src/app/item-page/versions/item-versions.component.html +++ b/src/app/item-page/versions/item-versions.component.html @@ -34,7 +34,7 @@

{{"item.version.history.head" | translate}}

- {{version.version}} + {{version.version}} {{version.version}} diff --git a/src/app/item-page/versions/item-versions.component.spec.ts b/src/app/item-page/versions/item-versions.component.spec.ts index 41dc382083b..3ae0bd8541c 100644 --- a/src/app/item-page/versions/item-versions.component.spec.ts +++ b/src/app/item-page/versions/item-versions.component.spec.ts @@ -10,7 +10,7 @@ import { Version } from '../../core/shared/version.model'; import { VersionHistory } from '../../core/shared/version-history.model'; import { VersionHistoryDataService } from '../../core/data/version-history-data.service'; import { BrowserModule, By } from '@angular/platform-browser'; -import { createSuccessfulRemoteDataObject$ } from '../../shared/remote-data.utils'; +import { createSuccessfulRemoteDataObject, createSuccessfulRemoteDataObject$ } from '../../shared/remote-data.utils'; import { createPaginatedList } from '../../shared/testing/utils.test'; import { EMPTY, of, of as observableOf } from 'rxjs'; import { PaginationService } from '../../core/pagination/pagination.service'; @@ -26,7 +26,7 @@ import { FeatureID } from '../../core/data/feature-authorization/feature-id'; import { WorkspaceitemDataService } from '../../core/submission/workspaceitem-data.service'; import { WorkflowItemDataService } from '../../core/submission/workflowitem-data.service'; import { ConfigurationDataService } from '../../core/data/configuration-data.service'; -import { Router } from '@angular/router'; +import { ActivatedRoute, Router } from '@angular/router'; import { CommonModule } from '@angular/common'; import { ItemSharedModule } from '../item-shared.module'; import { UUIDService } from '../../core/shared/uuid.service'; @@ -135,6 +135,25 @@ describe('ItemVersionsComponent', () => { navigateByUrl: null, }); + const mockItem = Object.assign(new Item(), { + id: 'fake-id', + uuid: 'fake-id', + handle: 'fake/handle', + lastModified: '2018', + _links: { + self: { + href: 'https://localhost:8000/items/fake-id' + } + } + }); + + const routeStub = { + data: observableOf({ + dso: createSuccessfulRemoteDataObject(mockItem) + }), + children: [] + }; + beforeEach(waitForAsync(() => { TestBed.configureTestingModule({ @@ -153,7 +172,8 @@ describe('ItemVersionsComponent', () => { {provide: WorkflowItemDataService, useValue: workflowItemDataServiceSpy}, {provide: ConfigurationDataService, useValue: configurationServiceSpy}, { provide: Router, useValue: routerSpy }, - { provide: UUIDService, useValue: getMockUUIDService() } + { provide: UUIDService, useValue: getMockUUIDService() }, + { provide: ActivatedRoute, useValue: routeStub }, ], schemas: [NO_ERRORS_SCHEMA] }).compileComponents(); diff --git a/src/app/item-page/versions/item-versions.component.ts b/src/app/item-page/versions/item-versions.component.ts index 6ca6abfbb11..508c9045c4f 100644 --- a/src/app/item-page/versions/item-versions.component.ts +++ b/src/app/item-page/versions/item-versions.component.ts @@ -40,7 +40,7 @@ import { TranslateService } from '@ngx-translate/core'; import { ItemVersionsDeleteModalComponent } from './item-versions-delete-modal/item-versions-delete-modal.component'; import { VersionDataService } from '../../core/data/version-data.service'; import { ItemDataService } from '../../core/data/item-data.service'; -import { Router } from '@angular/router'; +import { ActivatedRoute, Router } from '@angular/router'; import { AuthorizationDataService } from '../../core/data/feature-authorization/authorization-data.service'; import { FeatureID } from '../../core/data/feature-authorization/feature-id'; import { ItemVersionsSharedService } from './item-versions-shared.service'; @@ -49,7 +49,11 @@ import { WorkspaceitemDataService } from '../../core/submission/workspaceitem-da import { WorkflowItemDataService } from '../../core/submission/workflowitem-data.service'; import { ConfigurationDataService } from '../../core/data/configuration-data.service'; import { UUIDService } from '../../core/shared/uuid.service'; +import { RenderCrisLayoutBoxFor } from '../../cris-layout/decorators/cris-layout-box.decorator'; +import { LayoutBox } from '../../cris-layout/enums/layout-box.enum'; + +@RenderCrisLayoutBoxFor(LayoutBox.VERSIONING) @Component({ selector: 'ds-item-versions', templateUrl: './item-versions.component.html', @@ -182,7 +186,8 @@ export class ItemVersionsComponent implements OnDestroy, OnInit { private workspaceItemDataService: WorkspaceitemDataService, private workflowItemDataService: WorkflowItemDataService, private configurationService: ConfigurationDataService, - private uuidService: UUIDService + private uuidService: UUIDService, + private route: ActivatedRoute, ) { } @@ -222,10 +227,14 @@ export class ItemVersionsComponent implements OnDestroy, OnInit { /** * Get the route to the specified version - * @param versionId the ID of the version for which the route will be retrieved + * @param version the version for which the route will be retrieved */ - getVersionRoute(versionId: string) { - return getItemVersionRoute(versionId); + getVersionRoute(version: Version): Observable { + return version.item.pipe( + getFirstCompletedRemoteData(), + map(data => data.payload), + map(item => getItemVersionRoute(item.uuid)) + ); } /** @@ -484,6 +493,21 @@ export class ItemVersionsComponent implements OnDestroy, OnInit { * Initialize all observables */ ngOnInit(): void { + if (!hasValue(this.item)) { + this.subs.push(this.route.data.pipe( + map((data) => { + return data.dso as RemoteData; + }), + getFirstCompletedRemoteData(), + map(data => data.payload) + ).subscribe((item) => { + this.item = item; + this.ngOnInit(); + })); + + return; + } + if (hasValue(this.item.version)) { this.versionRD$ = this.item.version; this.versionHistoryRD$ = this.versionRD$.pipe( diff --git a/src/app/openaire/broker/project-entry-import-modal/project-entry-import-modal.component.html b/src/app/openaire/broker/project-entry-import-modal/project-entry-import-modal.component.html index 1090fd22fcf..0f44f6ba0cc 100644 --- a/src/app/openaire/broker/project-entry-import-modal/project-entry-import-modal.component.html +++ b/src/app/openaire/broker/project-entry-import-modal/project-entry-import-modal.component.html @@ -9,7 +9,7 @@
+ + +
+ {{'form.other-information.selection.' + otherInfoKey | translate}} +
    + +
  • + {{info}} +
  • +
    +
+
diff --git a/src/app/shared/form/builder/ds-dynamic-form-ui/models/onebox/dynamic-onebox.component.scss b/src/app/shared/form/builder/ds-dynamic-form-ui/models/onebox/dynamic-onebox.component.scss index bacf1267820..1f8188fc9ff 100644 --- a/src/app/shared/form/builder/ds-dynamic-form-ui/models/onebox/dynamic-onebox.component.scss +++ b/src/app/shared/form/builder/ds-dynamic-form-ui/models/onebox/dynamic-onebox.component.scss @@ -41,6 +41,19 @@ cursor: pointer; } -.list-item img { - height: 20px +.additional-items-icon { + padding-right: 5rem !important; + cursor: pointer; +} +.additional-info-selection { + z-index: 9999; + width: calc(100% - 10px); + border-radius: 4px; + box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.175); + .list-item { + cursor: pointer; + } + .list-item:hover { + background-color: var(--bs-dropdown-link-hover-bg); + } } diff --git a/src/app/shared/form/builder/ds-dynamic-form-ui/models/onebox/dynamic-onebox.component.ts b/src/app/shared/form/builder/ds-dynamic-form-ui/models/onebox/dynamic-onebox.component.ts index e026e424511..c98ceb272a1 100644 --- a/src/app/shared/form/builder/ds-dynamic-form-ui/models/onebox/dynamic-onebox.component.ts +++ b/src/app/shared/form/builder/ds-dynamic-form-ui/models/onebox/dynamic-onebox.component.ts @@ -1,4 +1,12 @@ -import { ChangeDetectorRef, Component, EventEmitter, Input, OnInit, Output, ViewChild } from '@angular/core'; +import { + ChangeDetectorRef, + Component, + EventEmitter, + Input, + OnInit, + Output, + ViewChild +} from '@angular/core'; import { UntypedFormGroup } from '@angular/forms'; import { @@ -67,6 +75,9 @@ export class DsDynamicOneboxComponent extends DsDynamicVocabularyComponent imple previousValue: any; inputValue: any; preloadLevel: number; + additionalInfoSelectIsOpen = false; + alternativeNamesKey = 'alternative-names'; + private isHierarchicalVocabulary$: Observable; private subs: Subscription[] = []; @@ -94,6 +105,7 @@ export class DsDynamicOneboxComponent extends DsDynamicVocabularyComponent imple * to display in the onebox popup. */ search = (text$: Observable) => { + this.additionalInfoSelectIsOpen = false; return text$.pipe( merge(this.click$), debounceTime(300), @@ -167,7 +179,7 @@ export class DsDynamicOneboxComponent extends DsDynamicVocabularyComponent imple * @param event */ onInput(event) { - if (!this.model.vocabularyOptions.closed && isNotEmpty(event.target.value)) { + if (!this.model.vocabularyOptions.closed && isNotEmpty(event.target.value)) { this.inputValue = new FormFieldMetadataValueObject(event.target.value); if (this.model.value) { if ((this.model.value as any).securityLevel != null) { @@ -187,7 +199,7 @@ export class DsDynamicOneboxComponent extends DsDynamicVocabularyComponent imple if (isNotNull(this.inputValue) && this.model.value !== this.inputValue) { this.dispatchUpdate(this.inputValue); } - this.inputValue = null; + this.inputValue = null; } this.blur.emit(event); } else { @@ -221,8 +233,23 @@ export class DsDynamicOneboxComponent extends DsDynamicVocabularyComponent imple */ onSelectItem(event: NgbTypeaheadSelectItemEvent) { this.inputValue = null; - this.setCurrentValue(event.item); - this.dispatchUpdate(event.item); + const item = event.item; + + if ( hasValue(item.otherInformation)) { + const otherInfoKeys = Object.keys(item.otherInformation).filter((key) => !key.startsWith('data')); + const hasMultipleValues = otherInfoKeys.some(key => hasValue(item.otherInformation[key]) && item.otherInformation[key].includes('|||')); + + if (hasMultipleValues) { + this.setMultipleValuesForOtherInfo(otherInfoKeys, item); + } else { + this.resetMultipleValuesForOtherInfo(); + } + } else { + this.resetMultipleValuesForOtherInfo(); + } + + this.setCurrentValue(item); + this.dispatchUpdate(item); } /** @@ -287,22 +314,37 @@ export class DsDynamicOneboxComponent extends DsDynamicVocabularyComponent imple } else { result = value; } + this.currentValue = null; + this.cdr.detectChanges(); this.currentValue = result; this.previousValue = result; this.cdr.detectChanges(); } - + if (hasValue(this.currentValue.otherInformation)) { + const infoKeys = Object.keys(this.currentValue.otherInformation); + this.setMultipleValuesForOtherInfo(infoKeys, this.currentValue); + } } /** * Get the other information value removing the authority section (after the last ::) * @param itemValue the initial item value + * @param itemKey */ - getOtherInfoValue(itemValue: string): string { + getOtherInfoValue(itemValue: string, itemKey: string): string { if (!itemValue || !itemValue.includes('::')) { return itemValue; } + + if (itemValue.includes('|||')) { + let result = ''; + const values = itemValue.split('|||').map(item => item.substring(0, item.lastIndexOf('::'))); + const lastIndex = values.length - 1; + values.forEach((value, i) => result += i === lastIndex ? value : value + ' · '); + return result; + } + return itemValue.substring(0, itemValue.lastIndexOf('::')); } @@ -312,11 +354,63 @@ export class DsDynamicOneboxComponent extends DsDynamicVocabularyComponent imple .forEach((sub) => sub.unsubscribe()); } - /** - * Hide image on error - * @param image - */ - handleImgError(image: HTMLElement): void { - image.style.display = 'none'; + toggleOtherInfoSelection() { + this.additionalInfoSelectIsOpen = !this.additionalInfoSelectIsOpen; + } + + selectAlternativeInfo(info: string) { + this.searching = true; + + if (this.otherInfoKey !== this.alternativeNamesKey) { + this.otherInfoValue = info; + } else { + this.otherName = info; + } + + const temp = this.createVocabularyObject(info, info, this.currentValue.otherInformation); + this.currentValue = null; + this.currentValue = temp; + + const event = { + item: this.currentValue + } as any; + + this.onSelectItem(event); + this.searching = false; + this.toggleOtherInfoSelection(); + } + + + setMultipleValuesForOtherInfo(keys: string[], item: any) { + const hasAlternativeNames = keys.includes(this.alternativeNamesKey); + + this.otherInfoKey = hasAlternativeNames ? this.alternativeNamesKey : keys.find(key => hasValue(item.otherInformation[key]) && item.otherInformation[key].includes('|||')); + this.otherInfoValuesUnformatted = item.otherInformation[this.otherInfoKey] ? item.otherInformation[this.otherInfoKey].split('|||') : []; + this.otherInfoValues = this.otherInfoValuesUnformatted.map(unformattedItem => unformattedItem.substring(0, unformattedItem.lastIndexOf('::'))); + + if (hasAlternativeNames) { + this.otherName = hasValue(this.otherName) ? this.otherName : this.otherInfoValues[0]; + } + + if (keys.length > 1) { + this.otherInfoValue = hasValue(this.otherInfoValue) ? this.otherInfoValue : this.otherInfoValues[0]; + } + } + + resetMultipleValuesForOtherInfo() { + this.otherInfoKey = undefined; + this.otherInfoValuesUnformatted = []; + this.otherInfoValues = []; + this.otherInfoValue = undefined; + this.otherName = undefined; + } + + createVocabularyObject(display, value, otherInformation) { + return Object.assign(new VocabularyEntry(), this.model.value, { + display: display, + value: value, + otherInformation: otherInformation, + type: 'vocabularyEntry' + }); } } diff --git a/src/app/shared/form/builder/ds-dynamic-form-ui/models/relation-group/dynamic-relation-group.components.ts b/src/app/shared/form/builder/ds-dynamic-form-ui/models/relation-group/dynamic-relation-group.components.ts index 7d02412082a..062b7d1b42d 100644 --- a/src/app/shared/form/builder/ds-dynamic-form-ui/models/relation-group/dynamic-relation-group.components.ts +++ b/src/app/shared/form/builder/ds-dynamic-form-ui/models/relation-group/dynamic-relation-group.components.ts @@ -55,6 +55,7 @@ export class DsDynamicRelationGroupComponent extends DynamicFormControlComponent protected selectedChipItemIndex: number; private subs: Subscription[] = []; + private valueChangeSubscription: Subscription; constructor(private vocabularyService: VocabularyService, private formBuilderService: FormBuilderService, @@ -70,12 +71,18 @@ export class DsDynamicRelationGroupComponent extends DynamicFormControlComponent ngOnInit() { this.initChipsFromModelValue(); + this.valueChangeSubscription = this.model.valueChanges.subscribe(() => { + this.initChipsFromModelValue(); + }); } ngOnDestroy(): void { this.subs .filter((sub) => hasValue(sub)) .forEach((sub) => sub.unsubscribe()); + if (hasValue(this.valueChangeSubscription)) { + this.valueChangeSubscription.unsubscribe(); + } } onBlur(event) { @@ -103,7 +110,7 @@ export class DsDynamicRelationGroupComponent extends DynamicFormControlComponent modalRef.componentInstance.group = this.group; modalRef.componentInstance.model = this.model; - modalRef.componentInstance.editMode = this.selectedChipItem ? true : false; + modalRef.componentInstance.editMode = !!this.selectedChipItem; modalRef.componentInstance.itemIndex = this.selectedChipItemIndex; modalRef.componentInstance.item = this.selectedChipItem?.item; modalRef.componentInstance.changedSecurity = false; @@ -114,15 +121,23 @@ export class DsDynamicRelationGroupComponent extends DynamicFormControlComponent modalRef.componentInstance.add.pipe(take(1)).subscribe((item) => { this.chips.add(item); }); - + if (hasValue(this.valueChangeSubscription)) { + this.valueChangeSubscription.unsubscribe(); + } modalRef.result.then(() => { // close this.selectedChipItemIndex = null; this.selectedChipItem = null; + this.valueChangeSubscription = this.model.valueChanges.subscribe(() => { + this.initChipsFromModelValue(); + }); }, () => { // dismiss this.selectedChipItemIndex = null; this.selectedChipItem = null; + this.valueChangeSubscription = this.model.valueChanges.subscribe(() => { + this.initChipsFromModelValue(); + }); }); return modalRef; @@ -133,12 +148,13 @@ export class DsDynamicRelationGroupComponent extends DynamicFormControlComponent if (this.model.isEmpty()) { this.initChips([]); } else { - initChipsValue$ = observableOf(this.model.value as any[]); + initChipsValue$ = observableOf(this.model.getGroupValue() as any[]); // If authority this.subs.push(initChipsValue$.pipe( mergeMap((valueModel) => { const returnList: Observable[] = []; valueModel.forEach((valueObj) => { + const returnObj = Object.keys(valueObj).map((fieldName) => { let return$: Observable; if (isObject(valueObj[fieldName]) && this.hasValidAuthority(valueObj[fieldName]) && valueObj[fieldName].otherInformation === null) { @@ -171,7 +187,7 @@ export class DsDynamicRelationGroupComponent extends DynamicFormControlComponent } return acc; }, []), - filter((modelValues: any[]) => (this.model.value as any[]).length === modelValues.length) + filter((modelValues: any[]) => this.model.getGroupValue().length === modelValues.length) ).subscribe((modelValue) => { this.model.value = modelValue; this.initChips(modelValue); diff --git a/src/app/shared/form/builder/ds-dynamic-form-ui/models/relation-group/dynamic-relation-group.model.ts b/src/app/shared/form/builder/ds-dynamic-form-ui/models/relation-group/dynamic-relation-group.model.ts index 30217e8657e..0f0ba86d838 100644 --- a/src/app/shared/form/builder/ds-dynamic-form-ui/models/relation-group/dynamic-relation-group.model.ts +++ b/src/app/shared/form/builder/ds-dynamic-form-ui/models/relation-group/dynamic-relation-group.model.ts @@ -1,8 +1,10 @@ import { DynamicFormControlLayout, serializable } from '@ng-dynamic-forms/core'; import { DsDynamicInputModel, DsDynamicInputModelConfig } from '../ds-dynamic-input.model'; -import { isEmpty, isNull } from '../../../../../empty.util'; +import { hasValue, isEmpty, isNull } from '../../../../../empty.util'; import { FormRowModel } from '../../../../../../core/config/models/config-submission-form.model'; import { DYNAMIC_FORM_CONTROL_TYPE_RELATION_GROUP } from '../../ds-dynamic-form-constants'; +import { FormFieldMetadataValueObject } from '../../../models/form-field-metadata-value.model'; +import { VocabularyEntry } from '../../../../../../core/submission/vocabularies/models/vocabulary-entry.model'; /** * Dynamic Group Model configuration interface @@ -45,17 +47,17 @@ export class DynamicRelationGroupModel extends DsDynamicInputModel { this.value = config.value || []; } -/* get value() { - return (isEmpty(this.value)) ? null : this.value - }*/ + /* get value() { + return (isEmpty(this.value)) ? null : this.value + }*/ isEmpty() { const value = this.getGroupValue(); return (value.length === 1 && isNull(value[0][this.mandatoryField])); } - getGroupValue(): any[] { - if (isEmpty(this.value)) { + getGroupValue(value?: any): any[] { + if (isEmpty(this.value) && isEmpty(value)) { // If items is empty, last element has been removed // so emit an empty value that allows to dispatch // a remove JSON PATCH operation @@ -66,6 +68,17 @@ export class DynamicRelationGroupModel extends DsDynamicInputModel { emptyItem[field] = null; }); return [emptyItem]; + } else if ((this.value instanceof VocabularyEntry || this.value instanceof FormFieldMetadataValueObject) || + (hasValue(value) && (value instanceof VocabularyEntry || value instanceof FormFieldMetadataValueObject))) { + + const emptyItem = {}; + emptyItem[this.mandatoryField] = hasValue(value) && (value instanceof VocabularyEntry || value instanceof FormFieldMetadataValueObject) ? value : this.value; + this.relationFields + .forEach((field) => { + emptyItem[field] = hasValue((this.value as any).otherInformation) ? (this.value as any).otherInformation[field] : null; + }); + + return [emptyItem]; } return this.value as any[]; } diff --git a/src/app/shared/form/builder/ds-dynamic-form-ui/models/tag/dynamic-tag.component.html b/src/app/shared/form/builder/ds-dynamic-form-ui/models/tag/dynamic-tag.component.html index 887e0f00588..669de36ad6c 100644 --- a/src/app/shared/form/builder/ds-dynamic-form-ui/models/tag/dynamic-tag.component.html +++ b/src/app/shared/form/builder/ds-dynamic-form-ui/models/tag/dynamic-tag.component.html @@ -6,7 +6,7 @@ + [wrapperClass]="'border-bottom'"> { + [...this.formModels.keys()].forEach((formId) => { const models = this.formModels.get(formId); - const fieldModel: any = this.findById(fieldId, models); + let fieldModel: any = this.findById(fieldId, models); if (hasValue(fieldModel) && !fieldModel.hidden) { + const isIterable = (typeof value[Symbol.iterator] === 'function'); if (isNotEmpty(value)) { - if (fieldModel.repeatable && isNotEmpty(fieldModel.value)) { + if (fieldModel.repeatable && isNotEmpty(fieldModel.value) && !(!isIterable && fieldModel instanceof DynamicRelationGroupModel)) { // if model is repeatable and has already a value add a new field instead of replacing it const formGroup = this.formGroups.get(formId); const arrayContext = fieldModel.parent?.context; @@ -538,13 +542,30 @@ export class FormBuilderService extends DynamicFormService { returnModel = newAddedModel; } } else { - fieldModel.value = value; + if ((!isIterable && fieldModel instanceof DynamicRelationGroupModel) && isEmpty(fieldModel.value)) { + const config: DynamicRelationGroupModelConfig = { + submissionId: fieldModel.submissionId, + formConfiguration: fieldModel.formConfiguration, + isInlineGroup: fieldModel.isInlineGroup, + mandatoryField: fieldModel.mandatoryField, + relationFields: fieldModel.relationFields, + scopeUUID: fieldModel.scopeUUID, + submissionScope: fieldModel.submissionScope, + repeatable: fieldModel.repeatable, + metadataFields: fieldModel.metadataFields, + hasSelectableMetadata: fieldModel.hasSelectableMetadata, + id: fieldModel.id, + value: fieldModel.getGroupValue(value) + }; + fieldModel = new DynamicRelationGroupModel(config); + } else { + fieldModel.value = value; + } returnModel = fieldModel; + } } - return returnModel; } - return false; }); return returnModel; } diff --git a/src/app/shared/form/builder/parsers/tag-field-parser.ts b/src/app/shared/form/builder/parsers/tag-field-parser.ts index 08685e0e350..de5e99a8e4b 100644 --- a/src/app/shared/form/builder/parsers/tag-field-parser.ts +++ b/src/app/shared/form/builder/parsers/tag-field-parser.ts @@ -1,20 +1,23 @@ import { FieldParser } from './field-parser'; import { FormFieldMetadataValueObject } from '../models/form-field-metadata-value.model'; import { DynamicTagModel, DynamicTagModelConfig } from '../ds-dynamic-form-ui/models/tag/dynamic-tag.model'; - +import {DynamicFormControlLayout} from '@ng-dynamic-forms/core'; export class TagFieldParser extends FieldParser { - public modelFactory(fieldValue?: FormFieldMetadataValueObject | any, label?: boolean): any { + let clsTag: DynamicFormControlLayout; + clsTag = { + grid: { + container: 'mb-3 mt-3', + } + }; const tagModelConfig: DynamicTagModelConfig = this.initModel(null, label); if (this.configData.selectableMetadata[0].controlledVocabulary && this.configData.selectableMetadata[0].controlledVocabulary.length > 0) { this.setVocabularyOptions(tagModelConfig, this.parserOptions.collectionUUID); } - this.setValues(tagModelConfig, fieldValue, null, true); - - const tagModel = new DynamicTagModel(tagModelConfig); - + tagModelConfig.placeholder = 'Enter the Keywords'; + const tagModel = new DynamicTagModel(tagModelConfig, clsTag); return tagModel; } diff --git a/src/app/shared/form/chips/chips.component.ts b/src/app/shared/form/chips/chips.component.ts index e893a90bf06..cb8a33d413c 100644 --- a/src/app/shared/form/chips/chips.component.ts +++ b/src/app/shared/form/chips/chips.component.ts @@ -175,7 +175,7 @@ export class ChipsComponent implements OnChanges { } textTruncate(text: string): string { - if (text.length >= TOOLTIP_TEXT_LIMIT) { + if (text && text.length >= TOOLTIP_TEXT_LIMIT) { return `${text.substring(0, TOOLTIP_TEXT_LIMIT)}...`; } return text; diff --git a/src/app/shared/form/chips/models/chips-item.model.ts b/src/app/shared/form/chips/models/chips-item.model.ts index 277e6477cef..c2858263c8d 100644 --- a/src/app/shared/form/chips/models/chips-item.model.ts +++ b/src/app/shared/form/chips/models/chips-item.model.ts @@ -4,6 +4,7 @@ import { hasValue, isNotEmpty } from '../../../empty.util'; import { FormFieldMetadataValueObject } from '../../builder/models/form-field-metadata-value.model'; import { ConfidenceType } from '../../../../core/shared/confidence-type'; import { PLACEHOLDER_PARENT_METADATA } from '../../builder/ds-dynamic-form-ui/ds-dynamic-form-constants'; +import { environment } from '../../../../../environments/environment'; export interface ChipsItemIcon { metadata: string; @@ -57,16 +58,18 @@ export class ChipsItem { } hasVisibleIcons(): boolean { + const iconsVisibleWithNoAuthority = environment.submission.icons.iconsVisibleWithNoAuthority ?? []; if (isNotEmpty(this.icons)) { let hasVisible = false; // check if it has at least one visible icon for (const icon of this.icons) { if (this._item.hasOwnProperty(icon.metadata) && (((typeof this._item[icon.metadata] === 'string') && hasValue(this._item[icon.metadata])) - || (this._item[icon.metadata] as FormFieldMetadataValueObject).hasValue()) + || this._item[icon.metadata] && hasValue(this._item[icon.metadata].value)) && !this.hasPlaceholder(this._item[icon.metadata])) { if ((icon.visibleWhenAuthorityEmpty - || (this._item[icon.metadata] as FormFieldMetadataValueObject).confidence !== ConfidenceType.CF_UNSET) + || (this._item[icon.metadata] as FormFieldMetadataValueObject).confidence !== ConfidenceType.CF_UNSET + || iconsVisibleWithNoAuthority.includes(icon.style)) && isNotEmpty(icon.style)) { hasVisible = true; break; diff --git a/src/app/shared/form/chips/models/chips.model.ts b/src/app/shared/form/chips/models/chips.model.ts index 0617929d140..f62f90d4a50 100644 --- a/src/app/shared/form/chips/models/chips.model.ts +++ b/src/app/shared/form/chips/models/chips.model.ts @@ -8,6 +8,7 @@ import { MetadataIconConfig } from '../../../../../config/submission-config.inte import { FormFieldMetadataValueObject } from '../../builder/models/form-field-metadata-value.model'; import { VocabularyEntry } from '../../../../core/submission/vocabularies/models/vocabulary-entry.model'; import { PLACEHOLDER_PARENT_METADATA } from '../../builder/ds-dynamic-form-ui/ds-dynamic-form-constants'; +import { environment } from '../../../../../environments/environment'; export class Chips { chipsItems: BehaviorSubject; @@ -110,6 +111,8 @@ export class Chips { const defaultConfigIndex: number = findIndex(this.iconsConfig, {name: 'default'}); const defaultConfig: MetadataIconConfig = (defaultConfigIndex !== -1) ? this.iconsConfig[defaultConfigIndex] : undefined; + const iconsVisibleWithNoAuthority = environment.submission.icons.iconsVisibleWithNoAuthority ?? []; + let config: MetadataIconConfig; let configIndex: number; let value: any; @@ -125,7 +128,7 @@ export class Chips { if (hasValue(value) && isNotEmpty(config) && !this.hasPlaceholder(value)) { let icon: ChipsItemIcon; - const visibleWhenAuthorityEmpty = this.displayObj !== metadata; + const visibleWhenAuthorityEmpty = this.displayObj !== metadata || (iconsVisibleWithNoAuthority.includes(config.style)); // Set icon icon = { diff --git a/src/app/shared/form/form.component.ts b/src/app/shared/form/form.component.ts index c8a006a2818..566c0afa9bd 100644 --- a/src/app/shared/form/form.component.ts +++ b/src/app/shared/form/form.component.ts @@ -23,6 +23,15 @@ import cloneDeep from 'lodash/cloneDeep'; import { DynamicScrollableDropdownModel } from './builder/ds-dynamic-form-ui/models/scrollable-dropdown/dynamic-scrollable-dropdown.model'; +import isEqual from 'lodash/isEqual'; +import { DynamicRowGroupModel } from './builder/ds-dynamic-form-ui/models/ds-dynamic-row-group-model'; +import { + DynamicRelationGroupModel +} from './builder/ds-dynamic-form-ui/models/relation-group/dynamic-relation-group.model'; + +export interface MetadataFields { + [key: string]: FormFieldMetadataValueObject[] +} /** * The default form component. @@ -77,7 +86,7 @@ export class FormComponent implements OnDestroy, OnInit { * An array of DynamicFormControlModel type */ @Input() formModel: DynamicFormControlModel[]; - @Input() parentFormModel: DynamicFormGroupModel | DynamicFormGroupModel[]; + @Input() parentFormModel: DynamicRowGroupModel | DynamicFormGroupModel | DynamicFormGroupModel[]; @Input() formGroup: UntypedFormGroup; @Input() formLayout = null as DynamicFormLayout; @Input() arrayButtonsStyle: string; @@ -147,7 +156,7 @@ export class FormComponent implements OnDestroy, OnInit { } private getFormGroupValidStatus() { - return this.getFormGroup().valid; + return this.getFormGroup().valid || this.getFormGroup().disabled; } /** @@ -168,8 +177,7 @@ export class FormComponent implements OnDestroy, OnInit { this.formService.initForm(this.formId, this.formModel, this.getFormGroupValidStatus()); - // TODO: take a look to the following method: - // this.keepSync(); + this.keepSync(); this.formValid = this.getFormGroupValidStatus(); @@ -260,9 +268,7 @@ export class FormComponent implements OnDestroy, OnInit { private keepSync(): void { this.subs.push(this.formService.getFormData(this.formId) .subscribe((stateFormData) => { - if (!Object.is(stateFormData, this.formGroup.value) && this.formGroup) { - this.formGroup.setValue(stateFormData); - } + this.updateMetadataValue(stateFormData); })); } @@ -283,6 +289,9 @@ export class FormComponent implements OnDestroy, OnInit { const control: FormControl = this.formBuilderService.getFormControlByModel(this.formGroup, model) as FormControl; if (control) { const changeEvent = this.formBuilderService.createDynamicFormControlEvent(control, control.parent as UntypedFormGroup, model, 'change'); + if (model instanceof DynamicRelationGroupModel) { + control.setValue(model.value); + } this.onChange(changeEvent); } }); @@ -412,4 +421,30 @@ export class FormComponent implements OnDestroy, OnInit { const control = group.controls[index] as UntypedFormControl; return { $event, context, control, group, model, type }; } + + private updateMetadataValue(metadataFields: MetadataFields): void { + const metadataKeys = hasValue(metadataFields) ? Object.keys(metadataFields) : []; + const formKeys = hasValue(this.formGroup.value) ? Object.keys(this.formGroup.value) : []; + + formKeys.forEach((key) => { + const innerObjectKeys = (Object.keys(this.formGroup.value[key] ) as any[]).map((oldKey) => oldKey.replaceAll('_', '.')); + const filteredKeys = innerObjectKeys.filter(innerKey => metadataKeys.includes(innerKey)); + const oldValue = this.formGroup.value[key]; + + if (filteredKeys.length > 0) { + filteredKeys.forEach((oldValueKey) => { + const newValue = {...oldValue}; + const formattedKey = (oldValueKey as any).replaceAll('.', '_'); + const patchValue = {}; + + newValue[formattedKey] = metadataFields[oldValueKey][0]; + patchValue[key] = newValue; + + if (!isEqual(oldValue[oldValueKey], newValue[oldValueKey])) { + this.formGroup.patchValue(patchValue); + } + }); + } + }); + } } diff --git a/src/app/shared/loading/loading.component.html b/src/app/shared/loading/loading.component.html index bb407098d78..8b120751baf 100644 --- a/src/app/shared/loading/loading.component.html +++ b/src/app/shared/loading/loading.component.html @@ -1,18 +1,28 @@ -
- -
- - - - - - - - - - -
-
-
- {{ message }} +
+ + + + + + + + + + +
+ + + + + + + + + + +
+
+ + {{ message }} +
diff --git a/src/app/shared/loading/loading.component.spec.ts b/src/app/shared/loading/loading.component.spec.ts index d564d43dc0d..9f358a6158b 100644 --- a/src/app/shared/loading/loading.component.spec.ts +++ b/src/app/shared/loading/loading.component.spec.ts @@ -35,10 +35,10 @@ describe('LoadingComponent (inline template)', () => { comp = fixture.componentInstance; // LoadingComponent test instance comp.message = 'test message'; + comp.warningMessage = 'test warning message'; + comp.errorMessage = 'test error message'; + fixture.detectChanges(); - // query for the message