version stable

anis
Viicious 6 years ago
commit 11929a80b8

@ -56,7 +56,7 @@ app.use('/home', express.static(path.join(__dirname, 'dist')));
app.use('/main/:id', express.static(path.join(__dirname, 'dist'))); app.use('/main/:id', express.static(path.join(__dirname, 'dist')));
app.use('/login', express.static(path.join(__dirname, 'dist'))); app.use('/login', express.static(path.join(__dirname, 'dist')));
app.use('/signup', express.static(path.join(__dirname, 'dist'))); app.use('/signup', express.static(path.join(__dirname, 'dist')));
app.use('/download/:id', express.static(path.join(__dirname, 'dist')));
app.use('/api', api); app.use('/api', api);
app.set('view engine', 'pug'); app.set('view engine', 'pug');

@ -35,8 +35,8 @@ var credentials = {
passphrase: 'titi' passphrase: 'titi'
}; };
//var server = https.createServer(credentials, app); var server = https.createServer(credentials, app);
var server = http.createServer(app); //var server = http.createServer(app);

@ -249,6 +249,15 @@ router.post('/getUserById', function(req, res) {
}); });
}); });
router.post('/getFileById', function(req, res) {
console.log('yaa' + req.body.id);
FileMongo.find({
_id: req.body.id
}, function (err, file) {
res.json(file);
});
});
/* Login */ /* Login */
router.post('/signin', function(req, res) { router.post('/signin', function(req, res) {
User.findOne({ User.findOne({

@ -68,56 +68,56 @@ const appRoutes: Routes = [
data: { title: 'Sign Up' } data: { title: 'Sign Up' }
}, },
{ {
path: 'download', path: 'download/:id',
component: DownloadLinkComponent , component: DownloadLinkComponent ,
data: { title: 'download' } data: { title: 'download' }
}, },
{ path: '', { path: '',
redirectTo: '/home', redirectTo: '/home',
pathMatch: 'full' pathMatch: 'full'
} }
]; ];
@NgModule({ @NgModule({
declarations: [ declarations: [
AppComponent, AppComponent,
HomeComponent, HomeComponent,
LoginComponent, LoginComponent,
SignupComponent, SignupComponent,
MainComponent, MainComponent,
BookComponent, BookComponent,
DownloadLinkComponent DownloadLinkComponent
], ],
imports: [ imports: [
BrowserModule, BrowserModule,
DropzoneModule, DropzoneModule,
VgCoreModule, VgCoreModule,
VgControlsModule, VgControlsModule,
VgOverlayPlayModule, VgOverlayPlayModule,
VgBufferingModule, VgBufferingModule,
BrowserAnimationsModule, BrowserAnimationsModule,
FormsModule, FormsModule,
HttpModule, HttpModule,
HttpClientModule, HttpClientModule,
RouterModule.forRoot( RouterModule.forRoot(
appRoutes, appRoutes,
{ enableTracing: false } // <-- debugging purposes only { enableTracing: false } // <-- debugging purposes only
), ),
ToastModule.forRoot(), ToastModule.forRoot(),
MDBBootstrapModule.forRoot(), MDBBootstrapModule.forRoot(),
MDBBootstrapModulePro.forRoot(), MDBBootstrapModulePro.forRoot(),
AgmCoreModule.forRoot({ AgmCoreModule.forRoot({
// https://developers.google.com/maps/documentation/javascript/get-api-key?hl=en#key // https://developers.google.com/maps/documentation/javascript/get-api-key?hl=en#key
apiKey: 'Your_api_key' apiKey: 'Your_api_key'
}) })
], ],
providers: [ providers: [
MDBSpinningPreloader, MDBSpinningPreloader,
{ {
provide: DROPZONE_CONFIG, provide: DROPZONE_CONFIG,
useValue: DEFAULT_DROPZONE_CONFIG useValue: DEFAULT_DROPZONE_CONFIG
}], }],
bootstrap: [AppComponent], bootstrap: [AppComponent],
schemas: [ NO_ERRORS_SCHEMA ] schemas: [ NO_ERRORS_SCHEMA ]
}) })
export class AppModule { } export class AppModule { }

@ -51,17 +51,17 @@
<div class="col-md-4 align-middle l-flex--center"> <div class="col-md-4 align-middle l-flex--center">
<!--<form class="md-form active-pink active-pink-2 mb-3">--> <!--<form class="md-form active-pink active-pink-2 mb-3">-->
<!--<div class="row">--> <!--<div class="row">-->
<!--<i class="fa fa-search" aria-hidden="true"></i>--> <!--<i class="fa fa-search" aria-hidden="true"></i>-->
<!--<input #query (keyup)="filter(query.value)" type="text" class="form-control form-control-sm ml-3 w-75" placeholder="Search File" aria-label="Search">--> <!--<input #query (keyup)="filter(query.value)" type="text" class="form-control form-control-sm ml-3 w-75" placeholder="Search File" aria-label="Search">-->
<!--<div class="list-group" *ngFor="let folder of filterAllUserFolder">--> <!--<div class="list-group" *ngFor="let folder of filterAllUserFolder">-->
<!--<a class="list-group-item list-group-item-action waves-light" (click)="openFolder(folder.path)" mdbWavesEffect>--> <!--<a class="list-group-item list-group-item-action waves-light" (click)="openFolder(folder.path)" mdbWavesEffect>-->
<!--{{folder.name}}--> <!--{{folder.name}}-->
<!--<br>--> <!--<br>-->
<!--{{folder?.path}}--> <!--{{folder?.path}}-->
<!--</a>--> <!--</a>-->
<!--</div>--> <!--</div>-->
<!--</div>--> <!--</div>-->
<!--</form>--> <!--</form>-->
<mdb-completer [label]="'Find folder'" initialValue="Find folder" (selected)="openFolder('searchStr')" name="autocomplete" [(ngModel)]="searchStr" [datasource]="dataService" [minSearchLength]="0"> <mdb-completer [label]="'Find folder'" initialValue="Find folder" (selected)="openFolder('searchStr')" name="autocomplete" [(ngModel)]="searchStr" [datasource]="dataService" [minSearchLength]="0">
</mdb-completer> </mdb-completer>
@ -345,14 +345,14 @@
<i class="fa unlock-alt" aria-hidden="true"></i> <i class="fa unlock-alt" aria-hidden="true"></i>
<i class="fa unlock" aria-hidden="true"></i> <i class="fa unlock" aria-hidden="true"></i>
<div class="md-form"> <div class="md-form">
<input value="http://localhost:3000/{{file.url}}" disabled="disabled" type="text" id="shareLink" class="form-control" mdbInputDirective> <input value="https://localhost:3000/download/{{file._id.toString()}}" disabled="disabled" type="text" id="shareLink" class="form-control" mdbInputDirective>
<label for="shareLink">Link</label> <label for="shareLink">Link</label>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
<div class="modal-footer justify-content-center"> <div class="modal-footer justify-content-center">
<a type="button" class="btn btn-secondary waves-light" (click)="copyLink(file.url)" mdbWavesEffect>Copy Link <i class="fa fa-copy ml-1" aria-hidden="true"></i></a> <a type="button" class="btn btn-secondary waves-light" (click)="copyLink(file._id.toString())" mdbWavesEffect>Copy Link <i class="fa fa-copy ml-1" aria-hidden="true"></i></a>
</div> </div>
</div> </div>
</div> </div>

@ -20,38 +20,38 @@ import { CompleterService } from '../typescripts/pro';
@Component({ @Component({
selector: 'app-book', selector: 'app-book',
templateUrl: './book.component.html', templateUrl: './book.component.html',
styleUrls: ['./book.component.css'] styleUrls: ['./book.component.css']
}) })
export class BookComponent implements OnInit, OnDestroy { export class BookComponent implements OnInit, OnDestroy {
@Input() socialIdUser: String; @Input() socialIdUser: String;
@Output() setAllUserAppFolder = new EventEmitter<String>(); @Output() setAllUserAppFolder = new EventEmitter<String>();
message: string = 'Hola Mundo!'; message: string = 'Hola Mundo!';
sizeFile:any; sizeFile:any;
@Output() messageEvent = new EventEmitter<string>(); @Output() messageEvent = new EventEmitter<string>();
currentUser: any; currentUser: any;
allUserFile: any; allUserFile: any;
allUserFolder: any; allUserFolder: any;
//socialIdUser: String; //socialIdUser: String;
filterAllUserFolder: any; filterAllUserFolder: any;
allUserAppFolder: any; allUserAppFolder: any;
allUserAppFile: any; allUserAppFile: any;
mainFolder: any; mainFolder: any;
books: any; books: any;
filesToUpload: Array<File>; filesToUpload: Array<File>;
fileChooseName: string; fileChooseName: string;
httpOptions: any; httpOptions: any;
isClickCreateFolder: boolean; isClickCreateFolder: boolean;
inputCreateFolder: String; inputCreateFolder: String;
reader: FileReader; reader: FileReader;
test: String; test: String;
previewNode:any; previewNode:any;
previewTemplate:any; previewTemplate:any;
// config: DropzoneConfigInterface; // config: DropzoneConfigInterface;
//myDropzone: Dropzone; //myDropzone: Dropzone;
droptestt:any; droptestt:any;
allItems: any; allItems: any;
@ -86,7 +86,7 @@ export class BookComponent implements OnInit, OnDestroy {
this.sizeFile = 0; this.sizeFile = 0;
} }
copyLink(val) { copyLink(val) {
let selBox = document.createElement('textarea'); let selBox = document.createElement('textarea');
@ -95,7 +95,7 @@ export class BookComponent implements OnInit, OnDestroy {
selBox.style.left = '0'; selBox.style.left = '0';
selBox.style.top = '0'; selBox.style.top = '0';
selBox.style.opacity = '0'; selBox.style.opacity = '0';
selBox.value = 'http://localhost:3000/' + val; selBox.value = 'https://localhost:3000/download/' + val;
document.body.appendChild(selBox); document.body.appendChild(selBox);
selBox.focus(); selBox.focus();
@ -162,30 +162,30 @@ export class BookComponent implements OnInit, OnDestroy {
} }
ngOnInit() { ngOnInit() {
this.httpOptions = { this.httpOptions = {
headers: new HttpHeaders({ headers: new HttpHeaders({
'Authorization': localStorage.getItem('jwtToken'), 'Authorization': localStorage.getItem('jwtToken'),
'Access-Control-Allow-Origin' : '*', 'Access-Control-Allow-Origin' : '*',
'Access-Control-Allow-Methods' : 'GET, POST, OPTIONS, PUT, PATCH, DELETE', 'Access-Control-Allow-Methods' : 'GET, POST, OPTIONS, PUT, PATCH, DELETE',
'Access-Control-Allow-Headers' : 'Origin, X-Requested-With, Content-Type, Accept' 'Access-Control-Allow-Headers' : 'Origin, X-Requested-With, Content-Type, Accept'
}), }),
pathFolder: '' pathFolder: ''
}; };
// this.httpOptions = { // this.httpOptions = {
// headers: new HttpHeaders({ 'Authorization': localStorage.getItem('jwtToken'), 'Access-Control-Allow-Origin' : '*'}), // headers: new HttpHeaders({ 'Authorization': localStorage.getItem('jwtToken'), 'Access-Control-Allow-Origin' : '*'}),
// pathFolder: '' // pathFolder: ''
// }; // };
this.http.get('/api/getCurrentUser', this.httpOptions).subscribe(user => { this.http.get('/api/getCurrentUser', this.httpOptions).subscribe(user => {
this.currentUser = user; this.currentUser = user;
this.openFolder('Home'); this.openFolder('Home');
}, err => { }, err => {
if (err.status === 401) { if (err.status === 401) {
this.router.navigate(['login']); this.router.navigate(['login']);
} }
}); });
} }
deleteFile(fileToDelete) { deleteFile(fileToDelete) {
this.http.post('/api/deleteFileMongo', fileToDelete).subscribe(file => { this.http.post('/api/deleteFileMongo', fileToDelete).subscribe(file => {
@ -214,7 +214,7 @@ export class BookComponent implements OnInit, OnDestroy {
}); });
} }
saveURLFileMongo(fileToCreateURL){ saveURLFileMongo(fileToCreateURL){
fileToCreateURL.url = '../../../public/' + fileToCreateURL._id.toString() + this.getStringExtention(fileToCreateURL); fileToCreateURL.url = '../../../public/' + fileToCreateURL._id.toString() + this.getStringExtention(fileToCreateURL);
this.http.post('/api/saveURLFileMongo', fileToCreateURL).subscribe(data => { this.http.post('/api/saveURLFileMongo', fileToCreateURL).subscribe(data => {
console.log('data : ' + data); console.log('data : ' + data);
@ -243,28 +243,27 @@ export class BookComponent implements OnInit, OnDestroy {
} }
getfiles() { getfiles() {
//var preview = document.querySelector('img'); //var preview = document.querySelector('img');
this.sizeFile = 0; this.sizeFile = 0;
var meee = this; var meee = this;
this.http.post('/api/getFileList', {mainPath : this.mainFolder.path, owner: this.currentUser.username.toString()}).subscribe(files => { this.http.post('/api/getFileList', {mainPath : this.mainFolder.path, owner: this.currentUser.username.toString()}).subscribe(files => {
if (files) { if (files) {
this.allUserFile = files; this.allUserFile = files;
for (let f of this.allUserFile) { for (let f of this.allUserFile) {
this.sizeFile += +f.taille; this.sizeFile += +f.taille;
if(f.type === ('image/jpeg') || f.type === ('image/png')){ if(f.type === ('image/jpeg') || f.type === ('image/png')){
f.data = f.url; f.data = f.url;
f.urlSafe = this.sanitizer.bypassSecurityTrustUrl(f.url); f.urlSafe = this.sanitizer.bypassSecurityTrustUrl(f.url);
}else{ }else{
f.data = f.url; f.data = f.url;
f.urlSafe = this.sanitizer.bypassSecurityTrustResourceUrl(f.url); f.urlSafe = this.sanitizer.bypassSecurityTrustResourceUrl(f.url);
} }
} }
} else { } else {
this.allUserFile = []; this.allUserFile = [];
@ -274,7 +273,7 @@ export class BookComponent implements OnInit, OnDestroy {
} }
getfolders() { getfolders() {
this.http.post('/api/getFolderList', {mainPath : this.mainFolder.path, owner: this.currentUser.username.toString()}).subscribe(folders => { this.http.post('/api/getFolderList', {mainPath : this.mainFolder.path, owner: this.currentUser.username.toString()}).subscribe(folders => {
if (folders) { if (folders) {
this.allUserFolder = folders; this.allUserFolder = folders;
} else { } else {
@ -324,110 +323,110 @@ export class BookComponent implements OnInit, OnDestroy {
}); });
} }
backToParentFolder(){ backToParentFolder(){
this.openFolder(this.mainFolder.parent); this.openFolder(this.mainFolder.parent);
} }
openFolder(path: String) { openFolder(path: String) {
if(this.searchStr !== undefined ? path === 'searchStr' : false){ if(this.searchStr !== undefined ? path === 'searchStr' : false){
path = this.searchStr; path = this.searchStr;
} }
this.httpOptions.pathFolder = path; this.httpOptions.pathFolder = path;
this.isClickCreateFolder = false; this.isClickCreateFolder = false;
this.http.post('/api/getMainFolder', {path: path, owner: this.currentUser.username.toString()}).subscribe(folder => { this.http.post('/api/getMainFolder', {path: path, owner: this.currentUser.username.toString()}).subscribe(folder => {
if (folder) { if (folder) {
this.mainFolder = folder; this.mainFolder = folder;
this.getfolders(); this.getfolders();
this.getAllUserAppFolder(); this.getAllUserAppFolder();
this.getfiles(); this.getfiles();
} else { } else {
if (path === 'Home'){ if (path === 'Home'){
this.http.post('api/createFolder', { name: 'Home', parent: '', path: 'Home', taille: 'taiile', idUser: this.currentUser._id.toString(), fileList: [], folderList: [], owner: this.currentUser.username.toString() }).subscribe(resp => { this.http.post('api/createFolder', { name: 'Home', parent: '', path: 'Home', taille: 'taiile', idUser: this.currentUser._id.toString(), fileList: [], folderList: [], owner: this.currentUser.username.toString() }).subscribe(resp => {
this.mainFolder = resp; this.mainFolder = resp;
}, err => { }, err => {
console.log('err open Folder : ' + err); console.log('err open Folder : ' + err);
}); });
} }
} }
}); });
} }
createBool() { createBool() {
this.isClickCreateFolder = true; this.isClickCreateFolder = true;
} }
createFolder() { createFolder() {
var newPath = this.mainFolder.path + '/' + this.inputCreateFolder; var newPath = this.mainFolder.path + '/' + this.inputCreateFolder;
this.isClickCreateFolder = false; this.isClickCreateFolder = false;
this.http.post('api/createFolder', { name: this.inputCreateFolder.toString(), parent: this.mainFolder.path, path: newPath.toString(), taille: 'taiile', idUser: this.currentUser._id.toString(), fileList: [], folderList: [], owner: this.currentUser.username.toString() }).subscribe(resp => { this.http.post('api/createFolder', { name: this.inputCreateFolder.toString(), parent: this.mainFolder.path, path: newPath.toString(), taille: 'taiile', idUser: this.currentUser._id.toString(), fileList: [], folderList: [], owner: this.currentUser.username.toString() }).subscribe(resp => {
this.getfolders(); this.getfolders();
this.getAllUserAppFolder(); this.getAllUserAppFolder();
}, err => { }, err => {
console.log('err create Folder : ' + err); console.log('err create Folder : ' + err);
}); });
} }
ngOnDestroy() { ngOnDestroy() {
localStorage.removeItem('jwtToken'); localStorage.removeItem('jwtToken');
this.httpOptions = null; this.httpOptions = null;
this.currentUser = null; this.currentUser = null;
} }
upload() { upload() {
var me = this; var me = this;
if(this.mainFolder !== undefined){ if(this.mainFolder !== undefined){
this.http.post('api/uploadFileMongo', { name: this.fileChooseName.toString(), path: this.mainFolder.path.toString(), type: this.filesToUpload[0].type.toString(), taille: this.filesToUpload[0].size.toString(), idUser: this.currentUser._id.toString(), owner: this.currentUser.username.toString(), lastDate: Date.now().toString(), url: ''}).subscribe(resp => { this.http.post('api/uploadFileMongo', { name: this.fileChooseName.toString(), path: this.mainFolder.path.toString(), type: this.filesToUpload[0].type.toString(), taille: this.filesToUpload[0].size.toString(), idUser: this.currentUser._id.toString(), owner: this.currentUser.username.toString(), lastDate: Date.now().toString(), url: ''}).subscribe(resp => {
me.saveURLFileMongo(resp); me.saveURLFileMongo(resp);
var mee = this; var mee = this;
//////// ////////
me.makeFileRequest('https://localhost:3000/api/upload', [], this.filesToUpload, resp).then((result) => { me.makeFileRequest('https://localhost:3000/api/upload', [], this.filesToUpload, resp).then((result) => {
mee.getfiles(); mee.getfiles();
mee.filesToUpload = null; mee.filesToUpload = null;
mee.fileChooseName = 'None'; mee.fileChooseName = 'None';
}, (error) => { }, (error) => {
console.error(error); console.error(error);
}); });
}, err => { }, err => {
console.error(err); console.error(err);
}); });
} }
} }
fileChangeEvent(fileInput: any){ fileChangeEvent(fileInput: any){
this.filesToUpload = <Array<File>> fileInput.target.files; this.filesToUpload = <Array<File>> fileInput.target.files;
this.fileChooseName = this.filesToUpload[0].name; this.fileChooseName = this.filesToUpload[0].name;
} }
makeFileRequest(url: string, params: Array<string>, files: Array<File>, monFile: any) { makeFileRequest(url: string, params: Array<string>, files: Array<File>, monFile: any) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
var formData: any = new FormData(); var formData: any = new FormData();
var xhr = new XMLHttpRequest(); var xhr = new XMLHttpRequest();
formData.append("public[]", files[0], monFile._id + this.getStringExtention(monFile)); formData.append("public[]", files[0], monFile._id + this.getStringExtention(monFile));
xhr.onreadystatechange = function () { xhr.onreadystatechange = function () {
if (xhr.readyState == 4) { if (xhr.readyState == 4) {
if (xhr.status == 200) { if (xhr.status == 200) {
resolve(JSON.parse(xhr.response)); resolve(JSON.parse(xhr.response));
} else { } else {
reject(xhr.response); reject(xhr.response);
} }
} }
}; };
xhr.open("POST", url, true); xhr.open("POST", url, true);
xhr.send(formData); xhr.send(formData);
}); });
} }
getStringExtention(monFile: any){ getStringExtention(monFile: any){

@ -3,6 +3,10 @@
<logo> <logo>
<a class="logo navbar-brand waves-light" mdbWavesEffect href="#"><strong>SupFile</strong></a> <a class="logo navbar-brand waves-light" mdbWavesEffect href="#"><strong>SupFile</strong></a>
</logo> </logo>
<links>
<ul class="navbar-nav mr-auto">
</ul>
</links>
</mdb-navbar> </mdb-navbar>
<section class="view intro-2 mask rgba-gradient"> <section class="view intro-2 mask rgba-gradient">
@ -14,11 +18,11 @@
<div class="card text-white card-cascade narrower card-image"> <div class="card text-white card-cascade narrower card-image">
<div class="text-white text-center align-items-center"> <div class="text-white text-center align-items-center">
<!--Card image--> <!--Card image-->
<div class="card-header text-center"> <div class="card-header text-center">
<h2 class="h2-responsive">File Download</h2> <h2 class="h2-responsive">File Download</h2>
</div> </div>
<!--/Card image--> <!--/Card image-->
<!--Card content--> <!--Card content-->
@ -29,16 +33,15 @@
<i class="fa fa-file fa-4x" aria-hidden="true"></i> <i class="fa fa-file fa-4x" aria-hidden="true"></i>
</div> </div>
<div class="col-md-6"> <div class="col-md-6">
<p><strong>Name</strong></p> <p><strong>{{file?.name}}</strong></p>
<p>Lenght</p> <p>{{ file?.taille / ( 1024 * 1024 ) | number : '1.2-2'}} Mo</p>
</div> </div>
</div> </div>
</div> </div>
<hr class="hr-light mb-3 mt-4"> <hr class="hr-light mb-3 mt-4">
<button class="btn btn-outline-white wow fadeInleft waves-light">Download</button> <button class="btn btn-outline-white wow fadeInleft waves-light"><a class="text-white" href="{{file?.url}}" download="{{ file?.name.toString()}}">Download</a></button>
</div> </div>
<!--/.Card content--> <!--/.Card content-->
</div> </div>

@ -1,15 +1,40 @@
import { Component, OnInit } from '@angular/core'; import { Component, OnInit } from '@angular/core';
import { Router, ActivatedRoute } from '@angular/router';
import {HttpClient} from '@angular/common/http';
@Component({ @Component({
selector: 'app-download-link', selector: 'app-download-link',
templateUrl: './download-link.component.html', templateUrl: './download-link.component.html',
styleUrls: ['./download-link.component.scss'] styleUrls: ['./download-link.component.scss']
}) })
export class DownloadLinkComponent implements OnInit { export class DownloadLinkComponent implements OnInit {
idFile: String;
urlFile: String;
file: any;
constructor(private http: HttpClient, private router: Router, private route: ActivatedRoute) {
this.idFile = '';
this.urlFile = '';
this.route.params.subscribe(params => {
this.idFile = params['id'];
this.urlFile = 'http://localhost:3000/' + this.idFile;
console.log('this.idFile : ' + this.idFile );
this.http.post('/api/getFileById', {id: this.idFile.toString()}).subscribe(file => {
if (file) {
this.file = file[0];
console.log(this.file)
} else {
this.file = [];
}
});
constructor() { } });
}
ngOnInit() { ngOnInit() {
} }
} }

@ -73,8 +73,8 @@ export class HomeComponent implements OnInit {
ngOnInit() { ngOnInit() {
setTimeout(() => setTimeout(() =>
{ {
this.showCookies();); this.showCookies();
}, 1000); }, 1000); ;
} }
} }

@ -13,7 +13,7 @@
<!-- Logo --> <!-- Logo -->
<li> <li>
<div class="logo-wrapper flex-center waves-light" style="padding-bottom: 5%"> <div class="logo-wrapper flex-center waves-light" style="padding-bottom: 5%">
<a href="#"><img style="height: 90px; margin-left: 13%;"src="../../assets/logo%20supfile.png" class="img-fluid flex-center"></a> <a href="#"><img style="height: 90px; margin-left: 13%;"src="../../assets/logoSupfile.png" class="img-fluid flex-center"></a>
</div> </div>
</li> </li>
<!--/. Logo --> <!--/. Logo -->

Loading…
Cancel
Save