mirror of
https://github.com/GNS3/gns3-web-ui.git
synced 2025-01-20 03:36:46 +00:00
Option to upload appliance added
This commit is contained in:
parent
d15b4db9fe
commit
25b1edd178
@ -277,6 +277,7 @@ import { WebConsoleFullWindowComponent } from './components/web-console-full-win
|
||||
import { ConsoleGuard } from './guards/console-guard';
|
||||
import { NewTemplateDialogComponent } from './components/project-map/new-template-dialog/new-template-dialog.component';
|
||||
import { ApplianceService } from './services/appliances.service';
|
||||
import { DataSourceFilter } from './filters/dataSourceFilter';
|
||||
|
||||
if (environment.production) {
|
||||
Raven.config('https://b2b1cfd9b043491eb6b566fd8acee358@sentry.io/842726', {
|
||||
@ -390,6 +391,7 @@ if (environment.production) {
|
||||
SearchFilter,
|
||||
DateFilter,
|
||||
NameFilter,
|
||||
DataSourceFilter,
|
||||
TemplateFilter,
|
||||
ProjectsFilter,
|
||||
ListOfSnapshotsComponent,
|
||||
|
@ -11,13 +11,14 @@
|
||||
|
||||
<div>
|
||||
<button mat-button matStepperNext>Next</button>
|
||||
<button mat-button (click)="onCloseClick()">Cancel</button>
|
||||
</div>
|
||||
</mat-step>
|
||||
|
||||
<mat-step [stepControl]="secondFormGroup">
|
||||
<ng-template matStepLabel>{{actionTitle}}</ng-template>
|
||||
|
||||
<mat-card>
|
||||
<mat-card [hidden]="!(action==='install')">
|
||||
<div class="tableHeader">
|
||||
<mat-form-field class="filter-field">
|
||||
<input matInput [(ngModel)]="searchText" placeholder="Filter">
|
||||
@ -34,8 +35,9 @@
|
||||
class="mat-table"
|
||||
#table
|
||||
matSort
|
||||
multiTemplateDataRows
|
||||
(matSortChange)= "sortData($event)"
|
||||
[dataSource]="appliances | namefilter: searchText">
|
||||
[dataSource]="dataSource | datasourcefilter: searchText">
|
||||
<ng-container matColumnDef="name">
|
||||
<mat-header-cell *matHeaderCellDef mat-sort-header> Name </mat-header-cell>
|
||||
<mat-cell *matCellDef="let row"> {{row.name}} </mat-cell>
|
||||
@ -63,10 +65,43 @@
|
||||
</button>
|
||||
</mat-cell>
|
||||
</ng-container> -->
|
||||
|
||||
<!-- <ng-container matColumnDef="expandedDetail">
|
||||
<mat-cell *matCellDef="let detail">
|
||||
The symbol for {{detail.element}}
|
||||
</mat-cell>
|
||||
</ng-container> -->
|
||||
|
||||
<mat-header-row *matHeaderRowDef="displayedColumns"></mat-header-row>
|
||||
<mat-row *matRowDef="let row; columns: displayedColumns"></mat-row>
|
||||
|
||||
<!-- <mat-row
|
||||
*matRowDef="let row; columns: displayedColumns;"
|
||||
matRipple
|
||||
class="element-row"
|
||||
[class.expanded]="expandedElement == row"
|
||||
(click)="expandedElement = row">
|
||||
</mat-row>
|
||||
<mat-row
|
||||
*matRowDef="let row; columns: ['expandedDetail']; when: isExpansionDetailRow"
|
||||
[@detailExpand]="row.element == expandedElement ? 'expanded' : 'collapsed'"
|
||||
style="overflow: hidden">
|
||||
</mat-row> -->
|
||||
</mat-table>
|
||||
|
||||
<mat-paginator [pageSizeOptions]="[5, 10, 20, 50, 100]" showFirstLastButtons></mat-paginator>
|
||||
</mat-card>
|
||||
|
||||
<mat-card [hidden]="action==='install'">
|
||||
<input
|
||||
type="file"
|
||||
accept=".gns3appliance, .gns3a"
|
||||
class="non-visible"
|
||||
#file
|
||||
(change)="addAppliance($event)"
|
||||
ng2FileSelect
|
||||
[uploader]="uploader"/>
|
||||
<button mat-raised-button color="primary" (click)="file.click()">Click to import appliance</button>
|
||||
</mat-card>
|
||||
</mat-step>
|
||||
|
||||
|
@ -9,3 +9,31 @@
|
||||
.filter-field {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.example-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
max-height: 500px;
|
||||
min-width: 300px;
|
||||
}
|
||||
|
||||
.mat-table {
|
||||
overflow: auto;
|
||||
max-height: 500px;
|
||||
}
|
||||
|
||||
.element-row {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.element-row:not(.expanded) {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.element-row:not(.expanded):hover {
|
||||
background: #f5f5f5;
|
||||
}
|
||||
|
||||
.element-row.expanded {
|
||||
border-bottom-color: transparent;
|
||||
}
|
||||
|
@ -1,20 +1,32 @@
|
||||
import { Component, Input, OnInit, ChangeDetectorRef } from '@angular/core';
|
||||
import { MatDialogRef, Sort } from '@angular/material';
|
||||
import { Component, Input, OnInit, ChangeDetectorRef, ViewChild } from '@angular/core';
|
||||
import { MatDialogRef, Sort, MatTableDataSource, MatPaginator } from '@angular/material';
|
||||
import { Server } from '../../../models/server';
|
||||
import { Node } from '../../../cartography/models/node';
|
||||
import { Project } from '../../../models/project';
|
||||
import { ApplianceService } from '../../../services/appliances.service';
|
||||
import { Appliance } from '../../../models/appliance';
|
||||
import { animate, state, style, transition, trigger } from '@angular/animations';
|
||||
import { FileUploader, FileItem, ParsedResponseHeaders } from 'ng2-file-upload';
|
||||
import { ToasterService } from '../../../services/toaster.service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-new-template-dialog',
|
||||
templateUrl: './new-template-dialog.component.html',
|
||||
styleUrls: ['./new-template-dialog.component.scss']
|
||||
styleUrls: ['./new-template-dialog.component.scss'],
|
||||
animations: [
|
||||
trigger('detailExpand', [
|
||||
state('collapsed', style({ height: '0px', minHeight: '0', visibility: 'hidden' })),
|
||||
state('expanded', style({ height: '*', visibility: 'visible' })),
|
||||
transition('expanded <=> collapsed', animate('225ms cubic-bezier(0.4, 0.0, 0.2, 1)')),
|
||||
]),
|
||||
],
|
||||
})
|
||||
export class NewTemplateDialogComponent implements OnInit {
|
||||
@Input() server: Server;
|
||||
@Input() project: Project;
|
||||
|
||||
uploader: FileUploader;
|
||||
|
||||
public action: string = 'install';
|
||||
public actionTitle: string = 'Install appliance from server';
|
||||
|
||||
@ -26,10 +38,15 @@ export class NewTemplateDialogComponent implements OnInit {
|
||||
public category: string = 'all categories';
|
||||
public displayedColumns: string[] = ['name', 'emulator', 'vendor'];
|
||||
|
||||
public dataSource: MatTableDataSource<Appliance>;
|
||||
|
||||
@ViewChild(MatPaginator, {static: true}) paginator: MatPaginator;
|
||||
|
||||
constructor(
|
||||
public dialogRef: MatDialogRef<NewTemplateDialogComponent>,
|
||||
private applianceService: ApplianceService,
|
||||
private changeDetector: ChangeDetectorRef
|
||||
private changeDetector: ChangeDetectorRef,
|
||||
private toasterService: ToasterService
|
||||
) {}
|
||||
|
||||
ngOnInit() {
|
||||
@ -42,7 +59,49 @@ export class NewTemplateDialogComponent implements OnInit {
|
||||
if (appliance.qemu) appliance.emulator = 'Qemu';
|
||||
});
|
||||
this.allAppliances = appliances;
|
||||
this.dataSource = new MatTableDataSource(this.allAppliances);
|
||||
this.dataSource.paginator = this.paginator;
|
||||
});
|
||||
|
||||
this.uploader = new FileUploader({});
|
||||
this.uploader.onAfterAddingFile = file => {
|
||||
file.withCredentials = false;
|
||||
};
|
||||
|
||||
this.uploader.onErrorItem = (item: FileItem, response: string, status: number, headers: ParsedResponseHeaders) => {
|
||||
this.toasterService.error('An error has occured');
|
||||
};
|
||||
|
||||
this.uploader.onSuccessItem = (item: FileItem, response: string, status: number, headers: ParsedResponseHeaders) => {
|
||||
this.toasterService.success('Appliance imported succesfully');
|
||||
};
|
||||
}
|
||||
|
||||
addAppliance(event): void {
|
||||
let name = event.target.files[0].name.split('-')[0];
|
||||
let fileName = event.target.files[0].name;
|
||||
let file = event.target.files[0];
|
||||
let fileReader: FileReader = new FileReader();
|
||||
let emulator;
|
||||
|
||||
fileReader.onloadend = () => {
|
||||
let appliance = JSON.parse(fileReader.result as string);
|
||||
|
||||
if (appliance.docker) emulator = 'docker';
|
||||
if (appliance.dynamips) emulator = 'dynamips';
|
||||
if (appliance.iou) emulator = 'iou';
|
||||
if (appliance.qemu) emulator = 'qemu';
|
||||
|
||||
const url = this.applianceService.getUploadPath(this.server, emulator, fileName);
|
||||
this.uploader.queue.forEach(elem => (elem.url = url));
|
||||
|
||||
const itemToUpload = this.uploader.queue[0];
|
||||
(itemToUpload as any).options.disableMultipart = true;
|
||||
|
||||
this.uploader.uploadItem(itemToUpload);
|
||||
};
|
||||
|
||||
fileReader.readAsText(file);
|
||||
}
|
||||
|
||||
filterAppliances(event) {
|
||||
@ -55,6 +114,9 @@ export class NewTemplateDialogComponent implements OnInit {
|
||||
} else {
|
||||
this.appliances = temporaryAppliances.filter(t => t.category === this.category);
|
||||
}
|
||||
|
||||
this.dataSource = new MatTableDataSource(this.appliances);
|
||||
this.dataSource.paginator = this.paginator;
|
||||
}
|
||||
|
||||
setAction(action: string) {
|
||||
|
17
src/app/filters/dataSourceFilter.ts
Normal file
17
src/app/filters/dataSourceFilter.ts
Normal file
@ -0,0 +1,17 @@
|
||||
import { Pipe, PipeTransform } from '@angular/core';
|
||||
|
||||
|
||||
@Pipe({
|
||||
name: 'datasourcefilter'
|
||||
})
|
||||
export class DataSourceFilter implements PipeTransform {
|
||||
transform(items: any, searchText: string): any[] {
|
||||
if(!items) return [];
|
||||
if(!searchText) return items;
|
||||
|
||||
searchText = searchText.toLowerCase();
|
||||
return items.filteredData.filter( item => {
|
||||
return item.name.toLowerCase().includes(searchText);
|
||||
});
|
||||
}
|
||||
}
|
@ -23,10 +23,12 @@ import {
|
||||
MatTabsModule,
|
||||
MatTreeModule,
|
||||
MatBottomSheetModule,
|
||||
MatChipsModule
|
||||
MatChipsModule,
|
||||
MatPaginatorModule
|
||||
} from '@angular/material';
|
||||
|
||||
export const MATERIAL_IMPORTS = [
|
||||
MatPaginatorModule,
|
||||
MatButtonModule,
|
||||
MatMenuModule,
|
||||
MatCardModule,
|
||||
|
@ -13,4 +13,8 @@ export class ApplianceService {
|
||||
getAppliances(server: Server): Observable<Appliance[]> {
|
||||
return this.httpServer.get<Appliance[]>(server, '/appliances') as Observable<Appliance[]>;
|
||||
}
|
||||
|
||||
getUploadPath(server: Server, emulator: string, filename: string) {
|
||||
return `http://${server.host}:${server.port}/v2/${emulator}/images/${filename}`;
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user