Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: allows to download original asset (DEV-4402) #1953

Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,7 @@
<mat-icon>more_vert</mat-icon>
</button>
<mat-menu #more="matMenu" class="representation-menu">
<button
class="menu-content"
mat-menu-item
(click)="download(src.fileValue.fileUrl)"
data-cy="download-file-button">
<button class="menu-content" mat-menu-item (click)="download()" data-cy="download-file-button">
Download file
</button>
<button
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,8 +58,8 @@ export class ArchiveComponent implements OnChanges {
}
}

download(url: string) {
this._rs.downloadFile(url);
download() {
this._rs.downloadProjectFile(this.src.fileValue, this.parentResource);
}

openReplaceFileDialog() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ export class AudioMoreButtonComponent {
}

download(url: string) {
this._rs.downloadFile(url);
this._rs.downloadProjectFile(this.src.fileValue, this.parentResource);
}

private _replaceFile(file: UpdateFileValue) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
<mat-icon>more_vert</mat-icon>
</button>
<mat-menu #more="matMenu" class="representation-menu">
<button class="menu-content" mat-menu-item (click)="download(src.fileValue.fileUrl)">Download file</button>
<button class="menu-content" mat-menu-item (click)="download(src.fileValue)">Download file</button>
<button [disabled]="!usercanEdit" class="menu-content" mat-menu-item (click)="openReplaceFileDialog()">
Replace file
</button>
Expand Down Expand Up @@ -111,7 +111,7 @@
<mat-icon>more_vert</mat-icon>
</button>
<mat-menu #more="matMenu" class="mat-menu-custom-black">
<button class="menu-content" mat-menu-item (click)="download(src.fileValue.fileUrl)" [disabled]="failedToLoad">
<button class="menu-content" mat-menu-item (click)="download(src.fileValue)" [disabled]="failedToLoad">
Download file
</button>
<button [disabled]="!usercanEdit" class="menu-content" mat-menu-item (click)="openReplaceFileDialog()">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -93,8 +93,8 @@ export class DocumentComponent implements OnChanges {
}
}

