mirror of
https://github.com/GNS3/gns3-web-ui.git
synced 2025-06-25 01:19:11 +00:00
Compare commits
246 Commits
Author | SHA1 | Date | |
---|---|---|---|
a9314b65f8 | |||
e5b3a101b8 | |||
9a2e06471c | |||
4d695274bb | |||
e5af89821d | |||
78b910504d | |||
27446f8d14 | |||
a026374e75 | |||
4f303921c6 | |||
dea6a5021d | |||
46c7c58402 | |||
0df3525bbf | |||
9667e2363c | |||
24ec96a2dd | |||
9844a2f88f | |||
0cdbeb98a2 | |||
dc40d3be6e | |||
2015917767 | |||
360d77d2ef | |||
75c3d8ed97 | |||
f94d59206e | |||
5b41f9789a | |||
ac84106dbc | |||
21f2267960 | |||
a9781943d5 | |||
6fc2f6f964 | |||
cd5773e58a | |||
f4bcb844dc | |||
24cf0f5623 | |||
5e02a3d757 | |||
42599dafb5 | |||
9c754f3444 | |||
6772f0cb16 | |||
2730ee6f6e | |||
de058e175b | |||
e9487c5ada | |||
035c846f85 | |||
80239229aa | |||
c12a7acb2c | |||
23bf5ad2f3 | |||
bed98624cc | |||
383c26dcef | |||
f505c101f7 | |||
ca071f79c2 | |||
20be9027fd | |||
63389812b5 | |||
6873432833 | |||
a7e3c24a27 | |||
358f50596c | |||
b61f5803fd | |||
c27f4aee57 | |||
5aa00a9f77 | |||
91075a60b1 | |||
57e590a704 | |||
8d3e571aa4 | |||
af5917b6e4 | |||
21ed977a55 | |||
f861364727 | |||
0ff4d534f4 | |||
ca408663a5 | |||
c96d66b34a | |||
896ca927f3 | |||
8253f8da38 | |||
cbb1c9ecfc | |||
1e8b6261dc | |||
f3b8a42d89 | |||
7812ff38cc | |||
d725363fe5 | |||
58d42558f7 | |||
aecbe32c6c | |||
35193043a2 | |||
7a229d8e3e | |||
ba1180786f | |||
535649f0a9 | |||
a4f7db62ba | |||
662aba4ec8 | |||
bfc72c219c | |||
997b8df598 | |||
89bff8ac30 | |||
df6248d641 | |||
cefbc3c9be | |||
de07558349 | |||
b59c528ece | |||
61334d197d | |||
d855e5cb33 | |||
b8253d365d | |||
05685af5c4 | |||
8dbaa11808 | |||
0d7020af97 | |||
58b9083c49 | |||
43213d0669 | |||
924cbe2542 | |||
d06a3efd2c | |||
e2466ca4ab | |||
1da94efe63 | |||
838480509e | |||
04c28bd40a | |||
328dd37ffe | |||
c5a692babf | |||
119afd14d2 | |||
37e6921ffb | |||
5d48ea046d | |||
4342d27d07 | |||
3394035e2e | |||
49403a5568 | |||
70e4745657 | |||
543b81c81b | |||
02562cd046 | |||
252452051a | |||
a64ff3503e | |||
318143f5a8 | |||
e029bccf18 | |||
721adacde4 | |||
4374573c60 | |||
1e7c04f93c | |||
7e172e30ba | |||
4d243f895c | |||
aeef3e74ed | |||
83d72787f4 | |||
829bfe12d7 | |||
f5b5c717b4 | |||
5b7da298d6 | |||
d7742a7c59 | |||
e0ce8c0770 | |||
061dec9d75 | |||
684a160d99 | |||
21a12c151b | |||
17be201862 | |||
57385b84f7 | |||
8e4f860b43 | |||
2c015e695d | |||
9fe2b3646b | |||
313966548f | |||
69a7cf44c5 | |||
faec4b07be | |||
908c721094 | |||
e3c4188171 | |||
f48471cdd4 | |||
50fb05aa8e | |||
63728091c1 | |||
03a417d78c | |||
c7a7a357c6 | |||
7d95267283 | |||
8a7309bde1 | |||
afccf4955b | |||
ad57a5f3f7 | |||
7407ddafd3 | |||
168de3aecb | |||
13f80cdaad | |||
28f32de0b2 | |||
a6a4fb401d | |||
f62366440c | |||
135ecbdc33 | |||
9ebbbb197b | |||
f90c074191 | |||
2d49ca30fa | |||
83f7d36e2d | |||
174053f297 | |||
0e4e124c14 | |||
c928ab0342 | |||
9efd99dccb | |||
7e43dc77cb | |||
d7752a4d7b | |||
40df0fe1ee | |||
d5bd84234d | |||
a9a7ecf3e7 | |||
5dc5a953e6 | |||
32c78450a2 | |||
82feb9aa92 | |||
a08a7e1476 | |||
b5e4972bdb | |||
dc5c0d3d94 | |||
04936cfc8d | |||
8728056b8d | |||
138d1f8552 | |||
dc31d51844 | |||
def33a353d | |||
ed3db2ea4d | |||
7ad6de2256 | |||
6dcc5cdc2e | |||
089e66a02b | |||
da848d42af | |||
6b08fb8d9a | |||
8874e7efbc | |||
2b834768c6 | |||
eabdda0e74 | |||
9e3f667767 | |||
8898141bc1 | |||
c8753ed45c | |||
d496d8dc64 | |||
a8f9b6948d | |||
ccd3ff61f1 | |||
abf9d8b387 | |||
f8cc654539 | |||
56554f6d0c | |||
7be137dc1c | |||
1b45a2284d | |||
6b4f5186d0 | |||
c3f2ebad0c | |||
249f63a97a | |||
cc6b8cd28c | |||
96928d86f8 | |||
a213a7aca1 | |||
fc1d17b921 | |||
0ddf4f6e95 | |||
8d466d655e | |||
2c7dd5f179 | |||
64999f2b72 | |||
253c65b8c1 | |||
74c1a82524 | |||
4e42bd7a54 | |||
891e65b094 | |||
c808477914 | |||
8503a17187 | |||
8afea664ff | |||
538ae8b7fb | |||
370694f3b0 | |||
5175b3beac | |||
2df1956dbc | |||
56384fbcc0 | |||
15faca6d89 | |||
4142144d4d | |||
e2e87db039 | |||
c868f08a25 | |||
9aedd410bb | |||
5fb76d7d11 | |||
a7c343aa7c | |||
cfe8c4760b | |||
7cbcc84cc1 | |||
357e478fb8 | |||
063d8c9dc7 | |||
63ecacb6b6 | |||
6cecacf611 | |||
c389404e58 | |||
b1aba60410 | |||
4cd9f77732 | |||
1619c3ec05 | |||
b33a01e225 | |||
353740376e | |||
9fe899e4df | |||
dd1f16c53d | |||
3fa52d3c9c | |||
bc5dd0271f | |||
81ca3e2af2 | |||
d7a0d2f69a | |||
8f0bbafa72 |
16
.github/workflows/add-new-issues-to-project.yml
vendored
Normal file
16
.github/workflows/add-new-issues-to-project.yml
vendored
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
name: Add new issues to GNS3 project
|
||||||
|
|
||||||
|
on:
|
||||||
|
issues:
|
||||||
|
types:
|
||||||
|
- opened
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
add-to-project:
|
||||||
|
name: Add issue to project
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/add-to-project@v0.4.0
|
||||||
|
with:
|
||||||
|
project-url: https://github.com/orgs/GNS3/projects/3
|
||||||
|
github-token: ${{ secrets.ADD_NEW_ISSUES_TO_PROJECT }}
|
76
.github/workflows/codeql.yml
vendored
Normal file
76
.github/workflows/codeql.yml
vendored
Normal file
@ -0,0 +1,76 @@
|
|||||||
|
# For most projects, this workflow file will not need changing; you simply need
|
||||||
|
# to commit it to your repository.
|
||||||
|
#
|
||||||
|
# You may wish to alter this file to override the set of languages analyzed,
|
||||||
|
# or to provide custom queries or build logic.
|
||||||
|
#
|
||||||
|
# ******** NOTE ********
|
||||||
|
# We have attempted to detect the languages in your repository. Please check
|
||||||
|
# the `language` matrix defined below to confirm you have the correct set of
|
||||||
|
# supported CodeQL languages.
|
||||||
|
#
|
||||||
|
name: "CodeQL"
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [ "master", "master-3.0" ]
|
||||||
|
pull_request:
|
||||||
|
# The branches below must be a subset of the branches above
|
||||||
|
branches: [ "master", "master-3.0" ]
|
||||||
|
schedule:
|
||||||
|
- cron: '38 18 * * 6'
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
analyze:
|
||||||
|
name: Analyze
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
permissions:
|
||||||
|
actions: read
|
||||||
|
contents: read
|
||||||
|
security-events: write
|
||||||
|
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
language: [ 'javascript', 'python' ]
|
||||||
|
# CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ]
|
||||||
|
# Use only 'java' to analyze code written in Java, Kotlin or both
|
||||||
|
# Use only 'javascript' to analyze code written in JavaScript, TypeScript or both
|
||||||
|
# Learn more about CodeQL language support at https://aka.ms/codeql-docs/language-support
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout repository
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
|
||||||
|
# Initializes the CodeQL tools for scanning.
|
||||||
|
- name: Initialize CodeQL
|
||||||
|
uses: github/codeql-action/init@v2
|
||||||
|
with:
|
||||||
|
languages: ${{ matrix.language }}
|
||||||
|
# If you wish to specify custom queries, you can do so here or in a config file.
|
||||||
|
# By default, queries listed here will override any specified in a config file.
|
||||||
|
# Prefix the list here with "+" to use these queries and those in the config file.
|
||||||
|
|
||||||
|
# Details on CodeQL's query packs refer to : https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs
|
||||||
|
# queries: security-extended,security-and-quality
|
||||||
|
|
||||||
|
|
||||||
|
# Autobuild attempts to build any compiled languages (C/C++, C#, Go, or Java).
|
||||||
|
# If this step fails, then you should remove it and run the build manually (see below)
|
||||||
|
- name: Autobuild
|
||||||
|
uses: github/codeql-action/autobuild@v2
|
||||||
|
|
||||||
|
# ℹ️ Command-line programs to run using the OS shell.
|
||||||
|
# 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun
|
||||||
|
|
||||||
|
# If the Autobuild fails above, remove it and uncomment the following three lines.
|
||||||
|
# modify them (or add more) to build your code if your project, please refer to the EXAMPLE below for guidance.
|
||||||
|
|
||||||
|
# - run: |
|
||||||
|
# echo "Run, Build Application using script"
|
||||||
|
# ./location_of_script_within_repo/buildscript.sh
|
||||||
|
|
||||||
|
- name: Perform CodeQL Analysis
|
||||||
|
uses: github/codeql-action/analyze@v2
|
||||||
|
with:
|
||||||
|
category: "/language:${{matrix.language}}"
|
1
.gitignore
vendored
1
.gitignore
vendored
@ -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
|
||||||
|
@ -2,7 +2,8 @@
|
|||||||
"scanSettings": {
|
"scanSettings": {
|
||||||
"configMode": "AUTO",
|
"configMode": "AUTO",
|
||||||
"configExternalURL": "",
|
"configExternalURL": "",
|
||||||
"projectToken" : ""
|
"projectToken" : "",
|
||||||
|
"baseBranches": ["master", "master-3.0"]
|
||||||
},
|
},
|
||||||
"checkRunSettings": {
|
"checkRunSettings": {
|
||||||
"vulnerableCheckRunConclusionLevel": "failure"
|
"vulnerableCheckRunConclusionLevel": "failure"
|
||||||
@ -10,4 +11,4 @@
|
|||||||
"issueSettings": {
|
"issueSettings": {
|
||||||
"minSeverityLevel": "LOW"
|
"minSeverityLevel": "LOW"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
5
SECURITY.md
Normal file
5
SECURITY.md
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
# Security Policy
|
||||||
|
|
||||||
|
## Reporting a Vulnerability
|
||||||
|
|
||||||
|
Please use GitHub's report a vulnerability feature. More information can be found in https://docs.github.com/en/code-security/security-advisories/guidance-on-reporting-and-writing/privately-reporting-a-security-vulnerability
|
13
angular.json
13
angular.json
@ -26,8 +26,9 @@
|
|||||||
"rxjs/add/operator/map",
|
"rxjs/add/operator/map",
|
||||||
"rxjs-compat/add/operator/map",
|
"rxjs-compat/add/operator/map",
|
||||||
"classnames",
|
"classnames",
|
||||||
"stylenames"
|
"stylenames",
|
||||||
],
|
"ipaddr.js"
|
||||||
|
],
|
||||||
"outputPath": "dist",
|
"outputPath": "dist",
|
||||||
"index": "src/index.html",
|
"index": "src/index.html",
|
||||||
"main": "src/main.ts",
|
"main": "src/main.ts",
|
||||||
@ -47,10 +48,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 +178,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"
|
||||||
@ -241,4 +244,4 @@
|
|||||||
"cli": {
|
"cli": {
|
||||||
"analytics": false
|
"analytics": false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -25,7 +25,7 @@ module.exports = function (config) {
|
|||||||
colors: true,
|
colors: true,
|
||||||
logLevel: config.LOG_INFO,
|
logLevel: config.LOG_INFO,
|
||||||
autoWatch: true,
|
autoWatch: true,
|
||||||
browsers: ['ChromeHeadless'],
|
browsers: ['Chrome'],
|
||||||
singleRun: true
|
singleRun: true
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
93
package.json
93
package.json
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "gns3-web-ui",
|
"name": "gns3-web-ui",
|
||||||
"version": "2.2.23",
|
"version": "2.2.49",
|
||||||
"author": {
|
"author": {
|
||||||
"name": "GNS3 Technology Inc.",
|
"name": "GNS3 Technology Inc.",
|
||||||
"email": "developers@gns3.com"
|
"email": "developers@gns3.com"
|
||||||
@ -36,34 +36,37 @@
|
|||||||
"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.1.4",
|
"@angular/animations": "^12.2.12",
|
||||||
"@angular/cdk": "^12.1.4",
|
"@angular/cdk": "^12.2.12",
|
||||||
"@angular/common": "^12.1.4",
|
"@angular/common": "^12.2.12",
|
||||||
"@angular/compiler": "^12.1.4",
|
"@angular/compiler": "^12.2.12",
|
||||||
"@angular/core": "^12.1.4",
|
"@angular/core": "^12.2.12",
|
||||||
"@angular/forms": "^12.1.4",
|
"@angular/forms": "^12.2.12",
|
||||||
"@angular/material": "^12.1.4",
|
"@angular/material": "^12.2.12",
|
||||||
"@angular/platform-browser": "^12.1.4",
|
"@angular/platform-browser": "^12.2.12",
|
||||||
"@angular/platform-browser-dynamic": "^12.1.4",
|
"@angular/platform-browser-dynamic": "^12.2.12",
|
||||||
"@angular/router": "^12.1.4",
|
"@angular/router": "^12.2.12",
|
||||||
"@sentry/browser": "^6.10.0",
|
"@sentry/browser": "^6.14.1",
|
||||||
"@types/jest": "^26.0.24",
|
"@types/jest": "^27.0.2",
|
||||||
"@types/mocha": "^9.0.0",
|
"@types/mocha": "^9.0.0",
|
||||||
"@types/react": "^17.0.15",
|
"@types/react": "^17.0.34",
|
||||||
"@types/react-dom": "^17.0.9",
|
"@types/react-dom": "^17.0.11",
|
||||||
"angular-draggable-droppable": "^4.6.0",
|
"angular-draggable-droppable": "^5.0.0",
|
||||||
"angular-resizable-element": "^3.4.0",
|
"angular-resizable-element": "^3.4.0",
|
||||||
"bootstrap": "^5.0.2",
|
"bootstrap": "^5.1.3",
|
||||||
"command-exists": "^1.2.9",
|
"command-exists": "^1.2.9",
|
||||||
"core-js": "^3.16.0",
|
"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",
|
||||||
|
"ipaddr.js": "^2.1.0",
|
||||||
"material-design-icons": "^3.0.1",
|
"material-design-icons": "^3.0.1",
|
||||||
"mousetrap": "^1.6.5",
|
"mousetrap": "^1.6.5",
|
||||||
"ng-circle-progress": "^1.6.0",
|
"ng-circle-progress": "^1.6.0",
|
||||||
@ -71,42 +74,42 @@
|
|||||||
"ngx-childprocess": "^0.0.6",
|
"ngx-childprocess": "^0.0.6",
|
||||||
"ngx-device-detector": "^2.1.1",
|
"ngx-device-detector": "^2.1.1",
|
||||||
"ngx-electron": "^2.2.0",
|
"ngx-electron": "^2.2.0",
|
||||||
"node-fetch": "^2.6.1",
|
"node-fetch": "^3.2.10",
|
||||||
"notosans-fontface": "1.2.2",
|
"notosans-fontface": "1.2.2",
|
||||||
"prettier-plugin-organize-imports": "^2.3.3",
|
"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.671.0",
|
"snyk": "^1.1064.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.3.0",
|
"tslib": "^2.3.1",
|
||||||
"typeface-roboto": "^1.1.13",
|
"typeface-roboto": "^1.1.13",
|
||||||
"xterm": "^4.13.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.1.4",
|
"@angular-devkit/build-angular": "^12.2.12",
|
||||||
"@angular/cli": "^12.1.4",
|
"@angular/cli": "^12.2.12",
|
||||||
"@angular/compiler-cli": "^12.1.4",
|
"@angular/compiler-cli": "^12.2.12",
|
||||||
"@angular/language-service": "^12.1.4",
|
"@angular/language-service": "^12.2.12",
|
||||||
"@sentry/cli": "^1.67.2",
|
"@sentry/cli": "^1.71.0",
|
||||||
"@sentry/electron": "^2.5.1",
|
"@sentry/electron": "^2.5.4",
|
||||||
"@types/jasmine": "^3.8.2",
|
"@types/jasmine": "^3.10.2",
|
||||||
"@types/jasminewd2": "^2.0.10",
|
"@types/jasminewd2": "^2.0.10",
|
||||||
"@types/node": "16.4.7",
|
"@types/node": "16.11.6",
|
||||||
"codelyzer": "^6.0.2",
|
"codelyzer": "^6.0.2",
|
||||||
"electron": "^13.1.7",
|
"electron": "^13.6.6",
|
||||||
"electron-builder": "22.11.7",
|
"electron-builder": "^22.9.1",
|
||||||
"file-loader": "^6.2.0",
|
"file-loader": "^6.2.0",
|
||||||
"jasmine-core": "~3.8.0",
|
"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.4",
|
"karma": "^6.3.16",
|
||||||
"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",
|
||||||
@ -114,16 +117,16 @@
|
|||||||
"karma-jasmine-html-reporter": "^1.7.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.2",
|
"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.1.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.3.5",
|
"typescript": "4.2.3",
|
||||||
"webpack": "5.47.1",
|
"webpack": "5.76.0",
|
||||||
"yarn-upgrade-all": "^0.5.4"
|
"yarn-upgrade-all": "^0.5.4"
|
||||||
},
|
},
|
||||||
"greenkeeper": {
|
"greenkeeper": {
|
||||||
@ -132,4 +135,4 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"snyk": true
|
"snyk": true
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
setuptools==54.2.0
|
setuptools==71.1.0
|
||||||
cx_Freeze==5.1.1
|
cx_Freeze==5.1.1
|
||||||
requests==2.25.1
|
requests==2.32.3
|
||||||
packaging==20.9
|
packaging==20.9
|
||||||
appdirs==1.4.4
|
appdirs==1.4.4
|
||||||
psutil==5.8.0
|
psutil==5.8.0
|
||||||
|
@ -1,6 +1,10 @@
|
|||||||
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.22
|
Current version: 2.2.32
|
||||||
|
|
||||||
|
Bug Fixes & enhancements
|
||||||
|
- Fixed generated capture file is not valid
|
||||||
|
- Fixed Docker additional directories
|
||||||
|
|
||||||
Current version: 2020.4.0-beta.1
|
Current version: 2020.4.0-beta.1
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
<div [ngClass]="{ dark: darkThemeEnabled, light: !darkThemeEnabled }">
|
<div [ngClass]="{ dark: darkThemeEnabled, light: !darkThemeEnabled }">
|
||||||
<router-outlet></router-outlet>
|
<router-outlet></router-outlet>
|
||||||
<app-adbutler></app-adbutler>
|
<!-- <app-adbutler></app-adbutler> -->
|
||||||
</div>
|
</div>
|
||||||
|
@ -215,6 +215,7 @@ import { DefaultLayoutComponent } from './layouts/default-layout/default-layout.
|
|||||||
import { MATERIAL_IMPORTS } from './material.imports';
|
import { MATERIAL_IMPORTS } from './material.imports';
|
||||||
import { ServerResolve } from './resolvers/server-resolve';
|
import { ServerResolve } from './resolvers/server-resolve';
|
||||||
import { ApplianceService } from './services/appliances.service';
|
import { ApplianceService } from './services/appliances.service';
|
||||||
|
import { ProtocolHandlerService } from './services/protocol-handler.service';
|
||||||
import { BuiltInTemplatesConfigurationService } from './services/built-in-templates-configuration.service';
|
import { BuiltInTemplatesConfigurationService } from './services/built-in-templates-configuration.service';
|
||||||
import { BuiltInTemplatesService } from './services/built-in-templates.service';
|
import { BuiltInTemplatesService } from './services/built-in-templates.service';
|
||||||
import { ComputeService } from './services/compute.service';
|
import { ComputeService } from './services/compute.service';
|
||||||
@ -538,6 +539,7 @@ import { RotationValidator } from './validators/rotation-validator';
|
|||||||
ComputeService,
|
ComputeService,
|
||||||
TracengService,
|
TracengService,
|
||||||
PacketCaptureService,
|
PacketCaptureService,
|
||||||
|
ProtocolHandlerService,
|
||||||
NotificationService,
|
NotificationService,
|
||||||
Gns3vmService,
|
Gns3vmService,
|
||||||
ThemeService,
|
ThemeService,
|
||||||
|
@ -7,4 +7,6 @@
|
|||||||
[attr.stroke-dasharray]="stroke_dasharray"
|
[attr.stroke-dasharray]="stroke_dasharray"
|
||||||
[attr.width]="rect.width"
|
[attr.width]="rect.width"
|
||||||
[attr.height]="rect.height"
|
[attr.height]="rect.height"
|
||||||
|
[attr.rx]="rect.rx"
|
||||||
|
[attr.ry]="rect.ry"
|
||||||
/>
|
/>
|
||||||
|
Before Width: | Height: | Size: 278 B After Width: | Height: | Size: 322 B |
@ -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(
|
||||||
|
@ -14,7 +14,7 @@ export class MapDrawingToSvgConverter implements Converter<MapDrawing, string> {
|
|||||||
let elem = ``;
|
let elem = ``;
|
||||||
|
|
||||||
if (mapDrawing.element instanceof RectElement) {
|
if (mapDrawing.element instanceof RectElement) {
|
||||||
elem = `<rect fill=\"${mapDrawing.element.fill}\" fill-opacity=\"${mapDrawing.element.fill_opacity}\" height=\"${mapDrawing.element.height}\" width=\"${mapDrawing.element.width}\" stroke=\"${mapDrawing.element.stroke}\" stroke-width=\"${mapDrawing.element.stroke_width}\" />`;
|
elem = `<rect fill=\"${mapDrawing.element.fill}\" fill-opacity=\"${mapDrawing.element.fill_opacity}\" height=\"${mapDrawing.element.height}\" width=\"${mapDrawing.element.width}\" stroke=\"${mapDrawing.element.stroke}\" stroke-width=\"${mapDrawing.element.stroke_width}\" rx=\"${mapDrawing.element.rx}\" ry=\"${mapDrawing.element.ry}\" />`;
|
||||||
} else if (mapDrawing.element instanceof EllipseElement) {
|
} else if (mapDrawing.element instanceof EllipseElement) {
|
||||||
elem = `<ellipse fill=\"${mapDrawing.element.fill}\" fill-opacity=\"${mapDrawing.element.fill_opacity}\" cx=\"${mapDrawing.element.cx}\" cy=\"${mapDrawing.element.cy}\" rx=\"${mapDrawing.element.rx}\" ry=\"${mapDrawing.element.ry}\" stroke=\"${mapDrawing.element.stroke}\" stroke-width=\"${mapDrawing.element.stroke_width}\" />`;
|
elem = `<ellipse fill=\"${mapDrawing.element.fill}\" fill-opacity=\"${mapDrawing.element.fill_opacity}\" cx=\"${mapDrawing.element.cx}\" cy=\"${mapDrawing.element.cy}\" rx=\"${mapDrawing.element.rx}\" ry=\"${mapDrawing.element.ry}\" stroke=\"${mapDrawing.element.stroke}\" stroke-width=\"${mapDrawing.element.stroke_width}\" />`;
|
||||||
} else if (mapDrawing.element instanceof LineElement) {
|
} else if (mapDrawing.element instanceof LineElement) {
|
||||||
|
@ -13,6 +13,8 @@ export class RectangleElementFactory implements DrawingElementFactory {
|
|||||||
rectElement.stroke_width = 2;
|
rectElement.stroke_width = 2;
|
||||||
rectElement.width = 200;
|
rectElement.width = 200;
|
||||||
rectElement.height = 100;
|
rectElement.height = 100;
|
||||||
|
rectElement.rx = 0;
|
||||||
|
rectElement.ry = 0;
|
||||||
return rectElement;
|
return rectElement;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -16,8 +16,8 @@ describe('FontFixer', () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
expect(fixer.fix(font)).toEqual({
|
expect(fixer.fix(font)).toEqual({
|
||||||
font_family: 'Noto Sans',
|
font_family: 'Arial',
|
||||||
font_size: 11,
|
font_size: 12,
|
||||||
font_weight: 'bold',
|
font_weight: 'bold',
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@ -39,12 +39,12 @@ describe('FontFixer', () => {
|
|||||||
it('should fix TypeWriter font and 10px size in styles', () => {
|
it('should fix TypeWriter font and 10px size in styles', () => {
|
||||||
const styles = 'font-family: TypeWriter; font-size: 10px; font-weight: bold';
|
const styles = 'font-family: TypeWriter; font-size: 10px; font-weight: bold';
|
||||||
|
|
||||||
expect(fixer.fixStyles(styles)).toEqual('font-family:Noto Sans;font-size:11px;font-weight:bold');
|
expect(fixer.fixStyles(styles)).toEqual('font-family:Arial;font-size:12px;font-weight:bold');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should fix TypeWriter font and 10px size in styles with quotes', () => {
|
it('should fix TypeWriter font and 10px size in styles with quotes', () => {
|
||||||
const styles = 'font-family: "TypeWriter"; font-size: 10px; font-weight: bold';
|
const styles = 'font-family: "TypeWriter"; font-size: 10px; font-weight: bold';
|
||||||
|
|
||||||
expect(fixer.fixStyles(styles)).toEqual('font-family:Noto Sans;font-size:11px;font-weight:bold');
|
expect(fixer.fixStyles(styles)).toEqual('font-family:Arial;font-size:12px;font-weight:bold');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -9,8 +9,8 @@ import { Font } from '../models/font';
|
|||||||
export class FontFixer {
|
export class FontFixer {
|
||||||
static DEFAULT_FONT = 'TypeWriter';
|
static DEFAULT_FONT = 'TypeWriter';
|
||||||
static DEFAULT_SIZE = 10;
|
static DEFAULT_SIZE = 10;
|
||||||
static REPLACE_BY_FONT = 'Noto Sans';
|
static REPLACE_BY_FONT = 'Arial';
|
||||||
static REPLACE_BY_SIZE = 11;
|
static REPLACE_BY_SIZE = 12;
|
||||||
|
|
||||||
public fix(font: Font): Font {
|
public fix(font: Font): Font {
|
||||||
if (font.font_family === FontFixer.DEFAULT_FONT && font.font_size === FontFixer.DEFAULT_SIZE) {
|
if (font.font_family === FontFixer.DEFAULT_FONT && font.font_size === FontFixer.DEFAULT_SIZE) {
|
||||||
|
@ -17,6 +17,8 @@ describe('RectConverter', () => {
|
|||||||
|
|
||||||
element.setAttribute('width', '100px');
|
element.setAttribute('width', '100px');
|
||||||
element.setAttribute('height', '200px');
|
element.setAttribute('height', '200px');
|
||||||
|
element.setAttribute('rx', '0');
|
||||||
|
element.setAttribute('ry', '0');
|
||||||
|
|
||||||
const drawing = rectConverter.convert(element);
|
const drawing = rectConverter.convert(element);
|
||||||
expect(drawing.fill).toEqual('#ffffff');
|
expect(drawing.fill).toEqual('#ffffff');
|
||||||
@ -25,6 +27,8 @@ describe('RectConverter', () => {
|
|||||||
expect(drawing.stroke_dasharray).toEqual('5,25,25');
|
expect(drawing.stroke_dasharray).toEqual('5,25,25');
|
||||||
expect(drawing.width).toEqual(100);
|
expect(drawing.width).toEqual(100);
|
||||||
expect(drawing.height).toEqual(200);
|
expect(drawing.height).toEqual(200);
|
||||||
|
expect(drawing.rx).toEqual(0);
|
||||||
|
expect(drawing.ry).toEqual(0);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should parse with no attributes', () => {
|
it('should parse with no attributes', () => {
|
||||||
@ -37,5 +41,7 @@ describe('RectConverter', () => {
|
|||||||
expect(drawing.stroke_dasharray).toBeUndefined();
|
expect(drawing.stroke_dasharray).toBeUndefined();
|
||||||
expect(drawing.width).toBeUndefined();
|
expect(drawing.width).toBeUndefined();
|
||||||
expect(drawing.height).toBeUndefined();
|
expect(drawing.height).toBeUndefined();
|
||||||
|
expect(drawing.rx).toBeUndefined();
|
||||||
|
expect(drawing.ry).toBeUndefined();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -40,6 +40,16 @@ export class RectConverter implements SvgConverter {
|
|||||||
drawing.height = parseInt(height.value, 10);
|
drawing.height = parseInt(height.value, 10);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const rx = element.attributes.getNamedItem('rx');
|
||||||
|
if (rx) {
|
||||||
|
drawing.rx = parseInt(rx.value, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
const ry = element.attributes.getNamedItem('ry');
|
||||||
|
if (ry) {
|
||||||
|
drawing.ry = parseInt(ry.value, 0);
|
||||||
|
}
|
||||||
|
|
||||||
return drawing;
|
return drawing;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,4 +8,6 @@ export class RectElement implements DrawingElement {
|
|||||||
stroke: string;
|
stroke: string;
|
||||||
stroke_width: number;
|
stroke_width: number;
|
||||||
stroke_dasharray: string;
|
stroke_dasharray: string;
|
||||||
|
rx: number;
|
||||||
|
ry: number;
|
||||||
}
|
}
|
||||||
|
@ -14,6 +14,7 @@ export class Properties {
|
|||||||
headless: boolean;
|
headless: boolean;
|
||||||
linked_clone: boolean;
|
linked_clone: boolean;
|
||||||
on_close: string;
|
on_close: string;
|
||||||
|
aux: number;
|
||||||
ram: number;
|
ram: number;
|
||||||
nvram: number;
|
nvram: number;
|
||||||
usage: string;
|
usage: string;
|
||||||
@ -53,7 +54,10 @@ export class Properties {
|
|||||||
qemu_path: string;
|
qemu_path: string;
|
||||||
environment: string;
|
environment: string;
|
||||||
extra_hosts: string;
|
extra_hosts: string;
|
||||||
|
extra_volumes: string[];
|
||||||
replicate_network_connection_state: boolean;
|
replicate_network_connection_state: boolean;
|
||||||
|
tpm: boolean;
|
||||||
|
uefi: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class Node {
|
export class Node {
|
||||||
|
@ -28,6 +28,8 @@ describe('RectDrawingWidget', () => {
|
|||||||
rect.stroke_dasharray = '5,25,25';
|
rect.stroke_dasharray = '5,25,25';
|
||||||
rect.width = 100;
|
rect.width = 100;
|
||||||
rect.height = 200;
|
rect.height = 200;
|
||||||
|
rect.rx = 0;
|
||||||
|
rect.ry = 0;
|
||||||
drawing.element = rect;
|
drawing.element = rect;
|
||||||
|
|
||||||
const drawings = svg.canvas.selectAll<SVGGElement, MapDrawing>('g.drawing').data([drawing]);
|
const drawings = svg.canvas.selectAll<SVGGElement, MapDrawing>('g.drawing').data([drawing]);
|
||||||
@ -46,5 +48,7 @@ describe('RectDrawingWidget', () => {
|
|||||||
expect(rect_element.getAttribute('stroke-dasharray')).toEqual('5,25,25');
|
expect(rect_element.getAttribute('stroke-dasharray')).toEqual('5,25,25');
|
||||||
expect(rect_element.getAttribute('width')).toEqual('100');
|
expect(rect_element.getAttribute('width')).toEqual('100');
|
||||||
expect(rect_element.getAttribute('height')).toEqual('200');
|
expect(rect_element.getAttribute('height')).toEqual('200');
|
||||||
|
expect(rect_element.getAttribute('rx')).toEqual('0');
|
||||||
|
expect(rect_element.getAttribute('ry')).toEqual('0');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -33,7 +33,9 @@ export class RectDrawingWidget implements DrawingShapeWidget {
|
|||||||
.attr('stroke-width', (rect) => rect.stroke_width)
|
.attr('stroke-width', (rect) => rect.stroke_width)
|
||||||
.attr('stroke-dasharray', (rect) => this.qtDasharrayFixer.fix(rect.stroke_dasharray))
|
.attr('stroke-dasharray', (rect) => this.qtDasharrayFixer.fix(rect.stroke_dasharray))
|
||||||
.attr('width', (rect) => rect.width)
|
.attr('width', (rect) => rect.width)
|
||||||
.attr('height', (rect) => rect.height);
|
.attr('height', (rect) => rect.height)
|
||||||
|
.attr('rx', (rect) => rect.rx)
|
||||||
|
.attr('ry', (rect) => rect.ry);
|
||||||
|
|
||||||
drawing.exit().remove();
|
drawing.exit().remove();
|
||||||
}
|
}
|
||||||
|
@ -42,7 +42,7 @@ describe('TextDrawingWidget', () => {
|
|||||||
const text_element = drew.nodes()[0];
|
const text_element = drew.nodes()[0];
|
||||||
expect(text_element.innerHTML).toEqual('<tspan xml:space="preserve" x="0" dy="0em">THIS IS TEXT</tspan>');
|
expect(text_element.innerHTML).toEqual('<tspan xml:space="preserve" x="0" dy="0em">THIS IS TEXT</tspan>');
|
||||||
expect(text_element.getAttribute('fill')).toEqual('#000000');
|
expect(text_element.getAttribute('fill')).toEqual('#000000');
|
||||||
expect(text_element.getAttribute('style')).toEqual('font-family: "Noto Sans"; font-size: 11pt; font-weight: bold');
|
expect(text_element.getAttribute('style')).toEqual('font-family: "Arial"; font-size: 12pt; font-weight: bold');
|
||||||
expect(text_element.getAttribute('text-decoration')).toEqual('line-through');
|
expect(text_element.getAttribute('text-decoration')).toEqual('line-through');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -90,16 +90,16 @@ export class InterfaceStatusWidget implements Widget {
|
|||||||
.merge(status_started_enter)
|
.merge(status_started_enter)
|
||||||
.attr('class', 'status_started')
|
.attr('class', 'status_started')
|
||||||
.attr('width', (ls: LinkStatus) => {
|
.attr('width', (ls: LinkStatus) => {
|
||||||
return ls.port.length * 8 + 10;
|
return ls.port.length * 10 + 5;
|
||||||
})
|
})
|
||||||
.attr('height', 20)
|
.attr('height', 20)
|
||||||
.attr('x', (ls: LinkStatus) => ls.x - 30)
|
.attr('x', (ls: LinkStatus) => ls.x - 30)
|
||||||
.attr('y', (ls: LinkStatus) => ls.y - 10)
|
.attr('y', (ls: LinkStatus) => ls.y - 10)
|
||||||
.attr('rx', 8)
|
.attr('rx', 8)
|
||||||
.attr('ry', 8)
|
.attr('ry', 8)
|
||||||
.style('fill', 'white')
|
.style('fill', '#c7ffdf')
|
||||||
.attr('stroke', '#2ecc71')
|
.attr('stroke', '#2ecc71')
|
||||||
.attr('stroke-width', 3);
|
.attr('stroke-width', 2);
|
||||||
status_started.exit().remove();
|
status_started.exit().remove();
|
||||||
const status_started_label = link_group
|
const status_started_label = link_group
|
||||||
.selectAll<SVGTextElement, LinkStatus>('text.status_started_label')
|
.selectAll<SVGTextElement, LinkStatus>('text.status_started_label')
|
||||||
@ -111,7 +111,7 @@ export class InterfaceStatusWidget implements Widget {
|
|||||||
.text((ls: LinkStatus) => ls.port)
|
.text((ls: LinkStatus) => ls.port)
|
||||||
.attr('x', (ls: LinkStatus) => ls.x - 25)
|
.attr('x', (ls: LinkStatus) => ls.x - 25)
|
||||||
.attr('y', (ls: LinkStatus) => ls.y + 5)
|
.attr('y', (ls: LinkStatus) => ls.y + 5)
|
||||||
.attr('fill', `black`);
|
.attr('fill', `#0e9647`);
|
||||||
status_started_label.exit().remove();
|
status_started_label.exit().remove();
|
||||||
|
|
||||||
const status_stopped = link_group
|
const status_stopped = link_group
|
||||||
@ -122,16 +122,16 @@ export class InterfaceStatusWidget implements Widget {
|
|||||||
.merge(status_stopped_enter)
|
.merge(status_stopped_enter)
|
||||||
.attr('class', 'status_stopped')
|
.attr('class', 'status_stopped')
|
||||||
.attr('width', (ls: LinkStatus) => {
|
.attr('width', (ls: LinkStatus) => {
|
||||||
return ls.port.length * 8 + 10;
|
return ls.port.length * 10 + 5;
|
||||||
})
|
})
|
||||||
.attr('height', 20)
|
.attr('height', 20)
|
||||||
.attr('x', (ls: LinkStatus) => ls.x - 30)
|
.attr('x', (ls: LinkStatus) => ls.x - 30)
|
||||||
.attr('y', (ls: LinkStatus) => ls.y - 10)
|
.attr('y', (ls: LinkStatus) => ls.y - 10)
|
||||||
.attr('rx', 8)
|
.attr('rx', 8)
|
||||||
.attr('ry', 8)
|
.attr('ry', 8)
|
||||||
.style('fill', 'white')
|
.style('fill', '#ffe3e3')
|
||||||
.attr('stroke', 'red')
|
.attr('stroke', 'red')
|
||||||
.attr('stroke-width', 3);
|
.attr('stroke-width', 2);
|
||||||
status_stopped.exit().remove();
|
status_stopped.exit().remove();
|
||||||
const status_stopped_label = link_group
|
const status_stopped_label = link_group
|
||||||
.selectAll<SVGTextElement, LinkStatus>('text.status_stopped_label')
|
.selectAll<SVGTextElement, LinkStatus>('text.status_stopped_label')
|
||||||
@ -143,7 +143,7 @@ export class InterfaceStatusWidget implements Widget {
|
|||||||
.text((ls: LinkStatus) => ls.port)
|
.text((ls: LinkStatus) => ls.port)
|
||||||
.attr('x', (ls: LinkStatus) => ls.x - 25)
|
.attr('x', (ls: LinkStatus) => ls.x - 25)
|
||||||
.attr('y', (ls: LinkStatus) => ls.y + 5)
|
.attr('y', (ls: LinkStatus) => ls.y + 5)
|
||||||
.attr('fill', `black`);
|
.attr('fill', `red`);
|
||||||
status_stopped_label.exit().remove();
|
status_stopped_label.exit().remove();
|
||||||
|
|
||||||
const status_suspended = link_group
|
const status_suspended = link_group
|
||||||
@ -154,7 +154,7 @@ export class InterfaceStatusWidget implements Widget {
|
|||||||
.merge(status_suspended_enter)
|
.merge(status_suspended_enter)
|
||||||
.attr('class', 'status_suspended')
|
.attr('class', 'status_suspended')
|
||||||
.attr('width', (ls: LinkStatus) => {
|
.attr('width', (ls: LinkStatus) => {
|
||||||
return ls.port.length * 8 + 10;
|
return ls.port.length * 10 + 5;
|
||||||
})
|
})
|
||||||
.attr('height', 20)
|
.attr('height', 20)
|
||||||
.attr('x', (ls: LinkStatus) => ls.x - 30)
|
.attr('x', (ls: LinkStatus) => ls.x - 30)
|
||||||
@ -162,8 +162,8 @@ export class InterfaceStatusWidget implements Widget {
|
|||||||
.attr('rx', 8)
|
.attr('rx', 8)
|
||||||
.attr('ry', 8)
|
.attr('ry', 8)
|
||||||
.style('fill', 'white')
|
.style('fill', 'white')
|
||||||
.attr('stroke', '#FFFF00')
|
.attr('stroke', '#fffbc3')
|
||||||
.attr('stroke-width', 3);
|
.attr('stroke-width', 2);
|
||||||
status_suspended.exit().remove();
|
status_suspended.exit().remove();
|
||||||
const status_suspended_label = link_group
|
const status_suspended_label = link_group
|
||||||
.selectAll<SVGTextElement, LinkStatus>('text.status_suspended_label')
|
.selectAll<SVGTextElement, LinkStatus>('text.status_suspended_label')
|
||||||
@ -175,7 +175,7 @@ export class InterfaceStatusWidget implements Widget {
|
|||||||
.text((ls: LinkStatus) => ls.port)
|
.text((ls: LinkStatus) => ls.port)
|
||||||
.attr('x', (ls: LinkStatus) => ls.x - 25)
|
.attr('x', (ls: LinkStatus) => ls.x - 25)
|
||||||
.attr('y', (ls: LinkStatus) => ls.y + 5)
|
.attr('y', (ls: LinkStatus) => ls.y + 5)
|
||||||
.attr('fill', `black`);
|
.attr('fill', `#6b5633`);
|
||||||
status_suspended_label.exit().remove();
|
status_suspended_label.exit().remove();
|
||||||
} else {
|
} else {
|
||||||
const status_started = link_group
|
const status_started = link_group
|
||||||
|
@ -87,6 +87,7 @@ export class LinkWidget implements Widget {
|
|||||||
.filter((l) => {
|
.filter((l) => {
|
||||||
return (
|
return (
|
||||||
!l.capturing &&
|
!l.capturing &&
|
||||||
|
!l.suspend &&
|
||||||
(l.filters.bpf || l.filters.corrupt || l.filters.delay || l.filters.frequency_drop || l.filters.packet_loss)
|
(l.filters.bpf || l.filters.corrupt || l.filters.delay || l.filters.frequency_drop || l.filters.packet_loss)
|
||||||
);
|
);
|
||||||
})
|
})
|
||||||
@ -113,9 +114,7 @@ export class LinkWidget implements Widget {
|
|||||||
link_body
|
link_body
|
||||||
.filter((l) => {
|
.filter((l) => {
|
||||||
return (
|
return (
|
||||||
l.capturing &&
|
l.suspend
|
||||||
l.suspend &&
|
|
||||||
!(l.filters.bpf || l.filters.corrupt || l.filters.delay || l.filters.frequency_drop || l.filters.packet_loss)
|
|
||||||
);
|
);
|
||||||
})
|
})
|
||||||
.append<SVGGElement>('g')
|
.append<SVGGElement>('g')
|
||||||
|
@ -13,8 +13,8 @@
|
|||||||
<th mat-header-cell *matHeaderCellDef>Adapter type</th>
|
<th mat-header-cell *matHeaderCellDef>Adapter type</th>
|
||||||
<td mat-cell *matCellDef="let element; let i = index">
|
<td mat-cell *matCellDef="let element; let i = index">
|
||||||
<mat-select placeholder="Type" [(ngModel)]="element.adapter_type">
|
<mat-select placeholder="Type" [(ngModel)]="element.adapter_type">
|
||||||
<mat-option *ngFor="let type of networkTypes" [value]="type">
|
<mat-option *ngFor="let type of networkTypes" [value]="type.value">
|
||||||
{{ type }}
|
{{ type.name }}
|
||||||
</mat-option>
|
</mat-option>
|
||||||
</mat-select>
|
</mat-select>
|
||||||
</td>
|
</td>
|
||||||
|
@ -98,14 +98,14 @@
|
|||||||
<mat-checkbox [(ngModel)]="iouTemplate.l1_keepalives">
|
<mat-checkbox [(ngModel)]="iouTemplate.l1_keepalives">
|
||||||
Enable layer 1 keepalive messages (non-functional) </mat-checkbox
|
Enable layer 1 keepalive messages (non-functional) </mat-checkbox
|
||||||
><br />
|
><br />
|
||||||
<mat-checkbox [(ngModel)]="defaultSettings"> Use default IOU values for memories </mat-checkbox>
|
<mat-checkbox [(ngModel)]="iouTemplate.use_default_iou_values"> Use default IOU values for memories </mat-checkbox>
|
||||||
<mat-form-field class="form-field" *ngIf="!defaultSettings">
|
<mat-form-field class="form-field" *ngIf="!iouTemplate.use_default_iou_values">
|
||||||
<input matInput type="number" [(ngModel)]="iouTemplate.ram" placeholder="RAM size" />
|
<input matInput type="number" [(ngModel)]="iouTemplate.ram" placeholder="RAM size" />
|
||||||
<span matSuffix>MB</span>
|
<span matSuffix>MB</span>
|
||||||
</mat-form-field>
|
</mat-form-field>
|
||||||
<mat-form-field class="form-field" *ngIf="!defaultSettings">
|
<mat-form-field class="form-field" *ngIf="!iouTemplate.use_default_iou_values">
|
||||||
<input matInput type="number" [(ngModel)]="iouTemplate.nvram" placeholder="NVRAM size" />
|
<input matInput type="number" [(ngModel)]="iouTemplate.nvram" placeholder="NVRAM size" />
|
||||||
<span matSuffix>MB</span>
|
<span matSuffix>KB</span>
|
||||||
</mat-form-field>
|
</mat-form-field>
|
||||||
</mat-expansion-panel>
|
</mat-expansion-panel>
|
||||||
<mat-expansion-panel>
|
<mat-expansion-panel>
|
||||||
|
@ -110,6 +110,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>
|
||||||
|
@ -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;
|
||||||
@ -86,6 +88,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) => {
|
||||||
@ -127,6 +132,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);
|
||||||
|
|
||||||
|
@ -186,13 +186,15 @@
|
|||||||
</mat-form-field>
|
</mat-form-field>
|
||||||
<mat-form-field class="form-field">
|
<mat-form-field class="form-field">
|
||||||
<mat-select placeholder="Type" [(ngModel)]="qemuTemplate.adapter_type">
|
<mat-select placeholder="Type" [(ngModel)]="qemuTemplate.adapter_type">
|
||||||
<mat-option *ngFor="let type of networkTypes" [value]="type[0]"> {{ type[1] }} ({{ type[0] }}) </mat-option>
|
<mat-option *ngFor="let type of networkTypes" [value]="type.value">{{type.name}} ({{type.value}}) </mat-option>
|
||||||
</mat-select>
|
</mat-select>
|
||||||
</mat-form-field>
|
</mat-form-field>
|
||||||
<button mat-button class="configButton" (click)="setCustomAdaptersConfiguratorState(true)">
|
<button mat-button class="configButton" (click)="setCustomAdaptersConfiguratorState(true)">
|
||||||
Configure custom adapters</button
|
Configure custom adapters</button
|
||||||
><br />
|
>
|
||||||
<mat-checkbox [(ngModel)]="qemuTemplate.legacy_networking"> Use the legacy networking mode </mat-checkbox>
|
<br /><mat-checkbox [(ngModel)]="qemuTemplate.legacy_networking"> Use the legacy networking mode </mat-checkbox>
|
||||||
|
<br /><mat-checkbox [(ngModel)]="qemuTemplate.replicate_network_connection_state"> Replicate network connection state </mat-checkbox>
|
||||||
|
|
||||||
</mat-expansion-panel>
|
</mat-expansion-panel>
|
||||||
<mat-expansion-panel>
|
<mat-expansion-panel>
|
||||||
<mat-expansion-panel-header>
|
<mat-expansion-panel-header>
|
||||||
@ -271,6 +273,8 @@
|
|||||||
<input matInput type="text" [(ngModel)]="qemuTemplate.options" placeholder="Options" />
|
<input matInput type="text" [(ngModel)]="qemuTemplate.options" placeholder="Options" />
|
||||||
</mat-form-field>
|
</mat-form-field>
|
||||||
<mat-checkbox [(ngModel)]="qemuTemplate.linked_clone"> Use as a linked base VM </mat-checkbox>
|
<mat-checkbox [(ngModel)]="qemuTemplate.linked_clone"> Use as a linked base VM </mat-checkbox>
|
||||||
|
<br /><mat-checkbox [(ngModel)]="qemuTemplate.tpm"> Enable the Trusted Platform Module (TPM)</mat-checkbox>
|
||||||
|
<br /><mat-checkbox [(ngModel)]="qemuTemplate.uefi"> Enable the UEFI boot mode </mat-checkbox>
|
||||||
</mat-card-content>
|
</mat-card-content>
|
||||||
</mat-card>
|
</mat-card>
|
||||||
</mat-expansion-panel>
|
</mat-expansion-panel>
|
||||||
|
@ -2,3 +2,11 @@
|
|||||||
<mat-icon>web_asset</mat-icon>
|
<mat-icon>web_asset</mat-icon>
|
||||||
<span>Console</span>
|
<span>Console</span>
|
||||||
</button>
|
</button>
|
||||||
|
<button
|
||||||
|
mat-menu-item
|
||||||
|
*ngIf="node.node_type === 'docker' || node.node_type === 'dynamips' || node.node_type === 'qemu'"
|
||||||
|
(click)="openConsole(auxiliary=true)"
|
||||||
|
>
|
||||||
|
<mat-icon>web_asset</mat-icon>
|
||||||
|
<span>Auxiliary console</span>
|
||||||
|
</button>
|
||||||
|
@ -1,8 +1,12 @@
|
|||||||
import { Component, Input } from '@angular/core';
|
import { Component, Input } from '@angular/core';
|
||||||
|
import { DeviceDetectorService } from 'ngx-device-detector';
|
||||||
import { Node } from '../../../../../cartography/models/node';
|
import { Node } from '../../../../../cartography/models/node';
|
||||||
import { Server } from '../../../../../models/server';
|
import { Server } from '../../../../../models/server';
|
||||||
import { NodeService } from '../../../../../services/node.service';
|
import { NodeService } from '../../../../../services/node.service';
|
||||||
import { ToasterService } from '../../../../../services/toaster.service';
|
import { ToasterService } from '../../../../../services/toaster.service';
|
||||||
|
import { ProtocolHandlerService } from '../../../../../services/protocol-handler.service';
|
||||||
|
|
||||||
|
import * as ipaddr from 'ipaddr.js';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-console-device-action-browser',
|
selector: 'app-console-device-action-browser',
|
||||||
@ -12,16 +16,16 @@ export class ConsoleDeviceActionBrowserComponent {
|
|||||||
@Input() server: Server;
|
@Input() server: Server;
|
||||||
@Input() node: Node;
|
@Input() node: Node;
|
||||||
|
|
||||||
constructor(private toasterService: ToasterService, private nodeService: NodeService) {}
|
constructor(private toasterService: ToasterService, private nodeService: NodeService, private protocolHandlerService: ProtocolHandlerService) {}
|
||||||
|
|
||||||
openConsole() {
|
openConsole(auxiliary: boolean = false) {
|
||||||
this.nodeService.getNode(this.server, this.node).subscribe((node: Node) => {
|
this.nodeService.getNode(this.server, this.node).subscribe((node: Node) => {
|
||||||
this.node = node;
|
this.node = node;
|
||||||
this.startConsole();
|
this.startConsole(auxiliary);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
startConsole() {
|
startConsole(auxiliary: boolean) {
|
||||||
if (this.node.status !== 'started') {
|
if (this.node.status !== 'started') {
|
||||||
this.toasterService.error('This node must be started before a console can be opened');
|
this.toasterService.error('This node must be started before a console can be opened');
|
||||||
} else {
|
} else {
|
||||||
@ -33,20 +37,41 @@ export class ConsoleDeviceActionBrowserComponent {
|
|||||||
this.node.console_host = this.server.host;
|
this.node.console_host = this.server.host;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (
|
try {
|
||||||
this.node.console_type === 'telnet' ||
|
var uri;
|
||||||
this.node.console_type === 'vnc' ||
|
var host = this.node.console_host;
|
||||||
this.node.console_type === 'spice'
|
if (ipaddr.IPv6.isValid(host)) {
|
||||||
) {
|
host = `[${host}]`;
|
||||||
try {
|
|
||||||
location.assign(
|
|
||||||
`gns3+${this.node.console_type}://${this.node.console_host}:${this.node.console}?name=${this.node.name}&project_id=${this.node.project_id}&node_id=${this.node.node_id}`
|
|
||||||
);
|
|
||||||
} catch (e) {
|
|
||||||
this.toasterService.error(e);
|
|
||||||
}
|
}
|
||||||
} else {
|
if (this.node.console_type === 'telnet') {
|
||||||
this.toasterService.error('Supported console types: telnet, vnc, spice.');
|
|
||||||
|
var console_port;
|
||||||
|
if (auxiliary === true) {
|
||||||
|
console_port = this.node.properties.aux;
|
||||||
|
if (console_port === undefined) {
|
||||||
|
this.toasterService.error('Auxiliary console port is not set.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
console_port = this.node.console;
|
||||||
|
}
|
||||||
|
uri = `gns3+telnet://${host}:${console_port}?name=${this.node.name}&project_id=${this.node.project_id}&node_id=${this.node.node_id}`;
|
||||||
|
} else if (this.node.console_type === 'vnc') {
|
||||||
|
uri = `gns3+vnc://${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.startsWith('spice')) {
|
||||||
|
uri = `gns3+spice://${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.startsWith('http')) {
|
||||||
|
uri = `${this.node.console_type}://${host}:${this.node.console}`
|
||||||
|
return window.open(uri); // open an http console directly in a new window/tab
|
||||||
|
} else {
|
||||||
|
this.toasterService.error('Supported console types are: telnet, vnc, spice and spice+agent.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.protocolHandlerService.open(uri);
|
||||||
|
|
||||||
|
} catch (e) {
|
||||||
|
this.toasterService.error(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -29,7 +29,7 @@ export class ConsoleDeviceActionComponent implements OnInit {
|
|||||||
let consoleCommand = this.settingsService.getConsoleSettings()
|
let consoleCommand = this.settingsService.getConsoleSettings()
|
||||||
? this.settingsService.getConsoleSettings()
|
? this.settingsService.getConsoleSettings()
|
||||||
: this.nodeService.getDefaultCommand();
|
: this.nodeService.getDefaultCommand();
|
||||||
const startedNodes = this.nodes.filter((node) => node.status === 'started');
|
const startedNodes = this.nodes.filter((node) => node.status === 'started' && node.console_type !== 'none');
|
||||||
|
|
||||||
if (startedNodes.length === 0) {
|
if (startedNodes.length === 0) {
|
||||||
this.toasterService.error('Device needs to be started in order to console to it.');
|
this.toasterService.error('Device needs to be started in order to console to it.');
|
||||||
@ -37,7 +37,7 @@ export class ConsoleDeviceActionComponent implements OnInit {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for (var node of this.nodes) {
|
for (var node of this.nodes) {
|
||||||
if (node.status !== 'started') {
|
if (node.status !== 'started' && node.console_type !== 'none') {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
<div class="modal-form-container">
|
<div class="modal-form-container">
|
||||||
<form [formGroup]="formGroup">
|
<form [formGroup]="formGroup">
|
||||||
<mat-form-field class="form-field">
|
<mat-form-field class="form-field" *ngIf="element.fill !== undefined">
|
||||||
<input
|
<input
|
||||||
matInput
|
matInput
|
||||||
[ngModelOptions]="{ standalone: true }"
|
[ngModelOptions]="{ standalone: true }"
|
||||||
@ -23,7 +23,13 @@
|
|||||||
</mat-form-field>
|
</mat-form-field>
|
||||||
|
|
||||||
<mat-form-field class="form-field">
|
<mat-form-field class="form-field">
|
||||||
<input matInput formControlName="borderWidth" placeholder="Border width" type="number" />
|
<input
|
||||||
|
matInput formControlName="borderWidth"
|
||||||
|
placeholder="Border width"
|
||||||
|
type="number"
|
||||||
|
min="0"
|
||||||
|
max="100"
|
||||||
|
/>
|
||||||
</mat-form-field>
|
</mat-form-field>
|
||||||
|
|
||||||
<mat-form-field class="form-field" *ngIf="element.stroke_dasharray">
|
<mat-form-field class="form-field" *ngIf="element.stroke_dasharray">
|
||||||
@ -36,6 +42,41 @@
|
|||||||
/>
|
/>
|
||||||
</mat-form-field>
|
</mat-form-field>
|
||||||
|
|
||||||
|
<mat-form-field class="form-field" *ngIf="element.width !== undefined">
|
||||||
|
<input
|
||||||
|
matInput
|
||||||
|
[ngModelOptions]="{ standalone: true }"
|
||||||
|
placeholder="Width"
|
||||||
|
min="0"
|
||||||
|
type="number"
|
||||||
|
[(ngModel)]="element.width"
|
||||||
|
/>
|
||||||
|
</mat-form-field>
|
||||||
|
|
||||||
|
<mat-form-field class="form-field" *ngIf="element.height !== undefined">
|
||||||
|
<input
|
||||||
|
matInput
|
||||||
|
[ngModelOptions]="{ standalone: true }"
|
||||||
|
placeholder="Height"
|
||||||
|
min="0"
|
||||||
|
type="number"
|
||||||
|
[(ngModel)]="element.height"
|
||||||
|
/>
|
||||||
|
</mat-form-field>
|
||||||
|
|
||||||
|
|
||||||
|
<mat-form-field class="form-field" *ngIf="element.rx !== undefined">
|
||||||
|
<input
|
||||||
|
matInput
|
||||||
|
[ngModelOptions]="{ standalone: true }"
|
||||||
|
placeholder="Corner radius"
|
||||||
|
type="number"
|
||||||
|
min="0"
|
||||||
|
max="100"
|
||||||
|
[(ngModel)]="element.rx"
|
||||||
|
/>
|
||||||
|
</mat-form-field>
|
||||||
|
|
||||||
<mat-form-field class="form-field">
|
<mat-form-field class="form-field">
|
||||||
<input matInput formControlName="rotation" placeholder="Rotation" type="number" />
|
<input matInput formControlName="rotation" placeholder="Rotation" type="number" />
|
||||||
</mat-form-field>
|
</mat-form-field>
|
||||||
|
@ -49,6 +49,8 @@ export class StyleEditorDialogComponent implements OnInit {
|
|||||||
|
|
||||||
if (this.drawing.element instanceof RectElement || this.drawing.element instanceof EllipseElement) {
|
if (this.drawing.element instanceof RectElement || this.drawing.element instanceof EllipseElement) {
|
||||||
this.element.fill = this.drawing.element.fill;
|
this.element.fill = this.drawing.element.fill;
|
||||||
|
this.element.width = this.drawing.element.width;
|
||||||
|
this.element.height = this.drawing.element.height;
|
||||||
this.element.stroke = this.drawing.element.stroke;
|
this.element.stroke = this.drawing.element.stroke;
|
||||||
this.element.stroke_dasharray = this.drawing.element.stroke_dasharray;
|
this.element.stroke_dasharray = this.drawing.element.stroke_dasharray;
|
||||||
this.element.stroke_width = this.drawing.element.stroke_width;
|
this.element.stroke_width = this.drawing.element.stroke_width;
|
||||||
@ -58,6 +60,11 @@ export class StyleEditorDialogComponent implements OnInit {
|
|||||||
this.element.stroke_width = this.drawing.element.stroke_width;
|
this.element.stroke_width = this.drawing.element.stroke_width;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (this.drawing.element instanceof RectElement) {
|
||||||
|
this.element.rx = this.drawing.element.rx;
|
||||||
|
this.element.ry = this.drawing.element.ry;
|
||||||
|
}
|
||||||
|
|
||||||
if (this.element.stroke_width === undefined) this.element.stroke_width = 0;
|
if (this.element.stroke_width === undefined) this.element.stroke_width = 0;
|
||||||
this.formGroup.controls['borderWidth'].setValue(this.element.stroke_width);
|
this.formGroup.controls['borderWidth'].setValue(this.element.stroke_width);
|
||||||
this.formGroup.controls['rotation'].setValue(this.drawing.rotation);
|
this.formGroup.controls['rotation'].setValue(this.drawing.rotation);
|
||||||
@ -74,6 +81,8 @@ export class StyleEditorDialogComponent implements OnInit {
|
|||||||
|
|
||||||
if (this.drawing.element instanceof RectElement || this.drawing.element instanceof EllipseElement) {
|
if (this.drawing.element instanceof RectElement || this.drawing.element instanceof EllipseElement) {
|
||||||
this.drawing.element.fill = this.element.fill;
|
this.drawing.element.fill = this.element.fill;
|
||||||
|
this.drawing.element.width = this.element.width;
|
||||||
|
this.drawing.element.height = this.element.height;
|
||||||
this.drawing.element.stroke = this.element.stroke;
|
this.drawing.element.stroke = this.element.stroke;
|
||||||
this.drawing.element.stroke_dasharray = this.element.stroke_dasharray;
|
this.drawing.element.stroke_dasharray = this.element.stroke_dasharray;
|
||||||
this.drawing.element.stroke_width = this.element.stroke_width;
|
this.drawing.element.stroke_width = this.element.stroke_width;
|
||||||
@ -83,6 +92,11 @@ export class StyleEditorDialogComponent implements OnInit {
|
|||||||
this.drawing.element.stroke_width = this.element.stroke_width;
|
this.drawing.element.stroke_width = this.element.stroke_width;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (this.drawing.element instanceof RectElement) {
|
||||||
|
this.drawing.element.rx = this.element.rx;
|
||||||
|
this.drawing.element.ry = this.element.rx; // set ry with rx because we don't have ry in the form
|
||||||
|
}
|
||||||
|
|
||||||
let mapDrawing = this.drawingToMapDrawingConverter.convert(this.drawing);
|
let mapDrawing = this.drawingToMapDrawingConverter.convert(this.drawing);
|
||||||
mapDrawing.element = this.drawing.element;
|
mapDrawing.element = this.drawing.element;
|
||||||
|
|
||||||
@ -100,7 +114,11 @@ export class StyleEditorDialogComponent implements OnInit {
|
|||||||
|
|
||||||
export class ElementData {
|
export class ElementData {
|
||||||
fill: string;
|
fill: string;
|
||||||
|
width: number;
|
||||||
|
height: number;
|
||||||
stroke: string;
|
stroke: string;
|
||||||
stroke_width: number;
|
stroke_width: number;
|
||||||
stroke_dasharray: string;
|
stroke_dasharray: string;
|
||||||
|
rx: number;
|
||||||
|
ry: number;
|
||||||
}
|
}
|
||||||
|
@ -91,6 +91,7 @@ export class ImportApplianceComponent implements OnInit {
|
|||||||
template.console_auto_start = appliance.iou.console_auto_start;
|
template.console_auto_start = appliance.iou.console_auto_start;
|
||||||
template.ethernet_adapters = appliance.iou.ethernet_adapters;
|
template.ethernet_adapters = appliance.iou.ethernet_adapters;
|
||||||
template.l1_keepalives = appliance.iou.l1_keepalives;
|
template.l1_keepalives = appliance.iou.l1_keepalives;
|
||||||
|
template.use_default_iou_values = appliance.iou.use_default_iou_values;
|
||||||
template.nvram = appliance.iou.nvram;
|
template.nvram = appliance.iou.nvram;
|
||||||
template.ram = appliance.iou.ram;
|
template.ram = appliance.iou.ram;
|
||||||
template.serial_adapters = appliance.iou.serial_adapters;
|
template.serial_adapters = appliance.iou.serial_adapters;
|
||||||
|
@ -6,6 +6,7 @@ import { MatMenuModule } from '@angular/material/menu';
|
|||||||
import { BrowserModule } from '@angular/platform-browser';
|
import { BrowserModule } from '@angular/platform-browser';
|
||||||
import { RouterTestingModule } from '@angular/router/testing';
|
import { RouterTestingModule } from '@angular/router/testing';
|
||||||
import { ToasterService } from '../../../services/toaster.service';
|
import { ToasterService } from '../../../services/toaster.service';
|
||||||
|
import { ProtocolHandlerService } from '../../../services/protocol-handler.service';
|
||||||
import { of } from 'rxjs';
|
import { of } from 'rxjs';
|
||||||
import { NodesDataSource } from '../../../cartography/datasources/nodes-datasource';
|
import { NodesDataSource } from '../../../cartography/datasources/nodes-datasource';
|
||||||
import { ProjectWebServiceHandler, WebServiceMessage } from '../../../handlers/project-web-service-handler';
|
import { ProjectWebServiceHandler, WebServiceMessage } from '../../../handlers/project-web-service-handler';
|
||||||
@ -38,6 +39,7 @@ describe('LogConsoleComponent', () => {
|
|||||||
let nodeConsoleService: NodeConsoleService;
|
let nodeConsoleService: NodeConsoleService;
|
||||||
let mapSettingsService: MapSettingsService;
|
let mapSettingsService: MapSettingsService;
|
||||||
let toasterService: ToasterService;
|
let toasterService: ToasterService;
|
||||||
|
let protocolHandlerService: ProtocolHandlerService;
|
||||||
|
|
||||||
let httpServer = new HttpServer({} as HttpClient, {} as ServerErrorHandler);
|
let httpServer = new HttpServer({} as HttpClient, {} as ServerErrorHandler);
|
||||||
|
|
||||||
@ -52,6 +54,7 @@ describe('LogConsoleComponent', () => {
|
|||||||
{ provide: HttpServer, useValue: httpServer },
|
{ provide: HttpServer, useValue: httpServer },
|
||||||
NodeConsoleService,
|
NodeConsoleService,
|
||||||
ToasterService,
|
ToasterService,
|
||||||
|
ProtocolHandlerService,
|
||||||
MapSettingsService
|
MapSettingsService
|
||||||
],
|
],
|
||||||
declarations: [LogConsoleComponent],
|
declarations: [LogConsoleComponent],
|
||||||
@ -59,6 +62,7 @@ describe('LogConsoleComponent', () => {
|
|||||||
}).compileComponents();
|
}).compileComponents();
|
||||||
|
|
||||||
toasterService = TestBed.inject(ToasterService);
|
toasterService = TestBed.inject(ToasterService);
|
||||||
|
protocolHandlerService = TestBed.inject(ProtocolHandlerService);
|
||||||
mapSettingsService = TestBed.inject(MapSettingsService);
|
mapSettingsService = TestBed.inject(MapSettingsService);
|
||||||
nodeConsoleService = TestBed.inject(NodeConsoleService);
|
nodeConsoleService = TestBed.inject(NodeConsoleService);
|
||||||
}));
|
}));
|
||||||
|
@ -23,9 +23,11 @@ import { Server } from '../../../models/server';
|
|||||||
import { HttpServer } from '../../../services/http-server.service';
|
import { HttpServer } from '../../../services/http-server.service';
|
||||||
import { NodeService } from '../../../services/node.service';
|
import { NodeService } from '../../../services/node.service';
|
||||||
import { NodeConsoleService } from '../../../services/nodeConsole.service';
|
import { NodeConsoleService } from '../../../services/nodeConsole.service';
|
||||||
|
import { ProtocolHandlerService } from '../../../services/protocol-handler.service';
|
||||||
import { ThemeService } from '../../../services/theme.service';
|
import { ThemeService } from '../../../services/theme.service';
|
||||||
import { version } from '../../../version';
|
import { version } from '../../../version';
|
||||||
import { LogEventsDataSource } from './log-events-datasource';
|
import { LogEventsDataSource } from './log-events-datasource';
|
||||||
|
import * as ipaddr from 'ipaddr.js';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||||
@ -69,6 +71,7 @@ export class LogConsoleComponent implements OnInit, AfterViewInit, OnDestroy {
|
|||||||
private projectWebServiceHandler: ProjectWebServiceHandler,
|
private projectWebServiceHandler: ProjectWebServiceHandler,
|
||||||
private nodeService: NodeService,
|
private nodeService: NodeService,
|
||||||
private nodesDataSource: NodesDataSource,
|
private nodesDataSource: NodesDataSource,
|
||||||
|
private protocolHandlerService: ProtocolHandlerService,
|
||||||
private logEventsDataSource: LogEventsDataSource,
|
private logEventsDataSource: LogEventsDataSource,
|
||||||
private httpService: HttpServer,
|
private httpService: HttpServer,
|
||||||
private themeService: ThemeService,
|
private themeService: ThemeService,
|
||||||
@ -224,20 +227,26 @@ export class LogConsoleComponent implements OnInit, AfterViewInit, OnDestroy {
|
|||||||
} else if (this.regexConsole.test(this.command)) {
|
} else if (this.regexConsole.test(this.command)) {
|
||||||
if (node.status === 'started') {
|
if (node.status === 'started') {
|
||||||
this.showCommand(`Launching console for node ${splittedCommand[1]}...`);
|
this.showCommand(`Launching console for node ${splittedCommand[1]}...`);
|
||||||
|
var host = node.console_host;
|
||||||
|
if (ipaddr.IPv6.isValid(host)) {
|
||||||
|
host = `[${host}]`;
|
||||||
|
}
|
||||||
if (node.console_type === 'telnet') {
|
if (node.console_type === 'telnet') {
|
||||||
location.assign(
|
this.protocolHandlerService.open(
|
||||||
`gns3+telnet://${node.console_host}:${node.console}?name=${node.name}&project_id=${node.project_id}&node_id=${node.node_id}`
|
`gns3+telnet://${host}:${node.console}?name=${node.name}&project_id=${node.project_id}&node_id=${node.node_id}`
|
||||||
);
|
);
|
||||||
} else if (node.console_type === 'vnc') {
|
} else if (node.console_type === 'vnc') {
|
||||||
location.assign(
|
this.protocolHandlerService.open(
|
||||||
`gns3+vnc://${node.console_host}:${node.console}?name=${node.name}&project_id=${node.project_id}&node_id=${node.node_id}`
|
`gns3+vnc://${host}:${node.console}?name=${node.name}&project_id=${node.project_id}&node_id=${node.node_id}`
|
||||||
);
|
);
|
||||||
} else if (node.console_type === 'spice') {
|
} else if (node.console_type.startsWith('spice')) {
|
||||||
location.assign(
|
this.protocolHandlerService.open(
|
||||||
`gns3+spice://${node.console_host}:${node.console}?name=${node.name}&project_id=${node.project_id}&node_id=${node.node_id}`
|
`gns3+spice://${host}:${node.console}?name=${node.name}&project_id=${node.project_id}&node_id=${node.node_id}`
|
||||||
);
|
);
|
||||||
|
} else if (node.console_type.startsWith('http')) {
|
||||||
|
window.open(`${node.console_type}://${host}:${node.console}`);
|
||||||
} else {
|
} else {
|
||||||
this.showCommand('Supported console types: telnet, vnc, spice.');
|
this.showCommand('Supported console types are: telnet, vnc, spice and spice+agent');
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
this.showCommand(`This node must be started before a console can be opened.`);
|
this.showCommand(`This node must be started before a console can be opened.`);
|
||||||
@ -297,28 +306,28 @@ export class LogConsoleComponent implements OnInit, AfterViewInit, OnDestroy {
|
|||||||
|
|
||||||
printNode(node: Node): string {
|
printNode(node: Node): string {
|
||||||
return (
|
return (
|
||||||
`command_line: ${node.command_line},
|
`command_line: ${node.command_line},
|
||||||
compute_id: ${node.compute_id},
|
compute_id: ${node.compute_id},
|
||||||
console: ${node.console},
|
console: ${node.console},
|
||||||
console_host: ${node.console_host},
|
console_host: ${node.console_host},
|
||||||
console_type: ${node.console_type},
|
console_type: ${node.console_type},
|
||||||
first_port_name: ${node.first_port_name},
|
first_port_name: ${node.first_port_name},
|
||||||
height: ${node.height},
|
height: ${node.height},
|
||||||
label: ${node.label.text},
|
label: ${node.label.text},
|
||||||
name: ${node.name},
|
name: ${node.name},
|
||||||
node_directory: ${node.node_directory},
|
node_directory: ${node.node_directory},
|
||||||
node_id: ${node.node_id},
|
node_id: ${node.node_id},
|
||||||
node_type: ${node.node_type},
|
node_type: ${node.node_type},
|
||||||
port_name_format: ${node.port_name_format},
|
port_name_format: ${node.port_name_format},
|
||||||
port_segment_size: ${node.port_segment_size}, ` +
|
port_segment_size: ${node.port_segment_size}, ` +
|
||||||
this.printPorts(node.ports) +
|
this.printPorts(node.ports) +
|
||||||
`project_id: ${node.project_id},
|
`project_id: ${node.project_id},
|
||||||
status: ${node.status},
|
status: ${node.status},
|
||||||
symbol: ${node.symbol},
|
symbol: ${node.symbol},
|
||||||
symbol_url: ${node.symbol_url},
|
symbol_url: ${node.symbol_url},
|
||||||
width: ${node.width},
|
width: ${node.width},
|
||||||
x: ${node.x},
|
x: ${node.x},
|
||||||
y: ${node.y},
|
y: ${node.y},
|
||||||
z: ${node.z}`
|
z: ${node.z}`
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -328,31 +337,31 @@ export class LogConsoleComponent implements OnInit, AfterViewInit, OnDestroy {
|
|||||||
ports.forEach((port) => {
|
ports.forEach((port) => {
|
||||||
response =
|
response =
|
||||||
response +
|
response +
|
||||||
`adapter_number: ${port.adapter_number},
|
`adapter_number: ${port.adapter_number},
|
||||||
link_type: ${port.link_type},
|
link_type: ${port.link_type},
|
||||||
name: ${port.name},
|
name: ${port.name},
|
||||||
port_number: ${port.port_number},
|
port_number: ${port.port_number},
|
||||||
short_name: ${port.short_name}, `;
|
short_name: ${port.short_name}, `;
|
||||||
});
|
});
|
||||||
return response;
|
return response;
|
||||||
}
|
}
|
||||||
|
|
||||||
printLink(link: Link): string {
|
printLink(link: Link): string {
|
||||||
return `capture_file_name: ${link.capture_file_name},
|
return `capture_file_name: ${link.capture_file_name},
|
||||||
capture_file_path: ${link.capture_file_path},
|
capture_file_path: ${link.capture_file_path},
|
||||||
capturing: ${link.capturing},
|
capturing: ${link.capturing},
|
||||||
link_id: ${link.link_id},
|
link_id: ${link.link_id},
|
||||||
link_type: ${link.link_type},
|
link_type: ${link.link_type},
|
||||||
project_id: ${link.project_id},
|
project_id: ${link.project_id},
|
||||||
suspend: ${link.suspend}, `;
|
suspend: ${link.suspend}, `;
|
||||||
}
|
}
|
||||||
|
|
||||||
printDrawing(drawing: Drawing): string {
|
printDrawing(drawing: Drawing): string {
|
||||||
return `drawing_id: ${drawing.drawing_id},
|
return `drawing_id: ${drawing.drawing_id},
|
||||||
project_id: ${drawing.project_id},
|
project_id: ${drawing.project_id},
|
||||||
rotation: ${drawing.rotation},
|
rotation: ${drawing.rotation},
|
||||||
x: ${drawing.x},
|
x: ${drawing.x},
|
||||||
y: ${drawing.y},
|
y: ${drawing.y},
|
||||||
z: ${drawing.z}`;
|
z: ${drawing.z}`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -77,7 +77,7 @@
|
|||||||
</ng-container>
|
</ng-container>
|
||||||
|
|
||||||
<!-- <ng-container matColumnDef="expandedDetail">
|
<!-- <ng-container matColumnDef="expandedDetail">
|
||||||
<mat-cell *matCellDef="let detail">
|
<mat-cell *matCellDef="let detail">
|
||||||
The symbol for {{detail.element}}
|
The symbol for {{detail.element}}
|
||||||
</mat-cell>
|
</mat-cell>
|
||||||
</ng-container> -->
|
</ng-container> -->
|
||||||
@ -85,14 +85,14 @@
|
|||||||
<mat-header-row *matHeaderRowDef="displayedColumns"></mat-header-row>
|
<mat-header-row *matHeaderRowDef="displayedColumns"></mat-header-row>
|
||||||
<mat-row *matRowDef="let row; columns: displayedColumns"></mat-row>
|
<mat-row *matRowDef="let row; columns: displayedColumns"></mat-row>
|
||||||
|
|
||||||
<!-- <mat-row
|
<!-- <mat-row
|
||||||
*matRowDef="let row; columns: displayedColumns;"
|
*matRowDef="let row; columns: displayedColumns;"
|
||||||
matRipple
|
matRipple
|
||||||
class="element-row"
|
class="element-row"
|
||||||
[class.expanded]="expandedElement == row"
|
[class.expanded]="expandedElement == row"
|
||||||
(click)="expandedElement = row">
|
(click)="expandedElement = row">
|
||||||
</mat-row>
|
</mat-row>
|
||||||
<mat-row
|
<mat-row
|
||||||
*matRowDef="let row; columns: ['expandedDetail']; when: isExpansionDetailRow"
|
*matRowDef="let row; columns: ['expandedDetail']; when: isExpansionDetailRow"
|
||||||
[@detailExpand]="row.element == expandedElement ? 'expanded' : 'collapsed'"
|
[@detailExpand]="row.element == expandedElement ? 'expanded' : 'collapsed'"
|
||||||
style="overflow: hidden">
|
style="overflow: hidden">
|
||||||
@ -196,6 +196,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 }}
|
||||||
@ -260,6 +292,70 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="list-item-inside" *ngIf="version.images.hdc_disk_image">
|
||||||
|
<span>
|
||||||
|
{{ version.images.hdb_disk_image }}
|
||||||
|
</span>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<span *ngIf="checkImageFromVersion(version.images.hdc_disk_image)"
|
||||||
|
><mat-icon matTooltip="Ready to install" matTooltipClass="custom-tooltip">check</mat-icon></span
|
||||||
|
>
|
||||||
|
<span *ngIf="!checkImageFromVersion(version.images.hdc_disk_image)"
|
||||||
|
><mat-icon matTooltip="Missing" matTooltipClass="custom-tooltip">close</mat-icon></span
|
||||||
|
>
|
||||||
|
|
||||||
|
<input
|
||||||
|
type="file"
|
||||||
|
class="non-visible"
|
||||||
|
#file4
|
||||||
|
(change)="importImage($event, version.images.hdc_disk_image)"
|
||||||
|
ng2FileSelect
|
||||||
|
[uploader]="uploaderImage"
|
||||||
|
/>
|
||||||
|
<button class="button" mat-raised-button (click)="file4.click()">Import</button>
|
||||||
|
<button
|
||||||
|
class="button"
|
||||||
|
mat-raised-button
|
||||||
|
(click)="downloadImageFromVersion(version.images.hdc_disk_image)"
|
||||||
|
>
|
||||||
|
Download
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="list-item-inside" *ngIf="version.images.hdd_disk_image">
|
||||||
|
<span>
|
||||||
|
{{ version.images.hdd_disk_image }}
|
||||||
|
</span>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<span *ngIf="checkImageFromVersion(version.images.hdd_disk_image)"
|
||||||
|
><mat-icon matTooltip="Ready to install" matTooltipClass="custom-tooltip">check</mat-icon></span
|
||||||
|
>
|
||||||
|
<span *ngIf="!checkImageFromVersion(version.images.hdd_disk_image)"
|
||||||
|
><mat-icon matTooltip="Missing" matTooltipClass="custom-tooltip">close</mat-icon></span
|
||||||
|
>
|
||||||
|
|
||||||
|
<input
|
||||||
|
type="file"
|
||||||
|
class="non-visible"
|
||||||
|
#file5
|
||||||
|
(change)="importImage($event, version.images.hdd_disk_image)"
|
||||||
|
ng2FileSelect
|
||||||
|
[uploader]="uploaderImage"
|
||||||
|
/>
|
||||||
|
<button class="button" mat-raised-button (click)="file5.click()">Import</button>
|
||||||
|
<button
|
||||||
|
class="button"
|
||||||
|
mat-raised-button
|
||||||
|
(click)="downloadImageFromVersion(version.images.hdd_disk_image)"
|
||||||
|
>
|
||||||
|
Download
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="list-item-inside" *ngIf="version.images.cdrom_image">
|
<div class="list-item-inside" *ngIf="version.images.cdrom_image">
|
||||||
<span>
|
<span>
|
||||||
{{ version.images.cdrom_image}}
|
{{ version.images.cdrom_image}}
|
||||||
@ -276,12 +372,12 @@
|
|||||||
<input
|
<input
|
||||||
type="file"
|
type="file"
|
||||||
class="non-visible"
|
class="non-visible"
|
||||||
#file4
|
#file6
|
||||||
(change)="importImage($event, version.images.cdrom_image)"
|
(change)="importImage($event, version.images.cdrom_image)"
|
||||||
ng2FileSelect
|
ng2FileSelect
|
||||||
[uploader]="uploaderImage"
|
[uploader]="uploaderImage"
|
||||||
/>
|
/>
|
||||||
<button class="button" mat-raised-button (click)="file4.click()">Import</button>
|
<button class="button" mat-raised-button (click)="file6.click()">Import</button>
|
||||||
<button
|
<button
|
||||||
class="button"
|
class="button"
|
||||||
mat-raised-button
|
mat-raised-button
|
||||||
|
@ -320,17 +320,17 @@ export class NewTemplateDialogComponent implements OnInit {
|
|||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
if (this.qemuBinaries.length) {
|
if (this.qemuBinaries.length) {
|
||||||
if (this.applianceToInstall.qemu.arch === 'x86_64') {
|
if (this.applianceToInstall.qemu.arch === 'x86_64') {
|
||||||
let filtered_binaries = this.qemuBinaries.filter((n) => n.path.includes('qemu-system-x86_64'));
|
let filtered_binaries = this.qemuBinaries.filter((n) => n.path.endsWith('qemu-system-x86_64'));
|
||||||
if (filtered_binaries.length) {
|
if (filtered_binaries.length) {
|
||||||
this.selectedBinary = filtered_binaries[0];
|
this.selectedBinary = filtered_binaries[0];
|
||||||
}
|
}
|
||||||
} else if (this.applianceToInstall.qemu.arch === 'i386') {
|
} else if (this.applianceToInstall.qemu.arch === 'i386') {
|
||||||
let filtered_binaries = this.qemuBinaries.filter((n) => n.path.includes('qemu-system-i386'));
|
let filtered_binaries = this.qemuBinaries.filter((n) => n.path.endsWith('qemu-system-i386'));
|
||||||
if (filtered_binaries.length) {
|
if (filtered_binaries.length) {
|
||||||
this.selectedBinary = filtered_binaries[0];
|
this.selectedBinary = filtered_binaries[0];
|
||||||
}
|
}
|
||||||
} else if (this.applianceToInstall.qemu.arch === 'x86_64') {
|
} else if (this.applianceToInstall.qemu.arch === 'arm') {
|
||||||
let filtered_binaries = this.qemuBinaries.filter((n) => n.path.includes('qemu-system-arm'));
|
let filtered_binaries = this.qemuBinaries.filter((n) => n.path.endsWith('qemu-system-arm'));
|
||||||
if (filtered_binaries.length) {
|
if (filtered_binaries.length) {
|
||||||
this.selectedBinary = filtered_binaries[0];
|
this.selectedBinary = filtered_binaries[0];
|
||||||
}
|
}
|
||||||
@ -364,7 +364,7 @@ export class NewTemplateDialogComponent implements OnInit {
|
|||||||
autoFocus: false,
|
autoFocus: false,
|
||||||
disableClose: true,
|
disableClose: true,
|
||||||
});
|
});
|
||||||
dialogRef.componentInstance.confirmationMessage = `This is not the correct file.
|
dialogRef.componentInstance.confirmationMessage = `This is not the correct file.
|
||||||
The MD5 sum is ${output} and should be ${imageToInstall.md5sum}. Do you want to accept it at your own risks?`;
|
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) => {
|
dialogRef.afterClosed().subscribe((answer: boolean) => {
|
||||||
if (answer) {
|
if (answer) {
|
||||||
@ -401,7 +401,8 @@ export class NewTemplateDialogComponent implements OnInit {
|
|||||||
this.progressService.activate();
|
this.progressService.activate();
|
||||||
};
|
};
|
||||||
|
|
||||||
fileReader.readAsText(file);
|
//fileReader.readAsText(file); //web browser out ouf memory when upload large image file
|
||||||
|
fileReader.onloadend(undefined);
|
||||||
}
|
}
|
||||||
|
|
||||||
checkImageFromVersion(image: string): boolean {
|
checkImageFromVersion(image: string): boolean {
|
||||||
@ -485,7 +486,7 @@ export class NewTemplateDialogComponent implements OnInit {
|
|||||||
iouTemplate.startup_config = this.applianceToInstall.iou.startup_config;
|
iouTemplate.startup_config = this.applianceToInstall.iou.startup_config;
|
||||||
iouTemplate.builtin = this.applianceToInstall.builtin;
|
iouTemplate.builtin = this.applianceToInstall.builtin;
|
||||||
iouTemplate.category = this.getCategory();
|
iouTemplate.category = this.getCategory();
|
||||||
iouTemplate.default_name_format = this.applianceToInstall.port_name_format;
|
iouTemplate.default_name_format = this.applianceToInstall.default_name_format;
|
||||||
iouTemplate.symbol = this.applianceToInstall.symbol;
|
iouTemplate.symbol = this.applianceToInstall.symbol;
|
||||||
iouTemplate.compute_id = this.isGns3VmChosen ? 'vm' : 'local';
|
iouTemplate.compute_id = this.isGns3VmChosen ? 'vm' : 'local';
|
||||||
iouTemplate.template_id = uuid();
|
iouTemplate.template_id = uuid();
|
||||||
@ -534,7 +535,7 @@ export class NewTemplateDialogComponent implements OnInit {
|
|||||||
iosTemplate.slot7 = this.applianceToInstall.dynamips.slot7;
|
iosTemplate.slot7 = this.applianceToInstall.dynamips.slot7;
|
||||||
iosTemplate.builtin = this.applianceToInstall.builtin;
|
iosTemplate.builtin = this.applianceToInstall.builtin;
|
||||||
iosTemplate.category = this.getCategory();
|
iosTemplate.category = this.getCategory();
|
||||||
iosTemplate.default_name_format = this.applianceToInstall.port_name_format;
|
iosTemplate.default_name_format = this.applianceToInstall.default_name_format;
|
||||||
iosTemplate.symbol = this.applianceToInstall.symbol;
|
iosTemplate.symbol = this.applianceToInstall.symbol;
|
||||||
iosTemplate.compute_id = this.isGns3VmChosen ? 'vm' : 'local';
|
iosTemplate.compute_id = this.isGns3VmChosen ? 'vm' : 'local';
|
||||||
iosTemplate.template_id = uuid();
|
iosTemplate.template_id = uuid();
|
||||||
@ -572,7 +573,7 @@ export class NewTemplateDialogComponent implements OnInit {
|
|||||||
dockerTemplate.console_type = this.applianceToInstall.docker.console_type;
|
dockerTemplate.console_type = this.applianceToInstall.docker.console_type;
|
||||||
dockerTemplate.builtin = this.applianceToInstall.builtin;
|
dockerTemplate.builtin = this.applianceToInstall.builtin;
|
||||||
dockerTemplate.category = this.getCategory();
|
dockerTemplate.category = this.getCategory();
|
||||||
dockerTemplate.default_name_format = this.applianceToInstall.port_name_format;
|
dockerTemplate.default_name_format = this.applianceToInstall.default_name_format;
|
||||||
dockerTemplate.symbol = this.applianceToInstall.symbol;
|
dockerTemplate.symbol = this.applianceToInstall.symbol;
|
||||||
dockerTemplate.compute_id = this.isGns3VmChosen ? 'vm' : 'local';
|
dockerTemplate.compute_id = this.isGns3VmChosen ? 'vm' : 'local';
|
||||||
dockerTemplate.template_id = uuid();
|
dockerTemplate.template_id = uuid();
|
||||||
@ -629,12 +630,17 @@ export class NewTemplateDialogComponent implements OnInit {
|
|||||||
qemuTemplate.category = this.getCategory();
|
qemuTemplate.category = this.getCategory();
|
||||||
qemuTemplate.first_port_name = this.applianceToInstall.first_port_name;
|
qemuTemplate.first_port_name = this.applianceToInstall.first_port_name;
|
||||||
qemuTemplate.port_name_format = this.applianceToInstall.port_name_format;
|
qemuTemplate.port_name_format = this.applianceToInstall.port_name_format;
|
||||||
|
qemuTemplate.port_segment_size = this.applianceToInstall.port_segment_size;
|
||||||
|
qemuTemplate.default_name_format = this.applianceToInstall.default_name_format
|
||||||
qemuTemplate.symbol = this.applianceToInstall.symbol;
|
qemuTemplate.symbol = this.applianceToInstall.symbol;
|
||||||
qemuTemplate.qemu_path = this.selectedBinary.path;
|
qemuTemplate.qemu_path = this.selectedBinary.path;
|
||||||
qemuTemplate.compute_id = this.isGns3VmChosen ? 'vm' : 'local';
|
qemuTemplate.compute_id = this.isGns3VmChosen ? 'vm' : 'local';
|
||||||
qemuTemplate.template_id = uuid();
|
qemuTemplate.template_id = uuid();
|
||||||
qemuTemplate.hda_disk_image = version.images.hda_disk_image;
|
qemuTemplate.hda_disk_image = version.images.hda_disk_image;
|
||||||
qemuTemplate.hdb_disk_image = version.images.hdb_disk_image;
|
qemuTemplate.hdb_disk_image = version.images.hdb_disk_image;
|
||||||
|
qemuTemplate.hdc_disk_image = version.images.hdc_disk_image;
|
||||||
|
qemuTemplate.hdd_disk_image = version.images.hdd_disk_image;
|
||||||
|
qemuTemplate.cdrom_image = version.images.cdrom_image;
|
||||||
qemuTemplate.template_type = 'qemu';
|
qemuTemplate.template_type = 'qemu';
|
||||||
qemuTemplate.usage = this.applianceToInstall.usage;
|
qemuTemplate.usage = this.applianceToInstall.usage;
|
||||||
|
|
||||||
|
@ -101,7 +101,7 @@
|
|||||||
|
|
||||||
<h6>Additional directories</h6>
|
<h6>Additional directories</h6>
|
||||||
<mat-form-field class="form-field">
|
<mat-form-field class="form-field">
|
||||||
<textarea matInput type="text" [(ngModel)]="node.properties.extra_volumes"></textarea>
|
<textarea matInput type="text" [(ngModel)]="additionalDirectories"></textarea>
|
||||||
</mat-form-field>
|
</mat-form-field>
|
||||||
</mat-tab>
|
</mat-tab>
|
||||||
|
|
||||||
|
@ -20,13 +20,14 @@ export class ConfiguratorDialogDockerComponent implements OnInit {
|
|||||||
name: string;
|
name: string;
|
||||||
generalSettingsForm: FormGroup;
|
generalSettingsForm: FormGroup;
|
||||||
consoleTypes: string[] = [];
|
consoleTypes: string[] = [];
|
||||||
consoleResolutions: string[] = ['640x480', '800x600', '1024x768', '1280x800', '1280x1024', '1366x768', '1920x1080'];
|
consoleResolutions: string[] = ['2560x1440', '1920x1080', '1680x1050', '1440x900', '1366x768', '1280x1024', '1280x800', '1024x768', '800x600', '640x480'];
|
||||||
private conf = {
|
private conf = {
|
||||||
autoFocus: false,
|
autoFocus: false,
|
||||||
width: '800px',
|
width: '800px',
|
||||||
disableClose: true,
|
disableClose: true,
|
||||||
};
|
};
|
||||||
dialogRef;
|
dialogRef;
|
||||||
|
additionalDirectories: string = "";
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
public dialogReference: MatDialogRef<ConfiguratorDialogDockerComponent>,
|
public dialogReference: MatDialogRef<ConfiguratorDialogDockerComponent>,
|
||||||
@ -39,7 +40,7 @@ export class ConfiguratorDialogDockerComponent implements OnInit {
|
|||||||
this.generalSettingsForm = this.formBuilder.group({
|
this.generalSettingsForm = this.formBuilder.group({
|
||||||
name: new FormControl('', Validators.required),
|
name: new FormControl('', Validators.required),
|
||||||
adapter: new FormControl('', Validators.required),
|
adapter: new FormControl('', Validators.required),
|
||||||
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),
|
||||||
});
|
});
|
||||||
@ -50,6 +51,12 @@ export class ConfiguratorDialogDockerComponent implements OnInit {
|
|||||||
this.node = node;
|
this.node = node;
|
||||||
this.name = node.name;
|
this.name = node.name;
|
||||||
this.getConfiguration();
|
this.getConfiguration();
|
||||||
|
if (this.node.properties.extra_volumes && this.node.properties.extra_volumes.length>0) {
|
||||||
|
for (let index = 0; index < this.node.properties.extra_volumes.length - 1; index++) {
|
||||||
|
this.additionalDirectories = this.additionalDirectories + this.node.properties.extra_volumes[index] + "\n";
|
||||||
|
}
|
||||||
|
this.additionalDirectories = this.additionalDirectories + this.node.properties.extra_volumes[this.node.properties.extra_volumes.length - 1];
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -72,7 +79,16 @@ export class ConfiguratorDialogDockerComponent implements OnInit {
|
|||||||
}
|
}
|
||||||
|
|
||||||
onSaveClick() {
|
onSaveClick() {
|
||||||
|
var extraVolumes = this.additionalDirectories.split("\n").filter(elem => elem != "");
|
||||||
|
for (const item of extraVolumes) {
|
||||||
|
console.log(item);
|
||||||
|
if (!item.startsWith("/")) {
|
||||||
|
this.toasterService.error(`Wrong format for additional directories.`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
if (this.generalSettingsForm.valid) {
|
if (this.generalSettingsForm.valid) {
|
||||||
|
this.node.properties.extra_volumes = extraVolumes;
|
||||||
this.nodeService.updateNode(this.server, this.node).subscribe(() => {
|
this.nodeService.updateNode(this.server, this.node).subscribe(() => {
|
||||||
this.toasterService.success(`Node ${this.node.name} updated.`);
|
this.toasterService.success(`Node ${this.node.name} updated.`);
|
||||||
this.onCancelClick();
|
this.onCancelClick();
|
||||||
|
@ -20,13 +20,14 @@
|
|||||||
</mat-select>
|
</mat-select>
|
||||||
</mat-form-field>
|
</mat-form-field>
|
||||||
<mat-checkbox [(ngModel)]="node.console_auto_start"> Auto start console </mat-checkbox><br />
|
<mat-checkbox [(ngModel)]="node.console_auto_start"> Auto start console </mat-checkbox><br />
|
||||||
<mat-form-field class="form-field">
|
<mat-checkbox [(ngModel)]="node.properties.use_default_iou_values"> Use default IOU values for memories </mat-checkbox>
|
||||||
|
<mat-form-field class="form-field" *ngIf="!node.properties.use_default_iou_values">
|
||||||
<input matInput type="number" [(ngModel)]="node.properties.ram" placeholder="RAM size" />
|
<input matInput type="number" [(ngModel)]="node.properties.ram" placeholder="RAM size" />
|
||||||
<span matSuffix>MB</span>
|
<span matSuffix>MB</span>
|
||||||
</mat-form-field>
|
</mat-form-field>
|
||||||
<mat-form-field class="form-field">
|
<mat-form-field class="form-field" *ngIf="!node.properties.use_default_iou_values">
|
||||||
<input matInput type="number" [(ngModel)]="node.properties.nvram" placeholder="NVRAM size" />
|
<input matInput type="number" [(ngModel)]="node.properties.nvram" placeholder="NVRAM size" />
|
||||||
<span matSuffix>MB</span>
|
<span matSuffix>KB</span>
|
||||||
</mat-form-field>
|
</mat-form-field>
|
||||||
</mat-tab>
|
</mat-tab>
|
||||||
|
|
||||||
@ -38,7 +39,7 @@
|
|||||||
matInput
|
matInput
|
||||||
formControlName="ethernetAdapters"
|
formControlName="ethernetAdapters"
|
||||||
type="number"
|
type="number"
|
||||||
[(ngModel)]="node.ethernet_adapters"
|
[(ngModel)]="node.properties.ethernet_adapters"
|
||||||
placeholder="Ethernet adapters"
|
placeholder="Ethernet adapters"
|
||||||
/>
|
/>
|
||||||
</mat-form-field>
|
</mat-form-field>
|
||||||
@ -47,7 +48,7 @@
|
|||||||
matInput
|
matInput
|
||||||
formControlName="serialAdapters"
|
formControlName="serialAdapters"
|
||||||
type="number"
|
type="number"
|
||||||
[(ngModel)]="node.serial_adapters"
|
[(ngModel)]="node.properties.serial_adapters"
|
||||||
placeholder="Serial adapters"
|
placeholder="Serial adapters"
|
||||||
/>
|
/>
|
||||||
</mat-form-field>
|
</mat-form-field>
|
||||||
|
@ -238,6 +238,12 @@
|
|||||||
<mat-form-field class="form-field">
|
<mat-form-field class="form-field">
|
||||||
<input matInput type="text" [(ngModel)]="node.properties.options" placeholder="Options" />
|
<input matInput type="text" [(ngModel)]="node.properties.options" placeholder="Options" />
|
||||||
</mat-form-field>
|
</mat-form-field>
|
||||||
|
<br /><mat-checkbox [(ngModel)]="node.properties.tpm">
|
||||||
|
Enable the Trusted Platform Module (TPM)
|
||||||
|
</mat-checkbox>
|
||||||
|
<br /><mat-checkbox [(ngModel)]="node.properties.uefi">
|
||||||
|
Enable the UEFI boot mode
|
||||||
|
</mat-checkbox>
|
||||||
</mat-card-content>
|
</mat-card-content>
|
||||||
</mat-card>
|
</mat-card>
|
||||||
</mat-tab>
|
</mat-tab>
|
||||||
|
@ -17,6 +17,7 @@ import { ToasterService } from '../../../../services/toaster.service';
|
|||||||
import { MockedToasterService } from '../../../../services/toaster.service.spec';
|
import { MockedToasterService } from '../../../../services/toaster.service.spec';
|
||||||
import { MockedLinkService, MockedNodesDataSource } from '../../project-map.component.spec';
|
import { MockedLinkService, MockedNodesDataSource } from '../../project-map.component.spec';
|
||||||
import { StartCaptureDialogComponent } from './start-capture.component';
|
import { StartCaptureDialogComponent } from './start-capture.component';
|
||||||
|
import { ProtocolHandlerService } from '../../../../services/protocol-handler.service';
|
||||||
|
|
||||||
describe('StartCaptureDialogComponent', () => {
|
describe('StartCaptureDialogComponent', () => {
|
||||||
let component: StartCaptureDialogComponent;
|
let component: StartCaptureDialogComponent;
|
||||||
@ -25,6 +26,8 @@ describe('StartCaptureDialogComponent', () => {
|
|||||||
let mockedToasterService = new MockedToasterService();
|
let mockedToasterService = new MockedToasterService();
|
||||||
let mockedLinkService = new MockedLinkService();
|
let mockedLinkService = new MockedLinkService();
|
||||||
let mockedNodesDataSource = new MockedNodesDataSource();
|
let mockedNodesDataSource = new MockedNodesDataSource();
|
||||||
|
let protocolHandlerService: ProtocolHandlerService;
|
||||||
|
|
||||||
let dialogRef = {
|
let dialogRef = {
|
||||||
close: jasmine.createSpy('close'),
|
close: jasmine.createSpy('close'),
|
||||||
};
|
};
|
||||||
@ -49,10 +52,13 @@ describe('StartCaptureDialogComponent', () => {
|
|||||||
{ provide: LinkService, useValue: mockedLinkService },
|
{ provide: LinkService, useValue: mockedLinkService },
|
||||||
{ provide: NodesDataSource, useValue: mockedNodesDataSource },
|
{ provide: NodesDataSource, useValue: mockedNodesDataSource },
|
||||||
{ provide: PacketCaptureService },
|
{ provide: PacketCaptureService },
|
||||||
|
ProtocolHandlerService,
|
||||||
],
|
],
|
||||||
declarations: [StartCaptureDialogComponent],
|
declarations: [StartCaptureDialogComponent],
|
||||||
schemas: [NO_ERRORS_SCHEMA],
|
schemas: [NO_ERRORS_SCHEMA],
|
||||||
}).compileComponents();
|
}).compileComponents();
|
||||||
|
|
||||||
|
protocolHandlerService = TestBed.inject(ProtocolHandlerService);
|
||||||
}));
|
}));
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
|
@ -56,7 +56,7 @@ export class StartCaptureDialogComponent implements OnInit {
|
|||||||
const sourcePort = sourceNode.ports[this.link.nodes[0].port_number];
|
const sourcePort = sourceNode.ports[this.link.nodes[0].port_number];
|
||||||
const targetPort = targetNode.ports[this.link.nodes[1].port_number];
|
const targetPort = targetNode.ports[this.link.nodes[1].port_number];
|
||||||
this.inputForm.controls['fileName'].setValue(
|
this.inputForm.controls['fileName'].setValue(
|
||||||
`${sourceNode.name}_${sourcePort.name}_to_${targetNode.name}_${targetPort.name}`
|
`${sourceNode.name}_${sourcePort.name}_to_${targetNode.name}_${targetPort.name}`.replace(new RegExp('[^0-9A-Za-z_-]', 'g'), '')
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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;
|
||||||
|
@ -67,6 +67,14 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
|
<button
|
||||||
|
class="map-settings-button"
|
||||||
|
matTooltip="Project Map Settings"
|
||||||
|
matTooltipClass="custom-tooltip"
|
||||||
|
mat-icon-button
|
||||||
|
[matMenuTriggerFor]="viewMenu">
|
||||||
|
<mat-icon>view_module</mat-icon>
|
||||||
|
</button>
|
||||||
<button
|
<button
|
||||||
matTooltip="Toggle topology/servers summary"
|
matTooltip="Toggle topology/servers summary"
|
||||||
matTooltipClass="custom-tooltip"
|
matTooltipClass="custom-tooltip"
|
||||||
@ -83,39 +91,27 @@
|
|||||||
|
|
||||||
<!-- GNS3 menu -->
|
<!-- GNS3 menu -->
|
||||||
<mat-menu #mainMenu="matMenu" [overlapTrigger]="false">
|
<mat-menu #mainMenu="matMenu" [overlapTrigger]="false">
|
||||||
|
<button mat-menu-item [matMenuTriggerFor]="projectMenu">
|
||||||
|
<mat-icon>insert_drive_file</mat-icon>
|
||||||
|
<span>Project settings</span>
|
||||||
|
</button>
|
||||||
<button mat-menu-item [routerLink]="['/server', server.id, 'projects']">
|
<button mat-menu-item [routerLink]="['/server', server.id, 'projects']">
|
||||||
<mat-icon>work</mat-icon>
|
<mat-icon>work</mat-icon>
|
||||||
<span>Go to projects</span>
|
<span>Projects</span>
|
||||||
</button>
|
</button>
|
||||||
<button mat-menu-item [routerLink]="['/servers']">
|
<button mat-menu-item [routerLink]="['/servers']">
|
||||||
<mat-icon>developer_board</mat-icon>
|
<mat-icon>developer_board</mat-icon>
|
||||||
<span>Go to servers</span>
|
<span>Servers</span>
|
||||||
</button>
|
|
||||||
<button mat-menu-item routerLink="/server/{{ server.id }}/preferences">
|
|
||||||
<mat-icon>settings_applications</mat-icon>
|
|
||||||
<span>Go to preferences</span>
|
|
||||||
</button>
|
</button>
|
||||||
<button mat-menu-item routerLink="/server/{{ server.id }}/systemstatus">
|
<button mat-menu-item routerLink="/server/{{ server.id }}/systemstatus">
|
||||||
<mat-icon>info</mat-icon>
|
<mat-icon>data_usage</mat-icon>
|
||||||
<span>Go to system status</span>
|
<span>System Status</span>
|
||||||
</button>
|
</button>
|
||||||
<button mat-menu-item routerLink="/settings">
|
<button mat-menu-item routerLink="/settings">
|
||||||
<mat-icon>settings</mat-icon>
|
<mat-icon>settings</mat-icon>
|
||||||
<span>Go to settings</span>
|
<span>Settings</span>
|
||||||
</button>
|
|
||||||
<button mat-menu-item (click)="addNewTemplate()">
|
|
||||||
<mat-icon>control_point</mat-icon>
|
|
||||||
<span>New template</span>
|
|
||||||
</button>
|
</button>
|
||||||
<app-import-appliance [server]="server" [project]="project"></app-import-appliance>
|
<app-import-appliance [server]="server" [project]="project"></app-import-appliance>
|
||||||
<button mat-menu-item [matMenuTriggerFor]="projectMenu">
|
|
||||||
<mat-icon>settings</mat-icon>
|
|
||||||
<span>Project settings</span>
|
|
||||||
</button>
|
|
||||||
<button mat-menu-item [matMenuTriggerFor]="viewMenu">
|
|
||||||
<mat-icon>view_module</mat-icon>
|
|
||||||
<span>Map settings</span>
|
|
||||||
</button>
|
|
||||||
</mat-menu>
|
</mat-menu>
|
||||||
|
|
||||||
<!-- Project Settings sub-menu -->
|
<!-- Project Settings sub-menu -->
|
||||||
@ -256,4 +252,4 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<ng-template #topologySummaryContainer></ng-template>
|
<ng-template #topologySummaryContainer></ng-template>
|
@ -73,6 +73,10 @@ g.node:hover {
|
|||||||
font-size: 28px !important;
|
font-size: 28px !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.map-settings-button mat-icon {
|
||||||
|
font-size: 22px !important;
|
||||||
|
}
|
||||||
|
|
||||||
.selected {
|
.selected {
|
||||||
background: rgba(0, 151, 167, 0.1);
|
background: rgba(0, 151, 167, 0.1);
|
||||||
|
|
||||||
|
@ -67,6 +67,7 @@ import { SymbolService } from '../../services/symbol.service';
|
|||||||
import { ThemeService } from '../../services/theme.service';
|
import { ThemeService } from '../../services/theme.service';
|
||||||
import { ToasterService } from '../../services/toaster.service';
|
import { ToasterService } from '../../services/toaster.service';
|
||||||
import { ToolsService } from '../../services/tools.service';
|
import { ToolsService } from '../../services/tools.service';
|
||||||
|
import { ProtocolHandlerService } from '../../services/protocol-handler.service';
|
||||||
import { AddBlankProjectDialogComponent } from '../projects/add-blank-project-dialog/add-blank-project-dialog.component';
|
import { AddBlankProjectDialogComponent } from '../projects/add-blank-project-dialog/add-blank-project-dialog.component';
|
||||||
import { ConfirmationBottomSheetComponent } from '../projects/confirmation-bottomsheet/confirmation-bottomsheet.component';
|
import { ConfirmationBottomSheetComponent } from '../projects/confirmation-bottomsheet/confirmation-bottomsheet.component';
|
||||||
import { EditProjectDialogComponent } from '../projects/edit-project-dialog/edit-project-dialog.component';
|
import { EditProjectDialogComponent } from '../projects/edit-project-dialog/edit-project-dialog.component';
|
||||||
@ -173,8 +174,9 @@ export class ProjectMapComponent implements OnInit, OnDestroy {
|
|||||||
private title: Title,
|
private title: Title,
|
||||||
private nodeConsoleService: NodeConsoleService,
|
private nodeConsoleService: NodeConsoleService,
|
||||||
private symbolService: SymbolService,
|
private symbolService: SymbolService,
|
||||||
|
private protocolHandlerService: ProtocolHandlerService,
|
||||||
private cd: ChangeDetectorRef,
|
private cd: ChangeDetectorRef,
|
||||||
private cfr: ComponentFactoryResolver,
|
private cfr: ComponentFactoryResolver,
|
||||||
private injector: Injector
|
private injector: Injector
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
@ -229,7 +231,7 @@ export class ProjectMapComponent implements OnInit, OnDestroy {
|
|||||||
this.instance.instance.ngOnDestroy();
|
this.instance.instance.ngOnDestroy();
|
||||||
this.instance.destroy();
|
this.instance.destroy();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
addSubscriptions() {
|
addSubscriptions() {
|
||||||
@ -334,7 +336,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 +424,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)
|
||||||
@ -966,7 +977,7 @@ export class ProjectMapComponent implements OnInit, OnDestroy {
|
|||||||
) {
|
) {
|
||||||
this.toasterService.error('Project with running nodes cannot be exported.');
|
this.toasterService.error('Project with running nodes cannot be exported.');
|
||||||
} else {
|
} else {
|
||||||
location.assign(this.projectService.getExportPath(this.server, this.project));
|
this.protocolHandlerService.open(this.projectService.getExportPath(this.server, this.project));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -981,8 +992,8 @@ export class ProjectMapComponent implements OnInit, OnDestroy {
|
|||||||
|
|
||||||
fileReader.onloadend = () => {
|
fileReader.onloadend = () => {
|
||||||
let image = fileReader.result;
|
let image = fileReader.result;
|
||||||
let svg = `<svg xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\"
|
let svg = `<svg xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\"
|
||||||
height=\"${imageToUpload.height}\" width=\"${imageToUpload.width}\">\n<image height=\"${imageToUpload.height}\" width=\"${imageToUpload.width}\"
|
height=\"${imageToUpload.height}\" width=\"${imageToUpload.width}\">\n<image height=\"${imageToUpload.height}\" width=\"${imageToUpload.width}\"
|
||||||
xlink:href=\"${image}\"/>\n</svg>`;
|
xlink:href=\"${image}\"/>\n</svg>`;
|
||||||
this.drawingService
|
this.drawingService
|
||||||
.add(this.server, this.project.project_id, -(imageToUpload.width / 2), -(imageToUpload.height / 2), svg)
|
.add(this.server, this.project.project_id, -(imageToUpload.width / 2), -(imageToUpload.height / 2), svg)
|
||||||
|
@ -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
|
||||||
|
@ -89,6 +89,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[]) => {
|
||||||
|
@ -11,6 +11,7 @@
|
|||||||
|
|
||||||
<div>
|
<div>
|
||||||
<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)]="settings.anonymous_statistics">Send anonymous usage statistics</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)]="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>
|
||||||
|
@ -69,6 +69,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: '',
|
||||||
};
|
};
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
<div class="title-container">
|
<div class="title-container">
|
||||||
<h1 mat-dialog-title>Add a node</h1>
|
<h1 mat-dialog-title>Insert New Node</h1>
|
||||||
<button
|
<button
|
||||||
mat-button
|
mat-button
|
||||||
class="top-button"
|
class="top-button"
|
||||||
|
@ -10,63 +10,95 @@
|
|||||||
</button>
|
</button>
|
||||||
|
|
||||||
<mat-menu #mainMenu="matMenu">
|
<mat-menu #mainMenu="matMenu">
|
||||||
<button mat-menu-item (click)="openDialog()">
|
<div class="templateMenu">
|
||||||
<mat-icon>add_to_queue</mat-icon>
|
<div class="templateMenuHeader">
|
||||||
<span>Open dialog to configure</span>
|
<button mat-menu-item (click)="openDialog()">
|
||||||
</button>
|
<mat-icon>add_to_queue</mat-icon>
|
||||||
|
<span>
|
||||||
|
Insert New Node...
|
||||||
|
</span>
|
||||||
|
</button>
|
||||||
|
<button mat-menu-item (click)="addNewTemplate()">
|
||||||
|
<mat-icon>library_add</mat-icon>
|
||||||
|
<span>
|
||||||
|
New Template...
|
||||||
|
</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
<mat-form-field (click)="$event.stopPropagation()" class="form-field" floatPlaceholder="never">
|
<div class="templateFilterBar">
|
||||||
<input
|
<mat-form-field (click)="$event.stopPropagation()" class="form-field" floatPlaceholder="never">
|
||||||
matInput
|
<input
|
||||||
placeholder="Search by name"
|
matInput
|
||||||
(keyup)="filterTemplates($event)"
|
placeholder="Filter by name"
|
||||||
[(ngModel)]="searchText"
|
search="search"
|
||||||
[ngModelOptions]="{ standalone: true }"
|
(keyup)="filterTemplates($event)"
|
||||||
/>
|
[(ngModel)]="searchText"
|
||||||
</mat-form-field>
|
[ngModelOptions]="{ standalone: true }"
|
||||||
<mat-form-field (click)="$event.stopPropagation()" class="form-field">
|
/>
|
||||||
<mat-select
|
<mat-icon class="searchIcon" matSuffix>search</mat-icon>
|
||||||
[ngModelOptions]="{ standalone: true }"
|
</mat-form-field>
|
||||||
placeholder="Filter templates by type"
|
<mat-form-field (click)="$event.stopPropagation()" class="form-field">
|
||||||
(selectionChange)="filterTemplates($event)"
|
<mat-select
|
||||||
[(ngModel)]="selectedType"
|
[ngModelOptions]="{ standalone: true }"
|
||||||
>
|
placeholder="Filter by type"
|
||||||
<mat-option *ngFor="let type of templateTypes" [value]="type">
|
(selectionChange)="filterTemplates($event)"
|
||||||
{{ type }}
|
[(ngModel)]="selectedType"
|
||||||
</mat-option>
|
>
|
||||||
</mat-select>
|
<mat-option *ngFor="let type of templateTypes" [value]="type">
|
||||||
</mat-form-field>
|
{{ type }}
|
||||||
|
</mat-option>
|
||||||
|
</mat-select>
|
||||||
|
</mat-form-field>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="menu">
|
<div class="menu">
|
||||||
<div class="templateList">
|
<div class="templateList">
|
||||||
<mat-list-item *ngFor="let template of filteredTemplates; let i = index">
|
<mat-list-item *ngFor="let template of filteredTemplates; let i = index">
|
||||||
<span *ngIf="i % 4 === 0" class="templateRow">
|
<span *ngIf="i % 4 === 0" class="templateRow">
|
||||||
<span class="templateIcon">
|
<span class="templateIcon">
|
||||||
<div mwlDraggable (dragStart)="dragStart($event)" (dragEnd)="dragEnd($event, filteredTemplates[i])">
|
<div
|
||||||
<img class="image" [src]="getImageSourceForTemplate(filteredTemplates[i])" />
|
mwlDraggable
|
||||||
</div>
|
(dragStart)="dragStart($event)"
|
||||||
<div class="templateText">{{ filteredTemplates[i].name }}</div>
|
(dragEnd)="dragEnd($event, filteredTemplates[i])"
|
||||||
</span>
|
class="iconContainer">
|
||||||
<span *ngIf="filteredTemplates[i + 1]" class="templateIcon">
|
<img class="image" [src]="getImageSourceForTemplate(filteredTemplates[i])" />
|
||||||
<div mwlDraggable (dragStart)="dragStart($event)" (dragEnd)="dragEnd($event, filteredTemplates[i + 1])">
|
</div>
|
||||||
<img class="image" [src]="getImageSourceForTemplate(filteredTemplates[i + 1])" />
|
<div class="templateText">{{ filteredTemplates[i].name }}</div>
|
||||||
</div>
|
</span>
|
||||||
<div class="templateText">{{ filteredTemplates[i + 1].name }}</div>
|
<span *ngIf="filteredTemplates[i + 1]" class="templateIcon">
|
||||||
</span>
|
<div
|
||||||
<span *ngIf="filteredTemplates[i + 2]" class="templateIcon">
|
mwlDraggable
|
||||||
<div mwlDraggable (dragStart)="dragStart($event)" (dragEnd)="dragEnd($event, filteredTemplates[i + 2])">
|
(dragStart)="dragStart($event)"
|
||||||
<img class="image" [src]="getImageSourceForTemplate(filteredTemplates[i + 2])" />
|
(dragEnd)="dragEnd($event, filteredTemplates[i + 1])"
|
||||||
</div>
|
class="iconContainer">
|
||||||
<div class="templateText">{{ filteredTemplates[i + 2].name }}</div>
|
<img class="image" [src]="getImageSourceForTemplate(filteredTemplates[i + 1])" />
|
||||||
</span>
|
</div>
|
||||||
<span *ngIf="filteredTemplates[i + 3]" class="templateIcon">
|
<div class="templateText">{{ filteredTemplates[i + 1].name }}</div>
|
||||||
<div mwlDraggable (dragStart)="dragStart($event)" (dragEnd)="dragEnd($event, filteredTemplates[i + 3])">
|
</span>
|
||||||
<img class="image" [src]="getImageSourceForTemplate(filteredTemplates[i + 3])" />
|
<span *ngIf="filteredTemplates[i + 2]" class="templateIcon">
|
||||||
</div>
|
<div
|
||||||
<div class="templateText">{{ filteredTemplates[i + 3].name }}</div>
|
mwlDraggable
|
||||||
</span>
|
(dragStart)="dragStart($event)"
|
||||||
</span>
|
(dragEnd)="dragEnd($event, filteredTemplates[i + 2])"
|
||||||
</mat-list-item>
|
class="iconContainer">
|
||||||
</div>
|
<img class="image" [src]="getImageSourceForTemplate(filteredTemplates[i + 2])" />
|
||||||
</div>
|
</div>
|
||||||
</mat-menu>
|
<div class="templateText">{{ filteredTemplates[i + 2].name }}</div>
|
||||||
|
</span>
|
||||||
|
<span *ngIf="filteredTemplates[i + 3]" class="templateIcon">
|
||||||
|
<div
|
||||||
|
mwlDraggable
|
||||||
|
(dragStart)="dragStart($event)"
|
||||||
|
(dragEnd)="dragEnd($event, filteredTemplates[i + 3])"
|
||||||
|
class="iconContainer">
|
||||||
|
<img class="image" [src]="getImageSourceForTemplate(filteredTemplates[i + 3])" />
|
||||||
|
</div>
|
||||||
|
<div class="templateText">{{ filteredTemplates[i + 3].name }}</div>
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
</mat-list-item>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</mat-menu>
|
@ -1,6 +1,7 @@
|
|||||||
::ng-deep .mat-menu-panel {
|
::ng-deep .mat-menu-panel {
|
||||||
max-width: 400px;
|
max-width: 400px;
|
||||||
max-height: 500px;
|
max-height: 640px;
|
||||||
|
border-radius: 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.menu {
|
.menu {
|
||||||
@ -8,6 +9,26 @@
|
|||||||
overflow-y: scroll;
|
overflow-y: scroll;
|
||||||
scrollbar-color: darkgrey #263238;
|
scrollbar-color: darkgrey #263238;
|
||||||
scrollbar-width: thin;
|
scrollbar-width: thin;
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.templateMenuHeader {
|
||||||
|
border-bottom: 1px solid rgba(255,255,255,0.05);
|
||||||
|
}
|
||||||
|
|
||||||
|
.templateFilterBar {
|
||||||
|
padding: 10px 2%;
|
||||||
|
background-color: rgba(0,0,0,0.2);
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.templateFilterBar > .form-field {
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.templateFilterBar .searchIcon {
|
||||||
|
position: relative;
|
||||||
|
top: 5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
::-webkit-scrollbar {
|
::-webkit-scrollbar {
|
||||||
@ -24,20 +45,22 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.form-field {
|
.form-field {
|
||||||
width: 90%;
|
width: 44%;
|
||||||
margin-left: 5%;
|
margin-left: 3%;
|
||||||
margin-right: 5%;
|
margin-right: 3%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.image {
|
.image {
|
||||||
width: 65px;
|
display: inline-block;
|
||||||
height: 65px;
|
width: 55px;
|
||||||
|
height: 55px;
|
||||||
filter: invert(0);
|
filter: invert(0);
|
||||||
--webkit-filter: invert(0) !important;
|
--webkit-filter: invert(0) !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.templateList {
|
.templateList {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
padding: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.templateRow {
|
.templateRow {
|
||||||
@ -50,6 +73,23 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.templateIcon {
|
.templateIcon {
|
||||||
width: 80px !important;
|
width: 90px !important;
|
||||||
padding: 10px;
|
padding: 2px 5px;
|
||||||
|
text-align: center;
|
||||||
|
margin: 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.templateIcon > .iconContainer {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
width: 70px;
|
||||||
|
height: 70px;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
border-radius: 50%;
|
||||||
|
cursor: move;
|
||||||
|
}
|
||||||
|
|
||||||
|
.templateIcon > .iconContainer:hover {
|
||||||
|
background-color: rgba(237, 246, 231, 0.08);
|
||||||
}
|
}
|
||||||
|
@ -8,6 +8,7 @@ import { MapScaleService } from '../../services/mapScale.service';
|
|||||||
import { SymbolService } from '../../services/symbol.service';
|
import { SymbolService } from '../../services/symbol.service';
|
||||||
import { TemplateService } from '../../services/template.service';
|
import { TemplateService } from '../../services/template.service';
|
||||||
import { NodeAddedEvent, TemplateListDialogComponent } from './template-list-dialog/template-list-dialog.component';
|
import { NodeAddedEvent, TemplateListDialogComponent } from './template-list-dialog/template-list-dialog.component';
|
||||||
|
import { NewTemplateDialogComponent } from '../project-map/new-template-dialog/new-template-dialog.component';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-template',
|
selector: 'app-template',
|
||||||
@ -127,6 +128,19 @@ export class TemplateComponent implements OnInit, OnDestroy {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
addNewTemplate() {
|
||||||
|
const dialogRef = this.dialog.open(NewTemplateDialogComponent, {
|
||||||
|
width: '1000px',
|
||||||
|
maxHeight: '700px',
|
||||||
|
autoFocus: false,
|
||||||
|
disableClose: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
let instance = dialogRef.componentInstance;
|
||||||
|
instance.server = this.server;
|
||||||
|
instance.project = this.project;
|
||||||
|
}
|
||||||
|
|
||||||
getImageSourceForTemplate(template: Template) {
|
getImageSourceForTemplate(template: Template) {
|
||||||
return `${this.server.protocol}//${this.server.host}:${this.server.port}/v2/symbols/${template.symbol}/raw`;
|
return `${this.server.protocol}//${this.server.host}:${this.server.port}/v2/symbols/${template.symbol}/raw`;
|
||||||
}
|
}
|
||||||
@ -134,4 +148,4 @@ export class TemplateComponent implements OnInit, OnDestroy {
|
|||||||
ngOnDestroy() {
|
ngOnDestroy() {
|
||||||
this.subscription.unsubscribe();
|
this.subscription.unsubscribe();
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -61,7 +61,7 @@
|
|||||||
{{ node.name }}
|
{{ node.name }}
|
||||||
</div>
|
</div>
|
||||||
<div *ngIf="node.console != null && node.console != undefined && node.console_type != 'none'">
|
<div *ngIf="node.console != null && node.console != undefined && node.console_type != 'none'">
|
||||||
{{ node.console_type }} {{ node.console_host }}:{{ node.console }}
|
{{ node.console_type }}://{{ node.console_host }}:{{ node.console }}
|
||||||
</div>
|
</div>
|
||||||
<div *ngIf="node.console === null || node.console === undefined || node.console_type === 'none'">
|
<div *ngIf="node.console === null || node.console === undefined || node.console_type === 'none'">
|
||||||
none
|
none
|
||||||
|
@ -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) {
|
||||||
|
@ -39,4 +39,4 @@
|
|||||||
|
|
||||||
<app-progress></app-progress>
|
<app-progress></app-progress>
|
||||||
|
|
||||||
<footer class="footer mat-app-background">GNS3 Web UI © 2020 - v{{ uiVersion }}</footer>
|
<footer class="footer mat-app-background">GNS3 Web-UI ©2018-{{ currentYear }} v{{ uiVersion }}</footer>
|
||||||
|
@ -17,6 +17,7 @@ import { version } from './../../version';
|
|||||||
export class DefaultLayoutComponent implements OnInit, OnDestroy {
|
export class DefaultLayoutComponent implements OnInit, OnDestroy {
|
||||||
public isInstalledSoftwareAvailable = false;
|
public isInstalledSoftwareAvailable = false;
|
||||||
public uiVersion = version;
|
public uiVersion = version;
|
||||||
|
public currentYear = new Date().getFullYear();
|
||||||
|
|
||||||
serverStatusSubscription: Subscription;
|
serverStatusSubscription: Subscription;
|
||||||
shouldStopServersOnClosing = true;
|
shouldStopServersOnClosing = true;
|
||||||
|
@ -55,6 +55,9 @@ export interface Iou {
|
|||||||
export interface Images {
|
export interface Images {
|
||||||
hda_disk_image: string;
|
hda_disk_image: string;
|
||||||
hdb_disk_image: string;
|
hdb_disk_image: string;
|
||||||
|
hdc_disk_image: string;
|
||||||
|
hdd_disk_image: string;
|
||||||
|
cdrom_image: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Version {
|
export interface Version {
|
||||||
@ -74,11 +77,13 @@ export interface Appliance {
|
|||||||
maintainer_email: string;
|
maintainer_email: string;
|
||||||
name: string;
|
name: string;
|
||||||
port_name_format: string;
|
port_name_format: string;
|
||||||
|
port_segment_size: number;
|
||||||
product_name: string;
|
product_name: string;
|
||||||
product_url: string;
|
product_url: string;
|
||||||
registry_version: number;
|
registry_version: number;
|
||||||
status: string;
|
status: string;
|
||||||
symbol: string;
|
symbol: string;
|
||||||
|
default_name_format: string;
|
||||||
usage: string;
|
usage: string;
|
||||||
vendor_name: string;
|
vendor_name: string;
|
||||||
vendor_url: string;
|
vendor_url: string;
|
||||||
|
@ -43,4 +43,7 @@ export class QemuTemplate {
|
|||||||
template_id: string;
|
template_id: string;
|
||||||
template_type: string;
|
template_type: string;
|
||||||
usage: string;
|
usage: string;
|
||||||
|
replicate_network_connection_state: boolean;
|
||||||
|
tpm: boolean;
|
||||||
|
uefi: boolean;
|
||||||
}
|
}
|
||||||
|
@ -19,7 +19,7 @@ export class DockerConfigurationService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
getConsoleResolutions() {
|
getConsoleResolutions() {
|
||||||
let consoleResolutions = ['1920x1080', '1366x768', '1280x1024', '1280x800', '1024x768', '800x600', '640x480'];
|
let consoleResolutions = ['2560x1440', '1920x1080', '1680x1050', '1440x900', '1366x768', '1280x1024', '1280x800', '1024x768', '800x600', '640x480'];
|
||||||
|
|
||||||
return consoleResolutions;
|
return consoleResolutions;
|
||||||
}
|
}
|
||||||
|
@ -149,7 +149,7 @@ describe('DrawingService', () => {
|
|||||||
drawing.z = 1;
|
drawing.z = 1;
|
||||||
drawing.rotation = 0;
|
drawing.rotation = 0;
|
||||||
drawing.svg =
|
drawing.svg =
|
||||||
'<svg height="100" width="200"><rect fill="#ffffff" fill-opacity="1.0" height="100" stroke="#000000" stroke-width="2" width="200" /></svg>';
|
'<svg height="100" width="200"><rect fill="#ffffff" fill-opacity="1.0" height="100" stroke="#000000" stroke-width="2" width="200" rx="0" ry="0" /></svg>';
|
||||||
|
|
||||||
service.duplicate(server, drawing.project_id, drawing).subscribe();
|
service.duplicate(server, drawing.project_id, drawing).subscribe();
|
||||||
|
|
||||||
|
@ -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');
|
||||||
}
|
}
|
||||||
|
@ -63,9 +63,6 @@ export class LinkService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
updateLink(server: Server, link: Link) {
|
updateLink(server: Server, link: Link) {
|
||||||
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);
|
return this.httpServer.put<Link>(server, `/projects/${link.project_id}/links/${link.link_id}`, link);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -78,13 +78,15 @@ export class NodeConsoleService {
|
|||||||
let nodesToStart = 'Please start the following nodes if you want to open consoles for them: ';
|
let nodesToStart = 'Please start the following nodes if you want to open consoles for them: ';
|
||||||
let nodesToStartCounter = 0;
|
let nodesToStartCounter = 0;
|
||||||
nodes.forEach((n) => {
|
nodes.forEach((n) => {
|
||||||
if (n.status === 'started') {
|
if (n.console_type !== "none") {
|
||||||
this.mapSettingsService.logConsoleSubject.next(true);
|
if (n.status === 'started') {
|
||||||
// this timeout is required due to xterm.js implementation
|
this.mapSettingsService.logConsoleSubject.next(true);
|
||||||
setTimeout(() => { this.openConsoleForNode(n); }, 500);
|
// this timeout is required due to xterm.js implementation
|
||||||
} else {
|
setTimeout(() => { this.openConsoleForNode(n); }, 500);
|
||||||
nodesToStartCounter++;
|
} else {
|
||||||
nodesToStart += n.name + ' '
|
nodesToStartCounter++;
|
||||||
|
nodesToStart += n.name + ' '
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
if (nodesToStartCounter > 0) {
|
if (nodesToStartCounter > 0) {
|
||||||
@ -93,16 +95,19 @@ export class NodeConsoleService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
openConsolesForAllNodesInNewTabs(nodes: Node[]) {
|
openConsolesForAllNodesInNewTabs(nodes: Node[]) {
|
||||||
let nodesToStart = 'Please start the following nodes if you want to open consoles for them: ';
|
let nodesToStart = 'Please start the following nodes if you want to open consoles in tabs for them: ';
|
||||||
let nodesToStartCounter = 0;
|
let nodesToStartCounter = 0;
|
||||||
nodes.forEach((n) => {
|
nodes.forEach((n) => {
|
||||||
if (n.status === 'started') {
|
// opening a console in tab is only supported for telnet type
|
||||||
let url = this.router.url.split('/');
|
if (n.console_type === "telnet") {
|
||||||
let urlString = `/static/web-ui/${url[1]}/${url[2]}/${url[3]}/${url[4]}/nodes/${n.node_id}`;
|
if (n.status === 'started') {
|
||||||
window.open(urlString);
|
let url = this.router.url.split('/');
|
||||||
} else {
|
let urlString = `/static/web-ui/${url[1]}/${url[2]}/${url[3]}/${url[4]}/nodes/${n.node_id}`;
|
||||||
nodesToStartCounter++;
|
window.open(urlString);
|
||||||
nodesToStart += n.name + ' '
|
} else {
|
||||||
|
nodesToStartCounter++;
|
||||||
|
nodesToStart += n.name + ' '
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
if (nodesToStartCounter > 0) {
|
if (nodesToStartCounter > 0) {
|
||||||
|
@ -2,14 +2,17 @@ import { Injectable } from '@angular/core';
|
|||||||
import { Link } from '../models/link';
|
import { Link } from '../models/link';
|
||||||
import { Project } from '../models/project';
|
import { Project } from '../models/project';
|
||||||
import { Server } from '../models/server';
|
import { Server } from '../models/server';
|
||||||
|
import { ProtocolHandlerService } from './protocol-handler.service';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class PacketCaptureService {
|
export class PacketCaptureService {
|
||||||
constructor() {}
|
|
||||||
|
constructor(private protocolHandlerService: ProtocolHandlerService) {}
|
||||||
|
|
||||||
startCapture(server: Server, project: Project, link: Link, name: string) {
|
startCapture(server: Server, project: Project, link: Link, name: string) {
|
||||||
location.assign(
|
|
||||||
`gns3+pcap://${server.host}:${server.port}?project_id=${project.project_id}&link_id=${link.link_id}&name=${name}`
|
const uri = `gns3+pcap://${server.host}:${server.port}?protocol=${server.protocol.slice(0, -1)}&project_id=${project.project_id}&link_id=${link.link_id}&project=${project.name}&name=${name}`;
|
||||||
);
|
this.protocolHandlerService.open(uri);
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
48
src/app/services/protocol-handler.service.ts
Normal file
48
src/app/services/protocol-handler.service.ts
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
import { Injectable } from '@angular/core';
|
||||||
|
import { DeviceDetectorService } from 'ngx-device-detector';
|
||||||
|
import { ToasterService } from './toaster.service';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class ProtocolHandlerService {
|
||||||
|
|
||||||
|
constructor(private toasterService: ToasterService, private deviceService: DeviceDetectorService) {}
|
||||||
|
|
||||||
|
createHiddenIframe(target: Element, uri: string) {
|
||||||
|
const iframe = document.createElement("iframe");
|
||||||
|
iframe.src = uri;
|
||||||
|
iframe.id = "hiddenIframe";
|
||||||
|
iframe.style.display = "none";
|
||||||
|
target.appendChild(iframe);
|
||||||
|
return iframe;
|
||||||
|
}
|
||||||
|
|
||||||
|
openUriUsingFirefox(uri: string) {
|
||||||
|
var iframe = (document.querySelector("#hiddenIframe") as HTMLIFrameElement);
|
||||||
|
|
||||||
|
if (!iframe) {
|
||||||
|
iframe = this.createHiddenIframe(document.body, "about:blank");
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
iframe.contentWindow.location.href = uri;
|
||||||
|
} catch (e) {
|
||||||
|
if (e.name === "NS_ERROR_UNKNOWN_PROTOCOL") {
|
||||||
|
this.toasterService.error('Protocol handler does not exist');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
open(uri: string) {
|
||||||
|
|
||||||
|
const device = this.deviceService.getDeviceInfo();
|
||||||
|
|
||||||
|
console.log("Launching external protocol handler with " + device.browser + ": " + uri)
|
||||||
|
if (device.browser === "Firefox") {
|
||||||
|
// Use a hidden iframe otherwise Firefox will disconnect
|
||||||
|
// from the GNS3 controller websocket if we use location.assign()
|
||||||
|
this.openUriUsingFirefox(uri);
|
||||||
|
} else {
|
||||||
|
location.assign(uri);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -12,53 +12,31 @@ export class QemuConfigurationService {
|
|||||||
|
|
||||||
getNetworkTypes() {
|
getNetworkTypes() {
|
||||||
// needs extending of custom adapter component
|
// needs extending of custom adapter component
|
||||||
// let networkTypes = [["e1000", "Intel Gigabit Ethernet"],
|
|
||||||
// ["i82550", "Intel i82550 Ethernet"],
|
|
||||||
// ["i82551", "Intel i82551 Ethernet"],
|
|
||||||
// ["i82557a", "Intel i82557A Ethernet"],
|
|
||||||
// ["i82557b", "Intel i82557B Ethernet"],
|
|
||||||
// ["i82557c", "Intel i82557C Ethernet"],
|
|
||||||
// ["i82558a", "Intel i82558A Ethernet"],
|
|
||||||
// ["i82558b", "Intel i82558B Ethernet"],
|
|
||||||
// ["i82559a", "Intel i82559A Ethernet"],
|
|
||||||
// ["i82559b", "Intel i82559B Ethernet"],
|
|
||||||
// ["i82559c", "Intel i82559C Ethernet"],
|
|
||||||
// ["i82559er", "Intel i82559ER Ethernet"],
|
|
||||||
// ["i82562", "Intel i82562 Ethernet"],
|
|
||||||
// ["i82801", "Intel i82801 Ethernet"],
|
|
||||||
// ["ne2k_pci", "NE2000 Ethernet"],
|
|
||||||
// ["pcnet", "AMD PCNet Ethernet"],
|
|
||||||
// ["rtl8139", "Realtek 8139 Ethernet"],
|
|
||||||
// ["virtio", "Legacy paravirtualized Network I/O"],
|
|
||||||
// ["virtio-net-pci", "Paravirtualized Network I/O"],
|
|
||||||
// ["vmxnet3", "VMWare Paravirtualized Ethernet v3"]];
|
|
||||||
|
|
||||||
let networkTypes = [
|
let networkTypes = [
|
||||||
'e1000',
|
{ value: 'e1000', name: 'Intel Gigabit Ethernet' },
|
||||||
'e1000-82544gc',
|
{ value: 'e1000-82544gc', name: 'Intel 82544GC Gigabit Ethernet' },
|
||||||
'e1000-82545em',
|
{ value: 'e1000-82545em', name: 'Intel 82545EM Gigabit Ethernet' },
|
||||||
'e1000e',
|
{ value: 'e1000e', name: 'Intel PCIe Gigabit Ethernet' },
|
||||||
'rocker',
|
{ value: 'i82550', name: 'Intel i82550 Ethernet' },
|
||||||
'Intel Gigabit Ethernet',
|
{ value: 'i82551', name: 'Intel i82551 Ethernet' },
|
||||||
'i82550',
|
{ value: 'i82557a', name: 'Intel i82557A Ethernet' },
|
||||||
'i82551',
|
{ value: 'i82557b', name: 'Intel i82557B Ethernet' },
|
||||||
'i82557a',
|
{ value: 'i82557c', name: 'Intel i82557C Ethernet' },
|
||||||
'i82557b',
|
{ value: 'i82558a', name: 'Intel i82558A Ethernet' },
|
||||||
'i82557c',
|
{ value: 'i82558b', name: 'Intel i82558B Ethernet' },
|
||||||
'i82558a',
|
{ value: 'i82559a', name: 'Intel i82559A Ethernet' },
|
||||||
'i82558b',
|
{ value: 'i82559b', name: 'Intel i82559B Ethernet' },
|
||||||
'i82559a',
|
{ value: 'i82559c', name: 'Intel i82559C Ethernet' },
|
||||||
'i82559b',
|
{ value: 'i82559er', name: 'Intel i82559ER Ethernet' },
|
||||||
'i82559c',
|
{ value: 'i82562', name: 'Intel i82562 Ethernet' },
|
||||||
'i82559er',
|
{ value: 'i82801', name: 'Intel i82801 Ethernet' },
|
||||||
'i82562',
|
{ value: 'igb', name: 'Intel 82576 Gigabit Ethernet' },
|
||||||
'i82801',
|
{ value: 'ne2k_pci', name: 'NE2000 Ethernet' },
|
||||||
'ne2k_pci',
|
{ value: 'pcnet', name: 'AMD PCNet Ethernet' },
|
||||||
'pcnet',
|
{ value: 'rocker', name: 'Rocker L2 switch device' },
|
||||||
'rtl8139',
|
{ value: 'rtl8139', name: 'Realtek 8139 Ethernet' },
|
||||||
'virtio',
|
{ value: 'virtio-net-pci', name: 'Paravirtualized Network I/O' },
|
||||||
'virtio-net-pci',
|
{ value: 'vmxnet3', name: 'VMWare Paravirtualized Ethernet v3' },
|
||||||
'vmxnet3',
|
|
||||||
];
|
];
|
||||||
|
|
||||||
return networkTypes;
|
return networkTypes;
|
||||||
|
@ -78,6 +78,9 @@ describe('QemuService', () => {
|
|||||||
template_id: '1',
|
template_id: '1',
|
||||||
template_type: 'qemu',
|
template_type: 'qemu',
|
||||||
usage: '',
|
usage: '',
|
||||||
|
replicate_network_connection_state: true,
|
||||||
|
tpm: false,
|
||||||
|
uefi: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
service.saveTemplate(server, template).subscribe();
|
service.saveTemplate(server, template).subscribe();
|
||||||
@ -131,6 +134,9 @@ describe('QemuService', () => {
|
|||||||
template_id: '',
|
template_id: '',
|
||||||
template_type: 'qemu',
|
template_type: 'qemu',
|
||||||
usage: '',
|
usage: '',
|
||||||
|
replicate_network_connection_state: true,
|
||||||
|
tpm: false,
|
||||||
|
uefi: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
service.addTemplate(server, template).subscribe();
|
service.addTemplate(server, template).subscribe();
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -75,6 +75,9 @@ export class TemplateMocksService {
|
|||||||
template_id: '',
|
template_id: '',
|
||||||
template_type: 'qemu',
|
template_type: 'qemu',
|
||||||
usage: '',
|
usage: '',
|
||||||
|
replicate_network_connection_state: true,
|
||||||
|
tpm: false,
|
||||||
|
uefi: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
return of(template);
|
return of(template);
|
||||||
|
@ -38,7 +38,7 @@
|
|||||||
<body class="mat-app-background" oncontextmenu="return false;">
|
<body class="mat-app-background" oncontextmenu="return false;">
|
||||||
<app-root></app-root>
|
<app-root></app-root>
|
||||||
<!-- Global site tag (gtag.js) - Google Analytics -->
|
<!-- Global site tag (gtag.js) - Google Analytics -->
|
||||||
<script async src="https://www.googletagmanager.com/gtag/js?id=G-5D6FZL9923"></script>
|
<script async src="https://www.googletagmanager.com/gtag/js?id=G-0BT7QQV1W1"></script>
|
||||||
<script>
|
<script>
|
||||||
window.dataLayer = window.dataLayer || [];
|
window.dataLayer = window.dataLayer || [];
|
||||||
function gtag() {
|
function gtag() {
|
||||||
@ -46,7 +46,7 @@
|
|||||||
}
|
}
|
||||||
gtag('js', new Date());
|
gtag('js', new Date());
|
||||||
|
|
||||||
gtag('config', 'G-5D6FZL9923');
|
gtag('config', 'G-0BT7QQV1W1');
|
||||||
</script>
|
</script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
@ -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.
|
||||||
|
@ -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();
|
||||||
|
Reference in New Issue
Block a user