mirror of
https://github.com/GNS3/gns3-web-ui.git
synced 2025-06-24 17:15:22 +00:00
Compare commits
17 Commits
bugfix/150
...
v3.0.3
Author | SHA1 | Date | |
---|---|---|---|
67b597d8ec | |||
7fd37610ee | |||
c8ccf4d475 | |||
1a49bec3d4 | |||
64f9631946 | |||
b02fe4c751 | |||
84169a2c1e | |||
2a9ced5cbd | |||
7c7e20d95f | |||
e959a947cc | |||
7b633c29dd | |||
c24517d1f0 | |||
9043c5b97c | |||
2227d11932 | |||
2e581c4495 | |||
ee5b88e19a | |||
cddce63e2b |
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "gns3-web-ui",
|
||||
"version": "3.1.0.dev1",
|
||||
"version": "3.0.3",
|
||||
"author": {
|
||||
"name": "GNS3 Technology Inc.",
|
||||
"email": "developers@gns3.com"
|
||||
|
@ -142,7 +142,7 @@ export class ImportApplianceComponent implements OnInit {
|
||||
}
|
||||
this.template = template;
|
||||
|
||||
const url = this.getUploadPath(this.controller, template.template_type, name);
|
||||
const url = this.getUploadPath(this.controller, name);
|
||||
this.uploader.queue.forEach((elem) => (elem.url = url));
|
||||
const itemToUpload = this.uploader.queue[0];
|
||||
this.uploader.uploadItem(itemToUpload);
|
||||
@ -150,7 +150,7 @@ export class ImportApplianceComponent implements OnInit {
|
||||
fileReader.readAsText(file);
|
||||
}
|
||||
|
||||
private getUploadPath(controller:Controller , emulator: string, filename: string) {
|
||||
return `${controller.protocol}//${controller.host}:${controller.port}/${environment.current_version}/${emulator}/images/${filename}`;
|
||||
private getUploadPath(controller:Controller , filename: string) {
|
||||
return `${controller.protocol}//${controller.host}:${controller.port}/${environment.current_version}/images/upload/${filename}`;
|
||||
}
|
||||
}
|
||||
|
@ -132,7 +132,7 @@
|
||||
<mat-card [hidden]="!(!isLinuxPlatform || applianceToInstall.dynamips)">
|
||||
<div *ngIf="applianceToInstall.qemu">
|
||||
<div>
|
||||
Install required files
|
||||
Install the required files
|
||||
<button
|
||||
mat-icon-button
|
||||
matTooltip="Refresh list of images"
|
||||
@ -380,11 +380,11 @@
|
||||
{{ image.filename }}
|
||||
</span>
|
||||
<div>
|
||||
<span *ngIf="checkImageFromVersion(image.filename)"
|
||||
><mat-icon matTooltip="Ready to install" matTooltipClass="custom-tooltip">check</mat-icon>
|
||||
<span *ngIf="checkImageFromVersion(image.filename)">
|
||||
<mat-icon matTooltip="Ready to install" matTooltipClass="custom-tooltip">check</mat-icon>
|
||||
</span>
|
||||
<span *ngIf="!checkImageFromVersion(image.filename)"
|
||||
><mat-icon matTooltip="Missing" matTooltipClass="custom-tooltip">close</mat-icon>
|
||||
<span *ngIf="!checkImageFromVersion(image.filename)">
|
||||
<mat-icon matTooltip="Missing" matTooltipClass="custom-tooltip">close</mat-icon>
|
||||
</span>
|
||||
<input
|
||||
type="file"
|
||||
@ -407,7 +407,7 @@
|
||||
|
||||
<div *ngIf="applianceToInstall.iou">
|
||||
<div>
|
||||
Install required images
|
||||
Install the required images
|
||||
<button
|
||||
mat-icon-button
|
||||
matTooltip="Refresh list of images"
|
||||
@ -419,16 +419,16 @@
|
||||
<mat-list>
|
||||
<mat-list-item *ngFor="let image of applianceToInstall.images">
|
||||
<div class="list-item">
|
||||
<div>
|
||||
<span>
|
||||
{{ image.filename }}
|
||||
</div>
|
||||
</span>
|
||||
<span *ngIf="checkImageFromVersion(image.filename)">
|
||||
<mat-icon matTooltip="Ready to install" matTooltipClass="custom-tooltip">check</mat-icon>
|
||||
</span>
|
||||
<span *ngIf="!checkImageFromVersion(image.filename)">
|
||||
<mat-icon matTooltip="Missing" matTooltipClass="custom-tooltip">close</mat-icon>
|
||||
</span>
|
||||
<div>
|
||||
<span *ngIf="checkImageFromVersion(image.filename)"
|
||||
><mat-icon matTooltip="Ready to install" matTooltipClass="custom-tooltip">check</mat-icon>
|
||||
</span>
|
||||
<span *ngIf="!checkImageFromVersion(image.filename)"
|
||||
><mat-icon matTooltip="Missing" matTooltipClass="custom-tooltip">close</mat-icon>
|
||||
</span>
|
||||
<input
|
||||
type="file"
|
||||
class="non-visible"
|
||||
|
@ -266,7 +266,7 @@ export class NewTemplateDialogComponent implements OnInit {
|
||||
if (appliance.iou) emulator = 'iou';
|
||||
if (appliance.qemu) emulator = 'qemu';
|
||||
|
||||
const url = this.applianceService.getUploadPath(this.controller, emulator, fileName);
|
||||
const url = this.applianceService.getUploadPath(this.controller, fileName);
|
||||
this.uploader.queue.forEach((elem) => (elem.url = url));
|
||||
|
||||
const itemToUpload = this.uploader.queue[0];
|
||||
@ -341,8 +341,6 @@ export class NewTemplateDialogComponent implements OnInit {
|
||||
dialogRef.componentInstance.appliance = object;
|
||||
}
|
||||
|
||||
|
||||
|
||||
importImage(event, imageName) {
|
||||
this.computeChecksumMd5(event.target.files[0], false).then((output) => {
|
||||
let imageToInstall = this.applianceToInstall.images.filter((n) => n.filename === imageName)[0];
|
||||
@ -359,22 +357,21 @@ export class NewTemplateDialogComponent implements OnInit {
|
||||
The MD5 sum is ${output} and should be ${imageToInstall.md5sum}. Do you want to accept it at your own risks?`;
|
||||
dialogRef.afterClosed().subscribe((answer: boolean) => {
|
||||
if (answer) {
|
||||
this.importImageFile(event);
|
||||
this.importImageFile(event, imageName);
|
||||
this.openSnackBar()
|
||||
} else {
|
||||
this.uploaderImage.clearQueue();
|
||||
}
|
||||
});
|
||||
} else {
|
||||
this.importImageFile(event);
|
||||
this.importImageFile(event, imageName);
|
||||
this.openSnackBar()
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
importImageFile(event) {
|
||||
importImageFile(event, imageName) {
|
||||
let name = event.target.files[0].name.split('-')[0];
|
||||
let fileName = event.target.files[0].name;
|
||||
let file = event.target.files[0];
|
||||
let fileReader: FileReader = new FileReader();
|
||||
let emulator;
|
||||
@ -384,7 +381,7 @@ export class NewTemplateDialogComponent implements OnInit {
|
||||
if (this.applianceToInstall.dynamips) emulator = 'dynamips';
|
||||
if (this.applianceToInstall.iou) emulator = 'iou';
|
||||
|
||||
const url = this.applianceService.getUploadPath(this.controller, emulator, fileName);
|
||||
const url = this.applianceService.getUploadPath(this.controller, imageName);
|
||||
this.uploaderImage.queue.forEach((elem) => (elem.url = url));
|
||||
|
||||
const itemToUpload = this.uploaderImage.queue[0];
|
||||
|
@ -98,42 +98,25 @@ export class ProjectMapMenuComponent implements OnInit, OnDestroy {
|
||||
|
||||
private async saveImage(screenshotProperties: Screenshot) {
|
||||
if (screenshotProperties.filetype === 'png') {
|
||||
try {
|
||||
// Get the SVG element and clone it to avoid modifying the original
|
||||
const originalSvg = document.getElementsByTagName('svg')[0];
|
||||
const svgClone = originalSvg.cloneNode(true) as SVGElement;
|
||||
let splittedSvg = document.getElementsByTagName('svg')[0].outerHTML.split('image');
|
||||
let i = 1;
|
||||
|
||||
// Process any embedded images
|
||||
const images = svgClone.getElementsByTagName('image');
|
||||
for (let i = 0; i < images.length; i++) {
|
||||
const image = images[i];
|
||||
const href = image.getAttribute('href') || image.getAttribute('xlink:href');
|
||||
if (href) {
|
||||
const urlParts = href.split('/');
|
||||
const symbolId = urlParts[urlParts.length - 1];
|
||||
try {
|
||||
const rawSvg = await this.symbolService.raw(this.controller, symbolId).toPromise();
|
||||
if (rawSvg) {
|
||||
// Extract SVG content, fallback to raw content if parsing fails
|
||||
const svgContent = rawSvg.includes('-->') ?
|
||||
rawSvg.split('-->')[1].trim() :
|
||||
rawSvg.trim();
|
||||
image.outerHTML = svgContent;
|
||||
}
|
||||
} catch (err) {
|
||||
console.warn(`Failed to process embedded image: ${symbolId}`, err);
|
||||
}
|
||||
}
|
||||
}
|
||||
while (i < splittedSvg.length) {
|
||||
let splittedImage = splittedSvg[i].split('"');
|
||||
let splittedUrl = splittedImage[1].split('/');
|
||||
|
||||
// Create a temporary container and save as PNG
|
||||
const container = document.createElement('div');
|
||||
container.appendChild(svgClone);
|
||||
svg.saveSvgAsPng(svgClone, `${screenshotProperties.name}.png`);
|
||||
} catch (err) {
|
||||
console.error('Failed to save PNG:', err);
|
||||
throw err;
|
||||
let elem = await this.symbolService.raw(this.controller, splittedUrl[7]).toPromise();
|
||||
let splittedElement = elem.split('-->');
|
||||
splittedSvg[i] = splittedElement[1].substring(2);
|
||||
i += 2;
|
||||
}
|
||||
let svgString = splittedSvg.join();
|
||||
|
||||
let placeholder = document.createElement('div');
|
||||
placeholder.innerHTML = svgString;
|
||||
let element = placeholder.firstChild;
|
||||
|
||||
svg.saveSvgAsPng(element, `${screenshotProperties.name}.png`);
|
||||
} else {
|
||||
var svg_el = select('svg').attr('version', 1.1).attr('xmlns', 'http://www.w3.org/2000/svg').node();
|
||||
downloadSvg(select('svg').node(), `${screenshotProperties.name}`);
|
||||
|
@ -25,7 +25,7 @@ export class ScreenshotDialogComponent implements OnInit {
|
||||
this.nameForm = this.formBuilder.group({
|
||||
screenshotName: new UntypedFormControl(`screenshot-${Date.now()}`, [Validators.required]),
|
||||
});
|
||||
this.isPngAvailable = true;
|
||||
this.isPngAvailable = this.electronService.isWindows || this.deviceService.getDeviceInfo().os === 'Windows';
|
||||
}
|
||||
|
||||
ngOnInit() {}
|
||||
|
@ -21,8 +21,18 @@ export class ReadmeEditorComponent implements OnInit {
|
||||
) {}
|
||||
|
||||
ngOnInit() {
|
||||
this.projectService.getReadmeFile(this.controller, this.project.project_id).subscribe(file => {
|
||||
if (file) this.markdown = file;
|
||||
this.projectService.getReadmeFile(this.controller, this.project.project_id).subscribe({
|
||||
next: (file) => {
|
||||
if (file) {
|
||||
this.markdown = file;
|
||||
}
|
||||
},
|
||||
error: (err) => {
|
||||
if (err.status === 404) {
|
||||
// File doesn't exist yet, which is fine
|
||||
this.markdown = '';
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -3,7 +3,7 @@ import { Injectable } from '@angular/core';
|
||||
@Injectable()
|
||||
export class ProjectNameValidator {
|
||||
get(projectName) {
|
||||
var pattern = new RegExp(/[~`!#$%\^&*+=\[\]\\';,/{}|\\":<>\?]/);
|
||||
var pattern = new RegExp(/[~`!#$%\^&*+=\[\]\\';,/{}|\\"<>\?]/);
|
||||
|
||||
if (!pattern.test(projectName.value)) {
|
||||
return null;
|
||||
|
@ -11,6 +11,7 @@
|
||||
* Author: Sylvain MATHIEU, Elise LEBEAU
|
||||
*/
|
||||
import {Component, OnInit, QueryList, ViewChild, ViewChildren} from '@angular/core';
|
||||
import {Location} from '@angular/common';
|
||||
import {ActivatedRoute, Router} from "@angular/router";
|
||||
import {Controller} from "@models/controller";
|
||||
import {MatSort} from "@angular/material/sort";
|
||||
@ -49,7 +50,8 @@ export class UserManagementComponent implements OnInit {
|
||||
private progressService: ProgressService,
|
||||
private controllerService: ControllerService,
|
||||
public dialog: MatDialog,
|
||||
private toasterService: ToasterService) { }
|
||||
private toasterService: ToasterService,
|
||||
private location: Location) { }
|
||||
|
||||
ngOnInit() {
|
||||
const controllerId = this.route.parent.snapshot.paramMap.get('controller_id');
|
||||
@ -88,6 +90,8 @@ export class UserManagementComponent implements OnInit {
|
||||
},
|
||||
(error) => {
|
||||
this.progressService.setError(error);
|
||||
this.toasterService.error(`Cannot open the user management page`);
|
||||
this.location.back();
|
||||
}
|
||||
);
|
||||
}
|
||||
|
@ -17,8 +17,8 @@ export class ApplianceService {
|
||||
return this.httpController.get<Appliance>(controller, url) as Observable<Appliance>;
|
||||
}
|
||||
|
||||
getUploadPath(controller:Controller , emulator: string, filename: string) {
|
||||
return `${controller.protocol}//${controller.host}:${controller.port}/${environment.current_version}/images/upload/${filename}?allow_raw_image=true`;
|
||||
getUploadPath(controller:Controller, filename: string) {
|
||||
return `${controller.protocol}//${controller.host}:${controller.port}/${environment.current_version}/images/upload/${filename}`;
|
||||
}
|
||||
|
||||
updateAppliances(controller:Controller ): Observable<Appliance[]> {
|
||||
|
@ -14,10 +14,6 @@ export class ComputeService {
|
||||
return this.httpController.get<Compute[]>(controller, '/computes') as Observable<Compute[]>;
|
||||
}
|
||||
|
||||
getUploadPath(controller:Controller , emulator: string, filename: string) {
|
||||
return `${controller.protocol}//${controller.host}:${controller.port}/${environment.current_version}/${emulator}/images/${filename}`;
|
||||
}
|
||||
|
||||
getStatistics(controller:Controller ): Observable<ComputeStatistics[]> {
|
||||
return this.httpController.get(controller, `/statistics`);
|
||||
}
|
||||
|
@ -20,10 +20,6 @@ export class ImageManagerService {
|
||||
return `${controller.protocol}//${controller.host}:${controller.port}/${environment.current_version}/images/upload/${image_path}?install_appliances=${install_appliance}`;
|
||||
}
|
||||
|
||||
getUploadPath(controller:Controller , emulator: string, filename: string) {
|
||||
return `${controller.protocol}//${controller.host}:${controller.port}/${environment.current_version}/images/upload/${filename}`;
|
||||
}
|
||||
|
||||
uploadedImage(controller :Controller, install_appliance, image_path, flie){
|
||||
return this.httpController.post<Image[]>(controller, `/images/upload/${image_path}?install_appliances=${install_appliance}`,flie) as Observable<Image[]>;
|
||||
}
|
||||
|
Reference in New Issue
Block a user