Merge branch 'master' into fix-for-tests

This commit is contained in:
piotrpekala7 2020-08-25 15:46:57 +02:00
commit 9dfd58b14b
46 changed files with 363 additions and 148 deletions

View File

@ -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",

View File

@ -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

View File

@ -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) { }
}

View File

@ -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();
}

View File

@ -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
}

View File

@ -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();

View File

@ -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 });
}
}

View File

@ -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);

View File

@ -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>

View File

@ -8,7 +8,7 @@
button {
background-color: #0097a7;
margin-top: 10px;
margin-top: 2px;
border: none;
outline: none;
padding: 5px;

View File

@ -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);

View File

@ -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>

View 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);
}
}

View File

@ -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;

View File

@ -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>

View File

@ -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.`);
});
}
});

View File

@ -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>

View File

@ -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>

View File

@ -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;

View File

@ -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") {

View File

@ -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>

View File

@ -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 {

View File

@ -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);

View File

@ -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>

View File

@ -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>

View File

@ -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"

View File

@ -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()"

View File

@ -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>

View File

@ -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
});

View File

@ -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>

View File

@ -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

View File

@ -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>

View File

@ -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>

View File

@ -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) {

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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;
}
});

View File

@ -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 {

View File

@ -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');
}
})
}

View File

@ -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);
}

View File

@ -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');
}
}
}

View File

@ -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' });
}
}

View File

@ -42,3 +42,8 @@ app-root {
mat-menu-panel {
min-height: 0px;
}
.custom-tooltip {
background-color: grey;
color: #ffffff;
}

View File

@ -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"