mirror of
https://github.com/GNS3/gns3-web-ui.git
synced 2025-04-09 03:24:13 +00:00
Merge branch 'master' into fix-for-tests
This commit is contained in:
commit
9dfd58b14b
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "gns3-web-ui",
|
||||
"version": "2020.3.0-beta.1",
|
||||
"version": "2020.3.0-beta.2",
|
||||
"author": {
|
||||
"name": "GNS3 Technology Inc.",
|
||||
"email": "developers@gns3.com"
|
||||
@ -51,10 +51,10 @@
|
||||
"@angular/platform-browser": "^10.0.2",
|
||||
"@angular/platform-browser-dynamic": "^10.0.2",
|
||||
"@angular/router": "^10.0.2",
|
||||
"@sentry/browser": "^5.18.0",
|
||||
"@types/jest": "^26.0.3",
|
||||
"@types/mocha": "^7.0.2",
|
||||
"angular-draggable-droppable": "^4.5.1",
|
||||
"@sentry/browser": "^5.18.0",
|
||||
"angular-persistence": "^1.0.1",
|
||||
"angular-resizable-element": "^3.3.2",
|
||||
"angular2-draggable": "^2.3.2",
|
||||
@ -79,6 +79,7 @@
|
||||
"rxjs-compat": "^6.5.5",
|
||||
"save-html-as-image": "^1.3.3",
|
||||
"save-svg-as-png": "^1.4.14",
|
||||
"snyk": "^1.361.3",
|
||||
"svg-crowbar": "^0.6.0",
|
||||
"tree-kill": "^1.2.1",
|
||||
"tslib": "^2.0.0",
|
||||
@ -87,8 +88,7 @@
|
||||
"xterm-addon-attach": "^0.6.0",
|
||||
"xterm-addon-fit": "^0.4.0",
|
||||
"yargs": "^15.3.1",
|
||||
"zone.js": "~0.10.3",
|
||||
"snyk": "^1.361.3"
|
||||
"zone.js": "~0.10.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@angular-devkit/build-angular": "~0.1000.0",
|
||||
|
@ -159,6 +159,10 @@ const routes: Routes = [
|
||||
path: 'server/:server_id/project/:project_id/nodes/:node_id',
|
||||
component: WebConsoleFullWindowComponent
|
||||
},
|
||||
{
|
||||
path: 'static/web-ui/server/:server_id/project/:project_id/nodes/:node_id',
|
||||
component: WebConsoleFullWindowComponent
|
||||
},
|
||||
{
|
||||
path: '**',
|
||||
component: PageNotFoundComponent
|
||||
|
@ -278,6 +278,7 @@ import { DataSourceFilter } from './filters/dataSourceFilter';
|
||||
import { ChangeHostnameActionComponent } from './components/project-map/context-menu/actions/change-hostname/change-hostname-action.component';
|
||||
import { ChangeHostnameDialogComponent } from './components/project-map/change-hostname-dialog/change-hostname-dialog.component';
|
||||
import { ApplianceInfoDialogComponent } from './components/project-map/new-template-dialog/appliance-info-dialog/appliance-info-dialog.component';
|
||||
import { InformationDialogComponent } from './components/dialogs/information-dialog.component';
|
||||
|
||||
@NgModule({
|
||||
declarations: [
|
||||
@ -460,7 +461,8 @@ import { ApplianceInfoDialogComponent } from './components/project-map/new-templ
|
||||
NewTemplateDialogComponent,
|
||||
ChangeHostnameActionComponent,
|
||||
ChangeHostnameDialogComponent,
|
||||
ApplianceInfoDialogComponent
|
||||
ApplianceInfoDialogComponent,
|
||||
InformationDialogComponent
|
||||
],
|
||||
imports: [
|
||||
BrowserModule,
|
||||
@ -606,4 +608,6 @@ import { ApplianceInfoDialogComponent } from './components/project-map/new-templ
|
||||
],
|
||||
bootstrap: [AppComponent]
|
||||
})
|
||||
export class AppModule {}
|
||||
export class AppModule {
|
||||
constructor(protected _googleAnalyticsService: GoogleAnalyticsService) { }
|
||||
}
|
||||
|
@ -89,8 +89,14 @@ export class D3MapComponent implements OnInit, OnChanges, OnDestroy {
|
||||
|
||||
@Input('show-interface-labels')
|
||||
set showInterfaceLabels(value) {
|
||||
this.settings.show_interface_labels = value;
|
||||
this.interfaceLabelWidget.setEnabled(value);
|
||||
if (value && !this.mapSettingsService.integrateLinkLabelsToLinks) {
|
||||
this.settings.show_interface_labels = true;
|
||||
this.interfaceLabelWidget.setEnabled(true);
|
||||
} else {
|
||||
this.settings.show_interface_labels = false;
|
||||
this.interfaceLabelWidget.setEnabled(false);
|
||||
}
|
||||
|
||||
this.mapChangeDetectorRef.detectChanges();
|
||||
}
|
||||
|
||||
|
@ -20,6 +20,7 @@ export class MapLink implements Indexed {
|
||||
target: MapNode; // this is not from server
|
||||
|
||||
isSelected = false; // this is not from server
|
||||
isMultiplied = false; // this is not from server
|
||||
x: number; // this is not from server
|
||||
y: number; // this is not from server
|
||||
}
|
||||
|
@ -20,6 +20,22 @@ export class InterfaceStatusWidget implements Widget {
|
||||
|
||||
public draw(view: SVGSelection) {
|
||||
const self = this;
|
||||
let mapLinks: MapLink[] = [];
|
||||
|
||||
view.each(function(this: SVGGElement, l: MapLink) {
|
||||
mapLinks.push(l);
|
||||
});
|
||||
mapLinks.forEach(mapLink => {
|
||||
mapLinks.forEach(n => {
|
||||
if (n.nodes[0].linkId !== mapLink.nodes[0].linkId){
|
||||
if ((mapLink.nodes[0].nodeId === n.nodes[0].nodeId && mapLink.nodes[1].nodeId === n.nodes[1].nodeId) ||
|
||||
(mapLink.nodes[0].nodeId === n.nodes[1].nodeId && mapLink.nodes[1].nodeId === n.nodes[0].nodeId) ||
|
||||
(mapLink.nodes[1].nodeId === n.nodes[0].nodeId && mapLink.nodes[0].nodeId === n.nodes[1].nodeId)) {
|
||||
mapLink.isMultiplied = true;
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
view.each(function(this: SVGGElement, l: MapLink) {
|
||||
const link_group = select<SVGGElement, MapLink>(this);
|
||||
@ -27,10 +43,10 @@ export class InterfaceStatusWidget implements Widget {
|
||||
|
||||
let statuses = [];
|
||||
if (link_path.node()) {
|
||||
const start_point: SVGPoint = link_path.node().getPointAtLength(100);
|
||||
const end_point: SVGPoint = link_path.node().getPointAtLength(link_path.node().getTotalLength() - 100);
|
||||
const start_point: SVGPoint = link_path.node().getPointAtLength(80);
|
||||
const end_point: SVGPoint = link_path.node().getPointAtLength(link_path.node().getTotalLength() - 80);
|
||||
|
||||
if (link_path.node().getTotalLength() > 2 * 45 + 10) {
|
||||
if (link_path.node().getTotalLength() > 2 * 45 + 130) {
|
||||
if (l.source && l.target) {
|
||||
let sourcePort = l.nodes.find(node => node.nodeId === l.source.id).label.text;
|
||||
let destinationPort = l.nodes.find(node => node.nodeId === l.target.id).label.text;
|
||||
@ -62,7 +78,7 @@ export class InterfaceStatusWidget implements Widget {
|
||||
link_group
|
||||
.selectAll<SVGTextElement, LinkStatus>('text.status_suspended_label').remove();
|
||||
|
||||
if (self.mapSettingsService.integrateLinkLabelsToLinks) {
|
||||
if (self.mapSettingsService.showInterfaceLabels && self.mapSettingsService.integrateLinkLabelsToLinks && !l.isMultiplied) {
|
||||
const status_started = link_group
|
||||
.selectAll<SVGRectElement, LinkStatus>('rect.status_started')
|
||||
.data(statuses.filter((link_status: LinkStatus) => link_status.status === 'started'));
|
||||
@ -70,9 +86,11 @@ export class InterfaceStatusWidget implements Widget {
|
||||
status_started
|
||||
.merge(status_started_enter)
|
||||
.attr('class', 'status_started')
|
||||
.attr('width', 40)
|
||||
.attr('width', (ls: LinkStatus) => {
|
||||
return (ls.port.length * 8) + 10;
|
||||
})
|
||||
.attr('height', 20)
|
||||
.attr('x', (ls: LinkStatus) => ls.x - 10)
|
||||
.attr('x', (ls: LinkStatus) => ls.x - 30)
|
||||
.attr('y', (ls: LinkStatus) => ls.y - 10)
|
||||
.attr('rx', 8)
|
||||
.attr('ry', 8)
|
||||
@ -88,7 +106,7 @@ export class InterfaceStatusWidget implements Widget {
|
||||
.merge(status_started_label_enter)
|
||||
.attr('class', 'status_started_label')
|
||||
.text((ls: LinkStatus) => ls.port)
|
||||
.attr('x', (ls: LinkStatus) => ls.x - 5)
|
||||
.attr('x', (ls: LinkStatus) => ls.x - 25)
|
||||
.attr('y', (ls: LinkStatus) => ls.y + 5)
|
||||
.attr('fill', `black`);
|
||||
status_started_label.exit().remove();
|
||||
@ -100,9 +118,11 @@ export class InterfaceStatusWidget implements Widget {
|
||||
status_stopped
|
||||
.merge(status_stopped_enter)
|
||||
.attr('class', 'status_stopped')
|
||||
.attr('width', 40)
|
||||
.attr('width', (ls: LinkStatus) => {
|
||||
return (ls.port.length * 8) + 10;
|
||||
})
|
||||
.attr('height', 20)
|
||||
.attr('x', (ls: LinkStatus) => ls.x - 10)
|
||||
.attr('x', (ls: LinkStatus) => ls.x - 30)
|
||||
.attr('y', (ls: LinkStatus) => ls.y - 10)
|
||||
.attr('rx', 8)
|
||||
.attr('ry', 8)
|
||||
@ -118,7 +138,7 @@ export class InterfaceStatusWidget implements Widget {
|
||||
.merge(status_stopped_label_enter)
|
||||
.attr('class', 'status_stopped_label')
|
||||
.text((ls: LinkStatus) => ls.port)
|
||||
.attr('x', (ls: LinkStatus) => ls.x - 5)
|
||||
.attr('x', (ls: LinkStatus) => ls.x - 25)
|
||||
.attr('y', (ls: LinkStatus) => ls.y + 5)
|
||||
.attr('fill', `black`);
|
||||
status_stopped_label.exit().remove();
|
||||
@ -130,9 +150,11 @@ export class InterfaceStatusWidget implements Widget {
|
||||
status_suspended
|
||||
.merge(status_suspended_enter)
|
||||
.attr('class', 'status_suspended')
|
||||
.attr('width', 40)
|
||||
.attr('width', (ls: LinkStatus) => {
|
||||
return (ls.port.length * 8) + 10;
|
||||
})
|
||||
.attr('height', 20)
|
||||
.attr('x', (ls: LinkStatus) => ls.x - 10)
|
||||
.attr('x', (ls: LinkStatus) => ls.x - 30)
|
||||
.attr('y', (ls: LinkStatus) => ls.y - 10)
|
||||
.attr('rx', 8)
|
||||
.attr('ry', 8)
|
||||
@ -148,7 +170,7 @@ export class InterfaceStatusWidget implements Widget {
|
||||
.merge(status_suspended_label_enter)
|
||||
.attr('class', 'status_suspended_label')
|
||||
.text((ls: LinkStatus) => ls.port)
|
||||
.attr('x', (ls: LinkStatus) => ls.x - 5)
|
||||
.attr('x', (ls: LinkStatus) => ls.x - 25)
|
||||
.attr('y', (ls: LinkStatus) => ls.y + 5)
|
||||
.attr('fill', `black`);
|
||||
status_suspended_label.exit().remove();
|
||||
|
@ -66,6 +66,6 @@ export class SentryErrorHandler implements ErrorHandler {
|
||||
}
|
||||
|
||||
// Optionally show user dialog to provide details on what happened.
|
||||
Sentry.showReportDialog({ eventId });
|
||||
// Sentry.showReportDialog({ eventId });
|
||||
}
|
||||
}
|
||||
|
@ -5,7 +5,10 @@ import { SentryErrorHandler } from './sentry-error-handler';
|
||||
@Injectable()
|
||||
export class ToasterErrorHandler extends SentryErrorHandler {
|
||||
handleError(err: any): void {
|
||||
super.handleError(err);
|
||||
if (err.error && err.error.status && !(err.error.status === 403 || err.error.status === 404)) {
|
||||
super.handleError(err);
|
||||
}
|
||||
|
||||
if (!err) return;
|
||||
|
||||
const toasterService = this.injector.get(ToasterService);
|
||||
|
@ -4,8 +4,8 @@
|
||||
<div class="error-icon"><mat-icon>error_outline</mat-icon></div>
|
||||
<div>Error occurred: {{ error.message }}</div>
|
||||
<div>
|
||||
<button mat-button (click)="refresh()" matTooltip="Refresh page"><mat-icon>refresh</mat-icon></button>
|
||||
<button mat-button routerLink="/" matTooltip="Go to home"><mat-icon>home</mat-icon></button>
|
||||
<button mat-button (click)="refresh()" matTooltip="Refresh page" matTooltipClass="custom-tooltip"><mat-icon>refresh</mat-icon></button>
|
||||
<button mat-button routerLink="/" matTooltip="Go to home" matTooltipClass="custom-tooltip"><mat-icon>home</mat-icon></button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -8,7 +8,7 @@
|
||||
|
||||
button {
|
||||
background-color: #0097a7;
|
||||
margin-top: 10px;
|
||||
margin-top: 2px;
|
||||
border: none;
|
||||
outline: none;
|
||||
padding: 5px;
|
||||
|
@ -23,7 +23,7 @@ export class AdbutlerComponent implements OnInit {
|
||||
this.httpClient
|
||||
.get('https://servedbyadbutler.com/adserve/;ID=165803;size=0x0;setID=371476;type=json;').subscribe(
|
||||
response => {
|
||||
if (response && response['placements']) {
|
||||
if (response && response['placements'] && response['placements'].placement_1 && response['placements'].placement_1.body) {
|
||||
this.onLoad.emit(true);
|
||||
this.htmlCode = response['placements'].placement_1.body;
|
||||
this.ad.nativeElement.insertAdjacentHTML('beforeend', this.htmlCode);
|
||||
|
@ -0,0 +1,7 @@
|
||||
<span>{{ confirmationMessage }}</span>
|
||||
<div mat-dialog-actions>
|
||||
<button mat-button class="cancelButton" (click)="onNoClick()" color="accent">No</button>
|
||||
<button mat-button class="confirmButton" (click)="onYesClick()" tabindex="2" mat-raised-button color="primary">
|
||||
Yes
|
||||
</button>
|
||||
</div>
|
22
src/app/components/dialogs/information-dialog.component.ts
Normal file
22
src/app/components/dialogs/information-dialog.component.ts
Normal file
@ -0,0 +1,22 @@
|
||||
import { Component, OnInit, Inject } from '@angular/core';
|
||||
import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog';
|
||||
|
||||
@Component({
|
||||
selector: 'app-information-dialog',
|
||||
templateUrl: 'information-dialog.component.html',
|
||||
styleUrls: ['information-dialog.component.css']
|
||||
})
|
||||
export class InformationDialogComponent implements OnInit {
|
||||
public confirmationMessage: string;
|
||||
constructor(public dialogRef: MatDialogRef<InformationDialogComponent>) {}
|
||||
|
||||
ngOnInit() {}
|
||||
|
||||
onNoClick(): void {
|
||||
this.dialogRef.close(false);
|
||||
}
|
||||
|
||||
onYesClick(): void {
|
||||
this.dialogRef.close(true);
|
||||
}
|
||||
}
|
@ -25,8 +25,9 @@ export class NotificationBoxComponent implements OnInit, OnDestroy {
|
||||
interval = 10;
|
||||
|
||||
delayTime: number = 5000;
|
||||
breakTime: number = 20;
|
||||
isEndless: boolean = false;
|
||||
breakTime: number = 20 * 60;
|
||||
isEndless: boolean = true;
|
||||
|
||||
numberOfViews: number = 1;
|
||||
isLightThemeEnabled: boolean = false;
|
||||
|
||||
@ -41,7 +42,10 @@ export class NotificationBoxComponent implements OnInit, OnDestroy {
|
||||
let adbutler = localStorage.getItem('adbutler');
|
||||
var today = new Date().toISOString().substring(0, 10);
|
||||
|
||||
if (!this.location.path().includes('nodes') && !(adbutler == today)) this.startTimer();
|
||||
// to show ad once a day
|
||||
// if (!this.location.path().includes('nodes') && !(adbutler == today)) this.startTimer();
|
||||
|
||||
if (!this.location.path().includes('nodes')) this.startTimer();
|
||||
this.themeService.getActualTheme() === 'light' ? this.isLightThemeEnabled = true : this.isLightThemeEnabled = false;
|
||||
}
|
||||
|
||||
@ -61,6 +65,9 @@ export class NotificationBoxComponent implements OnInit, OnDestroy {
|
||||
|
||||
startTimer() {
|
||||
this.timer = timer(this.delayTime, 1000);
|
||||
setTimeout(() => {
|
||||
this.showNotification();
|
||||
}, 5000);
|
||||
|
||||
this.timerSubscription = this.timer.subscribe(() => {
|
||||
this.ticks++;
|
||||
@ -82,7 +89,7 @@ export class NotificationBoxComponent implements OnInit, OnDestroy {
|
||||
}
|
||||
|
||||
showNotification() {
|
||||
localStorage.setItem('adbutler', new Date().toISOString().substring(0, 10));
|
||||
// localStorage.setItem('adbutler', new Date().toISOString().substring(0, 10));
|
||||
|
||||
this.viewTimer = timer(0, 100);
|
||||
this.progress = 0;
|
||||
|
@ -23,7 +23,7 @@
|
||||
<ng-container matColumnDef="actions">
|
||||
<th mat-header-cell *matHeaderCellDef> Actions </th>
|
||||
<td mat-cell *matCellDef="let element">
|
||||
<button mat-icon-button matTooltip="Delete adapter" (click)="delete(element)">
|
||||
<button mat-icon-button matTooltip="Delete adapter" matTooltipClass="custom-tooltip" (click)="delete(element)">
|
||||
<mat-icon aria-label="Delete adapter">delete</mat-icon>
|
||||
</button>
|
||||
</td>
|
||||
|
@ -33,11 +33,9 @@ export class DeleteTemplateComponent {
|
||||
|
||||
dialogRef.afterClosed().subscribe((answer: boolean) => {
|
||||
if (answer) {
|
||||
this.templateService.deleteTemplate(this.server, templateId).subscribe((answer: boolean) => {
|
||||
if(answer) {
|
||||
this.deleteEvent.emit(templateId);
|
||||
this.toasterService.success(`Template ${templateName} deleted.`);
|
||||
}
|
||||
this.templateService.deleteTemplate(this.server, templateId).subscribe((answer) => {
|
||||
this.deleteEvent.emit(templateId);
|
||||
this.toasterService.success(`Template ${templateName} deleted.`);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
@ -22,7 +22,7 @@
|
||||
<ng-container matColumnDef="action">
|
||||
<th mat-header-cell *matHeaderCellDef> Actions </th>
|
||||
<td mat-cell *matCellDef="let element">
|
||||
<button mat-icon-button matTooltip="Delete port" (click)="delete(element)">
|
||||
<button mat-icon-button matTooltip="Delete port" matTooltipClass="custom-tooltip" (click)="delete(element)">
|
||||
<mat-icon aria-label="Delete port">delete</mat-icon>
|
||||
</button>
|
||||
</td>
|
||||
|
@ -22,7 +22,7 @@
|
||||
<ng-container matColumnDef="action">
|
||||
<th mat-header-cell *matHeaderCellDef> Actions </th>
|
||||
<td mat-cell *matCellDef="let element">
|
||||
<button mat-icon-button matTooltip="Delete port" (click)="delete(element)">
|
||||
<button mat-icon-button matTooltip="Delete port" matTooltipClass="custom-tooltip" (click)="delete(element)">
|
||||
<mat-icon aria-label="Delete port">delete</mat-icon>
|
||||
</button>
|
||||
</td>
|
||||
|
@ -146,7 +146,7 @@ export class AddQemuVmTemplateComponent implements OnInit {
|
||||
|
||||
addTemplate() {
|
||||
if (!this.nameForm.invalid && !this.memoryForm.invalid && (this.selectedImage || this.chosenImage)) {
|
||||
this.qemuTemplate.ram = this.memoryForm.get("ramMemory").value;
|
||||
this.qemuTemplate.ram = +this.memoryForm.get("ramMemory").value;
|
||||
this.qemuTemplate.qemu_path = this.selectedBinary.path;
|
||||
if (this.newImageSelected) {
|
||||
this.qemuTemplate.hda_disk_image = this.diskForm.get("fileName").value;
|
||||
|
@ -29,6 +29,10 @@ export class ConsoleDeviceActionBrowserComponent {
|
||||
if (this.node.status !== "started") {
|
||||
this.toasterService.error("This node must be started before a console can be opened");
|
||||
} else {
|
||||
if (this.node.console_host === '0.0.0.0' || this.node.console_host === '0:0:0:0:0:0:0:0' || this.node.console_host === '::') {
|
||||
this.node.console_host = this.server.host;
|
||||
}
|
||||
|
||||
if (this.node.console_type === "telnet") {
|
||||
location.assign(`gns3+telnet://${this.node.console_host}:${this.node.console}?name=${this.node.name}&project_id=${this.node.project_id}&node_id=${this.node.node_id}`);
|
||||
} else if (this.node.console_type === "vnc") {
|
||||
|
@ -56,11 +56,11 @@
|
||||
<ng-container matColumnDef="actions">
|
||||
<mat-header-cell *matHeaderCellDef> Actions </mat-header-cell>
|
||||
<mat-cell *matCellDef="let row" style="text-align: right">
|
||||
<button mat-icon-button matTooltip="Install" (click)="install(row)">
|
||||
<button mat-icon-button matTooltip="Install" matTooltipClass="custom-tooltip" (click)="install(row)">
|
||||
<mat-icon aria-label="Install">archive</mat-icon>
|
||||
</button>
|
||||
|
||||
<button mat-icon-button matTooltip="Show info" (click)="showInfo(row)">
|
||||
<button mat-icon-button matTooltip="Show info" matTooltipClass="custom-tooltip" (click)="showInfo(row)">
|
||||
<mat-icon aria-label="Show info">info</mat-icon>
|
||||
</button>
|
||||
</mat-cell>
|
||||
@ -113,17 +113,17 @@
|
||||
<mat-step *ngIf="applianceToInstall">
|
||||
<ng-template matStepLabel>{{secondActionTitle}}</ng-template>
|
||||
|
||||
<mat-card [hidden]="applianceToInstall">
|
||||
Please select appliance to install first
|
||||
<mat-card [hidden]="!(!isLinuxPlatform && !isGns3VmAvailable && !applianceToInstall.dynamips)">
|
||||
Please configure GNS3 VM to install selected appliance
|
||||
</mat-card>
|
||||
|
||||
<mat-card [hidden]="!applianceToInstall">
|
||||
<mat-card [hidden]="!(isLinuxPlatform || isGns3VmAvailable || applianceToInstall.dynamips)">
|
||||
<div *ngIf="applianceToInstall.qemu">
|
||||
<div>
|
||||
Server type<br/>
|
||||
<mat-radio-group class="radio-group">
|
||||
<mat-radio-button [disabled]="true" class="radio-button" value="1" (click)="setServerType('local')">Install the appliance locally</mat-radio-button>
|
||||
<mat-radio-button class="radio-button" value="2" checked (click)="setServerType('gns3 vm')">Install the appliance on the GNS3 VM</mat-radio-button>
|
||||
<mat-radio-button [disabled]="!isLinuxPlatform" [checked]="!isGns3VmChosen" class="radio-button" value="1" (click)="setServerType('local')">Install the appliance locally</mat-radio-button>
|
||||
<mat-radio-button [disabled]="!isGns3VmAvailable" [checked]="isGns3VmChosen" class="radio-button" value="2" (click)="setServerType('gns3 vm')">Install the appliance on the GNS3 VM</mat-radio-button>
|
||||
</mat-radio-group>
|
||||
</div>
|
||||
<div>
|
||||
@ -139,30 +139,60 @@
|
||||
</mat-select>
|
||||
</div>
|
||||
<div>
|
||||
Install required files
|
||||
<mat-list>
|
||||
<mat-list-item *ngFor="let image of applianceToInstall.images">
|
||||
<div class="list-item">
|
||||
<div>
|
||||
{{image.filename}}
|
||||
Install required files <br/>
|
||||
<div>
|
||||
<div *ngFor="let version of applianceToInstall.versions">
|
||||
<div class="list-item">
|
||||
<span>{{applianceToInstall.name}} version {{version.name}}</span>
|
||||
|
||||
<div>
|
||||
<button class="button" mat-raised-button color="primary" (click)="createQemuTemplateFromVersion(version)">Create</button>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<span *ngIf="checkImage(image)"><mat-icon matTooltip="Ready to install">check</mat-icon></span>
|
||||
<span *ngIf="!checkImage(image)"><mat-icon matTooltip="Missing">close</mat-icon></span>
|
||||
<input
|
||||
type="file"
|
||||
class="non-visible"
|
||||
#file2
|
||||
(change)="importImage($event)"
|
||||
ng2FileSelect
|
||||
[uploader]="uploaderImage"/>
|
||||
<button class="button" mat-raised-button (click)="file2.click()">Import</button>
|
||||
<button class="button" mat-raised-button (click)="downloadImage(image)">Download</button>
|
||||
<button *ngIf="checkImage(image)" class="button" mat-raised-button color="primary" (click)="createQemuTemplate(image)">Create</button>
|
||||
|
||||
<div class="list-item-inside" *ngIf="version.images.hda_disk_image">
|
||||
<span>
|
||||
{{version.images.hda_disk_image}}
|
||||
</span>
|
||||
|
||||
<div>
|
||||
<span *ngIf="checkImageFromVersion(version.images.hda_disk_image)"><mat-icon matTooltip="Ready to install" matTooltipClass="custom-tooltip">check</mat-icon></span>
|
||||
<span *ngIf="!checkImageFromVersion(version.images.hda_disk_image)"><mat-icon matTooltip="Missing" matTooltipClass="custom-tooltip">close</mat-icon></span>
|
||||
|
||||
<input
|
||||
type="file"
|
||||
class="non-visible"
|
||||
#file2
|
||||
(change)="importImage($event)"
|
||||
ng2FileSelect
|
||||
[uploader]="uploaderImage"/>
|
||||
<button class="button" mat-raised-button (click)="file2.click()">Import</button>
|
||||
<button class="button" mat-raised-button (click)="downloadImageFromVersion(version.images.hda_disk_image)">Download</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</mat-list-item>
|
||||
</mat-list>
|
||||
|
||||
<div class="list-item-inside" *ngIf="version.images.hdb_disk_image">
|
||||
<span>
|
||||
{{version.images.hdb_disk_image}}
|
||||
</span>
|
||||
|
||||
<div>
|
||||
<span *ngIf="checkImageFromVersion(version.images.hdb_disk_image)"><mat-icon matTooltip="Ready to install" matTooltipClass="custom-tooltip">check</mat-icon></span>
|
||||
<span *ngIf="!checkImageFromVersion(version.images.hdb_disk_image)"><mat-icon matTooltip="Missing" matTooltipClass="custom-tooltip">close</mat-icon></span>
|
||||
|
||||
<input
|
||||
type="file"
|
||||
class="non-visible"
|
||||
#file2
|
||||
(change)="importImage($event)"
|
||||
ng2FileSelect
|
||||
[uploader]="uploaderImage"/>
|
||||
<button class="button" mat-raised-button (click)="file2.click()">Import</button>
|
||||
<button class="button" mat-raised-button (click)="downloadImageFromVersion(version.images.hdb_disk_image)">Download</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -170,8 +200,8 @@
|
||||
<div>
|
||||
Server type<br/>
|
||||
<mat-radio-group class="radio-group">
|
||||
<mat-radio-button [disabled]="true" class="radio-button" value="1" (click)="setServerType('local')">Install the appliance locally</mat-radio-button>
|
||||
<mat-radio-button class="radio-button" value="2" checked (click)="setServerType('gns3 vm')">Install the appliance on the GNS3 VM</mat-radio-button>
|
||||
<mat-radio-button [disabled]="!isLinuxPlatform" [checked]="!isGns3VmChosen" class="radio-button" value="1" (click)="setServerType('local')">Install the appliance locally</mat-radio-button>
|
||||
<mat-radio-button [disabled]="!isGns3VmAvailable" [checked]="isGns3VmChosen" class="radio-button" value="2" (click)="setServerType('gns3 vm')">Install the appliance on the GNS3 VM</mat-radio-button>
|
||||
</mat-radio-group>
|
||||
</div>
|
||||
<button mat-raised-button color="primary" (click)="createDockerTemplate()" class="create-button">Create docker template</button>
|
||||
@ -181,8 +211,8 @@
|
||||
<div>
|
||||
Server type<br/>
|
||||
<mat-radio-group class="radio-group">
|
||||
<mat-radio-button [disabled]="true" class="radio-button" value="1" (click)="setServerType('local')">Install the appliance locally</mat-radio-button>
|
||||
<mat-radio-button class="radio-button" value="2" checked (click)="setServerType('gns3 vm')">Install the appliance on the GNS3 VM</mat-radio-button>
|
||||
<mat-radio-button [checked]="!isGns3VmChosen" class="radio-button" value="1" (click)="setServerType('local')">Install the appliance locally</mat-radio-button>
|
||||
<mat-radio-button [disabled]="!isGns3VmAvailable" [checked]="isGns3VmChosen" class="radio-button" value="2" (click)="setServerType('gns3 vm')">Install the appliance on the GNS3 VM</mat-radio-button>
|
||||
</mat-radio-group>
|
||||
</div>
|
||||
<div>
|
||||
@ -214,8 +244,8 @@
|
||||
<div>
|
||||
Server type<br/>
|
||||
<mat-radio-group class="radio-group">
|
||||
<mat-radio-button [disabled]="true" class="radio-button" value="1" (click)="setServerType('local')">Install the appliance locally</mat-radio-button>
|
||||
<mat-radio-button class="radio-button" value="2" checked (click)="setServerType('gns3 vm')">Install the appliance on the GNS3 VM</mat-radio-button>
|
||||
<mat-radio-button [disabled]="!isLinuxPlatform" [checked]="!isGns3VmChosen" class="radio-button" value="1" (click)="setServerType('local')">Install the appliance locally</mat-radio-button>
|
||||
<mat-radio-button [disabled]="!isGns3VmAvailable" [checked]="isGns3VmChosen" class="radio-button" value="2" (click)="setServerType('gns3 vm')">Install the appliance on the GNS3 VM</mat-radio-button>
|
||||
</mat-radio-group>
|
||||
</div>
|
||||
<div>
|
||||
|
@ -42,6 +42,15 @@
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
width: 100%;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.list-item-inside {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
width: 100%;
|
||||
padding-left: 30px;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.button {
|
||||
|
@ -9,7 +9,7 @@ import { Server } from '../../../models/server';
|
||||
import { Node } from '../../../cartography/models/node';
|
||||
import { Project } from '../../../models/project';
|
||||
import { ApplianceService } from '../../../services/appliances.service';
|
||||
import { Appliance, Image } from '../../../models/appliance';
|
||||
import { Appliance, Image, Version } 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';
|
||||
@ -26,6 +26,8 @@ import { IouService } from '../../../services/iou.service';
|
||||
import { IouTemplate } from '../../../models/templates/iou-template';
|
||||
import { TemplateService } from '../../../services/template.service';
|
||||
import { Template } from '../../../models/template';
|
||||
import { ComputeService } from '../../../services/compute.service';
|
||||
import { InformationDialogComponent } from '../../../components/dialogs/information-dialog.component';
|
||||
|
||||
@Component({
|
||||
selector: 'app-new-template-dialog',
|
||||
@ -56,7 +58,10 @@ export class NewTemplateDialogComponent implements OnInit {
|
||||
public applianceToInstall: Appliance;
|
||||
public selectedImages: any[];
|
||||
|
||||
private isGns3VmChosen = true;
|
||||
public isGns3VmAvailable = false;
|
||||
public isLinuxPlatform = false;
|
||||
|
||||
private isGns3VmChosen = false;
|
||||
private isLocalComputerChosen = false;
|
||||
|
||||
public qemuBinaries: QemuBinary[] = [];
|
||||
@ -85,10 +90,21 @@ export class NewTemplateDialogComponent implements OnInit {
|
||||
private iosService: IosService,
|
||||
private iouService: IouService,
|
||||
private templateService: TemplateService,
|
||||
public dialog: MatDialog
|
||||
public dialog: MatDialog,
|
||||
private computeService: ComputeService
|
||||
) {}
|
||||
|
||||
ngOnInit() {
|
||||
this.computeService.getComputes(this.server).subscribe((computes) => {
|
||||
computes.forEach(compute => {
|
||||
if (compute.compute_id === 'vm') {
|
||||
this.isGns3VmAvailable = true;
|
||||
this.isGns3VmChosen = true;
|
||||
}
|
||||
if (compute.capabilities.platform === 'linux') this.isLinuxPlatform = true;
|
||||
})
|
||||
});
|
||||
|
||||
this.qemuService.getImages(this.server).subscribe((qemuImages) => {
|
||||
this.qemuImages = qemuImages;
|
||||
});
|
||||
@ -289,8 +305,58 @@ export class NewTemplateDialogComponent implements OnInit {
|
||||
return false;
|
||||
}
|
||||
|
||||
checkImageFromVersion(image: string): boolean {
|
||||
if (this.applianceToInstall.qemu) {
|
||||
if (this.qemuImages.filter(n => n.filename === image).length > 0) return true;
|
||||
} else if (this.applianceToInstall.dynamips) {
|
||||
if (this.iosImages.filter(n => n.filename === image).length > 0) return true;
|
||||
} else if (this.applianceToInstall.iou) {
|
||||
if (this.iouImages.filter(n => n.filename === image).length > 0) return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
checkImages(version: Version): boolean {
|
||||
if (this.checkImageFromVersion(version.images.hda_disk_image) && this.checkImageFromVersion(version.images.hdb_disk_image)) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
openConfirmationDialog(message: string, link: string) {
|
||||
const dialogRef = this.dialog.open(InformationDialogComponent, {
|
||||
width: '400px',
|
||||
height: '200px',
|
||||
autoFocus: false,
|
||||
disableClose: true
|
||||
});
|
||||
dialogRef.componentInstance.confirmationMessage = message;
|
||||
|
||||
dialogRef.afterClosed().subscribe((answer: boolean) => {
|
||||
if (answer) {
|
||||
window.open(link);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
downloadImage(image: Image) {
|
||||
window.open(image.download_url);
|
||||
const directDownloadMessage: string = "Download will redirect you where the required file can be downloaded, you may have to be registered with the vendor in order to download the file.";
|
||||
const compressionMessage: string = `The file is compressed with ${image.compression}, it must be uncompressed first.`;
|
||||
|
||||
if (image.direct_download_url) {
|
||||
if (image.compression) {
|
||||
this.openConfirmationDialog(compressionMessage, image.direct_download_url);
|
||||
} else {
|
||||
window.open(image.direct_download_url);
|
||||
}
|
||||
} else {
|
||||
this.openConfirmationDialog(directDownloadMessage, image.download_url);
|
||||
}
|
||||
}
|
||||
|
||||
downloadImageFromVersion(image: string) {
|
||||
this.applianceToInstall.images.forEach(n => {
|
||||
if (n.filename === image) this.downloadImage(n);
|
||||
});
|
||||
}
|
||||
|
||||
createIouTemplate (image: Image) {
|
||||
@ -370,7 +436,12 @@ export class NewTemplateDialogComponent implements OnInit {
|
||||
});
|
||||
}
|
||||
|
||||
createQemuTemplate(image: Image) {
|
||||
createQemuTemplateFromVersion(version: Version) {
|
||||
if (!this.checkImages(version)) {
|
||||
this.toasterService.error('Please install required images first');
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this.selectedBinary) {
|
||||
this.toasterService.error('Please select QEMU binary first');
|
||||
return;
|
||||
@ -383,6 +454,9 @@ export class NewTemplateDialogComponent implements OnInit {
|
||||
qemuTemplate.boot_priority = this.applianceToInstall.qemu.boot_priority;
|
||||
qemuTemplate.console_type = this.applianceToInstall.qemu.console_type;
|
||||
qemuTemplate.hda_disk_interface = this.applianceToInstall.qemu.hda_disk_interface;
|
||||
qemuTemplate.hdb_disk_interface = this.applianceToInstall.qemu.hdb_disk_interface;
|
||||
qemuTemplate.hdc_disk_interface = this.applianceToInstall.qemu.hdc_disk_interface;
|
||||
qemuTemplate.hdd_disk_interface = this.applianceToInstall.qemu.hdd_disk_interface;
|
||||
qemuTemplate.builtin = this.applianceToInstall.builtin;
|
||||
qemuTemplate.category = this.applianceToInstall.category;
|
||||
qemuTemplate.first_port_name = this.applianceToInstall.first_port_name;
|
||||
@ -391,8 +465,10 @@ export class NewTemplateDialogComponent implements OnInit {
|
||||
qemuTemplate.qemu_path = this.selectedBinary.path;
|
||||
qemuTemplate.compute_id = this.isGns3VmChosen ? 'vm' : 'local';
|
||||
qemuTemplate.template_id = uuid();
|
||||
qemuTemplate.hda_disk_image = image.filename;
|
||||
qemuTemplate.hda_disk_image = version.images.hda_disk_image;
|
||||
qemuTemplate.hdb_disk_image = version.images.hdb_disk_image;
|
||||
qemuTemplate.template_type = 'qemu';
|
||||
qemuTemplate.usage = this.applianceToInstall.usage;
|
||||
|
||||
this.qemuService.addTemplate(this.server, qemuTemplate).subscribe((template) => {
|
||||
this.templateService.newTemplateCreated.next(template as any as Template);
|
||||
|
@ -18,7 +18,7 @@
|
||||
<ng-container matColumnDef="actions">
|
||||
<th mat-header-cell *matHeaderCellDef> Actions </th>
|
||||
<td mat-cell *matCellDef="let element">
|
||||
<button mat-icon-button matTooltip="Delete port" (click)="delete(element)">
|
||||
<button mat-icon-button matTooltip="Delete port" matTooltipClass="custom-tooltip" (click)="delete(element)">
|
||||
<mat-icon aria-label="Delete port">delete</mat-icon>
|
||||
</button>
|
||||
</td>
|
||||
|
@ -18,7 +18,7 @@
|
||||
<ng-container matColumnDef="actions">
|
||||
<th mat-header-cell *matHeaderCellDef> Actions </th>
|
||||
<td mat-cell *matCellDef="let element">
|
||||
<button mat-icon-button matTooltip="Delete port" (click)="delete(element)">
|
||||
<button mat-icon-button matTooltip="Delete port" matTooltipClass="custom-tooltip" (click)="delete(element)">
|
||||
<mat-icon aria-label="Delete port">delete</mat-icon>
|
||||
</button>
|
||||
</td>
|
||||
|
@ -1,5 +1,6 @@
|
||||
<button
|
||||
matTooltip="Console connect to all nodes"
|
||||
matTooltipClass="custom-tooltip"
|
||||
mat-icon-button
|
||||
(click)="startConsoleForAllNodes()"
|
||||
class="menu-button"
|
||||
@ -8,6 +9,7 @@
|
||||
</button>
|
||||
<button
|
||||
matTooltip="Start/Resume all nodes"
|
||||
matTooltipClass="custom-tooltip"
|
||||
mat-icon-button
|
||||
(click)="startNodes()"
|
||||
class="menu-button"
|
||||
@ -16,6 +18,7 @@
|
||||
</button>
|
||||
<button
|
||||
matTooltip="Suspend all nodes"
|
||||
matTooltipClass="custom-tooltip"
|
||||
mat-icon-button
|
||||
(click)="suspendNodes()"
|
||||
class="menu-button"
|
||||
@ -24,6 +27,7 @@
|
||||
</button>
|
||||
<button
|
||||
matTooltip="Stop all nodes"
|
||||
matTooltipClass="custom-tooltip"
|
||||
mat-icon-button
|
||||
(click)="stopNodes()"
|
||||
class="menu-button"
|
||||
@ -32,6 +36,7 @@
|
||||
</button>
|
||||
<button
|
||||
matTooltip="Reload all nodes"
|
||||
matTooltipClass="custom-tooltip"
|
||||
mat-icon-button
|
||||
(click)="reloadNodes()"
|
||||
class="menu-button"
|
||||
|
@ -1,5 +1,6 @@
|
||||
<button
|
||||
matTooltip="Add a note"
|
||||
matTooltipClass="custom-tooltip"
|
||||
mat-icon-button
|
||||
class="menu-button"
|
||||
(click)="addDrawing('text')">
|
||||
@ -13,6 +14,7 @@
|
||||
(change)="uploadImageFile($event)"/>
|
||||
<button
|
||||
matTooltip="Insert a picture"
|
||||
matTooltipClass="custom-tooltip"
|
||||
mat-icon-button
|
||||
class="menu-button"
|
||||
(click)="file.click()">
|
||||
@ -20,6 +22,7 @@
|
||||
</button>
|
||||
<button
|
||||
matTooltip="Draw a rectangle"
|
||||
matTooltipClass="custom-tooltip"
|
||||
mat-icon-button
|
||||
class="menu-button"
|
||||
(click)="addDrawing('rectangle')">
|
||||
@ -27,6 +30,7 @@
|
||||
</button>
|
||||
<button
|
||||
matTooltip="Draw an ellipse"
|
||||
matTooltipClass="custom-tooltip"
|
||||
mat-icon-button
|
||||
class="menu-button"
|
||||
(click)="addDrawing('ellipse')">
|
||||
@ -34,6 +38,7 @@
|
||||
</button>
|
||||
<button *ngIf="!isLightThemeEnabled"
|
||||
matTooltip="Draw a line"
|
||||
matTooltipClass="custom-tooltip"
|
||||
mat-icon-button class="menu-button"
|
||||
(click)="addDrawing('line')">
|
||||
<svg height="40" width="40">
|
||||
@ -48,6 +53,7 @@
|
||||
</button>
|
||||
<button *ngIf="isLightThemeEnabled"
|
||||
matTooltip="Draw a line"
|
||||
matTooltipClass="custom-tooltip"
|
||||
mat-icon-button class="menu-button"
|
||||
(click)="addDrawing('line')">
|
||||
<svg height="40" width="40">
|
||||
@ -62,6 +68,7 @@
|
||||
</button>
|
||||
<button
|
||||
matTooltip="Lock or unlock all items"
|
||||
matTooltipClass="custom-tooltip"
|
||||
mat-icon-button
|
||||
class="menu-button"
|
||||
(click)="changeLockValue()">
|
||||
@ -69,6 +76,7 @@
|
||||
</button>
|
||||
<button
|
||||
matTooltip="Take a screenshot"
|
||||
matTooltipClass="custom-tooltip"
|
||||
mat-icon-button
|
||||
class="menu-button"
|
||||
(click)="takeScreenshot()"
|
||||
|
@ -35,11 +35,11 @@
|
||||
<div *ngIf="toolbarVisibility" class="project-toolbar">
|
||||
<mat-toolbar color="primary" class="project-toolbar" [ngClass]="{lightTheme: isLightThemeEnabled}">
|
||||
<mat-toolbar-row *ngIf="!isLightThemeEnabled">
|
||||
<button matTooltip="Open menu" mat-icon-button [matMenuTriggerFor]="mainMenu"><mat-icon svgIcon="gns3"></mat-icon></button>
|
||||
<button matTooltip="Open menu" matTooltipClass="custom-tooltip" mat-icon-button [matMenuTriggerFor]="mainMenu"><mat-icon svgIcon="gns3"></mat-icon></button>
|
||||
</mat-toolbar-row>
|
||||
|
||||
<mat-toolbar-row *ngIf="isLightThemeEnabled">
|
||||
<button matTooltip="Open menu" mat-icon-button [matMenuTriggerFor]="mainMenu"><mat-icon svgIcon="gns3black"></mat-icon></button>
|
||||
<button matTooltip="Open menu" matTooltipClass="custom-tooltip" mat-icon-button [matMenuTriggerFor]="mainMenu"><mat-icon svgIcon="gns3black"></mat-icon></button>
|
||||
</mat-toolbar-row>
|
||||
|
||||
<mat-menu #mainMenu="matMenu" [overlapTrigger]="false">
|
||||
@ -109,11 +109,8 @@
|
||||
|
||||
<mat-menu #viewMenu="matMenu" [overlapTrigger]="false">
|
||||
<div class="options-item">
|
||||
<mat-checkbox [ngModel]="mapSettingsService.integrateLinkLabelsToLinks" (change)="toggleIntegrateLinkLabelsToLinks($event.checked)">
|
||||
Integrate link labels to links
|
||||
</mat-checkbox>
|
||||
<mat-checkbox [ngModel]="isInterfaceLabelVisible" (change)="toggleShowInterfaceLabels($event.checked)">
|
||||
Show interface labels separately
|
||||
Show interface labels
|
||||
</mat-checkbox><br/>
|
||||
<mat-checkbox [ngModel]="isConsoleVisible" (change)="toggleShowConsole($event.checked)">
|
||||
Show console
|
||||
@ -141,13 +138,13 @@
|
||||
</mat-toolbar-row>
|
||||
|
||||
<mat-toolbar-row *ngIf="!readonly">
|
||||
<button matTooltip="Add a link" mat-icon-button [color]="tools.draw_link ? 'primary' : 'basic'" (click)="toggleDrawLineMode()">
|
||||
<button matTooltip="Add a link" matTooltipClass="custom-tooltip" mat-icon-button [color]="tools.draw_link ? 'primary' : 'basic'" (click)="toggleDrawLineMode()">
|
||||
<mat-icon>timeline</mat-icon>
|
||||
</button>
|
||||
</mat-toolbar-row>
|
||||
|
||||
<mat-toolbar-row>
|
||||
<button matTooltip="Enable/disable moving mode" mat-icon-button [color]="tools.moving ? 'primary' : 'basic'" (click)="toggleMovingMode()">
|
||||
<button matTooltip="Enable/disable moving mode" matTooltipClass="custom-tooltip" mat-icon-button [color]="tools.moving ? 'primary' : 'basic'" (click)="toggleMovingMode()">
|
||||
<mat-icon>zoom_out_map</mat-icon>
|
||||
</button>
|
||||
</mat-toolbar-row>
|
||||
@ -157,13 +154,13 @@
|
||||
</mat-toolbar-row>
|
||||
|
||||
<mat-toolbar-row *ngIf="!readonly">
|
||||
<button matTooltip="Fit in view" mat-icon-button (click)="fitInView()">
|
||||
<button matTooltip="Fit in view" matTooltipClass="custom-tooltip" mat-icon-button (click)="fitInView()">
|
||||
<mat-icon>fullscreen</mat-icon>
|
||||
</button>
|
||||
</mat-toolbar-row>
|
||||
|
||||
<mat-toolbar-row *ngIf="!readonly">
|
||||
<button matTooltip="Center view" mat-icon-button (click)="centerView()">
|
||||
<button matTooltip="Center view" matTooltipClass="custom-tooltip" mat-icon-button (click)="centerView()">
|
||||
<mat-icon>center_focus_strong</mat-icon>
|
||||
</button>
|
||||
</mat-toolbar-row>
|
||||
@ -185,9 +182,9 @@
|
||||
</div>
|
||||
|
||||
<div [ngClass]="{lightTheme: isLightThemeEnabled}" class="zoom-buttons">
|
||||
<button matTooltip="Zoom in" [ngClass]="{lightTheme: isLightThemeEnabled}" class="zoom-button" (click)="zoomIn()"><mat-icon>zoom_in</mat-icon></button>
|
||||
<button matTooltip="Reset zoom" [ngClass]="{lightTheme: isLightThemeEnabled}" class="zoom-button" (click)="resetZoom()"><mat-icon>adjust</mat-icon></button>
|
||||
<button matTooltip="Zoom out" [ngClass]="{lightTheme: isLightThemeEnabled}" class="zoom-button" (click)="zoomOut()"><mat-icon>zoom_out</mat-icon></button>
|
||||
<button matTooltip="Zoom in" matTooltipClass="custom-tooltip" [ngClass]="{lightTheme: isLightThemeEnabled}" class="zoom-button" (click)="zoomIn()"><mat-icon>zoom_in</mat-icon></button>
|
||||
<button matTooltip="Reset zoom" matTooltipClass="custom-tooltip" [ngClass]="{lightTheme: isLightThemeEnabled}" class="zoom-button" (click)="resetZoom()"><mat-icon>adjust</mat-icon></button>
|
||||
<button matTooltip="Zoom out" matTooltipClass="custom-tooltip" [ngClass]="{lightTheme: isLightThemeEnabled}" class="zoom-button" (click)="zoomOut()"><mat-icon>zoom_out</mat-icon></button>
|
||||
</div>
|
||||
|
||||
<app-progress></app-progress>
|
||||
|
@ -258,12 +258,7 @@ export class ProjectMapComponent implements OnInit, OnDestroy {
|
||||
this.projectService.open(this.server, this.project.project_id);
|
||||
this.title.setTitle(this.project.name);
|
||||
|
||||
// old settings
|
||||
// if (this.mapSettingsService.interfaceLabels.has(project.project_id)) {
|
||||
// this.isInterfaceLabelVisible = this.mapSettingsService.interfaceLabels.get(project.project_id);
|
||||
// } else {
|
||||
// this.isInterfaceLabelVisible = this.project.show_interface_labels;
|
||||
// }
|
||||
this.isInterfaceLabelVisible = this.mapSettingsService.showInterfaceLabels;
|
||||
|
||||
this.recentlyOpenedProjectService.setServerId(this.server.id.toString());
|
||||
this.recentlyOpenedProjectService.setProjectId(this.project.project_id);
|
||||
@ -481,6 +476,10 @@ export class ProjectMapComponent implements OnInit, OnDestroy {
|
||||
this.progressService.deactivate();
|
||||
}
|
||||
});
|
||||
},
|
||||
error => {
|
||||
this.toasterService.error(error.error.message);
|
||||
this.progressService.deactivate();
|
||||
});
|
||||
}
|
||||
|
||||
@ -675,17 +674,7 @@ export class ProjectMapComponent implements OnInit, OnDestroy {
|
||||
|
||||
public toggleShowInterfaceLabels(enabled: boolean) {
|
||||
this.isInterfaceLabelVisible = enabled;
|
||||
this.mapSettingsService.toggleShowInterfaceLabels(this.project.project_id, this.isInterfaceLabelVisible);
|
||||
|
||||
this.mapSettingsService.integrateLinkLabelsToLinks = false;
|
||||
this.mapSettingsService.mapRenderedEmitter.emit(true);
|
||||
}
|
||||
|
||||
public toggleIntegrateLinkLabelsToLinks(enabled: boolean) {
|
||||
this.isInterfaceLabelVisible = false;
|
||||
this.mapSettingsService.toggleShowInterfaceLabels(this.project.project_id, this.isInterfaceLabelVisible);
|
||||
|
||||
this.mapSettingsService.integrateLinkLabelsToLinks = enabled;
|
||||
this.mapSettingsService.toggleShowInterfaceLabels(this.isInterfaceLabelVisible);
|
||||
this.mapSettingsService.mapRenderedEmitter.emit(true);
|
||||
}
|
||||
|
||||
@ -891,7 +880,7 @@ export class ProjectMapComponent implements OnInit, OnDestroy {
|
||||
public addNewTemplate() {
|
||||
const dialogRef = this.dialog.open(NewTemplateDialogComponent, {
|
||||
width: '1000px',
|
||||
maxHeight: '500px',
|
||||
maxHeight: '700px',
|
||||
autoFocus: false,
|
||||
disableClose: true
|
||||
});
|
||||
|
@ -66,7 +66,7 @@
|
||||
<ng-container matColumnDef="actions">
|
||||
<th mat-header-cell *matHeaderCellDef>Actions</th>
|
||||
<td mat-cell *matCellDef="let element">
|
||||
<button mat-icon-button matTooltip="Delete variable" (click)="deleteVariable(element)">
|
||||
<button mat-icon-button matTooltip="Delete variable" matTooltipClass="custom-tooltip" (click)="deleteVariable(element)">
|
||||
<mat-icon aria-label="Delete adapter">delete</mat-icon>
|
||||
</button>
|
||||
</td>
|
||||
|
@ -10,7 +10,7 @@
|
||||
ng2FileSelect
|
||||
[uploader]="uploader"
|
||||
/>
|
||||
<button mat-raised-button color="primary" (click)="file.click()" matTooltip="Import your .gns3p or .gns3project file" class="file-button">Choose file</button>
|
||||
<button mat-raised-button color="primary" (click)="file.click()" matTooltip="Import your .gns3p or .gns3project file" matTooltipClass="custom-tooltip" class="file-button">Choose file</button>
|
||||
<mat-form-field [ngClass]="{ empty: !isDeleteVisible }" class="file-name-form-field">
|
||||
<input
|
||||
matInput
|
||||
|
@ -53,16 +53,16 @@
|
||||
<ng-container matColumnDef="actions">
|
||||
<mat-header-cell *matHeaderCellDef> Actions </mat-header-cell>
|
||||
<mat-cell *matCellDef="let row" style="text-align: right">
|
||||
<button mat-icon-button matTooltip="Open project" (click)="open(row)" *ngIf="row.status == 'closed'">
|
||||
<button mat-icon-button matTooltip="Open project" matTooltipClass="custom-tooltip" (click)="open(row)" *ngIf="row.status == 'closed'">
|
||||
<mat-icon aria-label="Open project">play_arrow</mat-icon>
|
||||
</button>
|
||||
<button mat-icon-button matTooltip="Close project" (click)="close(row)" *ngIf="row.status == 'opened'">
|
||||
<button mat-icon-button matTooltip="Close project" matTooltipClass="custom-tooltip" (click)="close(row)" *ngIf="row.status == 'opened'">
|
||||
<mat-icon aria-label="Close project">pause</mat-icon>
|
||||
</button>
|
||||
<button mat-icon-button matTooltip="Duplicate project" (click)="duplicate(row)" *ngIf="row.status == 'closed'">
|
||||
<button mat-icon-button matTooltip="Duplicate project" matTooltipClass="custom-tooltip" (click)="duplicate(row)" *ngIf="row.status == 'closed'">
|
||||
<mat-icon aria-label="Duplicate project">filter_2</mat-icon>
|
||||
</button>
|
||||
<button mat-icon-button matTooltip="Delete project" (click)="delete(row)" *ngIf="row.status == 'closed'">
|
||||
<button mat-icon-button matTooltip="Delete project" matTooltipClass="custom-tooltip" (click)="delete(row)" *ngIf="row.status == 'closed'">
|
||||
<mat-icon aria-label="Delete project">delete</mat-icon>
|
||||
</button>
|
||||
</mat-cell>
|
||||
|
@ -10,9 +10,8 @@
|
||||
</mat-expansion-panel-header>
|
||||
|
||||
<div>
|
||||
<mat-checkbox [(ngModel)]="settings.crash_reports"
|
||||
>Send anonymous crash reports</mat-checkbox
|
||||
>
|
||||
<mat-checkbox [(ngModel)]="settings.crash_reports">Send anonymous crash reports</mat-checkbox><br/>
|
||||
<mat-checkbox [(ngModel)]="integrateLinksLabelsToLinks">Integrate link labels to links</mat-checkbox>
|
||||
</div>
|
||||
|
||||
<!-- <div>
|
||||
|
@ -3,6 +3,7 @@ import { SettingsService } from '../../services/settings.service';
|
||||
import { ToasterService } from '../../services/toaster.service';
|
||||
import { ConsoleService } from '../../services/settings/console.service';
|
||||
import { ThemeService } from '../../services/theme.service';
|
||||
import { MapSettingsService } from '../../services/mapsettings.service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-settings',
|
||||
@ -12,22 +13,26 @@ import { ThemeService } from '../../services/theme.service';
|
||||
export class SettingsComponent implements OnInit {
|
||||
settings = { ...SettingsService.DEFAULTS };
|
||||
consoleCommand: string;
|
||||
integrateLinksLabelsToLinks: boolean;
|
||||
|
||||
constructor(
|
||||
private settingsService: SettingsService,
|
||||
private toaster: ToasterService,
|
||||
private consoleService: ConsoleService,
|
||||
private themeService: ThemeService
|
||||
private themeService: ThemeService,
|
||||
public mapSettingsService: MapSettingsService
|
||||
) {}
|
||||
|
||||
ngOnInit() {
|
||||
this.settings = this.settingsService.getAll();
|
||||
this.consoleCommand = this.consoleService.command;
|
||||
this.integrateLinksLabelsToLinks = this.mapSettingsService.integrateLinkLabelsToLinks;
|
||||
}
|
||||
|
||||
save() {
|
||||
this.settingsService.setAll(this.settings);
|
||||
this.toaster.success('Settings have been saved.');
|
||||
this.mapSettingsService.toggleIntegrateInterfaceLabels(this.integrateLinksLabelsToLinks);
|
||||
}
|
||||
|
||||
setDarkMode(value: boolean) {
|
||||
|
@ -28,11 +28,11 @@
|
||||
<ng-container matColumnDef="actions">
|
||||
<mat-header-cell *matHeaderCellDef> Actions </mat-header-cell>
|
||||
<mat-cell *matCellDef="let row" style="text-align: right">
|
||||
<button mat-icon-button matTooltip="Restore snapshot" (click)="restoreSnapshot(row)">
|
||||
<button mat-icon-button matTooltip="Restore snapshot" matTooltipClass="custom-tooltip" (click)="restoreSnapshot(row)">
|
||||
<mat-icon aria-label="Restore snapshot">restore</mat-icon>
|
||||
</button>
|
||||
|
||||
<button mat-icon-button matTooltip="Delete snapshot" (click)="deleteSnapshot(row)">
|
||||
<button mat-icon-button matTooltip="Delete snapshot" matTooltipClass="custom-tooltip" (click)="deleteSnapshot(row)">
|
||||
<mat-icon aria-label="Delete snapshot">delete</mat-icon>
|
||||
</button>
|
||||
</mat-cell>
|
||||
|
@ -1 +1 @@
|
||||
<button matTooltip="Manage snapshots" mat-icon-button (click)="createSnapshotModal()"><mat-icon>snooze</mat-icon></button>
|
||||
<button matTooltip="Manage snapshots" matTooltipClass="custom-tooltip" mat-icon-button (click)="createSnapshotModal()"><mat-icon>snooze</mat-icon></button>
|
||||
|
@ -1,4 +1,4 @@
|
||||
<button class="addNode" matTooltip="Add a node" mat-icon-button [matMenuTriggerFor]="mainMenu">
|
||||
<button class="addNode" matTooltip="Add a node" matTooltipClass="custom-tooltip" mat-icon-button [matMenuTriggerFor]="mainMenu">
|
||||
<mat-icon>add_to_queue</mat-icon>
|
||||
</button>
|
||||
|
||||
|
@ -57,7 +57,7 @@ export class TopologySummaryComponent implements OnInit, OnDestroy {
|
||||
this.nodesDataSource.changes.subscribe((nodes: Node[]) => {
|
||||
this.nodes = nodes;
|
||||
this.nodes.forEach(n => {
|
||||
if (n.console_host === '0.0.0.0') {
|
||||
if (n.console_host === '0.0.0.0' || n.console_host === '0:0:0:0:0:0:0:0' || n.console_host === '::') {
|
||||
n.console_host = this.server.host;
|
||||
}
|
||||
});
|
||||
|
@ -1,4 +1,6 @@
|
||||
export interface Image {
|
||||
compression? : string;
|
||||
direct_download_url? : string;
|
||||
download_url: string;
|
||||
filename: string;
|
||||
filesize: any;
|
||||
@ -13,6 +15,9 @@ export interface Qemu {
|
||||
boot_priority: string;
|
||||
console_type: string;
|
||||
hda_disk_interface: string;
|
||||
hdb_disk_interface: string;
|
||||
hdc_disk_interface: string;
|
||||
hdd_disk_interface: string;
|
||||
kvm: string;
|
||||
ram: number;
|
||||
}
|
||||
@ -49,6 +54,7 @@ export interface Iou {
|
||||
|
||||
export interface Images {
|
||||
hda_disk_image: string;
|
||||
hdb_disk_image: string;
|
||||
}
|
||||
|
||||
export interface Version {
|
||||
|
@ -2,7 +2,7 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import {Router, NavigationEnd} from '@angular/router';
|
||||
import { environment } from '../../environments/environment';
|
||||
declare var ga:Function;
|
||||
declare var gtag:Function;
|
||||
|
||||
@Injectable()
|
||||
export class GoogleAnalyticsService {
|
||||
@ -11,8 +11,8 @@ export class GoogleAnalyticsService {
|
||||
if (!environment.production) return;
|
||||
router.events.subscribe(event => {
|
||||
if (event instanceof NavigationEnd) {
|
||||
ga('set', 'page', event.url);
|
||||
ga('send', 'pageview');
|
||||
gtag('set', 'page', event.url);
|
||||
gtag('send', 'pageview');
|
||||
}
|
||||
})
|
||||
}
|
||||
|
@ -55,8 +55,8 @@ export class LinkService {
|
||||
}
|
||||
|
||||
updateLink(server: Server, link: Link) {
|
||||
// link.x = Math.round(link.x);
|
||||
// link.y = Math.round(link.y);
|
||||
link.x = Math.round(link.x);
|
||||
link.y = Math.round(link.y);
|
||||
|
||||
return this.httpServer.put<Link>(server, `/projects/${link.project_id}/links/${link.link_id}`, link);
|
||||
}
|
||||
|
@ -7,14 +7,15 @@ export class MapSettingsService {
|
||||
public isTopologySummaryVisible: boolean = true;
|
||||
public isLogConsoleVisible: boolean = false;
|
||||
public isLayerNumberVisible: boolean = false;
|
||||
public interfaceLabels: Map<string, boolean> = new Map<string, boolean>();
|
||||
public logConsoleSubject = new Subject<boolean>();
|
||||
public mapRenderedEmitter = new EventEmitter<boolean>();
|
||||
|
||||
public showInterfaceLabels: boolean = true;
|
||||
public integrateLinkLabelsToLinks: boolean = true;
|
||||
|
||||
constructor() {
|
||||
this.isLayerNumberVisible = localStorage.getItem('layersVisibility') === 'true' ? true : false;
|
||||
if (localStorage.getItem('integrateLinkLabelsToLinks')) this.integrateLinkLabelsToLinks = localStorage.getItem('integrateLinkLabelsToLinks') === 'true' ? true : false;
|
||||
}
|
||||
|
||||
changeMapLockValue(value: boolean) {
|
||||
@ -33,7 +34,17 @@ export class MapSettingsService {
|
||||
this.isLayerNumberVisible = value;
|
||||
}
|
||||
|
||||
toggleShowInterfaceLabels(projectId: string, value: boolean) {
|
||||
this.interfaceLabels.set(projectId, value);
|
||||
toggleShowInterfaceLabels(value: boolean) {
|
||||
this.showInterfaceLabels = value;
|
||||
}
|
||||
|
||||
toggleIntegrateInterfaceLabels(value: boolean) {
|
||||
this.integrateLinkLabelsToLinks = value;
|
||||
localStorage.removeItem('integrateLinkLabelsToLinks');
|
||||
if (value) {
|
||||
localStorage.setItem('integrateLinkLabelsToLinks', 'true');
|
||||
} else {
|
||||
localStorage.setItem('integrateLinkLabelsToLinks', 'false');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -17,10 +17,7 @@ export class TemplateService {
|
||||
return this.httpServer.get<Template[]>(server, '/templates') as Observable<Template[]>;
|
||||
}
|
||||
|
||||
deleteTemplate(server: Server, templateId: string): Observable<boolean> {
|
||||
return this.httpServer.delete(server, `/templates/${templateId}`, { observe: 'body' }).map(response => {
|
||||
return true;
|
||||
})
|
||||
.catch((response) => { return Observable.throw(false)});
|
||||
deleteTemplate(server: Server, templateId: string): Observable<any> {
|
||||
return this.httpServer.delete(server, `/templates/${templateId}`, { observe: 'body' });
|
||||
}
|
||||
}
|
||||
|
@ -42,3 +42,8 @@ app-root {
|
||||
mat-menu-panel {
|
||||
min-height: 0px;
|
||||
}
|
||||
|
||||
.custom-tooltip {
|
||||
background-color: grey;
|
||||
color: #ffffff;
|
||||
}
|
||||
|
12
yarn.lock
12
yarn.lock
@ -7813,9 +7813,9 @@ lodash.values@^4.3.0:
|
||||
integrity sha1-o6bCsOvsxcLLocF+bmIP6BtT00c=
|
||||
|
||||
lodash@^4.0.0, lodash@^4.17.10, lodash@^4.17.11, lodash@^4.17.13, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.5, lodash@~4.17.10:
|
||||
version "4.17.15"
|
||||
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.15.tgz#b447f6670a0455bbfeedd11392eff330ea097548"
|
||||
integrity sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==
|
||||
version "4.17.19"
|
||||
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.19.tgz#e48ddedbe30b3321783c5b4301fbd353bc1e4a4b"
|
||||
integrity sha512-JNvd8XER9GQX0v2qJgsaN/mzFCNA5BRe/j8JN9d+tWyGLSodKQHKFicdwNYzWwI3wjRnaKPsGj1XkBjx/F96DQ==
|
||||
|
||||
log-symbols@^3.0.0:
|
||||
version "3.0.0"
|
||||
@ -13006,9 +13006,9 @@ xterm-addon-fit@^0.4.0:
|
||||
integrity sha512-p4BESuV/g2L6pZzFHpeNLLnep9mp/DkF3qrPglMiucSFtD8iJxtMufEoEJbN8LZwB4i+8PFpFvVuFrGOSpW05w==
|
||||
|
||||
xterm@^4.1.0:
|
||||
version "4.7.0"
|
||||
resolved "https://registry.yarnpkg.com/xterm/-/xterm-4.7.0.tgz#254485811146b03fbea10c911f7f68a99e1d3bfd"
|
||||
integrity sha512-UeH6U/1iknCBP94/AcKAFBeQz6ZicMugJHGXruTmsY8RcZt+mkx+vl8jLLOqNYweXdBbywCg2kK88WDKjcmSmg==
|
||||
version "4.8.1"
|
||||
resolved "https://registry.yarnpkg.com/xterm/-/xterm-4.8.1.tgz#155a1729a43e1a89b406524e22c5634339e39ca1"
|
||||
integrity sha512-ax91ny4tI5eklqIfH79OUSGE2PUX2rGbwONmB6DfqpyhSZO8/cf++sqiaMWEVCMjACyMfnISW7C3gGMoNvNolQ==
|
||||
|
||||
y18n@^3.2.0:
|
||||
version "3.2.1"
|
||||
|
Loading…
x
Reference in New Issue
Block a user