Skip to content
Permalink
Browse files
feat(replace-file): Replace Uploaded Files (DEV-684) (#695)
* feat(replace-file): add support for replacing an image file

* feat(replace-file): reload UI to show new image

* chore: remove console logs

* feat(replace-file): add support for replacing an archive file

* feat(replace-file): add support for replacing an audio file

* feat(replace-file): add support for replacing a document file

* style(replace-file): add css to style warning message

* feat(replace-file): generate warning messages dynamically

* chore(mkdocs): bump version to 1.3.0 and add generated docs to gitignore

* test(replace-file): fix tests

* chore(replace-file): cleanup
  • Loading branch information
mdelez committed Apr 4, 2022
1 parent e154fa3 commit df9de8c5781e11fb2d8dfbcbcd011cbc7bbd4bc5
Show file tree
Hide file tree
Showing 21 changed files with 589 additions and 35 deletions.
@@ -48,3 +48,6 @@ docs/bin
.DS_Store
Thumbs.db
*.code-workspace

# Generated docs
/site
@@ -153,6 +153,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 { ReplaceFileFormComponent } from './workspace/resource/representation/replace-file-form/replace-file-form.component';

// translate: AoT requires an exported function for factories
export function httpLoaderFactory(httpClient: HttpClient) {
@@ -294,6 +295,7 @@ export function httpLoaderFactory(httpClient: HttpClient) {
UserMenuComponent,
UsersComponent,
UsersListComponent,
ReplaceFileFormComponent,
],
imports: [
AngularSplitModule.forRoot(),
@@ -392,6 +392,13 @@
<app-add-region-form [resourceIri]="data.id"></app-add-region-form>
</div>

<div *ngSwitchCase="'replaceFile'">
<app-dialog-header [title]="data.title" [subtitle]="data.subtitle"></app-dialog-header>
<mat-dialog-content>
<app-replace-file-form [representation]="data.representation" [propId]="data.id" (closeDialog)="dialogRef.close($event)"></app-replace-file-form>
</mat-dialog-content>
</div>

<div *ngSwitchCase="'linkResources'">
<app-dialog-header [title]="data.title" [subtitle]="'Link resources'"></app-dialog-header>
<mat-dialog-content>
@@ -23,6 +23,7 @@ export interface DialogData {
resourceClassDefinition?: string;
fullSize?: boolean;
ontoIri?: string;
representation?: string; // respresentation type (stillImage, audio, etc.)
}

export interface ConfirmationWithComment {
@@ -5,6 +5,9 @@
</mat-icon>
Click to download
</button>
<button mat-button matTooltip="Replace archive file" (click)="openReplaceFileDialog()">
<mat-icon>cloud_upload</mat-icon>
</button>
</div>
<div *ngIf="!src || !src.fileValue.fileUrl">
No valid file url found for this resource.
@@ -1,6 +1,12 @@
import { HttpClient, HttpHeaders } from '@angular/common/http';
import { Component, Input, OnInit } from '@angular/core';
import { Component, Inject, Input, OnInit } from '@angular/core';
import { MatDialog, MatDialogConfig } from '@angular/material/dialog';
import { Constants, UpdateFileValue, UpdateResource, UpdateValue, WriteValueResponse, ReadResource, ApiResponseError, KnoraApiConnection, ReadArchiveFileValue } from '@dasch-swiss/dsp-js';
import { mergeMap } from 'rxjs/operators';
import { DspApiConnectionToken } from 'src/app/main/declarations/dsp-api-tokens';
import { DialogComponent } from 'src/app/main/dialog/dialog.component';
import { ErrorHandlerService } from 'src/app/main/error/error-handler.service';
import { EmitEvent, Events, UpdatedFileEventValue, ValueOperationEventService } from '../../services/value-operation-event.service';
import { FileRepresentation } from '../file-representation';

@Component({
@@ -11,27 +17,21 @@ import { FileRepresentation } from '../file-representation';
export class ArchiveComponent implements OnInit {

@Input() src: FileRepresentation;
@Input() parentResource: ReadResource;

originalFilename: string;
temp: string;

constructor(
@Inject(DspApiConnectionToken) private _dspApiConnection: KnoraApiConnection,
private readonly _http: HttpClient,
private _errorHandler: ErrorHandlerService
private _dialog: MatDialog,
private _errorHandler: ErrorHandlerService,
private _valueOperationEventService: ValueOperationEventService
) { }

ngOnInit(): void {
const requestOptions = {
headers: new HttpHeaders({ 'Content-Type': 'application/json' }),
withCredentials: true
};

const pathToJson = this.src.fileValue.fileUrl.substring(0, this.src.fileValue.fileUrl.lastIndexOf('/')) + '/knora.json';

this._http.get(pathToJson, requestOptions).subscribe(
res => {
this.originalFilename = res['originalFilename'];
}
);
this._getOriginalFilename();
}

// https://stackoverflow.com/questions/66986983/angular-10-download-file-from-firebase-link-without-opening-into-new-tab
@@ -60,4 +60,68 @@ export class ArchiveComponent implements OnInit {
e.click();
document.body.removeChild(e);
}

openReplaceFileDialog(){
const propId = this.parentResource.properties[Constants.HasArchiveFileValue][0].id;

const dialogConfig: MatDialogConfig = {
width: '800px',
maxHeight: '80vh',
position: {
top: '112px'
},
data: { mode: 'replaceFile', title: 'Archive (zip, x-tar, gzip)', subtitle: 'Update the archive file of this resource' , representation: 'archive', id: propId },
disableClose: true
};
const dialogRef = this._dialog.open(
DialogComponent,
dialogConfig
);

dialogRef.afterClosed().subscribe((data) => {
this._replaceFile(data);
});
}

private _getOriginalFilename() {
const requestOptions = {
headers: new HttpHeaders({ 'Content-Type': 'application/json' }),
withCredentials: true
};

const pathToJson = this.src.fileValue.fileUrl.substring(0, this.src.fileValue.fileUrl.lastIndexOf('/')) + '/knora.json';

this._http.get(pathToJson, requestOptions).subscribe(
res => {
this.originalFilename = res['originalFilename'];
}
);
}

private _replaceFile(file: UpdateFileValue) {
const updateRes = new UpdateResource();
updateRes.id = this.parentResource.id;
updateRes.type = this.parentResource.type;
updateRes.property = Constants.HasArchiveFileValue;
updateRes.value = file;

this._dspApiConnection.v2.values.updateValue(updateRes as UpdateResource<UpdateValue>).pipe(
mergeMap((res: WriteValueResponse) => this._dspApiConnection.v2.values.getValue(this.parentResource.id, res.uuid))
).subscribe(
(res2: ReadResource) => {
this.src.fileValue.fileUrl = (res2.properties[Constants.HasArchiveFileValue][0] as ReadArchiveFileValue).fileUrl;
this.src.fileValue.filename = (res2.properties[Constants.HasArchiveFileValue][0] as ReadArchiveFileValue).filename;
this.src.fileValue.strval = (res2.properties[Constants.HasArchiveFileValue][0] as ReadArchiveFileValue).strval;

this._getOriginalFilename();

this._valueOperationEventService.emit(
new EmitEvent(Events.FileValueUpdated, new UpdatedFileEventValue(
res2.properties[Constants.HasArchiveFileValue][0])));
},
(error: ApiResponseError) => {
this._errorHandler.showMessage(error);
}
);
}
}
@@ -1,4 +1,13 @@
<audio controls preload="auto">
<source [src]="audio" type="audio/mpeg">
Your browser does not support the audio element.
</audio>
<div class="controls">
<div class="audio-player">
<audio id="audio" controls preload="auto">
<source [src]="audio" type="audio/mpeg">
Your browser does not support the audio element.
</audio>
</div>
<div class="upload-button">
<button mat-button matTooltip="Replace audio file" (click)="openReplaceFileDialog()">
<mat-icon>cloud_upload</mat-icon>
</button>
</div>
</div>
@@ -1,3 +1,18 @@
audio {
.controls {
width: 100%;

.audio-player,
.upload-button {
display: inline-block;
vertical-align: middle;
}

.audio-player {
width: 80%;

audio {
width: 100%;
}
}
}

@@ -1,5 +1,12 @@
import { Component, Input, OnInit } from '@angular/core';
import { Component, Inject, Input, OnInit } from '@angular/core';
import { MatDialog, MatDialogConfig } from '@angular/material/dialog';
import { DomSanitizer, SafeUrl } from '@angular/platform-browser';
import { UpdateFileValue, UpdateResource, Constants, UpdateValue, WriteValueResponse, ReadResource, ApiResponseError, KnoraApiConnection, ReadAudioFileValue } from '@dasch-swiss/dsp-js';
import { mergeMap } from 'rxjs/operators';
import { DspApiConnectionToken } from 'src/app/main/declarations/dsp-api-tokens';
import { DialogComponent } from 'src/app/main/dialog/dialog.component';
import { ErrorHandlerService } from 'src/app/main/error/error-handler.service';
import { EmitEvent, Events, UpdatedFileEventValue, ValueOperationEventService } from '../../services/value-operation-event.service';

import { FileRepresentation } from '../file-representation';

@@ -11,15 +18,75 @@ import { FileRepresentation } from '../file-representation';
export class AudioComponent implements OnInit {

@Input() src: FileRepresentation;
@Input() parentResource: ReadResource;

audio: SafeUrl;

constructor(
private _sanitizer: DomSanitizer
@Inject(DspApiConnectionToken) private _dspApiConnection: KnoraApiConnection,
private _sanitizer: DomSanitizer,
private _dialog: MatDialog,
private _errorHandler: ErrorHandlerService,
private _valueOperationEventService: ValueOperationEventService
) { }

ngOnInit(): void {
this.audio = this._sanitizer.bypassSecurityTrustUrl(this.src.fileValue.fileUrl);
}

openReplaceFileDialog(){
const propId = this.parentResource.properties[Constants.HasAudioFileValue][0].id;

const dialogConfig: MatDialogConfig = {
width: '800px',
maxHeight: '80vh',
position: {
top: '112px'
},
data: { mode: 'replaceFile', title: 'Audio', subtitle: 'Update the audio file of this resource' , representation: 'audio', id: propId },
disableClose: true
};
const dialogRef = this._dialog.open(
DialogComponent,
dialogConfig
);

dialogRef.afterClosed().subscribe((data) => {
this._replaceFile(data);
});
}

private _replaceFile(file: UpdateFileValue) {
const updateRes = new UpdateResource();
updateRes.id = this.parentResource.id;
updateRes.type = this.parentResource.type;
updateRes.property = Constants.HasAudioFileValue;
updateRes.value = file;

this._dspApiConnection.v2.values.updateValue(updateRes as UpdateResource<UpdateValue>).pipe(
mergeMap((res: WriteValueResponse) => this._dspApiConnection.v2.values.getValue(this.parentResource.id, res.uuid))
).subscribe(
(res2: ReadResource) => {

this.src.fileValue.fileUrl = (res2.properties[Constants.HasAudioFileValue][0] as ReadAudioFileValue).fileUrl;
this.src.fileValue.filename = (res2.properties[Constants.HasAudioFileValue][0] as ReadAudioFileValue).filename;
this.src.fileValue.strval = (res2.properties[Constants.HasAudioFileValue][0] as ReadAudioFileValue).strval;
this.src.fileValue.valueCreationDate = (res2.properties[Constants.HasAudioFileValue][0] as ReadAudioFileValue).valueCreationDate;

this.audio = this._sanitizer.bypassSecurityTrustUrl(this.src.fileValue.fileUrl);

this._valueOperationEventService.emit(
new EmitEvent(Events.FileValueUpdated, new UpdatedFileEventValue(
res2.properties[Constants.HasAudioFileValue][0])));

const audioElem = document.getElementById('audio');
(audioElem as HTMLAudioElement).load();

},
(error: ApiResponseError) => {
this._errorHandler.showMessage(error);
}
);
}

}
@@ -27,6 +27,9 @@
<button mat-icon-button id="DSP_PDF_ZOOM_IN" matTooltip="Zoom in" (click)="zoomFactor = zoomFactor + 0.2">
<mat-icon>add_circle_outline</mat-icon>
</button>
<button mat-icon-button id="DSP_PDF_REPLACE_FILE" class="replace-file" matTooltip="Replace PDF file" (click)="openReplaceFileDialog()">
<mat-icon>cloud_upload</mat-icon>
</button>
<a [href]="src.fileValue.fileUrl" download mat-icon-button id="DSP_PDF_DOWNLOAD" matTooltip="Open pdf in new tab">
<mat-icon>launch</mat-icon>
</a>

0 comments on commit df9de8c

Please sign in to comment.