download(url: string) {
this._rs.downloadFile(url);
download(fileValue: ReadDocumentFileValue) {
this._rs.downloadProjectFile(fileValue, this.parentResource);
}

openReplaceFileDialog() {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,20 @@
import { HttpClient } from '@angular/common/http';
import { HttpClient, HttpResponse } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { ReadProject, ReadResource } from '@dasch-swiss/dsp-js';
import {
ReadArchiveFileValue,
ReadAudioFileValue,
ReadDocumentFileValue,
ReadMovingImageFileValue,
ReadProject,
ReadResource,
ReadStillImageExternalFileValue,
ReadStillImageFileValue,
} from '@dasch-swiss/dsp-js';
import { ResourceUtil } from '@dasch-swiss/vre/shared/app-common';
import { AppConfigService } from '@dasch-swiss/vre/shared/app-config';
import { AppError } from '@dasch-swiss/vre/shared/app-error-handler';
import { AccessTokenService } from '@dasch-swiss/vre/shared/app-session';
import { IKeyValuePairs, ResourceSelectors } from '@dasch-swiss/vre/shared/app-state';
import { IKeyValuePairs, ResourceSelectors, UserSelectors } from '@dasch-swiss/vre/shared/app-state';
import { Store } from '@ngxs/store';
import { Observable } from 'rxjs';
import { take } from 'rxjs/operators';
Expand All @@ -30,6 +41,16 @@ export class RepresentationService {
return this._http.get<FileInfo>(url);
}

getIiifFileInfo(fileName: string, projectShort: string): Observable<FileInfo> {
const url = `${this._appConfigService.dspIiifConfig.iiifUrl}/${projectShort}/${fileName}/knora.json`;
return this._http.get<FileInfo>(url);
}

getIngestFileUrl(projectShort: string, assetId: string): string {
const url = `${this._appConfigService.dspIngestConfig.url}/projects/${projectShort}/assets/${assetId}`;
return url;
}

getAttachedProject(parentResource: ReadResource): ReadProject | undefined {
const attachedProjects = this._store.selectSnapshot(ResourceSelectors.attachedProjects);
return this.getParentResourceAttachedProject(attachedProjects, parentResource);
Expand All @@ -41,24 +62,61 @@ export class RepresentationService {
: undefined;
}

downloadFile(url: string, fileName?: string, withCredentials = true) {
userCanView(fileValue: ReadDocumentFileValue) {
return fileValue && ResourceUtil.userCanView(fileValue);
}

downloadProjectFile(
fileValue:
| ReadAudioFileValue
| ReadDocumentFileValue
| ReadMovingImageFileValue
| ReadStillImageFileValue
| ReadStillImageExternalFileValue
| ReadArchiveFileValue,
resource: ReadResource
) {
const attachedProject = this.getParentResourceAttachedProject(
this._store.selectSnapshot(ResourceSelectors.attachedProjects),
resource
)!;
if (!attachedProject) {
throw new AppError('Project is not present');
}

const assetId = fileValue.filename.split('.')[0] || '';
const ingestFileUrl = this.getIngestFileUrl(attachedProject.shortcode, assetId);
this.downloadFile(ingestFileUrl, this.userCanView(fileValue));
}

private downloadFile(url: string, userCanView = true) {
let headers = {};
const isLoggedIn = this._store.selectSnapshot(UserSelectors.isLoggedIn);
const withCredentials = isLoggedIn && userCanView;
if (withCredentials) {
const authToken = this._accessTokenService.getAccessToken();
headers = { Authorization: `Bearer ${authToken}` };
headers = {
Authorization: `Bearer ${authToken}`,
};
}

this._http
.get(url, {
.get(userCanView ? `${url}/original` : url, {
responseType: 'blob',
withCredentials,
headers,
observe: 'response',
})
.pipe(take(1))
.subscribe(res => {
.subscribe((res: HttpResponse<Blob>) => {
const contentDisposition = res.headers.get('content-disposition');
const fileNameRegex = /filename[^;=\n]*=((['"]).*?\2|[^;\n]*)/;
const matches = fileNameRegex.exec(contentDisposition || '');
const fileName =
contentDisposition && matches != null && matches[1] ? matches[1].replace(/['"]/g, '') : url.split('/').pop()!;
const a = document.createElement('a');
a.href = window.URL.createObjectURL(res);
a.download = fileName || url.split('/').pop()!;
a.href = window.URL.createObjectURL(res.body!);
a.download = fileName;
a.click();
window.URL.revokeObjectURL(a.href);
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import { DomSanitizer } from '@angular/platform-browser';
import {
Constants,
KnoraApiConnection,
ReadProject,
ReadResource,
ReadStillImageExternalFileValue,
ReadStillImageFileValue,
Expand All @@ -17,8 +16,6 @@ import { DspApiConnectionToken, DspDialogConfig } from '@dasch-swiss/vre/shared/
import { AppError } from '@dasch-swiss/vre/shared/app-error-handler';
import { ProjectService } from '@dasch-swiss/vre/shared/app-helper-services';
import { NotificationService } from '@dasch-swiss/vre/shared/app-notification';
import { UserSelectors } from '@dasch-swiss/vre/shared/app-state';
import { Store } from '@ngxs/store';
import { filter, switchMap } from 'rxjs/operators';
import { EditThirdPartyIiifFormComponent } from '../edit-third-party-iiif-form/edit-third-party-iiif-form.component';
import { ThirdPartyIiifProps } from '../edit-third-party-iiif-form/edit-third-party-iiif-types';
Expand Down Expand Up @@ -51,7 +48,6 @@ export class StillImageToolbarComponent {
@Input({ required: true }) resource!: ReadResource;
@Input({ required: true }) compoundMode!: boolean;
@Input({ required: true }) isPng!: boolean;
@Input() attachedProject: ReadProject | undefined;
@Output() imageIsPng = new EventEmitter<boolean>();

get imageFileValue() {
Expand Down Expand Up @@ -82,7 +78,6 @@ export class StillImageToolbarComponent {
public notification: NotificationService,
@Inject(DspApiConnectionToken)
private _dspApiConnection: KnoraApiConnection,
private _store: Store,
public resourceFetcherService: ResourceFetcherService,
private _rs: RepresentationService,
private _dialog: MatDialog,
Expand All @@ -98,21 +93,7 @@ export class StillImageToolbarComponent {
}

download() {
const projectShort = this.attachedProject?.shortcode;
const assetId = this.imageFileValue.filename.split('.')[0] || '';

if (!projectShort) {
throw new AppError('Error with project shortcode');
}

const isLoggedIn = this._store.selectSnapshot(UserSelectors.isLoggedIn);
if (isLoggedIn && this.userCanView) {
this._rs.getIngestFileInfo(projectShort, assetId).subscribe(response => {
this._rs.downloadFile(this.imageFileValue.fileUrl, response.originalFilename);
});
} else {
this._rs.downloadFile(this.imageFileValue.fileUrl, this.imageFileValue.filename, false);
}
this._rs.downloadProjectFile(this.imageFileValue, this.resource);
}

replaceImage() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,6 @@ import { StillImageHelper } from './still-image-helper';
<app-still-image-toolbar
*ngIf="isViewInitialized"
[resource]="resource"
[attachedProject]="attachedProject$ | async"
[compoundMode]="compoundMode"
[isPng]="isPng"
(imageIsPng)="afterFormatChange($event)" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import { MatDialog } from '@angular/material/dialog';
import {
Constants,
KnoraApiConnection,
ReadProject,
ReadResource,
ReadTextFileValue,
UpdateFileValue,
Expand All @@ -13,6 +12,8 @@ import {
} from '@dasch-swiss/dsp-js';
import { ResourceUtil } from '@dasch-swiss/vre/shared/app-common';
import { DspApiConnectionToken } from '@dasch-swiss/vre/shared/app-config';
import { ResourceSelectors } from '@dasch-swiss/vre/shared/app-state';
import { Store } from '@ngxs/store';
import { mergeMap } from 'rxjs/operators';
import { FileRepresentation } from '../file-representation';
import {
Expand All @@ -28,7 +29,6 @@ import { RepresentationService } from '../representation.service';
})
export class TextComponent implements OnChanges {
@Input() src: FileRepresentation;
@Input() attachedProject: ReadProject | undefined;
@Input() parentResource: ReadResource;

originalFilename: string;
Expand All @@ -43,7 +43,8 @@ export class TextComponent implements OnChanges {
@Inject(DspApiConnectionToken)
private _dspApiConnection: KnoraApiConnection,
private _dialog: MatDialog,
private _rs: RepresentationService
private _rs: RepresentationService,
private _store: Store
) {}

ngOnChanges(): void {
Expand All @@ -58,17 +59,21 @@ export class TextComponent implements OnChanges {
}

download(url: string) {
this._rs.downloadFile(url);
this._rs.downloadProjectFile(this.src.fileValue, this.parentResource);
}

openReplaceFileDialog() {
const attachedProject = this._rs.getParentResourceAttachedProject(
this._store.selectSnapshot(ResourceSelectors.attachedProjects),
this.parentResource
)!;
this._dialog
.open<ReplaceFileDialogComponent, ReplaceFileDialogProps>(ReplaceFileDialogComponent, {
data: {
title: 'Text (csv, txt, xml)',
subtitle: 'Update the text file of this resource',
representation: Constants.HasTextFileValue,
projectUuid: this.attachedProject!.id,
projectUuid: attachedProject!.id,
propId: this.parentResource.properties[Constants.HasTextFileValue][0].id,
},
})
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import { HttpClient } from '@angular/common/http';
import { Component, Inject, Input } from '@angular/core';
import { MatDialog } from '@angular/material/dialog';
import {
Expand Down Expand Up @@ -61,8 +60,7 @@ export class VideoMoreButtonComponent {
private _dialog: MatDialog,
@Inject(DspApiConnectionToken)
private _dspApiConnection: KnoraApiConnection,
private _rs: RepresentationService,
private readonly _http: HttpClient
private _rs: RepresentationService
) {}

openVideoInNewTab(url: string) {
Expand All @@ -74,8 +72,7 @@ export class VideoMoreButtonComponent {
}

async downloadVideo(url: string) {
const res = await this._http.get(url, { responseType: 'blob', withCredentials: true }).toPromise();
this._downloadFile(res);
this._rs.downloadProjectFile(this.src.fileValue, this.parentResource);
}

openReplaceFileDialog() {
Expand Down Expand Up @@ -127,20 +124,4 @@ export class VideoMoreButtonComponent {
window.location.reload();
});
}

private _downloadFile(data: Blob) {
const url = window.URL.createObjectURL(data);
const linkElement = document.createElement('a');
linkElement.href = url;

if (this.fileInfo?.originalFilename === undefined) {
linkElement.download = url.substring(url.lastIndexOf('/') + 1);
} else {
linkElement.download = this.fileInfo.originalFilename;
}

document.body.appendChild(linkElement);
linkElement.click();
document.body.removeChild(linkElement);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,15 @@ import { Component, Input, OnChanges } from '@angular/core';
import { ReadProject } from '@dasch-swiss/dsp-js';
import {
FileRepresentation,
getFileValue,
RepresentationConstants,
RepresentationService,
getFileValue,
} from '@dasch-swiss/vre/resource-editor/representations';
import { DspResource } from '@dasch-swiss/vre/shared/app-common';
import { ProjectService } from '@dasch-swiss/vre/shared/app-helper-services';
import { ResourceSelectors, UserSelectors } from '@dasch-swiss/vre/shared/app-state';
import { Store } from '@ngxs/store';
import { combineLatest, Observable } from 'rxjs';
import { Observable, combineLatest } from 'rxjs';
import { map } from 'rxjs/operators';

@Component({
Expand Down Expand Up @@ -62,17 +62,15 @@ import { map } from 'rxjs/operators';
class="dsp-representation archive"
*ngSwitchCase="representationConstants.archive"
[src]="representationToDisplay"
[parentResource]="resource.res"
[attachedProject]="attachedProject$ | async">
[parentResource]="resource.res">
</app-archive>

<app-text
#text
class="dsp-representation text"
*ngSwitchCase="representationConstants.text"
[src]="representationToDisplay"
[parentResource]="resource.res"
[attachedProject]="attachedProject$ | async">
[parentResource]="resource.res">
</app-text>
</div>`,
})
Expand Down
Loading