Updates from main branch (#1227)

* Update ReleaseNotes.txt

* Release 2.2.22

* Updating dependencies

* Update package.json

* Update project-map.component.ts

* Updating dependencies

* Update main.yml

* Bump postcss from 7.0.35 to 7.0.36

Bumps [postcss](https://github.com/postcss/postcss) from 7.0.35 to 7.0.36.
- [Release notes](https://github.com/postcss/postcss/releases)
- [Changelog](https://github.com/postcss/postcss/blob/main/CHANGELOG.md)
- [Commits](https://github.com/postcss/postcss/compare/7.0.35...7.0.36)

---
updated-dependencies:
- dependency-name: postcss
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>

* Bump ws from 6.2.1 to 6.2.2

Bumps [ws](https://github.com/websockets/ws) from 6.2.1 to 6.2.2.
- [Release notes](https://github.com/websockets/ws/releases)
- [Commits](https://github.com/websockets/ws/compare/6.2.1...6.2.2)

---
updated-dependencies:
- dependency-name: ws
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>

* fix: package.json & yarn.lock to reduce vulnerabilities

The following vulnerabilities are fixed with an upgrade:
- https://snyk.io/vuln/SNYK-JS-JSZIP-1251497

* Updating packages to latest versions

* Release 2.2.23

* Updating dependencies

* Bump tar from 6.1.0 to 6.1.6

Bumps [tar](https://github.com/npm/node-tar) from 6.1.0 to 6.1.6.
- [Release notes](https://github.com/npm/node-tar/releases)
- [Changelog](https://github.com/npm/node-tar/blob/main/CHANGELOG.md)
- [Commits](https://github.com/npm/node-tar/compare/v6.1.0...v6.1.6)

---
updated-dependencies:
- dependency-name: tar
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>

* Bump url-parse from 1.5.1 to 1.5.3

Bumps [url-parse](https://github.com/unshiftio/url-parse) from 1.5.1 to 1.5.3.
- [Release notes](https://github.com/unshiftio/url-parse/releases)
- [Commits](https://github.com/unshiftio/url-parse/compare/1.5.1...1.5.3)

---
updated-dependencies:
- dependency-name: url-parse
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>

* Updating dependencies

* Removing vulnerabilities

* Updating angular-devkit/build-angular

* Update yarn.lock

* Fix for https://github.com/GNS3/gns3-web-ui/issues/1184

* Release 2.2.24

* Option to access system status from servers page

* Updating dependencies

* Release 2.2.25

* Update package.json

* Bump nth-check from 2.0.0 to 2.0.1

Bumps [nth-check](https://github.com/fb55/nth-check) from 2.0.0 to 2.0.1.
- [Release notes](https://github.com/fb55/nth-check/releases)
- [Commits](https://github.com/fb55/nth-check/compare/v2.0.0...v2.0.1)

---
updated-dependencies:
- dependency-name: nth-check
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>

* Fix for theming

* Update template.component.ts

* Updating dependencies

* Update yarn.lock

* Update yarn.lock

* Update yarn.lock

* Release 2.2.26

* Update package.json

* show upload file progress when uploading qemu template.

* fix progress bar incorrect when upload file twice

* Updating packages

* Updating packages

* Update package.json

* Update yarn.lock

* updating packages

* Updating angular material

* Fix for error with component factory after migration to angular v13

* Update yarn.lock

* Update .gitignore

* Fix for builds on github

* Removing electron builder

* Removing sentry/cli

* Updating dependencies

* Release web UI 2.2.27

* Reverting updates

* Angular version set to 12.2.12

* Updating snyk version

* Update configurator-docker.component.ts

* Update new-template-dialog.component.html

* Mouse pointer #1219

* Update topology-summary.component.ts

* Update project-map.component.ts

* Update project-map.component.ts

* Release web UI 2.2.28

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: snyk-bot <snyk-bot@snyk.io>
Co-authored-by: potats0 <potatso>
This commit is contained in:
piotrpekala7
2021-12-28 05:39:15 -08:00
committed by GitHub
parent 6353207d40
commit 8b177013d1
26 changed files with 3255 additions and 4328 deletions

File diff suppressed because one or more lines are too long

View File

@ -14,10 +14,10 @@ jobs:
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v2
- name: Setup node 12 - name: Setup node 14
uses: actions/setup-node@v2 uses: actions/setup-node@v2
with: with:
node-version: 12.x node-version: 14.x
- uses: c-hive/gha-yarn-cache@v1 - uses: c-hive/gha-yarn-cache@v1
- name: Install JS dependencies - name: Install JS dependencies
run: yarn install run: yarn install

1
.gitignore vendored
View File

@ -1,6 +1,7 @@
# See http://help.github.com/ignore-files/ for more about ignoring files. # See http://help.github.com/ignore-files/ for more about ignoring files.
# compiled output # compiled output
/.angular
/dist /dist
/tmp /tmp
/out-tsc /out-tsc

View File

@ -47,10 +47,11 @@
"scripts": [], "scripts": [],
"vendorChunk": true, "vendorChunk": true,
"extractLicenses": false, "extractLicenses": false,
"buildOptimizer": false, "buildOptimizer": true,
"sourceMap": true, "sourceMap": true,
"optimization": false, "optimization": false,
"namedChunks": true "namedChunks": true,
"aot": true
}, },
"configurations": { "configurations": {
"production": { "production": {
@ -176,6 +177,7 @@
"src/styles.scss", "src/styles.scss",
"src/theme.scss" "src/theme.scss"
], ],
"sourceMap": false,
"assets": [ "assets": [
"src/assets", "src/assets",
"src/favicon.ico" "src/favicon.ico"

View File

@ -36,31 +36,33 @@
"generate-licenses-file": "yarn license-checker --production --csv --out licenses.csv", "generate-licenses-file": "yarn license-checker --production --csv --out licenses.csv",
"prebuildforelectron": "node set-variables-in-env.js --set src/environments/environment.electron.prod.ts", "prebuildforelectron": "node set-variables-in-env.js --set src/environments/environment.electron.prod.ts",
"postbuildforelectron": "node set-variables-in-env.js --unset src/environments/environment.electron.prod.ts", "postbuildforelectron": "node set-variables-in-env.js --unset src/environments/environment.electron.prod.ts",
"postinstall": "ngcc --properties es5 browser module main --first-only --create-ivy-entry-points && ngcc --properties es2015 browser module main --first-only --create-ivy-entry-points",
"snyk-protect": "snyk protect", "snyk-protect": "snyk protect",
"prepare": "yarn run snyk-protect" "prepare": "yarn run snyk-protect"
}, },
"private": true, "private": true,
"dependencies": { "dependencies": {
"@angular/animations": "^12.0.2", "@angular/animations": "^12.2.12",
"@angular/cdk": "^12.0.2", "@angular/cdk": "^12.2.12",
"@angular/common": "^12.0.2", "@angular/common": "^12.2.12",
"@angular/compiler": "^12.0.2", "@angular/compiler": "^12.2.12",
"@angular/core": "^12.0.2", "@angular/core": "^12.2.12",
"@angular/forms": "^12.0.2", "@angular/forms": "^12.2.12",
"@angular/material": "^12.0.2", "@angular/material": "^12.2.12",
"@angular/platform-browser": "^12.0.2", "@angular/platform-browser": "^12.2.12",
"@angular/platform-browser-dynamic": "^12.0.2", "@angular/platform-browser-dynamic": "^12.2.12",
"@angular/router": "^12.0.2", "@angular/router": "^12.2.12",
"@sentry/browser": "^6.3.6", "@sentry/browser": "^6.14.1",
"@types/jest": "^26.0.23", "@types/jest": "^27.0.2",
"@types/mocha": "^8.2.2", "@types/mocha": "^9.0.0",
"@types/react": "^17.0.5", "@types/react": "^17.0.34",
"@types/react-dom": "^17.0.3", "@types/react-dom": "^17.0.11",
"angular-draggable-droppable": "^4.6.0", "angular-draggable-droppable": "^5.0.0",
"angular-resizable-element": "^3.3.5", "angular-resizable-element": "^3.4.0",
"bootstrap": "^5.0.0", "bootstrap": "^5.1.3",
"command-exists": "^1.2.9", "command-exists": "^1.2.9",
"core-js": "^3.12.1", "core-js": "^3.19.1",
"css-tree": "^1.1.3",
"d3-ng2-service": "^2.2.0", "d3-ng2-service": "^2.2.0",
"eev": "^0.1.5", "eev": "^0.1.5",
"ini": "^2.0.0", "ini": "^2.0.0",
@ -70,61 +72,61 @@
"ng-circle-progress": "^1.6.0", "ng-circle-progress": "^1.6.0",
"ng2-file-upload": "^1.4.0", "ng2-file-upload": "^1.4.0",
"ngx-childprocess": "^0.0.6", "ngx-childprocess": "^0.0.6",
"ngx-device-detector": "^2.0.9", "ngx-device-detector": "^2.1.1",
"ngx-electron": "^2.2.0", "ngx-electron": "^2.2.0",
"node-fetch": "^2.6.1", "node-fetch": "^3.0.0",
"notosans-fontface": "1.2.2", "notosans-fontface": "1.2.2",
"prettier-plugin-organize-imports": "^2.0.0", "prettier-plugin-organize-imports": "^2.3.4",
"rxjs": "^6.5.3", "rxjs": "^6.6.7",
"rxjs-compat": "^6.5.3", "rxjs-compat": "^6.6.7",
"save-svg-as-png": "^1.4.17", "save-svg-as-png": "^1.4.17",
"snyk": "^1.589.0", "snyk": "^1.780.0",
"spark-md5": "^3.0.1", "spark-md5": "^3.0.2",
"svg-crowbar": "^0.6.5", "svg-crowbar": "^0.7.0",
"tree-kill": "^1.2.2", "tree-kill": "^1.2.2",
"tslib": "^2.2.0", "tslib": "^2.3.1",
"typeface-roboto": "^1.1.13", "typeface-roboto": "^1.1.13",
"xterm": "^4.11.0", "xterm": "^4.15.0",
"xterm-addon-attach": "^0.6.0", "xterm-addon-attach": "^0.6.0",
"xterm-addon-fit": "^0.5.0", "xterm-addon-fit": "^0.5.0",
"yargs": "^17.0.1", "yargs": "^17.2.1",
"zone.js": "~0.11.4" "zone.js": "~0.11.4"
}, },
"devDependencies": { "devDependencies": {
"@angular-devkit/build-angular": "^12.0.2", "@angular-devkit/build-angular": "^12.2.12",
"@angular/cli": "^12.0.2", "@angular/cli": "^12.2.12",
"@angular/compiler-cli": "^12.0.2", "@angular/compiler-cli": "^12.2.12",
"@angular/language-service": "^12.0.2", "@angular/language-service": "^12.2.12",
"@sentry/cli": "^1.64.2", "@sentry/cli": "^1.71.0",
"@sentry/electron": "^2.4.1", "@sentry/electron": "^2.5.4",
"@types/jasmine": "^3.7.1", "@types/jasmine": "^3.10.2",
"@types/jasminewd2": "^2.0.9", "@types/jasminewd2": "^2.0.10",
"@types/node": "15.6.1", "@types/node": "16.11.6",
"codelyzer": "^6.0.2", "codelyzer": "^6.0.2",
"electron": "^13.0.1", "electron": "^13.2.2",
"electron-builder": "22.10.5", "electron-builder": "^22.9.1",
"file-loader": "^6.2.0", "file-loader": "^6.2.0",
"jasmine-core": "~3.7.1", "jasmine-core": "~3.10.1",
"jasmine-spec-reporter": "~7.0.0", "jasmine-spec-reporter": "~7.0.0",
"jquery": "^3.6.0", "jquery": "^3.6.0",
"karma": "^6.3.2", "karma": "^6.3.8",
"karma-chrome-launcher": "~3.1.0", "karma-chrome-launcher": "~3.1.0",
"karma-cli": "^2.0.0", "karma-cli": "^2.0.0",
"karma-coverage-istanbul-reporter": "~3.0.3", "karma-coverage-istanbul-reporter": "~3.0.3",
"karma-jasmine": "~4.0.1", "karma-jasmine": "~4.0.1",
"karma-jasmine-html-reporter": "^1.6.0", "karma-jasmine-html-reporter": "^1.7.0",
"license-checker": "^25.0.1", "license-checker": "^25.0.1",
"popper.js": "^1.16.1", "popper.js": "^1.16.1",
"prettier": "^2.3.0", "prettier": "^2.4.1",
"protractor": "^7.0.0", "protractor": "^7.0.0",
"replace": "^1.2.1", "replace": "^1.2.1",
"rxjs-tslint": "^0.1.8", "rxjs-tslint": "^0.1.8",
"ts-mockito": "^2.6.1", "ts-mockito": "^2.6.1",
"ts-node": "~10.0.0", "ts-node": "~10.4.0",
"tslint": "^6.1.3", "tslint": "^6.1.3",
"tslint-config-prettier": "^1.18.0", "tslint-config-prettier": "^1.18.0",
"typescript": "4.2.4", "typescript": "4.2.3",
"webpack": "5.38.0", "webpack": "5.62.1",
"yarn-upgrade-all": "^0.5.4" "yarn-upgrade-all": "^0.5.4"
}, },
"greenkeeper": { "greenkeeper": {

View File

@ -1,6 +1,9 @@
GNS3 WebUI is web implementation of user interface for GNS3 software. GNS3 WebUI is web implementation of user interface for GNS3 software.
Current version: 2.2.19 Current version: 2.2.24
Bug Fixes & enhancements
- security fixes
Current version: 2020.4.0-beta.1 Current version: 2020.4.0-beta.1

View File

@ -87,6 +87,7 @@ export class TextEditorComponent implements OnInit, OnDestroy {
`scale(${this.mapScaleService.getScale()})` `scale(${this.mapScaleService.getScale()})`
); );
this.temporaryTextElement.nativeElement.focus(); this.temporaryTextElement.nativeElement.focus();
document.documentElement.style.cursor = "default";
let textListener = () => { let textListener = () => {
this.drawingsEventSource.textAdded.emit( this.drawingsEventSource.textAdded.emit(

View File

@ -103,6 +103,9 @@
placeholder="Please enter name" placeholder="Please enter name"
/> />
</mat-form-field> </mat-form-field>
<div *ngIf="uploadedFile">
<mat-progress-bar mode="determinate" [value]="uploadProgress" aria-valuemin="0" aria-valuemax="100"></mat-progress-bar>
</div>
</div> </div>
</form> </form>
</mat-step> </mat-step>

View File

@ -32,6 +32,8 @@ export class AddQemuVmTemplateComponent implements OnInit {
chosenImage: string = ''; chosenImage: string = '';
qemuTemplate: QemuTemplate; qemuTemplate: QemuTemplate;
uploader: FileUploader; uploader: FileUploader;
uploadedFile: boolean = false;
uploadProgress: number = 0;
nameForm: FormGroup; nameForm: FormGroup;
memoryForm: FormGroup; memoryForm: FormGroup;
@ -83,6 +85,9 @@ export class AddQemuVmTemplateComponent implements OnInit {
}); });
this.toasterService.success('Image uploaded'); this.toasterService.success('Image uploaded');
}; };
this.uploader.onProgressItem = (progress: any) => {
this.uploadProgress = progress['progress'];
};
const server_id = this.route.snapshot.paramMap.get('server_id'); const server_id = this.route.snapshot.paramMap.get('server_id');
this.serverService.get(parseInt(server_id, 10)).then((server: Server) => { this.serverService.get(parseInt(server_id, 10)).then((server: Server) => {
@ -116,6 +121,7 @@ export class AddQemuVmTemplateComponent implements OnInit {
} }
uploadImageFile(event) { uploadImageFile(event) {
this.uploadedFile = true;
let name = event.target.files[0].name; let name = event.target.files[0].name;
this.diskForm.controls['fileName'].setValue(name); this.diskForm.controls['fileName'].setValue(name);

View File

@ -188,6 +188,38 @@
</div> </div>
</div> </div>
<div class="list-item-inside" *ngIf="version.images.bios_image">
<span>
{{ version.images.bios_image }}
</span>
<div>
<span *ngIf="checkImageFromVersion(version.images.bios_image)"
><mat-icon matTooltip="Ready to install" matTooltipClass="custom-tooltip">check</mat-icon></span
>
<span *ngIf="!checkImageFromVersion(version.images.bios_image)"
><mat-icon matTooltip="Missing" matTooltipClass="custom-tooltip">close</mat-icon></span
>
<input
type="file"
class="non-visible"
#fileBios
(change)="importImage($event, version.images.bios_image)"
ng2FileSelect
[uploader]="uploaderImage"
/>
<button class="button" mat-raised-button (click)="fileBios.click()">Import</button>
<button
class="button"
mat-raised-button
(click)="downloadImageFromVersion(version.images.bios_image)"
>
Download
</button>
</div>
</div>
<div class="list-item-inside" *ngIf="version.images.hda_disk_image"> <div class="list-item-inside" *ngIf="version.images.hda_disk_image">
<span> <span>
{{ version.images.hda_disk_image }} {{ version.images.hda_disk_image }}

View File

@ -43,7 +43,7 @@ export class ConfiguratorDialogDockerComponent implements OnInit {
adapter: new FormControl('', Validators.required), adapter: new FormControl('', Validators.required),
memory: new FormControl('', nonNegativeValidator.get), memory: new FormControl('', nonNegativeValidator.get),
cpus: new FormControl('', nonNegativeValidator.get), cpus: new FormControl('', nonNegativeValidator.get),
startCommand: new FormControl('', Validators.required), startCommand: new FormControl(''),
consoleHttpPort: new FormControl('', Validators.required), consoleHttpPort: new FormControl('', Validators.required),
consoleHttpPath: new FormControl('', Validators.required) consoleHttpPath: new FormControl('', Validators.required)
}); });

View File

@ -104,6 +104,13 @@ export class ProjectMapMenuComponent implements OnInit, OnDestroy {
} }
public addDrawing(selectedObject: string) { public addDrawing(selectedObject: string) {
if ((selectedObject === 'rectangle' && this.drawTools.isRectangleChosen) || (selectedObject === 'ellipse' && this.drawTools.isEllipseChosen) ||
(selectedObject === 'line' && this.drawTools.isLineChosen) || (selectedObject === 'text' && this.drawTools.isTextChosen)) {
document.documentElement.style.cursor = "default";
} else {
document.documentElement.style.cursor = "crosshair";
}
switch (selectedObject) { switch (selectedObject) {
case 'rectangle': case 'rectangle':
this.drawTools.isTextChosen = false; this.drawTools.isTextChosen = false;
@ -140,6 +147,8 @@ export class ProjectMapMenuComponent implements OnInit, OnDestroy {
} }
public resetDrawToolChoice() { public resetDrawToolChoice() {
document.documentElement.style.cursor = "default";
this.drawTools.isRectangleChosen = false; this.drawTools.isRectangleChosen = false;
this.drawTools.isEllipseChosen = false; this.drawTools.isEllipseChosen = false;
this.drawTools.isLineChosen = false; this.drawTools.isLineChosen = false;

View File

@ -333,7 +333,6 @@ export class ProjectMapComponent implements OnInit, OnDestroy {
this.toggleShowTopologySummary(this.mapSettingsService.isTopologySummaryVisible); this.toggleShowTopologySummary(this.mapSettingsService.isTopologySummaryVisible);
this.recentlyOpenedProjectService.setServerId(this.server.id.toString()); this.recentlyOpenedProjectService.setServerId(this.server.id.toString());
this.recentlyOpenedProjectService.setProjectId(this.project.project_id);
if (this.project.status === 'opened') { if (this.project.status === 'opened') {
return new Observable<Project>((observer) => { return new Observable<Project>((observer) => {
@ -423,12 +422,22 @@ export class ProjectMapComponent implements OnInit, OnDestroy {
this.toasterService.success('Node has been deleted'); this.toasterService.success('Node has been deleted');
}); });
}); });
}
selected
.filter((item) => item instanceof MapDrawing)
.forEach((item: MapDrawing) => {
const drawing = this.mapDrawingToDrawing.convert(item);
this.drawingService.delete(this.server, drawing).subscribe((data) => {
this.toasterService.success('Drawing has been deleted');
});
});
}
}); });
} }
onProjectLoad(project: Project) { onProjectLoad(project: Project) {
this.readonly = this.projectService.isReadOnly(project); this.readonly = this.projectService.isReadOnly(project);
this.recentlyOpenedProjectService.setProjectId(this.project.project_id);
const subscription = this.projectService const subscription = this.projectService
.nodes(this.server, project.project_id) .nodes(this.server, project.project_id)
@ -476,27 +485,27 @@ export class ProjectMapComponent implements OnInit, OnDestroy {
const onLinkContextMenu = this.linkWidget.onContextMenu.subscribe((eventLink: LinkContextMenu) => { const onLinkContextMenu = this.linkWidget.onContextMenu.subscribe((eventLink: LinkContextMenu) => {
const link = this.mapLinkToLink.convert(eventLink.link); const link = this.mapLinkToLink.convert(eventLink.link);
this.contextMenu.openMenuForListOfElements([], [], [], [link], eventLink.event.screenY - 60, eventLink.event.screenX); this.contextMenu.openMenuForListOfElements([], [], [], [link], eventLink.event.pageY, eventLink.event.pageX);
}); });
const onEthernetLinkContextMenu = this.ethernetLinkWidget.onContextMenu.subscribe((eventLink: LinkContextMenu) => { const onEthernetLinkContextMenu = this.ethernetLinkWidget.onContextMenu.subscribe((eventLink: LinkContextMenu) => {
const link = this.mapLinkToLink.convert(eventLink.link); const link = this.mapLinkToLink.convert(eventLink.link);
this.contextMenu.openMenuForListOfElements([], [], [], [link], eventLink.event.screenY - 60, eventLink.event.screenX); this.contextMenu.openMenuForListOfElements([], [], [], [link], eventLink.event.pageY, eventLink.event.pageX);
}); });
const onSerialLinkContextMenu = this.serialLinkWidget.onContextMenu.subscribe((eventLink: LinkContextMenu) => { const onSerialLinkContextMenu = this.serialLinkWidget.onContextMenu.subscribe((eventLink: LinkContextMenu) => {
const link = this.mapLinkToLink.convert(eventLink.link); const link = this.mapLinkToLink.convert(eventLink.link);
this.contextMenu.openMenuForListOfElements([], [], [], [link], eventLink.event.screenY - 60, eventLink.event.screenX); this.contextMenu.openMenuForListOfElements([], [], [], [link], eventLink.event.pageY, eventLink.event.pageX);
}); });
const onNodeContextMenu = this.nodeWidget.onContextMenu.subscribe((eventNode: NodeContextMenu) => { const onNodeContextMenu = this.nodeWidget.onContextMenu.subscribe((eventNode: NodeContextMenu) => {
const node = this.mapNodeToNode.convert(eventNode.node); const node = this.mapNodeToNode.convert(eventNode.node);
this.contextMenu.openMenuForNode(node, eventNode.event.screenY - 60, eventNode.event.screenX); this.contextMenu.openMenuForNode(node, eventNode.event.pageY, eventNode.event.pageX);
}); });
const onDrawingContextMenu = this.drawingsWidget.onContextMenu.subscribe((eventDrawing: DrawingContextMenu) => { const onDrawingContextMenu = this.drawingsWidget.onContextMenu.subscribe((eventDrawing: DrawingContextMenu) => {
const drawing = this.mapDrawingToDrawing.convert(eventDrawing.drawing); const drawing = this.mapDrawingToDrawing.convert(eventDrawing.drawing);
this.contextMenu.openMenuForDrawing(drawing, eventDrawing.event.screenY - 60, eventDrawing.event.screenX); this.contextMenu.openMenuForDrawing(drawing, eventDrawing.event.pageY, eventDrawing.event.pageX);
}); });
const onLabelContextMenu = this.labelWidget.onContextMenu.subscribe((eventLabel: LabelContextMenu) => { const onLabelContextMenu = this.labelWidget.onContextMenu.subscribe((eventLabel: LabelContextMenu) => {
@ -512,8 +521,8 @@ export class ProjectMapComponent implements OnInit, OnDestroy {
this.contextMenu.openMenuForInterfaceLabel( this.contextMenu.openMenuForInterfaceLabel(
linkNode, linkNode,
link, link,
eventInterfaceLabel.event.screenY - 60, eventInterfaceLabel.event.pageY,
eventInterfaceLabel.event.screenX eventInterfaceLabel.event.pageX
); );
} }
); );

View File

@ -2,6 +2,7 @@
<div class="default-header"> <div class="default-header">
<div class="row"> <div class="row">
<h1 class="col">Projects</h1> <h1 class="col">Projects</h1>
<button class="col" mat-raised-button (click)="goToSystemStatus()" class="add-button">Go to system status</button>
<button class="col" mat-raised-button (click)="goToPreferences()" class="add-button">Go to preferences</button> <button class="col" mat-raised-button (click)="goToPreferences()" class="add-button">Go to preferences</button>
<button class="col" mat-raised-button color="primary" (click)="addBlankProject()" class="add-button"> <button class="col" mat-raised-button color="primary" (click)="addBlankProject()" class="add-button">
Add blank project Add blank project

View File

@ -71,6 +71,12 @@ export class ProjectsComponent implements OnInit {
.catch((error) => this.toasterService.error('Cannot navigate to the preferences')); .catch((error) => this.toasterService.error('Cannot navigate to the preferences'));
} }
goToSystemStatus() {
this.router
.navigate(['/server', this.server.id, 'systemstatus'])
.catch((error) => this.toasterService.error('Cannot navigate to the system status'));
}
refresh() { refresh() {
this.projectService.list(this.server).subscribe( this.projectService.list(this.server).subscribe(
(projects: Project[]) => { (projects: Project[]) => {

View File

@ -13,6 +13,7 @@
<mat-checkbox [(ngModel)]="settings.crash_reports">Send anonymous crash reports</mat-checkbox><br/> <mat-checkbox [(ngModel)]="settings.crash_reports">Send anonymous crash reports</mat-checkbox><br/>
<mat-checkbox [(ngModel)]="integrateLinksLabelsToLinks">Integrate link labels to links</mat-checkbox><br/> <mat-checkbox [(ngModel)]="integrateLinksLabelsToLinks">Integrate link labels to links</mat-checkbox><br/>
<mat-checkbox [(ngModel)]="openReadme">Automatically open project README files</mat-checkbox><br/> <mat-checkbox [(ngModel)]="openReadme">Automatically open project README files</mat-checkbox><br/>
<mat-checkbox [(ngModel)]="settings.anonymous_statistics">Send anonymous usage statistics</mat-checkbox><br />
<mat-checkbox [(ngModel)]="openConsolesInWidget">Open consoles in the widget instead of in new tabs after clicking start consoles for all nodes</mat-checkbox> <mat-checkbox [(ngModel)]="openConsolesInWidget">Open consoles in the widget instead of in new tabs after clicking start consoles for all nodes</mat-checkbox>
</div> </div>
</mat-expansion-panel> </mat-expansion-panel>

View File

@ -70,6 +70,7 @@ describe('SettingsComponent', () => {
const settings = { const settings = {
crash_reports: true, crash_reports: true,
experimental_features: true, experimental_features: true,
anonymous_statistics: true,
angular_map: false, angular_map: false,
console_command: '', console_command: '',
}; };

View File

@ -45,25 +45,25 @@
<div mwlDraggable (dragStart)="dragStart($event)" (dragEnd)="dragEnd($event, filteredTemplates[i])"> <div mwlDraggable (dragStart)="dragStart($event)" (dragEnd)="dragEnd($event, filteredTemplates[i])">
<img class="image" [src]="getImageSourceForTemplate(filteredTemplates[i])" /> <img class="image" [src]="getImageSourceForTemplate(filteredTemplates[i])" />
</div> </div>
<div class="templateText">{{ filteredTemplates[i].name }}</div> <div [ngClass]="{ templateText: !isLightThemeEnabled, lightTemplateText: isLightThemeEnabled }">{{ filteredTemplates[i].name }}</div>
</span> </span>
<span *ngIf="filteredTemplates[i + 1]" class="templateIcon"> <span *ngIf="filteredTemplates[i + 1]" class="templateIcon">
<div mwlDraggable (dragStart)="dragStart($event)" (dragEnd)="dragEnd($event, filteredTemplates[i + 1])"> <div mwlDraggable (dragStart)="dragStart($event)" (dragEnd)="dragEnd($event, filteredTemplates[i + 1])">
<img class="image" [src]="getImageSourceForTemplate(filteredTemplates[i + 1])" /> <img class="image" [src]="getImageSourceForTemplate(filteredTemplates[i + 1])" />
</div> </div>
<div class="templateText">{{ filteredTemplates[i + 1].name }}</div> <div [ngClass]="{ templateText: !isLightThemeEnabled, lightTemplateText: isLightThemeEnabled }">{{ filteredTemplates[i + 1].name }}</div>
</span> </span>
<span *ngIf="filteredTemplates[i + 2]" class="templateIcon"> <span *ngIf="filteredTemplates[i + 2]" class="templateIcon">
<div mwlDraggable (dragStart)="dragStart($event)" (dragEnd)="dragEnd($event, filteredTemplates[i + 2])"> <div mwlDraggable (dragStart)="dragStart($event)" (dragEnd)="dragEnd($event, filteredTemplates[i + 2])">
<img class="image" [src]="getImageSourceForTemplate(filteredTemplates[i + 2])" /> <img class="image" [src]="getImageSourceForTemplate(filteredTemplates[i + 2])" />
</div> </div>
<div class="templateText">{{ filteredTemplates[i + 2].name }}</div> <div [ngClass]="{ templateText: !isLightThemeEnabled, lightTemplateText: isLightThemeEnabled }">{{ filteredTemplates[i + 2].name }}</div>
</span> </span>
<span *ngIf="filteredTemplates[i + 3]" class="templateIcon"> <span *ngIf="filteredTemplates[i + 3]" class="templateIcon">
<div mwlDraggable (dragStart)="dragStart($event)" (dragEnd)="dragEnd($event, filteredTemplates[i + 3])"> <div mwlDraggable (dragStart)="dragStart($event)" (dragEnd)="dragEnd($event, filteredTemplates[i + 3])">
<img class="image" [src]="getImageSourceForTemplate(filteredTemplates[i + 3])" /> <img class="image" [src]="getImageSourceForTemplate(filteredTemplates[i + 3])" />
</div> </div>
<div class="templateText">{{ filteredTemplates[i + 3].name }}</div> <div [ngClass]="{ templateText: !isLightThemeEnabled, lightTemplateText: isLightThemeEnabled }">{{ filteredTemplates[i + 3].name }}</div>
</span> </span>
</span> </span>
</mat-list-item> </mat-list-item>

View File

@ -49,6 +49,11 @@
word-wrap: break-word; word-wrap: break-word;
} }
.lightTemplateText {
word-wrap: break-word;
color: black;
}
.templateIcon { .templateIcon {
width: 80px !important; width: 80px !important;
padding: 10px; padding: 10px;

View File

@ -1,5 +1,7 @@
import { OverlayContainer } from '@angular/cdk/overlay';
import { Component, EventEmitter, Input, OnDestroy, OnInit, Output } from '@angular/core'; import { Component, EventEmitter, Input, OnDestroy, OnInit, Output } from '@angular/core';
import { MatDialog } from '@angular/material/dialog'; import { MatDialog } from '@angular/material/dialog';
import { ThemeService } from '../../services/theme.service';
import { Subscription } from 'rxjs'; import { Subscription } from 'rxjs';
import { Project } from '../../models/project'; import { Project } from '../../models/project';
import { Server } from '../../models/server'; import { Server } from '../../models/server';
@ -19,7 +21,7 @@ export class TemplateComponent implements OnInit, OnDestroy {
@Input() server: Server; @Input() server: Server;
@Input() project: Project; @Input() project: Project;
@Output() onNodeCreation = new EventEmitter<any>(); @Output() onNodeCreation = new EventEmitter<any>();
overlay;
templates: Template[] = []; templates: Template[] = [];
filteredTemplates: Template[] = []; filteredTemplates: Template[] = [];
searchText: string = ''; searchText: string = '';
@ -45,14 +47,20 @@ export class TemplateComponent implements OnInit, OnDestroy {
startY: number; startY: number;
private subscription: Subscription; private subscription: Subscription;
private themeSubscription: Subscription;
private isLightThemeEnabled: boolean = false;
constructor( constructor(
private dialog: MatDialog, private dialog: MatDialog,
private templateService: TemplateService, private templateService: TemplateService,
private scaleService: MapScaleService, private scaleService: MapScaleService,
private symbolService: SymbolService, private symbolService: SymbolService,
private domSanitizer: DomSanitizer private domSanitizer: DomSanitizer,
) {} private themeService: ThemeService,
private overlayContainer: OverlayContainer,
) {
this.overlay = overlayContainer.getContainerElement();
}
ngOnInit() { ngOnInit() {
this.subscription = this.templateService.newTemplateCreated.subscribe((template: Template) => { this.subscription = this.templateService.newTemplateCreated.subscribe((template: Template) => {
@ -65,6 +73,23 @@ export class TemplateComponent implements OnInit, OnDestroy {
this.templates = listOfTemplates; this.templates = listOfTemplates;
}); });
this.symbolService.list(this.server); this.symbolService.list(this.server);
if (this.themeService.getActualTheme() === 'light') this.isLightThemeEnabled = true;
this.themeSubscription = this.themeService.themeChanged.subscribe((value: string) => {
if (value === 'light-theme') this.isLightThemeEnabled = true;
this.toggleTheme();
});
}
toggleTheme(): void {
if (this.overlay.classList.contains("dark-theme")) {
this.overlay.classList.remove("dark-theme");
this.overlay.classList.add("light-theme");
} else if (this.overlay.classList.contains("light-theme")) {
this.overlay.classList.remove("light-theme");
this.overlay.classList.add("dark-theme");
} else {
this.overlay.classList.add("light-theme");
}
} }
sortTemplates() { sortTemplates() {

View File

@ -78,7 +78,28 @@ export class TopologySummaryComponent implements OnInit, OnDestroy {
this.computes = computes; this.computes = computes;
}); });
this.style = { top: '60px', right: '0px', width: '320px', height: '400px' }; this.revertPosition();
}
revertPosition(){
let leftPosition = localStorage.getItem('leftPosition');
let rightPosition = localStorage.getItem('rightPosition');
let topPosition = localStorage.getItem('topPosition');
let widthOfWidget = localStorage.getItem('widthOfWidget');
let heightOfWidget = localStorage.getItem('heightOfWidget');
if (!topPosition) {
this.style = { top: '60px', right: '0px', width: '320px', height: '400px' };
} else {
this.style = {
position: 'fixed',
left: `${+leftPosition}px`,
right: `${+rightPosition}px`,
top: `${+topPosition}px`,
width: `${+widthOfWidget}px`,
height: `${+heightOfWidget}px`,
};
}
} }
toggleDragging(value: boolean) { toggleDragging(value: boolean) {
@ -101,6 +122,12 @@ export class TopologySummaryComponent implements OnInit, OnDestroy {
width: `${width}px`, width: `${width}px`,
height: `${height}px`, height: `${height}px`,
}; };
localStorage.setItem('leftPosition', left.toString());
localStorage.setItem('topPosition', top.toString());
localStorage.setItem('widthOfWidget', width.toString());
localStorage.setItem('heightOfWidget', height.toString());
} else { } else {
let right: number = Number(this.style['right'].split('px')[0]) - x; let right: number = Number(this.style['right'].split('px')[0]) - x;
this.style = { this.style = {
@ -110,6 +137,11 @@ export class TopologySummaryComponent implements OnInit, OnDestroy {
width: `${width}px`, width: `${width}px`,
height: `${height}px`, height: `${height}px`,
}; };
localStorage.setItem('rightPosition', right.toString());
localStorage.setItem('topPosition', top.toString());
localStorage.setItem('widthOfWidget', width.toString());
localStorage.setItem('heightOfWidget', height.toString());
} }
} }
@ -140,6 +172,7 @@ export class TopologySummaryComponent implements OnInit, OnDestroy {
toggleTopologyVisibility(value: boolean) { toggleTopologyVisibility(value: boolean) {
this.isTopologyVisible = value; this.isTopologyVisible = value;
this.revertPosition();
} }
compareAsc(first: Node, second: Node) { compareAsc(first: Node, second: Node) {

View File

@ -1,14 +1,17 @@
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { NavigationEnd, Router } from '@angular/router'; import { NavigationEnd, Router } from '@angular/router';
import { environment } from '../../environments/environment'; import { environment } from '../../environments/environment';
import { SettingsService } from './settings.service';
declare var gtag: Function; declare var gtag: Function;
@Injectable() @Injectable()
export class GoogleAnalyticsService { export class GoogleAnalyticsService {
constructor(router: Router) { private settingsService: SettingsService;
constructor(router: Router, settingsService: SettingsService) {
if (!environment.production) return; if (!environment.production) return;
router.events.subscribe((event) => { router.events.subscribe((event) => {
if (event instanceof NavigationEnd) { if (settingsService.getStatisticsSettings() && event instanceof NavigationEnd) {
gtag('set', 'page', event.url); gtag('set', 'page', event.url);
gtag('send', 'pageview'); gtag('send', 'pageview');
} }

View File

@ -4,6 +4,7 @@ import { BehaviorSubject } from 'rxjs';
export interface Settings { export interface Settings {
crash_reports: boolean; crash_reports: boolean;
console_command: string; console_command: string;
anonymous_statistics: boolean;
} }
@Injectable({ @Injectable({
@ -13,10 +14,12 @@ export class SettingsService {
private settings: Settings = { private settings: Settings = {
crash_reports: true, crash_reports: true,
console_command: undefined, console_command: undefined,
anonymous_statistics: true
}; };
private readonly reportsSettings: string = 'crash_reports'; private readonly reportsSettings: string = 'crash_reports';
private readonly consoleSettings: string = 'console_command'; private readonly consoleSettings: string = 'console_command';
private readonly statisticsSettings: string = 'statistics_command';
constructor() { constructor() {
if (this.getItem(this.reportsSettings)) if (this.getItem(this.reportsSettings))
@ -24,6 +27,9 @@ export class SettingsService {
if (this.getItem(this.consoleSettings)) if (this.getItem(this.consoleSettings))
this.settings.console_command = this.getItem(this.consoleSettings); this.settings.console_command = this.getItem(this.consoleSettings);
if (this.getItem(this.statisticsSettings))
this.settings.anonymous_statistics = this.getItem(this.statisticsSettings) === 'true' ? true : false;
} }
setReportsSettings(value: boolean) { setReportsSettings(value: boolean) {
@ -36,10 +42,24 @@ export class SettingsService {
} }
} }
setStatisticsSettings(value: boolean) {
this.settings.anonymous_statistics = value;
this.removeItem(this.statisticsSettings);
if (value) {
this.setItem(this.statisticsSettings, 'true');
} else {
this.setItem(this.statisticsSettings, 'false');
}
}
getReportsSettings() { getReportsSettings() {
return this.getItem(this.reportsSettings) === 'true' ? true : false; return this.getItem(this.reportsSettings) === 'true' ? true : false;
} }
getStatisticsSettings() {
return this.getItem(this.statisticsSettings) === 'true' ? true : false;
}
setConsoleSettings(value: string) { setConsoleSettings(value: string) {
this.settings.console_command = value; this.settings.console_command = value;
this.removeItem(this.consoleSettings); this.removeItem(this.consoleSettings);
@ -70,5 +90,6 @@ export class SettingsService {
this.settings = settings; this.settings = settings;
this.setConsoleSettings(settings.console_command); this.setConsoleSettings(settings.console_command);
this.setReportsSettings(settings.crash_reports); this.setReportsSettings(settings.crash_reports);
this.setStatisticsSettings(settings.anonymous_statistics);
} }
} }

View File

@ -5,7 +5,9 @@ import { BrowserDynamicTestingModule, platformBrowserDynamicTesting } from '@ang
declare const require: any; declare const require: any;
// First, initialize the Angular testing environment. // First, initialize the Angular testing environment.
getTestBed().initTestEnvironment(BrowserDynamicTestingModule, platformBrowserDynamicTesting()); getTestBed().initTestEnvironment(BrowserDynamicTestingModule, platformBrowserDynamicTesting(), {
teardown: { destroyAfterEach: false }
});
// Then we find all the tests. // Then we find all the tests.
const context = require.context('./', true, /\.spec\.ts$/); const context = require.context('./', true, /\.spec\.ts$/);
// And load the modules. // And load the modules.

View File

@ -1,4 +1,4 @@
@import '~@angular/material/theming'; @import '@angular/material/theming';
@import '~material-design-icons/iconfont/material-icons.css'; @import '~material-design-icons/iconfont/material-icons.css';
@import '~typeface-roboto/index.css'; @import '~typeface-roboto/index.css';
@include mat-core(); @include mat-core();

7266
yarn.lock

File diff suppressed because it is too large Load Diff