From 9bb63d71234fc49f2afcb959603b8bcd4deb4429 Mon Sep 17 00:00:00 2001 From: mdelez <60604010+mdelez@users.noreply.github.com> Date: Mon, 6 Dec 2021 15:37:44 +0100 Subject: [PATCH] feat(representation): add support for uploading and viewing archive files (DEV-18) (#600) * feat(archive): adds support for uploading and viewing archive files * test(archive): adds test to archive comp * test(archive): fixes import --- package-lock.json | 28 +++-- package.json | 4 +- src/app/app.module.ts | 2 + .../default-data/default-resource-classes.ts | 9 ++ .../properties/properties.component.html | 1 + .../archive/archive.component.html | 12 ++ .../archive/archive.component.scss | 0 .../archive/archive.component.spec.ts | 110 ++++++++++++++++++ .../archive/archive.component.ts | 41 +++++++ .../representation/file-representation.ts | 6 +- .../representation/upload/upload.component.ts | 22 +++- .../resource-instance-form.component.ts | 11 +- .../select-properties.component.ts | 2 +- .../resource/resource.component.html | 2 + .../workspace/resource/resource.component.ts | 12 +- ...ch-select-resource-class.component.spec.ts | 6 +- src/assets/style/_elements.scss | 3 + 17 files changed, 248 insertions(+), 23 deletions(-) create mode 100644 src/app/workspace/resource/representation/archive/archive.component.html create mode 100644 src/app/workspace/resource/representation/archive/archive.component.scss create mode 100644 src/app/workspace/resource/representation/archive/archive.component.spec.ts create mode 100644 src/app/workspace/resource/representation/archive/archive.component.ts diff --git a/package-lock.json b/package-lock.json index 13fee6deac..f36c70d791 100644 --- a/package-lock.json +++ b/package-lock.json @@ -19,7 +19,7 @@ "@angular/platform-browser-dynamic": "^12.2.13", "@angular/router": "^12.2.13", "@ckeditor/ckeditor5-angular": "^2.0.2", - "@dasch-swiss/dsp-js": "^5.0.0", + "@dasch-swiss/dsp-js": "^5.1.0", "@datadog/browser-rum": "^3.7.0", "@ngx-translate/core": "^12.1.2", "@ngx-translate/http-loader": "5.0.0", @@ -79,6 +79,20 @@ "typescript": "4.3.5" } }, + ".yalc/@dasch-swiss/dsp-js": { + "version": "5.0.0+77be05e7", + "extraneous": true, + "license": "AGPL-3.0", + "dependencies": { + "@babel/helper-compilation-targets": "^7.14.5", + "@types/jsonld": "^1.5.0", + "json2typescript": "1.4.1", + "jsonld": "^5.2.0" + }, + "peerDependencies": { + "rxjs": "6.x" + } + }, "node_modules/@ampproject/remapping": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-1.0.1.tgz", @@ -2459,9 +2473,9 @@ } }, "node_modules/@dasch-swiss/dsp-js": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/@dasch-swiss/dsp-js/-/dsp-js-5.0.0.tgz", - "integrity": "sha512-TLX5l4H3tf60aMiIedurB7ypeo8acIv45ZaZs01OpdUXJTvxzGA4HRblZ6tZ1HSWOnKgD/j55lTunKZFZuTQ7g==", + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@dasch-swiss/dsp-js/-/dsp-js-5.1.0.tgz", + "integrity": "sha512-YdoUCwJM4x3NQ2tzA0hnsKorzNrvuZcLAAPyqitO3esNlv2DyfD8CH5Rl6e9qTcDBeGat6tV8wiJFr3IN74Q3g==", "dependencies": { "@babel/helper-compilation-targets": "^7.14.5", "@types/jsonld": "^1.5.0", @@ -22373,9 +22387,9 @@ "dev": true }, "@dasch-swiss/dsp-js": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/@dasch-swiss/dsp-js/-/dsp-js-5.0.0.tgz", - "integrity": "sha512-TLX5l4H3tf60aMiIedurB7ypeo8acIv45ZaZs01OpdUXJTvxzGA4HRblZ6tZ1HSWOnKgD/j55lTunKZFZuTQ7g==", + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@dasch-swiss/dsp-js/-/dsp-js-5.1.0.tgz", + "integrity": "sha512-YdoUCwJM4x3NQ2tzA0hnsKorzNrvuZcLAAPyqitO3esNlv2DyfD8CH5Rl6e9qTcDBeGat6tV8wiJFr3IN74Q3g==", "requires": { "@babel/helper-compilation-targets": "^7.14.5", "@types/jsonld": "^1.5.0", diff --git a/package.json b/package.json index 749a3d9c90..eec776acd2 100644 --- a/package.json +++ b/package.json @@ -18,7 +18,7 @@ "test-local": "ng test", "test-e2e-protractor": "ng e2e --configuration production --protractor-config=./e2e/protractor-ci.conf.js --webdriver-update=false", "webdriver-update": "webdriver-manager update --standalone false --gecko false --versions.chrome 2.37", - "yalc-add-lib": "rm -rf .yalc/@dasch-swiss && yalc add @dasch-swiss/dsp-js && yalc add @dasch-swiss/dsp-ui && npm install" + "yalc-add-lib": "rm -rf .yalc/@dasch-swiss && yalc add @dasch-swiss/dsp-js && npm install" }, "private": true, "dependencies": { @@ -33,7 +33,7 @@ "@angular/platform-browser-dynamic": "^12.2.13", "@angular/router": "^12.2.13", "@ckeditor/ckeditor5-angular": "^2.0.2", - "@dasch-swiss/dsp-js": "^5.0.0", + "@dasch-swiss/dsp-js": "^5.1.0", "@datadog/browser-rum": "^3.7.0", "@ngx-translate/core": "^12.1.2", "@ngx-translate/http-loader": "5.0.0", diff --git a/src/app/app.module.ts b/src/app/app.module.ts index c39984c330..6f12e33c57 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -166,6 +166,7 @@ import { SearchSelectOntologyComponent } from './workspace/search/advanced-searc import { ExpertSearchComponent } from './workspace/search/expert-search/expert-search.component'; import { FulltextSearchComponent } from './workspace/search/fulltext-search/fulltext-search.component'; import { SearchPanelComponent } from './workspace/search/search-panel/search-panel.component'; +import { ArchiveComponent } from './workspace/resource/representation/archive/archive.component'; // translate: AoT requires an exported function for factories export function httpLoaderFactory(httpClient: HttpClient) { @@ -320,6 +321,7 @@ export function httpLoaderFactory(httpClient: HttpClient) { UsersListComponent, VisualizerComponent, YetAnotherDateValueComponent, + ArchiveComponent, ], imports: [ AngularSplitModule.forRoot(), diff --git a/src/app/project/ontology/default-data/default-resource-classes.ts b/src/app/project/ontology/default-data/default-resource-classes.ts index bc9e1be295..070fc5a059 100644 --- a/src/app/project/ontology/default-data/default-resource-classes.ts +++ b/src/app/project/ontology/default-data/default-resource-classes.ts @@ -140,6 +140,15 @@ export class DefaultResourceClasses { 'drafts', 'library_books' ] + }, + { + iri: Constants.KnoraApiV2 + Constants.HashDelimiter + 'ArchiveRepresentation', + label: 'Archive (zip, x-tar, gzip)', + icons: [ + 'archive', + 'folder', + 'folder_open' + ] } ]; } diff --git a/src/app/workspace/resource/properties/properties.component.html b/src/app/workspace/resource/properties/properties.component.html index 9739e24758..7091619343 100644 --- a/src/app/workspace/resource/properties/properties.component.html +++ b/src/app/workspace/resource/properties/properties.component.html @@ -101,6 +101,7 @@

prop.propDef.objectType !== representationConstants.audio && prop.propDef.objectType !== representationConstants.document && prop.propDef.objectType !== representationConstants.text && + prop.propDef.objectType !== representationConstants.archive && !(isAnnotation && ( prop.propDef.subjectType === representationConstants.region && prop.propDef.objectType !== representationConstants.color diff --git a/src/app/workspace/resource/representation/archive/archive.component.html b/src/app/workspace/resource/representation/archive/archive.component.html new file mode 100644 index 0000000000..90244b746c --- /dev/null +++ b/src/app/workspace/resource/representation/archive/archive.component.html @@ -0,0 +1,12 @@ +
+ +
+
+ No valid file url found for this resource. +
+ diff --git a/src/app/workspace/resource/representation/archive/archive.component.scss b/src/app/workspace/resource/representation/archive/archive.component.scss new file mode 100644 index 0000000000..e69de29bb2 diff --git a/src/app/workspace/resource/representation/archive/archive.component.spec.ts b/src/app/workspace/resource/representation/archive/archive.component.spec.ts new file mode 100644 index 0000000000..5b895bf416 --- /dev/null +++ b/src/app/workspace/resource/representation/archive/archive.component.spec.ts @@ -0,0 +1,110 @@ +import { KnoraApiConnection } from '@dasch-swiss/dsp-js'; +import { HarnessLoader } from '@angular/cdk/testing'; +import { TestbedHarnessEnvironment } from '@angular/cdk/testing/testbed'; +import { HttpClientTestingModule } from '@angular/common/http/testing'; +import { Component, OnInit, ViewChild } from '@angular/core'; +import { ComponentFixture, TestBed } from '@angular/core/testing'; +import { MatButtonHarness } from '@angular/material/button/testing'; +import { MatDialogModule } from '@angular/material/dialog'; +import { MatSnackBarModule } from '@angular/material/snack-bar'; +import { AppInitService } from 'src/app/app-init.service'; +import { DspApiConfigToken, DspApiConnectionToken } from 'src/app/main/declarations/dsp-api-tokens'; +import { TestConfig } from 'test.config'; +import { FileRepresentation } from '../file-representation'; + +import { ArchiveComponent } from './archive.component'; + +const archiveFileValue = { + 'arkUrl': 'http://0.0.0.0:3336/ark:/72163/1/0123/6c=f69h6Ss6GXPME565EqAS/dDHcFHlwQ9K46255QfUGrQ8', + 'attachedToUser': 'http://rdfh.ch/users/root', + 'fileUrl': 'http://0.0.0.0:1024/0123/Eu71soNXOAL-DVweVgODkFh.zip/file', + 'filename': 'Eu71soNXOAL-DVweVgODkFh.zip', + 'hasPermissions': 'CR knora-admin:ProjectAdmin|D knora-admin:ProjectAdmin|M knora-admin:ProjectAdmin|V knora-admin:ProjectAdmin|RV knora-admin:ProjectAdmin', + 'id': 'http://rdfh.ch/0123/6c-f69h6Ss6GXPME565EqA/values/dDHcFHlwQ9K46255QfUGrQ', + 'property': 'http://api.knora.org/ontology/knora-api/v2#hasArchiveFileValue', + 'propertyComment': 'Connects a Representation to a zip archive', + 'propertyLabel': 'hat Zip', + 'strval': 'http://0.0.0.0:1024/0123/Eu71soNXOAL-DVweVgODkFh.zip/file', + 'type': 'http://api.knora.org/ontology/knora-api/v2#ArchiveFileValue', + 'userHasPermission': 'CR', + 'uuid': 'dDHcFHlwQ9K46255QfUGrQ', + 'valueCreationDate': '2021-12-03T09:59:46.609839Z', + 'valueHasComment': undefined, + 'versionArkUrl': 'http://0.0.0.0:3336/ark:/72163/1/0123/6c=f69h6Ss6GXPME565EqAS/dDHcFHlwQ9K46255QfUGrQ8.20211203T095946609839Z' +}; + +@Component({ + template: ` + + ` +}) +class TestHostComponent implements OnInit { + + @ViewChild(ArchiveComponent) archiveComp: ArchiveComponent; + + archiveFileRepresentation: FileRepresentation; + + ngOnInit() { + + this.archiveFileRepresentation = new FileRepresentation(archiveFileValue); + } +} + +describe('ArchiveComponent', () => { + let testHostComponent: TestHostComponent; + let testHostFixture: ComponentFixture; + let loader: HarnessLoader; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ + ArchiveComponent, + TestHostComponent + ], + imports: [ + HttpClientTestingModule, + MatDialogModule, + MatSnackBarModule + ], + providers: [ + AppInitService, + { + provide: DspApiConfigToken, + useValue: TestConfig.ApiConfig + }, + { + provide: DspApiConnectionToken, + useValue: new KnoraApiConnection(TestConfig.ApiConfig) + } + ] + }) + .compileComponents(); + }); + + beforeEach(() => { + testHostFixture = TestBed.createComponent(TestHostComponent); + testHostComponent = testHostFixture.componentInstance; + loader = TestbedHarnessEnvironment.loader(testHostFixture); + testHostFixture.detectChanges(); + expect(testHostComponent).toBeTruthy(); + }); + + it('should have a file url', () => { + expect(testHostComponent.archiveFileRepresentation.fileValue.fileUrl).toEqual('http://0.0.0.0:1024/0123/Eu71soNXOAL-DVweVgODkFh.zip/file'); + }); + + it('should show a download button if the file url is provided', async () => { + const downloadButtonElement = await loader.getHarness(MatButtonHarness.with({ selector: '.download' })); + + expect(downloadButtonElement).toBeTruthy(); + }); + + it('should NOT show a download button if the file url is NOT provided', async () => { + testHostComponent.archiveFileRepresentation = undefined; + testHostFixture.detectChanges(); + + const downloadButtonElement = await loader.getAllHarnesses(MatButtonHarness.with({ selector: '.download' })); + + expect(downloadButtonElement.length).toEqual(0); + }); +}); diff --git a/src/app/workspace/resource/representation/archive/archive.component.ts b/src/app/workspace/resource/representation/archive/archive.component.ts new file mode 100644 index 0000000000..477f0edc96 --- /dev/null +++ b/src/app/workspace/resource/representation/archive/archive.component.ts @@ -0,0 +1,41 @@ +import { HttpClient } from '@angular/common/http'; +import { Component, Input, OnInit } from '@angular/core'; +import { ErrorHandlerService } from 'src/app/main/error/error-handler.service'; +import { FileRepresentation } from '../file-representation'; + +@Component({ + selector: 'app-archive', + templateUrl: './archive.component.html', + styleUrls: ['./archive.component.scss'] +}) +export class ArchiveComponent implements OnInit { + + @Input() src: FileRepresentation; + + constructor( + private readonly _http: HttpClient, + private _errorHandler: ErrorHandlerService + ) { } + + ngOnInit(): void { } + + // https://stackoverflow.com/questions/66986983/angular-10-download-file-from-firebase-link-without-opening-into-new-tab + async downloadArchive(url: string) { + try { + const res = await this._http.get(url, { responseType: 'blob' }).toPromise(); + this.downloadFile(res); + } catch (e) { + this._errorHandler.showMessage(e); + } + } + + downloadFile(data) { + const url = window.URL.createObjectURL(data); + const e = document.createElement('a'); + e.href = url; + e.download = url.substr(url.lastIndexOf('/') + 1); + document.body.appendChild(e); + e.click(); + document.body.removeChild(e); + } +} diff --git a/src/app/workspace/resource/representation/file-representation.ts b/src/app/workspace/resource/representation/file-representation.ts index 89c2461abf..ae90f35773 100644 --- a/src/app/workspace/resource/representation/file-representation.ts +++ b/src/app/workspace/resource/representation/file-representation.ts @@ -1,5 +1,6 @@ import { Constants, + ReadArchiveFileValue, ReadAudioFileValue, ReadDocumentFileValue, ReadMovingImageFileValue, @@ -14,11 +15,11 @@ export class FileRepresentation { /** * - * @param fileValue a [[ReadAudioFileValue | ReadDocumentFileValue | ReadMovingImageFileValue | ReadStillImageFileValue]] representing a file value + * @param fileValue a [[ReadAudioFileValue | ReadDocumentFileValue | ReadMovingImageFileValue | ReadStillImageFileValue | ReadArchiveFileValue]] representing a file value * @param annotations[] an array of [[Region]] --> TODO: will be expanded with [[Sequence]] */ constructor( - readonly fileValue: ReadAudioFileValue | ReadDocumentFileValue | ReadMovingImageFileValue | ReadStillImageFileValue, + readonly fileValue: ReadAudioFileValue | ReadDocumentFileValue | ReadMovingImageFileValue | ReadStillImageFileValue | ReadArchiveFileValue, readonly annotations?: Region[] ) { @@ -31,6 +32,7 @@ export class RepresentationConstants { static document = Constants.DocumentFileValue; static movingImage = Constants.MovingImageFileValue; static stillImage = Constants.StillImageFileValue; + static archive = Constants.ArchiveFileValue; static text = Constants.TextFileValue; static region = Constants.Region; static color = Constants.ColorValue; diff --git a/src/app/workspace/resource/representation/upload/upload.component.ts b/src/app/workspace/resource/representation/upload/upload.component.ts index c5bb370f6b..ef20e430e9 100644 --- a/src/app/workspace/resource/representation/upload/upload.component.ts +++ b/src/app/workspace/resource/representation/upload/upload.component.ts @@ -1,10 +1,12 @@ import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core'; import { FormBuilder, FormControl, FormGroup, Validators } from '@angular/forms'; import { + CreateArchiveFileValue, CreateAudioFileValue, CreateDocumentFileValue, CreateFileValue, CreateStillImageFileValue, + UpdateArchiveFileValue, UpdateAudioFileValue, UpdateDocumentFileValue, UpdateFileValue, @@ -26,8 +28,7 @@ export class UploadComponent implements OnInit { @Input() parentForm?: FormGroup; - @Input() representation: 'stillImage' | 'movingImage' | 'audio' | 'document' | 'text'; - // only StillImageRepresentation and DocumentPresentation is supported so far + @Input() representation: 'stillImage' | 'movingImage' | 'audio' | 'document' | 'text' | 'archive'; @Input() formName: string; @@ -44,6 +45,7 @@ export class UploadComponent implements OnInit { supportedImageTypes = ['image/jpeg', 'image/jp2', 'image/tiff', 'image/tiff-fx', 'image/png']; supportedDocumentTypes = ['application/pdf']; supportedAudioTypes = ['audio/mpeg']; + supportedArchiveTypes = ['application/zip', 'application/x-tar', 'application/gzip']; // readonly fromLabels = { // upload: 'Upload file', @@ -209,7 +211,7 @@ export class UploadComponent implements OnInit { const filename = this.fileControl.value.internalFilename; - let fileValue: CreateStillImageFileValue | CreateDocumentFileValue; + let fileValue: CreateStillImageFileValue | CreateDocumentFileValue | CreateAudioFileValue | CreateArchiveFileValue; switch (this.representation) { case 'stillImage': @@ -224,6 +226,10 @@ export class UploadComponent implements OnInit { fileValue = new CreateAudioFileValue(); break; + case 'archive': + fileValue = new CreateArchiveFileValue(); + break; + default: // --> TODO for UPLOAD: expand with other representation file types break; @@ -248,7 +254,7 @@ export class UploadComponent implements OnInit { const filename = this.fileControl.value.internalFilename; - let fileValue: UpdateStillImageFileValue | UpdateDocumentFileValue | UpdateAudioFileValue; + let fileValue: UpdateStillImageFileValue | UpdateDocumentFileValue | UpdateAudioFileValue | UpdateArchiveFileValue; switch (this.representation) { @@ -264,6 +270,10 @@ export class UploadComponent implements OnInit { fileValue = new UpdateAudioFileValue(); break; + case 'archive': + fileValue = new UpdateArchiveFileValue(); + break; + default: // --> TODO for UPLOAD: expand with other representation file types break; @@ -303,6 +313,10 @@ export class UploadComponent implements OnInit { this.allowedFileTypes = this.supportedAudioTypes; break; + case 'archive': + this.allowedFileTypes = this.supportedArchiveTypes; + break; + default: this.allowedFileTypes = []; break; diff --git a/src/app/workspace/resource/resource-instance-form/resource-instance-form.component.ts b/src/app/workspace/resource/resource-instance-form/resource-instance-form.component.ts index c057f24797..74a9d6a874 100644 --- a/src/app/workspace/resource/resource-instance-form/resource-instance-form.component.ts +++ b/src/app/workspace/resource/resource-instance-form/resource-instance-form.component.ts @@ -73,7 +73,7 @@ export class ResourceInstanceFormComponent implements OnInit, OnDestroy { defaultClasses: DefaultClass[] = DefaultResourceClasses.data; // selected resource class has a file value property: display the corresponding upload form - hasFileValue: 'stillImage' | 'movingImage' | 'audio' | 'document' | 'text'; + hasFileValue: 'stillImage' | 'movingImage' | 'audio' | 'document' | 'text' | 'archive'; fileValue: CreateFileValue; @@ -193,6 +193,8 @@ export class ResourceInstanceFormComponent implements OnInit, OnDestroy { case 'audio': this.propertiesObj[Constants.HasAudioFileValue] = [this.fileValue]; break; + case 'archive': + this.propertiesObj[Constants.HasArchiveFileValue] = [this.fileValue]; } } @@ -363,7 +365,8 @@ export class ResourceInstanceFormComponent implements OnInit, OnDestroy { prop.isEditable && prop.id !== Constants.HasStillImageFileValue && prop.id !== Constants.HasDocumentFileValue && - prop.id !== Constants.HasAudioFileValue // --> TODO for UPLOAD: expand with other representation file values + prop.id !== Constants.HasAudioFileValue && + prop.id !== Constants.HasArchiveFileValue // --> TODO for UPLOAD: expand with other representation file values ); if (onto.properties[Constants.HasStillImageFileValue]) { @@ -372,12 +375,14 @@ export class ResourceInstanceFormComponent implements OnInit, OnDestroy { this.hasFileValue = 'document'; } else if (onto.properties[Constants.HasAudioFileValue]) { this.hasFileValue = 'audio'; + } else if (onto.properties[Constants.HasArchiveFileValue]) { + this.hasFileValue = 'archive'; } else { this.hasFileValue = undefined; } // notifies the user that the selected resource does not have any properties defined yet. - if (!this.selectPropertiesComponent && this.properties.length === 0) { + if (!this.selectPropertiesComponent && this.properties.length === 0 && !this.hasFileValue) { this.errorMessage = 'No properties defined for the selected resource.'; } diff --git a/src/app/workspace/resource/resource-instance-form/select-properties/select-properties.component.ts b/src/app/workspace/resource/resource-instance-form/select-properties/select-properties.component.ts index 455ff0b175..70313b0b1d 100644 --- a/src/app/workspace/resource/resource-instance-form/select-properties/select-properties.component.ts +++ b/src/app/workspace/resource/resource-instance-form/select-properties/select-properties.component.ts @@ -135,7 +135,7 @@ export class SelectPropertiesComponent implements OnInit { // update the filtered version of the corresponding property values array. // used in the template to calculate if the delete button should be shown. - // e.i don't show the delete button if there is only one value + // i.e. don't show the delete button if there is only one value this.propertyValuesKeyValuePair[prop.id + '-filtered'] = this._filterValueArray(this.propertyValuesKeyValuePair[prop.id]); } diff --git a/src/app/workspace/resource/resource.component.html b/src/app/workspace/resource/resource.component.html index 1c158b4343..9c6713debf 100644 --- a/src/app/workspace/resource/resource.component.html +++ b/src/app/workspace/resource/resource.component.html @@ -22,6 +22,8 @@ + + The file representation type "{{representationsToDisplay[0].fileValue.type}}" is not yet implemented diff --git a/src/app/workspace/resource/resource.component.ts b/src/app/workspace/resource/resource.component.ts index 5e8da74cd1..a6a0c430a5 100644 --- a/src/app/workspace/resource/resource.component.ts +++ b/src/app/workspace/resource/resource.component.ts @@ -14,6 +14,7 @@ import { Constants, CountQueryResponse, IHasPropertyWithPropertyDefinition, KnoraApiConnection, + ReadArchiveFileValue, ReadAudioFileValue, ReadDocumentFileValue, ReadResource, ReadResourceSequence, @@ -411,6 +412,12 @@ export class ResourceComponent implements OnInit, OnChanges, OnDestroy { const fileValue: ReadAudioFileValue = resource.res.properties[Constants.HasAudioFileValue][0] as ReadAudioFileValue; const audio = new FileRepresentation(fileValue); representations.push(audio); + + } else if (resource.res.properties[Constants.HasArchiveFileValue]) { + + const fileValue: ReadArchiveFileValue = resource.res.properties[Constants.HasArchiveFileValue][0] as ReadArchiveFileValue; + const archive = new FileRepresentation(fileValue); + representations.push(archive); } this.representationsToDisplay = representations; @@ -465,7 +472,10 @@ export class ResourceComponent implements OnInit, OnChanges, OnDestroy { } // request incoming regions --> TODO: add case to get incoming sequences in case of video and audio - if (resource.res.properties[Constants.HasStillImageFileValue] || resource.res.properties[Constants.HasDocumentFileValue] || resource.res.properties[Constants.HasAudioFileValue]) { + if (resource.res.properties[Constants.HasStillImageFileValue] || + resource.res.properties[Constants.HasDocumentFileValue] || + resource.res.properties[Constants.HasAudioFileValue] || + resource.res.properties[Constants.HasArchiveFileValue]) { // --> TODO: check if resources is a StillImageRepresentation using the ontology responder (support for subclass relations required) // the resource is a StillImageRepresentation, check if there are regions pointing to it diff --git a/src/app/workspace/search/advanced-search/resource-and-property-selection/search-select-resource-class/search-select-resource-class.component.spec.ts b/src/app/workspace/search/advanced-search/resource-and-property-selection/search-select-resource-class/search-select-resource-class.component.spec.ts index 4c04d70596..5a59992ab1 100644 --- a/src/app/workspace/search/advanced-search/resource-and-property-selection/search-select-resource-class/search-select-resource-class.component.spec.ts +++ b/src/app/workspace/search/advanced-search/resource-and-property-selection/search-select-resource-class/search-select-resource-class.component.spec.ts @@ -46,7 +46,7 @@ class TestHostComponent implements OnInit { } -describe('SelectResourceClassComponent', () => { +describe('SearchSelectResourceClassComponent', () => { let testHostComponent: TestHostComponent; let testHostFixture: ComponentFixture; @@ -173,7 +173,7 @@ describe('SelectResourceClassComponent', () => { testHostFixture.detectChanges(); - expect(testHostComponent.selectResourceClass.resourceClassDefinitions.length).toEqual(12); + expect(testHostComponent.selectResourceClass.resourceClassDefinitions.length).toEqual(13); const select = await loader.getHarness(MatSelectHarness); const initVal = await select.getValueText(); @@ -185,7 +185,7 @@ describe('SelectResourceClassComponent', () => { const options = await select.getOptions(); - expect(options.length).toEqual(13); + expect(options.length).toEqual(14); expect(testHostComponent.selectResourceClass.selectedResourceClassIri).toBe(false); diff --git a/src/assets/style/_elements.scss b/src/assets/style/_elements.scss index c84394c567..eb22b2f3dc 100644 --- a/src/assets/style/_elements.scss +++ b/src/assets/style/_elements.scss @@ -296,6 +296,9 @@ a, &.audio { height: auto; } + &.archive { + height: auto; + } .caption { padding: 10px 16px; white-space: nowrap;