Attachments fully working
This commit is contained in:
parent
5748f762e9
commit
a109c9d0a5
@ -35,6 +35,7 @@ export const ROUTES: Routes = [
|
||||
{
|
||||
path: 'files/:hash',
|
||||
component: FileViewerComponent,
|
||||
canActivate: [ AuthGuard ]
|
||||
}
|
||||
];
|
||||
|
||||
|
@ -123,6 +123,14 @@ export class BoardService {
|
||||
);
|
||||
}
|
||||
|
||||
addComment(comment: Comment): Observable<ApiResponse> {
|
||||
return this.http.post('api/comments', comment)
|
||||
.pipe(
|
||||
map((response: ApiResponse) => response),
|
||||
catchError((err) => of(err.error as ApiResponse))
|
||||
);
|
||||
}
|
||||
|
||||
updateComment(comment: Comment): Observable<ApiResponse> {
|
||||
return this.http.post('api/comments/' + comment.id, comment)
|
||||
.pipe(
|
||||
@ -139,8 +147,7 @@ export class BoardService {
|
||||
);
|
||||
}
|
||||
|
||||
/* istanbul ignore next */
|
||||
uploadAttachment(attachment: Attachment): Observable<ApiResponse> {
|
||||
addAttachment(attachment: Attachment): Observable<ApiResponse> {
|
||||
return this.http.post('api/attachments', attachment)
|
||||
.pipe(
|
||||
map((response: ApiResponse) => response),
|
||||
@ -148,6 +155,14 @@ export class BoardService {
|
||||
);
|
||||
}
|
||||
|
||||
uploadAttachment(data: FormData, hash: string): Observable<ApiResponse> {
|
||||
return this.http.post('api/upload/' + hash, data)
|
||||
.pipe(
|
||||
map((response: ApiResponse) => response),
|
||||
catchError((err) => of(err.error as ApiResponse))
|
||||
);
|
||||
}
|
||||
|
||||
removeAttachment(id: number): Observable<ApiResponse> {
|
||||
return this.http.delete('api/attachments/' + id)
|
||||
.pipe(
|
||||
|
@ -178,8 +178,11 @@
|
||||
<i class="icon icon-eye" (click)="viewFile(item.diskfilename)"
|
||||
[title]="strings['boards_taskView'] + ' ' + item.filename"></i>
|
||||
|
||||
<i class="icon icon-download" [title]="strings['boards_taskDownload']
|
||||
+ ' ' + item.filename"></i>
|
||||
<a [href]="getUrl(item.diskfilename)" download="{{ item.filename }}">
|
||||
<i class="icon icon-download"
|
||||
[title]="strings['boards_taskDownload'] + ' ' +
|
||||
item.filename"></i>
|
||||
</a>
|
||||
|
||||
<i class="icon icon-trash-empty"
|
||||
[title]="strings['settings_remove'] + ' ' + item.filename"
|
||||
@ -191,12 +194,14 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div class="file-upload">
|
||||
<h3>{{ strings['boards_taskAddAttachment'] }}</h3>
|
||||
|
||||
<input type="file" #fileupload (change)="fileChange(fileupload.files[0])">
|
||||
<input type="file" class="fileuploadinput" #fileupload
|
||||
(change)="fileChange(fileupload.files[0])">
|
||||
|
||||
<button (click)="uploadFile()" [disabled]="!fileupload.files[0]">
|
||||
<button (click)="addFile()"
|
||||
[disabled]="!fileupload.files[0] || fileUploading">
|
||||
<i class="icon icon-upload"></i>
|
||||
|
||||
{{ strings['boards_taskUpload'] }}
|
||||
|
@ -7,7 +7,7 @@ import {
|
||||
OnDestroy,
|
||||
Output
|
||||
} from '@angular/core';
|
||||
import { DomSanitizer, } from '@angular/platform-browser';
|
||||
import { DomSanitizer } from '@angular/platform-browser';
|
||||
import {
|
||||
CdkDragDrop,
|
||||
moveItemInArray,
|
||||
@ -40,12 +40,15 @@ import { BoardService } from '../board.service';
|
||||
templateUrl: './column.component.html'
|
||||
})
|
||||
export class ColumnDisplayComponent implements OnInit, OnDestroy {
|
||||
public moveItemInArray: any;
|
||||
public transferArrayItem: any;
|
||||
public fileUpload: any;
|
||||
|
||||
private fileUpload: any;
|
||||
private subs = [];
|
||||
|
||||
public viewTaskActivities: ActivitySimple[];
|
||||
|
||||
public fileUploading: boolean;
|
||||
public showActivity: boolean;
|
||||
public collapseActivity: boolean;
|
||||
public isOverdue: boolean;
|
||||
@ -88,7 +91,7 @@ export class ColumnDisplayComponent implements OnInit, OnDestroy {
|
||||
|
||||
constructor(public elRef: ElementRef,
|
||||
private auth: AuthService,
|
||||
private notes: NotificationsService,
|
||||
public notes: NotificationsService,
|
||||
public modal: ModalService,
|
||||
public stringsService: StringsService,
|
||||
public boardService: BoardService,
|
||||
@ -107,6 +110,9 @@ export class ColumnDisplayComponent implements OnInit, OnDestroy {
|
||||
this.modalProps = new Task();
|
||||
this.viewModalProps = new Task();
|
||||
|
||||
this.moveItemInArray = moveItemInArray;
|
||||
this.transferArrayItem = transferArrayItem;
|
||||
|
||||
let sub = stringsService.stringsChanged.subscribe(newStrings => {
|
||||
this.strings = newStrings;
|
||||
});
|
||||
@ -231,9 +237,7 @@ export class ColumnDisplayComponent implements OnInit, OnDestroy {
|
||||
return;
|
||||
}
|
||||
|
||||
this.modal.close(this.MODAL_ID + (this.columnData
|
||||
? this.columnData.id + ''
|
||||
: ''));
|
||||
this.modal.close(this.MODAL_ID + this.columnData.id + '');
|
||||
|
||||
const boardData = response.data[2][0];
|
||||
boardData.ownColumn.forEach((column: any) => {
|
||||
@ -252,10 +256,10 @@ export class ColumnDisplayComponent implements OnInit, OnDestroy {
|
||||
|
||||
drop(event: CdkDragDrop<string[]>, colIndex: number) {
|
||||
if (event.previousContainer === event.container) {
|
||||
moveItemInArray(event.container.data,
|
||||
this.moveItemInArray(event.container.data,
|
||||
event.previousIndex, event.currentIndex);
|
||||
} else {
|
||||
transferArrayItem(event.previousContainer.data,
|
||||
this.transferArrayItem(event.previousContainer.data,
|
||||
event.container.data, event.previousIndex, event.currentIndex);
|
||||
}
|
||||
|
||||
@ -286,9 +290,7 @@ export class ColumnDisplayComponent implements OnInit, OnDestroy {
|
||||
}
|
||||
|
||||
this.boardService.updateActiveBoard(response.data[2][0]);
|
||||
this.modal.close(this.MODAL_ID + (this.columnData
|
||||
? this.columnData.id + ''
|
||||
: ''));
|
||||
this.modal.close(this.MODAL_ID + this.columnData?.id + '');
|
||||
|
||||
this.boardService.refreshToken();
|
||||
this.saving = false;
|
||||
@ -309,60 +311,63 @@ export class ColumnDisplayComponent implements OnInit, OnDestroy {
|
||||
});
|
||||
}
|
||||
|
||||
/* istanbul ignore next */
|
||||
fileChange(file: File) {
|
||||
this.fileUpload = file;
|
||||
}
|
||||
|
||||
/* istanbul ignore next */
|
||||
uploadFile() {
|
||||
addFile() {
|
||||
if (!this.fileUpload) {
|
||||
this.notes.add({ type: 'error', text: this.strings.boards_taskNoFileError });
|
||||
this.notes
|
||||
.add({ type: 'error', text: this.strings.boards_taskNoFileError });
|
||||
return;
|
||||
}
|
||||
|
||||
const fileReader = new FileReader();
|
||||
fileReader.onload = () => {
|
||||
const attachment = new Attachment();
|
||||
this.fileUploading = true;
|
||||
const attachment = new Attachment();
|
||||
|
||||
attachment.filename = this.fileUpload.name;
|
||||
attachment.name = attachment.filename.split('.').slice(0, -1).join('.');
|
||||
attachment.type = this.fileUpload.type;
|
||||
attachment.user_id = this.activeUser.id;
|
||||
attachment.task_id = this.viewModalProps.id;
|
||||
attachment.data = fileReader.result;
|
||||
attachment.filename = this.fileUpload.name;
|
||||
attachment.name = attachment.filename.split('.').slice(0, -1).join('.');
|
||||
attachment.type = this.fileUpload.type;
|
||||
attachment.user_id = this.activeUser.id;
|
||||
attachment.task_id = this.viewModalProps.id;
|
||||
|
||||
this.boardService.uploadAttachment(attachment)
|
||||
.subscribe(response => {
|
||||
response.alerts.forEach(note => this.notes.add(note));
|
||||
this.boardService.addAttachment(attachment).subscribe(response => {
|
||||
if (response.status !== 'success') {
|
||||
this.fileUploading = false;
|
||||
this.resetFileInput();
|
||||
|
||||
if (response.status === 'success') {
|
||||
attachment.id = response.data[1].id;
|
||||
attachment.diskfilename = response.data[1].diskfilename;
|
||||
return;
|
||||
}
|
||||
|
||||
this.viewModalProps.attachments.push(attachment);
|
||||
}
|
||||
});
|
||||
}
|
||||
attachment.id = response.data[1].id;
|
||||
attachment.diskfilename = response.data[1].diskfilename;
|
||||
|
||||
fileReader.readAsBinaryString(this.fileUpload);
|
||||
this.uploadFile(attachment, response);
|
||||
});
|
||||
}
|
||||
|
||||
viewFile(hash: string) {
|
||||
window.open(`./files/${hash}`, 'tb-file-view');
|
||||
}
|
||||
|
||||
getUrl(hash: string) {
|
||||
const url = `./api/uploads/${hash}`;
|
||||
return this.sanitizer.bypassSecurityTrustResourceUrl(url);
|
||||
}
|
||||
|
||||
removeAttachment() {
|
||||
this.boardService.removeAttachment(this.attachmentToRemove.id).subscribe(res => {
|
||||
res.alerts.forEach(note => this.notes.add(note));
|
||||
this.boardService.removeAttachment(this.attachmentToRemove.id)
|
||||
.subscribe(res => {
|
||||
res.alerts.forEach(note => this.notes.add(note));
|
||||
|
||||
if (res.status === 'success') {
|
||||
const index = this.viewModalProps.attachments
|
||||
.findIndex(x => x.id === this.attachmentToRemove.id);
|
||||
if (res.status === 'success') {
|
||||
const index = this.viewModalProps.attachments
|
||||
.findIndex(x => x.id === this.attachmentToRemove.id);
|
||||
|
||||
this.viewModalProps.attachments.splice(index, 1);
|
||||
}
|
||||
});
|
||||
this.viewModalProps.attachments.splice(index, 1);
|
||||
this.updateTaskActivity(this.viewModalProps.id);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
addComment() {
|
||||
@ -370,14 +375,15 @@ export class ColumnDisplayComponent implements OnInit, OnDestroy {
|
||||
return;
|
||||
}
|
||||
|
||||
this.viewModalProps.comments.push(
|
||||
new Comment(0, this.newComment, this.activeUser.id,
|
||||
this.viewModalProps.id));
|
||||
const comment = new Comment(0, this.newComment, this.activeUser.id,
|
||||
this.viewModalProps.id);
|
||||
|
||||
this.newComment = '';
|
||||
|
||||
this.boardService.updateTask(this.viewModalProps)
|
||||
this.boardService.addComment(comment)
|
||||
.subscribe((response: ApiResponse) => {
|
||||
response.alerts.forEach(note => this.notes.add(note));
|
||||
|
||||
if (response.status !== 'success') {
|
||||
return;
|
||||
}
|
||||
@ -582,7 +588,8 @@ export class ColumnDisplayComponent implements OnInit, OnDestroy {
|
||||
}
|
||||
|
||||
getUserName(userId: number) {
|
||||
const user = this.activeBoard.users.find((test: User) => test.id === +userId);
|
||||
const user = this.activeBoard.users
|
||||
.find((test: User) => test.id === +userId);
|
||||
|
||||
return user.username;
|
||||
}
|
||||
@ -614,6 +621,34 @@ export class ColumnDisplayComponent implements OnInit, OnDestroy {
|
||||
}
|
||||
}
|
||||
|
||||
private uploadFile(attachment: Attachment, response: ApiResponse) {
|
||||
const data = new FormData();
|
||||
data.append('file', this.fileUpload);
|
||||
|
||||
this.boardService.uploadAttachment(data, attachment.diskfilename)
|
||||
.subscribe(res => {
|
||||
res.alerts.forEach(note => this.notes.add(note));
|
||||
|
||||
this.fileUploading = false;
|
||||
this.resetFileInput();
|
||||
|
||||
if (res.status === 'success') {
|
||||
response.alerts.forEach(note => this.notes.add(note));
|
||||
|
||||
this.viewModalProps.attachments.push(attachment);
|
||||
this.updateTaskActivity(this.viewModalProps.id);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private resetFileInput() {
|
||||
const upload = document.getElementsByClassName('fileuploadinput');
|
||||
|
||||
Array.from(upload).forEach((input: any) => {
|
||||
input.value = '';
|
||||
})
|
||||
}
|
||||
|
||||
private updateTaskActivity(id: number) {
|
||||
this.viewTaskActivities = [];
|
||||
|
||||
|
@ -1 +1,17 @@
|
||||
<tb-top-nav page-name="{{ pageName }}"></tb-top-nav>
|
||||
<tb-top-nav page-name="{{ pageName }}" [show-buttons]="false"></tb-top-nav>
|
||||
|
||||
<div class="file-viewer">
|
||||
<div class="header">
|
||||
{{ strings['attachment'] }}: {{ attachment?.filename }}
|
||||
|
||||
<span class="right">
|
||||
<a [href]="fileUrl" download="{{ attachment?.filename }}">
|
||||
{{strings['boards_taskDownload']}}
|
||||
</a>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div class="content" *ngIf="isLoaded">
|
||||
<iframe seamless [src]="fileUrl"></iframe>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -1,42 +1,78 @@
|
||||
import { Component, OnDestroy, OnInit } from '@angular/core';
|
||||
import { Title } from '@angular/platform-browser';
|
||||
import { ActivatedRoute } from '@angular/router';
|
||||
import {
|
||||
Title,
|
||||
DomSanitizer,
|
||||
SafeResourceUrl
|
||||
} from '@angular/platform-browser';
|
||||
|
||||
import { StringsService, AuthService } from '../shared/services';
|
||||
import { User } from '../shared/models';
|
||||
import { FileViewerService } from './file-viewer.service';
|
||||
import { User, Attachment } from '../shared/models';
|
||||
import {
|
||||
StringsService,
|
||||
AuthService,
|
||||
NotificationsService
|
||||
} from '../shared/services';
|
||||
|
||||
@Component({
|
||||
selector: 'tb-file-viewer',
|
||||
templateUrl: './file-viewer.component.html'
|
||||
})
|
||||
export class FileViewerComponent implements OnInit, OnDestroy {
|
||||
export class FileViewerComponent implements OnInit, OnDestroy {
|
||||
private subs: any[];
|
||||
private fileHash: string;
|
||||
|
||||
public activeUser: User;
|
||||
public pageName: string;
|
||||
public strings: any;
|
||||
public pageName: string;
|
||||
public isLoaded: boolean;
|
||||
public fileUrl: SafeResourceUrl;
|
||||
|
||||
constructor(public title: Title,
|
||||
public auth: AuthService,
|
||||
public stringsService: StringsService) {
|
||||
public attachment: Attachment;
|
||||
public activeUser: User;
|
||||
|
||||
constructor(private title: Title,
|
||||
private active: ActivatedRoute,
|
||||
private sanitizer: DomSanitizer,
|
||||
public service: FileViewerService,
|
||||
private notes: NotificationsService,
|
||||
private auth: AuthService,
|
||||
private stringsService: StringsService) {
|
||||
title.setTitle('TaskBoard - File Viewer');
|
||||
this.isLoaded = false;
|
||||
this.subs = [];
|
||||
|
||||
let sub = stringsService.stringsChanged.subscribe(newStrings => {
|
||||
let sub = this.stringsService.stringsChanged.subscribe(newStrings => {
|
||||
this.strings = newStrings;
|
||||
|
||||
this.pageName = this.strings.files;
|
||||
this.title.setTitle(`TaskBoard - ${this.pageName}`);
|
||||
});
|
||||
this.subs.push(sub);
|
||||
|
||||
sub = auth.userChanged.subscribe((user: User) => {
|
||||
sub = this.auth.userChanged.subscribe((user: User) => {
|
||||
this.activeUser = user;
|
||||
});
|
||||
this.subs.push(sub);
|
||||
|
||||
sub = this.active.params.subscribe(params => {
|
||||
this.fileHash = params.hash;
|
||||
});
|
||||
this.subs.push(sub);
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
this.pageName = this.strings.files;
|
||||
this.service.getAttachmentInfo(this.fileHash).subscribe(res => {
|
||||
res.alerts.forEach(note => this.notes.add(note));
|
||||
|
||||
console.log(this.stringsService, this.activeUser, this.pageName);
|
||||
if (res.status === 'success') {
|
||||
this.attachment = res.data[1];
|
||||
|
||||
const url = `./api/uploads/${this.attachment.diskfilename}`;
|
||||
this.fileUrl = this.sanitizer.bypassSecurityTrustResourceUrl(url);
|
||||
|
||||
this.isLoaded = true;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
|
@ -5,6 +5,7 @@ import { RouterModule } from '@angular/router';
|
||||
import { SharedModule } from '../shared/shared.module';
|
||||
|
||||
import { FileViewerComponent } from './file-viewer.component';
|
||||
import { FileViewerService } from './file-viewer.service';
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
@ -15,6 +16,9 @@ import { FileViewerComponent } from './file-viewer.component';
|
||||
declarations: [
|
||||
FileViewerComponent
|
||||
],
|
||||
providers: [
|
||||
FileViewerService
|
||||
],
|
||||
exports: [
|
||||
FileViewerComponent
|
||||
]
|
||||
|
22
src/app/files/file-viewer.service.ts
Normal file
22
src/app/files/file-viewer.service.ts
Normal file
@ -0,0 +1,22 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { HttpClient } from '@angular/common/http';
|
||||
|
||||
import { Observable, of } from 'rxjs';
|
||||
import { map, catchError } from 'rxjs/operators';
|
||||
|
||||
import { ApiResponse } from '../shared/models';
|
||||
|
||||
@Injectable()
|
||||
export class FileViewerService {
|
||||
|
||||
constructor(private http: HttpClient) {}
|
||||
|
||||
getAttachmentInfo(hash: string): Observable<ApiResponse> {
|
||||
return this.http.get('api/attachments/hash/' + hash)
|
||||
.pipe(
|
||||
map((response: ApiResponse) => response),
|
||||
catchError((err) => of(err.error as ApiResponse))
|
||||
);
|
||||
}
|
||||
|
||||
}
|
Reference in New Issue
Block a user