664 changed files with 22 additions and 137674 deletions
@ -1,9 +0,0 @@ |
|||
{ |
|||
"presets": [ |
|||
"@babel/preset-react", |
|||
"@babel/preset-env" |
|||
], |
|||
"plugins": [ |
|||
"react-hot-loader/babel" |
|||
] |
|||
} |
|||
@ -1,18 +0,0 @@ |
|||
{ |
|||
"extends": [ |
|||
"eslint:recommended", |
|||
"plugin:import/errors", |
|||
"plugin:import/warnings", |
|||
"angular" |
|||
], |
|||
"parser": "babel-eslint", |
|||
"settings": { |
|||
"import/ignore": [ |
|||
"node_modules", |
|||
"\\.tpl\\.html$" |
|||
] |
|||
}, |
|||
"globals": { |
|||
"FileReader": true |
|||
} |
|||
} |
|||
@ -1,2 +0,0 @@ |
|||
node_modules |
|||
.tern-project |
|||
@ -1,13 +0,0 @@ |
|||
{ |
|||
"globalstrict": true, |
|||
"globals": { |
|||
"angular": false, |
|||
"describe": false, |
|||
"it": false, |
|||
"expect": false, |
|||
"beforeEach": false, |
|||
"afterEach": false, |
|||
"module": false, |
|||
"inject": false |
|||
} |
|||
} |
|||
@ -1,292 +0,0 @@ |
|||
{ |
|||
"extends": ["stylelint-config-standard", "stylelint-config-recommended-scss"], |
|||
"plugins": [ |
|||
"stylelint-order" |
|||
], |
|||
"rules": { |
|||
"at-rule-empty-line-before": ["always", { |
|||
"except": ["first-nested"], |
|||
"ignore": ["after-comment"] |
|||
}], |
|||
"at-rule-name-space-after": "always", |
|||
"at-rule-no-vendor-prefix": true, |
|||
"at-rule-semicolon-space-before": "never", |
|||
"block-closing-brace-empty-line-before": "never", |
|||
"block-closing-brace-newline-after": null, |
|||
"block-opening-brace-space-before": null, |
|||
"color-named": "never", |
|||
"declaration-block-semicolon-newline-after": "always-multi-line", |
|||
"declaration-block-semicolon-newline-before": "never-multi-line", |
|||
"declaration-block-semicolon-space-after": "always-single-line", |
|||
"declaration-empty-line-before": null, |
|||
"declaration-no-important": null, |
|||
"font-family-name-quotes": "always-where-recommended", |
|||
"font-weight-notation": [ |
|||
"numeric", { |
|||
"ignore": ["relative"] |
|||
}], |
|||
"function-url-no-scheme-relative": true, |
|||
"function-url-quotes": "always", |
|||
"length-zero-no-unit": true, |
|||
"max-empty-lines": 2, |
|||
"max-line-length": null, |
|||
"media-feature-name-no-vendor-prefix": true, |
|||
"media-feature-parentheses-space-inside": "never", |
|||
"media-feature-range-operator-space-after": "always", |
|||
"media-feature-range-operator-space-before": "never", |
|||
"no-descending-specificity": null, |
|||
"no-duplicate-selectors": true, |
|||
"number-leading-zero": "never", |
|||
"media-feature-name-no-unknown": [true, { |
|||
"ignoreMediaFeatureNames": ["prefers-reduced-motion"] |
|||
}], |
|||
"order/properties-order": [ |
|||
"position", |
|||
"top", |
|||
"right", |
|||
"bottom", |
|||
"left", |
|||
"z-index", |
|||
"box-sizing", |
|||
"display", |
|||
"flex", |
|||
"flex-align", |
|||
"flex-basis", |
|||
"flex-direction", |
|||
"flex-wrap", |
|||
"flex-flow", |
|||
"flex-shrink", |
|||
"flex-grow", |
|||
"flex-order", |
|||
"flex-pack", |
|||
"align-content", |
|||
"align-items", |
|||
"align-self", |
|||
"justify-content", |
|||
"order", |
|||
"float", |
|||
"width", |
|||
"min-width", |
|||
"max-width", |
|||
"height", |
|||
"min-height", |
|||
"max-height", |
|||
"padding", |
|||
"padding-top", |
|||
"padding-right", |
|||
"padding-bottom", |
|||
"padding-left", |
|||
"margin", |
|||
"margin-top", |
|||
"margin-right", |
|||
"margin-bottom", |
|||
"margin-left", |
|||
"overflow", |
|||
"overflow-x", |
|||
"overflow-y", |
|||
"-webkit-overflow-scrolling", |
|||
"-ms-overflow-x", |
|||
"-ms-overflow-y", |
|||
"-ms-overflow-style", |
|||
"columns", |
|||
"column-count", |
|||
"column-fill", |
|||
"column-gap", |
|||
"column-rule", |
|||
"column-rule-width", |
|||
"column-rule-style", |
|||
"column-rule-color", |
|||
"column-span", |
|||
"column-width", |
|||
"orphans", |
|||
"widows", |
|||
"clip", |
|||
"clear", |
|||
"font", |
|||
"font-family", |
|||
"font-size", |
|||
"font-style", |
|||
"font-weight", |
|||
"font-variant", |
|||
"font-size-adjust", |
|||
"font-stretch", |
|||
"font-effect", |
|||
"font-emphasize", |
|||
"font-emphasize-position", |
|||
"font-emphasize-style", |
|||
"font-smooth", |
|||
"src", |
|||
"hyphens", |
|||
"line-height", |
|||
"color", |
|||
"text-align", |
|||
"text-align-last", |
|||
"text-emphasis", |
|||
"text-emphasis-color", |
|||
"text-emphasis-style", |
|||
"text-emphasis-position", |
|||
"text-decoration", |
|||
"text-indent", |
|||
"text-justify", |
|||
"text-outline", |
|||
"-ms-text-overflow", |
|||
"text-overflow", |
|||
"text-overflow-ellipsis", |
|||
"text-overflow-mode", |
|||
"text-shadow", |
|||
"text-transform", |
|||
"text-wrap", |
|||
"-webkit-text-size-adjust", |
|||
"-ms-text-size-adjust", |
|||
"letter-spacing", |
|||
"-ms-word-break", |
|||
"word-break", |
|||
"word-spacing", |
|||
"-ms-word-wrap", |
|||
"word-wrap", |
|||
"overflow-wrap", |
|||
"tab-size", |
|||
"white-space", |
|||
"vertical-align", |
|||
"direction", |
|||
"unicode-bidi", |
|||
"list-style", |
|||
"list-style-position", |
|||
"list-style-type", |
|||
"list-style-image", |
|||
"pointer-events", |
|||
"-ms-touch-action", |
|||
"touch-action", |
|||
"cursor", |
|||
"visibility", |
|||
"zoom", |
|||
"table-layout", |
|||
"empty-cells", |
|||
"caption-side", |
|||
"border-spacing", |
|||
"border-collapse", |
|||
"content", |
|||
"quotes", |
|||
"counter-reset", |
|||
"counter-increment", |
|||
"resize", |
|||
"user-select", |
|||
"nav-index", |
|||
"nav-up", |
|||
"nav-right", |
|||
"nav-down", |
|||
"nav-left", |
|||
"background", |
|||
"background-color", |
|||
"background-image", |
|||
"filter", |
|||
"background-repeat", |
|||
"background-attachment", |
|||
"background-position", |
|||
"background-position-x", |
|||
"background-position-y", |
|||
"background-clip", |
|||
"background-origin", |
|||
"background-size", |
|||
"border", |
|||
"border-color", |
|||
"border-style", |
|||
"border-width", |
|||
"border-top", |
|||
"border-top-color", |
|||
"border-top-style", |
|||
"border-top-width", |
|||
"border-right", |
|||
"border-right-color", |
|||
"border-right-style", |
|||
"border-right-width", |
|||
"border-bottom", |
|||
"border-bottom-color", |
|||
"border-bottom-style", |
|||
"border-bottom-width", |
|||
"border-left", |
|||
"border-left-color", |
|||
"border-left-style", |
|||
"border-left-width", |
|||
"border-radius", |
|||
"border-top-left-radius", |
|||
"border-top-right-radius", |
|||
"border-bottom-right-radius", |
|||
"border-bottom-left-radius", |
|||
"border-image", |
|||
"border-image-source", |
|||
"border-image-slice", |
|||
"border-image-width", |
|||
"border-image-outset", |
|||
"border-image-repeat", |
|||
"outline", |
|||
"outline-width", |
|||
"outline-style", |
|||
"outline-color", |
|||
"outline-offset", |
|||
"box-shadow", |
|||
"opacity", |
|||
"-ms-interpolation-mode", |
|||
"page-break-after", |
|||
"page-break-before", |
|||
"page-break-inside", |
|||
"transition", |
|||
"transition-delay", |
|||
"transition-timing-function", |
|||
"transition-duration", |
|||
"transition-property", |
|||
"transform", |
|||
"transform-origin", |
|||
"perspective", |
|||
"appearance", |
|||
"animation", |
|||
"animation-name", |
|||
"animation-duration", |
|||
"animation-play-state", |
|||
"animation-timing-function", |
|||
"animation-delay", |
|||
"animation-iteration-count", |
|||
"animation-direction", |
|||
"animation-fill-mode", |
|||
"fill", |
|||
"stroke" |
|||
], |
|||
"property-no-vendor-prefix": true, |
|||
"rule-empty-line-before": ["always", { |
|||
"except": ["first-nested"], |
|||
"ignore": ["after-comment"] |
|||
}], |
|||
"scss/dollar-variable-default": [true, { "ignore": "local" }], |
|||
"selector-attribute-quotes": "always", |
|||
"selector-list-comma-newline-after": "always", |
|||
"selector-list-comma-newline-before": "never-multi-line", |
|||
"selector-list-comma-space-after": "always-single-line", |
|||
"selector-list-comma-space-before": "never-single-line", |
|||
"selector-max-attribute": 2, |
|||
"selector-max-class": 6, |
|||
"selector-max-combinators": 8, |
|||
"selector-max-compound-selectors": 9, |
|||
"selector-max-empty-lines": 1, |
|||
"selector-max-id": 1, |
|||
"selector-max-specificity": null, |
|||
"selector-max-type": 5, |
|||
"selector-max-universal": 1, |
|||
"selector-no-qualifying-type": null, |
|||
"selector-no-vendor-prefix": true, |
|||
"selector-type-no-unknown": [true, { |
|||
"ignoreTypes": [ |
|||
"/^md-/", |
|||
"/^mdp-/", |
|||
"/^ng-/", |
|||
"/^tb-/", |
|||
"/^v-pane/" |
|||
] |
|||
}], |
|||
"string-quotes": "double", |
|||
"value-keyword-case": "lower", |
|||
"value-list-comma-newline-after": "always-multi-line", |
|||
"value-list-comma-newline-before": "never-multi-line", |
|||
"value-list-comma-space-after": "always-single-line", |
|||
"value-no-vendor-prefix": true |
|||
} |
|||
} |
|||
File diff suppressed because it is too large
@ -1,179 +0,0 @@ |
|||
{ |
|||
"name": "thingsboard", |
|||
"private": true, |
|||
"version": "2.5.0", |
|||
"description": "ThingsBoard UI", |
|||
"licenses": [ |
|||
{ |
|||
"type": "Apache-2.0", |
|||
"url": "http://www.apache.org/licenses/LICENSE-2.0" |
|||
} |
|||
], |
|||
"scripts": { |
|||
"start": "babel-node --max_old_space_size=4096 server.js", |
|||
"build": "cross-env NODE_OPTIONS=--max_old_space_size=4096 NODE_ENV=production webpack" |
|||
}, |
|||
"dependencies": { |
|||
"@flowjs/ng-flow": "^2.7.1", |
|||
"angular": "1.5.8", |
|||
"angular-animate": "1.5.8", |
|||
"angular-aria": "1.5.8", |
|||
"angular-breadcrumb": "^0.4.1", |
|||
"angular-carousel": "^1.0.1", |
|||
"angular-cookies": "1.5.8", |
|||
"angular-drag-and-drop-lists": "^1.4.0", |
|||
"angular-fixed-table-header": "^0.2.1", |
|||
"angular-fullscreen": "git://github.com/fabiobiondi/angular-fullscreen.git#master", |
|||
"angular-gridster": "^0.13.14", |
|||
"angular-hotkeys": "^1.7.0", |
|||
"angular-jwt": "^0.1.11", |
|||
"angular-material": "1.1.19", |
|||
"angular-material-data-table": "^0.10.9", |
|||
"angular-material-expansion-panel": "^0.7.2", |
|||
"angular-material-icons": "^0.7.1", |
|||
"angular-messages": "1.5.8", |
|||
"angular-route": "1.5.8", |
|||
"angular-sanitize": "1.5.8", |
|||
"angular-socialshare": "^2.3.8", |
|||
"angular-storage": "0.0.15", |
|||
"angular-touch": "1.5.8", |
|||
"angular-translate": "2.18.1", |
|||
"angular-translate-handler-log": "2.18.1", |
|||
"angular-translate-interpolation-messageformat": "2.18.1", |
|||
"angular-translate-loader-static-files": "2.18.1", |
|||
"angular-translate-storage-cookie": "2.18.1", |
|||
"angular-translate-storage-local": "2.18.1", |
|||
"angular-ui-ace": "^0.2.3", |
|||
"angular-ui-router": "^0.3.1", |
|||
"angular-websocket": "^2.0.1", |
|||
"base64-js": "^1.2.1", |
|||
"brace": "^0.10.0", |
|||
"canvas-gauges": "^2.0.9", |
|||
"clipboard": "^1.5.15", |
|||
"compass-sass-mixins": "^0.12.7", |
|||
"event-source-polyfill": "0.0.9", |
|||
"flot": "git://github.com/thingsboard/flot.git#0.9-work", |
|||
"flot.curvedlines": "git://github.com/MichaelZinsmaier/CurvedLines.git#master", |
|||
"font-awesome": "^4.6.3", |
|||
"javascript-detect-element-resize": "^0.5.3", |
|||
"jquery": "^3.4.1", |
|||
"jquery.terminal": "^1.5.0", |
|||
"js-beautify": "^1.10.0", |
|||
"json-schema-defaults": "^0.2.0", |
|||
"jstree": "^3.3.8", |
|||
"jszip": "^3.2.2", |
|||
"jstree-bootstrap-theme": "^1.0.1", |
|||
"leaflet": "^1.5.1", |
|||
"leaflet-polylinedecorator": "^1.6.0", |
|||
"leaflet-providers": "^1.8.0", |
|||
"leaflet.markercluster": "^1.4.1", |
|||
"material-steppers": "git://github.com/thingsboard/material-steppers.git#master", |
|||
"material-ui": "^0.16.1", |
|||
"material-ui-number-input": "^5.0.16", |
|||
"md-color-picker": "0.2.6", |
|||
"md-date-range-picker": "^0.8.4", |
|||
"mdPickers": "git://github.com/alenaksu/mdPickers.git#0.7.5", |
|||
"moment": "^2.24.0", |
|||
"ngFlowchart": "git://github.com/thingsboard/ngFlowchart.git#master", |
|||
"ngclipboard": "^1.1.1", |
|||
"ngreact": "^0.3.0", |
|||
"objectpath": "^1.2.1", |
|||
"oclazyload": "^1.0.9", |
|||
"raphael": "^2.2.8", |
|||
"rc-select": "^6.6.1", |
|||
"react": "^15.4.1", |
|||
"react-ace": "^4.1.0", |
|||
"react-dom": "^15.4.1", |
|||
"react-dropzone": "^3.7.3", |
|||
"react-schema-form": "^0.3.1", |
|||
"react-tap-event-plugin": "^2.0.1", |
|||
"reactcss": "^1.0.9", |
|||
"sass-material-colors": "0.0.5", |
|||
"schema-inspector": "^1.6.6", |
|||
"split.js": "1.3.5", |
|||
"tinycolor2": "^1.4.1", |
|||
"tooltipster": "^4.2.4", |
|||
"typeface-roboto": "0.0.22", |
|||
"v-accordion": "^1.6.0" |
|||
}, |
|||
"devDependencies": { |
|||
"@babel/cli": "^7.5.5", |
|||
"@babel/core": "^7.5.5", |
|||
"@babel/node": "^7.5.5", |
|||
"@babel/preset-env": "^7.5.5", |
|||
"@babel/preset-react": "^7.0.0", |
|||
"babel-eslint": "^10.0.2", |
|||
"babel-loader": "^8.0.6", |
|||
"browserslist": "^4.6.6", |
|||
"compression-webpack-plugin": "^3.0.0", |
|||
"connect-history-api-fallback": "^1.6.0", |
|||
"copy-webpack-plugin": "^5.0.3", |
|||
"cross-env": "^5.2.0", |
|||
"css-loader": "^3.1.0", |
|||
"directory-tree": "^2.2.3", |
|||
"eslint": "^6.0.1", |
|||
"eslint-config-angular": "^0.5.0", |
|||
"eslint-loader": "^2.2.1", |
|||
"eslint-plugin-angular": "^4.0.1", |
|||
"eslint-plugin-import": "^2.18.1", |
|||
"file-loader": "^4.1.0", |
|||
"html-loader": "^0.5.5", |
|||
"html-minifier": "^4.0.0", |
|||
"html-minifier-loader": "^1.4.1", |
|||
"html-webpack-plugin": "^3.2.0", |
|||
"imagemin": "^6.0.0", |
|||
"img-loader": "^3.0.1", |
|||
"jsonminify": "^0.4.1", |
|||
"less": "^3.9.0", |
|||
"less-loader": "^5.0.0", |
|||
"mini-css-extract-plugin": "^0.8.0", |
|||
"ng-annotate-loader": "^0.6.1", |
|||
"ng-annotate-patched": "^1.10.0", |
|||
"ngtemplate-loader": "^2.0.1", |
|||
"node-sass": "^4.12.0", |
|||
"postcss-loader": "^3.0.0", |
|||
"raw-loader": "^3.1.0", |
|||
"react-hot-loader": "^4.12.8", |
|||
"sass-loader": "^7.1.0", |
|||
"style-loader": "^0.23.1", |
|||
"stylelint": "13.0.0", |
|||
"stylelint-config-recommended-scss": "4.1.0", |
|||
"stylelint-config-standard": "19.0.0", |
|||
"stylelint-order": "4.0.0", |
|||
"stylelint-scss": "3.13.0", |
|||
"stylelint-webpack-plugin": "^1.2.1", |
|||
"uglifyjs-webpack-plugin": "^2.1.3", |
|||
"url-loader": "^2.1.0", |
|||
"webpack": "^4.37.0", |
|||
"webpack-cli": "^3.3.6", |
|||
"webpack-dev-middleware": "^3.7.0", |
|||
"webpack-dev-server": "^3.7.2", |
|||
"webpack-hot-middleware": "^2.25.0", |
|||
"webpack-material-design-icons": "^0.1.0" |
|||
}, |
|||
"engines": { |
|||
"node": ">=8.0.0 <11.0.0" |
|||
}, |
|||
"nyc": { |
|||
"exclude": [ |
|||
"test", |
|||
"__tests__", |
|||
"node_modules", |
|||
"target" |
|||
] |
|||
}, |
|||
"browserslist": [ |
|||
"> 0.5%", |
|||
"last 2 versions", |
|||
"Firefox ESR", |
|||
"not ie <= 10", |
|||
"not ie_mob <= 10", |
|||
"not bb <= 10", |
|||
"not op_mob <= 12.1" |
|||
], |
|||
"postcss": { |
|||
"plugins": { |
|||
"autoprefixer": true |
|||
} |
|||
} |
|||
} |
|||
@ -1,142 +0,0 @@ |
|||
<!-- |
|||
|
|||
Copyright © 2016-2020 The Thingsboard Authors |
|||
|
|||
Licensed under the Apache License, Version 2.0 (the "License"); |
|||
you may not use this file except in compliance with the License. |
|||
You may obtain a copy of the License at |
|||
|
|||
http://www.apache.org/licenses/LICENSE-2.0 |
|||
|
|||
Unless required by applicable law or agreed to in writing, software |
|||
distributed under the License is distributed on an "AS IS" BASIS, |
|||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
See the License for the specific language governing permissions and |
|||
limitations under the License. |
|||
|
|||
--> |
|||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" |
|||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> |
|||
<modelVersion>4.0.0</modelVersion> |
|||
<parent> |
|||
<groupId>org.thingsboard</groupId> |
|||
<version>2.5.0-SNAPSHOT</version> |
|||
<artifactId>thingsboard</artifactId> |
|||
</parent> |
|||
<groupId>org.thingsboard</groupId> |
|||
<artifactId>ui</artifactId> |
|||
<packaging>jar</packaging> |
|||
|
|||
<name>Thingsboard Server UI</name> |
|||
<url>https://thingsboard.io</url> |
|||
|
|||
<properties> |
|||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> |
|||
<main.dir>${basedir}/..</main.dir> |
|||
</properties> |
|||
|
|||
<build> |
|||
<resources> |
|||
<resource> |
|||
<directory>${project.build.directory}/generated-resources</directory> |
|||
</resource> |
|||
</resources> |
|||
<plugins> |
|||
<plugin> |
|||
<groupId>com.github.eirslett</groupId> |
|||
<artifactId>frontend-maven-plugin</artifactId> |
|||
<version>1.0</version> |
|||
<configuration> |
|||
<installDirectory>target</installDirectory> |
|||
<workingDirectory>${basedir}</workingDirectory> |
|||
</configuration> |
|||
<executions> |
|||
<execution> |
|||
<id>install node and npm</id> |
|||
<goals> |
|||
<goal>install-node-and-npm</goal> |
|||
</goals> |
|||
<configuration> |
|||
<nodeVersion>v10.16.0</nodeVersion> |
|||
<npmVersion>6.4.1</npmVersion> |
|||
</configuration> |
|||
</execution> |
|||
<execution> |
|||
<id>npm install</id> |
|||
<goals> |
|||
<goal>npm</goal> |
|||
</goals> |
|||
<configuration> |
|||
<arguments>install</arguments> |
|||
</configuration> |
|||
</execution> |
|||
</executions> |
|||
</plugin> |
|||
</plugins> |
|||
</build> |
|||
<profiles> |
|||
<profile> |
|||
<id>npm-build</id> |
|||
<activation> |
|||
<activeByDefault>true</activeByDefault> |
|||
</activation> |
|||
<build> |
|||
<plugins> |
|||
<plugin> |
|||
<groupId>com.github.eirslett</groupId> |
|||
<artifactId>frontend-maven-plugin</artifactId> |
|||
<version>1.0</version> |
|||
<configuration> |
|||
<installDirectory>target</installDirectory> |
|||
<workingDirectory>${basedir}</workingDirectory> |
|||
</configuration> |
|||
<executions> |
|||
<execution> |
|||
<id>npm build</id> |
|||
<goals> |
|||
<goal>npm</goal> |
|||
</goals> |
|||
<configuration> |
|||
<arguments>run build</arguments> |
|||
</configuration> |
|||
</execution> |
|||
</executions> |
|||
</plugin> |
|||
</plugins> |
|||
</build> |
|||
</profile> |
|||
<profile> |
|||
<id>npm-start</id> |
|||
<activation> |
|||
<property> |
|||
<name>npm-start</name> |
|||
</property> |
|||
</activation> |
|||
<build> |
|||
<plugins> |
|||
<plugin> |
|||
<groupId>com.github.eirslett</groupId> |
|||
<artifactId>frontend-maven-plugin</artifactId> |
|||
<version>1.0</version> |
|||
<configuration> |
|||
<installDirectory>target</installDirectory> |
|||
<workingDirectory>${basedir}</workingDirectory> |
|||
</configuration> |
|||
<executions> |
|||
<execution> |
|||
<id>npm start</id> |
|||
<goals> |
|||
<goal>npm</goal> |
|||
</goals> |
|||
|
|||
<configuration> |
|||
<arguments>start</arguments> |
|||
</configuration> |
|||
</execution> |
|||
</executions> |
|||
</plugin> |
|||
</plugins> |
|||
</build> |
|||
</profile> |
|||
</profiles> |
|||
</project> |
|||
@ -1,100 +0,0 @@ |
|||
/* |
|||
* Copyright © 2016-2020 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
/* eslint-disable import/no-commonjs */ |
|||
/* eslint-disable global-require */ |
|||
/* eslint-disable import/no-nodejs-modules */ |
|||
|
|||
const path = require('path'); |
|||
const webpack = require('webpack'); |
|||
const historyApiFallback = require("connect-history-api-fallback"); |
|||
const webpackDevMiddleware = require('webpack-dev-middleware'); |
|||
const webpackHotMiddleware = require('webpack-hot-middleware'); |
|||
const config = require('./webpack.config'); |
|||
|
|||
const express = require('express'); |
|||
const http = require('http'); |
|||
const httpProxy = require('http-proxy'); |
|||
const forwardHost = 'localhost'; |
|||
const forwardPort = 8080; |
|||
|
|||
const ruleNodeUiforwardHost = 'localhost'; |
|||
const ruleNodeUiforwardPort = 8080; |
|||
|
|||
const app = express(); |
|||
const server = http.createServer(app); |
|||
|
|||
const PORT = 3000; |
|||
|
|||
const compiler = webpack(config); |
|||
|
|||
app.use(historyApiFallback()); |
|||
app.use(webpackDevMiddleware(compiler, {noInfo: true, publicPath: config.output.publicPath})); |
|||
app.use(webpackHotMiddleware(compiler)); |
|||
|
|||
const root = path.join(__dirname, '/src'); |
|||
|
|||
app.use('/static', express.static(root)); |
|||
|
|||
const apiProxy = httpProxy.createProxyServer({ |
|||
target: { |
|||
host: forwardHost, |
|||
port: forwardPort |
|||
} |
|||
}); |
|||
|
|||
const ruleNodeUiApiProxy = httpProxy.createProxyServer({ |
|||
target: { |
|||
host: ruleNodeUiforwardHost, |
|||
port: ruleNodeUiforwardPort |
|||
} |
|||
}); |
|||
|
|||
apiProxy.on('error', function (err, req, res) { |
|||
console.warn('API proxy error: ' + err); |
|||
res.end('Error.'); |
|||
}); |
|||
|
|||
ruleNodeUiApiProxy.on('error', function (err, req, res) { |
|||
console.warn('RuleNode UI API proxy error: ' + err); |
|||
res.end('Error.'); |
|||
}); |
|||
|
|||
console.info(`Forwarding API requests to http://${forwardHost}:${forwardPort}`); |
|||
console.info(`Forwarding Rule Node UI requests to http://${ruleNodeUiforwardHost}:${ruleNodeUiforwardPort}`); |
|||
|
|||
app.all('/api/*', (req, res) => { |
|||
apiProxy.web(req, res); |
|||
}); |
|||
|
|||
app.all('/static/rulenode/*', (req, res) => { |
|||
ruleNodeUiApiProxy.web(req, res); |
|||
}); |
|||
|
|||
app.get('*', function(req, res) { |
|||
res.sendFile(path.join(__dirname, 'src/index.html')); |
|||
}); |
|||
|
|||
server.on('upgrade', (req, socket, head) => { |
|||
apiProxy.ws(req, socket, head); |
|||
}); |
|||
|
|||
server.listen(PORT, '0.0.0.0', (error) => { |
|||
if (error) { |
|||
console.error(error); |
|||
} else { |
|||
console.info(`==> 🌎 Listening on port ${PORT}. Open up http://localhost:${PORT}/ in your browser.`); |
|||
} |
|||
}); |
|||
@ -1,58 +0,0 @@ |
|||
/* |
|||
* Copyright © 2016-2020 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
import './settings-card.scss'; |
|||
|
|||
/*@ngInject*/ |
|||
export default function AdminController(adminService, toast, $scope, $rootScope, $state, $translate) { |
|||
|
|||
var vm = this; |
|||
vm.save = save; |
|||
vm.sendTestMail = sendTestMail; |
|||
vm.smtpProtocols = ('smtp smtps').split(' ').map(function (protocol) { |
|||
return protocol; |
|||
}); |
|||
|
|||
vm.tlsVersions = ['TLSv1', 'TLSv1.1', 'TLSv1.2', 'TLSv1.3']; |
|||
|
|||
$translate('admin.test-mail-sent').then(function (translation) { |
|||
vm.testMailSent = translation; |
|||
}, function (translationId) { |
|||
vm.testMailSent = translationId; |
|||
}); |
|||
|
|||
|
|||
loadSettings(); |
|||
|
|||
function loadSettings() { |
|||
adminService.getAdminSettings($state.$current.data.key).then(function success(settings) { |
|||
vm.settings = settings; |
|||
}); |
|||
} |
|||
|
|||
function save() { |
|||
adminService.saveAdminSettings(vm.settings).then(function success(settings) { |
|||
vm.settings = settings; |
|||
vm.settingsForm.$setPristine(); |
|||
}); |
|||
} |
|||
|
|||
function sendTestMail() { |
|||
adminService.sendTestMail(vm.settings).then(function success() { |
|||
toast.showSuccess($translate.instant('admin.test-mail-sent')); |
|||
}); |
|||
} |
|||
|
|||
} |
|||
@ -1,92 +0,0 @@ |
|||
/* |
|||
* Copyright © 2016-2020 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
/* eslint-disable import/no-unresolved, import/default */ |
|||
|
|||
import generalSettingsTemplate from '../admin/general-settings.tpl.html'; |
|||
import outgoingMailSettingsTemplate from '../admin/outgoing-mail-settings.tpl.html'; |
|||
import securitySettingsTemplate from '../admin/security-settings.tpl.html'; |
|||
|
|||
/* eslint-enable import/no-unresolved, import/default */ |
|||
|
|||
/*@ngInject*/ |
|||
export default function AdminRoutes($stateProvider) { |
|||
$stateProvider |
|||
.state('home.settings', { |
|||
url: '/settings', |
|||
module: 'private', |
|||
auth: ['SYS_ADMIN'], |
|||
redirectTo: 'home.settings.general', |
|||
ncyBreadcrumb: { |
|||
label: '{"icon": "settings", "label": "admin.system-settings"}' |
|||
} |
|||
}) |
|||
.state('home.settings.general', { |
|||
url: '/general', |
|||
module: 'private', |
|||
auth: ['SYS_ADMIN'], |
|||
views: { |
|||
"content@home": { |
|||
templateUrl: generalSettingsTemplate, |
|||
controllerAs: 'vm', |
|||
controller: 'AdminController' |
|||
} |
|||
}, |
|||
data: { |
|||
key: 'general', |
|||
pageTitle: 'admin.general-settings' |
|||
}, |
|||
ncyBreadcrumb: { |
|||
label: '{"icon": "settings_applications", "label": "admin.general"}' |
|||
} |
|||
}) |
|||
.state('home.settings.outgoing-mail', { |
|||
url: '/outgoing-mail', |
|||
module: 'private', |
|||
auth: ['SYS_ADMIN'], |
|||
views: { |
|||
"content@home": { |
|||
templateUrl: outgoingMailSettingsTemplate, |
|||
controllerAs: 'vm', |
|||
controller: 'AdminController' |
|||
} |
|||
}, |
|||
data: { |
|||
key: 'mail', |
|||
pageTitle: 'admin.outgoing-mail-settings' |
|||
}, |
|||
ncyBreadcrumb: { |
|||
label: '{"icon": "mail", "label": "admin.outgoing-mail"}' |
|||
} |
|||
}) |
|||
.state('home.settings.security-settings', { |
|||
url: '/security-settings', |
|||
module: 'private', |
|||
auth: ['SYS_ADMIN'], |
|||
views: { |
|||
"content@home": { |
|||
templateUrl: securitySettingsTemplate, |
|||
controllerAs: 'vm', |
|||
controller: 'SecuritySettingsController' |
|||
} |
|||
}, |
|||
data: { |
|||
pageTitle: 'admin.security-settings' |
|||
}, |
|||
ncyBreadcrumb: { |
|||
label: '{"icon": "security", "label": "admin.security-settings"}' |
|||
} |
|||
}); |
|||
} |
|||
@ -1,45 +0,0 @@ |
|||
<!-- |
|||
|
|||
Copyright © 2016-2020 The Thingsboard Authors |
|||
|
|||
Licensed under the Apache License, Version 2.0 (the "License"); |
|||
you may not use this file except in compliance with the License. |
|||
You may obtain a copy of the License at |
|||
|
|||
http://www.apache.org/licenses/LICENSE-2.0 |
|||
|
|||
Unless required by applicable law or agreed to in writing, software |
|||
distributed under the License is distributed on an "AS IS" BASIS, |
|||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
See the License for the specific language governing permissions and |
|||
limitations under the License. |
|||
|
|||
--> |
|||
<div> |
|||
<md-card class="settings-card"> |
|||
<md-card-title> |
|||
<md-card-title-text> |
|||
<span translate class="md-headline">admin.general-settings</span> |
|||
</md-card-title-text> |
|||
</md-card-title> |
|||
<md-progress-linear md-mode="indeterminate" ng-disabled="!$root.loading" ng-show="$root.loading"></md-progress-linear> |
|||
<span style="min-height: 5px;" flex="" ng-show="!$root.loading"></span> |
|||
<md-card-content> |
|||
<form name="vm.settingsForm" ng-submit="vm.save()" tb-confirm-on-exit confirm-form="vm.settingsForm"> |
|||
<fieldset ng-disabled="$root.loading"> |
|||
<md-input-container class="md-block"> |
|||
<label translate>admin.base-url</label> |
|||
<input required name="baseUrl" ng-model="vm.settings.jsonValue.baseUrl"> |
|||
<div ng-messages="vm.settingsForm.baseUrl.$error"> |
|||
<div translate ng-message="required">admin.base-url-required</div> |
|||
</div> |
|||
</md-input-container> |
|||
<div layout="row" layout-align="end center" width="100%" layout-wrap> |
|||
<md-button ng-disabled="$root.loading || vm.settingsForm.$invalid || !vm.settingsForm.$dirty" type="submit" class="md-raised md-primary">{{'action.save' | translate}}</md-button> |
|||
</div> |
|||
</fieldset> |
|||
</form> |
|||
</md-card-content> |
|||
</md-card> |
|||
</div> |
|||
|
|||
@ -1,38 +0,0 @@ |
|||
/* |
|||
* Copyright © 2016-2020 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
import uiRouter from 'angular-ui-router'; |
|||
import ngMaterial from 'angular-material'; |
|||
import ngMessages from 'angular-messages'; |
|||
import thingsboardApiAdmin from '../api/admin.service'; |
|||
import thingsboardConfirmOnExit from '../components/confirm-on-exit.directive'; |
|||
import thingsboardToast from '../services/toast'; |
|||
|
|||
import AdminRoutes from './admin.routes'; |
|||
import AdminController from './admin.controller'; |
|||
import SecuritySettingsController from './security-settings.controller'; |
|||
|
|||
export default angular.module('thingsboard.admin', [ |
|||
uiRouter, |
|||
ngMaterial, |
|||
ngMessages, |
|||
thingsboardApiAdmin, |
|||
thingsboardConfirmOnExit, |
|||
thingsboardToast |
|||
]) |
|||
.config(AdminRoutes) |
|||
.controller('AdminController', AdminController) |
|||
.controller('SecuritySettingsController', SecuritySettingsController) |
|||
.name; |
|||
@ -1,107 +0,0 @@ |
|||
<!-- |
|||
|
|||
Copyright © 2016-2020 The Thingsboard Authors |
|||
|
|||
Licensed under the Apache License, Version 2.0 (the "License"); |
|||
you may not use this file except in compliance with the License. |
|||
You may obtain a copy of the License at |
|||
|
|||
http://www.apache.org/licenses/LICENSE-2.0 |
|||
|
|||
Unless required by applicable law or agreed to in writing, software |
|||
distributed under the License is distributed on an "AS IS" BASIS, |
|||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
See the License for the specific language governing permissions and |
|||
limitations under the License. |
|||
|
|||
--> |
|||
<div tb-help="'outgoingMailSettings'" help-container-id="help-container"> |
|||
<md-card class="settings-card"> |
|||
<md-card-title> |
|||
<md-card-title-text layout="row"> |
|||
<span translate class="md-headline">admin.outgoing-mail-settings</span> |
|||
<span flex></span> |
|||
<div id="help-container"></div> |
|||
</md-card-title-text> |
|||
</md-card-title> |
|||
<md-progress-linear md-mode="indeterminate" ng-disabled="!$root.loading" ng-show="$root.loading"></md-progress-linear> |
|||
<span style="min-height: 5px;" flex="" ng-show="!$root.loading"></span> |
|||
<md-card-content> |
|||
<form name="vm.settingsForm" ng-submit="vm.save()" tb-confirm-on-exit confirm-form="vm.settingsForm"> |
|||
<fieldset ng-disabled="$root.loading"> |
|||
<md-input-container class="md-block"> |
|||
<label translate>admin.mail-from</label> |
|||
<input required name="mailFrom" ng-model="vm.settings.jsonValue.mailFrom"> |
|||
<div ng-messages="vm.settingsForm.mailFrom.$error"> |
|||
<div translate ng-message="required">admin.mail-from-required</div> |
|||
</div> |
|||
</md-input-container> |
|||
<md-input-container class="md-block"> |
|||
<label translate>admin.smtp-protocol</label> |
|||
<md-select ng-disabled="$root.loading" ng-model="vm.settings.jsonValue.smtpProtocol"> |
|||
<md-option ng-repeat="smtpProtocol in vm.smtpProtocols" value="{{smtpProtocol}}"> |
|||
{{smtpProtocol.toUpperCase()}} |
|||
</md-option> |
|||
</md-select> |
|||
</md-input-container> |
|||
<div layout-gt-sm="row"> |
|||
<md-input-container class="md-block" flex="100" flex-gt-sm="60"> |
|||
<label translate>admin.smtp-host</label> |
|||
<input required name="smtpHost" ng-model="vm.settings.jsonValue.smtpHost" |
|||
placeholder="localhost"> |
|||
<div ng-messages="vm.settingsForm.smtpHost.$error"> |
|||
<div translate ng-message="required">admin.smtp-host-required</div> |
|||
</div> |
|||
</md-input-container> |
|||
<md-input-container class="md-block" flex="100" flex-gt-sm="40"> |
|||
<label translate>admin.smtp-port</label> |
|||
<input required name="smtpPort" ng-model="vm.settings.jsonValue.smtpPort" |
|||
placeholder="25" |
|||
ng-pattern="/^([0-9]{1,4}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])$/" |
|||
md-maxlength="5"> |
|||
<div ng-messages="vm.settingsForm.smtpPort.$error" role="alert" multiple> |
|||
<div translate ng-message="required">admin.smtp-port-required</div> |
|||
<div translate ng-message="pattern">admin.smtp-port-invalid</div> |
|||
<div translate ng-message="md-maxlength">admin.smtp-port-invalid</div> |
|||
</div> |
|||
</md-input-container> |
|||
</div> |
|||
<md-input-container class="md-block"> |
|||
<label translate>admin.timeout-msec</label> |
|||
<input required name="timeout" ng-model="vm.settings.jsonValue.timeout" |
|||
placeholder="10000" |
|||
ng-pattern="/^[0-9]{1,6}$/" |
|||
md-maxlength="6"> |
|||
<div ng-messages="vm.settingsForm.timeout.$error" role="alert" multiple> |
|||
<div translate ng-message="required">admin.timeout-required</div> |
|||
<div translate ng-message="pattern">admin.timeout-invalid</div> |
|||
<div translate ng-message="md-maxlength">admin.timeout-invalid</div> |
|||
</div> |
|||
</md-input-container> |
|||
<md-checkbox ng-disabled="$root.loading" |
|||
aria-label="{{ 'admin.enable-tls' | translate }}" ng-model="vm.settings.jsonValue.enableTls">{{ 'admin.enable-tls' | translate }}</md-checkbox> |
|||
<md-input-container class="md-block" ng-if="vm.settings.jsonValue.enableTls"> |
|||
<label translate>admin.tls-version</label> |
|||
<md-select ng-disabled="$root.loading" ng-model="vm.settings.jsonValue.tlsVersion"> |
|||
<md-option ng-repeat="tlsVersion in vm.tlsVersions" value="{{tlsVersion}}"> |
|||
{{tlsVersion}} |
|||
</md-option> |
|||
</md-select> |
|||
</md-input-container> |
|||
<md-input-container class="md-block"> |
|||
<label translate>common.username</label> |
|||
<input name="username" placeholder="{{ 'common.enter-username' | translate }}" ng-model="vm.settings.jsonValue.username"> |
|||
</md-input-container> |
|||
<md-input-container class="md-block"> |
|||
<label translate>common.password</label> |
|||
<input name="password" placeholder="{{ 'common.enter-password' | translate }}" type="password" ng-model="vm.settings.jsonValue.password"> |
|||
</md-input-container> |
|||
<div layout="row" layout-align="end center" width="100%" layout-wrap> |
|||
<md-button ng-disabled="$root.loading || vm.settingsForm.$invalid" ng-click="vm.sendTestMail()" class="md-raised">{{'admin.send-test-mail' | translate}}</md-button> |
|||
<md-button ng-disabled="$root.loading || vm.settingsForm.$invalid || !vm.settingsForm.$dirty" type="submit" class="md-raised md-primary">{{'action.save' | translate}}</md-button> |
|||
</div> |
|||
</fieldset> |
|||
</form> |
|||
</md-card-content> |
|||
</md-card> |
|||
</div> |
|||
@ -1,41 +0,0 @@ |
|||
/* |
|||
* Copyright © 2016-2020 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
import './settings-card.scss'; |
|||
|
|||
/*@ngInject*/ |
|||
export default function SecuritySettingsController(adminService, $mdExpansionPanel) { |
|||
|
|||
var vm = this; |
|||
vm.$mdExpansionPanel = $mdExpansionPanel; |
|||
|
|||
vm.save = save; |
|||
|
|||
loadSettings(); |
|||
|
|||
function loadSettings() { |
|||
adminService.getSecuritySettings().then(function success(securitySettings) { |
|||
vm.securitySettings = securitySettings; |
|||
}); |
|||
} |
|||
|
|||
function save() { |
|||
adminService.saveSecuritySettings(vm.securitySettings).then(function success(securitySettings) { |
|||
vm.securitySettings = securitySettings; |
|||
vm.settingsForm.$setPristine(); |
|||
}); |
|||
} |
|||
|
|||
} |
|||
@ -1,167 +0,0 @@ |
|||
<!-- |
|||
|
|||
Copyright © 2016-2020 The Thingsboard Authors |
|||
|
|||
Licensed under the Apache License, Version 2.0 (the "License"); |
|||
you may not use this file except in compliance with the License. |
|||
You may obtain a copy of the License at |
|||
|
|||
http://www.apache.org/licenses/LICENSE-2.0 |
|||
|
|||
Unless required by applicable law or agreed to in writing, software |
|||
distributed under the License is distributed on an "AS IS" BASIS, |
|||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
See the License for the specific language governing permissions and |
|||
limitations under the License. |
|||
|
|||
--> |
|||
<div tb-help="'securitySettings'" help-container-id="help-container"> |
|||
<md-card class="settings-card"> |
|||
<md-card-title> |
|||
<md-card-title-text layout="row"> |
|||
<span translate class="md-headline">admin.security-settings</span> |
|||
<span flex></span> |
|||
<div id="help-container"></div> |
|||
</md-card-title-text> |
|||
</md-card-title> |
|||
<md-progress-linear md-mode="indeterminate" ng-disabled="!$root.loading" ng-show="$root.loading"></md-progress-linear> |
|||
<span style="min-height: 5px;" flex="" ng-show="!$root.loading"></span> |
|||
<md-card-content> |
|||
<form name="vm.settingsForm" ng-submit="vm.save()" tb-confirm-on-exit confirm-form="vm.settingsForm"> |
|||
<fieldset ng-disabled="$root.loading"> |
|||
<md-expansion-panel-group md-component-id="securitySettingsPanelGroup" auto-expand="true" multiple> |
|||
<md-expansion-panel md-component-id="generalPolicyPanel" id="generalPolicyPanel"> |
|||
<md-expansion-panel-collapsed> |
|||
<div class="tb-panel-title" translate>admin.general-policy</div> |
|||
<md-expansion-panel-icon></md-expansion-panel-icon> |
|||
</md-expansion-panel-collapsed> |
|||
<md-expansion-panel-expanded> |
|||
<md-expansion-panel-header ng-click="vm.$mdExpansionPanel('generalPolicyPanel').collapse()"> |
|||
<div class="tb-panel-title" translate>admin.general-policy</div> |
|||
<md-expansion-panel-icon></md-expansion-panel-icon> |
|||
</md-expansion-panel-header> |
|||
<md-expansion-panel-content> |
|||
<md-input-container class="md-block"> |
|||
<label translate>admin.max-failed-login-attempts</label> |
|||
<input type="number" |
|||
step="1" |
|||
min="0" |
|||
name="maxFailedLoginAttempts" |
|||
ng-model="vm.securitySettings.maxFailedLoginAttempts"> |
|||
<div ng-messages="vm.settingsForm.maxFailedLoginAttempts.$error"> |
|||
<div translate ng-message="min">admin.minimum-max-failed-login-attempts-range</div> |
|||
</div> |
|||
</md-input-container> |
|||
<md-input-container class="md-block"> |
|||
<label translate>admin.user-lockout-notification-email</label> |
|||
<input type="email" |
|||
name="userLockoutNotificationEmail" |
|||
ng-model="vm.securitySettings.userLockoutNotificationEmail"> |
|||
</md-input-container> |
|||
</md-expansion-panel-content> |
|||
</md-expansion-panel-expanded> |
|||
</md-expansion-panel> |
|||
<md-expansion-panel md-component-id="passwordPolicyPanel" id="passwordPolicyPanel"> |
|||
<md-expansion-panel-collapsed> |
|||
<div class="tb-panel-title" translate>admin.password-policy</div> |
|||
<md-expansion-panel-icon></md-expansion-panel-icon> |
|||
</md-expansion-panel-collapsed> |
|||
<md-expansion-panel-expanded> |
|||
<md-expansion-panel-header ng-click="vm.$mdExpansionPanel('passwordPolicyPanel').collapse()"> |
|||
<div class="tb-panel-title" translate>admin.password-policy</div> |
|||
<md-expansion-panel-icon></md-expansion-panel-icon> |
|||
</md-expansion-panel-header> |
|||
<md-expansion-panel-content> |
|||
<md-input-container class="md-block"> |
|||
<label translate>admin.minimum-password-length</label> |
|||
<input type="number" |
|||
step="1" |
|||
min="5" |
|||
max="50" |
|||
required |
|||
name="minimumPasswordLength" |
|||
ng-model="vm.securitySettings.passwordPolicy.minimumLength"> |
|||
<div ng-messages="vm.settingsForm.minimumPasswordLength.$error"> |
|||
<div translate ng-message="required">admin.minimum-password-length-required</div> |
|||
<div translate ng-message="min">admin.minimum-password-length-range</div> |
|||
<div translate ng-message="max">admin.minimum-password-length-range</div> |
|||
</div> |
|||
</md-input-container> |
|||
<md-input-container class="md-block"> |
|||
<label translate>admin.minimum-uppercase-letters</label> |
|||
<input type="number" |
|||
step="1" |
|||
min="0" |
|||
name="minimumUppercaseLetters" |
|||
ng-model="vm.securitySettings.passwordPolicy.minimumUppercaseLetters"> |
|||
<div ng-messages="vm.settingsForm.minimumUppercaseLetters.$error"> |
|||
<div translate ng-message="min">admin.minimum-uppercase-letters-range</div> |
|||
</div> |
|||
</md-input-container> |
|||
<md-input-container class="md-block"> |
|||
<label translate>admin.minimum-lowercase-letters</label> |
|||
<input type="number" |
|||
step="1" |
|||
min="0" |
|||
name="minimumLowercaseLetters" |
|||
ng-model="vm.securitySettings.passwordPolicy.minimumLowercaseLetters"> |
|||
<div ng-messages="vm.settingsForm.minimumLowercaseLetters.$error"> |
|||
<div translate ng-message="min">admin.minimum-lowercase-letters-range</div> |
|||
</div> |
|||
</md-input-container> |
|||
<md-input-container class="md-block"> |
|||
<label translate>admin.minimum-digits</label> |
|||
<input type="number" |
|||
step="1" |
|||
min="0" |
|||
name="minimumDigits" |
|||
ng-model="vm.securitySettings.passwordPolicy.minimumDigits"> |
|||
<div ng-messages="vm.settingsForm.minimumDigits.$error"> |
|||
<div translate ng-message="min">admin.minimum-digits-range</div> |
|||
</div> |
|||
</md-input-container> |
|||
<md-input-container class="md-block"> |
|||
<label translate>admin.minimum-special-characters</label> |
|||
<input type="number" |
|||
step="1" |
|||
min="0" |
|||
name="minimumSpecialCharacters" |
|||
ng-model="vm.securitySettings.passwordPolicy.minimumSpecialCharacters"> |
|||
<div ng-messages="vm.settingsForm.minimumSpecialCharacters.$error"> |
|||
<div translate ng-message="min">admin.minimum-special-characters-range</div> |
|||
</div> |
|||
</md-input-container> |
|||
<md-input-container class="md-block"> |
|||
<label translate>admin.password-expiration-period-days</label> |
|||
<input type="number" |
|||
step="1" |
|||
min="0" |
|||
name="passwordExpirationPeriodDays" |
|||
ng-model="vm.securitySettings.passwordPolicy.passwordExpirationPeriodDays"> |
|||
<div ng-messages="vm.settingsForm.passwordExpirationPeriodDays.$error"> |
|||
<div translate ng-message="min">admin.password-expiration-period-days-range</div> |
|||
</div> |
|||
</md-input-container> |
|||
<md-input-container class="md-block"> |
|||
<label translate>admin.password-reuse-frequency-days</label> |
|||
<input type="number" |
|||
step="1" |
|||
min="0" |
|||
name="passwordReuseFrequencyDays" |
|||
ng-model="vm.securitySettings.passwordPolicy.passwordReuseFrequencyDays"> |
|||
<div ng-messages="vm.settingsForm.passwordReuseFrequencyDays.$error"> |
|||
<div translate ng-message="min">admin.password-reuse-frequency-days-range</div> |
|||
</div> |
|||
</md-input-container> |
|||
</md-expansion-panel-content> |
|||
</md-expansion-panel-expanded> |
|||
</md-expansion-panel> |
|||
</md-expansion-panel-group> |
|||
<div layout="row" layout-align="end center" width="100%" layout-wrap> |
|||
<md-button ng-disabled="$root.loading || vm.settingsForm.$invalid || !vm.settingsForm.$dirty" type="submit" class="md-raised md-primary">{{'action.save' | translate}}</md-button> |
|||
</div> |
|||
</fieldset> |
|||
</form> |
|||
</md-card-content> |
|||
</md-card> |
|||
</div> |
|||
@ -1,26 +0,0 @@ |
|||
/** |
|||
* Copyright © 2016-2020 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0 |
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
@import "../../scss/constants"; |
|||
|
|||
md-card.settings-card { |
|||
@media (min-width: $layout-breakpoint-sm) { |
|||
width: 60%; |
|||
} |
|||
|
|||
md-icon.md-expansion-panel-icon { |
|||
margin-right: 0; |
|||
} |
|||
} |
|||
@ -1,141 +0,0 @@ |
|||
/* |
|||
* Copyright © 2016-2020 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
import 'brace/ext/language_tools'; |
|||
import 'brace/ext/searchbox'; |
|||
import 'brace/mode/json'; |
|||
import 'brace/theme/github'; |
|||
import beautify from 'js-beautify'; |
|||
|
|||
import './alarm-details-dialog.scss'; |
|||
|
|||
const js_beautify = beautify.js; |
|||
|
|||
/*@ngInject*/ |
|||
export default function AlarmDetailsDialogController($mdDialog, $filter, $translate, types, |
|||
alarmService, alarmId, allowAcknowledgment, allowClear, displayDetails, showingCallback) { |
|||
|
|||
var vm = this; |
|||
|
|||
vm.alarmId = alarmId; |
|||
vm.allowAcknowledgment = allowAcknowledgment; |
|||
vm.allowClear = allowClear; |
|||
vm.displayDetails = displayDetails; |
|||
vm.types = types; |
|||
vm.alarm = null; |
|||
|
|||
vm.alarmUpdated = false; |
|||
|
|||
showingCallback.onShowing = function(scope, element) { |
|||
if (vm.displayDetails) { |
|||
updateEditorSize(element); |
|||
} |
|||
} |
|||
|
|||
vm.alarmDetailsOptions = { |
|||
useWrapMode: false, |
|||
mode: 'json', |
|||
showGutter: false, |
|||
showPrintMargin: false, |
|||
theme: 'github', |
|||
advanced: { |
|||
enableSnippets: false, |
|||
enableBasicAutocompletion: false, |
|||
enableLiveAutocompletion: false |
|||
}, |
|||
onLoad: function (_ace) { |
|||
vm.editor = _ace; |
|||
} |
|||
}; |
|||
|
|||
vm.close = close; |
|||
vm.acknowledge = acknowledge; |
|||
vm.clear = clear; |
|||
|
|||
loadAlarm(); |
|||
|
|||
function updateEditorSize(element) { |
|||
var newWidth = 600; |
|||
var newHeight = 200; |
|||
angular.element('#tb-alarm-details', element).height(newHeight.toString() + "px") |
|||
.width(newWidth.toString() + "px"); |
|||
vm.editor.resize(); |
|||
} |
|||
|
|||
function loadAlarm() { |
|||
alarmService.getAlarmInfo(vm.alarmId).then( |
|||
function success(alarm) { |
|||
vm.alarm = alarm; |
|||
loadAlarmFields(); |
|||
}, |
|||
function fail() { |
|||
vm.alarm = null; |
|||
} |
|||
); |
|||
} |
|||
|
|||
function loadAlarmFields() { |
|||
vm.createdTime = $filter('date')(vm.alarm.createdTime, 'yyyy-MM-dd HH:mm:ss'); |
|||
vm.startTime = null; |
|||
if (vm.alarm.startTs) { |
|||
vm.startTime = $filter('date')(vm.alarm.startTs, 'yyyy-MM-dd HH:mm:ss'); |
|||
} |
|||
vm.endTime = null; |
|||
if (vm.alarm.endTs) { |
|||
vm.endTime = $filter('date')(vm.alarm.endTs, 'yyyy-MM-dd HH:mm:ss'); |
|||
} |
|||
vm.ackTime = null; |
|||
if (vm.alarm.ackTs) { |
|||
vm.ackTime = $filter('date')(vm.alarm.ackTs, 'yyyy-MM-dd HH:mm:ss') |
|||
} |
|||
vm.clearTime = null; |
|||
if (vm.alarm.clearTs) { |
|||
vm.clearTime = $filter('date')(vm.alarm.clearTs, 'yyyy-MM-dd HH:mm:ss'); |
|||
} |
|||
|
|||
vm.alarmSeverity = $translate.instant(types.alarmSeverity[vm.alarm.severity].name); |
|||
|
|||
vm.alarmStatus = $translate.instant('alarm.display-status.' + vm.alarm.status); |
|||
|
|||
vm.alarmDetails = null; |
|||
if (vm.alarm.details) { |
|||
vm.alarmDetails = angular.toJson(vm.alarm.details); |
|||
vm.alarmDetails = js_beautify(vm.alarmDetails, {indent_size: 4}); |
|||
} |
|||
} |
|||
|
|||
function acknowledge () { |
|||
alarmService.ackAlarm(vm.alarmId).then( |
|||
function success() { |
|||
vm.alarmUpdated = true; |
|||
loadAlarm(); |
|||
} |
|||
); |
|||
} |
|||
|
|||
function clear () { |
|||
alarmService.clearAlarm(vm.alarmId).then( |
|||
function success() { |
|||
vm.alarmUpdated = true; |
|||
loadAlarm(); |
|||
} |
|||
); |
|||
} |
|||
|
|||
function close () { |
|||
$mdDialog.hide(vm.alarmUpdated ? vm.alarm : null); |
|||
} |
|||
|
|||
} |
|||
@ -1,27 +0,0 @@ |
|||
/** |
|||
* Copyright © 2016-2020 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0 |
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
.tb-alarm-details-panel { |
|||
height: 100%; |
|||
margin-left: 15px; |
|||
border: 1px solid #c0c0c0; |
|||
|
|||
#tb-alarm-details { |
|||
width: 100%; |
|||
min-width: 600px; |
|||
height: 100%; |
|||
min-height: 200px; |
|||
} |
|||
} |
|||
@ -1,107 +0,0 @@ |
|||
<!-- |
|||
|
|||
Copyright © 2016-2020 The Thingsboard Authors |
|||
|
|||
Licensed under the Apache License, Version 2.0 (the "License"); |
|||
you may not use this file except in compliance with the License. |
|||
You may obtain a copy of the License at |
|||
|
|||
http://www.apache.org/licenses/LICENSE-2.0 |
|||
|
|||
Unless required by applicable law or agreed to in writing, software |
|||
distributed under the License is distributed on an "AS IS" BASIS, |
|||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
See the License for the specific language governing permissions and |
|||
limitations under the License. |
|||
|
|||
--> |
|||
<md-dialog aria-label="{{ 'alarm.alarm-details' | translate }}"> |
|||
<md-toolbar> |
|||
<div class="md-toolbar-tools"> |
|||
<h2 translate>alarm.alarm-details</h2> |
|||
<span flex></span> |
|||
<md-button class="md-icon-button" ng-click="vm.close()"> |
|||
<ng-md-icon icon="close" aria-label="{{ 'dialog.close' | translate }}"></ng-md-icon> |
|||
</md-button> |
|||
</div> |
|||
</md-toolbar> |
|||
<md-dialog-content> |
|||
<div class="md-dialog-content" layout="column"> |
|||
<div layout="row"> |
|||
<md-input-container class="md-block"> |
|||
<label translate>alarm.created-time</label> |
|||
<input ng-model="vm.createdTime" readonly> |
|||
</md-input-container> |
|||
<md-input-container flex class="md-block"> |
|||
<label translate>alarm.originator</label> |
|||
<input ng-model="vm.alarm.originatorName" readonly> |
|||
</md-input-container> |
|||
</div> |
|||
<div layout="row" ng-if="vm.startTime || vm.endTime"> |
|||
<md-input-container ng-if="vm.startTime" flex class="md-block"> |
|||
<label translate>alarm.start-time</label> |
|||
<input ng-model="vm.startTime" readonly> |
|||
</md-input-container> |
|||
<md-input-container ng-if="vm.endTime" flex class="md-block"> |
|||
<label translate>alarm.end-time</label> |
|||
<input ng-model="vm.endTime" readonly> |
|||
</md-input-container> |
|||
<span flex ng-if="!vm.startTime || !vm.endTime"></span> |
|||
</div> |
|||
<div layout="row" ng-if="vm.ackTime || vm.clearTime"> |
|||
<md-input-container ng-if="vm.ackTime" flex class="md-block"> |
|||
<label translate>alarm.ack-time</label> |
|||
<input ng-model="vm.ackTime" readonly> |
|||
</md-input-container> |
|||
<md-input-container ng-if="vm.clearTime" flex class="md-block"> |
|||
<label translate>alarm.clear-time</label> |
|||
<input ng-model="vm.clearTime" readonly> |
|||
</md-input-container> |
|||
<span flex ng-if="!vm.ackTime || !vm.clearTime"></span> |
|||
</div> |
|||
<div layout="row"> |
|||
<md-input-container flex class="md-block"> |
|||
<label translate>alarm.type</label> |
|||
<input ng-model="vm.alarm.type" readonly> |
|||
</md-input-container> |
|||
<md-input-container flex class="md-block"> |
|||
<label translate>alarm.severity</label> |
|||
<input class="tb-severity" ng-class="vm.types.alarmSeverity[vm.alarm.severity].class" |
|||
ng-model="vm.alarmSeverity" readonly> |
|||
</md-input-container> |
|||
<md-input-container flex class="md-block"> |
|||
<label translate>alarm.status</label> |
|||
<input ng-model="vm.alarmStatus" readonly> |
|||
</md-input-container> |
|||
</div> |
|||
<div ng-show="vm.displayDetails" class="md-caption" style="padding-left: 3px; padding-bottom: 10px; color: rgba(0,0,0,0.57);" translate>alarm.details</div> |
|||
<div ng-show="vm.displayDetails" flex class="tb-alarm-details-panel" layout="column"> |
|||
<div flex id="tb-alarm-details" readonly |
|||
ui-ace="vm.alarmDetailsOptions" |
|||
ng-model="vm.alarmDetails"> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</md-dialog-content> |
|||
<md-dialog-actions layout="row"> |
|||
<md-button ng-if="vm.allowAcknowledgment && (vm.alarm.status==vm.types.alarmStatus.activeUnack || |
|||
vm.alarm.status==vm.types.alarmStatus.clearedUnack)" |
|||
class="md-raised md-primary" |
|||
ng-disabled="$root.loading" |
|||
ng-click="vm.acknowledge()" |
|||
style="margin-right:20px;">{{ 'alarm.acknowledge' | |
|||
translate }} |
|||
</md-button> |
|||
<md-button ng-if="vm.allowClear && (vm.alarm.status==vm.types.alarmStatus.activeAck || |
|||
vm.alarm.status==vm.types.alarmStatus.activeUnack)" |
|||
class="md-raised md-primary" |
|||
ng-disabled="$root.loading" |
|||
ng-click="vm.clear()">{{ 'alarm.clear' | |
|||
translate }} |
|||
</md-button> |
|||
<span flex></span> |
|||
<md-button ng-disabled="$root.loading" ng-click="vm.close()" style="margin-right:20px;">{{ 'action.close' | |
|||
translate }} |
|||
</md-button> |
|||
</md-dialog-actions> |
|||
</md-dialog> |
|||
@ -1,39 +0,0 @@ |
|||
/* |
|||
* Copyright © 2016-2020 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
/* eslint-disable import/no-unresolved, import/default */ |
|||
|
|||
import alarmHeaderTemplate from './alarm-header.tpl.html'; |
|||
|
|||
/* eslint-enable import/no-unresolved, import/default */ |
|||
|
|||
/*@ngInject*/ |
|||
export default function AlarmHeaderDirective($compile, $templateCache) { |
|||
|
|||
var linker = function (scope, element) { |
|||
|
|||
var template = $templateCache.get(alarmHeaderTemplate); |
|||
element.html(template); |
|||
$compile(element.contents())(scope); |
|||
|
|||
} |
|||
|
|||
return { |
|||
restrict: "A", |
|||
replace: false, |
|||
link: linker, |
|||
scope: false |
|||
}; |
|||
} |
|||
@ -1,23 +0,0 @@ |
|||
<!-- |
|||
|
|||
Copyright © 2016-2020 The Thingsboard Authors |
|||
|
|||
Licensed under the Apache License, Version 2.0 (the "License"); |
|||
you may not use this file except in compliance with the License. |
|||
You may obtain a copy of the License at |
|||
|
|||
http://www.apache.org/licenses/LICENSE-2.0 |
|||
|
|||
Unless required by applicable law or agreed to in writing, software |
|||
distributed under the License is distributed on an "AS IS" BASIS, |
|||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
See the License for the specific language governing permissions and |
|||
limitations under the License. |
|||
|
|||
--> |
|||
<div translate class="tb-cell" flex="30">alarm.created-time</div> |
|||
<div translate class="tb-cell" flex="15">alarm.originator</div> |
|||
<div translate class="tb-cell" flex="20">alarm.type</div> |
|||
<div translate class="tb-cell" flex="15">alarm.severity</div> |
|||
<div translate class="tb-cell" flex="20">alarm.status</div> |
|||
<div translate class="tb-cell" flex="15">alarm.details</div> |
|||
@ -1,73 +0,0 @@ |
|||
/* |
|||
* Copyright © 2016-2020 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
/* eslint-disable import/no-unresolved, import/default */ |
|||
|
|||
import alarmDetailsDialogTemplate from './alarm-details-dialog.tpl.html'; |
|||
|
|||
import alarmRowTemplate from './alarm-row.tpl.html'; |
|||
|
|||
/* eslint-enable import/no-unresolved, import/default */ |
|||
|
|||
/*@ngInject*/ |
|||
export default function AlarmRowDirective($compile, $templateCache, types, $mdDialog, $document) { |
|||
|
|||
var linker = function (scope, element, attrs) { |
|||
|
|||
var template = $templateCache.get(alarmRowTemplate); |
|||
element.html(template); |
|||
|
|||
scope.alarm = attrs.alarm; |
|||
scope.types = types; |
|||
|
|||
scope.showAlarmDetails = function($event) { |
|||
var onShowingCallback = { |
|||
onShowing: function(){} |
|||
} |
|||
$mdDialog.show({ |
|||
controller: 'AlarmDetailsDialogController', |
|||
controllerAs: 'vm', |
|||
templateUrl: alarmDetailsDialogTemplate, |
|||
locals: { |
|||
alarmId: scope.alarm.id.id, |
|||
allowAcknowledgment: true, |
|||
allowClear: true, |
|||
displayDetails: true, |
|||
showingCallback: onShowingCallback |
|||
}, |
|||
parent: angular.element($document[0].body), |
|||
targetEvent: $event, |
|||
fullscreen: true, |
|||
multiple: true, |
|||
onShowing: function(scope, element) { |
|||
onShowingCallback.onShowing(scope, element); |
|||
} |
|||
}).then(function (alarm) { |
|||
if (alarm) { |
|||
scope.alarm = alarm; |
|||
} |
|||
}); |
|||
} |
|||
|
|||
$compile(element.contents())(scope); |
|||
} |
|||
|
|||
return { |
|||
restrict: "A", |
|||
replace: false, |
|||
link: linker, |
|||
scope: false |
|||
}; |
|||
} |
|||
@ -1,37 +0,0 @@ |
|||
<!-- |
|||
|
|||
Copyright © 2016-2020 The Thingsboard Authors |
|||
|
|||
Licensed under the Apache License, Version 2.0 (the "License"); |
|||
you may not use this file except in compliance with the License. |
|||
You may obtain a copy of the License at |
|||
|
|||
http://www.apache.org/licenses/LICENSE-2.0 |
|||
|
|||
Unless required by applicable law or agreed to in writing, software |
|||
distributed under the License is distributed on an "AS IS" BASIS, |
|||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
See the License for the specific language governing permissions and |
|||
limitations under the License. |
|||
|
|||
--> |
|||
<div class="tb-cell" flex="30">{{alarm.createdTime | date : 'yyyy-MM-dd HH:mm:ss'}}</div> |
|||
<div class="tb-cell" flex="15">{{alarm.originatorName}}</div> |
|||
<div class="tb-cell" flex="20">{{alarm.type}}</div> |
|||
<div class="tb-cell tb-severity" flex="15" ng-class="types.alarmSeverity[alarm.severity].class"> |
|||
{{ alarm ? (types.alarmSeverity[alarm.severity].name | translate) : '' }} |
|||
</div> |
|||
<div class="tb-cell" flex="20">{{ alarm ? (('alarm.display-status.' + alarm.status) | translate) : '' }}</div> |
|||
<div class="tb-cell" flex="15"> |
|||
<md-button class="md-icon-button md-primary" |
|||
ng-click="showAlarmDetails($event)" |
|||
aria-label="{{ 'action.view' | translate }}"> |
|||
<md-tooltip md-direction="top"> |
|||
{{ 'alarm.details' | translate }} |
|||
</md-tooltip> |
|||
<md-icon aria-label="{{ 'action.view' | translate }}" |
|||
class="material-icons"> |
|||
more_horiz |
|||
</md-icon> |
|||
</md-button> |
|||
</div> |
|||
@ -1,213 +0,0 @@ |
|||
/* |
|||
* Copyright © 2016-2020 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
import './alarm.scss'; |
|||
|
|||
/* eslint-disable import/no-unresolved, import/default */ |
|||
|
|||
import alarmTableTemplate from './alarm-table.tpl.html'; |
|||
|
|||
/* eslint-enable import/no-unresolved, import/default */ |
|||
|
|||
/*@ngInject*/ |
|||
export default function AlarmTableDirective($compile, $templateCache, $rootScope, types, alarmService) { |
|||
|
|||
var linker = function (scope, element) { |
|||
|
|||
var template = $templateCache.get(alarmTableTemplate); |
|||
|
|||
element.html(template); |
|||
|
|||
scope.types = types; |
|||
|
|||
scope.alarmSearchStatus = types.alarmSearchStatus.any; |
|||
|
|||
var pageSize = 20; |
|||
var startTime = 0; |
|||
var endTime = 0; |
|||
|
|||
scope.timewindow = { |
|||
history: { |
|||
timewindowMs: 24 * 60 * 60 * 1000 // 1 day
|
|||
} |
|||
}; |
|||
|
|||
scope.topIndex = 0; |
|||
|
|||
scope.theAlarms = { |
|||
getItemAtIndex: function (index) { |
|||
if (index > scope.alarms.data.length) { |
|||
scope.theAlarms.fetchMoreItems_(index); |
|||
return null; |
|||
} |
|||
var item = scope.alarms.data[index]; |
|||
if (item) { |
|||
item.indexNumber = index + 1; |
|||
} |
|||
return item; |
|||
}, |
|||
|
|||
getLength: function () { |
|||
if (scope.alarms.hasNext) { |
|||
return scope.alarms.data.length + scope.alarms.nextPageLink.limit; |
|||
} else { |
|||
return scope.alarms.data.length; |
|||
} |
|||
}, |
|||
|
|||
fetchMoreItems_: function () { |
|||
if (scope.alarms.hasNext && !scope.alarms.pending) { |
|||
if (scope.entityType && scope.entityId && scope.alarmSearchStatus) { |
|||
var promise = alarmService.getAlarms(scope.entityType, scope.entityId, |
|||
scope.alarms.nextPageLink, scope.alarmSearchStatus, null, true, false); |
|||
if (promise) { |
|||
scope.alarms.pending = true; |
|||
promise.then( |
|||
function success(alarms) { |
|||
scope.alarms.data = scope.alarms.data.concat(alarms.data); |
|||
scope.alarms.nextPageLink = alarms.nextPageLink; |
|||
scope.alarms.hasNext = alarms.hasNext; |
|||
if (scope.alarms.hasNext) { |
|||
scope.alarms.nextPageLink.limit = pageSize; |
|||
} |
|||
scope.alarms.pending = false; |
|||
}, |
|||
function fail() { |
|||
scope.alarms.hasNext = false; |
|||
scope.alarms.pending = false; |
|||
}); |
|||
} else { |
|||
scope.alarms.hasNext = false; |
|||
} |
|||
} else { |
|||
scope.alarms.hasNext = false; |
|||
} |
|||
} |
|||
} |
|||
}; |
|||
|
|||
scope.reload = reload; |
|||
|
|||
scope.$watch("entityId", function(newVal, prevVal) { |
|||
if (newVal && !angular.equals(newVal, prevVal)) { |
|||
resetFilter(); |
|||
reload(); |
|||
} |
|||
}); |
|||
|
|||
|
|||
|
|||
function destroyWatchers() { |
|||
if (scope.alarmSearchStatusWatchHandle) { |
|||
scope.alarmSearchStatusWatchHandle(); |
|||
scope.alarmSearchStatusWatchHandle = null; |
|||
} |
|||
if (scope.timewindowWatchHandle) { |
|||
scope.timewindowWatchHandle(); |
|||
scope.timewindowWatchHandle = null; |
|||
} |
|||
} |
|||
|
|||
function initWatchers() { |
|||
scope.alarmSearchStatusWatchHandle = scope.$watch("alarmSearchStatus", function(newVal, prevVal) { |
|||
if (newVal && !angular.equals(newVal, prevVal)) { |
|||
reload(); |
|||
} |
|||
}); |
|||
scope.timewindowWatchHandle = scope.$watch("timewindow", function(newVal, prevVal) { |
|||
if (newVal && !angular.equals(newVal, prevVal)) { |
|||
reload(); |
|||
} |
|||
}, true); |
|||
} |
|||
|
|||
function resetFilter() { |
|||
destroyWatchers(); |
|||
scope.timewindow = { |
|||
history: { |
|||
timewindowMs: 24 * 60 * 60 * 1000 // 1 day
|
|||
} |
|||
}; |
|||
scope.alarmSearchStatus = types.alarmSearchStatus.any; |
|||
initWatchers(); |
|||
} |
|||
|
|||
function updateTimeWindowRange () { |
|||
if (scope.timewindow.history.timewindowMs) { |
|||
var currentTime = (new Date).getTime(); |
|||
startTime = currentTime - scope.timewindow.history.timewindowMs; |
|||
endTime = currentTime; |
|||
} else { |
|||
startTime = scope.timewindow.history.fixedTimewindow.startTimeMs; |
|||
endTime = scope.timewindow.history.fixedTimewindow.endTimeMs; |
|||
} |
|||
} |
|||
|
|||
function reload () { |
|||
scope.topIndex = 0; |
|||
scope.selected = []; |
|||
updateTimeWindowRange(); |
|||
scope.alarms = { |
|||
data: [], |
|||
nextPageLink: { |
|||
limit: pageSize, |
|||
startTime: startTime, |
|||
endTime: endTime |
|||
}, |
|||
hasNext: true, |
|||
pending: false |
|||
}; |
|||
scope.theAlarms.getItemAtIndex(pageSize); |
|||
} |
|||
|
|||
scope.noData = function() { |
|||
return scope.alarms.data.length == 0 && !scope.alarms.hasNext; |
|||
} |
|||
|
|||
scope.hasData = function() { |
|||
return scope.alarms.data.length > 0; |
|||
} |
|||
|
|||
scope.loading = function() { |
|||
return $rootScope.loading; |
|||
} |
|||
|
|||
scope.hasScroll = function() { |
|||
var repeatContainer = scope.repeatContainer[0]; |
|||
if (repeatContainer) { |
|||
var scrollElement = repeatContainer.children[0]; |
|||
if (scrollElement) { |
|||
return scrollElement.scrollHeight > scrollElement.clientHeight; |
|||
} |
|||
} |
|||
return false; |
|||
} |
|||
|
|||
reload(); |
|||
|
|||
initWatchers(); |
|||
|
|||
$compile(element.contents())(scope); |
|||
} |
|||
|
|||
return { |
|||
restrict: "E", |
|||
link: linker, |
|||
scope: { |
|||
entityType: '=', |
|||
entityId: '=' |
|||
} |
|||
}; |
|||
} |
|||
@ -1,56 +0,0 @@ |
|||
<!-- |
|||
|
|||
Copyright © 2016-2020 The Thingsboard Authors |
|||
|
|||
Licensed under the Apache License, Version 2.0 (the "License"); |
|||
you may not use this file except in compliance with the License. |
|||
You may obtain a copy of the License at |
|||
|
|||
http://www.apache.org/licenses/LICENSE-2.0 |
|||
|
|||
Unless required by applicable law or agreed to in writing, software |
|||
distributed under the License is distributed on an "AS IS" BASIS, |
|||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
See the License for the specific language governing permissions and |
|||
limitations under the License. |
|||
|
|||
--> |
|||
<md-content flex class="md-padding tb-absolute-fill" layout="column"> |
|||
<section layout="row"> |
|||
<md-input-container class="md-block" style="width: 200px;"> |
|||
<label translate>alarm.alarm-status</label> |
|||
<md-select ng-model="alarmSearchStatus" ng-disabled="$root.loading"> |
|||
<md-option ng-repeat="searchStatus in types.alarmSearchStatus" ng-value="searchStatus"> |
|||
{{ ('alarm.search-status.' + searchStatus) | translate }} |
|||
</md-option> |
|||
</md-select> |
|||
</md-input-container> |
|||
<tb-timewindow flex ng-model="timewindow" history-only as-button="true"></tb-timewindow> |
|||
<md-button ng-disabled="$root.loading" |
|||
class="md-icon-button" ng-click="reload()"> |
|||
<md-icon>refresh</md-icon> |
|||
<md-tooltip md-direction="top"> |
|||
{{ 'action.refresh' | translate }} |
|||
</md-tooltip> |
|||
</md-button> |
|||
</section> |
|||
<div flex layout="column" class="tb-alarm-container md-whiteframe-z1"> |
|||
<md-list flex layout="column" class="tb-alarm-table"> |
|||
<md-list class="tb-row tb-header" layout="row" layout-align="start center" tb-alarm-header> |
|||
</md-list> |
|||
<md-progress-linear style="max-height: 0px;" md-mode="indeterminate" ng-disabled="!$root.loading" |
|||
ng-show="$root.loading"></md-progress-linear> |
|||
<md-divider></md-divider> |
|||
<span translate layout-align="center center" |
|||
style="margin-top: 25px;" |
|||
class="tb-prompt" ng-show="noData()">alarm.no-alarms-prompt</span> |
|||
<md-virtual-repeat-container ng-show="hasData()" flex md-top-index="topIndex" tb-scope-element="repeatContainer"> |
|||
<md-list-item md-virtual-repeat="alarm in theAlarms" md-on-demand flex ng-style="hasScroll() ? {'margin-right':'-15px'} : {}"> |
|||
<md-list class="tb-row" flex layout="row" layout-align="start center" tb-alarm-row alarm="{{alarm}}"> |
|||
</md-list> |
|||
<md-divider flex></md-divider> |
|||
</md-list-item> |
|||
</md-virtual-repeat-container> |
|||
</md-list> |
|||
</div> |
|||
</md-content> |
|||
@ -1,77 +0,0 @@ |
|||
/** |
|||
* Copyright © 2016-2020 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0 |
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
.tb-alarm-container { |
|||
overflow-x: auto; |
|||
} |
|||
|
|||
md-list.tb-alarm-table { |
|||
min-width: 700px; |
|||
padding: 0; |
|||
|
|||
md-list-item { |
|||
padding: 0; |
|||
} |
|||
|
|||
.tb-row { |
|||
height: 48px; |
|||
padding: 0; |
|||
overflow: hidden; |
|||
} |
|||
|
|||
.tb-row:hover { |
|||
background-color: #eee; |
|||
} |
|||
|
|||
.tb-header:hover { |
|||
background: none; |
|||
} |
|||
|
|||
.tb-header { |
|||
.tb-cell { |
|||
font-size: 12px; |
|||
font-weight: 700; |
|||
color: rgba(0, 0, 0, .54); |
|||
white-space: nowrap; |
|||
background: none; |
|||
} |
|||
} |
|||
|
|||
.tb-cell { |
|||
padding: 0 24px; |
|||
margin: auto 0; |
|||
overflow: hidden; |
|||
font-size: 13px; |
|||
color: rgba(0, 0, 0, .87); |
|||
text-align: left; |
|||
vertical-align: middle; |
|||
|
|||
.md-button { |
|||
padding: 0; |
|||
margin: 0; |
|||
} |
|||
} |
|||
|
|||
.tb-cell.tb-number { |
|||
text-align: right; |
|||
} |
|||
} |
|||
|
|||
#tb-alarm-content { |
|||
width: 100%; |
|||
min-width: 400px; |
|||
height: 100%; |
|||
min-height: 50px; |
|||
} |
|||
@ -1,26 +0,0 @@ |
|||
/* |
|||
* Copyright © 2016-2020 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
import AlarmDetailsDialogController from './alarm-details-dialog.controller'; |
|||
import AlarmHeaderDirective from './alarm-header.directive'; |
|||
import AlarmRowDirective from './alarm-row.directive'; |
|||
import AlarmTableDirective from './alarm-table.directive'; |
|||
|
|||
export default angular.module('thingsboard.alarm', []) |
|||
.controller('AlarmDetailsDialogController', AlarmDetailsDialogController) |
|||
.directive('tbAlarmHeader', AlarmHeaderDirective) |
|||
.directive('tbAlarmRow', AlarmRowDirective) |
|||
.directive('tbAlarmTable', AlarmTableDirective) |
|||
.name; |
|||
@ -1,99 +0,0 @@ |
|||
/* |
|||
* Copyright © 2016-2020 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
export default angular.module('thingsboard.api.admin', []) |
|||
.factory('adminService', AdminService) |
|||
.name; |
|||
|
|||
/*@ngInject*/ |
|||
function AdminService($http, $q) { |
|||
|
|||
var service = { |
|||
getAdminSettings: getAdminSettings, |
|||
saveAdminSettings: saveAdminSettings, |
|||
getSecuritySettings: getSecuritySettings, |
|||
saveSecuritySettings: saveSecuritySettings, |
|||
sendTestMail: sendTestMail, |
|||
checkUpdates: checkUpdates |
|||
} |
|||
|
|||
return service; |
|||
|
|||
function getAdminSettings(key) { |
|||
var deferred = $q.defer(); |
|||
var url = '/api/admin/settings/' + key; |
|||
$http.get(url, null).then(function success(response) { |
|||
deferred.resolve(response.data); |
|||
}, function fail() { |
|||
deferred.reject(); |
|||
}); |
|||
return deferred.promise; |
|||
} |
|||
|
|||
function saveAdminSettings(settings) { |
|||
var deferred = $q.defer(); |
|||
var url = '/api/admin/settings'; |
|||
$http.post(url, settings).then(function success(response) { |
|||
deferred.resolve(response.data); |
|||
}, function fail(response) { |
|||
deferred.reject(response.data); |
|||
}); |
|||
return deferred.promise; |
|||
} |
|||
|
|||
function getSecuritySettings() { |
|||
var deferred = $q.defer(); |
|||
var url = '/api/admin/securitySettings'; |
|||
$http.get(url, null).then(function success(response) { |
|||
deferred.resolve(response.data); |
|||
}, function fail() { |
|||
deferred.reject(); |
|||
}); |
|||
return deferred.promise; |
|||
} |
|||
|
|||
function saveSecuritySettings(securitySettings) { |
|||
var deferred = $q.defer(); |
|||
var url = '/api/admin/securitySettings'; |
|||
$http.post(url, securitySettings).then(function success(response) { |
|||
deferred.resolve(response.data); |
|||
}, function fail(response) { |
|||
deferred.reject(response.data); |
|||
}); |
|||
return deferred.promise; |
|||
} |
|||
|
|||
function sendTestMail(settings) { |
|||
var deferred = $q.defer(); |
|||
var url = '/api/admin/settings/testMail'; |
|||
$http.post(url, settings).then(function success() { |
|||
deferred.resolve(); |
|||
}, function fail(response) { |
|||
deferred.reject(response.data); |
|||
}); |
|||
return deferred.promise; |
|||
} |
|||
|
|||
function checkUpdates() { |
|||
var deferred = $q.defer(); |
|||
var url = '/api/admin/updates'; |
|||
$http.get(url, null).then(function success(response) { |
|||
deferred.resolve(response.data); |
|||
}, function fail() { |
|||
deferred.reject(); |
|||
}); |
|||
return deferred.promise; |
|||
} |
|||
} |
|||
@ -1,356 +0,0 @@ |
|||
/* |
|||
* Copyright © 2016-2020 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
export default angular.module('thingsboard.api.alarm', []) |
|||
.factory('alarmService', AlarmService) |
|||
.name; |
|||
|
|||
/*@ngInject*/ |
|||
function AlarmService($http, $q, $interval, $filter, $timeout, utils, types) { |
|||
|
|||
var alarmSourceListeners = {}; |
|||
|
|||
var simulatedAlarm = { |
|||
createdTime: (new Date).getTime(), |
|||
startTs: (new Date).getTime(), |
|||
endTs: 0, |
|||
ackTs: 0, |
|||
clearTs: 0, |
|||
originatorName: 'Simulated', |
|||
originator: { |
|||
entityType: "DEVICE", |
|||
id: "1" |
|||
}, |
|||
type: 'TEMPERATURE', |
|||
severity: "MAJOR", |
|||
status: types.alarmStatus.activeUnack, |
|||
details: { |
|||
message: "Temperature is high!" |
|||
} |
|||
}; |
|||
|
|||
var service = { |
|||
getAlarm: getAlarm, |
|||
getAlarmInfo: getAlarmInfo, |
|||
saveAlarm: saveAlarm, |
|||
ackAlarm: ackAlarm, |
|||
clearAlarm: clearAlarm, |
|||
deleteAlarm: deleteAlarm, |
|||
getAlarms: getAlarms, |
|||
getHighestAlarmSeverity: getHighestAlarmSeverity, |
|||
pollAlarms: pollAlarms, |
|||
cancelPollAlarms: cancelPollAlarms, |
|||
subscribeForAlarms: subscribeForAlarms, |
|||
unsubscribeFromAlarms: unsubscribeFromAlarms |
|||
} |
|||
|
|||
return service; |
|||
|
|||
function getAlarm(alarmId, ignoreErrors, config) { |
|||
var deferred = $q.defer(); |
|||
var url = '/api/alarm/' + alarmId; |
|||
if (!config) { |
|||
config = {}; |
|||
} |
|||
config = Object.assign(config, { ignoreErrors: ignoreErrors }); |
|||
$http.get(url, config).then(function success(response) { |
|||
deferred.resolve(response.data); |
|||
}, function fail() { |
|||
deferred.reject(); |
|||
}); |
|||
return deferred.promise; |
|||
} |
|||
|
|||
function getAlarmInfo(alarmId, ignoreErrors, config) { |
|||
var deferred = $q.defer(); |
|||
var url = '/api/alarm/info/' + alarmId; |
|||
if (!config) { |
|||
config = {}; |
|||
} |
|||
config = Object.assign(config, { ignoreErrors: ignoreErrors }); |
|||
$http.get(url, config).then(function success(response) { |
|||
deferred.resolve(response.data); |
|||
}, function fail() { |
|||
deferred.reject(); |
|||
}); |
|||
return deferred.promise; |
|||
} |
|||
|
|||
function saveAlarm(alarm, ignoreErrors, config) { |
|||
var deferred = $q.defer(); |
|||
var url = '/api/alarm'; |
|||
if (!config) { |
|||
config = {}; |
|||
} |
|||
config = Object.assign(config, { ignoreErrors: ignoreErrors }); |
|||
$http.post(url, alarm, config).then(function success(response) { |
|||
deferred.resolve(response.data); |
|||
}, function fail() { |
|||
deferred.reject(); |
|||
}); |
|||
return deferred.promise; |
|||
} |
|||
|
|||
function ackAlarm(alarmId, ignoreErrors, config) { |
|||
var deferred = $q.defer(); |
|||
var url = '/api/alarm/' + alarmId + '/ack'; |
|||
if (!config) { |
|||
config = {}; |
|||
} |
|||
config = Object.assign(config, { ignoreErrors: ignoreErrors }); |
|||
$http.post(url, null, config).then(function success(response) { |
|||
deferred.resolve(response.data); |
|||
}, function fail() { |
|||
deferred.reject(); |
|||
}); |
|||
return deferred.promise; |
|||
} |
|||
|
|||
function clearAlarm(alarmId, ignoreErrors, config) { |
|||
var deferred = $q.defer(); |
|||
var url = '/api/alarm/' + alarmId + '/clear'; |
|||
if (!config) { |
|||
config = {}; |
|||
} |
|||
config = Object.assign(config, { ignoreErrors: ignoreErrors }); |
|||
$http.post(url, null, config).then(function success(response) { |
|||
deferred.resolve(response.data); |
|||
}, function fail() { |
|||
deferred.reject(); |
|||
}); |
|||
return deferred.promise; |
|||
} |
|||
|
|||
function deleteAlarm(alarmId, ignoreErrors, config) { |
|||
var deferred = $q.defer(); |
|||
var url = '/api/alarm/' + alarmId; |
|||
if (!config) { |
|||
config = {}; |
|||
} |
|||
config = Object.assign(config, { ignoreErrors: ignoreErrors }); |
|||
$http.delete(url, config).then(function success(response) { |
|||
deferred.resolve(response.data); |
|||
}, function fail() { |
|||
deferred.reject(); |
|||
}); |
|||
return deferred.promise; |
|||
} |
|||
|
|||
function getAlarms(entityType, entityId, pageLink, alarmSearchStatus, alarmStatus, fetchOriginator, ascOrder, config) { |
|||
var deferred = $q.defer(); |
|||
var url = '/api/alarm/' + entityType + '/' + entityId + '?limit=' + pageLink.limit; |
|||
|
|||
if (angular.isDefined(pageLink.startTime) && pageLink.startTime != null) { |
|||
url += '&startTime=' + pageLink.startTime; |
|||
} |
|||
if (angular.isDefined(pageLink.endTime) && pageLink.endTime != null) { |
|||
url += '&endTime=' + pageLink.endTime; |
|||
} |
|||
if (angular.isDefined(pageLink.idOffset) && pageLink.idOffset != null) { |
|||
url += '&offset=' + pageLink.idOffset; |
|||
} |
|||
if (alarmSearchStatus) { |
|||
url += '&searchStatus=' + alarmSearchStatus; |
|||
} |
|||
if (alarmStatus) { |
|||
url += '&status=' + alarmStatus; |
|||
} |
|||
if (fetchOriginator) { |
|||
url += '&fetchOriginator=' + ((fetchOriginator===true) ? 'true' : 'false'); |
|||
} |
|||
if (angular.isDefined(ascOrder) && ascOrder != null) { |
|||
url += '&ascOrder=' + (ascOrder ? 'true' : 'false'); |
|||
} |
|||
|
|||
$http.get(url, config).then(function success(response) { |
|||
deferred.resolve(response.data); |
|||
}, function fail() { |
|||
deferred.reject(); |
|||
}); |
|||
return deferred.promise; |
|||
} |
|||
|
|||
function getHighestAlarmSeverity(entityType, entityId, alarmSearchStatus, alarmStatus, config) { |
|||
var deferred = $q.defer(); |
|||
var url = '/api/alarm/highestSeverity/' + entityType + '/' + entityId; |
|||
|
|||
if (alarmSearchStatus) { |
|||
url += '?searchStatus=' + alarmSearchStatus; |
|||
} else if (alarmStatus) { |
|||
url += '?status=' + alarmStatus; |
|||
} |
|||
$http.get(url, config).then(function success(response) { |
|||
deferred.resolve(response.data); |
|||
}, function fail() { |
|||
deferred.reject(); |
|||
}); |
|||
return deferred.promise; |
|||
} |
|||
|
|||
function fetchAlarms(alarmsQuery, pageLink, deferred, leftToLoad, alarmsList) { |
|||
getAlarms(alarmsQuery.entityType, alarmsQuery.entityId, |
|||
pageLink, alarmsQuery.alarmSearchStatus, alarmsQuery.alarmStatus, |
|||
alarmsQuery.fetchOriginator, false, {ignoreLoading: true}).then( |
|||
function success(alarms) { |
|||
if (!alarmsList) { |
|||
alarmsList = []; |
|||
} |
|||
alarmsList = alarmsList.concat(alarms.data); |
|||
if (angular.isDefined(leftToLoad)) { |
|||
leftToLoad -= pageLink.limit; |
|||
if (leftToLoad === 0) { |
|||
alarmsList = $filter('orderBy')(alarmsList, ['-createdTime']); |
|||
deferred.resolve(alarmsList); |
|||
return; |
|||
} |
|||
if (leftToLoad < pageLink.limit) { |
|||
alarms.nextPageLink.limit = leftToLoad; |
|||
} |
|||
} |
|||
if (alarms.hasNext && !alarmsQuery.limit) { |
|||
fetchAlarms(alarmsQuery, alarms.nextPageLink, deferred, leftToLoad, alarmsList); |
|||
} else { |
|||
alarmsList = $filter('orderBy')(alarmsList, ['-createdTime']); |
|||
deferred.resolve(alarmsList); |
|||
} |
|||
}, |
|||
function fail() { |
|||
deferred.reject(); |
|||
} |
|||
); |
|||
} |
|||
|
|||
function getAlarmsByQuery(alarmsQuery) { |
|||
var deferred = $q.defer(); |
|||
var time = Date.now(); |
|||
var pageLink; |
|||
var leftToLoad; |
|||
if (alarmsQuery.limit) { |
|||
pageLink = { |
|||
limit: alarmsQuery.limit |
|||
}; |
|||
} else if (alarmsQuery.interval) { |
|||
pageLink = { |
|||
limit: alarmsQuery.alarmsFetchSize || 100, |
|||
startTime: time - alarmsQuery.interval |
|||
}; |
|||
} else if (alarmsQuery.startTime) { |
|||
pageLink = { |
|||
limit: alarmsQuery.alarmsFetchSize || 100, |
|||
startTime: Math.round(alarmsQuery.startTime) |
|||
}; |
|||
if (alarmsQuery.endTime) { |
|||
pageLink.endTime = Math.round(alarmsQuery.endTime); |
|||
} |
|||
} |
|||
|
|||
if (angular.isDefined(alarmsQuery.alarmsMaxCountLoad) && alarmsQuery.alarmsMaxCountLoad !== 0) { |
|||
leftToLoad = alarmsQuery.alarmsMaxCountLoad; |
|||
if (leftToLoad < pageLink.limit) { |
|||
pageLink.limit = leftToLoad; |
|||
} |
|||
} |
|||
|
|||
fetchAlarms(alarmsQuery, pageLink, deferred, leftToLoad); |
|||
return deferred.promise; |
|||
} |
|||
|
|||
function onPollAlarms(alarmsQuery) { |
|||
getAlarmsByQuery(alarmsQuery).then( |
|||
function success(alarms) { |
|||
alarmsQuery.onAlarms(alarms); |
|||
}, |
|||
function fail() {} |
|||
); |
|||
} |
|||
|
|||
function pollAlarms(entityType, entityId, alarmStatus, interval, limit, pollingInterval, onAlarms) { |
|||
var alarmsQuery = { |
|||
entityType: entityType, |
|||
entityId: entityId, |
|||
alarmSearchStatus: null, |
|||
alarmStatus: alarmStatus, |
|||
fetchOriginator: false, |
|||
interval: interval, |
|||
limit: limit, |
|||
onAlarms: onAlarms |
|||
}; |
|||
onPollAlarms(alarmsQuery); |
|||
return $interval(onPollAlarms, pollingInterval, 0, false, alarmsQuery); |
|||
} |
|||
|
|||
function cancelPollAlarms(pollPromise) { |
|||
if (angular.isDefined(pollPromise)) { |
|||
$interval.cancel(pollPromise); |
|||
} |
|||
} |
|||
|
|||
function subscribeForAlarms(alarmSourceListener) { |
|||
alarmSourceListener.id = utils.guid(); |
|||
alarmSourceListeners[alarmSourceListener.id] = alarmSourceListener; |
|||
var alarmSource = alarmSourceListener.alarmSource; |
|||
if (alarmSource.type == types.datasourceType.function) { |
|||
$timeout(function() { |
|||
alarmSourceListener.alarmsUpdated([simulatedAlarm], false); |
|||
}); |
|||
} else if (alarmSource.entityType && alarmSource.entityId) { |
|||
var pollingInterval = alarmSourceListener.alarmsPollingInterval; |
|||
alarmSourceListener.alarmsQuery = { |
|||
entityType: alarmSource.entityType, |
|||
entityId: alarmSource.entityId, |
|||
alarmSearchStatus: alarmSourceListener.alarmSearchStatus, |
|||
alarmStatus: null, |
|||
alarmsMaxCountLoad: alarmSourceListener.alarmsMaxCountLoad, |
|||
alarmsFetchSize: alarmSourceListener.alarmsFetchSize |
|||
}; |
|||
var originatorKeys = $filter('filter')(alarmSource.dataKeys, {name: 'originator'}); |
|||
if (originatorKeys && originatorKeys.length) { |
|||
alarmSourceListener.alarmsQuery.fetchOriginator = true; |
|||
} |
|||
var subscriptionTimewindow = alarmSourceListener.subscriptionTimewindow; |
|||
if (subscriptionTimewindow.realtimeWindowMs) { |
|||
alarmSourceListener.alarmsQuery.startTime = subscriptionTimewindow.startTs; |
|||
} else { |
|||
alarmSourceListener.alarmsQuery.startTime = subscriptionTimewindow.fixedWindow.startTimeMs; |
|||
alarmSourceListener.alarmsQuery.endTime = subscriptionTimewindow.fixedWindow.endTimeMs; |
|||
} |
|||
alarmSourceListener.alarmsQuery.onAlarms = function(alarms) { |
|||
if (subscriptionTimewindow.realtimeWindowMs) { |
|||
var now = Date.now(); |
|||
if (alarmSourceListener.lastUpdateTs) { |
|||
var interval = now - alarmSourceListener.lastUpdateTs; |
|||
alarmSourceListener.alarmsQuery.startTime += interval; |
|||
} |
|||
alarmSourceListener.lastUpdateTs = now; |
|||
} |
|||
alarmSourceListener.alarmsUpdated(alarms, false); |
|||
} |
|||
onPollAlarms(alarmSourceListener.alarmsQuery); |
|||
alarmSourceListener.pollPromise = $interval(onPollAlarms, pollingInterval, |
|||
0, false, alarmSourceListener.alarmsQuery); |
|||
} |
|||
|
|||
} |
|||
|
|||
function unsubscribeFromAlarms(alarmSourceListener) { |
|||
if (alarmSourceListener && alarmSourceListener.id) { |
|||
if (alarmSourceListener.pollPromise) { |
|||
$interval.cancel(alarmSourceListener.pollPromise); |
|||
alarmSourceListener.pollPromise = null; |
|||
} |
|||
delete alarmSourceListeners[alarmSourceListener.id]; |
|||
} |
|||
} |
|||
} |
|||
@ -1,322 +0,0 @@ |
|||
/* |
|||
* Copyright © 2016-2020 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
export default class AliasController { |
|||
|
|||
constructor($scope, $q, $filter, utils, types, entityService, stateController, entityAliases) { |
|||
this.$scope = $scope; |
|||
this.$q = $q; |
|||
this.$filter = $filter; |
|||
this.utils = utils; |
|||
this.types = types; |
|||
this.entityService = entityService; |
|||
this.stateController = stateController; |
|||
this.entityAliases = angular.copy(entityAliases); |
|||
this.resolvedAliases = {}; |
|||
this.resolvedAliasesPromise = {}; |
|||
this.resolvedAliasesToStateEntities = {}; |
|||
} |
|||
|
|||
updateEntityAliases(newEntityAliases) { |
|||
var changedAliasIds = []; |
|||
for (var aliasId in newEntityAliases) { |
|||
var newEntityAlias = newEntityAliases[aliasId]; |
|||
var prevEntityAlias = this.entityAliases[aliasId]; |
|||
if (!angular.equals(newEntityAlias, prevEntityAlias)) { |
|||
changedAliasIds.push(aliasId); |
|||
this.setAliasUnresolved(aliasId); |
|||
} |
|||
} |
|||
for (aliasId in this.entityAliases) { |
|||
if (!newEntityAliases[aliasId]) { |
|||
changedAliasIds.push(aliasId); |
|||
this.setAliasUnresolved(aliasId); |
|||
} |
|||
} |
|||
this.entityAliases = angular.copy(newEntityAliases); |
|||
if (changedAliasIds.length) { |
|||
this.$scope.$broadcast('entityAliasesChanged', changedAliasIds); |
|||
} |
|||
} |
|||
|
|||
dashboardStateChanged() { |
|||
var changedAliasIds = []; |
|||
for (var aliasId in this.resolvedAliasesToStateEntities) { |
|||
var stateEntityInfo = this.resolvedAliasesToStateEntities[aliasId]; |
|||
var newEntityId = this.stateController.getEntityId(stateEntityInfo.entityParamName); |
|||
var prevEntityId = stateEntityInfo.entityId; |
|||
if (!angular.equals(newEntityId, prevEntityId)) { |
|||
changedAliasIds.push(aliasId); |
|||
this.setAliasUnresolved(aliasId); |
|||
} |
|||
} |
|||
if (changedAliasIds.length) { |
|||
this.$scope.$broadcast('entityAliasesChanged', changedAliasIds); |
|||
} |
|||
} |
|||
|
|||
setAliasUnresolved(aliasId) { |
|||
delete this.resolvedAliases[aliasId]; |
|||
delete this.resolvedAliasesPromise[aliasId]; |
|||
delete this.resolvedAliasesToStateEntities[aliasId]; |
|||
} |
|||
|
|||
getEntityAliases() { |
|||
return this.entityAliases; |
|||
} |
|||
|
|||
getEntityAliasId(aliasName) { |
|||
for (var aliasId in this.entityAliases) { |
|||
var alias = this.entityAliases[aliasId]; |
|||
if (alias.alias == aliasName) { |
|||
return aliasId; |
|||
} |
|||
} |
|||
return null; |
|||
} |
|||
|
|||
getAliasInfo(aliasId) { |
|||
var deferred = this.$q.defer(); |
|||
var aliasInfo = this.resolvedAliases[aliasId]; |
|||
if (aliasInfo) { |
|||
deferred.resolve(aliasInfo); |
|||
return deferred.promise; |
|||
} else if (this.resolvedAliasesPromise[aliasId]) { |
|||
return this.resolvedAliasesPromise[aliasId]; |
|||
} else { |
|||
this.resolvedAliasesPromise[aliasId] = deferred.promise; |
|||
var aliasCtrl = this; |
|||
var entityAlias = this.entityAliases[aliasId]; |
|||
if (entityAlias) { |
|||
this.entityService.resolveAlias(entityAlias, this.stateController.getStateParams()).then( |
|||
function success(aliasInfo) { |
|||
aliasCtrl.resolvedAliases[aliasId] = aliasInfo; |
|||
delete aliasCtrl.resolvedAliasesPromise[aliasId]; |
|||
if (aliasInfo.stateEntity) { |
|||
var stateEntityInfo = { |
|||
entityParamName: aliasInfo.entityParamName, |
|||
entityId: aliasCtrl.stateController.getEntityId(aliasInfo.entityParamName) |
|||
}; |
|||
aliasCtrl.resolvedAliasesToStateEntities[aliasId] = |
|||
stateEntityInfo; |
|||
} |
|||
aliasCtrl.$scope.$broadcast('entityAliasResolved', aliasId); |
|||
deferred.resolve(aliasInfo); |
|||
}, |
|||
function fail() { |
|||
deferred.reject(); |
|||
delete aliasCtrl.resolvedAliasesPromise[aliasId]; |
|||
} |
|||
); |
|||
} else { |
|||
deferred.reject(); |
|||
delete aliasCtrl.resolvedAliasesPromise[aliasId]; |
|||
} |
|||
return this.resolvedAliasesPromise[aliasId]; |
|||
} |
|||
} |
|||
|
|||
resolveDatasource(datasource, isSingle) { |
|||
var deferred = this.$q.defer(); |
|||
if (datasource.type === this.types.datasourceType.entity) { |
|||
if (datasource.entityAliasId) { |
|||
this.getAliasInfo(datasource.entityAliasId).then( |
|||
function success(aliasInfo) { |
|||
datasource.aliasName = aliasInfo.alias; |
|||
if (aliasInfo.resolveMultiple && !isSingle) { |
|||
var newDatasource; |
|||
var resolvedEntities = aliasInfo.resolvedEntities; |
|||
if (resolvedEntities && resolvedEntities.length) { |
|||
var datasources = []; |
|||
for (var i=0;i<resolvedEntities.length;i++) { |
|||
var resolvedEntity = resolvedEntities[i]; |
|||
newDatasource = angular.copy(datasource); |
|||
if (resolvedEntity.origEntity) { |
|||
newDatasource.entity = angular.copy(resolvedEntity.origEntity); |
|||
} else { |
|||
newDatasource.entity = {}; |
|||
} |
|||
newDatasource.entityId = resolvedEntity.id; |
|||
newDatasource.entityType = resolvedEntity.entityType; |
|||
newDatasource.entityName = resolvedEntity.name; |
|||
newDatasource.entityLabel = resolvedEntity.label; |
|||
newDatasource.entityDescription = resolvedEntity.entityDescription; |
|||
newDatasource.name = resolvedEntity.name; |
|||
newDatasource.generated = i > 0 ? true : false; |
|||
datasources.push(newDatasource); |
|||
} |
|||
deferred.resolve(datasources); |
|||
} else { |
|||
if (aliasInfo.stateEntity) { |
|||
newDatasource = angular.copy(datasource); |
|||
newDatasource.unresolvedStateEntity = true; |
|||
deferred.resolve([newDatasource]); |
|||
} else { |
|||
deferred.reject(); |
|||
} |
|||
} |
|||
} else { |
|||
var entity = aliasInfo.currentEntity; |
|||
if (entity) { |
|||
if (entity.origEntity) { |
|||
datasource.entity = angular.copy(entity.origEntity); |
|||
} else { |
|||
datasource.entity = {}; |
|||
} |
|||
datasource.entityId = entity.id; |
|||
datasource.entityType = entity.entityType; |
|||
datasource.entityName = entity.name; |
|||
datasource.entityLabel = entity.label; |
|||
datasource.name = entity.name; |
|||
datasource.entityDescription = entity.entityDescription; |
|||
deferred.resolve([datasource]); |
|||
} else { |
|||
if (aliasInfo.stateEntity) { |
|||
datasource.unresolvedStateEntity = true; |
|||
deferred.resolve([datasource]); |
|||
} else { |
|||
deferred.reject(); |
|||
} |
|||
} |
|||
} |
|||
}, |
|||
function fail() { |
|||
deferred.reject(); |
|||
} |
|||
); |
|||
} else { // entityId
|
|||
datasource.aliasName = datasource.entityName; |
|||
datasource.name = datasource.entityName; |
|||
deferred.resolve([datasource]); |
|||
} |
|||
} else { // function
|
|||
deferred.resolve([datasource]); |
|||
} |
|||
return deferred.promise; |
|||
} |
|||
|
|||
resolveAlarmSource(alarmSource) { |
|||
var deferred = this.$q.defer(); |
|||
var aliasCtrl = this; |
|||
this.resolveDatasource(alarmSource, true).then( |
|||
function success(datasources) { |
|||
var datasource = datasources[0]; |
|||
if (datasource.type === aliasCtrl.types.datasourceType.function) { |
|||
var name; |
|||
if (datasource.name && datasource.name.length) { |
|||
name = datasource.name; |
|||
} else { |
|||
name = aliasCtrl.types.datasourceType.function; |
|||
} |
|||
datasource.name = name; |
|||
datasource.aliasName = name; |
|||
datasource.entityName = name; |
|||
} else if (datasource.unresolvedStateEntity) { |
|||
datasource.name = "Unresolved"; |
|||
datasource.entityName = "Unresolved"; |
|||
} |
|||
deferred.resolve(datasource); |
|||
}, |
|||
function fail() { |
|||
deferred.reject(); |
|||
} |
|||
); |
|||
return deferred.promise; |
|||
} |
|||
|
|||
resolveDatasources(datasources) { |
|||
|
|||
var aliasCtrl = this; |
|||
|
|||
function updateDataKeyLabel(dataKey, datasource) { |
|||
if (!dataKey.pattern) { |
|||
dataKey.pattern = angular.copy(dataKey.label); |
|||
} |
|||
dataKey.label = aliasCtrl.utils.createLabelFromDatasource(datasource, dataKey.pattern); |
|||
} |
|||
|
|||
function updateDatasourceKeyLabels(datasource) { |
|||
for (var dk = 0; dk < datasource.dataKeys.length; dk++) { |
|||
updateDataKeyLabel(datasource.dataKeys[dk], datasource); |
|||
} |
|||
} |
|||
|
|||
var deferred = this.$q.defer(); |
|||
var newDatasources = angular.copy(datasources); |
|||
var datasorceResolveTasks = []; |
|||
|
|||
newDatasources.forEach(function (datasource) { |
|||
var resolveDatasourceTask = aliasCtrl.resolveDatasource(datasource); |
|||
datasorceResolveTasks.push(resolveDatasourceTask); |
|||
}); |
|||
this.$q.all(datasorceResolveTasks).then( |
|||
function success(datasourcesArrays) { |
|||
var datasources = [].concat.apply([], datasourcesArrays); |
|||
datasources = aliasCtrl.$filter('orderBy')(datasources, '+generated'); |
|||
var index = 0; |
|||
var functionIndex = 0; |
|||
datasources.forEach(function(datasource) { |
|||
if (datasource.type === aliasCtrl.types.datasourceType.function) { |
|||
var name; |
|||
if (datasource.name && datasource.name.length) { |
|||
name = datasource.name; |
|||
} else { |
|||
functionIndex++; |
|||
name = aliasCtrl.types.datasourceType.function; |
|||
if (functionIndex > 1) { |
|||
name += ' ' + functionIndex; |
|||
} |
|||
} |
|||
datasource.name = name; |
|||
datasource.aliasName = name; |
|||
datasource.entityName = name; |
|||
} else if (datasource.unresolvedStateEntity) { |
|||
datasource.name = "Unresolved"; |
|||
datasource.entityName = "Unresolved"; |
|||
} |
|||
datasource.dataKeys.forEach(function(dataKey) { |
|||
if (datasource.generated) { |
|||
dataKey._hash = Math.random(); |
|||
dataKey.color = aliasCtrl.utils.getMaterialColor(index); |
|||
} |
|||
index++; |
|||
}); |
|||
updateDatasourceKeyLabels(datasource); |
|||
}); |
|||
deferred.resolve(datasources); |
|||
}, |
|||
function fail() { |
|||
deferred.reject(); |
|||
} |
|||
); |
|||
return deferred.promise; |
|||
} |
|||
|
|||
getInstantAliasInfo(aliasId) { |
|||
return this.resolvedAliases[aliasId]; |
|||
} |
|||
|
|||
updateCurrentAliasEntity(aliasId, currentEntity) { |
|||
var aliasInfo = this.resolvedAliases[aliasId]; |
|||
if (aliasInfo) { |
|||
var prevCurrentEntity = aliasInfo.currentEntity; |
|||
if (!angular.equals(currentEntity, prevCurrentEntity)) { |
|||
aliasInfo.currentEntity = currentEntity; |
|||
this.$scope.$broadcast('entityAliasesChanged', [aliasId]); |
|||
} |
|||
} |
|||
} |
|||
|
|||
} |
|||
@ -1,292 +0,0 @@ |
|||
/* |
|||
* Copyright © 2016-2020 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
export default angular.module('thingsboard.api.asset', []) |
|||
.factory('assetService', AssetService) |
|||
.name; |
|||
|
|||
/*@ngInject*/ |
|||
function AssetService($http, $q, customerService, userService) { |
|||
|
|||
var service = { |
|||
getAsset: getAsset, |
|||
getAssets: getAssets, |
|||
saveAsset: saveAsset, |
|||
deleteAsset: deleteAsset, |
|||
assignAssetToCustomer: assignAssetToCustomer, |
|||
unassignAssetFromCustomer: unassignAssetFromCustomer, |
|||
makeAssetPublic: makeAssetPublic, |
|||
getTenantAssets: getTenantAssets, |
|||
getCustomerAssets: getCustomerAssets, |
|||
findByQuery: findByQuery, |
|||
fetchAssetsByNameFilter: fetchAssetsByNameFilter, |
|||
getAssetTypes: getAssetTypes, |
|||
findByName: findByName |
|||
} |
|||
|
|||
return service; |
|||
|
|||
function getAsset(assetId, ignoreErrors, config) { |
|||
var deferred = $q.defer(); |
|||
var url = '/api/asset/' + assetId; |
|||
if (!config) { |
|||
config = {}; |
|||
} |
|||
config = Object.assign(config, { ignoreErrors: ignoreErrors }); |
|||
$http.get(url, config).then(function success(response) { |
|||
deferred.resolve(response.data); |
|||
}, function fail() { |
|||
deferred.reject(); |
|||
}); |
|||
return deferred.promise; |
|||
} |
|||
|
|||
function getAssets(assetIds, config) { |
|||
var deferred = $q.defer(); |
|||
var ids = ''; |
|||
for (var i=0;i<assetIds.length;i++) { |
|||
if (i>0) { |
|||
ids += ','; |
|||
} |
|||
ids += assetIds[i]; |
|||
} |
|||
var url = '/api/assets?assetIds=' + ids; |
|||
$http.get(url, config).then(function success(response) { |
|||
var assets = response.data; |
|||
assets.sort(function (asset1, asset2) { |
|||
var id1 = asset1.id.id; |
|||
var id2 = asset2.id.id; |
|||
var index1 = assetIds.indexOf(id1); |
|||
var index2 = assetIds.indexOf(id2); |
|||
return index1 - index2; |
|||
}); |
|||
deferred.resolve(assets); |
|||
}, function fail() { |
|||
deferred.reject(); |
|||
}); |
|||
return deferred.promise; |
|||
} |
|||
|
|||
function saveAsset(asset, ignoreErrors, config) { |
|||
var deferred = $q.defer(); |
|||
var url = '/api/asset'; |
|||
if (!config) { |
|||
config = {}; |
|||
} |
|||
config = Object.assign(config, { ignoreErrors: ignoreErrors }); |
|||
$http.post(url, asset, config).then(function success(response) { |
|||
deferred.resolve(response.data); |
|||
}, function fail() { |
|||
deferred.reject(); |
|||
}); |
|||
return deferred.promise; |
|||
} |
|||
|
|||
function deleteAsset(assetId, ignoreErrors, config) { |
|||
var deferred = $q.defer(); |
|||
var url = '/api/asset/' + assetId; |
|||
if (!config) { |
|||
config = {}; |
|||
} |
|||
config = Object.assign(config, { ignoreErrors: ignoreErrors }); |
|||
$http.delete(url, config).then(function success() { |
|||
deferred.resolve(); |
|||
}, function fail() { |
|||
deferred.reject(); |
|||
}); |
|||
return deferred.promise; |
|||
} |
|||
|
|||
function assignAssetToCustomer(customerId, assetId, ignoreErrors, config) { |
|||
var deferred = $q.defer(); |
|||
var url = '/api/customer/' + customerId + '/asset/' + assetId; |
|||
if (!config) { |
|||
config = {}; |
|||
} |
|||
config = Object.assign(config, { ignoreErrors: ignoreErrors }); |
|||
$http.post(url, null, config).then(function success(response) { |
|||
deferred.resolve(response.data); |
|||
}, function fail() { |
|||
deferred.reject(); |
|||
}); |
|||
return deferred.promise; |
|||
} |
|||
|
|||
function unassignAssetFromCustomer(assetId, ignoreErrors, config) { |
|||
var deferred = $q.defer(); |
|||
var url = '/api/customer/asset/' + assetId; |
|||
if (!config) { |
|||
config = {}; |
|||
} |
|||
config = Object.assign(config, { ignoreErrors: ignoreErrors }); |
|||
$http.delete(url, config).then(function success(response) { |
|||
deferred.resolve(response.data); |
|||
}, function fail() { |
|||
deferred.reject(); |
|||
}); |
|||
return deferred.promise; |
|||
} |
|||
|
|||
function makeAssetPublic(assetId, ignoreErrors, config) { |
|||
var deferred = $q.defer(); |
|||
var url = '/api/customer/public/asset/' + assetId; |
|||
if (!config) { |
|||
config = {}; |
|||
} |
|||
config = Object.assign(config, { ignoreErrors: ignoreErrors }); |
|||
$http.post(url, null, config).then(function success(response) { |
|||
deferred.resolve(response.data); |
|||
}, function fail() { |
|||
deferred.reject(); |
|||
}); |
|||
return deferred.promise; |
|||
} |
|||
|
|||
function getTenantAssets(pageLink, applyCustomersInfo, config, type) { |
|||
var deferred = $q.defer(); |
|||
var url = '/api/tenant/assets?limit=' + pageLink.limit; |
|||
if (angular.isDefined(pageLink.textSearch)) { |
|||
url += '&textSearch=' + pageLink.textSearch; |
|||
} |
|||
if (angular.isDefined(pageLink.idOffset)) { |
|||
url += '&idOffset=' + pageLink.idOffset; |
|||
} |
|||
if (angular.isDefined(pageLink.textOffset)) { |
|||
url += '&textOffset=' + pageLink.textOffset; |
|||
} |
|||
if (angular.isDefined(type) && type.length) { |
|||
url += '&type=' + type; |
|||
} |
|||
$http.get(url, config).then(function success(response) { |
|||
if (applyCustomersInfo) { |
|||
customerService.applyAssignedCustomersInfo(response.data.data).then( |
|||
function success(data) { |
|||
response.data.data = data; |
|||
deferred.resolve(response.data); |
|||
}, |
|||
function fail() { |
|||
deferred.reject(); |
|||
} |
|||
); |
|||
} else { |
|||
deferred.resolve(response.data); |
|||
} |
|||
}, function fail() { |
|||
deferred.reject(); |
|||
}); |
|||
return deferred.promise; |
|||
} |
|||
|
|||
function getCustomerAssets(customerId, pageLink, applyCustomersInfo, config, type) { |
|||
var deferred = $q.defer(); |
|||
var url = '/api/customer/' + customerId + '/assets?limit=' + pageLink.limit; |
|||
if (angular.isDefined(pageLink.textSearch)) { |
|||
url += '&textSearch=' + pageLink.textSearch; |
|||
} |
|||
if (angular.isDefined(pageLink.idOffset)) { |
|||
url += '&idOffset=' + pageLink.idOffset; |
|||
} |
|||
if (angular.isDefined(pageLink.textOffset)) { |
|||
url += '&textOffset=' + pageLink.textOffset; |
|||
} |
|||
if (angular.isDefined(type) && type.length) { |
|||
url += '&type=' + type; |
|||
} |
|||
$http.get(url, config).then(function success(response) { |
|||
if (applyCustomersInfo) { |
|||
customerService.applyAssignedCustomerInfo(response.data.data, customerId).then( |
|||
function success(data) { |
|||
response.data.data = data; |
|||
deferred.resolve(response.data); |
|||
}, |
|||
function fail() { |
|||
deferred.reject(); |
|||
} |
|||
); |
|||
} else { |
|||
deferred.resolve(response.data); |
|||
} |
|||
}, function fail() { |
|||
deferred.reject(); |
|||
}); |
|||
|
|||
return deferred.promise; |
|||
} |
|||
|
|||
function findByQuery(query, ignoreErrors, config) { |
|||
var deferred = $q.defer(); |
|||
var url = '/api/assets'; |
|||
if (!config) { |
|||
config = {}; |
|||
} |
|||
config = Object.assign(config, { ignoreErrors: ignoreErrors }); |
|||
$http.post(url, query, config).then(function success(response) { |
|||
deferred.resolve(response.data); |
|||
}, function fail() { |
|||
deferred.reject(); |
|||
}); |
|||
return deferred.promise; |
|||
} |
|||
|
|||
function fetchAssetsByNameFilter(assetNameFilter, limit, applyCustomersInfo, config) { |
|||
var deferred = $q.defer(); |
|||
var user = userService.getCurrentUser(); |
|||
var promise; |
|||
var pageLink = {limit: limit, textSearch: assetNameFilter}; |
|||
if (user.authority === 'CUSTOMER_USER') { |
|||
var customerId = user.customerId; |
|||
promise = getCustomerAssets(customerId, pageLink, applyCustomersInfo, config); |
|||
} else { |
|||
promise = getTenantAssets(pageLink, applyCustomersInfo, config); |
|||
} |
|||
promise.then( |
|||
function success(result) { |
|||
if (result.data && result.data.length > 0) { |
|||
deferred.resolve(result.data); |
|||
} else { |
|||
deferred.resolve(null); |
|||
} |
|||
}, |
|||
function fail() { |
|||
deferred.resolve(null); |
|||
} |
|||
); |
|||
return deferred.promise; |
|||
} |
|||
|
|||
function getAssetTypes(config) { |
|||
var deferred = $q.defer(); |
|||
var url = '/api/asset/types'; |
|||
$http.get(url, config).then(function success(response) { |
|||
deferred.resolve(response.data); |
|||
}, function fail() { |
|||
deferred.reject(); |
|||
}); |
|||
return deferred.promise; |
|||
} |
|||
|
|||
function findByName(assetName, config) { |
|||
config = config || {}; |
|||
var deferred = $q.defer(); |
|||
var url = '/api/tenant/assets?assetName=' + assetName; |
|||
$http.get(url, config).then(function success(response) { |
|||
deferred.resolve(response.data); |
|||
}, function fail() { |
|||
deferred.reject(); |
|||
}); |
|||
return deferred.promise; |
|||
} |
|||
|
|||
} |
|||
@ -1,356 +0,0 @@ |
|||
/* |
|||
* Copyright © 2016-2020 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
export default angular.module('thingsboard.api.attribute', []) |
|||
.factory('attributeService', AttributeService) |
|||
.name; |
|||
|
|||
/*@ngInject*/ |
|||
function AttributeService($http, $q, $filter, types, telemetryWebsocketService) { |
|||
|
|||
var entityAttributesSubscriptionMap = {}; |
|||
|
|||
var service = { |
|||
getEntityKeys: getEntityKeys, |
|||
getEntityTimeseriesValues: getEntityTimeseriesValues, |
|||
getEntityAttributesValues: getEntityAttributesValues, |
|||
getEntityAttributes: getEntityAttributes, |
|||
subscribeForEntityAttributes: subscribeForEntityAttributes, |
|||
unsubscribeForEntityAttributes: unsubscribeForEntityAttributes, |
|||
saveEntityAttributes: saveEntityAttributes, |
|||
deleteEntityAttributes: deleteEntityAttributes, |
|||
saveEntityTimeseries: saveEntityTimeseries, |
|||
deleteEntityTimeseries: deleteEntityTimeseries |
|||
} |
|||
|
|||
return service; |
|||
|
|||
function getEntityKeys(entityType, entityId, query, type, config) { |
|||
var deferred = $q.defer(); |
|||
var url = '/api/plugins/telemetry/' + entityType + '/' + entityId + '/keys/'; |
|||
if (type === types.dataKeyType.timeseries) { |
|||
url += 'timeseries'; |
|||
} else if (type === types.dataKeyType.attribute) { |
|||
url += 'attributes'; |
|||
} |
|||
$http.get(url, config).then(function success(response) { |
|||
var result = []; |
|||
if (response.data) { |
|||
if (query) { |
|||
var dataKeys = response.data; |
|||
var lowercaseQuery = angular.lowercase(query); |
|||
for (var i=0; i<dataKeys.length;i++) { |
|||
if (angular.lowercase(dataKeys[i]).indexOf(lowercaseQuery) === 0) { |
|||
result.push(dataKeys[i]); |
|||
} |
|||
} |
|||
} else { |
|||
result = response.data; |
|||
} |
|||
} |
|||
deferred.resolve(result); |
|||
}, function fail(response) { |
|||
deferred.reject(response.data); |
|||
}); |
|||
return deferred.promise; |
|||
} |
|||
|
|||
function getEntityTimeseriesValues(entityType, entityId, keys, startTs, endTs, limit) { |
|||
var deferred = $q.defer(); |
|||
var url = '/api/plugins/telemetry/' + entityType + '/' + entityId + '/values/timeseries'; |
|||
url += '?keys=' + keys; |
|||
url += '&startTs=' + startTs; |
|||
url += '&endTs=' + endTs; |
|||
if (angular.isDefined(limit)) { |
|||
url += '&limit=' + limit; |
|||
} |
|||
$http.get(url, null).then(function success(response) { |
|||
deferred.resolve(response.data); |
|||
}, function fail(response) { |
|||
deferred.reject(response.data); |
|||
}); |
|||
return deferred.promise; |
|||
} |
|||
|
|||
function getEntityAttributesValues(entityType, entityId, attributeScope, keys, config) { |
|||
var deferred = $q.defer(); |
|||
var url = '/api/plugins/telemetry/' + entityType + '/' + entityId + '/values/attributes/' + attributeScope; |
|||
if (keys && keys.length) { |
|||
url += '?keys=' + keys; |
|||
} |
|||
$http.get(url, config).then(function success(response) { |
|||
deferred.resolve(response.data); |
|||
}, function fail() { |
|||
deferred.reject(); |
|||
}); |
|||
return deferred.promise; |
|||
} |
|||
|
|||
function processAttributes(attributes, query, deferred, successCallback, update, apply) { |
|||
attributes = $filter('orderBy')(attributes, query.order); |
|||
if (query.search != null) { |
|||
attributes = $filter('filter')(attributes, {key: query.search}); |
|||
} |
|||
var responseData = { |
|||
count: attributes.length |
|||
} |
|||
var startIndex = query.limit * (query.page - 1); |
|||
responseData.data = attributes.slice(startIndex, startIndex + query.limit); |
|||
successCallback(responseData, update, apply); |
|||
if (deferred) { |
|||
deferred.resolve(); |
|||
} |
|||
} |
|||
|
|||
function getEntityAttributes(entityType, entityId, attributeScope, query, successCallback, config) { |
|||
var deferred = $q.defer(); |
|||
var subscriptionId = entityType + entityId + attributeScope; |
|||
var eas = entityAttributesSubscriptionMap[subscriptionId]; |
|||
if (eas) { |
|||
if (eas.attributes) { |
|||
processAttributes(eas.attributes, query, deferred, successCallback); |
|||
eas.subscriptionCallback = function(attributes) { |
|||
processAttributes(attributes, query, null, successCallback, true, true); |
|||
} |
|||
} else { |
|||
eas.subscriptionCallback = function(attributes) { |
|||
processAttributes(attributes, query, deferred, successCallback, false, true); |
|||
eas.subscriptionCallback = function(attributes) { |
|||
processAttributes(attributes, query, null, successCallback, true, true); |
|||
} |
|||
} |
|||
} |
|||
} else { |
|||
var url = '/api/plugins/telemetry/' + entityType + '/' + entityId + '/values/attributes/' + attributeScope; |
|||
$http.get(url, config).then(function success(response) { |
|||
processAttributes(response.data, query, deferred, successCallback); |
|||
}, function fail() { |
|||
deferred.reject(); |
|||
}); |
|||
} |
|||
return deferred; |
|||
} |
|||
|
|||
function onSubscriptionData(data, subscriptionId) { |
|||
var entityAttributesSubscription = entityAttributesSubscriptionMap[subscriptionId]; |
|||
if (entityAttributesSubscription) { |
|||
if (!entityAttributesSubscription.attributes) { |
|||
entityAttributesSubscription.attributes = []; |
|||
entityAttributesSubscription.keys = {}; |
|||
} |
|||
var attributes = entityAttributesSubscription.attributes; |
|||
var keys = entityAttributesSubscription.keys; |
|||
for (var key in data) { |
|||
var index = keys[key]; |
|||
var attribute; |
|||
if (index > -1) { |
|||
attribute = attributes[index]; |
|||
} else { |
|||
attribute = { |
|||
key: key |
|||
}; |
|||
index = attributes.push(attribute)-1; |
|||
keys[key] = index; |
|||
} |
|||
var attrData = data[key][0]; |
|||
attribute.lastUpdateTs = attrData[0]; |
|||
attribute.value = attrData[1]; |
|||
} |
|||
if (entityAttributesSubscription.subscriptionCallback) { |
|||
entityAttributesSubscription.subscriptionCallback(attributes); |
|||
} |
|||
} |
|||
} |
|||
|
|||
function subscribeForEntityAttributes(entityType, entityId, attributeScope) { |
|||
var subscriptionId = entityType + entityId + attributeScope; |
|||
var entityAttributesSubscription = entityAttributesSubscriptionMap[subscriptionId]; |
|||
if (!entityAttributesSubscription) { |
|||
var subscriptionCommand = { |
|||
entityType: entityType, |
|||
entityId: entityId, |
|||
scope: attributeScope |
|||
}; |
|||
|
|||
var type = attributeScope === types.latestTelemetry.value ? |
|||
types.dataKeyType.timeseries : types.dataKeyType.attribute; |
|||
|
|||
var subscriber = { |
|||
subscriptionCommands: [subscriptionCommand], |
|||
type: type, |
|||
onData: function (data) { |
|||
if (data.data) { |
|||
onSubscriptionData(data.data, subscriptionId); |
|||
} |
|||
} |
|||
}; |
|||
entityAttributesSubscription = { |
|||
subscriber: subscriber, |
|||
attributes: null |
|||
}; |
|||
entityAttributesSubscriptionMap[subscriptionId] = entityAttributesSubscription; |
|||
telemetryWebsocketService.subscribe(subscriber); |
|||
} |
|||
return subscriptionId; |
|||
} |
|||
|
|||
function unsubscribeForEntityAttributes(subscriptionId) { |
|||
var entityAttributesSubscription = entityAttributesSubscriptionMap[subscriptionId]; |
|||
if (entityAttributesSubscription) { |
|||
telemetryWebsocketService.unsubscribe(entityAttributesSubscription.subscriber); |
|||
delete entityAttributesSubscriptionMap[subscriptionId]; |
|||
} |
|||
} |
|||
|
|||
function saveEntityAttributes(entityType, entityId, attributeScope, attributes, config) { |
|||
config = config || {}; |
|||
var deferred = $q.defer(); |
|||
var attributesData = {}; |
|||
var deleteAttributes = []; |
|||
for (var a=0; a<attributes.length;a++) { |
|||
if (angular.isDefined(attributes[a].value) && attributes[a].value !== null) { |
|||
attributesData[attributes[a].key] = attributes[a].value; |
|||
} else { |
|||
deleteAttributes.push(attributes[a]); |
|||
} |
|||
} |
|||
var deleteEntityAttributesPromise; |
|||
if (deleteAttributes.length) { |
|||
deleteEntityAttributesPromise = deleteEntityAttributes(entityType, entityId, attributeScope, deleteAttributes); |
|||
} |
|||
if (Object.keys(attributesData).length) { |
|||
var url = '/api/plugins/telemetry/' + entityType + '/' + entityId + '/' + attributeScope; |
|||
$http.post(url, attributesData, config).then(function success(response) { |
|||
if (deleteEntityAttributesPromise) { |
|||
deleteEntityAttributesPromise.then( |
|||
function success() { |
|||
deferred.resolve(response.data); |
|||
}, |
|||
function fail() { |
|||
deferred.reject(); |
|||
} |
|||
) |
|||
} else { |
|||
deferred.resolve(response.data); |
|||
} |
|||
}, function fail() { |
|||
deferred.reject(); |
|||
}); |
|||
} else if (deleteEntityAttributesPromise) { |
|||
deleteEntityAttributesPromise.then( |
|||
function success() { |
|||
deferred.resolve(); |
|||
}, |
|||
function fail() { |
|||
deferred.reject(); |
|||
} |
|||
) |
|||
} else { |
|||
deferred.resolve(); |
|||
} |
|||
return deferred.promise; |
|||
} |
|||
|
|||
function saveEntityTimeseries(entityType, entityId, timeseriesScope, timeseries, config) { |
|||
config = config || {}; |
|||
var deferred = $q.defer(); |
|||
var timeseriesData = {}; |
|||
var deleteTimeseries = []; |
|||
for (var a=0; a<timeseries.length;a++) { |
|||
if (angular.isDefined(timeseries[a].value) && timeseries[a].value !== null) { |
|||
timeseriesData[timeseries[a].key] = timeseries[a].value; |
|||
} else { |
|||
deleteTimeseries.push(timeseries[a]); |
|||
} |
|||
} |
|||
var deleteEntityTimeseriesPromise; |
|||
if (deleteTimeseries.length) { |
|||
deleteEntityTimeseriesPromise = deleteEntityTimeseries(entityType, entityId, deleteTimeseries, config, true); |
|||
} |
|||
if (Object.keys(timeseriesData).length) { |
|||
var url = '/api/plugins/telemetry/' + entityType + '/' + entityId + '/timeseries/' + timeseriesScope; |
|||
$http.post(url, timeseriesData, config).then(function success(response) { |
|||
if (deleteEntityTimeseriesPromise) { |
|||
deleteEntityTimeseriesPromise.then( |
|||
function success() { |
|||
deferred.resolve(response.data); |
|||
}, |
|||
function fail() { |
|||
deferred.reject(); |
|||
} |
|||
) |
|||
} else { |
|||
deferred.resolve(response.data); |
|||
} |
|||
}, function fail() { |
|||
deferred.reject(); |
|||
}); |
|||
} else if (deleteEntityTimeseriesPromise) { |
|||
deleteEntityTimeseriesPromise.then( |
|||
function success() { |
|||
deferred.resolve(); |
|||
}, |
|||
function fail() { |
|||
deferred.reject(); |
|||
} |
|||
) |
|||
} else { |
|||
deferred.resolve(); |
|||
} |
|||
return deferred.promise; |
|||
} |
|||
|
|||
function deleteEntityAttributes(entityType, entityId, attributeScope, attributes, config) { |
|||
config = config || {}; |
|||
var deferred = $q.defer(); |
|||
var keys = ''; |
|||
for (var i = 0; i < attributes.length; i++) { |
|||
if (i > 0) { |
|||
keys += ','; |
|||
} |
|||
keys += attributes[i].key; |
|||
} |
|||
var url = '/api/plugins/telemetry/' + entityType + '/' + entityId + '/' + attributeScope + '?keys=' + keys; |
|||
$http.delete(url, config).then(function success() { |
|||
deferred.resolve(); |
|||
}, function fail() { |
|||
deferred.reject(); |
|||
}); |
|||
return deferred.promise; |
|||
} |
|||
|
|||
function deleteEntityTimeseries(entityType, entityId, timeseries, config, deleteAllDataForKeys) { |
|||
config = config || {}; |
|||
deleteAllDataForKeys = deleteAllDataForKeys || false; |
|||
var deferred = $q.defer(); |
|||
var keys = ''; |
|||
for (var i = 0; i < timeseries.length; i++) { |
|||
if (i > 0) { |
|||
keys += ','; |
|||
} |
|||
keys += timeseries[i].key; |
|||
} |
|||
var url = '/api/plugins/telemetry/' + entityType + '/' + entityId + '/timeseries/delete' + |
|||
'?keys=' + keys + |
|||
'&deleteAllDataForKeys=' + deleteAllDataForKeys; |
|||
$http.delete(url, config).then(function success() { |
|||
deferred.resolve(); |
|||
}, function fail() { |
|||
deferred.reject(); |
|||
}); |
|||
return deferred.promise; |
|||
} |
|||
|
|||
} |
|||
@ -1,116 +0,0 @@ |
|||
/* |
|||
* Copyright © 2016-2020 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
export default angular.module('thingsboard.api.auditLog', []) |
|||
.factory('auditLogService', AuditLogService) |
|||
.name; |
|||
|
|||
/*@ngInject*/ |
|||
function AuditLogService($http, $q) { |
|||
|
|||
var service = { |
|||
getAuditLogsByEntityId: getAuditLogsByEntityId, |
|||
getAuditLogsByUserId: getAuditLogsByUserId, |
|||
getAuditLogsByCustomerId: getAuditLogsByCustomerId, |
|||
getAuditLogs: getAuditLogs |
|||
} |
|||
|
|||
return service; |
|||
|
|||
function getAuditLogsByEntityId (entityType, entityId, pageLink) { |
|||
var deferred = $q.defer(); |
|||
var url = `/api/audit/logs/entity/${entityType}/${entityId}?limit=${pageLink.limit}`; |
|||
|
|||
if (angular.isDefined(pageLink.startTime) && pageLink.startTime != null) { |
|||
url += '&startTime=' + pageLink.startTime; |
|||
} |
|||
if (angular.isDefined(pageLink.endTime) && pageLink.endTime != null) { |
|||
url += '&endTime=' + pageLink.endTime; |
|||
} |
|||
if (angular.isDefined(pageLink.idOffset) && pageLink.idOffset != null) { |
|||
url += '&offset=' + pageLink.idOffset; |
|||
} |
|||
$http.get(url, null).then(function success(response) { |
|||
deferred.resolve(response.data); |
|||
}, function fail() { |
|||
deferred.reject(); |
|||
}); |
|||
return deferred.promise; |
|||
} |
|||
|
|||
function getAuditLogsByUserId (userId, pageLink) { |
|||
var deferred = $q.defer(); |
|||
var url = `/api/audit/logs/user/${userId}?limit=${pageLink.limit}`; |
|||
|
|||
if (angular.isDefined(pageLink.startTime) && pageLink.startTime != null) { |
|||
url += '&startTime=' + pageLink.startTime; |
|||
} |
|||
if (angular.isDefined(pageLink.endTime) && pageLink.endTime != null) { |
|||
url += '&endTime=' + pageLink.endTime; |
|||
} |
|||
if (angular.isDefined(pageLink.idOffset) && pageLink.idOffset != null) { |
|||
url += '&offset=' + pageLink.idOffset; |
|||
} |
|||
$http.get(url, null).then(function success(response) { |
|||
deferred.resolve(response.data); |
|||
}, function fail() { |
|||
deferred.reject(); |
|||
}); |
|||
return deferred.promise; |
|||
} |
|||
|
|||
function getAuditLogsByCustomerId (customerId, pageLink) { |
|||
var deferred = $q.defer(); |
|||
var url = `/api/audit/logs/customer/${customerId}?limit=${pageLink.limit}`; |
|||
|
|||
if (angular.isDefined(pageLink.startTime) && pageLink.startTime != null) { |
|||
url += '&startTime=' + pageLink.startTime; |
|||
} |
|||
if (angular.isDefined(pageLink.endTime) && pageLink.endTime != null) { |
|||
url += '&endTime=' + pageLink.endTime; |
|||
} |
|||
if (angular.isDefined(pageLink.idOffset) && pageLink.idOffset != null) { |
|||
url += '&offset=' + pageLink.idOffset; |
|||
} |
|||
$http.get(url, null).then(function success(response) { |
|||
deferred.resolve(response.data); |
|||
}, function fail() { |
|||
deferred.reject(); |
|||
}); |
|||
return deferred.promise; |
|||
} |
|||
|
|||
function getAuditLogs (pageLink) { |
|||
var deferred = $q.defer(); |
|||
var url = `/api/audit/logs?limit=${pageLink.limit}`; |
|||
|
|||
if (angular.isDefined(pageLink.startTime) && pageLink.startTime != null) { |
|||
url += '&startTime=' + pageLink.startTime; |
|||
} |
|||
if (angular.isDefined(pageLink.endTime) && pageLink.endTime != null) { |
|||
url += '&endTime=' + pageLink.endTime; |
|||
} |
|||
if (angular.isDefined(pageLink.idOffset) && pageLink.idOffset != null) { |
|||
url += '&offset=' + pageLink.idOffset; |
|||
} |
|||
$http.get(url, null).then(function success(response) { |
|||
deferred.resolve(response.data); |
|||
}, function fail() { |
|||
deferred.reject(); |
|||
}); |
|||
return deferred.promise; |
|||
} |
|||
|
|||
} |
|||
@ -1,123 +0,0 @@ |
|||
/* |
|||
* Copyright © 2016-2020 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
export default angular.module('thingsboard.api.componentDescriptor', []) |
|||
.factory('componentDescriptorService', ComponentDescriptorService).name; |
|||
|
|||
/*@ngInject*/ |
|||
function ComponentDescriptorService($http, $q) { |
|||
|
|||
var componentsByType = {}; |
|||
var componentsByClazz = {}; |
|||
var actionsByPlugin = {}; |
|||
|
|||
var service = { |
|||
getComponentDescriptorsByType: getComponentDescriptorsByType, |
|||
getComponentDescriptorByClazz: getComponentDescriptorByClazz, |
|||
getPluginActionsByPluginClazz: getPluginActionsByPluginClazz, |
|||
getComponentDescriptorsByTypes: getComponentDescriptorsByTypes |
|||
} |
|||
|
|||
return service; |
|||
|
|||
function getComponentDescriptorsByType(componentType) { |
|||
var deferred = $q.defer(); |
|||
if (componentsByType[componentType]) { |
|||
deferred.resolve(componentsByType[componentType]); |
|||
} else { |
|||
var url = '/api/components/' + componentType; |
|||
$http.get(url, null).then(function success(response) { |
|||
componentsByType[componentType] = response.data; |
|||
for (var i = 0; i < componentsByType[componentType].length; i++) { |
|||
var component = componentsByType[componentType][i]; |
|||
componentsByClazz[component.clazz] = component; |
|||
} |
|||
deferred.resolve(componentsByType[componentType]); |
|||
}, function fail() { |
|||
deferred.reject(); |
|||
}); |
|||
|
|||
} |
|||
return deferred.promise; |
|||
} |
|||
|
|||
function getComponentDescriptorsByTypes(componentTypes) { |
|||
var deferred = $q.defer(); |
|||
var result = []; |
|||
for (var i=componentTypes.length-1;i>=0;i--) { |
|||
var componentType = componentTypes[i]; |
|||
if (componentsByType[componentType]) { |
|||
result = result.concat(componentsByType[componentType]); |
|||
componentTypes.splice(i, 1); |
|||
} |
|||
} |
|||
if (!componentTypes.length) { |
|||
deferred.resolve(result); |
|||
} else { |
|||
var url = '/api/components?componentTypes=' + componentTypes.join(','); |
|||
$http.get(url, null).then(function success(response) { |
|||
var components = response.data; |
|||
for (var i = 0; i < components.length; i++) { |
|||
var component = components[i]; |
|||
var componentsList = componentsByType[component.type]; |
|||
if (!componentsList) { |
|||
componentsList = []; |
|||
componentsByType[component.type] = componentsList; |
|||
} |
|||
componentsList.push(component); |
|||
componentsByClazz[component.clazz] = component; |
|||
} |
|||
result = result.concat(components); |
|||
deferred.resolve(components); |
|||
}, function fail() { |
|||
deferred.reject(); |
|||
}); |
|||
} |
|||
return deferred.promise; |
|||
} |
|||
|
|||
function getComponentDescriptorByClazz(componentDescriptorClazz) { |
|||
var deferred = $q.defer(); |
|||
if (componentsByClazz[componentDescriptorClazz]) { |
|||
deferred.resolve(componentsByClazz[componentDescriptorClazz]); |
|||
} else { |
|||
var url = '/api/component/' + componentDescriptorClazz; |
|||
$http.get(url, null).then(function success(response) { |
|||
componentsByClazz[componentDescriptorClazz] = response.data; |
|||
deferred.resolve(componentsByClazz[componentDescriptorClazz]); |
|||
}, function fail() { |
|||
deferred.reject(); |
|||
}); |
|||
} |
|||
return deferred.promise; |
|||
} |
|||
|
|||
function getPluginActionsByPluginClazz(pluginClazz) { |
|||
var deferred = $q.defer(); |
|||
if (actionsByPlugin[pluginClazz]) { |
|||
deferred.resolve(actionsByPlugin[pluginClazz]); |
|||
} else { |
|||
var url = '/api/components/actions/' + pluginClazz; |
|||
$http.get(url, null).then(function success(response) { |
|||
actionsByPlugin[pluginClazz] = response.data; |
|||
deferred.resolve(actionsByPlugin[pluginClazz]); |
|||
}, function fail() { |
|||
deferred.reject(); |
|||
}); |
|||
} |
|||
return deferred.promise; |
|||
} |
|||
|
|||
} |
|||
@ -1,170 +0,0 @@ |
|||
/* |
|||
* Copyright © 2016-2020 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
export default angular.module('thingsboard.api.customer', []) |
|||
.factory('customerService', CustomerService) |
|||
.name; |
|||
|
|||
/*@ngInject*/ |
|||
function CustomerService($http, $q, types) { |
|||
|
|||
var service = { |
|||
getCustomers: getCustomers, |
|||
getCustomer: getCustomer, |
|||
getShortCustomerInfo: getShortCustomerInfo, |
|||
applyAssignedCustomersInfo: applyAssignedCustomersInfo, |
|||
applyAssignedCustomerInfo: applyAssignedCustomerInfo, |
|||
deleteCustomer: deleteCustomer, |
|||
saveCustomer: saveCustomer |
|||
} |
|||
|
|||
return service; |
|||
|
|||
function getCustomers(pageLink, config) { |
|||
var deferred = $q.defer(); |
|||
var url = '/api/customers?limit=' + pageLink.limit; |
|||
if (angular.isDefined(pageLink.textSearch)) { |
|||
url += '&textSearch=' + pageLink.textSearch; |
|||
} |
|||
if (angular.isDefined(pageLink.idOffset)) { |
|||
url += '&idOffset=' + pageLink.idOffset; |
|||
} |
|||
if (angular.isDefined(pageLink.textOffset)) { |
|||
url += '&textOffset=' + pageLink.textOffset; |
|||
} |
|||
$http.get(url, config).then(function success(response) { |
|||
deferred.resolve(response.data); |
|||
}, function fail() { |
|||
deferred.reject(); |
|||
}); |
|||
return deferred.promise; |
|||
} |
|||
|
|||
function getCustomer(customerId, config) { |
|||
var deferred = $q.defer(); |
|||
var url = '/api/customer/' + customerId; |
|||
$http.get(url, config).then(function success(response) { |
|||
deferred.resolve(response.data); |
|||
}, function fail(response) { |
|||
deferred.reject(response.data); |
|||
}); |
|||
return deferred.promise; |
|||
} |
|||
|
|||
function getShortCustomerInfo(customerId) { |
|||
var deferred = $q.defer(); |
|||
var url = '/api/customer/' + customerId + '/shortInfo'; |
|||
$http.get(url, null).then(function success(response) { |
|||
deferred.resolve(response.data); |
|||
}, function fail(response) { |
|||
deferred.reject(response.data); |
|||
}); |
|||
return deferred.promise; |
|||
} |
|||
|
|||
function applyAssignedCustomersInfo(items) { |
|||
var deferred = $q.defer(); |
|||
var assignedCustomersMap = {}; |
|||
function loadNextCustomerInfoOrComplete(i) { |
|||
i++; |
|||
if (i < items.length) { |
|||
loadNextCustomerInfo(i); |
|||
} else { |
|||
deferred.resolve(items); |
|||
} |
|||
} |
|||
|
|||
function loadNextCustomerInfo(i) { |
|||
var item = items[i]; |
|||
item.assignedCustomer = {}; |
|||
if (item.customerId && item.customerId.id != types.id.nullUid) { |
|||
item.assignedCustomer.id = item.customerId.id; |
|||
var assignedCustomer = assignedCustomersMap[item.customerId.id]; |
|||
if (assignedCustomer){ |
|||
item.assignedCustomer = assignedCustomer; |
|||
loadNextCustomerInfoOrComplete(i); |
|||
} else { |
|||
getShortCustomerInfo(item.customerId.id).then( |
|||
function success(info) { |
|||
assignedCustomer = { |
|||
id: item.customerId.id, |
|||
title: info.title, |
|||
isPublic: info.isPublic |
|||
}; |
|||
assignedCustomersMap[assignedCustomer.id] = assignedCustomer; |
|||
item.assignedCustomer = assignedCustomer; |
|||
loadNextCustomerInfoOrComplete(i); |
|||
}, |
|||
function fail() { |
|||
loadNextCustomerInfoOrComplete(i); |
|||
} |
|||
); |
|||
} |
|||
} else { |
|||
loadNextCustomerInfoOrComplete(i); |
|||
} |
|||
} |
|||
if (items.length > 0) { |
|||
loadNextCustomerInfo(0); |
|||
} else { |
|||
deferred.resolve(items); |
|||
} |
|||
return deferred.promise; |
|||
} |
|||
|
|||
function applyAssignedCustomerInfo(items, customerId) { |
|||
var deferred = $q.defer(); |
|||
getShortCustomerInfo(customerId).then( |
|||
function success(info) { |
|||
var assignedCustomer = { |
|||
id: customerId, |
|||
title: info.title, |
|||
isPublic: info.isPublic |
|||
} |
|||
items.forEach(function(item) { |
|||
item.assignedCustomer = assignedCustomer; |
|||
}); |
|||
deferred.resolve(items); |
|||
}, |
|||
function fail() { |
|||
deferred.reject(); |
|||
} |
|||
); |
|||
return deferred.promise; |
|||
} |
|||
|
|||
function saveCustomer(customer) { |
|||
var deferred = $q.defer(); |
|||
var url = '/api/customer'; |
|||
$http.post(url, customer).then(function success(response) { |
|||
deferred.resolve(response.data); |
|||
}, function fail(response) { |
|||
deferred.reject(response.data); |
|||
}); |
|||
return deferred.promise; |
|||
} |
|||
|
|||
function deleteCustomer(customerId) { |
|||
var deferred = $q.defer(); |
|||
var url = '/api/customer/' + customerId; |
|||
$http.delete(url).then(function success() { |
|||
deferred.resolve(); |
|||
}, function fail(response) { |
|||
deferred.reject(response.data); |
|||
}); |
|||
return deferred.promise; |
|||
} |
|||
|
|||
} |
|||
@ -1,295 +0,0 @@ |
|||
/* |
|||
* Copyright © 2016-2020 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
export default angular.module('thingsboard.api.dashboard', []) |
|||
.factory('dashboardService', DashboardService).name; |
|||
|
|||
/*@ngInject*/ |
|||
function DashboardService($rootScope, $http, $q, $location, $filter) { |
|||
|
|||
var stDiffPromise; |
|||
|
|||
$rootScope.dadshboardServiceStateChangeStartHandle = $rootScope.$on('$stateChangeStart', function () { |
|||
stDiffPromise = undefined; |
|||
}); |
|||
|
|||
|
|||
var service = { |
|||
assignDashboardToCustomer: assignDashboardToCustomer, |
|||
getCustomerDashboards: getCustomerDashboards, |
|||
getServerTimeDiff: getServerTimeDiff, |
|||
getDashboard: getDashboard, |
|||
getDashboardInfo: getDashboardInfo, |
|||
getTenantDashboardsByTenantId: getTenantDashboardsByTenantId, |
|||
getTenantDashboards: getTenantDashboards, |
|||
deleteDashboard: deleteDashboard, |
|||
saveDashboard: saveDashboard, |
|||
unassignDashboardFromCustomer: unassignDashboardFromCustomer, |
|||
updateDashboardCustomers: updateDashboardCustomers, |
|||
addDashboardCustomers: addDashboardCustomers, |
|||
removeDashboardCustomers: removeDashboardCustomers, |
|||
makeDashboardPublic: makeDashboardPublic, |
|||
makeDashboardPrivate: makeDashboardPrivate, |
|||
getPublicDashboardLink: getPublicDashboardLink |
|||
} |
|||
|
|||
return service; |
|||
|
|||
function getTenantDashboardsByTenantId(tenantId, pageLink, config) { |
|||
var deferred = $q.defer(); |
|||
var url = '/api/tenant/' + tenantId + '/dashboards?limit=' + pageLink.limit; |
|||
if (angular.isDefined(pageLink.textSearch)) { |
|||
url += '&textSearch=' + pageLink.textSearch; |
|||
} |
|||
if (angular.isDefined(pageLink.idOffset)) { |
|||
url += '&idOffset=' + pageLink.idOffset; |
|||
} |
|||
if (angular.isDefined(pageLink.textOffset)) { |
|||
url += '&textOffset=' + pageLink.textOffset; |
|||
} |
|||
$http.get(url, config).then(function success(response) { |
|||
deferred.resolve(prepareDashboards(response.data)); |
|||
}, function fail() { |
|||
deferred.reject(); |
|||
}); |
|||
return deferred.promise; |
|||
} |
|||
|
|||
function getTenantDashboards(pageLink, config) { |
|||
var deferred = $q.defer(); |
|||
var url = '/api/tenant/dashboards?limit=' + pageLink.limit; |
|||
if (angular.isDefined(pageLink.textSearch)) { |
|||
url += '&textSearch=' + pageLink.textSearch; |
|||
} |
|||
if (angular.isDefined(pageLink.idOffset)) { |
|||
url += '&idOffset=' + pageLink.idOffset; |
|||
} |
|||
if (angular.isDefined(pageLink.textOffset)) { |
|||
url += '&textOffset=' + pageLink.textOffset; |
|||
} |
|||
$http.get(url, config).then(function success(response) { |
|||
deferred.resolve(prepareDashboards(response.data)); |
|||
}, function fail() { |
|||
deferred.reject(); |
|||
}); |
|||
return deferred.promise; |
|||
} |
|||
|
|||
function getCustomerDashboards(customerId, pageLink, config) { |
|||
var deferred = $q.defer(); |
|||
var url = '/api/customer/' + customerId + '/dashboards?limit=' + pageLink.limit; |
|||
if (angular.isDefined(pageLink.idOffset)) { |
|||
url += '&offset=' + pageLink.idOffset; |
|||
} |
|||
$http.get(url, config).then(function success(response) { |
|||
response.data = prepareDashboards(response.data); |
|||
if (pageLink.textSearch) { |
|||
response.data.data = $filter('filter')(response.data.data, {title: pageLink.textSearch}); |
|||
} |
|||
deferred.resolve(response.data); |
|||
}, function fail() { |
|||
deferred.reject(); |
|||
}); |
|||
return deferred.promise; |
|||
} |
|||
|
|||
function getServerTimeDiff() { |
|||
if (stDiffPromise) { |
|||
return stDiffPromise; |
|||
} else { |
|||
var deferred = $q.defer(); |
|||
stDiffPromise = deferred.promise; |
|||
var url = '/api/dashboard/serverTime'; |
|||
var ct1 = Date.now(); |
|||
$http.get(url, {ignoreLoading: true}).then(function success(response) { |
|||
var ct2 = Date.now(); |
|||
var st = response.data; |
|||
var stDiff = Math.ceil(st - (ct1 + ct2) / 2); |
|||
deferred.resolve(stDiff); |
|||
}, function fail() { |
|||
deferred.reject(); |
|||
}); |
|||
} |
|||
return stDiffPromise; |
|||
} |
|||
|
|||
function getDashboard(dashboardId) { |
|||
var deferred = $q.defer(); |
|||
var url = '/api/dashboard/' + dashboardId; |
|||
$http.get(url, null).then(function success(response) { |
|||
deferred.resolve(prepareDashboard(response.data)); |
|||
}, function fail() { |
|||
deferred.reject(); |
|||
}); |
|||
return deferred.promise; |
|||
} |
|||
|
|||
function getDashboardInfo(dashboardId, config) { |
|||
var deferred = $q.defer(); |
|||
var url = '/api/dashboard/info/' + dashboardId; |
|||
$http.get(url, config).then(function success(response) { |
|||
deferred.resolve(prepareDashboard(response.data)); |
|||
}, function fail() { |
|||
deferred.reject(); |
|||
}); |
|||
return deferred.promise; |
|||
} |
|||
|
|||
function saveDashboard(dashboard) { |
|||
var deferred = $q.defer(); |
|||
var url = '/api/dashboard'; |
|||
$http.post(url, cleanDashboard(dashboard)).then(function success(response) { |
|||
deferred.resolve(prepareDashboard(response.data)); |
|||
}, function fail() { |
|||
deferred.reject(); |
|||
}); |
|||
return deferred.promise; |
|||
} |
|||
|
|||
function deleteDashboard(dashboardId) { |
|||
var deferred = $q.defer(); |
|||
var url = '/api/dashboard/' + dashboardId; |
|||
$http.delete(url).then(function success() { |
|||
deferred.resolve(); |
|||
}, function fail() { |
|||
deferred.reject(); |
|||
}); |
|||
return deferred.promise; |
|||
} |
|||
|
|||
function assignDashboardToCustomer(customerId, dashboardId) { |
|||
var deferred = $q.defer(); |
|||
var url = '/api/customer/' + customerId + '/dashboard/' + dashboardId; |
|||
$http.post(url, null).then(function success(response) { |
|||
deferred.resolve(prepareDashboard(response.data)); |
|||
}, function fail() { |
|||
deferred.reject(); |
|||
}); |
|||
return deferred.promise; |
|||
} |
|||
|
|||
function unassignDashboardFromCustomer(customerId, dashboardId) { |
|||
var deferred = $q.defer(); |
|||
var url = '/api/customer/' + customerId + '/dashboard/' + dashboardId; |
|||
$http.delete(url).then(function success(response) { |
|||
deferred.resolve(prepareDashboard(response.data)); |
|||
}, function fail() { |
|||
deferred.reject(); |
|||
}); |
|||
return deferred.promise; |
|||
} |
|||
|
|||
function updateDashboardCustomers(dashboardId, customerIds) { |
|||
var deferred = $q.defer(); |
|||
var url = '/api/dashboard/' + dashboardId + '/customers'; |
|||
$http.post(url, customerIds).then(function success(response) { |
|||
deferred.resolve(prepareDashboard(response.data)); |
|||
}, function fail() { |
|||
deferred.reject(); |
|||
}); |
|||
return deferred.promise; |
|||
} |
|||
|
|||
function addDashboardCustomers(dashboardId, customerIds) { |
|||
var deferred = $q.defer(); |
|||
var url = '/api/dashboard/' + dashboardId + '/customers/add'; |
|||
$http.post(url, customerIds).then(function success(response) { |
|||
deferred.resolve(prepareDashboard(response.data)); |
|||
}, function fail() { |
|||
deferred.reject(); |
|||
}); |
|||
return deferred.promise; |
|||
} |
|||
|
|||
function removeDashboardCustomers(dashboardId, customerIds) { |
|||
var deferred = $q.defer(); |
|||
var url = '/api/dashboard/' + dashboardId + '/customers/remove'; |
|||
$http.post(url, customerIds).then(function success(response) { |
|||
deferred.resolve(prepareDashboard(response.data)); |
|||
}, function fail() { |
|||
deferred.reject(); |
|||
}); |
|||
return deferred.promise; |
|||
} |
|||
|
|||
function makeDashboardPublic(dashboardId) { |
|||
var deferred = $q.defer(); |
|||
var url = '/api/customer/public/dashboard/' + dashboardId; |
|||
$http.post(url, null).then(function success(response) { |
|||
deferred.resolve(prepareDashboard(response.data)); |
|||
}, function fail() { |
|||
deferred.reject(); |
|||
}); |
|||
return deferred.promise; |
|||
} |
|||
|
|||
function makeDashboardPrivate(dashboardId) { |
|||
var deferred = $q.defer(); |
|||
var url = '/api/customer/public/dashboard/' + dashboardId; |
|||
$http.delete(url).then(function success(response) { |
|||
deferred.resolve(prepareDashboard(response.data)); |
|||
}, function fail() { |
|||
deferred.reject(); |
|||
}); |
|||
return deferred.promise; |
|||
} |
|||
|
|||
function getPublicDashboardLink(dashboard) { |
|||
var url = $location.protocol() + '://' + $location.host(); |
|||
var port = $location.port(); |
|||
if (port != 80 && port != 443) { |
|||
url += ":" + port; |
|||
} |
|||
url += "/dashboard/" + dashboard.id.id + "?publicId=" + dashboard.publicCustomerId; |
|||
return url; |
|||
} |
|||
|
|||
function prepareDashboards(dashboardsData) { |
|||
if (dashboardsData.data) { |
|||
for (var i = 0; i < dashboardsData.data.length; i++) { |
|||
dashboardsData.data[i] = prepareDashboard(dashboardsData.data[i]); |
|||
} |
|||
} |
|||
return dashboardsData; |
|||
} |
|||
|
|||
function prepareDashboard(dashboard) { |
|||
dashboard.publicCustomerId = null; |
|||
dashboard.assignedCustomersText = ""; |
|||
dashboard.assignedCustomersIds = []; |
|||
if (dashboard.assignedCustomers && dashboard.assignedCustomers.length) { |
|||
var assignedCustomersTitles = []; |
|||
for (var i = 0; i < dashboard.assignedCustomers.length; i++) { |
|||
var assignedCustomer = dashboard.assignedCustomers[i]; |
|||
dashboard.assignedCustomersIds.push(assignedCustomer.customerId.id); |
|||
if (assignedCustomer.public) { |
|||
dashboard.publicCustomerId = assignedCustomer.customerId.id; |
|||
} else { |
|||
assignedCustomersTitles.push(assignedCustomer.title); |
|||
} |
|||
} |
|||
dashboard.assignedCustomersText = assignedCustomersTitles.join(', '); |
|||
} |
|||
return dashboard; |
|||
} |
|||
|
|||
function cleanDashboard(dashboard) { |
|||
delete dashboard.publicCustomerId; |
|||
delete dashboard.assignedCustomersText; |
|||
delete dashboard.assignedCustomersIds; |
|||
return dashboard; |
|||
} |
|||
|
|||
} |
|||
@ -1,316 +0,0 @@ |
|||
/* |
|||
* Copyright © 2016-2020 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
export default class DataAggregator { |
|||
|
|||
constructor(onDataCb, tsKeyNames, startTs, limit, aggregationType, timeWindow, interval, |
|||
stateData, types, $timeout, $filter) { |
|||
this.onDataCb = onDataCb; |
|||
this.tsKeyNames = tsKeyNames; |
|||
this.dataBuffer = {}; |
|||
for (var k = 0; k < tsKeyNames.length; k++) { |
|||
this.dataBuffer[tsKeyNames[k]] = []; |
|||
} |
|||
this.startTs = startTs; |
|||
this.aggregationType = aggregationType; |
|||
this.types = types; |
|||
this.$timeout = $timeout; |
|||
this.$filter = $filter; |
|||
this.dataReceived = false; |
|||
this.resetPending = false; |
|||
this.noAggregation = aggregationType === types.aggregation.none.value; |
|||
this.limit = limit; |
|||
this.timeWindow = timeWindow; |
|||
this.interval = interval; |
|||
this.stateData = stateData; |
|||
if (this.stateData) { |
|||
this.lastPrevKvPairData = {}; |
|||
} |
|||
this.aggregationTimeout = Math.max(this.interval, 1000); |
|||
switch (aggregationType) { |
|||
case types.aggregation.min.value: |
|||
this.aggFunction = min; |
|||
break; |
|||
case types.aggregation.max.value: |
|||
this.aggFunction = max; |
|||
break; |
|||
case types.aggregation.avg.value: |
|||
this.aggFunction = avg; |
|||
break; |
|||
case types.aggregation.sum.value: |
|||
this.aggFunction = sum; |
|||
break; |
|||
case types.aggregation.count.value: |
|||
this.aggFunction = count; |
|||
break; |
|||
case types.aggregation.none.value: |
|||
this.aggFunction = none; |
|||
break; |
|||
default: |
|||
this.aggFunction = avg; |
|||
} |
|||
} |
|||
|
|||
reset(startTs, timeWindow, interval) { |
|||
if (this.intervalTimeoutHandle) { |
|||
this.$timeout.cancel(this.intervalTimeoutHandle); |
|||
this.intervalTimeoutHandle = null; |
|||
} |
|||
this.intervalScheduledTime = currentTime(); |
|||
this.startTs = startTs; |
|||
this.timeWindow = timeWindow; |
|||
this.interval = interval; |
|||
this.endTs = this.startTs + this.timeWindow; |
|||
this.elapsed = 0; |
|||
this.aggregationTimeout = Math.max(this.interval, 1000); |
|||
this.resetPending = true; |
|||
var self = this; |
|||
this.intervalTimeoutHandle = this.$timeout(function() { |
|||
self.onInterval(); |
|||
}, this.aggregationTimeout, false); |
|||
} |
|||
|
|||
onData(data, update, history, apply) { |
|||
if (!this.dataReceived || this.resetPending) { |
|||
var updateIntervalScheduledTime = true; |
|||
if (!this.dataReceived) { |
|||
this.elapsed = 0; |
|||
this.dataReceived = true; |
|||
this.endTs = this.startTs + this.timeWindow; |
|||
} |
|||
if (this.resetPending) { |
|||
this.resetPending = false; |
|||
updateIntervalScheduledTime = false; |
|||
} |
|||
if (update) { |
|||
this.aggregationMap = {}; |
|||
updateAggregatedData(this.aggregationMap, this.aggregationType === this.types.aggregation.count.value, |
|||
this.noAggregation, this.aggFunction, data.data, this.interval, this.startTs); |
|||
} else { |
|||
this.aggregationMap = processAggregatedData(data.data, this.aggregationType === this.types.aggregation.count.value, this.noAggregation); |
|||
} |
|||
if (updateIntervalScheduledTime) { |
|||
this.intervalScheduledTime = currentTime(); |
|||
} |
|||
this.onInterval(history, apply); |
|||
} else { |
|||
updateAggregatedData(this.aggregationMap, this.aggregationType === this.types.aggregation.count.value, |
|||
this.noAggregation, this.aggFunction, data.data, this.interval, this.startTs); |
|||
if (history) { |
|||
this.intervalScheduledTime = currentTime(); |
|||
this.onInterval(history, apply); |
|||
} |
|||
} |
|||
} |
|||
|
|||
onInterval(history, apply) { |
|||
var now = currentTime(); |
|||
this.elapsed += now - this.intervalScheduledTime; |
|||
this.intervalScheduledTime = now; |
|||
if (this.intervalTimeoutHandle) { |
|||
this.$timeout.cancel(this.intervalTimeoutHandle); |
|||
this.intervalTimeoutHandle = null; |
|||
} |
|||
if (!history) { |
|||
var delta = Math.floor(this.elapsed / this.interval); |
|||
if (delta || !this.data) { |
|||
this.startTs += delta * this.interval; |
|||
this.endTs += delta * this.interval; |
|||
this.data = this.updateData(); |
|||
this.elapsed = this.elapsed - delta * this.interval; |
|||
} |
|||
} else { |
|||
this.data = this.updateData(); |
|||
} |
|||
if (this.onDataCb) { |
|||
this.onDataCb(this.data, apply); |
|||
} |
|||
|
|||
var self = this; |
|||
if (!history) { |
|||
this.intervalTimeoutHandle = this.$timeout(function() { |
|||
self.onInterval(); |
|||
}, this.aggregationTimeout, false); |
|||
} |
|||
} |
|||
|
|||
updateData() { |
|||
for (var k = 0; k < this.tsKeyNames.length; k++) { |
|||
this.dataBuffer[this.tsKeyNames[k]] = []; |
|||
} |
|||
for (var key in this.aggregationMap) { |
|||
var aggKeyData = this.aggregationMap[key]; |
|||
var keyData = this.dataBuffer[key]; |
|||
for (var aggTimestamp in aggKeyData) { |
|||
if (aggTimestamp <= this.startTs) { |
|||
if (this.stateData && |
|||
(!this.lastPrevKvPairData[key] || this.lastPrevKvPairData[key][0] < aggTimestamp)) { |
|||
this.lastPrevKvPairData[key] = [Number(aggTimestamp), aggKeyData[aggTimestamp].aggValue]; |
|||
} |
|||
delete aggKeyData[aggTimestamp]; |
|||
} else if (aggTimestamp <= this.endTs) { |
|||
var aggData = aggKeyData[aggTimestamp]; |
|||
var kvPair = [Number(aggTimestamp), aggData.aggValue]; |
|||
keyData.push(kvPair); |
|||
} |
|||
} |
|||
keyData = this.$filter('orderBy')(keyData, '+this[0]'); |
|||
if (this.stateData) { |
|||
this.updateStateBounds(keyData, angular.copy(this.lastPrevKvPairData[key])); |
|||
} |
|||
if (keyData.length > this.limit) { |
|||
keyData = keyData.slice(keyData.length - this.limit); |
|||
} |
|||
this.dataBuffer[key] = keyData; |
|||
} |
|||
return this.dataBuffer; |
|||
} |
|||
|
|||
updateStateBounds(keyData, lastPrevKvPair) { |
|||
if (lastPrevKvPair) { |
|||
lastPrevKvPair[0] = this.startTs; |
|||
} |
|||
var firstKvPair; |
|||
if (!keyData.length) { |
|||
if (lastPrevKvPair) { |
|||
firstKvPair = lastPrevKvPair; |
|||
keyData.push(firstKvPair); |
|||
} |
|||
} else { |
|||
firstKvPair = keyData[0]; |
|||
} |
|||
if (firstKvPair && firstKvPair[0] > this.startTs) { |
|||
if (lastPrevKvPair) { |
|||
keyData.unshift(lastPrevKvPair); |
|||
} |
|||
} |
|||
if (keyData.length) { |
|||
var lastKvPair = keyData[keyData.length-1]; |
|||
if (lastKvPair[0] < this.endTs) { |
|||
lastKvPair = angular.copy(lastKvPair); |
|||
lastKvPair[0] = this.endTs; |
|||
keyData.push(lastKvPair); |
|||
} |
|||
} |
|||
} |
|||
|
|||
destroy() { |
|||
if (this.intervalTimeoutHandle) { |
|||
this.$timeout.cancel(this.intervalTimeoutHandle); |
|||
this.intervalTimeoutHandle = null; |
|||
} |
|||
this.aggregationMap = null; |
|||
} |
|||
|
|||
} |
|||
|
|||
/* eslint-disable */ |
|||
function currentTime() { |
|||
return window.performance && window.performance.now ? |
|||
window.performance.now() : Date.now(); |
|||
} |
|||
/* eslint-enable */ |
|||
|
|||
function processAggregatedData(data, isCount, noAggregation) { |
|||
var aggregationMap = {}; |
|||
for (var key in data) { |
|||
var aggKeyData = aggregationMap[key]; |
|||
if (!aggKeyData) { |
|||
aggKeyData = {}; |
|||
aggregationMap[key] = aggKeyData; |
|||
} |
|||
var keyData = data[key]; |
|||
for (var i = 0; i < keyData.length; i++) { |
|||
var kvPair = keyData[i]; |
|||
var timestamp = kvPair[0]; |
|||
var value = convertValue(kvPair[1], noAggregation); |
|||
var aggKey = timestamp; |
|||
var aggData = { |
|||
count: isCount ? value : 1, |
|||
sum: value, |
|||
aggValue: value |
|||
} |
|||
aggKeyData[aggKey] = aggData; |
|||
} |
|||
} |
|||
return aggregationMap; |
|||
} |
|||
|
|||
function updateAggregatedData(aggregationMap, isCount, noAggregation, aggFunction, data, interval, startTs) { |
|||
for (var key in data) { |
|||
var aggKeyData = aggregationMap[key]; |
|||
if (!aggKeyData) { |
|||
aggKeyData = {}; |
|||
aggregationMap[key] = aggKeyData; |
|||
} |
|||
var keyData = data[key]; |
|||
for (var i = 0; i < keyData.length; i++) { |
|||
var kvPair = keyData[i]; |
|||
var timestamp = kvPair[0]; |
|||
var value = convertValue(kvPair[1], noAggregation); |
|||
var aggTimestamp = noAggregation ? timestamp : (startTs + Math.floor((timestamp - startTs) / interval) * interval + interval/2); |
|||
var aggData = aggKeyData[aggTimestamp]; |
|||
if (!aggData) { |
|||
aggData = { |
|||
count: 1, |
|||
sum: value, |
|||
aggValue: isCount ? 1 : value |
|||
} |
|||
aggKeyData[aggTimestamp] = aggData; |
|||
} else { |
|||
aggFunction(aggData, value); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
function convertValue(value, noAggregation) { |
|||
if (!noAggregation || value && isNumeric(value)) { |
|||
return Number(value); |
|||
} else { |
|||
return value; |
|||
} |
|||
} |
|||
|
|||
function isNumeric(value) { |
|||
return (value - parseFloat( value ) + 1) >= 0; |
|||
} |
|||
|
|||
function avg(aggData, value) { |
|||
aggData.count++; |
|||
aggData.sum += value; |
|||
aggData.aggValue = aggData.sum / aggData.count; |
|||
} |
|||
|
|||
function min(aggData, value) { |
|||
aggData.aggValue = Math.min(aggData.aggValue, value); |
|||
} |
|||
|
|||
function max(aggData, value) { |
|||
aggData.aggValue = Math.max(aggData.aggValue, value); |
|||
} |
|||
|
|||
function sum(aggData, value) { |
|||
aggData.aggValue = aggData.aggValue + value; |
|||
} |
|||
|
|||
function count(aggData) { |
|||
aggData.count++; |
|||
aggData.aggValue = aggData.count; |
|||
} |
|||
|
|||
function none(aggData, value) { |
|||
aggData.aggValue = value; |
|||
} |
|||
@ -1,747 +0,0 @@ |
|||
/* |
|||
* Copyright © 2016-2020 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
import thingsboardApiDevice from './device.service'; |
|||
import thingsboardApiTelemetryWebsocket from './telemetry-websocket.service'; |
|||
import thingsboardTypes from '../common/types.constant'; |
|||
import thingsboardUtils from '../common/utils.service'; |
|||
import DataAggregator from './data-aggregator'; |
|||
|
|||
export default angular.module('thingsboard.api.datasource', [thingsboardApiDevice, thingsboardApiTelemetryWebsocket, thingsboardTypes, thingsboardUtils]) |
|||
.factory('datasourceService', DatasourceService) |
|||
.name; |
|||
|
|||
const YEAR = 1000 * 60 * 60 * 24 * 365; |
|||
|
|||
/*@ngInject*/ |
|||
function DatasourceService($timeout, $filter, $log, telemetryWebsocketService, types, utils) { |
|||
|
|||
var subscriptions = {}; |
|||
|
|||
var service = { |
|||
subscribeToDatasource: subscribeToDatasource, |
|||
unsubscribeFromDatasource: unsubscribeFromDatasource |
|||
} |
|||
|
|||
return service; |
|||
|
|||
function subscribeToDatasource(listener) { |
|||
var datasource = listener.datasource; |
|||
|
|||
if (datasource.type === types.datasourceType.entity && (!listener.entityId || !listener.entityType)) { |
|||
return; |
|||
} |
|||
|
|||
var subscriptionDataKeys = []; |
|||
for (var d = 0; d < datasource.dataKeys.length; d++) { |
|||
var dataKey = datasource.dataKeys[d]; |
|||
var subscriptionDataKey = { |
|||
name: dataKey.name, |
|||
type: dataKey.type, |
|||
funcBody: dataKey.funcBody, |
|||
postFuncBody: dataKey.postFuncBody |
|||
} |
|||
subscriptionDataKeys.push(subscriptionDataKey); |
|||
} |
|||
|
|||
var datasourceSubscription = { |
|||
datasourceType: datasource.type, |
|||
dataKeys: subscriptionDataKeys, |
|||
type: listener.subscriptionType |
|||
}; |
|||
|
|||
if (listener.subscriptionType === types.widgetType.timeseries.value) { |
|||
datasourceSubscription.subscriptionTimewindow = angular.copy(listener.subscriptionTimewindow); |
|||
} |
|||
if (datasourceSubscription.datasourceType === types.datasourceType.entity) { |
|||
datasourceSubscription.entityType = listener.entityType; |
|||
datasourceSubscription.entityId = listener.entityId; |
|||
} |
|||
|
|||
listener.datasourceSubscriptionKey = utils.objectHashCode(datasourceSubscription); |
|||
var subscription; |
|||
if (subscriptions[listener.datasourceSubscriptionKey]) { |
|||
subscription = subscriptions[listener.datasourceSubscriptionKey]; |
|||
subscription.syncListener(listener); |
|||
} else { |
|||
subscription = new DatasourceSubscription(datasourceSubscription, telemetryWebsocketService, $timeout, $filter, $log, types, utils); |
|||
subscriptions[listener.datasourceSubscriptionKey] = subscription; |
|||
subscription.start(); |
|||
} |
|||
subscription.addListener(listener); |
|||
} |
|||
|
|||
function unsubscribeFromDatasource(listener) { |
|||
if (listener.datasourceSubscriptionKey) { |
|||
if (subscriptions[listener.datasourceSubscriptionKey]) { |
|||
var subscription = subscriptions[listener.datasourceSubscriptionKey]; |
|||
subscription.removeListener(listener); |
|||
if (!subscription.hasListeners()) { |
|||
subscription.unsubscribe(); |
|||
delete subscriptions[listener.datasourceSubscriptionKey]; |
|||
} |
|||
} |
|||
listener.datasourceSubscriptionKey = null; |
|||
} |
|||
} |
|||
|
|||
} |
|||
|
|||
function DatasourceSubscription(datasourceSubscription, telemetryWebsocketService, $timeout, $filter, $log, types, utils) { |
|||
|
|||
var listeners = []; |
|||
var datasourceType = datasourceSubscription.datasourceType; |
|||
var datasourceData = {}; |
|||
var dataSourceOrigData = {}; |
|||
var dataKeys = {}; |
|||
var subscribers = []; |
|||
var history = datasourceSubscription.subscriptionTimewindow && |
|||
datasourceSubscription.subscriptionTimewindow.fixedWindow; |
|||
var realtime = datasourceSubscription.subscriptionTimewindow && |
|||
datasourceSubscription.subscriptionTimewindow.realtimeWindowMs; |
|||
var timer; |
|||
var frequency; |
|||
var tickElapsed = 0; |
|||
var tickScheduledTime = 0; |
|||
var dataAggregator; |
|||
|
|||
var subscription = { |
|||
addListener: addListener, |
|||
hasListeners: hasListeners, |
|||
removeListener: removeListener, |
|||
syncListener: syncListener, |
|||
start: start, |
|||
unsubscribe: unsubscribe |
|||
} |
|||
|
|||
initializeSubscription(); |
|||
|
|||
return subscription; |
|||
|
|||
function initializeSubscription() { |
|||
for (var i = 0; i < datasourceSubscription.dataKeys.length; i++) { |
|||
var dataKey = angular.copy(datasourceSubscription.dataKeys[i]); |
|||
dataKey.index = i; |
|||
var key; |
|||
if (datasourceType === types.datasourceType.function) { |
|||
if (!dataKey.func) { |
|||
dataKey.func = new Function("time", "prevValue", dataKey.funcBody); |
|||
} |
|||
} else { |
|||
if (dataKey.postFuncBody && !dataKey.postFunc) { |
|||
dataKey.postFunc = new Function("time", "value", "prevValue", "timePrev", "prevOrigValue", dataKey.postFuncBody); |
|||
} |
|||
} |
|||
if (datasourceType === types.datasourceType.entity || datasourceSubscription.type === types.widgetType.timeseries.value) { |
|||
if (datasourceType === types.datasourceType.function) { |
|||
key = dataKey.name + '_' + dataKey.index + '_' + dataKey.type; |
|||
} else { |
|||
key = dataKey.name + '_' + dataKey.type; |
|||
} |
|||
var dataKeysList = dataKeys[key]; |
|||
if (!dataKeysList) { |
|||
dataKeysList = []; |
|||
dataKeys[key] = dataKeysList; |
|||
} |
|||
var index = dataKeysList.push(dataKey) - 1; |
|||
datasourceData[key + '_' + index] = { |
|||
data: [] |
|||
}; |
|||
} else { |
|||
key = utils.objectHashCode(dataKey); |
|||
datasourceData[key] = { |
|||
data: [] |
|||
}; |
|||
dataKeys[key] = dataKey; |
|||
} |
|||
dataSourceOrigData = angular.copy(datasourceData); |
|||
dataKey.key = key; |
|||
} |
|||
if (datasourceType === types.datasourceType.function) { |
|||
frequency = 1000; |
|||
if (datasourceSubscription.type === types.widgetType.timeseries.value) { |
|||
frequency = Math.min(datasourceSubscription.subscriptionTimewindow.aggregation.interval, 5000); |
|||
} |
|||
} |
|||
} |
|||
|
|||
function addListener(listener) { |
|||
listeners.push(listener); |
|||
if (history) { |
|||
start(); |
|||
} |
|||
} |
|||
|
|||
function hasListeners() { |
|||
return listeners.length > 0; |
|||
} |
|||
|
|||
function removeListener(listener) { |
|||
listeners.splice(listeners.indexOf(listener), 1); |
|||
} |
|||
|
|||
function syncListener(listener) { |
|||
var key; |
|||
var dataKey; |
|||
if (datasourceType === types.datasourceType.entity || datasourceSubscription.type === types.widgetType.timeseries.value) { |
|||
for (key in dataKeys) { |
|||
var dataKeysList = dataKeys[key]; |
|||
for (var i = 0; i < dataKeysList.length; i++) { |
|||
dataKey = dataKeysList[i]; |
|||
var datasourceKey = key + '_' + i; |
|||
listener.dataUpdated(datasourceData[datasourceKey], |
|||
listener.datasourceIndex, |
|||
dataKey.index, false); |
|||
} |
|||
} |
|||
} else { |
|||
for (key in dataKeys) { |
|||
dataKey = dataKeys[key]; |
|||
listener.dataUpdated(datasourceData[key], |
|||
listener.datasourceIndex, |
|||
dataKey.index, false); |
|||
} |
|||
} |
|||
} |
|||
|
|||
function start() { |
|||
if (history && !hasListeners()) { |
|||
return; |
|||
} |
|||
var subsTw = datasourceSubscription.subscriptionTimewindow; |
|||
var tsKeyNames = []; |
|||
var dataKey; |
|||
|
|||
if (datasourceType === types.datasourceType.entity) { |
|||
|
|||
//send subscribe command
|
|||
|
|||
var tsKeys = ''; |
|||
var attrKeys = ''; |
|||
|
|||
for (var key in dataKeys) { |
|||
var dataKeysList = dataKeys[key]; |
|||
dataKey = dataKeysList[0]; |
|||
if (dataKey.type === types.dataKeyType.timeseries) { |
|||
if (tsKeys.length > 0) { |
|||
tsKeys += ','; |
|||
} |
|||
tsKeys += dataKey.name; |
|||
tsKeyNames.push(dataKey.name); |
|||
} else if (dataKey.type === types.dataKeyType.attribute) { |
|||
if (attrKeys.length > 0) { |
|||
attrKeys += ','; |
|||
} |
|||
attrKeys += dataKey.name; |
|||
} |
|||
} |
|||
|
|||
if (tsKeys.length > 0) { |
|||
|
|||
var subscriber; |
|||
|
|||
if (history) { |
|||
|
|||
var historyCommand = { |
|||
entityType: datasourceSubscription.entityType, |
|||
entityId: datasourceSubscription.entityId, |
|||
keys: tsKeys, |
|||
startTs: subsTw.fixedWindow.startTimeMs, |
|||
endTs: subsTw.fixedWindow.endTimeMs, |
|||
interval: subsTw.aggregation.interval, |
|||
limit: subsTw.aggregation.limit, |
|||
agg: subsTw.aggregation.type |
|||
}; |
|||
|
|||
subscriber = { |
|||
historyCommands: [ historyCommand ], |
|||
type: types.dataKeyType.timeseries, |
|||
subsTw: subsTw |
|||
}; |
|||
|
|||
if (subsTw.aggregation.stateData) { |
|||
subscriber.firstStateHistoryCommand = createFirstStateHistoryCommand(subsTw.fixedWindow.startTimeMs, tsKeys); |
|||
subscriber.historyCommands.push(subscriber.firstStateHistoryCommand); |
|||
} |
|||
|
|||
subscriber.onData = function (data, subscriptionId) { |
|||
if (this.subsTw.aggregation.stateData && |
|||
this.firstStateHistoryCommand && this.firstStateHistoryCommand.cmdId == subscriptionId) { |
|||
if (this.data) { |
|||
onStateHistoryData(data, this.data, this.subsTw.aggregation.limit, |
|||
subsTw.fixedWindow.startTimeMs, this.subsTw.fixedWindow.endTimeMs, |
|||
(data) => { |
|||
onData(data.data, types.dataKeyType.timeseries, true); |
|||
}); |
|||
} else { |
|||
this.firstStateData = data; |
|||
} |
|||
} else { |
|||
if (this.subsTw.aggregation.stateData) { |
|||
if (this.firstStateData) { |
|||
onStateHistoryData(this.firstStateData, data, this.subsTw.aggregation.limit, |
|||
this.subsTw.fixedWindow.startTimeMs, this.subsTw.fixedWindow.endTimeMs, |
|||
(data) => { |
|||
onData(data.data, types.dataKeyType.timeseries, true); |
|||
}); |
|||
} else { |
|||
this.data = data; |
|||
} |
|||
} else { |
|||
for (key in data.data) { |
|||
var keyData = data.data[key]; |
|||
data.data[key] = $filter('orderBy')(keyData, '+this[0]'); |
|||
} |
|||
onData(data.data, types.dataKeyType.timeseries, true); |
|||
} |
|||
} |
|||
}; |
|||
subscriber.onReconnected = function() {}; |
|||
telemetryWebsocketService.subscribe(subscriber); |
|||
subscribers.push(subscriber); |
|||
|
|||
} else { |
|||
|
|||
var subscriptionCommand = { |
|||
entityType: datasourceSubscription.entityType, |
|||
entityId: datasourceSubscription.entityId, |
|||
keys: tsKeys |
|||
}; |
|||
|
|||
subscriber = { |
|||
subscriptionCommands: [subscriptionCommand], |
|||
type: types.dataKeyType.timeseries |
|||
}; |
|||
|
|||
if (datasourceSubscription.type === types.widgetType.timeseries.value) { |
|||
subscriber.subsTw = subsTw; |
|||
updateRealtimeSubscriptionCommand(subscriptionCommand, subsTw); |
|||
|
|||
if (subsTw.aggregation.stateData) { |
|||
subscriber.firstStateSubscriptionCommand = createFirstStateHistoryCommand(subsTw.startTs, tsKeys); |
|||
subscriber.historyCommands = [subscriber.firstStateSubscriptionCommand]; |
|||
} |
|||
dataAggregator = createRealtimeDataAggregator(subsTw, tsKeyNames, types.dataKeyType.timeseries); |
|||
subscriber.onData = function(data, subscriptionId) { |
|||
if (this.subsTw.aggregation.stateData && |
|||
this.firstStateSubscriptionCommand && this.firstStateSubscriptionCommand.cmdId == subscriptionId) { |
|||
if (this.data) { |
|||
onStateHistoryData(data, this.data, this.subsTw.aggregation.limit, |
|||
this.subsTw.startTs, this.subsTw.startTs + this.subsTw.aggregation.timeWindow, |
|||
(data) => { |
|||
dataAggregator.onData(data, false, false, true); |
|||
}); |
|||
this.stateDataReceived = true; |
|||
} else { |
|||
this.firstStateData = data; |
|||
} |
|||
} else { |
|||
if (this.subsTw.aggregation.stateData && !this.stateDataReceived) { |
|||
if (this.firstStateData) { |
|||
onStateHistoryData(this.firstStateData, data, this.subsTw.aggregation.limit, |
|||
this.subsTw.startTs, this.subsTw.startTs + this.subsTw.aggregation.timeWindow, |
|||
(data) => { |
|||
dataAggregator.onData(data, false, false, true); |
|||
}); |
|||
this.stateDataReceived = true; |
|||
} else { |
|||
this.data = data; |
|||
} |
|||
} else { |
|||
dataAggregator.onData(data, false, false, true); |
|||
} |
|||
} |
|||
} |
|||
subscriber.onReconnected = function() { |
|||
var newSubsTw = null; |
|||
for (var i2 = 0; i2 < listeners.length; i2++) { |
|||
var listener = listeners[i2]; |
|||
if (!newSubsTw) { |
|||
newSubsTw = listener.updateRealtimeSubscription(); |
|||
} else { |
|||
listener.setRealtimeSubscription(newSubsTw); |
|||
} |
|||
} |
|||
this.subsTw = newSubsTw; |
|||
this.firstStateData = null; |
|||
this.data = null; |
|||
this.stateDataReceived = false; |
|||
updateRealtimeSubscriptionCommand(this.subscriptionCommands[0], this.subsTw); |
|||
if (this.subsTw.aggregation.stateData) { |
|||
updateFirstStateHistoryCommand(this.firstStateSubscriptionCommand, this.subsTw.startTs); |
|||
} |
|||
dataAggregator.reset(newSubsTw.startTs, newSubsTw.aggregation.timeWindow, newSubsTw.aggregation.interval); |
|||
} |
|||
} else { |
|||
subscriber.onReconnected = function() {} |
|||
subscriber.onData = function(data) { |
|||
if (data.data) { |
|||
onData(data.data, types.dataKeyType.timeseries, true); |
|||
} |
|||
} |
|||
} |
|||
|
|||
telemetryWebsocketService.subscribe(subscriber); |
|||
subscribers.push(subscriber); |
|||
|
|||
} |
|||
} |
|||
|
|||
if (attrKeys.length > 0) { |
|||
|
|||
var attrsSubscriptionCommand = { |
|||
entityType: datasourceSubscription.entityType, |
|||
entityId: datasourceSubscription.entityId, |
|||
keys: attrKeys |
|||
}; |
|||
|
|||
subscriber = { |
|||
subscriptionCommands: [attrsSubscriptionCommand], |
|||
type: types.dataKeyType.attribute, |
|||
onData: function (data) { |
|||
if (data.data) { |
|||
onData(data.data, types.dataKeyType.attribute, true); |
|||
} |
|||
}, |
|||
onReconnected: function() {} |
|||
}; |
|||
|
|||
telemetryWebsocketService.subscribe(subscriber); |
|||
subscribers.push(subscriber); |
|||
|
|||
} |
|||
|
|||
} else if (datasourceType === types.datasourceType.function) { |
|||
if (datasourceSubscription.type === types.widgetType.timeseries.value) { |
|||
for (key in dataKeys) { |
|||
var dataKeyList = dataKeys[key]; |
|||
for (var index = 0; index < dataKeyList.length; index++) { |
|||
dataKey = dataKeyList[index]; |
|||
tsKeyNames.push(dataKey.name+'_'+dataKey.index); |
|||
} |
|||
} |
|||
dataAggregator = createRealtimeDataAggregator(subsTw, tsKeyNames, types.dataKeyType.function); |
|||
} |
|||
tickScheduledTime = currentTime(); |
|||
if (history) { |
|||
onTick(false); |
|||
} else { |
|||
timer = $timeout( |
|||
function() { |
|||
onTick(true) |
|||
}, |
|||
0, |
|||
false |
|||
); |
|||
} |
|||
} |
|||
} |
|||
|
|||
function createFirstStateHistoryCommand(startTs, tsKeys) { |
|||
return { |
|||
entityType: datasourceSubscription.entityType, |
|||
entityId: datasourceSubscription.entityId, |
|||
keys: tsKeys, |
|||
startTs: startTs - YEAR, |
|||
endTs: startTs, |
|||
interval: 1000, |
|||
limit: 1, |
|||
agg: types.aggregation.none.value |
|||
}; |
|||
} |
|||
|
|||
function updateFirstStateHistoryCommand(stateHistoryCommand, startTs) { |
|||
stateHistoryCommand.startTs = startTs - YEAR; |
|||
stateHistoryCommand.endTs = startTs; |
|||
} |
|||
|
|||
function onStateHistoryData(firstStateData, data, limit, startTs, endTs, onData) { |
|||
for (var key in data.data) { |
|||
var keyData = data.data[key]; |
|||
data.data[key] = $filter('orderBy')(keyData, '+this[0]'); |
|||
keyData = data.data[key]; |
|||
if (keyData.length < limit) { |
|||
var firstStateKeyData = firstStateData.data[key]; |
|||
if (firstStateKeyData.length) { |
|||
var firstStateDataTsKv = firstStateKeyData[0]; |
|||
firstStateDataTsKv[0] = startTs; |
|||
firstStateKeyData = [ |
|||
[ startTs, firstStateKeyData[0][1] ] |
|||
]; |
|||
keyData.unshift(firstStateDataTsKv); |
|||
} |
|||
} |
|||
if (keyData.length) { |
|||
var lastTsKv = angular.copy(keyData[keyData.length-1]); |
|||
lastTsKv[0] = endTs; |
|||
keyData.push(lastTsKv); |
|||
} |
|||
} |
|||
onData(data); |
|||
} |
|||
|
|||
function createRealtimeDataAggregator(subsTw, tsKeyNames, dataKeyType) { |
|||
return new DataAggregator( |
|||
function(data, apply) { |
|||
onData(data, dataKeyType, apply); |
|||
}, |
|||
tsKeyNames, |
|||
subsTw.startTs, |
|||
subsTw.aggregation.limit, |
|||
subsTw.aggregation.type, |
|||
subsTw.aggregation.timeWindow, |
|||
subsTw.aggregation.interval, |
|||
subsTw.aggregation.stateData, |
|||
types, |
|||
$timeout, |
|||
$filter |
|||
); |
|||
} |
|||
|
|||
function updateRealtimeSubscriptionCommand(subscriptionCommand, subsTw) { |
|||
subscriptionCommand.startTs = subsTw.startTs; |
|||
subscriptionCommand.timeWindow = subsTw.aggregation.timeWindow; |
|||
subscriptionCommand.interval = subsTw.aggregation.interval; |
|||
subscriptionCommand.limit = subsTw.aggregation.limit; |
|||
subscriptionCommand.agg = subsTw.aggregation.type; |
|||
} |
|||
|
|||
function unsubscribe() { |
|||
if (timer) { |
|||
$timeout.cancel(timer); |
|||
timer = null; |
|||
} |
|||
if (datasourceType === types.datasourceType.entity) { |
|||
for (var i=0;i<subscribers.length;i++) { |
|||
var subscriber = subscribers[i]; |
|||
telemetryWebsocketService.unsubscribe(subscriber); |
|||
if (subscriber.onDestroy) { |
|||
subscriber.onDestroy(); |
|||
} |
|||
} |
|||
subscribers.length = 0; |
|||
} |
|||
if (dataAggregator) { |
|||
dataAggregator.destroy(); |
|||
dataAggregator = null; |
|||
} |
|||
} |
|||
|
|||
function generateSeries(dataKey, index, startTime, endTime) { |
|||
var data = []; |
|||
var prevSeries; |
|||
var datasourceDataKey = dataKey.key + '_' + index; |
|||
var datasourceKeyData = datasourceData[datasourceDataKey].data; |
|||
if (datasourceKeyData.length > 0) { |
|||
prevSeries = datasourceKeyData[datasourceKeyData.length - 1]; |
|||
} else { |
|||
prevSeries = [0, 0]; |
|||
} |
|||
for (var time = startTime; time <= endTime && (timer || history); time += frequency) { |
|||
var series = []; |
|||
series.push(time); |
|||
var value = dataKey.func(time, prevSeries[1]); |
|||
series.push(value); |
|||
data.push(series); |
|||
prevSeries = series; |
|||
} |
|||
if (data.length > 0) { |
|||
dataKey.lastUpdateTime = data[data.length - 1][0]; |
|||
} |
|||
return data; |
|||
} |
|||
|
|||
function generateLatest(dataKey, apply) { |
|||
var prevSeries; |
|||
var datasourceKeyData = datasourceData[dataKey.key].data; |
|||
if (datasourceKeyData.length > 0) { |
|||
prevSeries = datasourceKeyData[datasourceKeyData.length - 1]; |
|||
} else { |
|||
prevSeries = [0, 0]; |
|||
} |
|||
var series = []; |
|||
var time = (new Date).getTime(); |
|||
series.push(time); |
|||
var value = dataKey.func(time, prevSeries[1]); |
|||
series.push(value); |
|||
datasourceData[dataKey.key].data = [series]; |
|||
for (var i = 0; i < listeners.length; i++) { |
|||
var listener = listeners[i]; |
|||
listener.dataUpdated(datasourceData[dataKey.key], |
|||
listener.datasourceIndex, |
|||
dataKey.index, apply); |
|||
} |
|||
} |
|||
|
|||
/* eslint-disable */ |
|||
function currentTime() { |
|||
return window.performance && window.performance.now ? |
|||
window.performance.now() : Date.now(); |
|||
} |
|||
/* eslint-enable */ |
|||
|
|||
|
|||
function onTick(apply) { |
|||
|
|||
var now = currentTime(); |
|||
tickElapsed += now - tickScheduledTime; |
|||
tickScheduledTime = now; |
|||
|
|||
if (timer) { |
|||
$timeout.cancel(timer); |
|||
} |
|||
|
|||
var key; |
|||
if (datasourceSubscription.type === types.widgetType.timeseries.value) { |
|||
var startTime; |
|||
var endTime; |
|||
var delta; |
|||
var generatedData = { |
|||
data: { |
|||
} |
|||
}; |
|||
if (!history) { |
|||
delta = Math.floor(tickElapsed / frequency); |
|||
} |
|||
var deltaElapsed = history ? frequency : delta * frequency; |
|||
tickElapsed = tickElapsed - deltaElapsed; |
|||
for (key in dataKeys) { |
|||
var dataKeyList = dataKeys[key]; |
|||
for (var index = 0; index < dataKeyList.length && (timer || history); index ++) { |
|||
var dataKey = dataKeyList[index]; |
|||
if (!startTime) { |
|||
if (realtime) { |
|||
if (dataKey.lastUpdateTime) { |
|||
startTime = dataKey.lastUpdateTime + frequency; |
|||
endTime = dataKey.lastUpdateTime + deltaElapsed; |
|||
} else { |
|||
startTime = datasourceSubscription.subscriptionTimewindow.startTs; |
|||
endTime = startTime + datasourceSubscription.subscriptionTimewindow.realtimeWindowMs + frequency; |
|||
if (datasourceSubscription.subscriptionTimewindow.aggregation.type == types.aggregation.none.value) { |
|||
var time = endTime - frequency * datasourceSubscription.subscriptionTimewindow.aggregation.limit; |
|||
startTime = Math.max(time, startTime); |
|||
} |
|||
} |
|||
} else { |
|||
startTime = datasourceSubscription.subscriptionTimewindow.fixedWindow.startTimeMs; |
|||
endTime = datasourceSubscription.subscriptionTimewindow.fixedWindow.endTimeMs; |
|||
} |
|||
} |
|||
var data = generateSeries(dataKey, index, startTime, endTime); |
|||
generatedData.data[dataKey.name+'_'+dataKey.index] = data; |
|||
} |
|||
} |
|||
if (dataAggregator) { |
|||
dataAggregator.onData(generatedData, true, history, apply); |
|||
} |
|||
} else if (datasourceSubscription.type === types.widgetType.latest.value) { |
|||
for (key in dataKeys) { |
|||
generateLatest(dataKeys[key], apply); |
|||
} |
|||
} |
|||
|
|||
if (!history) { |
|||
timer = $timeout(function() {onTick(true)}, frequency, false); |
|||
} |
|||
} |
|||
|
|||
function isNumeric(val) { |
|||
return (val - parseFloat( val ) + 1) >= 0; |
|||
} |
|||
|
|||
function convertValue(val) { |
|||
if (val && isNumeric(val)) { |
|||
return Number(val); |
|||
} else { |
|||
return val; |
|||
} |
|||
} |
|||
|
|||
function onData(sourceData, type, apply) { |
|||
for (var keyName in sourceData) { |
|||
var keyData = sourceData[keyName]; |
|||
var key = keyName + '_' + type; |
|||
var dataKeyList = dataKeys[key]; |
|||
for (var keyIndex = 0; dataKeyList && keyIndex < dataKeyList.length; keyIndex++) { |
|||
var datasourceKey = key + "_" + keyIndex; |
|||
if (datasourceData[datasourceKey].data) { |
|||
var dataKey = dataKeyList[keyIndex]; |
|||
var data = []; |
|||
var prevSeries; |
|||
var prevOrigSeries; |
|||
var datasourceKeyData; |
|||
var datasourceOrigKeyData; |
|||
var update = false; |
|||
if (realtime) { |
|||
datasourceKeyData = []; |
|||
datasourceOrigKeyData = []; |
|||
} else { |
|||
datasourceKeyData = datasourceData[datasourceKey].data; |
|||
datasourceOrigKeyData = dataSourceOrigData[datasourceKey].data; |
|||
} |
|||
if (datasourceKeyData.length > 0) { |
|||
prevSeries = datasourceKeyData[datasourceKeyData.length - 1]; |
|||
prevOrigSeries = datasourceOrigKeyData[datasourceOrigKeyData.length -1]; |
|||
} else { |
|||
prevSeries = [0, 0]; |
|||
prevOrigSeries = [0, 0]; |
|||
} |
|||
dataSourceOrigData[datasourceKey].data = []; |
|||
if (datasourceSubscription.type === types.widgetType.timeseries.value) { |
|||
var series, time, value; |
|||
for (var i = 0; i < keyData.length; i++) { |
|||
series = keyData[i]; |
|||
time = series[0]; |
|||
dataSourceOrigData[datasourceKey].data.push(series); |
|||
value = convertValue(series[1]); |
|||
if (dataKey.postFunc) { |
|||
value = dataKey.postFunc(time, value, prevSeries[1], prevOrigSeries[0], prevOrigSeries[1]); |
|||
} |
|||
prevOrigSeries = series; |
|||
series = [time, value]; |
|||
data.push(series); |
|||
prevSeries = series; |
|||
} |
|||
update = true; |
|||
} else if (datasourceSubscription.type === types.widgetType.latest.value) { |
|||
if (keyData.length > 0) { |
|||
series = keyData[0]; |
|||
time = series[0]; |
|||
dataSourceOrigData[datasourceKey].data.push(series); |
|||
value = convertValue(series[1]); |
|||
if (dataKey.postFunc) { |
|||
value = dataKey.postFunc(time, value, prevSeries[1], prevOrigSeries[0], prevOrigSeries[1]); |
|||
} |
|||
series = [time, value]; |
|||
data.push(series); |
|||
} |
|||
update = true; |
|||
} |
|||
if (update) { |
|||
datasourceData[datasourceKey].data = data; |
|||
for (var i2 = 0; i2 < listeners.length; i2++) { |
|||
var listener = listeners[i2]; |
|||
if (angular.isFunction(listener)) |
|||
continue; |
|||
listener.dataUpdated(datasourceData[datasourceKey], |
|||
listener.datasourceIndex, |
|||
dataKey.index, apply); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -1,361 +0,0 @@ |
|||
/* |
|||
* Copyright © 2016-2020 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
import thingsboardTypes from '../common/types.constant'; |
|||
|
|||
export default angular.module('thingsboard.api.device', [thingsboardTypes]) |
|||
.factory('deviceService', DeviceService) |
|||
.name; |
|||
|
|||
/*@ngInject*/ |
|||
function DeviceService($http, $q, $window, userService, attributeService, customerService, types) { |
|||
|
|||
var service = { |
|||
assignDeviceToCustomer: assignDeviceToCustomer, |
|||
deleteDevice: deleteDevice, |
|||
getCustomerDevices: getCustomerDevices, |
|||
getDevice: getDevice, |
|||
getDevices: getDevices, |
|||
getDeviceCredentials: getDeviceCredentials, |
|||
getTenantDevices: getTenantDevices, |
|||
saveDevice: saveDevice, |
|||
saveDeviceCredentials: saveDeviceCredentials, |
|||
unassignDeviceFromCustomer: unassignDeviceFromCustomer, |
|||
makeDevicePublic: makeDevicePublic, |
|||
getDeviceAttributes: getDeviceAttributes, |
|||
subscribeForDeviceAttributes: subscribeForDeviceAttributes, |
|||
unsubscribeForDeviceAttributes: unsubscribeForDeviceAttributes, |
|||
saveDeviceAttributes: saveDeviceAttributes, |
|||
deleteDeviceAttributes: deleteDeviceAttributes, |
|||
sendOneWayRpcCommand: sendOneWayRpcCommand, |
|||
sendTwoWayRpcCommand: sendTwoWayRpcCommand, |
|||
findByQuery: findByQuery, |
|||
getDeviceTypes: getDeviceTypes, |
|||
findByName: findByName, |
|||
claimDevice: claimDevice, |
|||
unclaimDevice: unclaimDevice |
|||
}; |
|||
|
|||
return service; |
|||
|
|||
function getTenantDevices(pageLink, applyCustomersInfo, config, type) { |
|||
var deferred = $q.defer(); |
|||
var url = '/api/tenant/devices?limit=' + pageLink.limit; |
|||
if (angular.isDefined(pageLink.textSearch)) { |
|||
url += '&textSearch=' + pageLink.textSearch; |
|||
} |
|||
if (angular.isDefined(pageLink.idOffset)) { |
|||
url += '&idOffset=' + pageLink.idOffset; |
|||
} |
|||
if (angular.isDefined(pageLink.textOffset)) { |
|||
url += '&textOffset=' + pageLink.textOffset; |
|||
} |
|||
if (angular.isDefined(type) && type.length) { |
|||
url += '&type=' + type; |
|||
} |
|||
$http.get(url, config).then(function success(response) { |
|||
if (applyCustomersInfo) { |
|||
customerService.applyAssignedCustomersInfo(response.data.data).then( |
|||
function success(data) { |
|||
response.data.data = data; |
|||
deferred.resolve(response.data); |
|||
}, |
|||
function fail() { |
|||
deferred.reject(); |
|||
} |
|||
); |
|||
} else { |
|||
deferred.resolve(response.data); |
|||
} |
|||
}, function fail() { |
|||
deferred.reject(); |
|||
}); |
|||
return deferred.promise; |
|||
} |
|||
|
|||
function getCustomerDevices(customerId, pageLink, applyCustomersInfo, config, type) { |
|||
var deferred = $q.defer(); |
|||
var url = '/api/customer/' + customerId + '/devices?limit=' + pageLink.limit; |
|||
if (angular.isDefined(pageLink.textSearch)) { |
|||
url += '&textSearch=' + pageLink.textSearch; |
|||
} |
|||
if (angular.isDefined(pageLink.idOffset)) { |
|||
url += '&idOffset=' + pageLink.idOffset; |
|||
} |
|||
if (angular.isDefined(pageLink.textOffset)) { |
|||
url += '&textOffset=' + pageLink.textOffset; |
|||
} |
|||
if (angular.isDefined(type) && type.length) { |
|||
url += '&type=' + type; |
|||
} |
|||
$http.get(url, config).then(function success(response) { |
|||
if (applyCustomersInfo) { |
|||
customerService.applyAssignedCustomerInfo(response.data.data, customerId).then( |
|||
function success(data) { |
|||
response.data.data = data; |
|||
deferred.resolve(response.data); |
|||
}, |
|||
function fail() { |
|||
deferred.reject(); |
|||
} |
|||
); |
|||
} else { |
|||
deferred.resolve(response.data); |
|||
} |
|||
}, function fail() { |
|||
deferred.reject(); |
|||
}); |
|||
|
|||
return deferred.promise; |
|||
} |
|||
|
|||
function getDevice(deviceId, ignoreErrors, config) { |
|||
var deferred = $q.defer(); |
|||
var url = '/api/device/' + deviceId; |
|||
if (!config) { |
|||
config = {}; |
|||
} |
|||
config = Object.assign(config, {ignoreErrors: ignoreErrors}); |
|||
$http.get(url, config).then(function success(response) { |
|||
deferred.resolve(response.data); |
|||
}, function fail(response) { |
|||
deferred.reject(response.data); |
|||
}); |
|||
return deferred.promise; |
|||
} |
|||
|
|||
function getDevices(deviceIds, config) { |
|||
var deferred = $q.defer(); |
|||
var ids = ''; |
|||
for (var i = 0; i < deviceIds.length; i++) { |
|||
if (i > 0) { |
|||
ids += ','; |
|||
} |
|||
ids += deviceIds[i]; |
|||
} |
|||
var url = '/api/devices?deviceIds=' + ids; |
|||
$http.get(url, config).then(function success(response) { |
|||
var devices = response.data; |
|||
devices.sort(function (device1, device2) { |
|||
var id1 = device1.id.id; |
|||
var id2 = device2.id.id; |
|||
var index1 = deviceIds.indexOf(id1); |
|||
var index2 = deviceIds.indexOf(id2); |
|||
return index1 - index2; |
|||
}); |
|||
deferred.resolve(devices); |
|||
}, function fail(response) { |
|||
deferred.reject(response.data); |
|||
}); |
|||
return deferred.promise; |
|||
} |
|||
|
|||
function saveDevice(device, config) { |
|||
config = config || {}; |
|||
var deferred = $q.defer(); |
|||
var url = '/api/device'; |
|||
$http.post(url, device, config).then(function success(response) { |
|||
deferred.resolve(response.data); |
|||
}, function fail() { |
|||
deferred.reject(); |
|||
}); |
|||
return deferred.promise; |
|||
} |
|||
|
|||
function deleteDevice(deviceId) { |
|||
var deferred = $q.defer(); |
|||
var url = '/api/device/' + deviceId; |
|||
$http.delete(url).then(function success() { |
|||
deferred.resolve(); |
|||
}, function fail() { |
|||
deferred.reject(); |
|||
}); |
|||
return deferred.promise; |
|||
} |
|||
|
|||
function getDeviceCredentials(deviceId, sync, config) { |
|||
config = config || {}; |
|||
var deferred = $q.defer(); |
|||
var url = '/api/device/' + deviceId + '/credentials'; |
|||
if (sync) { |
|||
var request = new $window.XMLHttpRequest(); |
|||
request.open('GET', url, false); |
|||
request.setRequestHeader("Accept", "application/json, text/plain, */*"); |
|||
userService.setAuthorizationRequestHeader(request); |
|||
request.send(null); |
|||
if (request.status === 200) { |
|||
deferred.resolve(angular.fromJson(request.responseText)); |
|||
} else { |
|||
deferred.reject(); |
|||
} |
|||
} else { |
|||
$http.get(url, config).then(function success(response) { |
|||
deferred.resolve(response.data); |
|||
}, function fail() { |
|||
deferred.reject(); |
|||
}); |
|||
} |
|||
return deferred.promise; |
|||
} |
|||
|
|||
function saveDeviceCredentials(deviceCredentials, config) { |
|||
config = config || {}; |
|||
var deferred = $q.defer(); |
|||
var url = '/api/device/credentials'; |
|||
$http.post(url, deviceCredentials, config).then(function success(response) { |
|||
deferred.resolve(response.data); |
|||
}, function fail() { |
|||
deferred.reject(); |
|||
}); |
|||
return deferred.promise; |
|||
} |
|||
|
|||
function assignDeviceToCustomer(customerId, deviceId) { |
|||
var deferred = $q.defer(); |
|||
var url = '/api/customer/' + customerId + '/device/' + deviceId; |
|||
$http.post(url, null).then(function success(response) { |
|||
deferred.resolve(response.data); |
|||
}, function fail() { |
|||
deferred.reject(); |
|||
}); |
|||
return deferred.promise; |
|||
} |
|||
|
|||
function unassignDeviceFromCustomer(deviceId) { |
|||
var deferred = $q.defer(); |
|||
var url = '/api/customer/device/' + deviceId; |
|||
$http.delete(url).then(function success(response) { |
|||
deferred.resolve(response.data); |
|||
}, function fail() { |
|||
deferred.reject(); |
|||
}); |
|||
return deferred.promise; |
|||
} |
|||
|
|||
function makeDevicePublic(deviceId) { |
|||
var deferred = $q.defer(); |
|||
var url = '/api/customer/public/device/' + deviceId; |
|||
$http.post(url, null).then(function success(response) { |
|||
deferred.resolve(response.data); |
|||
}, function fail() { |
|||
deferred.reject(); |
|||
}); |
|||
return deferred.promise; |
|||
} |
|||
|
|||
function getDeviceAttributes(deviceId, attributeScope, query, successCallback, config) { |
|||
return attributeService.getEntityAttributes(types.entityType.device, deviceId, attributeScope, query, successCallback, config); |
|||
} |
|||
|
|||
function subscribeForDeviceAttributes(deviceId, attributeScope) { |
|||
return attributeService.subscribeForEntityAttributes(types.entityType.device, deviceId, attributeScope); |
|||
} |
|||
|
|||
function unsubscribeForDeviceAttributes(subscriptionId) { |
|||
attributeService.unsubscribeForEntityAttributes(subscriptionId); |
|||
} |
|||
|
|||
function saveDeviceAttributes(deviceId, attributeScope, attributes) { |
|||
return attributeService.saveEntityAttributes(types.entityType.device, deviceId, attributeScope, attributes); |
|||
} |
|||
|
|||
function deleteDeviceAttributes(deviceId, attributeScope, attributes) { |
|||
return attributeService.deleteEntityAttributes(types.entityType.device, deviceId, attributeScope, attributes); |
|||
} |
|||
|
|||
function sendOneWayRpcCommand(deviceId, requestBody) { |
|||
var deferred = $q.defer(); |
|||
var url = '/api/plugins/rpc/oneway/' + deviceId; |
|||
$http.post(url, requestBody).then(function success(response) { |
|||
deferred.resolve(response.data); |
|||
}, function fail(rejection) { |
|||
deferred.reject(rejection); |
|||
}); |
|||
return deferred.promise; |
|||
} |
|||
|
|||
function sendTwoWayRpcCommand(deviceId, requestBody) { |
|||
var deferred = $q.defer(); |
|||
var url = '/api/plugins/rpc/twoway/' + deviceId; |
|||
$http.post(url, requestBody).then(function success(response) { |
|||
deferred.resolve(response.data); |
|||
}, function fail(rejection) { |
|||
deferred.reject(rejection); |
|||
}); |
|||
return deferred.promise; |
|||
} |
|||
|
|||
function findByQuery(query, ignoreErrors, config) { |
|||
var deferred = $q.defer(); |
|||
var url = '/api/devices'; |
|||
if (!config) { |
|||
config = {}; |
|||
} |
|||
config = Object.assign(config, {ignoreErrors: ignoreErrors}); |
|||
$http.post(url, query, config).then(function success(response) { |
|||
deferred.resolve(response.data); |
|||
}, function fail() { |
|||
deferred.reject(); |
|||
}); |
|||
return deferred.promise; |
|||
} |
|||
|
|||
function getDeviceTypes(config) { |
|||
var deferred = $q.defer(); |
|||
var url = '/api/device/types'; |
|||
$http.get(url, config).then(function success(response) { |
|||
deferred.resolve(response.data); |
|||
}, function fail() { |
|||
deferred.reject(); |
|||
}); |
|||
return deferred.promise; |
|||
} |
|||
|
|||
function findByName(deviceName, config) { |
|||
config = config || {}; |
|||
var deferred = $q.defer(); |
|||
var url = '/api/tenant/devices?deviceName=' + deviceName; |
|||
$http.get(url, config).then(function success(response) { |
|||
deferred.resolve(response.data); |
|||
}, function fail() { |
|||
deferred.reject(); |
|||
}); |
|||
return deferred.promise; |
|||
} |
|||
|
|||
function claimDevice(deviceName, deviceSecret, config) { |
|||
deviceSecret = deviceSecret || {}; |
|||
config = config || {}; |
|||
const deferred = $q.defer(); |
|||
const url = '/api/customer/device/' + deviceName + '/claim'; |
|||
$http.post(url, deviceSecret, config).then(function success(response) { |
|||
deferred.resolve(response.data); |
|||
}, function fail(rejection) { |
|||
deferred.reject(rejection); |
|||
}); |
|||
return deferred.promise; |
|||
} |
|||
|
|||
function unclaimDevice(deviceName) { |
|||
const deferred = $q.defer(); |
|||
const url = '/api/customer/device/' + deviceName + '/claim'; |
|||
$http.delete(url).then(function success(response) { |
|||
deferred.resolve(response.data); |
|||
}, function fail(rejection) { |
|||
deferred.reject(rejection); |
|||
}); |
|||
return deferred.promise; |
|||
} |
|||
} |
|||
@ -1,189 +0,0 @@ |
|||
/* |
|||
* Copyright © 2016-2020 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
export default angular.module('thingsboard.api.entityRelation', []) |
|||
.factory('entityRelationService', EntityRelationService) |
|||
.name; |
|||
|
|||
/*@ngInject*/ |
|||
function EntityRelationService($http, $q) { |
|||
|
|||
var service = { |
|||
saveRelation: saveRelation, |
|||
deleteRelation: deleteRelation, |
|||
deleteRelations: deleteRelations, |
|||
getRelation: getRelation, |
|||
findByFrom: findByFrom, |
|||
findInfoByFrom: findInfoByFrom, |
|||
findByFromAndType: findByFromAndType, |
|||
findByTo: findByTo, |
|||
findInfoByTo: findInfoByTo, |
|||
findByToAndType: findByToAndType, |
|||
findByQuery: findByQuery, |
|||
findInfoByQuery: findInfoByQuery |
|||
} |
|||
|
|||
return service; |
|||
|
|||
function saveRelation(relation) { |
|||
var deferred = $q.defer(); |
|||
var url = '/api/relation'; |
|||
$http.post(url, relation).then(function success(response) { |
|||
deferred.resolve(response.data); |
|||
}, function fail() { |
|||
deferred.reject(); |
|||
}); |
|||
return deferred.promise; |
|||
} |
|||
|
|||
function deleteRelation(fromId, fromType, relationType, toId, toType) { |
|||
var deferred = $q.defer(); |
|||
var url = '/api/relation?fromId=' + fromId; |
|||
url += '&fromType=' + fromType; |
|||
url += '&relationType=' + relationType; |
|||
url += '&toId=' + toId; |
|||
url += '&toType=' + toType; |
|||
$http.delete(url).then(function success() { |
|||
deferred.resolve(); |
|||
}, function fail() { |
|||
deferred.reject(); |
|||
}); |
|||
return deferred.promise; |
|||
} |
|||
|
|||
function deleteRelations(entityId, entityType) { |
|||
var deferred = $q.defer(); |
|||
var url = '/api/relations?entityId=' + entityId; |
|||
url += '&entityType=' + entityType; |
|||
$http.delete(url).then(function success() { |
|||
deferred.resolve(); |
|||
}, function fail() { |
|||
deferred.reject(); |
|||
}); |
|||
return deferred.promise; |
|||
} |
|||
|
|||
function getRelation(fromId, fromType, relationType, toId, toType) { |
|||
var deferred = $q.defer(); |
|||
var url = '/api/relation?fromId=' + fromId; |
|||
url += '&fromType=' + fromType; |
|||
url += '&relationType=' + relationType; |
|||
url += '&toId=' + toId; |
|||
url += '&toType=' + toType; |
|||
$http.get(url).then(function success(response) { |
|||
deferred.resolve(response.data); |
|||
}, function fail() { |
|||
deferred.reject(); |
|||
}); |
|||
return deferred.promise; |
|||
} |
|||
|
|||
function findByFrom(fromId, fromType) { |
|||
var deferred = $q.defer(); |
|||
var url = '/api/relations?fromId=' + fromId; |
|||
url += '&fromType=' + fromType; |
|||
$http.get(url, null).then(function success(response) { |
|||
deferred.resolve(response.data); |
|||
}, function fail() { |
|||
deferred.reject(); |
|||
}); |
|||
return deferred.promise; |
|||
} |
|||
|
|||
function findInfoByFrom(fromId, fromType) { |
|||
var deferred = $q.defer(); |
|||
var url = '/api/relations/info?fromId=' + fromId; |
|||
url += '&fromType=' + fromType; |
|||
$http.get(url, null).then(function success(response) { |
|||
deferred.resolve(response.data); |
|||
}, function fail() { |
|||
deferred.reject(); |
|||
}); |
|||
return deferred.promise; |
|||
} |
|||
|
|||
function findByFromAndType(fromId, fromType, relationType) { |
|||
var deferred = $q.defer(); |
|||
var url = '/api/relations?fromId=' + fromId; |
|||
url += '&fromType=' + fromType; |
|||
url += '&relationType=' + relationType; |
|||
$http.get(url, null).then(function success(response) { |
|||
deferred.resolve(response.data); |
|||
}, function fail() { |
|||
deferred.reject(); |
|||
}); |
|||
return deferred.promise; |
|||
} |
|||
|
|||
function findByTo(toId, toType) { |
|||
var deferred = $q.defer(); |
|||
var url = '/api/relations?toId=' + toId; |
|||
url += '&toType=' + toType; |
|||
$http.get(url, null).then(function success(response) { |
|||
deferred.resolve(response.data); |
|||
}, function fail() { |
|||
deferred.reject(); |
|||
}); |
|||
return deferred.promise; |
|||
} |
|||
|
|||
function findInfoByTo(toId, toType) { |
|||
var deferred = $q.defer(); |
|||
var url = '/api/relations/info?toId=' + toId; |
|||
url += '&toType=' + toType; |
|||
$http.get(url, null).then(function success(response) { |
|||
deferred.resolve(response.data); |
|||
}, function fail() { |
|||
deferred.reject(); |
|||
}); |
|||
return deferred.promise; |
|||
} |
|||
|
|||
function findByToAndType(toId, toType, relationType) { |
|||
var deferred = $q.defer(); |
|||
var url = '/api/relations?toId=' + toId; |
|||
url += '&toType=' + toType; |
|||
url += '&relationType=' + relationType; |
|||
$http.get(url, null).then(function success(response) { |
|||
deferred.resolve(response.data); |
|||
}, function fail() { |
|||
deferred.reject(); |
|||
}); |
|||
return deferred.promise; |
|||
} |
|||
|
|||
function findByQuery(query, config) { |
|||
var deferred = $q.defer(); |
|||
var url = '/api/relations'; |
|||
$http.post(url, query, config).then(function success(response) { |
|||
deferred.resolve(response.data); |
|||
}, function fail(e) { |
|||
deferred.reject(e); |
|||
}); |
|||
return deferred.promise; |
|||
} |
|||
|
|||
function findInfoByQuery(query, config) { |
|||
var deferred = $q.defer(); |
|||
var url = '/api/relations/info'; |
|||
$http.post(url, query, config).then(function success(response) { |
|||
deferred.resolve(response.data); |
|||
}, function fail() { |
|||
deferred.reject(); |
|||
}); |
|||
return deferred.promise; |
|||
} |
|||
|
|||
} |
|||
@ -1,224 +0,0 @@ |
|||
/* |
|||
* Copyright © 2016-2020 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
import thingsboardTypes from '../common/types.constant'; |
|||
|
|||
export default angular.module('thingsboard.api.entityView', [thingsboardTypes]) |
|||
.factory('entityViewService', EntityViewService) |
|||
.name; |
|||
|
|||
/*@ngInject*/ |
|||
function EntityViewService($http, $q, $window, userService, attributeService, customerService, types) { |
|||
|
|||
var service = { |
|||
assignEntityViewToCustomer: assignEntityViewToCustomer, |
|||
deleteEntityView: deleteEntityView, |
|||
getCustomerEntityViews: getCustomerEntityViews, |
|||
getEntityView: getEntityView, |
|||
getTenantEntityViews: getTenantEntityViews, |
|||
saveEntityView: saveEntityView, |
|||
unassignEntityViewFromCustomer: unassignEntityViewFromCustomer, |
|||
getEntityViewAttributes: getEntityViewAttributes, |
|||
subscribeForEntityViewAttributes: subscribeForEntityViewAttributes, |
|||
unsubscribeForEntityViewAttributes: unsubscribeForEntityViewAttributes, |
|||
findByQuery: findByQuery, |
|||
getEntityViewTypes: getEntityViewTypes, |
|||
makeEntityViewPublic: makeEntityViewPublic |
|||
} |
|||
|
|||
return service; |
|||
|
|||
function getTenantEntityViews(pageLink, applyCustomersInfo, config, type) { |
|||
var deferred = $q.defer(); |
|||
var url = '/api/tenant/entityViews?limit=' + pageLink.limit; |
|||
if (angular.isDefined(pageLink.textSearch)) { |
|||
url += '&textSearch=' + pageLink.textSearch; |
|||
} |
|||
if (angular.isDefined(pageLink.idOffset)) { |
|||
url += '&idOffset=' + pageLink.idOffset; |
|||
} |
|||
if (angular.isDefined(pageLink.textOffset)) { |
|||
url += '&textOffset=' + pageLink.textOffset; |
|||
} |
|||
if (angular.isDefined(type) && type.length) { |
|||
url += '&type=' + type; |
|||
} |
|||
$http.get(url, config).then(function success(response) { |
|||
if (applyCustomersInfo) { |
|||
customerService.applyAssignedCustomersInfo(response.data.data).then( |
|||
function success(data) { |
|||
response.data.data = data; |
|||
deferred.resolve(response.data); |
|||
}, |
|||
function fail() { |
|||
deferred.reject(); |
|||
} |
|||
); |
|||
} else { |
|||
deferred.resolve(response.data); |
|||
} |
|||
}, function fail() { |
|||
deferred.reject(); |
|||
}); |
|||
return deferred.promise; |
|||
} |
|||
|
|||
function getCustomerEntityViews(customerId, pageLink, applyCustomersInfo, config, type) { |
|||
var deferred = $q.defer(); |
|||
var url = '/api/customer/' + customerId + '/entityViews?limit=' + pageLink.limit; |
|||
if (angular.isDefined(pageLink.textSearch)) { |
|||
url += '&textSearch=' + pageLink.textSearch; |
|||
} |
|||
if (angular.isDefined(pageLink.idOffset)) { |
|||
url += '&idOffset=' + pageLink.idOffset; |
|||
} |
|||
if (angular.isDefined(pageLink.textOffset)) { |
|||
url += '&textOffset=' + pageLink.textOffset; |
|||
} |
|||
if (angular.isDefined(type) && type.length) { |
|||
url += '&type=' + type; |
|||
} |
|||
$http.get(url, config).then(function success(response) { |
|||
if (applyCustomersInfo) { |
|||
customerService.applyAssignedCustomerInfo(response.data.data, customerId).then( |
|||
function success(data) { |
|||
response.data.data = data; |
|||
deferred.resolve(response.data); |
|||
}, |
|||
function fail() { |
|||
deferred.reject(); |
|||
} |
|||
); |
|||
} else { |
|||
deferred.resolve(response.data); |
|||
} |
|||
}, function fail() { |
|||
deferred.reject(); |
|||
}); |
|||
|
|||
return deferred.promise; |
|||
} |
|||
|
|||
function getEntityView(entityViewId, ignoreErrors, config) { |
|||
var deferred = $q.defer(); |
|||
var url = '/api/entityView/' + entityViewId; |
|||
if (!config) { |
|||
config = {}; |
|||
} |
|||
config = Object.assign(config, { ignoreErrors: ignoreErrors }); |
|||
$http.get(url, config).then(function success(response) { |
|||
deferred.resolve(response.data); |
|||
}, function fail(response) { |
|||
deferred.reject(response.data); |
|||
}); |
|||
return deferred.promise; |
|||
} |
|||
|
|||
function saveEntityView(entityView) { |
|||
var deferred = $q.defer(); |
|||
var url = '/api/entityView'; |
|||
|
|||
$http.post(url, entityView).then(function success(response) { |
|||
deferred.resolve(response.data); |
|||
}, function fail() { |
|||
deferred.reject(); |
|||
}); |
|||
return deferred.promise; |
|||
} |
|||
|
|||
function deleteEntityView(entityViewId) { |
|||
var deferred = $q.defer(); |
|||
var url = '/api/entityView/' + entityViewId; |
|||
$http.delete(url).then(function success() { |
|||
deferred.resolve(); |
|||
}, function fail() { |
|||
deferred.reject(); |
|||
}); |
|||
return deferred.promise; |
|||
} |
|||
|
|||
function assignEntityViewToCustomer(customerId, entityViewId) { |
|||
var deferred = $q.defer(); |
|||
var url = '/api/customer/' + customerId + '/entityView/' + entityViewId; |
|||
$http.post(url, null).then(function success(response) { |
|||
deferred.resolve(response.data); |
|||
}, function fail() { |
|||
deferred.reject(); |
|||
}); |
|||
return deferred.promise; |
|||
} |
|||
|
|||
function unassignEntityViewFromCustomer(entityViewId) { |
|||
var deferred = $q.defer(); |
|||
var url = '/api/customer/entityView/' + entityViewId; |
|||
$http.delete(url).then(function success(response) { |
|||
deferred.resolve(response.data); |
|||
}, function fail() { |
|||
deferred.reject(); |
|||
}); |
|||
return deferred.promise; |
|||
} |
|||
|
|||
function getEntityViewAttributes(entityViewId, attributeScope, query, successCallback, config) { |
|||
return attributeService.getEntityAttributes(types.entityType.entityView, entityViewId, attributeScope, query, successCallback, config); |
|||
} |
|||
|
|||
function subscribeForEntityViewAttributes(entityViewId, attributeScope) { |
|||
return attributeService.subscribeForEntityAttributes(types.entityType.entityView, entityViewId, attributeScope); |
|||
} |
|||
|
|||
function unsubscribeForEntityViewAttributes(subscriptionId) { |
|||
attributeService.unsubscribeForEntityAttributes(subscriptionId); |
|||
} |
|||
|
|||
function findByQuery(query, ignoreErrors, config) { |
|||
var deferred = $q.defer(); |
|||
var url = '/api/entityViews'; |
|||
if (!config) { |
|||
config = {}; |
|||
} |
|||
config = Object.assign(config, { ignoreErrors: ignoreErrors }); |
|||
$http.post(url, query, config).then(function success(response) { |
|||
deferred.resolve(response.data); |
|||
}, function fail() { |
|||
deferred.reject(); |
|||
}); |
|||
return deferred.promise; |
|||
} |
|||
|
|||
function getEntityViewTypes(config) { |
|||
var deferred = $q.defer(); |
|||
var url = '/api/entityView/types'; |
|||
$http.get(url, config).then(function success(response) { |
|||
deferred.resolve(response.data); |
|||
}, function fail() { |
|||
deferred.reject(); |
|||
}); |
|||
return deferred.promise; |
|||
} |
|||
|
|||
function makeEntityViewPublic(entityViewId) { |
|||
var deferred = $q.defer(); |
|||
var url = '/api/customer/public/entityView/' + entityViewId; |
|||
$http.post(url, null).then(function success(response) { |
|||
deferred.resolve(response.data); |
|||
}, function fail() { |
|||
deferred.reject(); |
|||
}); |
|||
return deferred.promise; |
|||
} |
|||
|
|||
|
|||
} |
|||
File diff suppressed because it is too large
@ -1,50 +0,0 @@ |
|||
/* |
|||
* Copyright © 2016-2020 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
export default angular.module('thingsboard.api.event', []) |
|||
.factory('eventService', EventService) |
|||
.name; |
|||
|
|||
/*@ngInject*/ |
|||
function EventService($http, $q) { |
|||
|
|||
var service = { |
|||
getEvents: getEvents |
|||
} |
|||
|
|||
return service; |
|||
|
|||
function getEvents (entityType, entityId, eventType, tenantId, pageLink) { |
|||
var deferred = $q.defer(); |
|||
var url = '/api/events/'+entityType+'/'+entityId+'/'+eventType+'?tenantId=' + tenantId + '&limit=' + pageLink.limit; |
|||
|
|||
if (angular.isDefined(pageLink.startTime) && pageLink.startTime != null) { |
|||
url += '&startTime=' + pageLink.startTime; |
|||
} |
|||
if (angular.isDefined(pageLink.endTime) && pageLink.endTime != null) { |
|||
url += '&endTime=' + pageLink.endTime; |
|||
} |
|||
if (angular.isDefined(pageLink.idOffset) && pageLink.idOffset != null) { |
|||
url += '&offset=' + pageLink.idOffset; |
|||
} |
|||
$http.get(url, null).then(function success(response) { |
|||
deferred.resolve(response.data); |
|||
}, function fail() { |
|||
deferred.reject(); |
|||
}); |
|||
return deferred.promise; |
|||
} |
|||
|
|||
} |
|||
@ -1,125 +0,0 @@ |
|||
/* |
|||
* Copyright © 2016-2020 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
export default angular.module('thingsboard.api.login', []) |
|||
.factory('loginService', LoginService) |
|||
.name; |
|||
|
|||
/*@ngInject*/ |
|||
function LoginService($http, $q, $rootScope) { |
|||
|
|||
var service = { |
|||
activate: activate, |
|||
changePassword: changePassword, |
|||
hasUser: hasUser, |
|||
login: login, |
|||
publicLogin: publicLogin, |
|||
resetPassword: resetPassword, |
|||
sendResetPasswordLink: sendResetPasswordLink, |
|||
loadOAuth2Clients: loadOAuth2Clients |
|||
} |
|||
|
|||
return service; |
|||
|
|||
function hasUser() { |
|||
return true; |
|||
} |
|||
|
|||
function login(user) { |
|||
var deferred = $q.defer(); |
|||
var loginRequest = { |
|||
username: user.name, |
|||
password: user.password |
|||
}; |
|||
$http.post('/api/auth/login', loginRequest).then(function success(response) { |
|||
deferred.resolve(response); |
|||
}, function fail(response) { |
|||
deferred.reject(response); |
|||
}); |
|||
return deferred.promise; |
|||
} |
|||
|
|||
function publicLogin(publicId) { |
|||
var deferred = $q.defer(); |
|||
var pubilcLoginRequest = { |
|||
publicId: publicId |
|||
}; |
|||
$http.post('/api/auth/login/public', pubilcLoginRequest).then(function success(response) { |
|||
deferred.resolve(response); |
|||
}, function fail(response) { |
|||
deferred.reject(response); |
|||
}); |
|||
return deferred.promise; |
|||
} |
|||
|
|||
function sendResetPasswordLink(email) { |
|||
var deferred = $q.defer(); |
|||
var url = '/api/noauth/resetPasswordByEmail'; |
|||
$http.post(url, {email: email}).then(function success(response) { |
|||
deferred.resolve(response); |
|||
}, function fail() { |
|||
deferred.reject(); |
|||
}); |
|||
return deferred.promise; |
|||
} |
|||
|
|||
function resetPassword(resetToken, password) { |
|||
var deferred = $q.defer(); |
|||
var url = '/api/noauth/resetPassword'; |
|||
$http.post(url, {resetToken: resetToken, password: password}).then(function success(response) { |
|||
deferred.resolve(response); |
|||
}, function fail() { |
|||
deferred.reject(); |
|||
}); |
|||
return deferred.promise; |
|||
} |
|||
|
|||
function activate(activateToken, password, sendActivationMail) { |
|||
var deferred = $q.defer(); |
|||
var url = '/api/noauth/activate'; |
|||
if(sendActivationMail === true || sendActivationMail === false) { |
|||
url += '?sendActivationMail=' + sendActivationMail; |
|||
} |
|||
$http.post(url, {activateToken: activateToken, password: password}).then(function success(response) { |
|||
deferred.resolve(response); |
|||
}, function fail() { |
|||
deferred.reject(); |
|||
}); |
|||
return deferred.promise; |
|||
} |
|||
|
|||
function changePassword(currentPassword, newPassword) { |
|||
var deferred = $q.defer(); |
|||
var url = '/api/auth/changePassword'; |
|||
$http.post(url, {currentPassword: currentPassword, newPassword: newPassword}).then(function success(response) { |
|||
deferred.resolve(response); |
|||
}, function fail() { |
|||
deferred.reject(); |
|||
}); |
|||
return deferred.promise; |
|||
} |
|||
|
|||
function loadOAuth2Clients(){ |
|||
var deferred = $q.defer(); |
|||
var url = '/api/noauth/oauth2Clients'; |
|||
$http.post(url).then(function success(response) { |
|||
$rootScope.oauth2Clients = response.data; |
|||
deferred.resolve(); |
|||
}, function fail() { |
|||
deferred.reject(); |
|||
}); |
|||
return deferred.promise; |
|||
} |
|||
} |
|||
@ -1,40 +0,0 @@ |
|||
/* |
|||
* Copyright © 2016-2020 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
|
|||
export default angular.module('thingsboard.api.queue', []) |
|||
.factory('queueService', queueService) |
|||
.name; |
|||
|
|||
/*@ngInject*/ |
|||
function queueService($http, $q) { |
|||
var service = { |
|||
getTenantQueuesByServiceType: getTenantQueuesByServiceType |
|||
}; |
|||
|
|||
return service; |
|||
|
|||
function getTenantQueuesByServiceType(serviceType, config) { |
|||
let deferred = $q.defer(); |
|||
let url = '/api/tenant/queues?serviceType=' + serviceType; |
|||
|
|||
$http.get(url, config).then(function success(data) { |
|||
deferred.resolve(data); |
|||
}, function fail() { |
|||
deferred.reject(); |
|||
}); |
|||
return deferred.promise; |
|||
} |
|||
} |
|||
@ -1,302 +0,0 @@ |
|||
/* |
|||
* Copyright © 2016-2020 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
export default angular.module('thingsboard.api.ruleChain', []) |
|||
.factory('ruleChainService', RuleChainService).name; |
|||
|
|||
/*@ngInject*/ |
|||
function RuleChainService($http, $q, $filter, $ocLazyLoad, $translate, types, componentDescriptorService) { |
|||
|
|||
var ruleNodeComponents = null; |
|||
|
|||
var service = { |
|||
getRuleChains: getRuleChains, |
|||
getRuleChain: getRuleChain, |
|||
saveRuleChain: saveRuleChain, |
|||
setRootRuleChain: setRootRuleChain, |
|||
deleteRuleChain: deleteRuleChain, |
|||
getRuleChainMetaData: getRuleChainMetaData, |
|||
saveRuleChainMetaData: saveRuleChainMetaData, |
|||
getRuleNodeComponents: getRuleNodeComponents, |
|||
getRuleNodeComponentByClazz: getRuleNodeComponentByClazz, |
|||
getRuleNodeSupportedLinks: getRuleNodeSupportedLinks, |
|||
ruleNodeAllowCustomLinks: ruleNodeAllowCustomLinks, |
|||
resolveTargetRuleChains: resolveTargetRuleChains, |
|||
testScript: testScript, |
|||
getLatestRuleNodeDebugInput: getLatestRuleNodeDebugInput |
|||
}; |
|||
|
|||
return service; |
|||
|
|||
function getRuleChains (pageLink, config) { |
|||
var deferred = $q.defer(); |
|||
var url = '/api/ruleChains?limit=' + pageLink.limit; |
|||
if (angular.isDefined(pageLink.textSearch)) { |
|||
url += '&textSearch=' + pageLink.textSearch; |
|||
} |
|||
if (angular.isDefined(pageLink.idOffset)) { |
|||
url += '&idOffset=' + pageLink.idOffset; |
|||
} |
|||
if (angular.isDefined(pageLink.textOffset)) { |
|||
url += '&textOffset=' + pageLink.textOffset; |
|||
} |
|||
$http.get(url, config).then(function success(response) { |
|||
deferred.resolve(response.data); |
|||
}, function fail() { |
|||
deferred.reject(); |
|||
}); |
|||
return deferred.promise; |
|||
} |
|||
|
|||
function getRuleChain(ruleChainId, config) { |
|||
var deferred = $q.defer(); |
|||
var url = '/api/ruleChain/' + ruleChainId; |
|||
$http.get(url, config).then(function success(response) { |
|||
deferred.resolve(response.data); |
|||
}, function fail() { |
|||
deferred.reject(); |
|||
}); |
|||
return deferred.promise; |
|||
} |
|||
|
|||
function saveRuleChain(ruleChain) { |
|||
var deferred = $q.defer(); |
|||
var url = '/api/ruleChain'; |
|||
$http.post(url, ruleChain).then(function success(response) { |
|||
deferred.resolve(response.data); |
|||
}, function fail() { |
|||
deferred.reject(); |
|||
}); |
|||
return deferred.promise; |
|||
} |
|||
|
|||
function setRootRuleChain(ruleChainId) { |
|||
var deferred = $q.defer(); |
|||
var url = '/api/ruleChain/' + ruleChainId + '/root'; |
|||
$http.post(url).then(function success(response) { |
|||
deferred.resolve(response.data); |
|||
}, function fail() { |
|||
deferred.reject(); |
|||
}); |
|||
return deferred.promise; |
|||
} |
|||
|
|||
function deleteRuleChain(ruleChainId) { |
|||
var deferred = $q.defer(); |
|||
var url = '/api/ruleChain/' + ruleChainId; |
|||
$http.delete(url).then(function success() { |
|||
deferred.resolve(); |
|||
}, function fail() { |
|||
deferred.reject(); |
|||
}); |
|||
return deferred.promise; |
|||
} |
|||
|
|||
function getRuleChainMetaData(ruleChainId, config) { |
|||
var deferred = $q.defer(); |
|||
var url = '/api/ruleChain/' + ruleChainId + '/metadata'; |
|||
$http.get(url, config).then(function success(response) { |
|||
deferred.resolve(response.data); |
|||
}, function fail() { |
|||
deferred.reject(); |
|||
}); |
|||
return deferred.promise; |
|||
} |
|||
|
|||
function saveRuleChainMetaData(ruleChainMetaData) { |
|||
var deferred = $q.defer(); |
|||
var url = '/api/ruleChain/metadata'; |
|||
$http.post(url, ruleChainMetaData).then(function success(response) { |
|||
deferred.resolve(response.data); |
|||
}, function fail() { |
|||
deferred.reject(); |
|||
}); |
|||
return deferred.promise; |
|||
} |
|||
|
|||
function getRuleNodeSupportedLinks(component) { |
|||
var relationTypes = component.configurationDescriptor.nodeDefinition.relationTypes; |
|||
var linkLabels = {}; |
|||
for (var i=0;i<relationTypes.length;i++) { |
|||
var label = relationTypes[i]; |
|||
linkLabels[label] = { |
|||
name: label, |
|||
value: label |
|||
}; |
|||
} |
|||
return linkLabels; |
|||
} |
|||
|
|||
function ruleNodeAllowCustomLinks(component) { |
|||
return component.configurationDescriptor.nodeDefinition.customRelations; |
|||
} |
|||
|
|||
function getRuleNodeComponents() { |
|||
var deferred = $q.defer(); |
|||
if (ruleNodeComponents) { |
|||
deferred.resolve(ruleNodeComponents); |
|||
} else { |
|||
loadRuleNodeComponents().then( |
|||
(components) => { |
|||
resolveRuleNodeComponentsUiResources(components).then( |
|||
(components) => { |
|||
ruleNodeComponents = components; |
|||
ruleNodeComponents.push( |
|||
types.ruleChainNodeComponent |
|||
); |
|||
ruleNodeComponents.sort( |
|||
(comp1, comp2) => { |
|||
var result = comp1.type.localeCompare(comp2.type); |
|||
if (result == 0) { |
|||
result = comp1.name.localeCompare(comp2.name); |
|||
} |
|||
return result; |
|||
} |
|||
); |
|||
deferred.resolve(ruleNodeComponents); |
|||
}, |
|||
() => { |
|||
deferred.reject(); |
|||
} |
|||
); |
|||
}, |
|||
() => { |
|||
deferred.reject(); |
|||
} |
|||
); |
|||
} |
|||
return deferred.promise; |
|||
} |
|||
|
|||
function resolveRuleNodeComponentsUiResources(components) { |
|||
var deferred = $q.defer(); |
|||
var tasks = []; |
|||
for (var i=0;i<components.length;i++) { |
|||
var component = components[i]; |
|||
tasks.push(resolveRuleNodeComponentUiResources(component)); |
|||
} |
|||
$q.all(tasks).then( |
|||
(components) => { |
|||
deferred.resolve(components); |
|||
}, |
|||
() => { |
|||
deferred.resolve(components); |
|||
} |
|||
); |
|||
return deferred.promise; |
|||
} |
|||
|
|||
function resolveRuleNodeComponentUiResources(component) { |
|||
var deferred = $q.defer(); |
|||
var uiResources = component.configurationDescriptor.nodeDefinition.uiResources; |
|||
if (uiResources && uiResources.length) { |
|||
var tasks = []; |
|||
for (var i=0;i<uiResources.length;i++) { |
|||
var uiResource = uiResources[i]; |
|||
tasks.push($ocLazyLoad.load(uiResource)); |
|||
} |
|||
$q.all(tasks).then( |
|||
() => { |
|||
deferred.resolve(component); |
|||
}, |
|||
() => { |
|||
component.configurationDescriptor.nodeDefinition.uiResourceLoadError = $translate.instant('rulenode.ui-resources-load-error'); |
|||
deferred.resolve(component); |
|||
} |
|||
) |
|||
} else { |
|||
deferred.resolve(component); |
|||
} |
|||
return deferred.promise; |
|||
} |
|||
|
|||
function getRuleNodeComponentByClazz(clazz) { |
|||
var res = $filter('filter')(ruleNodeComponents, {clazz: clazz}, true); |
|||
if (res && res.length) { |
|||
return res[0]; |
|||
} |
|||
var unknownComponent = angular.copy(types.unknownNodeComponent); |
|||
unknownComponent.clazz = clazz; |
|||
unknownComponent.configurationDescriptor.nodeDefinition.details = "Unknown Rule Node class: " + clazz; |
|||
return unknownComponent; |
|||
} |
|||
|
|||
function resolveTargetRuleChains(ruleChainConnections) { |
|||
var deferred = $q.defer(); |
|||
if (ruleChainConnections && ruleChainConnections.length) { |
|||
var tasks = []; |
|||
for (var i = 0; i < ruleChainConnections.length; i++) { |
|||
tasks.push(resolveRuleChain(ruleChainConnections[i].targetRuleChainId.id)); |
|||
} |
|||
$q.all(tasks).then( |
|||
(ruleChains) => { |
|||
var ruleChainsMap = {}; |
|||
for (var i = 0; i < ruleChains.length; i++) { |
|||
ruleChainsMap[ruleChains[i].id.id] = ruleChains[i]; |
|||
} |
|||
deferred.resolve(ruleChainsMap); |
|||
}, |
|||
() => { |
|||
deferred.reject(); |
|||
} |
|||
); |
|||
} else { |
|||
deferred.resolve({}); |
|||
} |
|||
return deferred.promise; |
|||
} |
|||
|
|||
function resolveRuleChain(ruleChainId) { |
|||
var deferred = $q.defer(); |
|||
getRuleChain(ruleChainId, {ignoreErrors: true}).then( |
|||
(ruleChain) => { |
|||
deferred.resolve(ruleChain); |
|||
}, |
|||
() => { |
|||
deferred.resolve({ |
|||
id: {id: ruleChainId, entityType: types.entityType.rulechain} |
|||
}); |
|||
} |
|||
); |
|||
return deferred.promise; |
|||
} |
|||
|
|||
function loadRuleNodeComponents() { |
|||
return componentDescriptorService.getComponentDescriptorsByTypes(types.ruleNodeTypeComponentTypes); |
|||
} |
|||
|
|||
function testScript(inputParams) { |
|||
var deferred = $q.defer(); |
|||
var url = '/api/ruleChain/testScript'; |
|||
$http.post(url, inputParams).then(function success(response) { |
|||
deferred.resolve(response.data); |
|||
}, function fail() { |
|||
deferred.reject(); |
|||
}); |
|||
return deferred.promise; |
|||
} |
|||
|
|||
function getLatestRuleNodeDebugInput(ruleNodeId) { |
|||
var deferred = $q.defer(); |
|||
var url = '/api/ruleNode/' + ruleNodeId + '/debugIn'; |
|||
$http.get(url).then(function success(response) { |
|||
deferred.resolve(response.data); |
|||
}, function fail() { |
|||
deferred.reject(); |
|||
}); |
|||
return deferred.promise; |
|||
} |
|||
|
|||
} |
|||
File diff suppressed because it is too large
@ -1,362 +0,0 @@ |
|||
/* |
|||
* Copyright © 2016-2020 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
import 'angular-websocket'; |
|||
import thingsboardTypes from '../common/types.constant'; |
|||
|
|||
export default angular.module('thingsboard.api.telemetryWebsocket', [thingsboardTypes]) |
|||
.factory('telemetryWebsocketService', TelemetryWebsocketService) |
|||
.name; |
|||
|
|||
const RECONNECT_INTERVAL = 2000; |
|||
const WS_IDLE_TIMEOUT = 90000; |
|||
|
|||
const MAX_PUBLISH_COMMANDS = 10; |
|||
|
|||
/*@ngInject*/ |
|||
function TelemetryWebsocketService($rootScope, $websocket, $timeout, $window, $mdUtil, $log, toast, types, userService) { |
|||
|
|||
var isOpening = false, |
|||
isOpened = false, |
|||
isActive = false, |
|||
isReconnect = false, |
|||
reconnectSubscribers = [], |
|||
lastCmdId = 0, |
|||
subscribers = {}, |
|||
subscribersCount = 0, |
|||
commands = {}, |
|||
cmdsWrapper = { |
|||
tsSubCmds: [], |
|||
historyCmds: [], |
|||
attrSubCmds: [] |
|||
}, |
|||
telemetryUri, |
|||
dataStream, |
|||
location = $window.location, |
|||
socketCloseTimer, |
|||
reconnectTimer; |
|||
|
|||
var port = location.port; |
|||
if (location.protocol === "https:") { |
|||
if (!port) { |
|||
port = "443"; |
|||
} |
|||
telemetryUri = "wss:"; |
|||
} else { |
|||
if (!port) { |
|||
port = "80"; |
|||
} |
|||
telemetryUri = "ws:"; |
|||
} |
|||
telemetryUri += "//" + location.hostname + ":" + port; |
|||
telemetryUri += "/api/ws/plugins/telemetry"; |
|||
|
|||
var service = { |
|||
subscribe: subscribe, |
|||
unsubscribe: unsubscribe |
|||
} |
|||
|
|||
$rootScope.telemetryWsLogoutHandle = $rootScope.$on('unauthenticated', function (event, doLogout) { |
|||
if (doLogout) { |
|||
reset(true); |
|||
} |
|||
}); |
|||
|
|||
$rootScope.telemetryWsLoginHandle = $rootScope.$on('authenticated', function () { |
|||
reset(true); |
|||
}); |
|||
|
|||
return service; |
|||
|
|||
function publishCommands () { |
|||
while(isOpened && hasCommands()) { |
|||
dataStream.send(preparePublishCommands()).then(function () { |
|||
checkToClose(); |
|||
}); |
|||
} |
|||
tryOpenSocket(); |
|||
} |
|||
|
|||
function hasCommands() { |
|||
return cmdsWrapper.tsSubCmds.length > 0 || |
|||
cmdsWrapper.historyCmds.length > 0 || |
|||
cmdsWrapper.attrSubCmds.length > 0; |
|||
} |
|||
|
|||
function preparePublishCommands() { |
|||
var preparedWrapper = {}; |
|||
var leftCount = MAX_PUBLISH_COMMANDS; |
|||
preparedWrapper.tsSubCmds = popCmds(cmdsWrapper.tsSubCmds, leftCount); |
|||
leftCount -= preparedWrapper.tsSubCmds.length; |
|||
preparedWrapper.historyCmds = popCmds(cmdsWrapper.historyCmds, leftCount); |
|||
leftCount -= preparedWrapper.historyCmds.length; |
|||
preparedWrapper.attrSubCmds = popCmds(cmdsWrapper.attrSubCmds, leftCount); |
|||
return preparedWrapper; |
|||
} |
|||
|
|||
function popCmds(cmds, leftCount) { |
|||
var toPublish = Math.min(cmds.length, leftCount); |
|||
if (toPublish > 0) { |
|||
return cmds.splice(0, toPublish); |
|||
} else { |
|||
return []; |
|||
} |
|||
} |
|||
|
|||
function onError (errorEvent) { |
|||
if (errorEvent) { |
|||
//showWsError(0, errorEvent);
|
|||
$log.warn('WebSocket error event', errorEvent); |
|||
} |
|||
isOpening = false; |
|||
} |
|||
|
|||
function onOpen () { |
|||
isOpening = false; |
|||
isOpened = true; |
|||
if (reconnectTimer) { |
|||
$timeout.cancel(reconnectTimer); |
|||
reconnectTimer = null; |
|||
} |
|||
if (isReconnect) { |
|||
isReconnect = false; |
|||
for (var r=0; r<reconnectSubscribers.length;r++) { |
|||
var reconnectSubscriber = reconnectSubscribers[r]; |
|||
if (reconnectSubscriber.onReconnected) { |
|||
reconnectSubscriber.onReconnected(); |
|||
} |
|||
subscribe(reconnectSubscriber); |
|||
} |
|||
reconnectSubscribers = []; |
|||
} else { |
|||
publishCommands(); |
|||
} |
|||
} |
|||
|
|||
function onClose (closeEvent) { |
|||
if (closeEvent && closeEvent.code > 1000 && closeEvent.code !== 1006) { |
|||
showWsError(closeEvent.code, closeEvent.reason); |
|||
} |
|||
isOpening = false; |
|||
isOpened = false; |
|||
if (isActive) { |
|||
if (!isReconnect) { |
|||
reconnectSubscribers = []; |
|||
for (var id in subscribers) { |
|||
var subscriber = subscribers[id]; |
|||
if (reconnectSubscribers.indexOf(subscriber) === -1) { |
|||
reconnectSubscribers.push(subscriber); |
|||
} |
|||
} |
|||
reset(false); |
|||
isReconnect = true; |
|||
} |
|||
if (reconnectTimer) { |
|||
$timeout.cancel(reconnectTimer); |
|||
} |
|||
reconnectTimer = $timeout(tryOpenSocket, RECONNECT_INTERVAL, false); |
|||
} |
|||
} |
|||
|
|||
function onMessage (message) { |
|||
if (message.data) { |
|||
var data = angular.fromJson(message.data); |
|||
if (data.errorCode) { |
|||
showWsError(data.errorCode, data.errorMsg); |
|||
} else if (data.subscriptionId) { |
|||
var subscriber = subscribers[data.subscriptionId]; |
|||
if (subscriber && data) { |
|||
var keys = fetchKeys(data.subscriptionId); |
|||
if (!data.data) { |
|||
data.data = {}; |
|||
} |
|||
for (var k = 0; k < keys.length; k++) { |
|||
var key = keys[k]; |
|||
if (!data.data[key]) { |
|||
data.data[key] = []; |
|||
} |
|||
} |
|||
subscriber.onData(data, data.subscriptionId); |
|||
} |
|||
} |
|||
} |
|||
checkToClose(); |
|||
} |
|||
|
|||
function showWsError(errorCode, errorMsg) { |
|||
var message = 'WebSocket Error: '; |
|||
if (errorMsg) { |
|||
message += errorMsg; |
|||
} else { |
|||
message += "error code - " + errorCode + "."; |
|||
} |
|||
$mdUtil.nextTick(function () { |
|||
toast.showError(message); |
|||
}); |
|||
} |
|||
|
|||
function fetchKeys(subscriptionId) { |
|||
var command = commands[subscriptionId]; |
|||
if (command && command.keys && command.keys.length > 0) { |
|||
return command.keys.split(","); |
|||
} else { |
|||
return []; |
|||
} |
|||
} |
|||
|
|||
function nextCmdId () { |
|||
lastCmdId++; |
|||
return lastCmdId; |
|||
} |
|||
|
|||
function subscribe (subscriber) { |
|||
isActive = true; |
|||
var cmdId; |
|||
if (angular.isDefined(subscriber.subscriptionCommands)) { |
|||
for (var i=0;i<subscriber.subscriptionCommands.length;i++) { |
|||
var subscriptionCommand = subscriber.subscriptionCommands[i]; |
|||
cmdId = nextCmdId(); |
|||
subscribers[cmdId] = subscriber; |
|||
subscriptionCommand.cmdId = cmdId; |
|||
commands[cmdId] = subscriptionCommand; |
|||
if (subscriber.type === types.dataKeyType.timeseries) { |
|||
cmdsWrapper.tsSubCmds.push(subscriptionCommand); |
|||
} else if (subscriber.type === types.dataKeyType.attribute) { |
|||
cmdsWrapper.attrSubCmds.push(subscriptionCommand); |
|||
} |
|||
} |
|||
} |
|||
if (angular.isDefined(subscriber.historyCommands)) { |
|||
for (i=0;i<subscriber.historyCommands.length;i++) { |
|||
var historyCommand = subscriber.historyCommands[i]; |
|||
cmdId = nextCmdId(); |
|||
subscribers[cmdId] = subscriber; |
|||
historyCommand.cmdId = cmdId; |
|||
commands[cmdId] = historyCommand; |
|||
cmdsWrapper.historyCmds.push(historyCommand); |
|||
} |
|||
} |
|||
subscribersCount++; |
|||
publishCommands(); |
|||
} |
|||
|
|||
function unsubscribe (subscriber) { |
|||
if (isActive) { |
|||
var cmdId = null; |
|||
if (subscriber.subscriptionCommands) { |
|||
for (var i=0;i<subscriber.subscriptionCommands.length;i++) { |
|||
var subscriptionCommand = subscriber.subscriptionCommands[i]; |
|||
subscriptionCommand.unsubscribe = true; |
|||
if (subscriber.type === types.dataKeyType.timeseries) { |
|||
cmdsWrapper.tsSubCmds.push(subscriptionCommand); |
|||
} else if (subscriber.type === types.dataKeyType.attribute) { |
|||
cmdsWrapper.attrSubCmds.push(subscriptionCommand); |
|||
} |
|||
cmdId = subscriptionCommand.cmdId; |
|||
if (cmdId) { |
|||
if (subscribers[cmdId]) { |
|||
delete subscribers[cmdId]; |
|||
} |
|||
if (commands[cmdId]) { |
|||
delete commands[cmdId]; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
if (subscriber.historyCommands) { |
|||
for (i=0;i<subscriber.historyCommands.length;i++) { |
|||
var historyCommand = subscriber.historyCommands[i]; |
|||
cmdId = historyCommand.cmdId; |
|||
if (cmdId) { |
|||
if (subscribers[cmdId]) { |
|||
delete subscribers[cmdId]; |
|||
} |
|||
if (commands[cmdId]) { |
|||
delete commands[cmdId]; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
var index = reconnectSubscribers.indexOf(subscriber); |
|||
if (index > -1) { |
|||
reconnectSubscribers.splice(index, 1); |
|||
} |
|||
subscribersCount--; |
|||
publishCommands(); |
|||
} |
|||
} |
|||
|
|||
function checkToClose () { |
|||
if (subscribersCount === 0 && isOpened) { |
|||
if (!socketCloseTimer) { |
|||
socketCloseTimer = $timeout(closeSocket, WS_IDLE_TIMEOUT, false); |
|||
} |
|||
} |
|||
} |
|||
|
|||
function tryOpenSocket () { |
|||
if (isActive) { |
|||
if (!isOpened && !isOpening) { |
|||
isOpening = true; |
|||
if (userService.isJwtTokenValid()) { |
|||
openSocket(userService.getJwtToken()); |
|||
} else { |
|||
userService.refreshJwtToken().then(function success() { |
|||
openSocket(userService.getJwtToken()); |
|||
}, function fail() { |
|||
isOpening = false; |
|||
$rootScope.$broadcast('unauthenticated'); |
|||
}); |
|||
} |
|||
} |
|||
if (socketCloseTimer) { |
|||
$timeout.cancel(socketCloseTimer); |
|||
socketCloseTimer = null; |
|||
} |
|||
} |
|||
} |
|||
|
|||
function openSocket(token) { |
|||
dataStream = $websocket(telemetryUri + '?token=' + token); |
|||
dataStream.onError(onError); |
|||
dataStream.onOpen(onOpen); |
|||
dataStream.onClose(onClose); |
|||
dataStream.onMessage(onMessage, {autoApply: false}); |
|||
} |
|||
|
|||
function closeSocket() { |
|||
isActive = false; |
|||
if (isOpened) { |
|||
dataStream.close(); |
|||
} |
|||
} |
|||
|
|||
function reset(close) { |
|||
if (socketCloseTimer) { |
|||
$timeout.cancel(socketCloseTimer); |
|||
socketCloseTimer = null; |
|||
} |
|||
lastCmdId = 0; |
|||
subscribers = {}; |
|||
subscribersCount = 0; |
|||
commands = {}; |
|||
cmdsWrapper.tsSubCmds = []; |
|||
cmdsWrapper.historyCmds = []; |
|||
cmdsWrapper.attrSubCmds = []; |
|||
if (close) { |
|||
closeSocket(); |
|||
} |
|||
} |
|||
} |
|||
@ -1,85 +0,0 @@ |
|||
/* |
|||
* Copyright © 2016-2020 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
export default angular.module('thingsboard.api.tenant', []) |
|||
.factory('tenantService', TenantService) |
|||
.name; |
|||
|
|||
/*@ngInject*/ |
|||
function TenantService($http, $q) { |
|||
|
|||
var service = { |
|||
deleteTenant: deleteTenant, |
|||
getTenant: getTenant, |
|||
getTenants: getTenants, |
|||
saveTenant: saveTenant, |
|||
} |
|||
|
|||
return service; |
|||
|
|||
function getTenants (pageLink, config) { |
|||
var deferred = $q.defer(); |
|||
var url = '/api/tenants?limit=' + pageLink.limit; |
|||
if (angular.isDefined(pageLink.textSearch)) { |
|||
url += '&textSearch=' + pageLink.textSearch; |
|||
} |
|||
if (angular.isDefined(pageLink.idOffset)) { |
|||
url += '&idOffset=' + pageLink.idOffset; |
|||
} |
|||
if (angular.isDefined(pageLink.textOffset)) { |
|||
url += '&textOffset=' + pageLink.textOffset; |
|||
} |
|||
$http.get(url, config).then(function success(response) { |
|||
deferred.resolve(response.data); |
|||
}, function fail() { |
|||
deferred.reject(); |
|||
}); |
|||
return deferred.promise; |
|||
} |
|||
|
|||
function getTenant (tenantId, config) { |
|||
var deferred = $q.defer(); |
|||
var url = '/api/tenant/' + tenantId; |
|||
$http.get(url, config).then(function success(response) { |
|||
deferred.resolve(response.data); |
|||
}, function fail(response) { |
|||
deferred.reject(response.data); |
|||
}); |
|||
return deferred.promise; |
|||
} |
|||
|
|||
function saveTenant (tenant) { |
|||
var deferred = $q.defer(); |
|||
var url = '/api/tenant'; |
|||
$http.post(url, tenant).then(function success(response) { |
|||
deferred.resolve(response.data); |
|||
}, function fail(response) { |
|||
deferred.reject(response.data); |
|||
}); |
|||
return deferred.promise; |
|||
} |
|||
|
|||
function deleteTenant (tenantId) { |
|||
var deferred = $q.defer(); |
|||
var url = '/api/tenant/' + tenantId; |
|||
$http.delete(url).then(function success() { |
|||
deferred.resolve(); |
|||
}, function fail(response) { |
|||
deferred.reject(response.data); |
|||
}); |
|||
return deferred.promise; |
|||
} |
|||
|
|||
} |
|||
@ -1,413 +0,0 @@ |
|||
/* |
|||
* Copyright © 2016-2020 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
export default angular.module('thingsboard.api.time', []) |
|||
.factory('timeService', TimeService) |
|||
.name; |
|||
|
|||
const SECOND = 1000; |
|||
const MINUTE = 60 * SECOND; |
|||
const HOUR = 60 * MINUTE; |
|||
const DAY = 24 * HOUR; |
|||
|
|||
const MIN_INTERVAL = SECOND; |
|||
const MAX_INTERVAL = 365 * 20 * DAY; |
|||
|
|||
const MIN_LIMIT = 10; |
|||
//const AVG_LIMIT = 200;
|
|||
//const MAX_LIMIT = 500;
|
|||
|
|||
/*@ngInject*/ |
|||
function TimeService($translate, $http, $q, types) { |
|||
|
|||
var predefIntervals; |
|||
var maxDatapointsLimit; |
|||
|
|||
var service = { |
|||
loadMaxDatapointsLimit: loadMaxDatapointsLimit, |
|||
minIntervalLimit: minIntervalLimit, |
|||
maxIntervalLimit: maxIntervalLimit, |
|||
boundMinInterval: boundMinInterval, |
|||
boundMaxInterval: boundMaxInterval, |
|||
getIntervals: getIntervals, |
|||
matchesExistingInterval: matchesExistingInterval, |
|||
boundToPredefinedInterval: boundToPredefinedInterval, |
|||
defaultTimewindow: defaultTimewindow, |
|||
toHistoryTimewindow: toHistoryTimewindow, |
|||
createSubscriptionTimewindow: createSubscriptionTimewindow, |
|||
createTimewindowForComparison: createTimewindowForComparison, |
|||
getMaxDatapointsLimit: function () { |
|||
return maxDatapointsLimit; |
|||
}, |
|||
getMinDatapointsLimit: function () { |
|||
return MIN_LIMIT; |
|||
} |
|||
} |
|||
|
|||
return service; |
|||
|
|||
function loadMaxDatapointsLimit() { |
|||
var deferred = $q.defer(); |
|||
var url = '/api/dashboard/maxDatapointsLimit'; |
|||
$http.get(url, {ignoreLoading: true}).then(function success(response) { |
|||
maxDatapointsLimit = response.data; |
|||
if (!maxDatapointsLimit || maxDatapointsLimit <= MIN_LIMIT) { |
|||
maxDatapointsLimit = MIN_LIMIT + 1; |
|||
} |
|||
deferred.resolve(); |
|||
}, function fail() { |
|||
deferred.reject(); |
|||
}); |
|||
return deferred.promise; |
|||
} |
|||
|
|||
function minIntervalLimit(timewindow) { |
|||
var min = timewindow / 500; |
|||
return boundMinInterval(min); |
|||
} |
|||
|
|||
function avgInterval(timewindow) { |
|||
var avg = timewindow / 200; |
|||
return boundMinInterval(avg); |
|||
} |
|||
|
|||
function maxIntervalLimit(timewindow) { |
|||
var max = timewindow / MIN_LIMIT; |
|||
return boundMaxInterval(max); |
|||
} |
|||
|
|||
function boundMinInterval(min) { |
|||
return toBound(min, MIN_INTERVAL, MAX_INTERVAL, MIN_INTERVAL); |
|||
} |
|||
|
|||
function boundMaxInterval(max) { |
|||
return toBound(max, MIN_INTERVAL, MAX_INTERVAL, MAX_INTERVAL); |
|||
} |
|||
|
|||
function toBound(value, min, max, defValue) { |
|||
if (angular.isDefined(value)) { |
|||
value = Math.max(value, min); |
|||
value = Math.min(value, max); |
|||
return value; |
|||
} else { |
|||
return defValue; |
|||
} |
|||
} |
|||
|
|||
function getIntervals(min, max) { |
|||
min = boundMinInterval(min); |
|||
max = boundMaxInterval(max); |
|||
var intervals = []; |
|||
initPredefIntervals(); |
|||
for (var i in predefIntervals) { |
|||
var interval = predefIntervals[i]; |
|||
if (interval.value >= min && interval.value <= max) { |
|||
intervals.push(interval); |
|||
} |
|||
} |
|||
return intervals; |
|||
} |
|||
|
|||
function initPredefIntervals() { |
|||
if (!predefIntervals) { |
|||
predefIntervals = [ |
|||
{ |
|||
name: $translate.instant('timeinterval.seconds-interval', {seconds: 1}, 'messageformat'), |
|||
value: 1 * SECOND |
|||
}, |
|||
{ |
|||
name: $translate.instant('timeinterval.seconds-interval', {seconds: 5}, 'messageformat'), |
|||
value: 5 * SECOND |
|||
}, |
|||
{ |
|||
name: $translate.instant('timeinterval.seconds-interval', {seconds: 10}, 'messageformat'), |
|||
value: 10 * SECOND |
|||
}, |
|||
{ |
|||
name: $translate.instant('timeinterval.seconds-interval', {seconds: 15}, 'messageformat'), |
|||
value: 15 * SECOND |
|||
}, |
|||
{ |
|||
name: $translate.instant('timeinterval.seconds-interval', {seconds: 30}, 'messageformat'), |
|||
value: 30 * SECOND |
|||
}, |
|||
{ |
|||
name: $translate.instant('timeinterval.minutes-interval', {minutes: 1}, 'messageformat'), |
|||
value: 1 * MINUTE |
|||
}, |
|||
{ |
|||
name: $translate.instant('timeinterval.minutes-interval', {minutes: 2}, 'messageformat'), |
|||
value: 2 * MINUTE |
|||
}, |
|||
{ |
|||
name: $translate.instant('timeinterval.minutes-interval', {minutes: 5}, 'messageformat'), |
|||
value: 5 * MINUTE |
|||
}, |
|||
{ |
|||
name: $translate.instant('timeinterval.minutes-interval', {minutes: 10}, 'messageformat'), |
|||
value: 10 * MINUTE |
|||
}, |
|||
{ |
|||
name: $translate.instant('timeinterval.minutes-interval', {minutes: 15}, 'messageformat'), |
|||
value: 15 * MINUTE |
|||
}, |
|||
{ |
|||
name: $translate.instant('timeinterval.minutes-interval', {minutes: 30}, 'messageformat'), |
|||
value: 30 * MINUTE |
|||
}, |
|||
{ |
|||
name: $translate.instant('timeinterval.hours-interval', {hours: 1}, 'messageformat'), |
|||
value: 1 * HOUR |
|||
}, |
|||
{ |
|||
name: $translate.instant('timeinterval.hours-interval', {hours: 2}, 'messageformat'), |
|||
value: 2 * HOUR |
|||
}, |
|||
{ |
|||
name: $translate.instant('timeinterval.hours-interval', {hours: 5}, 'messageformat'), |
|||
value: 5 * HOUR |
|||
}, |
|||
{ |
|||
name: $translate.instant('timeinterval.hours-interval', {hours: 10}, 'messageformat'), |
|||
value: 10 * HOUR |
|||
}, |
|||
{ |
|||
name: $translate.instant('timeinterval.hours-interval', {hours: 12}, 'messageformat'), |
|||
value: 12 * HOUR |
|||
}, |
|||
{ |
|||
name: $translate.instant('timeinterval.days-interval', {days: 1}, 'messageformat'), |
|||
value: 1 * DAY |
|||
}, |
|||
{ |
|||
name: $translate.instant('timeinterval.days-interval', {days: 7}, 'messageformat'), |
|||
value: 7 * DAY |
|||
}, |
|||
{ |
|||
name: $translate.instant('timeinterval.days-interval', {days: 30}, 'messageformat'), |
|||
value: 30 * DAY |
|||
} |
|||
]; |
|||
} |
|||
} |
|||
|
|||
function matchesExistingInterval(min, max, intervalMs) { |
|||
var intervals = getIntervals(min, max); |
|||
for (var i in intervals) { |
|||
var interval = intervals[i]; |
|||
if (intervalMs === interval.value) { |
|||
return true; |
|||
} |
|||
} |
|||
return false; |
|||
} |
|||
|
|||
function boundToPredefinedInterval(min, max, intervalMs) { |
|||
var intervals = getIntervals(min, max); |
|||
var minDelta = MAX_INTERVAL; |
|||
var boundedInterval = intervalMs || min; |
|||
var matchedInterval; |
|||
for (var i in intervals) { |
|||
var interval = intervals[i]; |
|||
var delta = Math.abs(interval.value - boundedInterval); |
|||
if (delta < minDelta) { |
|||
matchedInterval = interval; |
|||
minDelta = delta; |
|||
} |
|||
} |
|||
boundedInterval = matchedInterval.value; |
|||
return boundedInterval; |
|||
} |
|||
|
|||
function defaultTimewindow() { |
|||
var currentTime = (new Date).getTime(); |
|||
var timewindow = { |
|||
displayValue: "", |
|||
selectedTab: 0, |
|||
hideInterval: false, |
|||
hideAggregation: false, |
|||
hideAggInterval: false, |
|||
realtime: { |
|||
interval: SECOND, |
|||
timewindowMs: MINUTE // 1 min by default
|
|||
}, |
|||
history: { |
|||
historyType: 0, |
|||
interval: SECOND, |
|||
timewindowMs: MINUTE, // 1 min by default
|
|||
fixedTimewindow: { |
|||
startTimeMs: currentTime - DAY, // 1 day by default
|
|||
endTimeMs: currentTime |
|||
} |
|||
}, |
|||
aggregation: { |
|||
type: types.aggregation.avg.value, |
|||
limit: Math.floor(maxDatapointsLimit / 2) |
|||
} |
|||
} |
|||
return timewindow; |
|||
} |
|||
|
|||
function toHistoryTimewindow(timewindow, startTimeMs, endTimeMs, interval) { |
|||
if (timewindow.history) { |
|||
interval = angular.isDefined(interval) ? interval : timewindow.history.interval; |
|||
} else if (timewindow.realtime) { |
|||
interval = timewindow.realtime.interval; |
|||
} else { |
|||
interval = 0; |
|||
} |
|||
|
|||
var aggType; |
|||
var limit; |
|||
if (timewindow.aggregation) { |
|||
aggType = timewindow.aggregation.type || types.aggregation.avg.value; |
|||
limit = timewindow.aggregation.limit || maxDatapointsLimit; |
|||
} else { |
|||
aggType = types.aggregation.avg.value; |
|||
limit = maxDatapointsLimit; |
|||
} |
|||
|
|||
|
|||
var historyTimewindow = { |
|||
hideInterval: timewindow.hideInterval || false, |
|||
hideAggregation: timewindow.hideAggregation || false, |
|||
hideAggInterval: timewindow.hideAggInterval || false, |
|||
history: { |
|||
fixedTimewindow: { |
|||
startTimeMs: startTimeMs, |
|||
endTimeMs: endTimeMs |
|||
}, |
|||
interval: boundIntervalToTimewindow(endTimeMs - startTimeMs, interval, types.aggregation.avg.value) |
|||
}, |
|||
aggregation: { |
|||
type: aggType, |
|||
limit: limit |
|||
} |
|||
} |
|||
|
|||
return historyTimewindow; |
|||
} |
|||
|
|||
function createSubscriptionTimewindow(timewindow, stDiff, stateData) { |
|||
|
|||
var subscriptionTimewindow = { |
|||
fixedWindow: null, |
|||
realtimeWindowMs: null, |
|||
aggregation: { |
|||
interval: SECOND, |
|||
limit: maxDatapointsLimit, |
|||
type: types.aggregation.avg.value |
|||
} |
|||
}; |
|||
var aggTimewindow = 0; |
|||
if (stateData) { |
|||
subscriptionTimewindow.aggregation = { |
|||
interval: SECOND, |
|||
limit: maxDatapointsLimit, |
|||
type: types.aggregation.none.value, |
|||
stateData: true |
|||
}; |
|||
} else { |
|||
subscriptionTimewindow.aggregation = { |
|||
interval: SECOND, |
|||
limit: maxDatapointsLimit, |
|||
type: types.aggregation.avg.value |
|||
}; |
|||
} |
|||
|
|||
if (angular.isDefined(timewindow.aggregation) && !stateData) { |
|||
subscriptionTimewindow.aggregation = { |
|||
type: timewindow.aggregation.type || types.aggregation.avg.value, |
|||
limit: timewindow.aggregation.limit || maxDatapointsLimit |
|||
}; |
|||
} |
|||
if (angular.isDefined(timewindow.realtime)) { |
|||
subscriptionTimewindow.realtimeWindowMs = timewindow.realtime.timewindowMs; |
|||
subscriptionTimewindow.aggregation.interval = |
|||
boundIntervalToTimewindow(subscriptionTimewindow.realtimeWindowMs, timewindow.realtime.interval, |
|||
subscriptionTimewindow.aggregation.type); |
|||
subscriptionTimewindow.startTs = (new Date).getTime() + stDiff - subscriptionTimewindow.realtimeWindowMs; |
|||
var startDiff = subscriptionTimewindow.startTs % subscriptionTimewindow.aggregation.interval; |
|||
aggTimewindow = subscriptionTimewindow.realtimeWindowMs; |
|||
if (startDiff) { |
|||
subscriptionTimewindow.startTs -= startDiff; |
|||
aggTimewindow += subscriptionTimewindow.aggregation.interval; |
|||
} |
|||
} else if (angular.isDefined(timewindow.history)) { |
|||
if (angular.isDefined(timewindow.history.timewindowMs)) { |
|||
var currentTime = (new Date).getTime(); |
|||
subscriptionTimewindow.fixedWindow = { |
|||
startTimeMs: currentTime - timewindow.history.timewindowMs, |
|||
endTimeMs: currentTime |
|||
} |
|||
aggTimewindow = timewindow.history.timewindowMs; |
|||
|
|||
} else { |
|||
subscriptionTimewindow.fixedWindow = { |
|||
startTimeMs: timewindow.history.fixedTimewindow.startTimeMs, |
|||
endTimeMs: timewindow.history.fixedTimewindow.endTimeMs |
|||
} |
|||
aggTimewindow = subscriptionTimewindow.fixedWindow.endTimeMs - subscriptionTimewindow.fixedWindow.startTimeMs; |
|||
} |
|||
subscriptionTimewindow.startTs = subscriptionTimewindow.fixedWindow.startTimeMs; |
|||
subscriptionTimewindow.aggregation.interval = |
|||
boundIntervalToTimewindow(aggTimewindow, timewindow.history.interval, subscriptionTimewindow.aggregation.type); |
|||
} |
|||
var aggregation = subscriptionTimewindow.aggregation; |
|||
aggregation.timeWindow = aggTimewindow; |
|||
if (aggregation.type !== types.aggregation.none.value) { |
|||
aggregation.limit = Math.ceil(aggTimewindow / subscriptionTimewindow.aggregation.interval); |
|||
} |
|||
return subscriptionTimewindow; |
|||
} |
|||
|
|||
function boundIntervalToTimewindow(timewindow, intervalMs, aggType) { |
|||
if (aggType === types.aggregation.none.value) { |
|||
return SECOND; |
|||
} else { |
|||
var min = minIntervalLimit(timewindow); |
|||
var max = maxIntervalLimit(timewindow); |
|||
if (intervalMs) { |
|||
return toBound(intervalMs, min, max, intervalMs); |
|||
} else { |
|||
return boundToPredefinedInterval(min, max, avgInterval(timewindow)); |
|||
} |
|||
} |
|||
} |
|||
|
|||
function createTimewindowForComparison(subscriptionTimewindow, timeUnit) { |
|||
var timewindowForComparison = { |
|||
fixedWindow: null, |
|||
realtimeWindowMs: null, |
|||
aggregation: subscriptionTimewindow.aggregation |
|||
}; |
|||
|
|||
if (subscriptionTimewindow.realtimeWindowMs) { |
|||
timewindowForComparison.startTs = moment(subscriptionTimewindow.startTs).subtract(1, timeUnit).valueOf(); //eslint-disable-line
|
|||
timewindowForComparison.realtimeWindowMs = subscriptionTimewindow.realtimeWindowMs; |
|||
} else if (subscriptionTimewindow.fixedWindow) { |
|||
var timeInterval = subscriptionTimewindow.fixedWindow.endTimeMs - subscriptionTimewindow.fixedWindow.startTimeMs; |
|||
var endTimeMs = moment(subscriptionTimewindow.fixedWindow.endTimeMs).subtract(1, timeUnit).valueOf(); //eslint-disable-line
|
|||
|
|||
timewindowForComparison.startTs = endTimeMs - timeInterval; |
|||
timewindowForComparison.fixedWindow = { |
|||
startTimeMs: timewindowForComparison.startTs, |
|||
endTimeMs: endTimeMs |
|||
}; |
|||
} |
|||
|
|||
return timewindowForComparison; |
|||
} |
|||
} |
|||
@ -1,688 +0,0 @@ |
|||
/* |
|||
* Copyright © 2016-2020 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
import thingsboardApiLogin from './login.service'; |
|||
import angularStorage from 'angular-storage'; |
|||
|
|||
export default angular.module('thingsboard.api.user', [thingsboardApiLogin, |
|||
angularStorage]) |
|||
.factory('userService', UserService) |
|||
.name; |
|||
|
|||
/*@ngInject*/ |
|||
function UserService($http, $q, $rootScope, adminService, dashboardService, timeService, loginService, toast, store, jwtHelper, $translate, $state, $location, $mdDialog) { |
|||
var currentUser = null, |
|||
currentUserDetails = null, |
|||
lastPublicDashboardId = null, |
|||
allowedDashboardIds = [], |
|||
redirectParams = null, |
|||
userTokenAccessEnabled = false, |
|||
userLoaded = false; |
|||
|
|||
var refreshTokenQueue = []; |
|||
|
|||
var service = { |
|||
deleteUser: deleteUser, |
|||
getAuthority: getAuthority, |
|||
isPublic: isPublic, |
|||
getPublicId: getPublicId, |
|||
parsePublicId: parsePublicId, |
|||
isAuthenticated: isAuthenticated, |
|||
getCurrentUser: getCurrentUser, |
|||
getCustomerUsers: getCustomerUsers, |
|||
getUser: getUser, |
|||
getTenantAdmins: getTenantAdmins, |
|||
isUserLoaded: isUserLoaded, |
|||
saveUser: saveUser, |
|||
sendActivationEmail: sendActivationEmail, |
|||
getActivationLink: getActivationLink, |
|||
setUserFromJwtToken: setUserFromJwtToken, |
|||
getJwtToken: getJwtToken, |
|||
clearJwtToken: clearJwtToken, |
|||
isJwtTokenValid : isJwtTokenValid, |
|||
validateJwtToken: validateJwtToken, |
|||
refreshJwtToken: refreshJwtToken, |
|||
refreshTokenPending: refreshTokenPending, |
|||
updateAuthorizationHeader: updateAuthorizationHeader, |
|||
setAuthorizationRequestHeader: setAuthorizationRequestHeader, |
|||
setRedirectParams: setRedirectParams, |
|||
gotoDefaultPlace: gotoDefaultPlace, |
|||
forceDefaultPlace: forceDefaultPlace, |
|||
updateLastPublicDashboardId: updateLastPublicDashboardId, |
|||
logout: logout, |
|||
reloadUser: reloadUser, |
|||
isUserTokenAccessEnabled: isUserTokenAccessEnabled, |
|||
loginAsUser: loginAsUser, |
|||
setUserCredentialsEnabled: setUserCredentialsEnabled |
|||
} |
|||
|
|||
reloadUser(); |
|||
|
|||
return service; |
|||
|
|||
function reloadUser() { |
|||
userLoaded = false; |
|||
loadUser(true).then(function success() { |
|||
notifyUserLoaded(); |
|||
}, function fail() { |
|||
notifyUserLoaded(); |
|||
}); |
|||
} |
|||
|
|||
function updateAndValidateToken(token, prefix, notify) { |
|||
var valid = false; |
|||
var tokenData = jwtHelper.decodeToken(token); |
|||
var issuedAt = tokenData.iat; |
|||
var expTime = tokenData.exp; |
|||
if (issuedAt && expTime) { |
|||
var ttl = expTime - issuedAt; |
|||
if (ttl > 0) { |
|||
var clientExpiration = new Date().valueOf() + ttl*1000; |
|||
store.set(prefix, token); |
|||
store.set(prefix + '_expiration', clientExpiration); |
|||
valid = true; |
|||
} |
|||
} |
|||
if (!valid && notify) { |
|||
$rootScope.$broadcast('unauthenticated'); |
|||
} |
|||
} |
|||
|
|||
function clearTokenData() { |
|||
store.remove('jwt_token'); |
|||
store.remove('jwt_token_expiration'); |
|||
store.remove('refresh_token'); |
|||
store.remove('refresh_token_expiration'); |
|||
} |
|||
|
|||
function setUserFromJwtToken(jwtToken, refreshToken, notify, doLogout) { |
|||
currentUser = null; |
|||
currentUserDetails = null; |
|||
lastPublicDashboardId = null; |
|||
userTokenAccessEnabled = false; |
|||
allowedDashboardIds = []; |
|||
if (!jwtToken) { |
|||
clearTokenData(); |
|||
if (notify) { |
|||
$rootScope.$broadcast('unauthenticated', doLogout); |
|||
} |
|||
} else { |
|||
updateAndValidateToken(jwtToken, 'jwt_token', true); |
|||
updateAndValidateToken(refreshToken, 'refresh_token', true); |
|||
if (notify) { |
|||
loadUser(false).then(function success() { |
|||
$rootScope.$broadcast('authenticated'); |
|||
}, function fail() { |
|||
$rootScope.$broadcast('unauthenticated'); |
|||
}); |
|||
} else { |
|||
loadUser(false); |
|||
} |
|||
} |
|||
} |
|||
|
|||
function isAuthenticated() { |
|||
return store.get('jwt_token'); |
|||
} |
|||
|
|||
function getJwtToken() { |
|||
return store.get('jwt_token'); |
|||
} |
|||
|
|||
function logout() { |
|||
$http.post('/api/auth/logout', null, {ignoreErrors: true}).then(function success() { |
|||
clearJwtToken(true); |
|||
}, function fail() { |
|||
clearJwtToken(true); |
|||
}); |
|||
} |
|||
|
|||
function clearJwtToken(doLogout) { |
|||
setUserFromJwtToken(null, null, true, doLogout); |
|||
} |
|||
|
|||
function isJwtTokenValid() { |
|||
return isTokenValid('jwt_token'); |
|||
} |
|||
|
|||
function isTokenValid(prefix) { |
|||
var clientExpiration = store.get(prefix + '_expiration'); |
|||
return clientExpiration && clientExpiration > new Date().valueOf(); |
|||
} |
|||
|
|||
function validateJwtToken(doRefresh) { |
|||
var deferred = $q.defer(); |
|||
if (!isTokenValid('jwt_token')) { |
|||
if (doRefresh) { |
|||
refreshJwtToken().then(function success() { |
|||
deferred.resolve(); |
|||
}, function fail() { |
|||
deferred.reject(); |
|||
}); |
|||
} else { |
|||
clearJwtToken(false); |
|||
deferred.reject(); |
|||
} |
|||
} else { |
|||
deferred.resolve(); |
|||
} |
|||
return deferred.promise; |
|||
} |
|||
|
|||
function resolveRefreshTokenQueue(data) { |
|||
for (var q=0; q < refreshTokenQueue.length;q++) { |
|||
refreshTokenQueue[q].resolve(data); |
|||
} |
|||
refreshTokenQueue = []; |
|||
} |
|||
|
|||
function rejectRefreshTokenQueue(message) { |
|||
for (var q=0;q<refreshTokenQueue.length;q++) { |
|||
refreshTokenQueue[q].reject(message); |
|||
} |
|||
refreshTokenQueue = []; |
|||
} |
|||
|
|||
function refreshTokenPending() { |
|||
return refreshTokenQueue.length > 0; |
|||
} |
|||
|
|||
function refreshJwtToken() { |
|||
var deferred = $q.defer(); |
|||
refreshTokenQueue.push(deferred); |
|||
if (refreshTokenQueue.length === 1) { |
|||
var refreshToken = store.get('refresh_token'); |
|||
var refreshTokenValid = isTokenValid('refresh_token'); |
|||
setUserFromJwtToken(null, null, false, false); |
|||
if (!refreshTokenValid) { |
|||
rejectRefreshTokenQueue($translate.instant('access.refresh-token-expired')); |
|||
} else { |
|||
var refreshTokenRequest = { |
|||
refreshToken: refreshToken |
|||
}; |
|||
$http.post('/api/auth/token', refreshTokenRequest).then(function success(response) { |
|||
var token = response.data.token; |
|||
var refreshToken = response.data.refreshToken; |
|||
setUserFromJwtToken(token, refreshToken, false); |
|||
resolveRefreshTokenQueue(response.data); |
|||
}, function fail() { |
|||
clearJwtToken(false); |
|||
rejectRefreshTokenQueue($translate.instant('access.refresh-token-failed')); |
|||
}); |
|||
} |
|||
} |
|||
return deferred.promise; |
|||
} |
|||
|
|||
function getCurrentUser() { |
|||
return currentUser; |
|||
} |
|||
|
|||
function getAuthority() { |
|||
if (currentUser) { |
|||
return currentUser.authority; |
|||
} else { |
|||
return ''; |
|||
} |
|||
} |
|||
|
|||
function isPublic() { |
|||
if (currentUser) { |
|||
return currentUser.isPublic; |
|||
} else { |
|||
return false; |
|||
} |
|||
} |
|||
|
|||
function getPublicId() { |
|||
if (isPublic()) { |
|||
return currentUser.sub; |
|||
} else { |
|||
return null; |
|||
} |
|||
} |
|||
|
|||
function parsePublicId() { |
|||
var token = getJwtToken(); |
|||
if (token) { |
|||
var tokenData = jwtHelper.decodeToken(token); |
|||
if (tokenData && tokenData.isPublic) { |
|||
return tokenData.sub; |
|||
} |
|||
} |
|||
return null; |
|||
} |
|||
|
|||
function isUserLoaded() { |
|||
return userLoaded; |
|||
} |
|||
|
|||
function loadUser(doTokenRefresh) { |
|||
|
|||
var deferred = $q.defer(); |
|||
|
|||
function fetchAllowedDashboardIds() { |
|||
var pageLink = {limit: 100}; |
|||
var fetchDashboardsPromise; |
|||
if (currentUser.authority === 'TENANT_ADMIN') { |
|||
fetchDashboardsPromise = dashboardService.getTenantDashboards(pageLink); |
|||
} else { |
|||
fetchDashboardsPromise = dashboardService.getCustomerDashboards(currentUser.customerId, pageLink); |
|||
} |
|||
fetchDashboardsPromise.then( |
|||
function success(result) { |
|||
var dashboards = result.data; |
|||
for (var d=0;d<dashboards.length;d++) { |
|||
allowedDashboardIds.push(dashboards[d].id.id); |
|||
} |
|||
deferred.resolve(); |
|||
}, |
|||
function fail() { |
|||
deferred.reject(); |
|||
} |
|||
); |
|||
} |
|||
|
|||
function updateUserLang() { |
|||
if (currentUserDetails.additionalInfo && currentUserDetails.additionalInfo.lang) { |
|||
$translate.use(currentUserDetails.additionalInfo.lang); |
|||
} |
|||
} |
|||
|
|||
function procceedJwtTokenValidate() { |
|||
validateJwtToken(doTokenRefresh).then(function success() { |
|||
var jwtToken = store.get('jwt_token'); |
|||
currentUser = jwtHelper.decodeToken(jwtToken); |
|||
if (currentUser && currentUser.scopes && currentUser.scopes.length > 0) { |
|||
currentUser.authority = currentUser.scopes[0]; |
|||
} else if (currentUser) { |
|||
currentUser.authority = "ANONYMOUS"; |
|||
} |
|||
var sysParamsPromise = loadSystemParams(); |
|||
if (currentUser.isPublic) { |
|||
$rootScope.forceFullscreen = true; |
|||
sysParamsPromise.then( |
|||
() => { fetchAllowedDashboardIds(); }, |
|||
() => { deferred.reject(); } |
|||
); |
|||
} else if (currentUser.userId) { |
|||
getUser(currentUser.userId, true).then( |
|||
function success(user) { |
|||
sysParamsPromise.then( |
|||
() => { |
|||
currentUserDetails = user; |
|||
updateUserLang(); |
|||
$rootScope.forceFullscreen = false; |
|||
if (userForceFullscreen()) { |
|||
$rootScope.forceFullscreen = true; |
|||
} |
|||
if ($rootScope.forceFullscreen && (currentUser.authority === 'TENANT_ADMIN' || |
|||
currentUser.authority === 'CUSTOMER_USER')) { |
|||
fetchAllowedDashboardIds(); |
|||
} else { |
|||
deferred.resolve(); |
|||
} |
|||
}, |
|||
() => { |
|||
deferred.reject(); |
|||
logout(); |
|||
} |
|||
); |
|||
}, |
|||
function fail() { |
|||
deferred.reject(); |
|||
logout(); |
|||
} |
|||
) |
|||
} else { |
|||
deferred.reject(); |
|||
} |
|||
}, function fail() { |
|||
deferred.reject(); |
|||
}); |
|||
} |
|||
|
|||
if (!currentUser) { |
|||
var locationSearch = $location.search(); |
|||
if (locationSearch.publicId) { |
|||
loginService.publicLogin(locationSearch.publicId).then(function success(response) { |
|||
var token = response.data.token; |
|||
var refreshToken = response.data.refreshToken; |
|||
updateAndValidateToken(token, 'jwt_token', false); |
|||
updateAndValidateToken(refreshToken, 'refresh_token', false); |
|||
procceedJwtTokenValidate(); |
|||
}, function fail() { |
|||
$location.search('publicId', null); |
|||
deferred.reject(); |
|||
}); |
|||
} else if (locationSearch.accessToken) { |
|||
var token = locationSearch.accessToken; |
|||
var refreshToken = locationSearch.refreshToken; |
|||
$location.search('accessToken', null); |
|||
if (refreshToken) { |
|||
$location.search('refreshToken', null); |
|||
} |
|||
try { |
|||
updateAndValidateToken(token, 'jwt_token', false); |
|||
if (refreshToken) { |
|||
updateAndValidateToken(refreshToken, 'refresh_token', false); |
|||
} else { |
|||
store.remove('refresh_token'); |
|||
store.remove('refresh_token_expiration'); |
|||
} |
|||
} catch (e) { |
|||
deferred.reject(); |
|||
} |
|||
procceedJwtTokenValidate(); |
|||
} else if (locationSearch.username && locationSearch.password) { |
|||
var user = {}; |
|||
user.name = locationSearch.username; |
|||
user.password = locationSearch.password; |
|||
$location.search('username', null); |
|||
$location.search('password', null); |
|||
|
|||
loginService.login(user).then(function success(response) { |
|||
var token = response.data.token; |
|||
var refreshToken = response.data.refreshToken; |
|||
try { |
|||
updateAndValidateToken(token, 'jwt_token', false); |
|||
updateAndValidateToken(refreshToken, 'refresh_token', false); |
|||
} catch (e) { |
|||
deferred.reject(); |
|||
} |
|||
procceedJwtTokenValidate(); |
|||
}, function fail() { |
|||
deferred.reject(); |
|||
}); |
|||
} else if (locationSearch.loginError) { |
|||
showLoginErrorDialog(locationSearch.loginError); |
|||
$location.search('loginError', null); |
|||
deferred.reject(); |
|||
} else { |
|||
procceedJwtTokenValidate(); |
|||
} |
|||
} else { |
|||
deferred.resolve(); |
|||
} |
|||
return deferred.promise; |
|||
} |
|||
|
|||
function showLoginErrorDialog(loginError) { |
|||
$translate(['login.error', |
|||
'action.close']).then(function (translations) { |
|||
var alert = $mdDialog.alert() |
|||
.title(translations['login.error']) |
|||
.htmlContent(loginError) |
|||
.ok(translations['action.close']); |
|||
$mdDialog.show(alert); |
|||
}); |
|||
} |
|||
|
|||
function loadIsUserTokenAccessEnabled() { |
|||
var deferred = $q.defer(); |
|||
if (currentUser.authority === 'SYS_ADMIN' || currentUser.authority === 'TENANT_ADMIN') { |
|||
var url = '/api/user/tokenAccessEnabled'; |
|||
$http.get(url).then(function success(response) { |
|||
userTokenAccessEnabled = response.data; |
|||
deferred.resolve(response.data); |
|||
}, function fail() { |
|||
userTokenAccessEnabled = false; |
|||
deferred.reject(); |
|||
}); |
|||
} else { |
|||
userTokenAccessEnabled = false; |
|||
deferred.resolve(false); |
|||
} |
|||
return deferred.promise; |
|||
} |
|||
|
|||
function loadSystemParams() { |
|||
var promises = []; |
|||
promises.push(loadIsUserTokenAccessEnabled()); |
|||
promises.push(timeService.loadMaxDatapointsLimit()); |
|||
return $q.all(promises); |
|||
} |
|||
|
|||
function notifyUserLoaded() { |
|||
if (!userLoaded) { |
|||
userLoaded = true; |
|||
$rootScope.$broadcast('userLoaded'); |
|||
} |
|||
} |
|||
|
|||
function updateAuthorizationHeader(headers) { |
|||
var jwtToken = store.get('jwt_token'); |
|||
if (jwtToken) { |
|||
headers['X-Authorization'] = 'Bearer ' + jwtToken; |
|||
} |
|||
return jwtToken; |
|||
} |
|||
|
|||
function setAuthorizationRequestHeader(request) { |
|||
var jwtToken = store.get('jwt_token'); |
|||
if (jwtToken) { |
|||
request.setRequestHeader('X-Authorization', 'Bearer ' + jwtToken); |
|||
} |
|||
return jwtToken; |
|||
} |
|||
|
|||
function getTenantAdmins(tenantId, pageLink) { |
|||
var deferred = $q.defer(); |
|||
var url = '/api/tenant/' + tenantId + '/users?limit=' + pageLink.limit; |
|||
if (angular.isDefined(pageLink.textSearch)) { |
|||
url += '&textSearch=' + pageLink.textSearch; |
|||
} |
|||
if (angular.isDefined(pageLink.idOffset)) { |
|||
url += '&idOffset=' + pageLink.idOffset; |
|||
} |
|||
if (angular.isDefined(pageLink.textOffset)) { |
|||
url += '&textOffset=' + pageLink.textOffset; |
|||
} |
|||
$http.get(url, null).then(function success(response) { |
|||
deferred.resolve(response.data); |
|||
}, function fail() { |
|||
deferred.reject(); |
|||
}); |
|||
return deferred.promise; |
|||
} |
|||
|
|||
function getCustomerUsers(customerId, pageLink) { |
|||
var deferred = $q.defer(); |
|||
var url = '/api/customer/' + customerId + '/users?limit=' + pageLink.limit; |
|||
if (angular.isDefined(pageLink.textSearch)) { |
|||
url += '&textSearch=' + pageLink.textSearch; |
|||
} |
|||
if (angular.isDefined(pageLink.idOffset)) { |
|||
url += '&idOffset=' + pageLink.idOffset; |
|||
} |
|||
if (angular.isDefined(pageLink.textOffset)) { |
|||
url += '&textOffset=' + pageLink.textOffset; |
|||
} |
|||
$http.get(url, null).then(function success(response) { |
|||
deferred.resolve(response.data); |
|||
}, function fail() { |
|||
deferred.reject(); |
|||
}); |
|||
return deferred.promise; |
|||
} |
|||
|
|||
function saveUser(user, sendActivationMail) { |
|||
var deferred = $q.defer(); |
|||
var url = '/api/user'; |
|||
if (angular.isDefined(sendActivationMail)) { |
|||
url += '?sendActivationMail=' + sendActivationMail; |
|||
} |
|||
$http.post(url, user).then(function success(response) { |
|||
deferred.resolve(response.data); |
|||
}, function fail() { |
|||
deferred.reject(); |
|||
}); |
|||
return deferred.promise; |
|||
} |
|||
|
|||
function setUserCredentialsEnabled(userId, userCredentialsEnabled) { |
|||
var deferred = $q.defer(); |
|||
var url = '/api/user/' + userId + '/userCredentialsEnabled'; |
|||
if (angular.isDefined(userCredentialsEnabled)) { |
|||
url += '?userCredentialsEnabled=' + userCredentialsEnabled; |
|||
} |
|||
$http.post(url, null).then(function success() { |
|||
deferred.resolve(); |
|||
}, function fail() { |
|||
deferred.reject(); |
|||
}); |
|||
return deferred.promise; |
|||
} |
|||
|
|||
function getUser(userId, ignoreErrors, config) { |
|||
var deferred = $q.defer(); |
|||
var url = '/api/user/' + userId; |
|||
if (!config) { |
|||
config = {}; |
|||
} |
|||
config = Object.assign(config, { ignoreErrors: ignoreErrors }); |
|||
$http.get(url, config).then(function success(response) { |
|||
deferred.resolve(response.data); |
|||
}, function fail() { |
|||
deferred.reject(); |
|||
}); |
|||
return deferred.promise; |
|||
} |
|||
|
|||
function deleteUser(userId) { |
|||
var deferred = $q.defer(); |
|||
var url = '/api/user/' + userId; |
|||
$http.delete(url).then(function success() { |
|||
deferred.resolve(); |
|||
}, function fail() { |
|||
deferred.reject(); |
|||
}); |
|||
return deferred.promise; |
|||
} |
|||
|
|||
function sendActivationEmail(email) { |
|||
var deferred = $q.defer(); |
|||
var url = '/api/user/sendActivationMail?email=' + email; |
|||
$http.post(url, null).then(function success(response) { |
|||
deferred.resolve(response); |
|||
}, function fail() { |
|||
deferred.reject(); |
|||
}); |
|||
return deferred.promise; |
|||
} |
|||
|
|||
function getActivationLink(userId) { |
|||
var deferred = $q.defer(); |
|||
var url = `/api/user/${userId}/activationLink` |
|||
$http.get(url).then(function success(response) { |
|||
deferred.resolve(response.data); |
|||
}, function fail() { |
|||
deferred.reject(); |
|||
}); |
|||
return deferred.promise; |
|||
} |
|||
|
|||
function forceDefaultPlace(to, params) { |
|||
if (currentUser && isAuthenticated()) { |
|||
if (currentUser.authority === 'TENANT_ADMIN' || currentUser.authority === 'CUSTOMER_USER') { |
|||
if ((userHasDefaultDashboard() && $rootScope.forceFullscreen) || isPublic()) { |
|||
if (to.name === 'home.profile') { |
|||
if (userHasProfile()) { |
|||
return false; |
|||
} else { |
|||
return true; |
|||
} |
|||
} else if ((to.name === 'home.dashboards.dashboard' || to.name === 'dashboard') |
|||
&& allowedDashboardIds.indexOf(params.dashboardId) > -1) { |
|||
return false; |
|||
} else { |
|||
return true; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
return false; |
|||
} |
|||
|
|||
function setRedirectParams(params) { |
|||
redirectParams = params; |
|||
} |
|||
|
|||
function gotoDefaultPlace(params) { |
|||
if (currentUser && isAuthenticated()) { |
|||
var place = redirectParams ? redirectParams.toName : 'home.links'; |
|||
params = redirectParams ? redirectParams.params : params; |
|||
redirectParams = null; |
|||
if (currentUser.authority === 'TENANT_ADMIN' || currentUser.authority === 'CUSTOMER_USER') { |
|||
if (userHasDefaultDashboard()) { |
|||
place = $rootScope.forceFullscreen ? 'dashboard' : 'home.dashboards.dashboard'; |
|||
params = {dashboardId: currentUserDetails.additionalInfo.defaultDashboardId}; |
|||
} else if (isPublic()) { |
|||
place = 'dashboard'; |
|||
params = {dashboardId: lastPublicDashboardId}; |
|||
} |
|||
} else if (currentUser.authority === 'SYS_ADMIN') { |
|||
adminService.checkUpdates().then( |
|||
function (updateMessage) { |
|||
if (updateMessage && updateMessage.updateAvailable) { |
|||
toast.showInfo(updateMessage.message, 0, null, 'bottom right'); |
|||
} |
|||
} |
|||
); |
|||
} |
|||
$state.go(place, params, {reload: true}); |
|||
} else { |
|||
$state.go('login', params); |
|||
} |
|||
} |
|||
|
|||
function userHasDefaultDashboard() { |
|||
return currentUserDetails && |
|||
currentUserDetails.additionalInfo && |
|||
currentUserDetails.additionalInfo.defaultDashboardId; |
|||
} |
|||
|
|||
function userForceFullscreen() { |
|||
return (currentUser && currentUser.isPublic) || |
|||
(currentUserDetails.additionalInfo && |
|||
currentUserDetails.additionalInfo.defaultDashboardFullscreen && |
|||
currentUserDetails.additionalInfo.defaultDashboardFullscreen === true); |
|||
} |
|||
|
|||
function userHasProfile() { |
|||
return currentUser && !currentUser.isPublic; |
|||
} |
|||
|
|||
function updateLastPublicDashboardId(dashboardId) { |
|||
if (isPublic()) { |
|||
lastPublicDashboardId = dashboardId; |
|||
} |
|||
} |
|||
|
|||
function isUserTokenAccessEnabled() { |
|||
return userTokenAccessEnabled; |
|||
} |
|||
|
|||
function loginAsUser(userId) { |
|||
var url = '/api/user/' + userId + '/token'; |
|||
$http.get(url).then(function success(response) { |
|||
var token = response.data.token; |
|||
var refreshToken = response.data.refreshToken; |
|||
setUserFromJwtToken(token, refreshToken, true); |
|||
}, function fail() { |
|||
}); |
|||
} |
|||
|
|||
} |
|||
@ -1,832 +0,0 @@ |
|||
/* |
|||
* Copyright © 2016-2020 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
import $ from 'jquery'; |
|||
import moment from 'moment'; |
|||
import tinycolor from 'tinycolor2'; |
|||
|
|||
import thingsboardLedLight from '../components/led-light.directive'; |
|||
import thingsboardTimeseriesTableWidget from '../widget/lib/timeseries-table-widget'; |
|||
import thingsboardAlarmsTableWidget from '../widget/lib/alarms-table-widget'; |
|||
import thingsboardEntitiesTableWidget from '../widget/lib/entities-table-widget'; |
|||
import thingsboardEntitiesHierarchyWidget from '../widget/lib/entities-hierarchy-widget'; |
|||
import thingsboardExtensionsTableWidget from '../widget/lib/extensions-table-widget'; |
|||
import thingsboardDateRangeNavigatorWidget from '../widget/lib/date-range-navigator/date-range-navigator'; |
|||
import thingsboardMultipleInputWidget from '../widget/lib/multiple-input-widget'; |
|||
import thingsboardWebCameraInputWidget from '../widget/lib/web-camera-input-widget'; |
|||
|
|||
import thingsboardRpcWidgets from '../widget/lib/rpc'; |
|||
|
|||
import thingsboardJsonToString from '../components/tb-json-to-string.directive'; |
|||
|
|||
import TbFlot from '../widget/lib/flot-widget'; |
|||
import TbAnalogueLinearGauge from '../widget/lib/analogue-linear-gauge'; |
|||
import TbAnalogueRadialGauge from '../widget/lib/analogue-radial-gauge'; |
|||
import TbAnalogueCompass from '../widget/lib/analogue-compass'; |
|||
import TbCanvasDigitalGauge from '../widget/lib/canvas-digital-gauge'; |
|||
import TbMapWidget from '../widget/lib/map-widget'; |
|||
import TbMapWidgetV2 from '../widget/lib/map-widget2'; |
|||
import TripAnimationWidget from '../widget/lib/tripAnimation/trip-animation-widget'; |
|||
|
|||
|
|||
import 'jquery.terminal/js/jquery.terminal.min.js'; |
|||
import 'jquery.terminal/css/jquery.terminal.min.css'; |
|||
|
|||
import 'oclazyload'; |
|||
import cssjs from '../../vendor/css.js/css'; |
|||
|
|||
import thingsboardTypes from '../common/types.constant'; |
|||
import thingsboardUtils from '../common/utils.service'; |
|||
|
|||
export default angular.module('thingsboard.api.widget', ['oc.lazyLoad', thingsboardLedLight, |
|||
thingsboardTimeseriesTableWidget, thingsboardAlarmsTableWidget, thingsboardEntitiesTableWidget, |
|||
thingsboardEntitiesHierarchyWidget, thingsboardExtensionsTableWidget, thingsboardDateRangeNavigatorWidget, |
|||
thingsboardMultipleInputWidget, thingsboardWebCameraInputWidget, thingsboardRpcWidgets, thingsboardTypes, |
|||
thingsboardUtils, thingsboardJsonToString, TripAnimationWidget]) |
|||
.factory('widgetService', WidgetService) |
|||
.name; |
|||
|
|||
/*@ngInject*/ |
|||
function WidgetService($rootScope, $http, $q, $filter, $ocLazyLoad, $window, $translate, types, utils) { |
|||
|
|||
$window.$ = $; |
|||
$window.jQuery = $; |
|||
$window.moment = moment; |
|||
$window.tinycolor = tinycolor; |
|||
$window.lazyLoad = $ocLazyLoad; |
|||
|
|||
$window.TbFlot = TbFlot; |
|||
$window.TbAnalogueLinearGauge = TbAnalogueLinearGauge; |
|||
$window.TbAnalogueRadialGauge = TbAnalogueRadialGauge; |
|||
$window.TbAnalogueCompass = TbAnalogueCompass; |
|||
$window.TbCanvasDigitalGauge = TbCanvasDigitalGauge; |
|||
$window.TbMapWidget = TbMapWidget; |
|||
$window.TbMapWidgetV2 = TbMapWidgetV2; |
|||
|
|||
$window.cssjs = cssjs; |
|||
|
|||
var cssParser = new cssjs(); |
|||
cssParser.testMode = false; |
|||
|
|||
var missingWidgetType; |
|||
var errorWidgetType; |
|||
|
|||
var editingWidgetType; |
|||
|
|||
var widgetsInfoInMemoryCache = {}; |
|||
var widgetsTypeFunctionsInMemoryCache = {}; |
|||
var widgetsInfoFetchQueue = {}; |
|||
|
|||
var allWidgetsBundles = undefined; |
|||
var systemWidgetsBundles = undefined; |
|||
var tenantWidgetsBundles = undefined; |
|||
|
|||
$rootScope.widgetServiceStateChangeStartHandle = $rootScope.$on('$stateChangeStart', function () { |
|||
invalidateWidgetsBundleCache(); |
|||
}); |
|||
|
|||
initEditingWidgetType(); |
|||
initWidgetPlaceholders(); |
|||
|
|||
var service = { |
|||
getWidgetTemplate: getWidgetTemplate, |
|||
getSystemWidgetsBundles: getSystemWidgetsBundles, |
|||
getTenantWidgetsBundles: getTenantWidgetsBundles, |
|||
getAllWidgetsBundles: getAllWidgetsBundles, |
|||
getSystemWidgetsBundlesByPageLink: getSystemWidgetsBundlesByPageLink, |
|||
getTenantWidgetsBundlesByPageLink: getTenantWidgetsBundlesByPageLink, |
|||
getAllWidgetsBundlesByPageLink: getAllWidgetsBundlesByPageLink, |
|||
getWidgetsBundleByAlias: getWidgetsBundleByAlias, |
|||
saveWidgetsBundle: saveWidgetsBundle, |
|||
getWidgetsBundle: getWidgetsBundle, |
|||
deleteWidgetsBundle: deleteWidgetsBundle, |
|||
getBundleWidgetTypes: getBundleWidgetTypes, |
|||
getWidgetInfo: getWidgetInfo, |
|||
getWidgetTypeFunction: getWidgetTypeFunction, |
|||
getInstantWidgetInfo: getInstantWidgetInfo, |
|||
deleteWidgetType: deleteWidgetType, |
|||
saveWidgetType: saveWidgetType, |
|||
saveImportedWidgetType: saveImportedWidgetType, |
|||
getWidgetType: getWidgetType, |
|||
getWidgetTypeById: getWidgetTypeById, |
|||
toWidgetInfo: toWidgetInfo |
|||
} |
|||
|
|||
return service; |
|||
|
|||
function initEditingWidgetType() { |
|||
if ($rootScope.widgetEditMode) { |
|||
editingWidgetType = |
|||
toWidgetType({ |
|||
widgetName: $rootScope.editWidgetInfo.widgetName, |
|||
alias: 'customWidget', |
|||
type: $rootScope.editWidgetInfo.type, |
|||
sizeX: $rootScope.editWidgetInfo.sizeX, |
|||
sizeY: $rootScope.editWidgetInfo.sizeY, |
|||
resources: $rootScope.editWidgetInfo.resources, |
|||
templateHtml: $rootScope.editWidgetInfo.templateHtml, |
|||
templateCss: $rootScope.editWidgetInfo.templateCss, |
|||
controllerScript: $rootScope.editWidgetInfo.controllerScript, |
|||
settingsSchema: $rootScope.editWidgetInfo.settingsSchema, |
|||
dataKeySettingsSchema: $rootScope.editWidgetInfo.dataKeySettingsSchema, |
|||
defaultConfig: $rootScope.editWidgetInfo.defaultConfig |
|||
}, {id: '1'}, { id: types.id.nullUid }, 'customWidgetBundle'); |
|||
} |
|||
} |
|||
|
|||
function initWidgetPlaceholders() { |
|||
|
|||
missingWidgetType = { |
|||
widgetName: 'Widget type not found', |
|||
alias: 'undefined', |
|||
sizeX: 8, |
|||
sizeY: 6, |
|||
resources: [], |
|||
templateHtml: '<div class="tb-widget-error-container"><div translate class="tb-widget-error-msg">widget.widget-type-not-found</div></div>', |
|||
templateCss: '', |
|||
controllerScript: 'self.onInit = function() {}', |
|||
settingsSchema: '{}\n', |
|||
dataKeySettingsSchema: '{}\n', |
|||
defaultConfig: '{\n' + |
|||
'"title": "Widget type not found",\n' + |
|||
'"datasources": [],\n' + |
|||
'"settings": {}\n' + |
|||
'}\n' |
|||
}; |
|||
|
|||
errorWidgetType = { |
|||
widgetName: 'Error loading widget', |
|||
alias: 'error', |
|||
sizeX: 8, |
|||
sizeY: 6, |
|||
resources: [], |
|||
templateHtml: '<div class="tb-widget-error-container"><div translate class="tb-widget-error-msg">widget.widget-type-load-error</div>', |
|||
templateCss: '', |
|||
controllerScript: 'self.onInit = function() {}', |
|||
settingsSchema: '{}\n', |
|||
dataKeySettingsSchema: '{}\n', |
|||
defaultConfig: '{\n' + |
|||
'"title": "Widget failed to load",\n' + |
|||
'"datasources": [],\n' + |
|||
'"settings": {}\n' + |
|||
'}\n' |
|||
}; |
|||
} |
|||
|
|||
function toWidgetInfo(widgetType) { |
|||
|
|||
var widgetInfo = { |
|||
widgetName: widgetType.name, |
|||
alias: widgetType.alias |
|||
} |
|||
|
|||
var descriptor = widgetType.descriptor; |
|||
|
|||
widgetInfo.type = descriptor.type; |
|||
widgetInfo.sizeX = descriptor.sizeX; |
|||
widgetInfo.sizeY = descriptor.sizeY; |
|||
widgetInfo.resources = descriptor.resources; |
|||
widgetInfo.templateHtml = descriptor.templateHtml; |
|||
widgetInfo.templateCss = descriptor.templateCss; |
|||
widgetInfo.controllerScript = descriptor.controllerScript; |
|||
widgetInfo.settingsSchema = descriptor.settingsSchema; |
|||
widgetInfo.dataKeySettingsSchema = descriptor.dataKeySettingsSchema; |
|||
widgetInfo.defaultConfig = descriptor.defaultConfig; |
|||
|
|||
return widgetInfo; |
|||
} |
|||
|
|||
function toWidgetType(widgetInfo, id, tenantId, bundleAlias) { |
|||
var widgetType = { |
|||
id: id, |
|||
tenantId: tenantId, |
|||
bundleAlias: bundleAlias, |
|||
alias: widgetInfo.alias, |
|||
name: widgetInfo.widgetName |
|||
} |
|||
|
|||
var descriptor = { |
|||
type: widgetInfo.type, |
|||
sizeX: widgetInfo.sizeX, |
|||
sizeY: widgetInfo.sizeY, |
|||
resources: widgetInfo.resources, |
|||
templateHtml: widgetInfo.templateHtml, |
|||
templateCss: widgetInfo.templateCss, |
|||
controllerScript: widgetInfo.controllerScript, |
|||
settingsSchema: widgetInfo.settingsSchema, |
|||
dataKeySettingsSchema: widgetInfo.dataKeySettingsSchema, |
|||
defaultConfig: widgetInfo.defaultConfig |
|||
} |
|||
|
|||
widgetType.descriptor = descriptor; |
|||
|
|||
return widgetType; |
|||
} |
|||
|
|||
function getWidgetTemplate(type) { |
|||
var deferred = $q.defer(); |
|||
var templateWidgetType = types.widgetType.timeseries; |
|||
for (var t in types.widgetType) { |
|||
var widgetType = types.widgetType[t]; |
|||
if (widgetType.value === type) { |
|||
templateWidgetType = widgetType; |
|||
break; |
|||
} |
|||
} |
|||
getWidgetType(templateWidgetType.template.bundleAlias, |
|||
templateWidgetType.template.alias, true).then( |
|||
function success(widgetType) { |
|||
var widgetInfo = toWidgetInfo(widgetType); |
|||
widgetInfo.alias = undefined; |
|||
deferred.resolve(widgetInfo); |
|||
}, |
|||
function fail() { |
|||
deferred.reject(); |
|||
} |
|||
); |
|||
return deferred.promise; |
|||
} |
|||
|
|||
/** Cache functions **/ |
|||
|
|||
function createWidgetInfoCacheKey(bundleAlias, widgetTypeAlias, isSystem) { |
|||
return (isSystem ? 'sys_' : '') + bundleAlias + '_' + widgetTypeAlias; |
|||
} |
|||
|
|||
function getWidgetInfoFromCache(bundleAlias, widgetTypeAlias, isSystem) { |
|||
var key = createWidgetInfoCacheKey(bundleAlias, widgetTypeAlias, isSystem); |
|||
return widgetsInfoInMemoryCache[key]; |
|||
} |
|||
|
|||
function getWidgetTypeFunctionFromCache(bundleAlias, widgetTypeAlias, isSystem) { |
|||
var key = createWidgetInfoCacheKey(bundleAlias, widgetTypeAlias, isSystem); |
|||
return widgetsTypeFunctionsInMemoryCache[key]; |
|||
} |
|||
|
|||
function putWidgetInfoToCache(widgetInfo, bundleAlias, widgetTypeAlias, isSystem) { |
|||
var key = createWidgetInfoCacheKey(bundleAlias, widgetTypeAlias, isSystem); |
|||
widgetsInfoInMemoryCache[key] = widgetInfo; |
|||
} |
|||
|
|||
function putWidgetTypeFunctionToCache(widgetTypeFunction, bundleAlias, widgetTypeAlias, isSystem) { |
|||
var key = createWidgetInfoCacheKey(bundleAlias, widgetTypeAlias, isSystem); |
|||
widgetsTypeFunctionsInMemoryCache[key] = widgetTypeFunction; |
|||
} |
|||
|
|||
function deleteWidgetInfoFromCache(bundleAlias, widgetTypeAlias, isSystem) { |
|||
var key = createWidgetInfoCacheKey(bundleAlias, widgetTypeAlias, isSystem); |
|||
delete widgetsInfoInMemoryCache[key]; |
|||
delete widgetsTypeFunctionsInMemoryCache[key]; |
|||
} |
|||
|
|||
function deleteWidgetsBundleFromCache(bundleAlias, isSystem) { |
|||
var key = (isSystem ? 'sys_' : '') + bundleAlias; |
|||
for (var cacheKey in widgetsInfoInMemoryCache) { |
|||
if (cacheKey.startsWith(key)) { |
|||
delete widgetsInfoInMemoryCache[cacheKey]; |
|||
delete widgetsTypeFunctionsInMemoryCache[cacheKey]; |
|||
} |
|||
} |
|||
} |
|||
|
|||
/** Bundle functions **/ |
|||
|
|||
function invalidateWidgetsBundleCache() { |
|||
allWidgetsBundles = undefined; |
|||
systemWidgetsBundles = undefined; |
|||
tenantWidgetsBundles = undefined; |
|||
} |
|||
|
|||
function loadWidgetsBundleCache(config) { |
|||
var deferred = $q.defer(); |
|||
if (!allWidgetsBundles) { |
|||
var url = '/api/widgetsBundles'; |
|||
$http.get(url, config).then(function success(response) { |
|||
allWidgetsBundles = response.data; |
|||
systemWidgetsBundles = []; |
|||
tenantWidgetsBundles = []; |
|||
allWidgetsBundles = $filter('orderBy')(allWidgetsBundles, ['+title', '-createdTime']); |
|||
for (var i = 0; i < allWidgetsBundles.length; i++) { |
|||
var widgetsBundle = allWidgetsBundles[i]; |
|||
if (widgetsBundle.tenantId.id === types.id.nullUid) { |
|||
systemWidgetsBundles.push(widgetsBundle); |
|||
} else { |
|||
tenantWidgetsBundles.push(widgetsBundle); |
|||
} |
|||
} |
|||
deferred.resolve(); |
|||
}, function fail() { |
|||
deferred.reject(); |
|||
}); |
|||
} else { |
|||
deferred.resolve(); |
|||
} |
|||
return deferred.promise; |
|||
} |
|||
|
|||
|
|||
function getSystemWidgetsBundles(config) { |
|||
var deferred = $q.defer(); |
|||
loadWidgetsBundleCache(config).then( |
|||
function success() { |
|||
deferred.resolve(systemWidgetsBundles); |
|||
}, |
|||
function fail() { |
|||
deferred.reject(); |
|||
} |
|||
); |
|||
return deferred.promise; |
|||
} |
|||
|
|||
function getTenantWidgetsBundles(config) { |
|||
var deferred = $q.defer(); |
|||
loadWidgetsBundleCache(config).then( |
|||
function success() { |
|||
deferred.resolve(tenantWidgetsBundles); |
|||
}, |
|||
function fail() { |
|||
deferred.reject(); |
|||
} |
|||
); |
|||
return deferred.promise; |
|||
} |
|||
|
|||
function getAllWidgetsBundles(config) { |
|||
var deferred = $q.defer(); |
|||
loadWidgetsBundleCache(config).then( |
|||
function success() { |
|||
deferred.resolve(allWidgetsBundles); |
|||
}, |
|||
function fail() { |
|||
deferred.reject(); |
|||
} |
|||
); |
|||
return deferred.promise; |
|||
} |
|||
|
|||
function getSystemWidgetsBundlesByPageLink(pageLink) { |
|||
var deferred = $q.defer(); |
|||
loadWidgetsBundleCache().then( |
|||
function success() { |
|||
utils.filterSearchTextEntities(systemWidgetsBundles, 'title', pageLink, deferred); |
|||
}, |
|||
function fail() { |
|||
deferred.reject(); |
|||
} |
|||
); |
|||
return deferred.promise; |
|||
} |
|||
|
|||
function getTenantWidgetsBundlesByPageLink(pageLink) { |
|||
var deferred = $q.defer(); |
|||
loadWidgetsBundleCache().then( |
|||
function success() { |
|||
utils.filterSearchTextEntities(tenantWidgetsBundles, 'title', pageLink, deferred); |
|||
}, |
|||
function fail() { |
|||
deferred.reject(); |
|||
} |
|||
); |
|||
return deferred.promise; |
|||
} |
|||
|
|||
function getAllWidgetsBundlesByPageLink(pageLink) { |
|||
var deferred = $q.defer(); |
|||
loadWidgetsBundleCache().then( |
|||
function success() { |
|||
utils.filterSearchTextEntities(allWidgetsBundles, 'title', pageLink, deferred); |
|||
}, |
|||
function fail() { |
|||
deferred.reject(); |
|||
} |
|||
); |
|||
return deferred.promise; |
|||
} |
|||
|
|||
function getWidgetsBundleByAlias(bundleAlias) { |
|||
var deferred = $q.defer(); |
|||
loadWidgetsBundleCache().then( |
|||
function success() { |
|||
var widgetsBundles = $filter('filter')(allWidgetsBundles, {alias: bundleAlias}); |
|||
if (widgetsBundles.length > 0) { |
|||
deferred.resolve(widgetsBundles[0]); |
|||
} else { |
|||
deferred.resolve(); |
|||
} |
|||
}, |
|||
function fail() { |
|||
deferred.reject(); |
|||
} |
|||
); |
|||
return deferred.promise; |
|||
} |
|||
|
|||
function saveWidgetsBundle(widgetsBundle) { |
|||
var deferred = $q.defer(); |
|||
var url = '/api/widgetsBundle'; |
|||
$http.post(url, widgetsBundle).then(function success(response) { |
|||
invalidateWidgetsBundleCache(); |
|||
deferred.resolve(response.data); |
|||
}, function fail(response) { |
|||
deferred.reject(response.data); |
|||
}); |
|||
return deferred.promise; |
|||
} |
|||
|
|||
function getWidgetsBundle(widgetsBundleId) { |
|||
var deferred = $q.defer(); |
|||
|
|||
var url = '/api/widgetsBundle/' + widgetsBundleId; |
|||
$http.get(url, null).then(function success(response) { |
|||
deferred.resolve(response.data); |
|||
}, function fail(response) { |
|||
deferred.reject(response.data); |
|||
}); |
|||
|
|||
return deferred.promise; |
|||
} |
|||
|
|||
function deleteWidgetsBundle(widgetsBundleId) { |
|||
var deferred = $q.defer(); |
|||
|
|||
getWidgetsBundle(widgetsBundleId).then( |
|||
function success(response) { |
|||
var widgetsBundle = response; |
|||
var url = '/api/widgetsBundle/' + widgetsBundleId; |
|||
$http.delete(url).then(function success() { |
|||
invalidateWidgetsBundleCache(); |
|||
deleteWidgetsBundleFromCache(widgetsBundle.alias, |
|||
widgetsBundle.tenantId.id === types.id.nullUid); |
|||
deferred.resolve(); |
|||
}, function fail(response) { |
|||
deferred.reject(response.data); |
|||
}); |
|||
}, |
|||
function fail() { |
|||
deferred.reject(); |
|||
} |
|||
); |
|||
|
|||
return deferred.promise; |
|||
} |
|||
|
|||
function getBundleWidgetTypes(bundleAlias, isSystem) { |
|||
var deferred = $q.defer(); |
|||
var url = '/api/widgetTypes?isSystem=' + (isSystem ? 'true' : 'false') + |
|||
'&bundleAlias='+bundleAlias; |
|||
$http.get(url, null).then(function success(response) { |
|||
deferred.resolve(response.data); |
|||
}, function fail(response) { |
|||
deferred.reject(response.data); |
|||
}); |
|||
return deferred.promise; |
|||
} |
|||
|
|||
/** Widget type functions **/ |
|||
|
|||
function getInstantWidgetInfo(widget) { |
|||
var widgetInfo = getWidgetInfoFromCache(widget.bundleAlias, widget.typeAlias, widget.isSystemType); |
|||
if (widgetInfo) { |
|||
return widgetInfo; |
|||
} else { |
|||
return {}; |
|||
} |
|||
} |
|||
|
|||
function resolveWidgetsInfoFetchQueue(key, widgetInfo) { |
|||
var fetchQueue = widgetsInfoFetchQueue[key]; |
|||
if (fetchQueue) { |
|||
for (var q in fetchQueue) { |
|||
if (isNaN(q)) |
|||
continue; |
|||
fetchQueue[q].resolve(widgetInfo); |
|||
} |
|||
delete widgetsInfoFetchQueue[key]; |
|||
} |
|||
} |
|||
|
|||
function getWidgetInfo(bundleAlias, widgetTypeAlias, isSystem) { |
|||
var deferred = $q.defer(); |
|||
var widgetInfo = getWidgetInfoFromCache(bundleAlias, widgetTypeAlias, isSystem); |
|||
if (widgetInfo) { |
|||
deferred.resolve(widgetInfo); |
|||
} else { |
|||
if ($rootScope.widgetEditMode) { |
|||
loadWidget(editingWidgetType, bundleAlias, isSystem, deferred); |
|||
} else { |
|||
var key = createWidgetInfoCacheKey(bundleAlias, widgetTypeAlias, isSystem); |
|||
var fetchQueue = widgetsInfoFetchQueue[key]; |
|||
if (fetchQueue) { |
|||
fetchQueue.push(deferred); |
|||
} else { |
|||
fetchQueue = []; |
|||
widgetsInfoFetchQueue[key] = fetchQueue; |
|||
getWidgetType(bundleAlias, widgetTypeAlias, isSystem).then( |
|||
function success(widgetType) { |
|||
loadWidget(widgetType, bundleAlias, isSystem, deferred); |
|||
}, function fail() { |
|||
deferred.resolve(missingWidgetType); |
|||
resolveWidgetsInfoFetchQueue(key, missingWidgetType); |
|||
} |
|||
); |
|||
} |
|||
} |
|||
} |
|||
return deferred.promise; |
|||
} |
|||
|
|||
function getWidgetTypeFunction(bundleAlias, widgetTypeAlias, isSystem) { |
|||
var widgetType = getWidgetTypeFunctionFromCache(bundleAlias, widgetTypeAlias, isSystem); |
|||
return widgetType; |
|||
} |
|||
|
|||
function createWidgetTypeFunction(widgetInfo, name) { |
|||
var widgetTypeFunctionBody = 'return function '+ name +' (ctx) {\n' + |
|||
' var self = this;\n' + |
|||
' self.ctx = ctx;\n\n'; /*+ |
|||
|
|||
' self.onInit = function() {\n\n' + |
|||
|
|||
' }\n\n' + |
|||
|
|||
' self.onDataUpdated = function() {\n\n' + |
|||
|
|||
' }\n\n' + |
|||
|
|||
' self.useCustomDatasources = function() {\n\n' + |
|||
|
|||
' }\n\n' + |
|||
|
|||
' self.typeParameters = function() {\n\n' + |
|||
return { |
|||
useCustomDatasources: false, |
|||
maxDatasources: -1, //unlimited
|
|||
maxDataKeys: -1, //unlimited
|
|||
dataKeysOptional: false, |
|||
stateData: false |
|||
}; |
|||
' }\n\n' + |
|||
|
|||
' self.actionSources = function() {\n\n' + |
|||
return { |
|||
'headerButton': { |
|||
name: 'Header button', |
|||
multiple: true |
|||
} |
|||
}; |
|||
}\n\n' + |
|||
' self.onResize = function() {\n\n' + |
|||
|
|||
' }\n\n' + |
|||
|
|||
' self.onEditModeChanged = function() {\n\n' + |
|||
|
|||
' }\n\n' + |
|||
|
|||
' self.onMobileModeChanged = function() {\n\n' + |
|||
|
|||
' }\n\n' + |
|||
|
|||
' self.getSettingsSchema = function() {\n\n' + |
|||
|
|||
' }\n\n' + |
|||
|
|||
' self.getDataKeySettingsSchema = function() {\n\n' + |
|||
|
|||
' }\n\n' + |
|||
|
|||
' self.onDestroy = function() {\n\n' + |
|||
|
|||
' }\n\n' + |
|||
'}';*/ |
|||
|
|||
widgetTypeFunctionBody += widgetInfo.controllerScript; |
|||
widgetTypeFunctionBody += '\n};\n'; |
|||
try { |
|||
var widgetTypeFunction = new Function(widgetTypeFunctionBody); |
|||
var widgetType = widgetTypeFunction.apply(this); |
|||
var widgetTypeInstance = new widgetType(); |
|||
var result = { |
|||
widgetTypeFunction: widgetType |
|||
}; |
|||
if (angular.isFunction(widgetTypeInstance.getSettingsSchema)) { |
|||
result.settingsSchema = widgetTypeInstance.getSettingsSchema(); |
|||
} |
|||
if (angular.isFunction(widgetTypeInstance.getDataKeySettingsSchema)) { |
|||
result.dataKeySettingsSchema = widgetTypeInstance.getDataKeySettingsSchema(); |
|||
} |
|||
if (angular.isFunction(widgetTypeInstance.typeParameters)) { |
|||
result.typeParameters = widgetTypeInstance.typeParameters(); |
|||
} else { |
|||
result.typeParameters = {}; |
|||
} |
|||
if (angular.isFunction(widgetTypeInstance.useCustomDatasources)) { |
|||
result.typeParameters.useCustomDatasources = widgetTypeInstance.useCustomDatasources(); |
|||
} else { |
|||
result.typeParameters.useCustomDatasources = false; |
|||
} |
|||
if (angular.isUndefined(result.typeParameters.maxDatasources)) { |
|||
result.typeParameters.maxDatasources = -1; |
|||
} |
|||
if (angular.isUndefined(result.typeParameters.maxDataKeys)) { |
|||
result.typeParameters.maxDataKeys = -1; |
|||
} |
|||
if (angular.isUndefined(result.typeParameters.dataKeysOptional)) { |
|||
result.typeParameters.dataKeysOptional = false; |
|||
} |
|||
if (angular.isUndefined(result.typeParameters.stateData)) { |
|||
result.typeParameters.stateData = false; |
|||
} |
|||
if (angular.isFunction(widgetTypeInstance.actionSources)) { |
|||
result.actionSources = widgetTypeInstance.actionSources(); |
|||
} else { |
|||
result.actionSources = {}; |
|||
} |
|||
for (var actionSourceId in types.widgetActionSources) { |
|||
result.actionSources[actionSourceId] = angular.copy(types.widgetActionSources[actionSourceId]); |
|||
result.actionSources[actionSourceId].name = $translate.instant(result.actionSources[actionSourceId].name) + ''; |
|||
} |
|||
|
|||
return result; |
|||
} catch (e) { |
|||
utils.processWidgetException(e); |
|||
throw e; |
|||
} |
|||
} |
|||
|
|||
function processWidgetLoadError(errorMessages, cacheKey, deferred) { |
|||
var widgetInfo = angular.copy(errorWidgetType); |
|||
for (var e in errorMessages) { |
|||
var error = errorMessages[e]; |
|||
widgetInfo.templateHtml += '<div class="tb-widget-error-msg">' + error + '</div>'; |
|||
} |
|||
widgetInfo.templateHtml += '</div>'; |
|||
deferred.resolve(widgetInfo); |
|||
resolveWidgetsInfoFetchQueue(cacheKey, widgetInfo); |
|||
} |
|||
|
|||
function loadWidget(widgetType, bundleAlias, isSystem, deferred) { |
|||
var widgetInfo = toWidgetInfo(widgetType); |
|||
var key = createWidgetInfoCacheKey(bundleAlias, widgetInfo.alias, isSystem); |
|||
loadWidgetResources(widgetInfo, bundleAlias, isSystem).then( |
|||
function success() { |
|||
var widgetType = null; |
|||
try { |
|||
widgetType = createWidgetTypeFunction(widgetInfo, key); |
|||
} catch (e) { |
|||
var details = utils.parseException(e); |
|||
var errorMessage = 'Failed to compile widget script. \n Error: ' + details.message; |
|||
processWidgetLoadError([errorMessage], key, deferred); |
|||
} |
|||
if (widgetType) { |
|||
if (widgetType.settingsSchema) { |
|||
widgetInfo.typeSettingsSchema = widgetType.settingsSchema; |
|||
} |
|||
if (widgetType.dataKeySettingsSchema) { |
|||
widgetInfo.typeDataKeySettingsSchema = widgetType.dataKeySettingsSchema; |
|||
} |
|||
widgetInfo.typeParameters = widgetType.typeParameters; |
|||
widgetInfo.actionSources = widgetType.actionSources; |
|||
putWidgetInfoToCache(widgetInfo, bundleAlias, widgetInfo.alias, isSystem); |
|||
putWidgetTypeFunctionToCache(widgetType.widgetTypeFunction, bundleAlias, widgetInfo.alias, isSystem); |
|||
deferred.resolve(widgetInfo); |
|||
resolveWidgetsInfoFetchQueue(key, widgetInfo); |
|||
} |
|||
}, function fail(errorMessages) { |
|||
processWidgetLoadError(errorMessages, key, deferred); |
|||
} |
|||
); |
|||
} |
|||
|
|||
function getWidgetType(bundleAlias, widgetTypeAlias, isSystem) { |
|||
var deferred = $q.defer(); |
|||
var url = '/api/widgetType?isSystem=' + (isSystem ? 'true' : 'false') + |
|||
'&bundleAlias='+bundleAlias+'&alias='+widgetTypeAlias; |
|||
$http.get(url, null).then(function success(response) { |
|||
var widgetType = response.data; |
|||
deferred.resolve(widgetType); |
|||
}, function fail() { |
|||
deferred.reject(); |
|||
}); |
|||
return deferred.promise; |
|||
} |
|||
|
|||
function getWidgetTypeById(widgetTypeId) { |
|||
var deferred = $q.defer(); |
|||
var url = '/api/widgetType/' + widgetTypeId; |
|||
$http.get(url, null).then(function success(response) { |
|||
var widgetType = response.data; |
|||
deferred.resolve(widgetType); |
|||
}, function fail() { |
|||
deferred.reject(); |
|||
}); |
|||
return deferred.promise; |
|||
} |
|||
|
|||
function deleteWidgetType(bundleAlias, widgetTypeAlias, isSystem) { |
|||
var deferred = $q.defer(); |
|||
getWidgetType(bundleAlias, widgetTypeAlias, isSystem).then( |
|||
function success(widgetType) { |
|||
var url = '/api/widgetType/' + widgetType.id.id; |
|||
$http.delete(url).then(function success() { |
|||
deleteWidgetInfoFromCache(bundleAlias, widgetTypeAlias, isSystem); |
|||
deferred.resolve(); |
|||
}, function fail() { |
|||
deferred.reject(); |
|||
}); |
|||
}, |
|||
function fail() { |
|||
deferred.reject(); |
|||
} |
|||
); |
|||
return deferred.promise; |
|||
} |
|||
|
|||
function saveWidgetType(widgetInfo, id, bundleAlias) { |
|||
var deferred = $q.defer(); |
|||
var widgetType = toWidgetType(widgetInfo, id, undefined, bundleAlias); |
|||
var url = '/api/widgetType'; |
|||
$http.post(url, widgetType).then(function success(response) { |
|||
var widgetType = response.data; |
|||
deleteWidgetInfoFromCache(widgetType.bundleAlias, widgetType.alias, widgetType.tenantId.id === types.id.nullUid); |
|||
deferred.resolve(widgetType); |
|||
}, function fail() { |
|||
deferred.reject(); |
|||
}); |
|||
return deferred.promise; |
|||
} |
|||
|
|||
function saveImportedWidgetType(widgetType) { |
|||
var deferred = $q.defer(); |
|||
var url = '/api/widgetType'; |
|||
$http.post(url, widgetType).then(function success(response) { |
|||
var widgetType = response.data; |
|||
deleteWidgetInfoFromCache(widgetType.bundleAlias, widgetType.alias, widgetType.tenantId.id === types.id.nullUid); |
|||
deferred.resolve(widgetType); |
|||
}, function fail() { |
|||
deferred.reject(); |
|||
}); |
|||
return deferred.promise; |
|||
} |
|||
|
|||
function loadWidgetResources(widgetInfo, bundleAlias, isSystem) { |
|||
|
|||
var deferred = $q.defer(); |
|||
var errors = []; |
|||
|
|||
var widgetNamespace = "widget-type-" + (isSystem ? 'sys-' : '') + bundleAlias + '-' + widgetInfo.alias; |
|||
cssParser.cssPreviewNamespace = widgetNamespace; |
|||
cssParser.createStyleElement(widgetNamespace, widgetInfo.templateCss); |
|||
|
|||
function loadNextOrComplete(i) { |
|||
i++; |
|||
if (i < widgetInfo.resources.length) { |
|||
loadNext(i); |
|||
} else { |
|||
if (errors.length > 0) { |
|||
deferred.reject(errors); |
|||
} else { |
|||
deferred.resolve(); |
|||
} |
|||
} |
|||
} |
|||
|
|||
function loadNext(i) { |
|||
var resourceUrl = widgetInfo.resources[i].url; |
|||
if (resourceUrl && resourceUrl.length > 0) { |
|||
$ocLazyLoad.load(resourceUrl).then( |
|||
function success() { |
|||
loadNextOrComplete(i); |
|||
}, |
|||
function fail() { |
|||
errors.push('Failed to load widget resource: \'' + resourceUrl + '\''); |
|||
loadNextOrComplete(i); |
|||
} |
|||
); |
|||
} else { |
|||
loadNextOrComplete(i); |
|||
} |
|||
} |
|||
|
|||
if (widgetInfo.resources.length > 0) { |
|||
loadNext(0); |
|||
} else { |
|||
deferred.resolve(); |
|||
} |
|||
|
|||
return deferred.promise; |
|||
} |
|||
|
|||
} |
|||
@ -1,174 +0,0 @@ |
|||
/* |
|||
* Copyright © 2016-2020 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
import injectTapEventPlugin from 'react-tap-event-plugin'; |
|||
import UrlHandler from './url.handler'; |
|||
|
|||
/* eslint-disable import/no-unresolved, import/default */ |
|||
|
|||
import mdiIconSet from '../svg/mdi.svg'; |
|||
|
|||
/* eslint-enable import/no-unresolved, import/default */ |
|||
|
|||
const PRIMARY_BACKGROUND_COLOR = "#305680";//#2856b6";//"#3f51b5";
|
|||
const SECONDARY_BACKGROUND_COLOR = "#527dad"; |
|||
const HUE3_COLOR = "#a7c1de"; |
|||
|
|||
/*@ngInject*/ |
|||
export default function AppConfig($provide, |
|||
$urlRouterProvider, |
|||
$locationProvider, |
|||
$mdIconProvider, |
|||
ngMdIconServiceProvider, |
|||
$mdThemingProvider, |
|||
$httpProvider, |
|||
$translateProvider, |
|||
storeProvider) { |
|||
|
|||
injectTapEventPlugin(); |
|||
$locationProvider.html5Mode(true); |
|||
$urlRouterProvider.otherwise(UrlHandler); |
|||
storeProvider.setCaching(false); |
|||
|
|||
$translateProvider.useSanitizeValueStrategy(null) |
|||
.useMissingTranslationHandler('tbMissingTranslationHandler') |
|||
.addInterpolation('$translateMessageFormatInterpolation') |
|||
.useStaticFilesLoader({ |
|||
files: [ |
|||
{ |
|||
prefix: PUBLIC_PATH + 'locale/locale.constant-', //eslint-disable-line
|
|||
suffix: '.json' |
|||
} |
|||
] |
|||
}) |
|||
.registerAvailableLanguageKeys(SUPPORTED_LANGS, getLanguageAliases(SUPPORTED_LANGS)) //eslint-disable-line
|
|||
.fallbackLanguage('en_US') // must be before determinePreferredLanguage
|
|||
.uniformLanguageTag('java') // must be before determinePreferredLanguage
|
|||
.determinePreferredLanguage(); |
|||
|
|||
$httpProvider.interceptors.push('globalInterceptor'); |
|||
|
|||
$provide.decorator("$exceptionHandler", ['$delegate', '$injector', function ($delegate/*, $injector*/) { |
|||
return function (exception, cause) { |
|||
/* var rootScope = $injector.get("$rootScope"); |
|||
var $window = $injector.get("$window"); |
|||
var utils = $injector.get("utils"); |
|||
if (rootScope.widgetEditMode) { |
|||
var parentScope = $window.parent.angular.element($window.frameElement).scope(); |
|||
var data = utils.parseException(exception); |
|||
parentScope.$emit('widgetException', data); |
|||
parentScope.$apply(); |
|||
}*/ |
|||
$delegate(exception, cause); |
|||
}; |
|||
}]); |
|||
|
|||
$mdIconProvider.iconSet('mdi', mdiIconSet); |
|||
|
|||
ngMdIconServiceProvider |
|||
.addShape('alpha-a-circle-outline', '<path d="M11,7H13A2,2 0 0,1 15,9V17H13V13H11V17H9V9A2,2 0 0,1 11,7M11,9V11H13V9H11M12,20A8,8 0 0,0 20,12A8,8 0 0,0 12,4A8,8 0 0,0 4,12A8,8 0 0,0 12,20M12,2A10,10 0 0,1 22,12A10,10 0 0,1 12,22A10,10 0 0,1 2,12A10,10 0 0,1 12,2Z" />') |
|||
.addShape('alpha-e-circle-outline', '<path d="M9,7H15V9H11V11H15V13H11V15H15V17H9V7M12,2A10,10 0 0,1 22,12A10,10 0 0,1 12,22A10,10 0 0,1 2,12A10,10 0 0,1 12,2M12,4A8,8 0 0,0 4,12A8,8 0 0,0 12,20A8,8 0 0,0 20,12A8,8 0 0,0 12,4Z" />'); |
|||
|
|||
configureTheme(); |
|||
|
|||
function blueGrayTheme() { |
|||
var tbPrimaryPalette = $mdThemingProvider.extendPalette('blue-grey'); |
|||
var tbAccentPalette = $mdThemingProvider.extendPalette('orange', { |
|||
'contrastDefaultColor': 'light' |
|||
}); |
|||
|
|||
$mdThemingProvider.definePalette('tb-primary', tbPrimaryPalette); |
|||
$mdThemingProvider.definePalette('tb-accent', tbAccentPalette); |
|||
|
|||
$mdThemingProvider.theme('default') |
|||
.primaryPalette('tb-primary') |
|||
.accentPalette('tb-accent'); |
|||
|
|||
$mdThemingProvider.theme('tb-dark') |
|||
.primaryPalette('tb-primary') |
|||
.accentPalette('tb-accent') |
|||
.backgroundPalette('tb-primary') |
|||
.dark(); |
|||
} |
|||
|
|||
function indigoTheme() { |
|||
var tbPrimaryPalette = $mdThemingProvider.extendPalette('indigo', { |
|||
'500': PRIMARY_BACKGROUND_COLOR, |
|||
'600': SECONDARY_BACKGROUND_COLOR, |
|||
'A100': HUE3_COLOR |
|||
}); |
|||
|
|||
var tbAccentPalette = $mdThemingProvider.extendPalette('deep-orange'); |
|||
|
|||
$mdThemingProvider.definePalette('tb-primary', tbPrimaryPalette); |
|||
$mdThemingProvider.definePalette('tb-accent', tbAccentPalette); |
|||
|
|||
var tbDarkPrimaryPalette = $mdThemingProvider.extendPalette('tb-primary', { |
|||
'500': '#9fa8da' |
|||
}); |
|||
|
|||
var tbDarkPrimaryBackgroundPalette = $mdThemingProvider.extendPalette('tb-primary', { |
|||
'800': PRIMARY_BACKGROUND_COLOR |
|||
}); |
|||
|
|||
$mdThemingProvider.definePalette('tb-dark-primary', tbDarkPrimaryPalette); |
|||
$mdThemingProvider.definePalette('tb-dark-primary-background', tbDarkPrimaryBackgroundPalette); |
|||
|
|||
$mdThemingProvider.theme('default') |
|||
.primaryPalette('tb-primary') |
|||
.accentPalette('tb-accent'); |
|||
|
|||
$mdThemingProvider.theme('tb-dark') |
|||
.primaryPalette('tb-dark-primary') |
|||
.accentPalette('tb-accent') |
|||
.backgroundPalette('tb-dark-primary-background') |
|||
.dark(); |
|||
} |
|||
|
|||
function configureTheme() { |
|||
|
|||
var theme = 'indigo'; |
|||
|
|||
if (theme === 'blueGray') { |
|||
blueGrayTheme(); |
|||
} else { |
|||
indigoTheme(); |
|||
} |
|||
|
|||
$mdThemingProvider.setDefaultTheme('default'); |
|||
//$mdThemingProvider.alwaysWatchTheme(true);
|
|||
} |
|||
|
|||
function getLanguageAliases(supportedLangs) { |
|||
var aliases = {}; |
|||
|
|||
supportedLangs.sort().forEach(function(item, index, array) { |
|||
if (item.length === 2) { |
|||
aliases[item] = item; |
|||
aliases[item + '_*'] = item; |
|||
} else { |
|||
var key = item.slice(0, 2); |
|||
if (index === 0 || key !== array[index - 1].slice(0, 2)) { |
|||
aliases[key] = item; |
|||
aliases[key + '_*'] = item; |
|||
} else { |
|||
aliases[item] = item; |
|||
} |
|||
} |
|||
}); |
|||
|
|||
return aliases; |
|||
} |
|||
} |
|||
@ -1,163 +0,0 @@ |
|||
/* |
|||
* Copyright © 2016-2020 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
import './ie.support'; |
|||
|
|||
import 'event-source-polyfill'; |
|||
|
|||
import angular from 'angular'; |
|||
import ngMaterial from 'angular-material'; |
|||
import ngMdIcons from 'angular-material-icons'; |
|||
import ngCookies from 'angular-cookies'; |
|||
import angularSocialshare from 'angular-socialshare'; |
|||
import 'angular-translate'; |
|||
import 'angular-translate-loader-static-files'; |
|||
import 'angular-translate-storage-local'; |
|||
import 'angular-translate-storage-cookie'; |
|||
import 'angular-translate-handler-log'; |
|||
import 'angular-translate-interpolation-messageformat'; |
|||
import 'md-color-picker'; |
|||
import 'md-date-range-picker'; |
|||
import mdPickers from 'mdPickers'; |
|||
import ngSanitize from 'angular-sanitize'; |
|||
import FBAngular from 'angular-fullscreen'; |
|||
import vAccordion from 'v-accordion'; |
|||
import ngAnimate from 'angular-animate'; |
|||
import 'angular-websocket'; |
|||
import uiRouter from 'angular-ui-router'; |
|||
import angularJwt from 'angular-jwt'; |
|||
import 'angular-drag-and-drop-lists'; |
|||
import mdDataTable from 'angular-material-data-table'; |
|||
import fixedTableHeader from 'angular-fixed-table-header'; |
|||
import 'angular-material-expansion-panel'; |
|||
import ngTouch from 'angular-touch'; |
|||
import 'angular-carousel'; |
|||
import 'clipboard'; |
|||
import 'ngclipboard'; |
|||
import 'react'; |
|||
import 'react-dom'; |
|||
import 'material-ui'; |
|||
import 'react-schema-form'; |
|||
import react from 'ngreact'; |
|||
import '@flowjs/ng-flow/dist/ng-flow-standalone.min'; |
|||
import 'ngFlowchart/dist/ngFlowchart'; |
|||
import 'jstree/dist/jstree.min'; |
|||
import 'material-steppers/dist/material-steppers'; |
|||
import 'material-steppers/dist/material-steppers.css' |
|||
import 'jstree-bootstrap-theme/dist/themes/proton/style.min.css'; |
|||
import 'typeface-roboto'; |
|||
import 'font-awesome/css/font-awesome.min.css'; |
|||
import 'angular-material/angular-material.min.css'; |
|||
import 'angular-material-icons/angular-material-icons.css'; |
|||
import 'angular-gridster/dist/angular-gridster.min.css'; |
|||
import 'v-accordion/dist/v-accordion.min.css'; |
|||
import 'md-color-picker/dist/mdColorPicker.min.css'; |
|||
import 'mdPickers/dist/mdPickers.min.css'; |
|||
import 'angular-hotkeys/build/hotkeys.min.css'; |
|||
import 'angular-carousel/dist/angular-carousel.min.css'; |
|||
import 'angular-material-expansion-panel/dist/md-expansion-panel.min.css'; |
|||
import 'ngFlowchart/dist/flowchart.css'; |
|||
import 'md-date-range-picker/src/md-date-range-picker.css'; |
|||
import '../scss/main.scss'; |
|||
|
|||
import thingsboardThirdpartyFix from './common/thirdparty-fix'; |
|||
import thingsboardTranslateHandler from './locale/translate-handler'; |
|||
import thingsboardLogin from './login'; |
|||
import thingsboardDialogs from './components/datakey-config-dialog.controller'; |
|||
import thingsboardMenu from './services/menu.service'; |
|||
import thingsboardRaf from './common/raf.provider'; |
|||
import thingsboardUtils from './common/utils.service'; |
|||
import thingsboardDashboardUtils from './common/dashboard-utils.service'; |
|||
import thingsboardTypes from './common/types.constant'; |
|||
import thingsboardApiTime from './api/time.service'; |
|||
import thingsboardKeyboardShortcut from './components/keyboard-shortcut.filter'; |
|||
import thingsboardHelp from './help/help.directive'; |
|||
import thingsboardToast from './services/toast'; |
|||
import thingsboardClipboard from './services/clipboard.service'; |
|||
import thingsboardHome from './layout'; |
|||
import thingsboardApiLogin from './api/login.service'; |
|||
import thingsboardApiDevice from './api/device.service'; |
|||
import thingsboardApiEntityView from './api/entity-view.service'; |
|||
import thingsboardApiUser from './api/user.service'; |
|||
import thingsboardApiEntityRelation from './api/entity-relation.service'; |
|||
import thingsboardApiAsset from './api/asset.service'; |
|||
import thingsboardApiAttribute from './api/attribute.service'; |
|||
import thingsboardApiEntity from './api/entity.service'; |
|||
import thingsboardApiAlarm from './api/alarm.service'; |
|||
import thingsboardApiAuditLog from './api/audit-log.service'; |
|||
import thingsboardApiComponentDescriptor from './api/component-descriptor.service'; |
|||
import thingsboardApiRuleChain from './api/rule-chain.service'; |
|||
|
|||
import AppConfig from './app.config'; |
|||
import GlobalInterceptor from './global-interceptor.service'; |
|||
import AppRun from './app.run'; |
|||
|
|||
angular.module('thingsboard', [ |
|||
ngMaterial, |
|||
ngMdIcons, |
|||
ngCookies, |
|||
angularSocialshare, |
|||
'pascalprecht.translate', |
|||
'mdColorPicker', |
|||
'ngMaterialDateRangePicker', |
|||
mdPickers, |
|||
ngSanitize, |
|||
FBAngular.name, |
|||
vAccordion, |
|||
ngAnimate, |
|||
'ngWebSocket', |
|||
angularJwt, |
|||
'dndLists', |
|||
mdDataTable, |
|||
fixedTableHeader, |
|||
'material.components.expansionPanels', |
|||
ngTouch, |
|||
'angular-carousel', |
|||
'ngclipboard', |
|||
react.name, |
|||
'flow', |
|||
'flowchart', |
|||
'mdSteppers', |
|||
thingsboardThirdpartyFix, |
|||
thingsboardTranslateHandler, |
|||
thingsboardLogin, |
|||
thingsboardDialogs, |
|||
thingsboardMenu, |
|||
thingsboardRaf, |
|||
thingsboardUtils, |
|||
thingsboardDashboardUtils, |
|||
thingsboardTypes, |
|||
thingsboardApiTime, |
|||
thingsboardKeyboardShortcut, |
|||
thingsboardHelp, |
|||
thingsboardToast, |
|||
thingsboardClipboard, |
|||
thingsboardHome, |
|||
thingsboardApiLogin, |
|||
thingsboardApiDevice, |
|||
thingsboardApiEntityView, |
|||
thingsboardApiUser, |
|||
thingsboardApiEntityRelation, |
|||
thingsboardApiAsset, |
|||
thingsboardApiAttribute, |
|||
thingsboardApiEntity, |
|||
thingsboardApiAlarm, |
|||
thingsboardApiAuditLog, |
|||
thingsboardApiComponentDescriptor, |
|||
thingsboardApiRuleChain, |
|||
uiRouter]) |
|||
.config(AppConfig) |
|||
.factory('globalInterceptor', GlobalInterceptor) |
|||
.run(AppRun); |
|||
@ -1,210 +0,0 @@ |
|||
/* |
|||
* Copyright © 2016-2020 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
import Flow from '@flowjs/ng-flow/dist/ng-flow-standalone.min'; |
|||
import UrlHandler from './url.handler'; |
|||
|
|||
/*@ngInject*/ |
|||
export default function AppRun($rootScope, $window, $injector, $location, $log, $state, $mdDialog, $filter, $q, loginService, userService, $translate) { |
|||
|
|||
$window.Flow = Flow; |
|||
var frame = null; |
|||
try { |
|||
frame = $window.frameElement; |
|||
} catch(e) { |
|||
// ie11 fix
|
|||
} |
|||
|
|||
var forbiddenDialog = null; |
|||
|
|||
$rootScope.iframeMode = false; |
|||
|
|||
if (frame) { |
|||
$rootScope.iframeMode = true; |
|||
var dataWidgetAttr = angular.element(frame).attr('data-widget'); |
|||
if (dataWidgetAttr) { |
|||
$rootScope.editWidgetInfo = angular.fromJson(dataWidgetAttr); |
|||
$rootScope.widgetEditMode = true; |
|||
} |
|||
} |
|||
|
|||
initWatchers(); |
|||
|
|||
var skipStateChange = false; |
|||
|
|||
function initWatchers() { |
|||
$rootScope.unauthenticatedHandle = $rootScope.$on('unauthenticated', function (event, doLogout) { |
|||
if (doLogout) { |
|||
gotoPublicModule('login'); |
|||
} else { |
|||
UrlHandler($injector, $location); |
|||
} |
|||
}); |
|||
|
|||
$rootScope.authenticatedHandle = $rootScope.$on('authenticated', function () { |
|||
UrlHandler($injector, $location); |
|||
}); |
|||
|
|||
$rootScope.forbiddenHandle = $rootScope.$on('forbidden', function () { |
|||
showForbiddenDialog(); |
|||
}); |
|||
|
|||
$rootScope.stateChangeStartHandle = $rootScope.$on('$stateChangeStart', function (evt, to, params) { |
|||
|
|||
if (skipStateChange) { |
|||
skipStateChange = false; |
|||
return; |
|||
} |
|||
|
|||
function waitForUserLoaded() { |
|||
if ($rootScope.userLoadedHandle) { |
|||
$rootScope.userLoadedHandle(); |
|||
} |
|||
$rootScope.userLoadedHandle = $rootScope.$on('userLoaded', function () { |
|||
$rootScope.userLoadedHandle(); |
|||
$state.go(to.name, params); |
|||
}); |
|||
} |
|||
|
|||
function reloadUserFromPublicId() { |
|||
userService.setUserFromJwtToken(null, null, false); |
|||
waitForUserLoaded(); |
|||
userService.reloadUser(); |
|||
} |
|||
|
|||
var locationSearch = $location.search(); |
|||
var publicId = locationSearch.publicId; |
|||
var activateToken = locationSearch.activateToken; |
|||
|
|||
if (to.url === '/createPassword?activateToken' && activateToken && activateToken.length) { |
|||
userService.setUserFromJwtToken(null, null, false); |
|||
} |
|||
|
|||
if (userService.isUserLoaded() === true) { |
|||
if (userService.isAuthenticated()) { |
|||
if (userService.isPublic()) { |
|||
if (userService.parsePublicId() !== publicId) { |
|||
evt.preventDefault(); |
|||
if (publicId && publicId.length > 0) { |
|||
reloadUserFromPublicId(); |
|||
} else { |
|||
userService.logout(); |
|||
} |
|||
return; |
|||
} |
|||
} |
|||
if (userService.forceDefaultPlace(to, params)) { |
|||
evt.preventDefault(); |
|||
gotoDefaultPlace(params); |
|||
} else { |
|||
var authority = userService.getAuthority(); |
|||
if (to.module === 'public') { |
|||
evt.preventDefault(); |
|||
gotoDefaultPlace(params); |
|||
} else if (angular.isDefined(to.auth) && |
|||
to.auth.indexOf(authority) === -1) { |
|||
evt.preventDefault(); |
|||
showForbiddenDialog(); |
|||
} else if (to.redirectTo) { |
|||
evt.preventDefault(); |
|||
$state.go(to.redirectTo, params); |
|||
} else if (to.name === 'home.dashboards.dashboard' && $rootScope.forceFullscreen) { |
|||
evt.preventDefault(); |
|||
$state.go('dashboard', params); |
|||
} |
|||
} |
|||
} else { |
|||
if (publicId && publicId.length > 0) { |
|||
evt.preventDefault(); |
|||
reloadUserFromPublicId(); |
|||
} else if (to.module === 'private') { |
|||
evt.preventDefault(); |
|||
var redirectParams = {}; |
|||
redirectParams.toName = to.name; |
|||
redirectParams.params = params; |
|||
userService.setRedirectParams(redirectParams); |
|||
gotoPublicModule('login', params); |
|||
} else { |
|||
evt.preventDefault(); |
|||
gotoPublicModule(to.name, params); |
|||
} |
|||
} |
|||
} else { |
|||
evt.preventDefault(); |
|||
waitForUserLoaded(); |
|||
} |
|||
}) |
|||
|
|||
$rootScope.pageTitle = 'ThingsBoard'; |
|||
|
|||
$rootScope.stateChangeSuccessHandle = $rootScope.$on('$stateChangeSuccess', function (evt, to, params) { |
|||
if (userService.isPublic() && to.name === 'dashboard') { |
|||
$location.search('publicId', userService.getPublicId()); |
|||
userService.updateLastPublicDashboardId(params.dashboardId); |
|||
} |
|||
if (angular.isDefined(to.data.pageTitle)) { |
|||
$translate(to.data.pageTitle).then(function (translation) { |
|||
$rootScope.pageTitle = 'ThingsBoard | ' + translation; |
|||
}, function (translationId) { |
|||
$rootScope.pageTitle = 'ThingsBoard | ' + translationId; |
|||
}); |
|||
} |
|||
}) |
|||
} |
|||
|
|||
function gotoDefaultPlace(params) { |
|||
userService.gotoDefaultPlace(params); |
|||
} |
|||
|
|||
function gotoPublicModule(name, params) { |
|||
let tasks = []; |
|||
if (name === "login") { |
|||
tasks.push(loginService.loadOAuth2Clients()); |
|||
} |
|||
$q.all(tasks).then( |
|||
() => { |
|||
skipStateChange = true; |
|||
$state.go(name, params); |
|||
}, |
|||
() => { |
|||
skipStateChange = true; |
|||
$state.go(name, params); |
|||
} |
|||
); |
|||
} |
|||
|
|||
function showForbiddenDialog() { |
|||
if (forbiddenDialog === null) { |
|||
$translate(['access.access-forbidden', |
|||
'access.access-forbidden-text', |
|||
'action.cancel', |
|||
'action.sign-in']).then(function (translations) { |
|||
if (forbiddenDialog === null) { |
|||
forbiddenDialog = $mdDialog.confirm() |
|||
.title(translations['access.access-forbidden']) |
|||
.htmlContent(translations['access.access-forbidden-text']) |
|||
.cancel(translations['action.cancel']) |
|||
.ok(translations['action.sign-in']); |
|||
$mdDialog.show(forbiddenDialog).then(function () { |
|||
forbiddenDialog = null; |
|||
userService.logout(); |
|||
}, function () { |
|||
forbiddenDialog = null; |
|||
}); |
|||
} |
|||
}); |
|||
} |
|||
} |
|||
} |
|||
@ -1,45 +0,0 @@ |
|||
<!-- |
|||
|
|||
Copyright © 2016-2020 The Thingsboard Authors |
|||
|
|||
Licensed under the Apache License, Version 2.0 (the "License"); |
|||
you may not use this file except in compliance with the License. |
|||
You may obtain a copy of the License at |
|||
|
|||
http://www.apache.org/licenses/LICENSE-2.0 |
|||
|
|||
Unless required by applicable law or agreed to in writing, software |
|||
distributed under the License is distributed on an "AS IS" BASIS, |
|||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
See the License for the specific language governing permissions and |
|||
limitations under the License. |
|||
|
|||
--> |
|||
<md-dialog aria-label="{{ 'asset.add' | translate }}" tb-help="'assets'" help-container-id="help-container"> |
|||
<form name="theForm" ng-submit="vm.add()"> |
|||
<md-toolbar> |
|||
<div class="md-toolbar-tools"> |
|||
<h2 translate>asset.add</h2> |
|||
<span flex></span> |
|||
<div id="help-container"></div> |
|||
<md-button class="md-icon-button" ng-click="vm.cancel()"> |
|||
<ng-md-icon icon="close" aria-label="{{ 'dialog.close' | translate }}"></ng-md-icon> |
|||
</md-button> |
|||
</div> |
|||
</md-toolbar> |
|||
<md-progress-linear class="md-warn" md-mode="indeterminate" ng-disabled="!$root.loading" ng-show="$root.loading"></md-progress-linear> |
|||
<span style="min-height: 5px;" flex="" ng-show="!$root.loading"></span> |
|||
<md-dialog-content> |
|||
<div class="md-dialog-content"> |
|||
<tb-asset asset="vm.item" is-edit="true" the-form="theForm"></tb-asset> |
|||
</div> |
|||
</md-dialog-content> |
|||
<md-dialog-actions layout="row"> |
|||
<span flex></span> |
|||
<md-button ng-disabled="$root.loading || theForm.$invalid || !theForm.$dirty" type="submit" class="md-raised md-primary"> |
|||
{{ 'action.add' | translate }} |
|||
</md-button> |
|||
<md-button ng-disabled="$root.loading" ng-click="vm.cancel()" style="margin-right:20px;">{{ 'action.cancel' | translate }}</md-button> |
|||
</md-dialog-actions> |
|||
</form> |
|||
</md-dialog> |
|||
@ -1,123 +0,0 @@ |
|||
/* |
|||
* Copyright © 2016-2020 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
/*@ngInject*/ |
|||
export default function AddAssetsToCustomerController(assetService, $mdDialog, $q, customerId, assets) { |
|||
|
|||
var vm = this; |
|||
|
|||
vm.assets = assets; |
|||
vm.searchText = ''; |
|||
|
|||
vm.assign = assign; |
|||
vm.cancel = cancel; |
|||
vm.hasData = hasData; |
|||
vm.noData = noData; |
|||
vm.searchAssetTextUpdated = searchAssetTextUpdated; |
|||
vm.toggleAssetSelection = toggleAssetSelection; |
|||
|
|||
vm.theAssets = { |
|||
getItemAtIndex: function (index) { |
|||
if (index > vm.assets.data.length) { |
|||
vm.theAssets.fetchMoreItems_(index); |
|||
return null; |
|||
} |
|||
var item = vm.assets.data[index]; |
|||
if (item) { |
|||
item.indexNumber = index + 1; |
|||
} |
|||
return item; |
|||
}, |
|||
|
|||
getLength: function () { |
|||
if (vm.assets.hasNext) { |
|||
return vm.assets.data.length + vm.assets.nextPageLink.limit; |
|||
} else { |
|||
return vm.assets.data.length; |
|||
} |
|||
}, |
|||
|
|||
fetchMoreItems_: function () { |
|||
if (vm.assets.hasNext && !vm.assets.pending) { |
|||
vm.assets.pending = true; |
|||
assetService.getTenantAssets(vm.assets.nextPageLink, false).then( |
|||
function success(assets) { |
|||
vm.assets.data = vm.assets.data.concat(assets.data); |
|||
vm.assets.nextPageLink = assets.nextPageLink; |
|||
vm.assets.hasNext = assets.hasNext; |
|||
if (vm.assets.hasNext) { |
|||
vm.assets.nextPageLink.limit = vm.assets.pageSize; |
|||
} |
|||
vm.assets.pending = false; |
|||
}, |
|||
function fail() { |
|||
vm.assets.hasNext = false; |
|||
vm.assets.pending = false; |
|||
}); |
|||
} |
|||
} |
|||
}; |
|||
|
|||
function cancel () { |
|||
$mdDialog.cancel(); |
|||
} |
|||
|
|||
function assign() { |
|||
var tasks = []; |
|||
for (var assetId in vm.assets.selections) { |
|||
tasks.push(assetService.assignAssetToCustomer(customerId, assetId)); |
|||
} |
|||
$q.all(tasks).then(function () { |
|||
$mdDialog.hide(); |
|||
}); |
|||
} |
|||
|
|||
function noData() { |
|||
return vm.assets.data.length == 0 && !vm.assets.hasNext; |
|||
} |
|||
|
|||
function hasData() { |
|||
return vm.assets.data.length > 0; |
|||
} |
|||
|
|||
function toggleAssetSelection($event, asset) { |
|||
$event.stopPropagation(); |
|||
var selected = angular.isDefined(asset.selected) && asset.selected; |
|||
asset.selected = !selected; |
|||
if (asset.selected) { |
|||
vm.assets.selections[asset.id.id] = true; |
|||
vm.assets.selectedCount++; |
|||
} else { |
|||
delete vm.assets.selections[asset.id.id]; |
|||
vm.assets.selectedCount--; |
|||
} |
|||
} |
|||
|
|||
function searchAssetTextUpdated() { |
|||
vm.assets = { |
|||
pageSize: vm.assets.pageSize, |
|||
data: [], |
|||
nextPageLink: { |
|||
limit: vm.assets.pageSize, |
|||
textSearch: vm.searchText |
|||
}, |
|||
selections: {}, |
|||
selectedCount: 0, |
|||
hasNext: true, |
|||
pending: false |
|||
}; |
|||
} |
|||
|
|||
} |
|||
@ -1,77 +0,0 @@ |
|||
<!-- |
|||
|
|||
Copyright © 2016-2020 The Thingsboard Authors |
|||
|
|||
Licensed under the Apache License, Version 2.0 (the "License"); |
|||
you may not use this file except in compliance with the License. |
|||
You may obtain a copy of the License at |
|||
|
|||
http://www.apache.org/licenses/LICENSE-2.0 |
|||
|
|||
Unless required by applicable law or agreed to in writing, software |
|||
distributed under the License is distributed on an "AS IS" BASIS, |
|||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
See the License for the specific language governing permissions and |
|||
limitations under the License. |
|||
|
|||
--> |
|||
<md-dialog aria-label="{{ 'asset.assign-to-customer' | translate }}"> |
|||
<form name="theForm" ng-submit="vm.assign()"> |
|||
<md-toolbar> |
|||
<div class="md-toolbar-tools"> |
|||
<h2 translate>asset.assign-asset-to-customer</h2> |
|||
<span flex></span> |
|||
<md-button class="md-icon-button" ng-click="vm.cancel()"> |
|||
<ng-md-icon icon="close" aria-label="{{ 'dialog.close' | translate }}"></ng-md-icon> |
|||
</md-button> |
|||
</div> |
|||
</md-toolbar> |
|||
<md-progress-linear class="md-warn" md-mode="indeterminate" ng-disabled="!$root.loading" ng-show="$root.loading"></md-progress-linear> |
|||
<span style="min-height: 5px;" flex="" ng-show="!$root.loading"></span> |
|||
<md-dialog-content> |
|||
<div class="md-dialog-content"> |
|||
<fieldset> |
|||
<span translate>asset.assign-asset-to-customer-text</span> |
|||
<md-input-container class="md-block" style='margin-bottom: 0px;'> |
|||
<label> </label> |
|||
<md-icon aria-label="{{ 'action.search' | translate }}" class="material-icons"> |
|||
search |
|||
</md-icon> |
|||
<input id="asset-search" autofocus ng-model="vm.searchText" |
|||
ng-change="vm.searchAssetTextUpdated()" |
|||
placeholder="{{ 'common.enter-search' | translate }}"/> |
|||
</md-input-container> |
|||
<div style='min-height: 150px;'> |
|||
<span translate layout-align="center center" |
|||
style="text-transform: uppercase; display: flex; height: 150px;" |
|||
class="md-subhead" |
|||
ng-show="vm.noData()">asset.no-assets-text</span> |
|||
<md-virtual-repeat-container ng-show="vm.hasData()" |
|||
tb-scope-element="repeatContainer" md-top-index="vm.topIndex" flex |
|||
style='min-height: 150px; width: 100%;'> |
|||
<md-list> |
|||
<md-list-item md-virtual-repeat="asset in vm.theAssets" md-on-demand |
|||
class="repeated-item" flex> |
|||
<md-checkbox ng-click="vm.toggleAssetSelection($event, asset)" |
|||
aria-label="{{ 'item.selected' | translate }}" |
|||
ng-checked="asset.selected"></md-checkbox> |
|||
<span> {{ asset.name }} </span> |
|||
</md-list-item> |
|||
</md-list> |
|||
</md-virtual-repeat-container> |
|||
</div> |
|||
</fieldset> |
|||
</div> |
|||
</md-dialog-content> |
|||
<md-dialog-actions layout="row"> |
|||
<span flex></span> |
|||
<md-button ng-disabled="$root.loading || vm.assets.selectedCount == 0" type="submit" |
|||
class="md-raised md-primary"> |
|||
{{ 'action.assign' | translate }} |
|||
</md-button> |
|||
<md-button ng-disabled="$root.loading" ng-click="vm.cancel()" style="margin-right:20px;">{{ 'action.cancel' | |
|||
translate }} |
|||
</md-button> |
|||
</md-dialog-actions> |
|||
</form> |
|||
</md-dialog> |
|||
@ -1,22 +0,0 @@ |
|||
<!-- |
|||
|
|||
Copyright © 2016-2020 The Thingsboard Authors |
|||
|
|||
Licensed under the Apache License, Version 2.0 (the "License"); |
|||
you may not use this file except in compliance with the License. |
|||
You may obtain a copy of the License at |
|||
|
|||
http://www.apache.org/licenses/LICENSE-2.0 |
|||
|
|||
Unless required by applicable law or agreed to in writing, software |
|||
distributed under the License is distributed on an "AS IS" BASIS, |
|||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
See the License for the specific language governing permissions and |
|||
limitations under the License. |
|||
|
|||
--> |
|||
<div flex layout="column" style="margin-top: -10px;"> |
|||
<div style="text-transform: uppercase; padding-bottom: 10px;">{{vm.item.type}}</div> |
|||
<div class="tb-small" ng-show="vm.isAssignedToCustomer()">{{'asset.assignedToCustomer' | translate}} '{{vm.item.assignedCustomer.title}}'</div> |
|||
<div class="tb-small" ng-show="vm.isPublic()">{{'asset.public' | translate}}</div> |
|||
</div> |
|||
@ -1,75 +0,0 @@ |
|||
<!-- |
|||
|
|||
Copyright © 2016-2020 The Thingsboard Authors |
|||
|
|||
Licensed under the Apache License, Version 2.0 (the "License"); |
|||
you may not use this file except in compliance with the License. |
|||
You may obtain a copy of the License at |
|||
|
|||
http://www.apache.org/licenses/LICENSE-2.0 |
|||
|
|||
Unless required by applicable law or agreed to in writing, software |
|||
distributed under the License is distributed on an "AS IS" BASIS, |
|||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
See the License for the specific language governing permissions and |
|||
limitations under the License. |
|||
|
|||
--> |
|||
<md-button ng-click="onMakePublic({event: $event})" |
|||
ng-show="!isEdit && assetScope === 'tenant' && !isAssignedToCustomer && !isPublic" |
|||
class="md-raised md-primary">{{ 'asset.make-public' | translate }}</md-button> |
|||
<md-button ng-click="onAssignToCustomer({event: $event})" |
|||
ng-show="!isEdit && assetScope === 'tenant' && !isAssignedToCustomer" |
|||
class="md-raised md-primary">{{ 'asset.assign-to-customer' | translate }}</md-button> |
|||
<md-button ng-click="onUnassignFromCustomer({event: $event, isPublic: isPublic})" |
|||
ng-show="!isEdit && (assetScope === 'customer' || assetScope === 'tenant') && isAssignedToCustomer" |
|||
class="md-raised md-primary">{{ isPublic ? 'asset.make-private' : 'asset.unassign-from-customer' | translate }}</md-button> |
|||
<md-button ng-click="onDeleteAsset({event: $event})" |
|||
ng-show="!isEdit && assetScope === 'tenant'" |
|||
class="md-raised md-primary">{{ 'asset.delete' | translate }}</md-button> |
|||
|
|||
<div layout="row"> |
|||
<md-button ngclipboard data-clipboard-action="copy" |
|||
ngclipboard-success="onAssetIdCopied(e)" |
|||
data-clipboard-text="{{asset.id.id}}" ng-show="!isEdit" |
|||
class="md-raised"> |
|||
<md-icon md-svg-icon="mdi:clipboard-arrow-left"></md-icon> |
|||
<span translate>asset.copyId</span> |
|||
</md-button> |
|||
</div> |
|||
|
|||
<md-content class="md-padding" layout="column"> |
|||
<md-input-container class="md-block" |
|||
ng-show="!isEdit && isAssignedToCustomer && !isPublic && assetScope === 'tenant'"> |
|||
<label translate>asset.assignedToCustomer</label> |
|||
<input ng-model="assignedCustomer.title" disabled> |
|||
</md-input-container> |
|||
<div class="tb-small" style="padding-bottom: 10px; padding-left: 2px;" |
|||
ng-show="!isEdit && isPublic && (assetScope === 'customer' || assetScope === 'tenant')"> |
|||
{{ 'asset.asset-public' | translate }} |
|||
</div> |
|||
<fieldset ng-disabled="$root.loading || !isEdit"> |
|||
<md-input-container class="md-block"> |
|||
<label translate>asset.name</label> |
|||
<input required name="name" ng-model="asset.name"> |
|||
<div ng-messages="theForm.name.$error"> |
|||
<div translate ng-message="required">asset.name-required</div> |
|||
</div> |
|||
</md-input-container> |
|||
<tb-entity-subtype-autocomplete |
|||
ng-disabled="$root.loading || !isEdit" |
|||
tb-required="true" |
|||
the-form="theForm" |
|||
ng-model="asset.type" |
|||
entity-type="types.entityType.asset"> |
|||
</tb-entity-subtype-autocomplete> |
|||
<md-input-container class="md-block"> |
|||
<label translate>asset.label</label> |
|||
<input name="label" ng-model="asset.label"> |
|||
</md-input-container> |
|||
<md-input-container class="md-block"> |
|||
<label translate>asset.description</label> |
|||
<textarea ng-model="asset.additionalInfo.description" rows="2"></textarea> |
|||
</md-input-container> |
|||
</fieldset> |
|||
</md-content> |
|||
@ -1,534 +0,0 @@ |
|||
/* |
|||
* Copyright © 2016-2020 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
/* eslint-disable import/no-unresolved, import/default */ |
|||
|
|||
import addAssetTemplate from './add-asset.tpl.html'; |
|||
import assetCard from './asset-card.tpl.html'; |
|||
import assignToCustomerTemplate from './assign-to-customer.tpl.html'; |
|||
import addAssetsToCustomerTemplate from './add-assets-to-customer.tpl.html'; |
|||
|
|||
/* eslint-enable import/no-unresolved, import/default */ |
|||
|
|||
/*@ngInject*/ |
|||
export function AssetCardController(types) { |
|||
|
|||
var vm = this; |
|||
|
|||
vm.types = types; |
|||
|
|||
vm.isAssignedToCustomer = function() { |
|||
if (vm.item && vm.item.customerId && vm.parentCtl.assetsScope === 'tenant' && |
|||
vm.item.customerId.id != vm.types.id.nullUid && !vm.item.assignedCustomer.isPublic) { |
|||
return true; |
|||
} |
|||
return false; |
|||
} |
|||
|
|||
vm.isPublic = function() { |
|||
if (vm.item && vm.item.assignedCustomer && vm.parentCtl.assetsScope === 'tenant' && vm.item.assignedCustomer.isPublic) { |
|||
return true; |
|||
} |
|||
return false; |
|||
} |
|||
} |
|||
|
|||
|
|||
/*@ngInject*/ |
|||
export function AssetController($rootScope, userService, assetService, customerService, $state, $stateParams, |
|||
$document, $mdDialog, $q, $translate, types, importExport) { |
|||
|
|||
var customerId = $stateParams.customerId; |
|||
|
|||
var assetActionsList = []; |
|||
|
|||
var assetGroupActionsList = []; |
|||
|
|||
var assetAddItemActionsList = [ |
|||
{ |
|||
onAction: function ($event) { |
|||
vm.grid.addItem($event); |
|||
}, |
|||
name: function() { return $translate.instant('action.add') }, |
|||
details: function() { return $translate.instant('asset.add-asset-text') }, |
|||
icon: "insert_drive_file" |
|||
}, |
|||
{ |
|||
onAction: function ($event) { |
|||
importExport.importEntities($event, types.entityType.asset).then( |
|||
function() { |
|||
vm.grid.refreshList(); |
|||
} |
|||
); |
|||
}, |
|||
name: function() { return $translate.instant('action.import') }, |
|||
details: function() { return $translate.instant('asset.import') }, |
|||
icon: "file_upload" |
|||
} |
|||
]; |
|||
|
|||
var vm = this; |
|||
|
|||
vm.types = types; |
|||
|
|||
vm.assetGridConfig = { |
|||
deleteItemTitleFunc: deleteAssetTitle, |
|||
deleteItemContentFunc: deleteAssetText, |
|||
deleteItemsTitleFunc: deleteAssetsTitle, |
|||
deleteItemsActionTitleFunc: deleteAssetsActionTitle, |
|||
deleteItemsContentFunc: deleteAssetsText, |
|||
|
|||
saveItemFunc: saveAsset, |
|||
|
|||
getItemTitleFunc: getAssetTitle, |
|||
|
|||
itemCardController: 'AssetCardController', |
|||
itemCardTemplateUrl: assetCard, |
|||
parentCtl: vm, |
|||
|
|||
actionsList: assetActionsList, |
|||
groupActionsList: assetGroupActionsList, |
|||
addItemActions: assetAddItemActionsList, |
|||
|
|||
onGridInited: gridInited, |
|||
|
|||
addItemTemplateUrl: addAssetTemplate, |
|||
|
|||
addItemText: function() { return $translate.instant('asset.add-asset-text') }, |
|||
noItemsText: function() { return $translate.instant('asset.no-assets-text') }, |
|||
itemDetailsText: function() { return $translate.instant('asset.asset-details') }, |
|||
isDetailsReadOnly: isCustomerUser, |
|||
isSelectionEnabled: function () { |
|||
return !isCustomerUser(); |
|||
} |
|||
}; |
|||
|
|||
if (angular.isDefined($stateParams.items) && $stateParams.items !== null) { |
|||
vm.assetGridConfig.items = $stateParams.items; |
|||
} |
|||
|
|||
if (angular.isDefined($stateParams.topIndex) && $stateParams.topIndex > 0) { |
|||
vm.assetGridConfig.topIndex = $stateParams.topIndex; |
|||
} |
|||
|
|||
vm.assetsScope = $state.$current.data.assetsType; |
|||
|
|||
vm.assignToCustomer = assignToCustomer; |
|||
vm.makePublic = makePublic; |
|||
vm.unassignFromCustomer = unassignFromCustomer; |
|||
|
|||
initController(); |
|||
|
|||
function initController() { |
|||
var fetchAssetsFunction = null; |
|||
var deleteAssetFunction = null; |
|||
var refreshAssetsParamsFunction = null; |
|||
|
|||
var user = userService.getCurrentUser(); |
|||
|
|||
if (user.authority === 'CUSTOMER_USER') { |
|||
vm.assetsScope = 'customer_user'; |
|||
customerId = user.customerId; |
|||
} |
|||
if (customerId) { |
|||
vm.customerAssetsTitle = $translate.instant('customer.assets'); |
|||
customerService.getShortCustomerInfo(customerId).then( |
|||
function success(info) { |
|||
if (info.isPublic) { |
|||
vm.customerAssetsTitle = $translate.instant('customer.public-assets'); |
|||
} |
|||
} |
|||
); |
|||
} |
|||
|
|||
if (vm.assetsScope === 'tenant') { |
|||
fetchAssetsFunction = function (pageLink, assetType) { |
|||
return assetService.getTenantAssets(pageLink, true, null, assetType); |
|||
}; |
|||
deleteAssetFunction = function (assetId) { |
|||
return assetService.deleteAsset(assetId); |
|||
}; |
|||
refreshAssetsParamsFunction = function() { |
|||
return {"topIndex": vm.topIndex}; |
|||
}; |
|||
|
|||
assetActionsList.push({ |
|||
onAction: function ($event, item) { |
|||
makePublic($event, item); |
|||
}, |
|||
name: function() { return $translate.instant('action.share') }, |
|||
details: function() { return $translate.instant('asset.make-public') }, |
|||
icon: "share", |
|||
isEnabled: function(asset) { |
|||
return asset && (!asset.customerId || asset.customerId.id === types.id.nullUid); |
|||
} |
|||
}); |
|||
|
|||
assetActionsList.push( |
|||
{ |
|||
onAction: function ($event, item) { |
|||
assignToCustomer($event, [ item.id.id ]); |
|||
}, |
|||
name: function() { return $translate.instant('action.assign') }, |
|||
details: function() { return $translate.instant('asset.assign-to-customer') }, |
|||
icon: "assignment_ind", |
|||
isEnabled: function(asset) { |
|||
return asset && (!asset.customerId || asset.customerId.id === types.id.nullUid); |
|||
} |
|||
} |
|||
); |
|||
|
|||
assetActionsList.push( |
|||
{ |
|||
onAction: function ($event, item) { |
|||
unassignFromCustomer($event, item, false); |
|||
}, |
|||
name: function() { return $translate.instant('action.unassign') }, |
|||
details: function() { return $translate.instant('asset.unassign-from-customer') }, |
|||
icon: "assignment_return", |
|||
isEnabled: function(asset) { |
|||
return asset && asset.customerId && asset.customerId.id !== types.id.nullUid && !asset.assignedCustomer.isPublic; |
|||
} |
|||
} |
|||
); |
|||
|
|||
assetActionsList.push({ |
|||
onAction: function ($event, item) { |
|||
unassignFromCustomer($event, item, true); |
|||
}, |
|||
name: function() { return $translate.instant('action.make-private') }, |
|||
details: function() { return $translate.instant('asset.make-private') }, |
|||
icon: "reply", |
|||
isEnabled: function(asset) { |
|||
return asset && asset.customerId && asset.customerId.id !== types.id.nullUid && asset.assignedCustomer.isPublic; |
|||
} |
|||
}); |
|||
|
|||
assetActionsList.push( |
|||
{ |
|||
onAction: function ($event, item) { |
|||
vm.grid.deleteItem($event, item); |
|||
}, |
|||
name: function() { return $translate.instant('action.delete') }, |
|||
details: function() { return $translate.instant('asset.delete') }, |
|||
icon: "delete" |
|||
} |
|||
); |
|||
|
|||
assetGroupActionsList.push( |
|||
{ |
|||
onAction: function ($event, items) { |
|||
assignAssetsToCustomer($event, items); |
|||
}, |
|||
name: function() { return $translate.instant('asset.assign-assets') }, |
|||
details: function(selectedCount) { |
|||
return $translate.instant('asset.assign-assets-text', {count: selectedCount}, "messageformat"); |
|||
}, |
|||
icon: "assignment_ind" |
|||
} |
|||
); |
|||
|
|||
assetGroupActionsList.push( |
|||
{ |
|||
onAction: function ($event) { |
|||
vm.grid.deleteItems($event); |
|||
}, |
|||
name: function() { return $translate.instant('asset.delete-assets') }, |
|||
details: deleteAssetsActionTitle, |
|||
icon: "delete" |
|||
} |
|||
); |
|||
|
|||
|
|||
|
|||
} else if (vm.assetsScope === 'customer' || vm.assetsScope === 'customer_user') { |
|||
fetchAssetsFunction = function (pageLink, assetType) { |
|||
return assetService.getCustomerAssets(customerId, pageLink, true, null, assetType); |
|||
}; |
|||
deleteAssetFunction = function (assetId) { |
|||
return assetService.unassignAssetFromCustomer(assetId); |
|||
}; |
|||
refreshAssetsParamsFunction = function () { |
|||
return {"customerId": customerId, "topIndex": vm.topIndex}; |
|||
}; |
|||
|
|||
if (vm.assetsScope === 'customer') { |
|||
assetActionsList.push( |
|||
{ |
|||
onAction: function ($event, item) { |
|||
unassignFromCustomer($event, item, false); |
|||
}, |
|||
name: function() { return $translate.instant('action.unassign') }, |
|||
details: function() { return $translate.instant('asset.unassign-from-customer') }, |
|||
icon: "assignment_return", |
|||
isEnabled: function(asset) { |
|||
return asset && !asset.assignedCustomer.isPublic; |
|||
} |
|||
} |
|||
); |
|||
assetActionsList.push( |
|||
{ |
|||
onAction: function ($event, item) { |
|||
unassignFromCustomer($event, item, true); |
|||
}, |
|||
name: function() { return $translate.instant('action.make-private') }, |
|||
details: function() { return $translate.instant('asset.make-private') }, |
|||
icon: "reply", |
|||
isEnabled: function(asset) { |
|||
return asset && asset.assignedCustomer.isPublic; |
|||
} |
|||
} |
|||
); |
|||
|
|||
assetGroupActionsList.push( |
|||
{ |
|||
onAction: function ($event, items) { |
|||
unassignAssetsFromCustomer($event, items); |
|||
}, |
|||
name: function() { return $translate.instant('asset.unassign-assets') }, |
|||
details: function(selectedCount) { |
|||
return $translate.instant('asset.unassign-assets-action-title', {count: selectedCount}, "messageformat"); |
|||
}, |
|||
icon: "assignment_return" |
|||
} |
|||
); |
|||
|
|||
vm.assetGridConfig.addItemAction = { |
|||
onAction: function ($event) { |
|||
addAssetsToCustomer($event); |
|||
}, |
|||
name: function() { return $translate.instant('asset.assign-assets') }, |
|||
details: function() { return $translate.instant('asset.assign-new-asset') }, |
|||
icon: "add" |
|||
}; |
|||
|
|||
|
|||
} else if (vm.assetsScope === 'customer_user') { |
|||
vm.assetGridConfig.addItemAction = {}; |
|||
} |
|||
vm.assetGridConfig.addItemActions = []; |
|||
|
|||
} |
|||
|
|||
vm.assetGridConfig.refreshParamsFunc = refreshAssetsParamsFunction; |
|||
vm.assetGridConfig.fetchItemsFunc = fetchAssetsFunction; |
|||
vm.assetGridConfig.deleteItemFunc = deleteAssetFunction; |
|||
|
|||
} |
|||
|
|||
function deleteAssetTitle(asset) { |
|||
return $translate.instant('asset.delete-asset-title', {assetName: asset.name}); |
|||
} |
|||
|
|||
function deleteAssetText() { |
|||
return $translate.instant('asset.delete-asset-text'); |
|||
} |
|||
|
|||
function deleteAssetsTitle(selectedCount) { |
|||
return $translate.instant('asset.delete-assets-title', {count: selectedCount}, 'messageformat'); |
|||
} |
|||
|
|||
function deleteAssetsActionTitle(selectedCount) { |
|||
return $translate.instant('asset.delete-assets-action-title', {count: selectedCount}, 'messageformat'); |
|||
} |
|||
|
|||
function deleteAssetsText () { |
|||
return $translate.instant('asset.delete-assets-text'); |
|||
} |
|||
|
|||
function gridInited(grid) { |
|||
vm.grid = grid; |
|||
} |
|||
|
|||
function getAssetTitle(asset) { |
|||
return asset ? asset.name : ''; |
|||
} |
|||
|
|||
function saveAsset(asset) { |
|||
var deferred = $q.defer(); |
|||
assetService.saveAsset(asset).then( |
|||
function success(savedAsset) { |
|||
$rootScope.$broadcast('assetSaved'); |
|||
var assets = [ savedAsset ]; |
|||
customerService.applyAssignedCustomersInfo(assets).then( |
|||
function success(items) { |
|||
if (items && items.length == 1) { |
|||
deferred.resolve(items[0]); |
|||
} else { |
|||
deferred.reject(); |
|||
} |
|||
}, |
|||
function fail() { |
|||
deferred.reject(); |
|||
} |
|||
); |
|||
}, |
|||
function fail() { |
|||
deferred.reject(); |
|||
} |
|||
); |
|||
return deferred.promise; |
|||
} |
|||
|
|||
function isCustomerUser() { |
|||
return vm.assetsScope === 'customer_user'; |
|||
} |
|||
|
|||
function assignToCustomer($event, assetIds) { |
|||
if ($event) { |
|||
$event.stopPropagation(); |
|||
} |
|||
var pageSize = 10; |
|||
customerService.getCustomers({limit: pageSize, textSearch: ''}).then( |
|||
function success(_customers) { |
|||
var customers = { |
|||
pageSize: pageSize, |
|||
data: _customers.data, |
|||
nextPageLink: _customers.nextPageLink, |
|||
selection: null, |
|||
hasNext: _customers.hasNext, |
|||
pending: false |
|||
}; |
|||
if (customers.hasNext) { |
|||
customers.nextPageLink.limit = pageSize; |
|||
} |
|||
$mdDialog.show({ |
|||
controller: 'AssignAssetToCustomerController', |
|||
controllerAs: 'vm', |
|||
templateUrl: assignToCustomerTemplate, |
|||
locals: {assetIds: assetIds, customers: customers}, |
|||
parent: angular.element($document[0].body), |
|||
fullscreen: true, |
|||
targetEvent: $event |
|||
}).then(function () { |
|||
vm.grid.refreshList(); |
|||
}, function () { |
|||
}); |
|||
}, |
|||
function fail() { |
|||
}); |
|||
} |
|||
|
|||
function addAssetsToCustomer($event) { |
|||
if ($event) { |
|||
$event.stopPropagation(); |
|||
} |
|||
var pageSize = 10; |
|||
assetService.getTenantAssets({limit: pageSize, textSearch: ''}, false).then( |
|||
function success(_assets) { |
|||
var assets = { |
|||
pageSize: pageSize, |
|||
data: _assets.data, |
|||
nextPageLink: _assets.nextPageLink, |
|||
selections: {}, |
|||
selectedCount: 0, |
|||
hasNext: _assets.hasNext, |
|||
pending: false |
|||
}; |
|||
if (assets.hasNext) { |
|||
assets.nextPageLink.limit = pageSize; |
|||
} |
|||
$mdDialog.show({ |
|||
controller: 'AddAssetsToCustomerController', |
|||
controllerAs: 'vm', |
|||
templateUrl: addAssetsToCustomerTemplate, |
|||
locals: {customerId: customerId, assets: assets}, |
|||
parent: angular.element($document[0].body), |
|||
fullscreen: true, |
|||
targetEvent: $event |
|||
}).then(function () { |
|||
vm.grid.refreshList(); |
|||
}, function () { |
|||
}); |
|||
}, |
|||
function fail() { |
|||
}); |
|||
} |
|||
|
|||
function assignAssetsToCustomer($event, items) { |
|||
var assetIds = []; |
|||
for (var id in items.selections) { |
|||
assetIds.push(id); |
|||
} |
|||
assignToCustomer($event, assetIds); |
|||
} |
|||
|
|||
function unassignFromCustomer($event, asset, isPublic) { |
|||
if ($event) { |
|||
$event.stopPropagation(); |
|||
} |
|||
var title; |
|||
var content; |
|||
var label; |
|||
if (isPublic) { |
|||
title = $translate.instant('asset.make-private-asset-title', {assetName: asset.name}); |
|||
content = $translate.instant('asset.make-private-asset-text'); |
|||
label = $translate.instant('asset.make-private'); |
|||
} else { |
|||
title = $translate.instant('asset.unassign-asset-title', {assetName: asset.name}); |
|||
content = $translate.instant('asset.unassign-asset-text'); |
|||
label = $translate.instant('asset.unassign-asset'); |
|||
} |
|||
var confirm = $mdDialog.confirm() |
|||
.targetEvent($event) |
|||
.title(title) |
|||
.htmlContent(content) |
|||
.ariaLabel(label) |
|||
.cancel($translate.instant('action.no')) |
|||
.ok($translate.instant('action.yes')); |
|||
$mdDialog.show(confirm).then(function () { |
|||
assetService.unassignAssetFromCustomer(asset.id.id).then(function success() { |
|||
vm.grid.refreshList(); |
|||
}); |
|||
}); |
|||
} |
|||
|
|||
function unassignAssetsFromCustomer($event, items) { |
|||
var confirm = $mdDialog.confirm() |
|||
.targetEvent($event) |
|||
.title($translate.instant('asset.unassign-assets-title', {count: items.selectedCount}, 'messageformat')) |
|||
.htmlContent($translate.instant('asset.unassign-assets-text')) |
|||
.ariaLabel($translate.instant('asset.unassign-asset')) |
|||
.cancel($translate.instant('action.no')) |
|||
.ok($translate.instant('action.yes')); |
|||
$mdDialog.show(confirm).then(function () { |
|||
var tasks = []; |
|||
for (var id in items.selections) { |
|||
tasks.push(assetService.unassignAssetFromCustomer(id)); |
|||
} |
|||
$q.all(tasks).then(function () { |
|||
vm.grid.refreshList(); |
|||
}); |
|||
}); |
|||
} |
|||
|
|||
function makePublic($event, asset) { |
|||
if ($event) { |
|||
$event.stopPropagation(); |
|||
} |
|||
var confirm = $mdDialog.confirm() |
|||
.targetEvent($event) |
|||
.title($translate.instant('asset.make-public-asset-title', {assetName: asset.name})) |
|||
.htmlContent($translate.instant('asset.make-public-asset-text')) |
|||
.ariaLabel($translate.instant('asset.make-public')) |
|||
.cancel($translate.instant('action.no')) |
|||
.ok($translate.instant('action.yes')); |
|||
$mdDialog.show(confirm).then(function () { |
|||
assetService.makeAssetPublic(asset.id.id).then(function success() { |
|||
vm.grid.refreshList(); |
|||
}); |
|||
}); |
|||
} |
|||
} |
|||
@ -1,72 +0,0 @@ |
|||
/* |
|||
* Copyright © 2016-2020 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
/* eslint-disable import/no-unresolved, import/default */ |
|||
|
|||
import assetFieldsetTemplate from './asset-fieldset.tpl.html'; |
|||
|
|||
/* eslint-enable import/no-unresolved, import/default */ |
|||
|
|||
/*@ngInject*/ |
|||
export default function AssetDirective($compile, $templateCache, toast, $translate, types, assetService, customerService) { |
|||
var linker = function (scope, element) { |
|||
var template = $templateCache.get(assetFieldsetTemplate); |
|||
element.html(template); |
|||
|
|||
scope.types = types; |
|||
scope.isAssignedToCustomer = false; |
|||
scope.isPublic = false; |
|||
scope.assignedCustomer = null; |
|||
|
|||
scope.$watch('asset', function(newVal) { |
|||
if (newVal) { |
|||
if (scope.asset.customerId && scope.asset.customerId.id !== types.id.nullUid) { |
|||
scope.isAssignedToCustomer = true; |
|||
customerService.getShortCustomerInfo(scope.asset.customerId.id).then( |
|||
function success(customer) { |
|||
scope.assignedCustomer = customer; |
|||
scope.isPublic = customer.isPublic; |
|||
} |
|||
); |
|||
} else { |
|||
scope.isAssignedToCustomer = false; |
|||
scope.isPublic = false; |
|||
scope.assignedCustomer = null; |
|||
} |
|||
} |
|||
}); |
|||
|
|||
scope.onAssetIdCopied = function() { |
|||
toast.showSuccess($translate.instant('asset.idCopiedMessage'), 750, angular.element(element).parent().parent(), 'bottom left'); |
|||
}; |
|||
|
|||
|
|||
$compile(element.contents())(scope); |
|||
} |
|||
return { |
|||
restrict: "E", |
|||
link: linker, |
|||
scope: { |
|||
asset: '=', |
|||
isEdit: '=', |
|||
assetScope: '=', |
|||
theForm: '=', |
|||
onAssignToCustomer: '&', |
|||
onMakePublic: '&', |
|||
onUnassignFromCustomer: '&', |
|||
onDeleteAsset: '&' |
|||
} |
|||
}; |
|||
} |
|||
@ -1,72 +0,0 @@ |
|||
/* |
|||
* Copyright © 2016-2020 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
/* eslint-disable import/no-unresolved, import/default */ |
|||
|
|||
import assetsTemplate from './assets.tpl.html'; |
|||
|
|||
/* eslint-enable import/no-unresolved, import/default */ |
|||
|
|||
/*@ngInject*/ |
|||
export default function AssetRoutes($stateProvider, types) { |
|||
$stateProvider |
|||
.state('home.assets', { |
|||
url: '/assets', |
|||
params: {'topIndex': 0}, |
|||
module: 'private', |
|||
auth: ['TENANT_ADMIN', 'CUSTOMER_USER'], |
|||
views: { |
|||
"content@home": { |
|||
templateUrl: assetsTemplate, |
|||
controller: 'AssetController', |
|||
controllerAs: 'vm' |
|||
} |
|||
}, |
|||
data: { |
|||
assetsType: 'tenant', |
|||
searchEnabled: true, |
|||
searchByEntitySubtype: true, |
|||
searchEntityType: types.entityType.asset, |
|||
pageTitle: 'asset.assets' |
|||
}, |
|||
ncyBreadcrumb: { |
|||
label: '{"icon": "domain", "label": "asset.assets"}' |
|||
} |
|||
}) |
|||
.state('home.customers.assets', { |
|||
url: '/:customerId/assets', |
|||
params: {'topIndex': 0}, |
|||
module: 'private', |
|||
auth: ['TENANT_ADMIN'], |
|||
views: { |
|||
"content@home": { |
|||
templateUrl: assetsTemplate, |
|||
controllerAs: 'vm', |
|||
controller: 'AssetController' |
|||
} |
|||
}, |
|||
data: { |
|||
assetsType: 'customer', |
|||
searchEnabled: true, |
|||
searchByEntitySubtype: true, |
|||
searchEntityType: types.entityType.asset, |
|||
pageTitle: 'customer.assets' |
|||
}, |
|||
ncyBreadcrumb: { |
|||
label: '{"icon": "domain", "label": "{{ vm.customerAssetsTitle }}", "translate": "false"}' |
|||
} |
|||
}); |
|||
|
|||
} |
|||
@ -1,75 +0,0 @@ |
|||
<!-- |
|||
|
|||
Copyright © 2016-2020 The Thingsboard Authors |
|||
|
|||
Licensed under the Apache License, Version 2.0 (the "License"); |
|||
you may not use this file except in compliance with the License. |
|||
You may obtain a copy of the License at |
|||
|
|||
http://www.apache.org/licenses/LICENSE-2.0 |
|||
|
|||
Unless required by applicable law or agreed to in writing, software |
|||
distributed under the License is distributed on an "AS IS" BASIS, |
|||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
See the License for the specific language governing permissions and |
|||
limitations under the License. |
|||
|
|||
--> |
|||
<tb-grid grid-configuration="vm.assetGridConfig"> |
|||
<details-buttons tb-help="'assets'" help-container-id="help-container"> |
|||
<div id="help-container"></div> |
|||
</details-buttons> |
|||
<md-tabs ng-class="{'tb-headless': vm.grid.detailsConfig.isDetailsEditMode}" |
|||
id="tabs" md-border-bottom flex class="tb-absolute-fill"> |
|||
<md-tab label="{{ 'asset.details' | translate }}"> |
|||
<tb-asset asset="vm.grid.operatingItem()" |
|||
is-edit="vm.grid.detailsConfig.isDetailsEditMode" |
|||
asset-scope="vm.assetsScope" |
|||
the-form="vm.grid.detailsForm" |
|||
on-assign-to-customer="vm.assignToCustomer(event, [ vm.grid.detailsConfig.currentItem.id.id ])" |
|||
on-make-public="vm.makePublic(event, vm.grid.detailsConfig.currentItem)" |
|||
on-unassign-from-customer="vm.unassignFromCustomer(event, vm.grid.detailsConfig.currentItem, isPublic)" |
|||
on-delete-asset="vm.grid.deleteItem(event, vm.grid.detailsConfig.currentItem)"></tb-asset> |
|||
</md-tab> |
|||
<md-tab ng-if="!vm.grid.detailsConfig.isDetailsEditMode" md-on-select="vm.grid.triggerResize()" label="{{ 'attribute.attributes' | translate }}"> |
|||
<tb-attribute-table flex |
|||
entity-id="vm.grid.operatingItem().id.id" |
|||
entity-type="{{vm.types.entityType.asset}}" |
|||
entity-name="vm.grid.operatingItem().name" |
|||
default-attribute-scope="{{vm.types.attributesScope.server.value}}"> |
|||
</tb-attribute-table> |
|||
</md-tab> |
|||
<md-tab ng-if="!vm.grid.detailsConfig.isDetailsEditMode" md-on-select="vm.grid.triggerResize()" label="{{ 'attribute.latest-telemetry' | translate }}"> |
|||
<tb-attribute-table flex |
|||
entity-id="vm.grid.operatingItem().id.id" |
|||
entity-type="{{vm.types.entityType.asset}}" |
|||
entity-name="vm.grid.operatingItem().name" |
|||
default-attribute-scope="{{vm.types.latestTelemetry.value}}" |
|||
disable-attribute-scope-selection="true"> |
|||
</tb-attribute-table> |
|||
</md-tab> |
|||
<md-tab ng-if="!vm.grid.detailsConfig.isDetailsEditMode" md-on-select="vm.grid.triggerResize()" label="{{ 'alarm.alarms' | translate }}"> |
|||
<tb-alarm-table flex entity-type="vm.types.entityType.asset" |
|||
entity-id="vm.grid.operatingItem().id.id"> |
|||
</tb-alarm-table> |
|||
</md-tab> |
|||
<md-tab ng-if="!vm.grid.detailsConfig.isDetailsEditMode" md-on-select="vm.grid.triggerResize()" label="{{ 'asset.events' | translate }}"> |
|||
<tb-event-table flex entity-type="vm.types.entityType.asset" |
|||
entity-id="vm.grid.operatingItem().id.id" |
|||
tenant-id="vm.grid.operatingItem().tenantId.id" |
|||
default-event-type="{{vm.types.eventType.error.value}}"> |
|||
</tb-event-table> |
|||
</md-tab> |
|||
<md-tab ng-if="!vm.grid.detailsConfig.isDetailsEditMode" md-on-select="vm.grid.triggerResize()" label="{{ 'relation.relations' | translate }}"> |
|||
<tb-relation-table flex |
|||
entity-id="vm.grid.operatingItem().id.id" |
|||
entity-type="{{vm.types.entityType.asset}}"> |
|||
</tb-relation-table> |
|||
</md-tab> |
|||
<md-tab ng-if="!vm.grid.detailsConfig.isDetailsEditMode && vm.grid.isTenantAdmin()" md-on-select="vm.grid.triggerResize()" label="{{ 'audit-log.audit-logs' | translate }}"> |
|||
<tb-audit-log-table flex entity-type="vm.types.entityType.asset" |
|||
entity-id="vm.grid.operatingItem().id.id" |
|||
audit-log-mode="{{vm.types.auditLogMode.entity}}"> |
|||
</tb-audit-log-table> |
|||
</md-tab> |
|||
</tb-grid> |
|||
@ -1,123 +0,0 @@ |
|||
/* |
|||
* Copyright © 2016-2020 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
/*@ngInject*/ |
|||
export default function AssignAssetToCustomerController(customerService, assetService, $mdDialog, $q, assetIds, customers) { |
|||
|
|||
var vm = this; |
|||
|
|||
vm.customers = customers; |
|||
vm.searchText = ''; |
|||
|
|||
vm.assign = assign; |
|||
vm.cancel = cancel; |
|||
vm.isCustomerSelected = isCustomerSelected; |
|||
vm.hasData = hasData; |
|||
vm.noData = noData; |
|||
vm.searchCustomerTextUpdated = searchCustomerTextUpdated; |
|||
vm.toggleCustomerSelection = toggleCustomerSelection; |
|||
|
|||
vm.theCustomers = { |
|||
getItemAtIndex: function (index) { |
|||
if (index > vm.customers.data.length) { |
|||
vm.theCustomers.fetchMoreItems_(index); |
|||
return null; |
|||
} |
|||
var item = vm.customers.data[index]; |
|||
if (item) { |
|||
item.indexNumber = index + 1; |
|||
} |
|||
return item; |
|||
}, |
|||
|
|||
getLength: function () { |
|||
if (vm.customers.hasNext) { |
|||
return vm.customers.data.length + vm.customers.nextPageLink.limit; |
|||
} else { |
|||
return vm.customers.data.length; |
|||
} |
|||
}, |
|||
|
|||
fetchMoreItems_: function () { |
|||
if (vm.customers.hasNext && !vm.customers.pending) { |
|||
vm.customers.pending = true; |
|||
customerService.getCustomers(vm.customers.nextPageLink).then( |
|||
function success(customers) { |
|||
vm.customers.data = vm.customers.data.concat(customers.data); |
|||
vm.customers.nextPageLink = customers.nextPageLink; |
|||
vm.customers.hasNext = customers.hasNext; |
|||
if (vm.customers.hasNext) { |
|||
vm.customers.nextPageLink.limit = vm.customers.pageSize; |
|||
} |
|||
vm.customers.pending = false; |
|||
}, |
|||
function fail() { |
|||
vm.customers.hasNext = false; |
|||
vm.customers.pending = false; |
|||
}); |
|||
} |
|||
} |
|||
}; |
|||
|
|||
function cancel() { |
|||
$mdDialog.cancel(); |
|||
} |
|||
|
|||
function assign() { |
|||
var tasks = []; |
|||
for (var i=0;i<assetIds.length;i++) { |
|||
tasks.push(assetService.assignAssetToCustomer(vm.customers.selection.id.id, assetIds[i])); |
|||
} |
|||
$q.all(tasks).then(function () { |
|||
$mdDialog.hide(); |
|||
}); |
|||
} |
|||
|
|||
function noData() { |
|||
return vm.customers.data.length == 0 && !vm.customers.hasNext; |
|||
} |
|||
|
|||
function hasData() { |
|||
return vm.customers.data.length > 0; |
|||
} |
|||
|
|||
function toggleCustomerSelection($event, customer) { |
|||
$event.stopPropagation(); |
|||
if (vm.isCustomerSelected(customer)) { |
|||
vm.customers.selection = null; |
|||
} else { |
|||
vm.customers.selection = customer; |
|||
} |
|||
} |
|||
|
|||
function isCustomerSelected(customer) { |
|||
return vm.customers.selection != null && customer && |
|||
customer.id.id === vm.customers.selection.id.id; |
|||
} |
|||
|
|||
function searchCustomerTextUpdated() { |
|||
vm.customers = { |
|||
pageSize: vm.customers.pageSize, |
|||
data: [], |
|||
nextPageLink: { |
|||
limit: vm.customers.pageSize, |
|||
textSearch: vm.searchText |
|||
}, |
|||
selection: null, |
|||
hasNext: true, |
|||
pending: false |
|||
}; |
|||
} |
|||
} |
|||
@ -1,76 +0,0 @@ |
|||
<!-- |
|||
|
|||
Copyright © 2016-2020 The Thingsboard Authors |
|||
|
|||
Licensed under the Apache License, Version 2.0 (the "License"); |
|||
you may not use this file except in compliance with the License. |
|||
You may obtain a copy of the License at |
|||
|
|||
http://www.apache.org/licenses/LICENSE-2.0 |
|||
|
|||
Unless required by applicable law or agreed to in writing, software |
|||
distributed under the License is distributed on an "AS IS" BASIS, |
|||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
See the License for the specific language governing permissions and |
|||
limitations under the License. |
|||
|
|||
--> |
|||
<md-dialog aria-label="{{ 'asset.assign-asset-to-customer' | translate }}"> |
|||
<form name="theForm" ng-submit="vm.assign()"> |
|||
<md-toolbar> |
|||
<div class="md-toolbar-tools"> |
|||
<h2 translate>asset.assign-asset-to-customer</h2> |
|||
<span flex></span> |
|||
<md-button class="md-icon-button" ng-click="vm.cancel()"> |
|||
<ng-md-icon icon="close" aria-label="{{ 'dialog.close' | translate }}"></ng-md-icon> |
|||
</md-button> |
|||
</div> |
|||
</md-toolbar> |
|||
<md-progress-linear class="md-warn" md-mode="indeterminate" ng-disabled="!$root.loading" ng-show="$root.loading"></md-progress-linear> |
|||
<span style="min-height: 5px;" flex="" ng-show="!$root.loading"></span> |
|||
<md-dialog-content> |
|||
<div class="md-dialog-content"> |
|||
<fieldset> |
|||
<span translate>asset.assign-to-customer-text</span> |
|||
<md-input-container class="md-block" style='margin-bottom: 0px;'> |
|||
<label> </label> |
|||
<md-icon aria-label="{{ 'action.search' | translate }}" class="material-icons"> |
|||
search |
|||
</md-icon> |
|||
<input id="customer-search" autofocus ng-model="vm.searchText" |
|||
ng-change="vm.searchCustomerTextUpdated()" |
|||
placeholder="{{ 'common.enter-search' | translate }}"/> |
|||
</md-input-container> |
|||
<div style='min-height: 150px;'> |
|||
<span translate layout-align="center center" |
|||
style="text-transform: uppercase; display: flex; height: 150px;" |
|||
class="md-subhead" |
|||
ng-show="vm.noData()">customer.no-customers-text</span> |
|||
<md-virtual-repeat-container ng-show="vm.hasData()" |
|||
tb-scope-element="repeatContainer" md-top-index="vm.topIndex" flex |
|||
style='min-height: 150px; width: 100%;'> |
|||
<md-list> |
|||
<md-list-item md-virtual-repeat="customer in vm.theCustomers" md-on-demand |
|||
class="repeated-item" flex> |
|||
<md-checkbox ng-click="vm.toggleCustomerSelection($event, customer)" |
|||
aria-label="{{ 'item.selected' | translate }}" |
|||
ng-checked="vm.isCustomerSelected(customer)"></md-checkbox> |
|||
<span> {{ customer.title }} </span> |
|||
</md-list-item> |
|||
</md-list> |
|||
</md-virtual-repeat-container> |
|||
</div> |
|||
</fieldset> |
|||
</div> |
|||
</md-dialog-content> |
|||
<md-dialog-actions layout="row"> |
|||
<span flex></span> |
|||
<md-button ng-disabled="$root.loading || vm.customers.selection==null" type="submit" class="md-raised md-primary"> |
|||
{{ 'action.assign' | translate }} |
|||
</md-button> |
|||
<md-button ng-disabled="$root.loading" ng-click="vm.cancel()" style="margin-right:20px;">{{ 'action.cancel' | |
|||
translate }} |
|||
</md-button> |
|||
</md-dialog-actions> |
|||
</form> |
|||
</md-dialog> |
|||
@ -1,41 +0,0 @@ |
|||
/* |
|||
* Copyright © 2016-2020 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
import uiRouter from 'angular-ui-router'; |
|||
import thingsboardGrid from '../components/grid.directive'; |
|||
import thingsboardApiUser from '../api/user.service'; |
|||
import thingsboardApiAsset from '../api/asset.service'; |
|||
import thingsboardApiCustomer from '../api/customer.service'; |
|||
|
|||
import AssetRoutes from './asset.routes'; |
|||
import {AssetController, AssetCardController} from './asset.controller'; |
|||
import AssignAssetToCustomerController from './assign-to-customer.controller'; |
|||
import AddAssetsToCustomerController from './add-assets-to-customer.controller'; |
|||
import AssetDirective from './asset.directive'; |
|||
|
|||
export default angular.module('thingsboard.asset', [ |
|||
uiRouter, |
|||
thingsboardGrid, |
|||
thingsboardApiUser, |
|||
thingsboardApiAsset, |
|||
thingsboardApiCustomer |
|||
]) |
|||
.config(AssetRoutes) |
|||
.controller('AssetController', AssetController) |
|||
.controller('AssetCardController', AssetCardController) |
|||
.controller('AssignAssetToCustomerController', AssignAssetToCustomerController) |
|||
.controller('AddAssetsToCustomerController', AddAssetsToCustomerController) |
|||
.directive('tbAsset', AssetDirective) |
|||
.name; |
|||
@ -1,104 +0,0 @@ |
|||
/* |
|||
* Copyright © 2016-2020 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
import $ from 'jquery'; |
|||
import 'brace/ext/language_tools'; |
|||
import 'brace/ext/searchbox'; |
|||
import 'brace/mode/java'; |
|||
import 'brace/theme/github'; |
|||
|
|||
/* eslint-disable angular/angularelement */ |
|||
|
|||
import './audit-log-details-dialog.scss'; |
|||
|
|||
/*@ngInject*/ |
|||
export default function AuditLogDetailsDialogController($mdDialog, types, auditLog, showingCallback) { |
|||
|
|||
var vm = this; |
|||
|
|||
showingCallback.onShowing = function(scope, element) { |
|||
updateEditorSize(element, vm.actionData, 'tb-audit-log-action-data'); |
|||
vm.actionDataEditor.resize(); |
|||
if (vm.displayFailureDetails) { |
|||
updateEditorSize(element, vm.actionFailureDetails, 'tb-audit-log-failure-details'); |
|||
vm.failureDetailsEditor.resize(); |
|||
} |
|||
}; |
|||
|
|||
vm.types = types; |
|||
vm.auditLog = auditLog; |
|||
vm.displayFailureDetails = auditLog.actionStatus == types.auditLogActionStatus.FAILURE.value; |
|||
vm.actionData = auditLog.actionDataText; |
|||
vm.actionFailureDetails = auditLog.actionFailureDetails; |
|||
|
|||
vm.actionDataContentOptions = { |
|||
useWrapMode: false, |
|||
mode: 'java', |
|||
showGutter: false, |
|||
showPrintMargin: false, |
|||
theme: 'github', |
|||
advanced: { |
|||
enableSnippets: false, |
|||
enableBasicAutocompletion: false, |
|||
enableLiveAutocompletion: false |
|||
}, |
|||
onLoad: function (_ace) { |
|||
vm.actionDataEditor = _ace; |
|||
} |
|||
}; |
|||
|
|||
vm.failureDetailsContentOptions = { |
|||
useWrapMode: false, |
|||
mode: 'java', |
|||
showGutter: false, |
|||
showPrintMargin: false, |
|||
theme: 'github', |
|||
advanced: { |
|||
enableSnippets: false, |
|||
enableBasicAutocompletion: false, |
|||
enableLiveAutocompletion: false |
|||
}, |
|||
onLoad: function (_ace) { |
|||
vm.failureDetailsEditor = _ace; |
|||
} |
|||
}; |
|||
|
|||
function updateEditorSize(element, content, editorId) { |
|||
var newHeight = 200; |
|||
var newWidth = 600; |
|||
if (content && content.length > 0) { |
|||
var lines = content.split('\n'); |
|||
newHeight = 16 * lines.length + 16; |
|||
var maxLineLength = 0; |
|||
for (var i in lines) { |
|||
var line = lines[i].replace(/\t/g, ' ').replace(/\n/g, ''); |
|||
var lineLength = line.length; |
|||
maxLineLength = Math.max(maxLineLength, lineLength); |
|||
} |
|||
newWidth = 8 * maxLineLength + 16; |
|||
} |
|||
$('#'+editorId, element).height(newHeight.toString() + "px").css('min-height', newHeight.toString() + "px") |
|||
.width(newWidth.toString() + "px"); |
|||
} |
|||
|
|||
vm.close = close; |
|||
|
|||
function close () { |
|||
$mdDialog.hide(); |
|||
} |
|||
|
|||
} |
|||
|
|||
/* eslint-enable angular/angularelement */ |
|||
@ -1,23 +0,0 @@ |
|||
/** |
|||
* Copyright © 2016-2020 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0 |
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
#tb-audit-log-action-data, |
|||
#tb-audit-log-failure-details { |
|||
width: 100%; |
|||
min-width: 400px; |
|||
height: 100%; |
|||
min-height: 50px; |
|||
border: 1px solid #c0c0c0; |
|||
} |
|||
@ -1,49 +0,0 @@ |
|||
<!-- |
|||
|
|||
Copyright © 2016-2020 The Thingsboard Authors |
|||
|
|||
Licensed under the Apache License, Version 2.0 (the "License"); |
|||
you may not use this file except in compliance with the License. |
|||
You may obtain a copy of the License at |
|||
|
|||
http://www.apache.org/licenses/LICENSE-2.0 |
|||
|
|||
Unless required by applicable law or agreed to in writing, software |
|||
distributed under the License is distributed on an "AS IS" BASIS, |
|||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
See the License for the specific language governing permissions and |
|||
limitations under the License. |
|||
|
|||
--> |
|||
<md-dialog aria-label="{{ 'audit-log.audit-log-details' | translate }}"> |
|||
<md-toolbar> |
|||
<div class="md-toolbar-tools"> |
|||
<h2 translate>audit-log.audit-log-details</h2> |
|||
<span flex></span> |
|||
<md-button class="md-icon-button" ng-click="vm.close()"> |
|||
<ng-md-icon icon="close" aria-label="{{ 'dialog.close' | translate }}"></ng-md-icon> |
|||
</md-button> |
|||
</div> |
|||
</md-toolbar> |
|||
<md-dialog-content> |
|||
<div class="md-dialog-content" layout="column"> |
|||
<label translate class="tb-title no-padding">audit-log.action-data</label> |
|||
<div flex id="tb-audit-log-action-data" readonly |
|||
ui-ace="vm.actionDataContentOptions" |
|||
ng-model="vm.actionData"> |
|||
</div> |
|||
<span style="height: 30px;"></span> |
|||
<label ng-show="vm.displayFailureDetails" translate class="tb-title no-padding">audit-log.failure-details</label> |
|||
<div ng-show="vm.displayFailureDetails" flex id="tb-audit-log-failure-details" readonly |
|||
ui-ace="vm.failureDetailsContentOptions" |
|||
ng-model="vm.actionFailureDetails"> |
|||
</div> |
|||
</div> |
|||
</md-dialog-content> |
|||
<md-dialog-actions layout="row"> |
|||
<span flex></span> |
|||
<md-button ng-disabled="$root.loading" ng-click="vm.close()" style="margin-right:20px;">{{ 'action.close' | |
|||
translate }} |
|||
</md-button> |
|||
</md-dialog-actions> |
|||
</md-dialog> |
|||
@ -1,41 +0,0 @@ |
|||
/* |
|||
* Copyright © 2016-2020 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
/* eslint-disable import/no-unresolved, import/default */ |
|||
|
|||
import auditLogHeaderTemplate from './audit-log-header.tpl.html'; |
|||
|
|||
/* eslint-enable import/no-unresolved, import/default */ |
|||
|
|||
/*@ngInject*/ |
|||
export default function AuditLogHeaderDirective($compile, $templateCache, types) { |
|||
|
|||
var linker = function (scope, element, attrs) { |
|||
|
|||
var template = $templateCache.get(auditLogHeaderTemplate); |
|||
element.html(template); |
|||
scope.auditLogMode = attrs.auditLogMode; |
|||
scope.types = types; |
|||
$compile(element.contents())(scope); |
|||
|
|||
}; |
|||
|
|||
return { |
|||
restrict: "A", |
|||
replace: false, |
|||
link: linker, |
|||
scope: false |
|||
}; |
|||
} |
|||
@ -1,24 +0,0 @@ |
|||
<!-- |
|||
|
|||
Copyright © 2016-2020 The Thingsboard Authors |
|||
|
|||
Licensed under the Apache License, Version 2.0 (the "License"); |
|||
you may not use this file except in compliance with the License. |
|||
You may obtain a copy of the License at |
|||
|
|||
http://www.apache.org/licenses/LICENSE-2.0 |
|||
|
|||
Unless required by applicable law or agreed to in writing, software |
|||
distributed under the License is distributed on an "AS IS" BASIS, |
|||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
See the License for the specific language governing permissions and |
|||
limitations under the License. |
|||
|
|||
--> |
|||
<div translate class="tb-cell" flex="30">audit-log.timestamp</div> |
|||
<div ng-if="auditLogMode != types.auditLogMode.entity" translate class="tb-cell" flex="10">audit-log.entity-type</div> |
|||
<div ng-if="auditLogMode != types.auditLogMode.entity" translate class="tb-cell" flex="30">audit-log.entity-name</div> |
|||
<div ng-if="auditLogMode != types.auditLogMode.user" translate class="tb-cell" flex="30">audit-log.user</div> |
|||
<div translate class="tb-cell" flex="15">audit-log.type</div> |
|||
<div translate class="tb-cell" flex="15">audit-log.status</div> |
|||
<div translate class="tb-cell" flex="10">audit-log.details</div> |
|||
@ -1,67 +0,0 @@ |
|||
/* |
|||
* Copyright © 2016-2020 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
/* eslint-disable import/no-unresolved, import/default */ |
|||
|
|||
import auditLogDetailsDialogTemplate from './audit-log-details-dialog.tpl.html'; |
|||
|
|||
import auditLogRowTemplate from './audit-log-row.tpl.html'; |
|||
|
|||
/* eslint-enable import/no-unresolved, import/default */ |
|||
|
|||
/*@ngInject*/ |
|||
export default function AuditLogRowDirective($compile, $templateCache, types, $mdDialog, $document) { |
|||
|
|||
var linker = function (scope, element, attrs) { |
|||
|
|||
var template = $templateCache.get(auditLogRowTemplate); |
|||
element.html(template); |
|||
|
|||
scope.auditLog = attrs.auditLog; |
|||
scope.auditLogMode = attrs.auditLogMode; |
|||
scope.types = types; |
|||
|
|||
scope.showAuditLogDetails = function($event) { |
|||
var onShowingCallback = { |
|||
onShowing: function(){} |
|||
} |
|||
$mdDialog.show({ |
|||
controller: 'AuditLogDetailsDialogController', |
|||
controllerAs: 'vm', |
|||
templateUrl: auditLogDetailsDialogTemplate, |
|||
locals: { |
|||
auditLog: scope.auditLog, |
|||
showingCallback: onShowingCallback |
|||
}, |
|||
parent: angular.element($document[0].body), |
|||
targetEvent: $event, |
|||
fullscreen: true, |
|||
multiple: true, |
|||
onShowing: function(scope, element) { |
|||
onShowingCallback.onShowing(scope, element); |
|||
} |
|||
}); |
|||
} |
|||
|
|||
$compile(element.contents())(scope); |
|||
} |
|||
|
|||
return { |
|||
restrict: "A", |
|||
replace: false, |
|||
link: linker, |
|||
scope: false |
|||
}; |
|||
} |
|||
@ -1,36 +0,0 @@ |
|||
<!-- |
|||
|
|||
Copyright © 2016-2020 The Thingsboard Authors |
|||
|
|||
Licensed under the Apache License, Version 2.0 (the "License"); |
|||
you may not use this file except in compliance with the License. |
|||
You may obtain a copy of the License at |
|||
|
|||
http://www.apache.org/licenses/LICENSE-2.0 |
|||
|
|||
Unless required by applicable law or agreed to in writing, software |
|||
distributed under the License is distributed on an "AS IS" BASIS, |
|||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
See the License for the specific language governing permissions and |
|||
limitations under the License. |
|||
|
|||
--> |
|||
<div class="tb-cell" flex="30">{{ auditLog.createdTime | date : 'yyyy-MM-dd HH:mm:ss' }}</div> |
|||
<div ng-if="auditLogMode != types.auditLogMode.entity" class="tb-cell" flex="10">{{ auditLog.entityTypeText }}</div> |
|||
<div ng-if="auditLogMode != types.auditLogMode.entity" class="tb-cell" flex="30">{{ auditLog.entityName }}</div> |
|||
<div ng-if="auditLogMode != types.auditLogMode.user" class="tb-cell" flex="30">{{ auditLog.userName }}</div> |
|||
<div class="tb-cell" flex="15">{{ auditLog.actionTypeText }}</div> |
|||
<div class="tb-cell" flex="15">{{ auditLog.actionStatusText }}</div> |
|||
<div class="tb-cell" flex="10"> |
|||
<md-button class="md-icon-button md-primary" |
|||
ng-click="showAuditLogDetails($event)" |
|||
aria-label="{{ 'action.view' | translate }}"> |
|||
<md-tooltip md-direction="top"> |
|||
{{ 'audit-log.details' | translate }} |
|||
</md-tooltip> |
|||
<md-icon aria-label="{{ 'action.view' | translate }}" |
|||
class="material-icons"> |
|||
more_horiz |
|||
</md-icon> |
|||
</md-button> |
|||
</div> |
|||
@ -1,262 +0,0 @@ |
|||
/* |
|||
* Copyright © 2016-2020 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
import './audit-log.scss'; |
|||
|
|||
/* eslint-disable import/no-unresolved, import/default */ |
|||
|
|||
import auditLogTableTemplate from './audit-log-table.tpl.html'; |
|||
|
|||
/* eslint-enable import/no-unresolved, import/default */ |
|||
|
|||
/*@ngInject*/ |
|||
export default function AuditLogTableDirective($compile, $templateCache, $rootScope, $filter, $translate, types, auditLogService) { |
|||
|
|||
var linker = function (scope, element) { |
|||
|
|||
var template = $templateCache.get(auditLogTableTemplate); |
|||
|
|||
element.html(template); |
|||
|
|||
scope.types = types; |
|||
|
|||
var pageSize = 20; |
|||
var startTime = 0; |
|||
var endTime = 0; |
|||
|
|||
scope.timewindow = { |
|||
history: { |
|||
timewindowMs: 24 * 60 * 60 * 1000 // 1 day
|
|||
} |
|||
} |
|||
|
|||
scope.topIndex = 0; |
|||
scope.searchText = ''; |
|||
|
|||
scope.theAuditLogs = { |
|||
getItemAtIndex: function (index) { |
|||
if (index > scope.auditLogs.filtered.length) { |
|||
scope.theAuditLogs.fetchMoreItems_(index); |
|||
return null; |
|||
} |
|||
return scope.auditLogs.filtered[index]; |
|||
}, |
|||
|
|||
getLength: function () { |
|||
if (scope.auditLogs.hasNext) { |
|||
return scope.auditLogs.filtered.length + scope.auditLogs.nextPageLink.limit; |
|||
} else { |
|||
return scope.auditLogs.filtered.length; |
|||
} |
|||
}, |
|||
|
|||
fetchMoreItems_: function () { |
|||
if (scope.auditLogs.hasNext && !scope.auditLogs.pending) { |
|||
var promise = getAuditLogsPromise(scope.auditLogs.nextPageLink); |
|||
if (promise) { |
|||
scope.auditLogs.pending = true; |
|||
promise.then( |
|||
function success(auditLogs) { |
|||
scope.auditLogs.data = scope.auditLogs.data.concat(prepareAuditLogsData(auditLogs.data)); |
|||
scope.auditLogs.filtered = $filter('filter')(scope.auditLogs.data, {$: scope.searchText}); |
|||
scope.auditLogs.nextPageLink = auditLogs.nextPageLink; |
|||
scope.auditLogs.hasNext = auditLogs.hasNext; |
|||
if (scope.auditLogs.hasNext) { |
|||
scope.auditLogs.nextPageLink.limit = pageSize; |
|||
} |
|||
scope.auditLogs.pending = false; |
|||
}, |
|||
function fail() { |
|||
scope.auditLogs.hasNext = false; |
|||
scope.auditLogs.pending = false; |
|||
}); |
|||
} else { |
|||
scope.auditLogs.hasNext = false; |
|||
} |
|||
} |
|||
} |
|||
}; |
|||
|
|||
function prepareAuditLogsData(data) { |
|||
data.forEach( |
|||
auditLog => { |
|||
auditLog.entityTypeText = $translate.instant(types.entityTypeTranslations[auditLog.entityId.entityType].type); |
|||
auditLog.actionTypeText = $translate.instant(types.auditLogActionType[auditLog.actionType].name); |
|||
auditLog.actionStatusText = $translate.instant(types.auditLogActionStatus[auditLog.actionStatus].name); |
|||
auditLog.actionDataText = auditLog.actionData ? angular.toJson(auditLog.actionData, true) : ''; |
|||
} |
|||
); |
|||
return data; |
|||
} |
|||
|
|||
scope.$watch("entityId", function(newVal, prevVal) { |
|||
if (newVal && !angular.equals(newVal, prevVal)) { |
|||
resetFilter(); |
|||
scope.reload(); |
|||
} |
|||
}); |
|||
|
|||
scope.$watch("userId", function(newVal, prevVal) { |
|||
if (newVal && !angular.equals(newVal, prevVal)) { |
|||
resetFilter(); |
|||
scope.reload(); |
|||
} |
|||
}); |
|||
|
|||
scope.$watch("customerId", function(newVal, prevVal) { |
|||
if (newVal && !angular.equals(newVal, prevVal)) { |
|||
resetFilter(); |
|||
scope.reload(); |
|||
} |
|||
}); |
|||
|
|||
function getAuditLogsPromise(pageLink) { |
|||
switch(scope.auditLogMode) { |
|||
case types.auditLogMode.tenant: |
|||
return auditLogService.getAuditLogs(pageLink); |
|||
case types.auditLogMode.entity: |
|||
if (scope.entityType && scope.entityId) { |
|||
return auditLogService.getAuditLogsByEntityId(scope.entityType, scope.entityId, |
|||
pageLink); |
|||
} else { |
|||
return null; |
|||
} |
|||
case types.auditLogMode.user: |
|||
if (scope.userId) { |
|||
return auditLogService.getAuditLogsByUserId(scope.userId, pageLink); |
|||
} else { |
|||
return null; |
|||
} |
|||
case types.auditLogMode.customer: |
|||
if (scope.customerId) { |
|||
return auditLogService.getAuditLogsByCustomerId(scope.customerId, pageLink); |
|||
} else { |
|||
return null; |
|||
} |
|||
} |
|||
} |
|||
|
|||
function destroyWatchers() { |
|||
if (scope.timewindowWatchHandle) { |
|||
scope.timewindowWatchHandle(); |
|||
scope.timewindowWatchHandle = null; |
|||
} |
|||
if (scope.searchTextWatchHandle) { |
|||
scope.searchTextWatchHandle(); |
|||
scope.searchTextWatchHandle = null; |
|||
} |
|||
} |
|||
|
|||
function initWatchers() { |
|||
scope.timewindowWatchHandle = scope.$watch("timewindow", function(newVal, prevVal) { |
|||
if (newVal && !angular.equals(newVal, prevVal)) { |
|||
scope.reload(); |
|||
} |
|||
}, true); |
|||
|
|||
scope.searchTextWatchHandle = scope.$watch("searchText", function(newVal, prevVal) { |
|||
if (!angular.equals(newVal, prevVal)) { |
|||
scope.searchTextUpdated(); |
|||
} |
|||
}, true); |
|||
} |
|||
|
|||
function resetFilter() { |
|||
destroyWatchers(); |
|||
scope.timewindow = { |
|||
history: { |
|||
timewindowMs: 24 * 60 * 60 * 1000 // 1 day
|
|||
} |
|||
}; |
|||
scope.searchText = ''; |
|||
initWatchers(); |
|||
} |
|||
|
|||
function updateTimeWindowRange () { |
|||
if (scope.timewindow.history.timewindowMs) { |
|||
var currentTime = (new Date).getTime(); |
|||
startTime = currentTime - scope.timewindow.history.timewindowMs; |
|||
endTime = currentTime; |
|||
} else { |
|||
startTime = scope.timewindow.history.fixedTimewindow.startTimeMs; |
|||
endTime = scope.timewindow.history.fixedTimewindow.endTimeMs; |
|||
} |
|||
} |
|||
|
|||
scope.reload = function() { |
|||
scope.topIndex = 0; |
|||
updateTimeWindowRange(); |
|||
scope.auditLogs = { |
|||
data: [], |
|||
filtered: [], |
|||
nextPageLink: { |
|||
limit: pageSize, |
|||
startTime: startTime, |
|||
endTime: endTime |
|||
}, |
|||
hasNext: true, |
|||
pending: false |
|||
}; |
|||
scope.theAuditLogs.getItemAtIndex(pageSize); |
|||
} |
|||
|
|||
scope.searchTextUpdated = function() { |
|||
scope.auditLogs.filtered = $filter('filter')(scope.auditLogs.data, {$: scope.searchText}); |
|||
scope.theAuditLogs.getItemAtIndex(pageSize); |
|||
} |
|||
|
|||
scope.noData = function() { |
|||
return scope.auditLogs.data.length == 0 && !scope.auditLogs.hasNext; |
|||
} |
|||
|
|||
scope.hasData = function() { |
|||
return scope.auditLogs.data.length > 0; |
|||
} |
|||
|
|||
scope.loading = function() { |
|||
return $rootScope.loading; |
|||
} |
|||
|
|||
scope.hasScroll = function() { |
|||
var repeatContainer = scope.repeatContainer[0]; |
|||
if (repeatContainer) { |
|||
var scrollElement = repeatContainer.children[0]; |
|||
if (scrollElement) { |
|||
return scrollElement.scrollHeight > scrollElement.clientHeight; |
|||
} |
|||
} |
|||
return false; |
|||
} |
|||
|
|||
scope.reload(); |
|||
|
|||
initWatchers(); |
|||
|
|||
$compile(element.contents())(scope); |
|||
} |
|||
|
|||
return { |
|||
restrict: "E", |
|||
link: linker, |
|||
scope: { |
|||
entityType: '=?', |
|||
entityId: '=?', |
|||
userId: '=?', |
|||
customerId: '=?', |
|||
auditLogMode: '@', |
|||
pageMode: '@?' |
|||
} |
|||
}; |
|||
} |
|||
@ -1,68 +0,0 @@ |
|||
<!-- |
|||
|
|||
Copyright © 2016-2020 The Thingsboard Authors |
|||
|
|||
Licensed under the Apache License, Version 2.0 (the "License"); |
|||
you may not use this file except in compliance with the License. |
|||
You may obtain a copy of the License at |
|||
|
|||
http://www.apache.org/licenses/LICENSE-2.0 |
|||
|
|||
Unless required by applicable law or agreed to in writing, software |
|||
distributed under the License is distributed on an "AS IS" BASIS, |
|||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
See the License for the specific language governing permissions and |
|||
limitations under the License. |
|||
|
|||
--> |
|||
<md-content flex class="md-padding tb-absolute-fill" layout="column"> |
|||
<div flex layout="column" class="tb-audit-logs" ng-class="{'md-whiteframe-z1': pageMode}"> |
|||
<div layout="column" layout-gt-sm="row" layout-align-gt-sm="start center" class="tb-audit-log-toolbar" ng-class="{'md-padding': pageMode, 'tb-audit-log-margin-18px': !pageMode}"> |
|||
<tb-timewindow ng-model="timewindow" history-only as-button="true"></tb-timewindow> |
|||
<div flex layout="row" layout-align="start center"> |
|||
<md-button class="md-icon-button" aria-label="{{ 'action.search' | translate }}"> |
|||
<md-icon aria-label="{{ 'action.search' | translate }}" class="material-icons">search</md-icon> |
|||
<md-tooltip md-direction="top"> |
|||
{{'audit-log.search' | translate}} |
|||
</md-tooltip> |
|||
</md-button> |
|||
<md-input-container flex class="tb-audit-log-search-input"> |
|||
<label> </label> |
|||
<input ng-model="searchText" placeholder="{{'audit-log.search' | translate}}"/> |
|||
</md-input-container> |
|||
<md-button ng-disabled="$root.loading" class="md-icon-button" aria-label="Close" ng-click="searchText = ''"> |
|||
<md-icon aria-label="Close" class="material-icons">close</md-icon> |
|||
<md-tooltip md-direction="top"> |
|||
{{ 'audit-log.clear-search' | translate }} |
|||
</md-tooltip> |
|||
</md-button> |
|||
<md-button ng-disabled="$root.loading" |
|||
class="md-icon-button" ng-click="reload()"> |
|||
<md-icon>refresh</md-icon> |
|||
<md-tooltip md-direction="top"> |
|||
{{ 'action.refresh' | translate }} |
|||
</md-tooltip> |
|||
</md-button> |
|||
</div> |
|||
</div> |
|||
<div flex layout="column" class="tb-audit-log-container" ng-class="{'md-whiteframe-z1': !pageMode}"> |
|||
<md-list flex layout="column" class="tb-audit-log-table" ng-class="{'tb-audit-log-table-full': pageMode}"> |
|||
<md-list class="tb-row tb-header" layout="row" layout-align="start center" tb-audit-log-header audit-log-mode="{{auditLogMode}}"> |
|||
</md-list> |
|||
<md-progress-linear style="max-height: 0px;" md-mode="indeterminate" ng-disabled="!$root.loading" |
|||
ng-show="$root.loading"></md-progress-linear> |
|||
<md-divider></md-divider> |
|||
<span translate layout-align="center center" |
|||
style="margin-top: 25px;" |
|||
class="tb-prompt" ng-show="noData()">audit-log.no-audit-logs-prompt</span> |
|||
<md-virtual-repeat-container ng-show="hasData()" flex md-top-index="topIndex" tb-scope-element="repeatContainer"> |
|||
<md-list-item md-virtual-repeat="auditLog in theAuditLogs" md-on-demand flex ng-style="hasScroll() ? {'margin-right':'-15px'} : {}"> |
|||
<md-list class="tb-row" flex layout="row" layout-align="start center" tb-audit-log-row audit-log-mode="{{auditLogMode}}" audit-log="{{auditLog}}"> |
|||
</md-list> |
|||
<md-divider flex></md-divider> |
|||
</md-list-item> |
|||
</md-virtual-repeat-container> |
|||
</md-list> |
|||
</div> |
|||
</div> |
|||
</md-content> |
|||
@ -1,44 +0,0 @@ |
|||
/* |
|||
* Copyright © 2016-2020 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
/* eslint-disable import/no-unresolved, import/default */ |
|||
|
|||
import auditLogsTemplate from './audit-logs.tpl.html'; |
|||
|
|||
/* eslint-enable import/no-unresolved, import/default */ |
|||
|
|||
/*@ngInject*/ |
|||
export default function AuditLogRoutes($stateProvider) { |
|||
$stateProvider |
|||
.state('home.auditLogs', { |
|||
url: '/auditLogs', |
|||
module: 'private', |
|||
auth: ['TENANT_ADMIN'], |
|||
views: { |
|||
"content@home": { |
|||
templateUrl: auditLogsTemplate, |
|||
controller: 'AuditLogsController', |
|||
controllerAs: 'vm' |
|||
} |
|||
}, |
|||
data: { |
|||
searchEnabled: false, |
|||
pageTitle: 'audit-log.audit-logs' |
|||
}, |
|||
ncyBreadcrumb: { |
|||
label: '{"icon": "track_changes", "label": "audit-log.audit-logs"}' |
|||
} |
|||
}); |
|||
} |
|||
@ -1,92 +0,0 @@ |
|||
/** |
|||
* Copyright © 2016-2020 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0 |
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
.tb-audit-logs { |
|||
background-color: #fff; |
|||
|
|||
.tb-audit-log-margin-18px { |
|||
margin-bottom: 18px; |
|||
} |
|||
|
|||
.tb-audit-log-toolbar { |
|||
font-size: 20px; |
|||
} |
|||
|
|||
md-input-container.tb-audit-log-search-input { |
|||
.md-errors-spacer { |
|||
min-height: 0; |
|||
} |
|||
} |
|||
} |
|||
|
|||
.tb-audit-log-container { |
|||
overflow-x: auto; |
|||
} |
|||
|
|||
md-list.tb-audit-log-table { |
|||
min-width: 700px; |
|||
padding: 0; |
|||
|
|||
&.tb-audit-log-table-full { |
|||
min-width: 900px; |
|||
} |
|||
|
|||
md-list-item { |
|||
padding: 0; |
|||
} |
|||
|
|||
.tb-row { |
|||
height: 48px; |
|||
padding: 0; |
|||
overflow: hidden; |
|||
} |
|||
|
|||
.tb-row:hover { |
|||
background-color: #eee; |
|||
} |
|||
|
|||
.tb-header:hover { |
|||
background: none; |
|||
} |
|||
|
|||
.tb-header { |
|||
.tb-cell { |
|||
font-size: 12px; |
|||
font-weight: 700; |
|||
color: rgba(0, 0, 0, .54); |
|||
white-space: nowrap; |
|||
background: none; |
|||
} |
|||
} |
|||
|
|||
.tb-cell { |
|||
padding: 0 24px; |
|||
margin: auto 0; |
|||
overflow: hidden; |
|||
font-size: 13px; |
|||
color: rgba(0, 0, 0, .87); |
|||
text-align: left; |
|||
vertical-align: middle; |
|||
|
|||
.md-button { |
|||
padding: 0; |
|||
margin: 0; |
|||
} |
|||
} |
|||
|
|||
.tb-cell.tb-number { |
|||
text-align: right; |
|||
} |
|||
} |
|||
@ -1,23 +0,0 @@ |
|||
/* |
|||
* Copyright © 2016-2020 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
/*@ngInject*/ |
|||
export default function AuditLogsController(types) { |
|||
|
|||
var vm = this; |
|||
|
|||
vm.types = types; |
|||
|
|||
} |
|||
@ -1,22 +0,0 @@ |
|||
<!-- |
|||
|
|||
Copyright © 2016-2020 The Thingsboard Authors |
|||
|
|||
Licensed under the Apache License, Version 2.0 (the "License"); |
|||
you may not use this file except in compliance with the License. |
|||
You may obtain a copy of the License at |
|||
|
|||
http://www.apache.org/licenses/LICENSE-2.0 |
|||
|
|||
Unless required by applicable law or agreed to in writing, software |
|||
distributed under the License is distributed on an "AS IS" BASIS, |
|||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
See the License for the specific language governing permissions and |
|||
limitations under the License. |
|||
|
|||
--> |
|||
<tb-audit-log-table class="md-whiteframe-z1" |
|||
flex |
|||
audit-log-mode="{{vm.types.auditLogMode.tenant}}" |
|||
page-mode="true"> |
|||
</tb-audit-log-table> |
|||
@ -1,30 +0,0 @@ |
|||
/* |
|||
* Copyright © 2016-2020 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
import AuditLogRoutes from './audit-log.routes'; |
|||
import AuditLogsController from './audit-logs.controller'; |
|||
import AuditLogDetailsDialogController from './audit-log-details-dialog.controller'; |
|||
import AuditLogHeaderDirective from './audit-log-header.directive'; |
|||
import AuditLogRowDirective from './audit-log-row.directive'; |
|||
import AuditLogTableDirective from './audit-log-table.directive'; |
|||
|
|||
export default angular.module('thingsboard.auditLog', []) |
|||
.config(AuditLogRoutes) |
|||
.controller('AuditLogsController', AuditLogsController) |
|||
.controller('AuditLogDetailsDialogController', AuditLogDetailsDialogController) |
|||
.directive('tbAuditLogHeader', AuditLogHeaderDirective) |
|||
.directive('tbAuditLogRow', AuditLogRowDirective) |
|||
.directive('tbAuditLogTable', AuditLogTableDirective) |
|||
.name; |
|||
@ -1,566 +0,0 @@ |
|||
/* |
|||
* Copyright © 2016-2020 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
export default angular.module('thingsboard.dashboardUtils', []) |
|||
.factory('dashboardUtils', DashboardUtils) |
|||
.name; |
|||
|
|||
/*@ngInject*/ |
|||
function DashboardUtils(types, utils, timeService) { |
|||
|
|||
var service = { |
|||
validateAndUpdateDashboard: validateAndUpdateDashboard, |
|||
validateAndUpdateWidget: validateAndUpdateWidget, |
|||
getRootStateId: getRootStateId, |
|||
createSingleWidgetDashboard: createSingleWidgetDashboard, |
|||
createSingleEntityFilter: createSingleEntityFilter, |
|||
getStateLayoutsData: getStateLayoutsData, |
|||
createDefaultState: createDefaultState, |
|||
createDefaultLayoutData: createDefaultLayoutData, |
|||
setLayouts: setLayouts, |
|||
updateLayoutSettings: updateLayoutSettings, |
|||
addWidgetToLayout: addWidgetToLayout, |
|||
removeWidgetFromLayout: removeWidgetFromLayout, |
|||
isSingleLayoutDashboard: isSingleLayoutDashboard, |
|||
removeUnusedWidgets: removeUnusedWidgets, |
|||
getWidgetsArray: getWidgetsArray |
|||
}; |
|||
|
|||
return service; |
|||
|
|||
function validateAndUpdateEntityAliases(configuration, datasourcesByAliasId, targetDevicesByAliasId) { |
|||
var aliasId, entityAlias; |
|||
if (angular.isUndefined(configuration.entityAliases)) { |
|||
configuration.entityAliases = {}; |
|||
if (configuration.deviceAliases) { |
|||
var deviceAliases = configuration.deviceAliases; |
|||
for (aliasId in deviceAliases) { |
|||
var deviceAlias = deviceAliases[aliasId]; |
|||
entityAlias = validateAndUpdateDeviceAlias(aliasId, deviceAlias, datasourcesByAliasId, targetDevicesByAliasId); |
|||
configuration.entityAliases[entityAlias.id] = entityAlias; |
|||
} |
|||
delete configuration.deviceAliases; |
|||
} |
|||
} else { |
|||
var entityAliases = configuration.entityAliases; |
|||
for (aliasId in entityAliases) { |
|||
entityAlias = entityAliases[aliasId]; |
|||
entityAlias = validateAndUpdateEntityAlias(aliasId, entityAlias, datasourcesByAliasId, targetDevicesByAliasId); |
|||
if (aliasId != entityAlias.id) { |
|||
delete entityAliases[aliasId]; |
|||
} |
|||
entityAliases[entityAlias.id] = entityAlias; |
|||
} |
|||
} |
|||
return configuration; |
|||
} |
|||
|
|||
function validateAliasId(aliasId, datasourcesByAliasId, targetDevicesByAliasId) { |
|||
if (!aliasId || !angular.isString(aliasId) || aliasId.length != 36) { |
|||
var newAliasId = utils.guid(); |
|||
var aliasDatasources = datasourcesByAliasId[aliasId]; |
|||
if (aliasDatasources) { |
|||
aliasDatasources.forEach( |
|||
function(datasource) { |
|||
datasource.entityAliasId = newAliasId; |
|||
} |
|||
); |
|||
} |
|||
var targetDeviceAliasIdsList = targetDevicesByAliasId[aliasId]; |
|||
if (targetDeviceAliasIdsList) { |
|||
targetDeviceAliasIdsList.forEach( |
|||
function(targetDeviceAliasIds) { |
|||
targetDeviceAliasIds[0] = newAliasId; |
|||
} |
|||
); |
|||
} |
|||
return newAliasId; |
|||
} else { |
|||
return aliasId; |
|||
} |
|||
} |
|||
|
|||
function validateAndUpdateDeviceAlias(aliasId, deviceAlias, datasourcesByAliasId, targetDevicesByAliasId) { |
|||
aliasId = validateAliasId(aliasId, datasourcesByAliasId, targetDevicesByAliasId); |
|||
var alias = deviceAlias.alias; |
|||
var entityAlias = { |
|||
id: aliasId, |
|||
alias: alias, |
|||
filter: { |
|||
type: null, |
|||
entityType: types.entityType.device, |
|||
resolveMultiple: false |
|||
}, |
|||
} |
|||
if (deviceAlias.deviceFilter) { |
|||
entityAlias.filter.type = |
|||
deviceAlias.deviceFilter.useFilter ? types.aliasFilterType.entityName.value : types.aliasFilterType.entityList.value; |
|||
if (entityAlias.filter.type == types.aliasFilterType.entityList.value) { |
|||
entityAlias.filter.entityList = deviceAlias.deviceFilter.deviceList; |
|||
} else { |
|||
entityAlias.filter.entityNameFilter = deviceAlias.deviceFilter.deviceNameFilter; |
|||
} |
|||
} else { |
|||
entityAlias.filter.type = types.aliasFilterType.entityList.value; |
|||
entityAlias.filter.entityList = [deviceAlias.deviceId]; |
|||
} |
|||
return entityAlias; |
|||
} |
|||
|
|||
function validateAndUpdateEntityAlias(aliasId, entityAlias, datasourcesByAliasId, targetDevicesByAliasId) { |
|||
entityAlias.id = validateAliasId(aliasId, datasourcesByAliasId, targetDevicesByAliasId); |
|||
if (!entityAlias.filter) { |
|||
entityAlias.filter = { |
|||
type: entityAlias.entityFilter.useFilter ? types.aliasFilterType.entityName.value : types.aliasFilterType.entityList.value, |
|||
entityType: entityAlias.entityType, |
|||
resolveMultiple: false |
|||
} |
|||
if (entityAlias.filter.type == types.aliasFilterType.entityList.value) { |
|||
entityAlias.filter.entityList = entityAlias.entityFilter.entityList; |
|||
} else { |
|||
entityAlias.filter.entityNameFilter = entityAlias.entityFilter.entityNameFilter; |
|||
} |
|||
delete entityAlias.entityType; |
|||
delete entityAlias.entityFilter; |
|||
} |
|||
return entityAlias; |
|||
} |
|||
|
|||
function validateAndUpdateWidget(widget) { |
|||
if (!widget.config) { |
|||
widget.config = {}; |
|||
} |
|||
if (!widget.config.datasources) { |
|||
widget.config.datasources = []; |
|||
} |
|||
widget.config.datasources.forEach(function(datasource) { |
|||
if (datasource.type === 'device') { |
|||
datasource.type = types.datasourceType.entity; |
|||
} |
|||
if (datasource.deviceAliasId) { |
|||
datasource.entityAliasId = datasource.deviceAliasId; |
|||
delete datasource.deviceAliasId; |
|||
} |
|||
}); |
|||
//TODO: Temp workaround
|
|||
if (widget.isSystemType && widget.bundleAlias == 'charts' && widget.typeAlias == 'timeseries') { |
|||
widget.typeAlias = 'basic_timeseries'; |
|||
} |
|||
return widget; |
|||
} |
|||
|
|||
function createDefaultLayoutData() { |
|||
return { |
|||
widgets: {}, |
|||
gridSettings: { |
|||
backgroundColor: '#eeeeee', |
|||
color: 'rgba(0,0,0,0.870588)', |
|||
columns: 24, |
|||
margins: [10, 10], |
|||
backgroundSizeMode: '100%' |
|||
} |
|||
}; |
|||
} |
|||
|
|||
function createDefaultLayouts() { |
|||
return { |
|||
'main': createDefaultLayoutData() |
|||
}; |
|||
} |
|||
|
|||
function createDefaultState(name, root) { |
|||
return { |
|||
name: name, |
|||
root: root, |
|||
layouts: createDefaultLayouts() |
|||
} |
|||
} |
|||
|
|||
function validateAndUpdateDashboard(dashboard) { |
|||
if (!dashboard.configuration) { |
|||
dashboard.configuration = {}; |
|||
} |
|||
if (angular.isUndefined(dashboard.configuration.widgets)) { |
|||
dashboard.configuration.widgets = {}; |
|||
} else if (angular.isArray(dashboard.configuration.widgets)) { |
|||
var widgetsMap = {}; |
|||
dashboard.configuration.widgets.forEach(function (widget) { |
|||
if (!widget.id) { |
|||
widget.id = utils.guid(); |
|||
} |
|||
widgetsMap[widget.id] = widget; |
|||
}); |
|||
dashboard.configuration.widgets = widgetsMap; |
|||
} |
|||
for (var id in dashboard.configuration.widgets) { |
|||
var widget = dashboard.configuration.widgets[id]; |
|||
dashboard.configuration.widgets[id] = validateAndUpdateWidget(widget); |
|||
} |
|||
if (angular.isUndefined(dashboard.configuration.states)) { |
|||
dashboard.configuration.states = { |
|||
'default': createDefaultState(dashboard.title, true) |
|||
}; |
|||
|
|||
var mainLayout = dashboard.configuration.states['default'].layouts['main']; |
|||
for (id in dashboard.configuration.widgets) { |
|||
widget = dashboard.configuration.widgets[id]; |
|||
mainLayout.widgets[id] = { |
|||
sizeX: widget.sizeX, |
|||
sizeY: widget.sizeY, |
|||
row: widget.row, |
|||
col: widget.col, |
|||
}; |
|||
if (angular.isDefined(widget.config.mobileHeight)) { |
|||
mainLayout.widgets[id].mobileHeight = widget.config.mobileHeight; |
|||
} |
|||
if (angular.isDefined(widget.config.mobileOrder)) { |
|||
mainLayout.widgets[id].mobileOrder = widget.config.mobileOrder; |
|||
} |
|||
} |
|||
} else { |
|||
var states = dashboard.configuration.states; |
|||
var rootFound = false; |
|||
for (var stateId in states) { |
|||
var state = states[stateId]; |
|||
if (angular.isUndefined(state.root)) { |
|||
state.root = false; |
|||
} else if (state.root) { |
|||
rootFound = true; |
|||
} |
|||
} |
|||
if (!rootFound) { |
|||
var firstStateId = Object.keys(states)[0]; |
|||
states[firstStateId].root = true; |
|||
} |
|||
} |
|||
|
|||
var datasourcesByAliasId = {}; |
|||
var targetDevicesByAliasId = {}; |
|||
for (var widgetId in dashboard.configuration.widgets) { |
|||
widget = dashboard.configuration.widgets[widgetId]; |
|||
widget.config.datasources.forEach(function (datasource) { |
|||
if (datasource.entityAliasId) { |
|||
var aliasId = datasource.entityAliasId; |
|||
var aliasDatasources = datasourcesByAliasId[aliasId]; |
|||
if (!aliasDatasources) { |
|||
aliasDatasources = []; |
|||
datasourcesByAliasId[aliasId] = aliasDatasources; |
|||
} |
|||
aliasDatasources.push(datasource); |
|||
} |
|||
}); |
|||
if (widget.config.targetDeviceAliasIds && widget.config.targetDeviceAliasIds.length) { |
|||
var aliasId = widget.config.targetDeviceAliasIds[0]; |
|||
var targetDeviceAliasIdsList = targetDevicesByAliasId[aliasId]; |
|||
if (!targetDeviceAliasIdsList) { |
|||
targetDeviceAliasIdsList = []; |
|||
targetDevicesByAliasId[aliasId] = targetDeviceAliasIdsList; |
|||
} |
|||
targetDeviceAliasIdsList.push(widget.config.targetDeviceAliasIds); |
|||
} |
|||
} |
|||
|
|||
dashboard.configuration = validateAndUpdateEntityAliases(dashboard.configuration, datasourcesByAliasId, targetDevicesByAliasId); |
|||
|
|||
if (angular.isUndefined(dashboard.configuration.timewindow)) { |
|||
dashboard.configuration.timewindow = timeService.defaultTimewindow(); |
|||
} |
|||
if (angular.isUndefined(dashboard.configuration.settings)) { |
|||
dashboard.configuration.settings = {}; |
|||
dashboard.configuration.settings.stateControllerId = 'entity'; |
|||
dashboard.configuration.settings.showTitle = false; |
|||
dashboard.configuration.settings.showDashboardsSelect = true; |
|||
dashboard.configuration.settings.showEntitiesSelect = true; |
|||
dashboard.configuration.settings.showDashboardTimewindow = true; |
|||
dashboard.configuration.settings.showDashboardExport = true; |
|||
dashboard.configuration.settings.toolbarAlwaysOpen = true; |
|||
} else { |
|||
if (angular.isUndefined(dashboard.configuration.settings.stateControllerId)) { |
|||
dashboard.configuration.settings.stateControllerId = 'entity'; |
|||
} |
|||
} |
|||
if (angular.isDefined(dashboard.configuration.gridSettings)) { |
|||
var gridSettings = dashboard.configuration.gridSettings; |
|||
if (angular.isDefined(gridSettings.showTitle)) { |
|||
dashboard.configuration.settings.showTitle = gridSettings.showTitle; |
|||
delete gridSettings.showTitle; |
|||
} |
|||
if (angular.isDefined(gridSettings.titleColor)) { |
|||
dashboard.configuration.settings.titleColor = gridSettings.titleColor; |
|||
delete gridSettings.titleColor; |
|||
} |
|||
if (angular.isDefined(gridSettings.showDevicesSelect)) { |
|||
dashboard.configuration.settings.showEntitiesSelect = gridSettings.showDevicesSelect; |
|||
delete gridSettings.showDevicesSelect; |
|||
} |
|||
if (angular.isDefined(gridSettings.showEntitiesSelect)) { |
|||
dashboard.configuration.settings.showEntitiesSelect = gridSettings.showEntitiesSelect; |
|||
delete gridSettings.showEntitiesSelect; |
|||
} |
|||
if (angular.isDefined(gridSettings.showDashboardTimewindow)) { |
|||
dashboard.configuration.settings.showDashboardTimewindow = gridSettings.showDashboardTimewindow; |
|||
delete gridSettings.showDashboardTimewindow; |
|||
} |
|||
if (angular.isDefined(gridSettings.showDashboardExport)) { |
|||
dashboard.configuration.settings.showDashboardExport = gridSettings.showDashboardExport; |
|||
delete gridSettings.showDashboardExport; |
|||
} |
|||
dashboard.configuration.states['default'].layouts['main'].gridSettings = gridSettings; |
|||
delete dashboard.configuration.gridSettings; |
|||
} |
|||
return dashboard; |
|||
} |
|||
|
|||
function getRootStateId(states) { |
|||
for (var stateId in states) { |
|||
var state = states[stateId]; |
|||
if (state.root) { |
|||
return stateId; |
|||
} |
|||
} |
|||
return Object.keys(states)[0]; |
|||
} |
|||
|
|||
function createSingleWidgetDashboard(widget) { |
|||
if (!widget.id) { |
|||
widget.id = utils.guid(); |
|||
} |
|||
var dashboard = {}; |
|||
dashboard = validateAndUpdateDashboard(dashboard); |
|||
dashboard.configuration.widgets[widget.id] = widget; |
|||
dashboard.configuration.states['default'].layouts['main'].widgets[widget.id] = { |
|||
sizeX: widget.sizeX, |
|||
sizeY: widget.sizeY, |
|||
row: widget.row, |
|||
col: widget.col, |
|||
}; |
|||
return dashboard; |
|||
} |
|||
|
|||
function createSingleEntityFilter(entityType, entityId) { |
|||
return { |
|||
type: types.aliasFilterType.singleEntity.value, |
|||
singleEntity: { entityType: entityType, id: entityId }, |
|||
resolveMultiple: false |
|||
}; |
|||
} |
|||
|
|||
function getStateLayoutsData(dashboard, targetState) { |
|||
var dashboardConfiguration = dashboard.configuration; |
|||
var states = dashboardConfiguration.states; |
|||
var state = states[targetState]; |
|||
if (state) { |
|||
var allWidgets = dashboardConfiguration.widgets; |
|||
var result = {}; |
|||
for (var l in state.layouts) { |
|||
var layout = state.layouts[l]; |
|||
if (layout) { |
|||
result[l] = { |
|||
widgets: [], |
|||
widgetLayouts: {}, |
|||
gridSettings: {} |
|||
} |
|||
for (var id in layout.widgets) { |
|||
result[l].widgets.push(allWidgets[id]); |
|||
} |
|||
result[l].widgetLayouts = layout.widgets; |
|||
result[l].gridSettings = layout.gridSettings; |
|||
} |
|||
} |
|||
return result; |
|||
} else { |
|||
return null; |
|||
} |
|||
} |
|||
|
|||
function setLayouts(dashboard, targetState, newLayouts) { |
|||
var dashboardConfiguration = dashboard.configuration; |
|||
var states = dashboardConfiguration.states; |
|||
var state = states[targetState]; |
|||
var addedCount = 0; |
|||
var removedCount = 0; |
|||
for (var l in state.layouts) { |
|||
if (!newLayouts[l]) { |
|||
removedCount++; |
|||
} |
|||
} |
|||
for (l in newLayouts) { |
|||
if (!state.layouts[l]) { |
|||
addedCount++; |
|||
} |
|||
} |
|||
state.layouts = newLayouts; |
|||
var layoutsCount = Object.keys(state.layouts).length; |
|||
var newColumns; |
|||
if (addedCount) { |
|||
for (l in state.layouts) { |
|||
newColumns = state.layouts[l].gridSettings.columns * (layoutsCount - addedCount) / layoutsCount; |
|||
state.layouts[l].gridSettings.columns = newColumns; |
|||
} |
|||
} |
|||
if (removedCount) { |
|||
for (l in state.layouts) { |
|||
newColumns = state.layouts[l].gridSettings.columns * (layoutsCount + removedCount) / layoutsCount; |
|||
state.layouts[l].gridSettings.columns = newColumns; |
|||
} |
|||
} |
|||
removeUnusedWidgets(dashboard); |
|||
} |
|||
|
|||
function updateLayoutSettings(layout, gridSettings) { |
|||
var prevGridSettings = layout.gridSettings; |
|||
var prevColumns = prevGridSettings ? prevGridSettings.columns : 24; |
|||
var ratio = gridSettings.columns / prevColumns; |
|||
layout.gridSettings = gridSettings; |
|||
var maxRow = 0; |
|||
for (var w in layout.widgets) { |
|||
var widget = layout.widgets[w]; |
|||
maxRow = Math.max(maxRow, widget.row + widget.sizeY); |
|||
} |
|||
var newMaxRow = Math.round(maxRow * ratio); |
|||
for (w in layout.widgets) { |
|||
widget = layout.widgets[w]; |
|||
if (widget.row + widget.sizeY == maxRow) { |
|||
widget.row = Math.round(widget.row * ratio); |
|||
widget.sizeY = newMaxRow - widget.row; |
|||
} else { |
|||
widget.row = Math.round(widget.row * ratio); |
|||
widget.sizeY = Math.round(widget.sizeY * ratio); |
|||
} |
|||
widget.sizeX = Math.round(widget.sizeX * ratio); |
|||
widget.col = Math.round(widget.col * ratio); |
|||
if (widget.col + widget.sizeX > gridSettings.columns) { |
|||
widget.sizeX = gridSettings.columns - widget.col; |
|||
} |
|||
} |
|||
} |
|||
|
|||
function addWidgetToLayout(dashboard, targetState, targetLayout, widget, originalColumns, originalSize, row, column) { |
|||
var dashboardConfiguration = dashboard.configuration; |
|||
var states = dashboardConfiguration.states; |
|||
var state = states[targetState]; |
|||
var layout = state.layouts[targetLayout]; |
|||
var layoutCount = Object.keys(state.layouts).length; |
|||
if (!widget.id) { |
|||
widget.id = utils.guid(); |
|||
} |
|||
if (!dashboardConfiguration.widgets[widget.id]) { |
|||
dashboardConfiguration.widgets[widget.id] = widget; |
|||
} |
|||
var widgetLayout = { |
|||
sizeX: originalSize ? originalSize.sizeX : widget.sizeX, |
|||
sizeY: originalSize ? originalSize.sizeY : widget.sizeY, |
|||
mobileOrder: widget.config.mobileOrder, |
|||
mobileHeight: widget.config.mobileHeight |
|||
}; |
|||
|
|||
if (angular.isUndefined(originalColumns)) { |
|||
originalColumns = 24; |
|||
} |
|||
|
|||
var gridSettings = layout.gridSettings; |
|||
var columns = 24; |
|||
if (gridSettings && gridSettings.columns) { |
|||
columns = gridSettings.columns; |
|||
} |
|||
|
|||
columns = columns * layoutCount; |
|||
|
|||
if (columns != originalColumns) { |
|||
var ratio = columns / originalColumns; |
|||
widgetLayout.sizeX *= ratio; |
|||
widgetLayout.sizeY *= ratio; |
|||
} |
|||
|
|||
if (row > -1 && column > - 1) { |
|||
widgetLayout.row = row; |
|||
widgetLayout.col = column; |
|||
} else { |
|||
row = 0; |
|||
for (var w in layout.widgets) { |
|||
var existingLayout = layout.widgets[w]; |
|||
var wRow = existingLayout.row ? existingLayout.row : 0; |
|||
var wSizeY = existingLayout.sizeY ? existingLayout.sizeY : 1; |
|||
var bottom = wRow + wSizeY; |
|||
row = Math.max(row, bottom); |
|||
} |
|||
widgetLayout.row = row; |
|||
widgetLayout.col = 0; |
|||
} |
|||
|
|||
layout.widgets[widget.id] = widgetLayout; |
|||
} |
|||
|
|||
function removeWidgetFromLayout(dashboard, targetState, targetLayout, widgetId) { |
|||
var dashboardConfiguration = dashboard.configuration; |
|||
var states = dashboardConfiguration.states; |
|||
var state = states[targetState]; |
|||
var layout = state.layouts[targetLayout]; |
|||
delete layout.widgets[widgetId]; |
|||
removeUnusedWidgets(dashboard); |
|||
} |
|||
|
|||
function isSingleLayoutDashboard(dashboard) { |
|||
var dashboardConfiguration = dashboard.configuration; |
|||
var states = dashboardConfiguration.states; |
|||
var stateKeys = Object.keys(states); |
|||
if (stateKeys.length === 1) { |
|||
var state = states[stateKeys[0]]; |
|||
var layouts = state.layouts; |
|||
var layoutKeys = Object.keys(layouts); |
|||
if (layoutKeys.length === 1) { |
|||
return { |
|||
state: stateKeys[0], |
|||
layout: layoutKeys[0] |
|||
} |
|||
} |
|||
} |
|||
return null; |
|||
} |
|||
|
|||
function removeUnusedWidgets(dashboard) { |
|||
var dashboardConfiguration = dashboard.configuration; |
|||
var states = dashboardConfiguration.states; |
|||
var widgets = dashboardConfiguration.widgets; |
|||
for (var widgetId in widgets) { |
|||
var found = false; |
|||
for (var s in states) { |
|||
var state = states[s]; |
|||
for (var l in state.layouts) { |
|||
var layout = state.layouts[l]; |
|||
if (layout.widgets[widgetId]) { |
|||
found = true; |
|||
break; |
|||
} |
|||
} |
|||
} |
|||
if (!found) { |
|||
delete dashboardConfiguration.widgets[widgetId]; |
|||
} |
|||
|
|||
} |
|||
} |
|||
|
|||
function getWidgetsArray(dashboard) { |
|||
var widgetsArray = []; |
|||
var dashboardConfiguration = dashboard.configuration; |
|||
var widgets = dashboardConfiguration.widgets; |
|||
for (var widgetId in widgets) { |
|||
var widget = widgets[widgetId]; |
|||
widgetsArray.push(widget); |
|||
} |
|||
return widgetsArray; |
|||
} |
|||
} |
|||
@ -1,49 +0,0 @@ |
|||
/* |
|||
* Copyright © 2016-2020 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
export default angular.module('thingsboard.raf', []) |
|||
.provider('tbRaf', TbRAFProvider) |
|||
.name; |
|||
|
|||
function TbRAFProvider() { |
|||
/*@ngInject*/ |
|||
this.$get = function($window, $timeout) { |
|||
var requestAnimationFrame = $window.requestAnimationFrame || |
|||
$window.webkitRequestAnimationFrame; |
|||
|
|||
var cancelAnimationFrame = $window.cancelAnimationFrame || |
|||
$window.webkitCancelAnimationFrame || |
|||
$window.webkitCancelRequestAnimationFrame; |
|||
|
|||
var rafSupported = !!requestAnimationFrame; |
|||
var raf = rafSupported |
|||
? function(fn) { |
|||
var id = requestAnimationFrame(fn); |
|||
return function() { |
|||
cancelAnimationFrame(id); |
|||
}; |
|||
} |
|||
: function(fn) { |
|||
var timer = $timeout(fn, 16.66, false); |
|||
return function() { |
|||
$timeout.cancel(timer); |
|||
}; |
|||
}; |
|||
|
|||
raf.supported = rafSupported; |
|||
|
|||
return raf; |
|||
}; |
|||
} |
|||
@ -1,458 +0,0 @@ |
|||
/* |
|||
* Copyright © 2016-2020 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
import tinycolor from 'tinycolor2'; |
|||
import moment from 'moment'; |
|||
|
|||
export default angular.module('thingsboard.thirdpartyFix', []) |
|||
.factory('Fullscreen', Fullscreen) |
|||
.factory('$mdColorPicker', mdColorPicker) |
|||
.provider('$mdpDatePicker', mdpDatePicker) |
|||
.provider('$mdpTimePicker', mdpTimePicker) |
|||
.name; |
|||
|
|||
/*@ngInject*/ |
|||
function Fullscreen($document, $rootScope) { |
|||
|
|||
/* eslint-disable */ |
|||
|
|||
var document = $document[0]; |
|||
|
|||
// ensure ALLOW_KEYBOARD_INPUT is available and enabled
|
|||
var isKeyboardAvailbleOnFullScreen = (typeof Element !== 'undefined' && 'ALLOW_KEYBOARD_INPUT' in Element) && Element.ALLOW_KEYBOARD_INPUT; |
|||
|
|||
var emitter = $rootScope.$new(); |
|||
|
|||
// listen event on document instead of element to avoid firefox limitation
|
|||
// see https://developer.mozilla.org/en-US/docs/Web/Guide/API/DOM/Using_full_screen_mode
|
|||
$document.on('fullscreenchange webkitfullscreenchange mozfullscreenchange MSFullscreenChange', function(){ |
|||
emitter.$emit('FBFullscreen.change', serviceInstance.isEnabled()); |
|||
}); |
|||
|
|||
var serviceInstance = { |
|||
$on: angular.bind(emitter, emitter.$on), |
|||
all: function() { |
|||
serviceInstance.enable( document.documentElement ); |
|||
}, |
|||
enable: function(element) { |
|||
if(element.requestFullScreen) { |
|||
element.requestFullScreen(); |
|||
} else if(element.mozRequestFullScreen) { |
|||
element.mozRequestFullScreen(); |
|||
} else if(element.webkitRequestFullscreen) { |
|||
// Safari temporary fix
|
|||
//if (/Version\/[\d]{1,2}(\.[\d]{1,2}){1}(\.(\d){1,2}){0,1} Safari/.test(navigator.userAgent)) {
|
|||
if (/Safari/.test(navigator.userAgent)) { |
|||
element.webkitRequestFullscreen(); |
|||
} else { |
|||
element.webkitRequestFullscreen(isKeyboardAvailbleOnFullScreen); |
|||
} |
|||
} else if (element.msRequestFullscreen) { |
|||
element.msRequestFullscreen(); |
|||
} |
|||
}, |
|||
cancel: function() { |
|||
if(document.cancelFullScreen) { |
|||
document.cancelFullScreen(); |
|||
} else if(document.mozCancelFullScreen) { |
|||
document.mozCancelFullScreen(); |
|||
} else if(document.webkitExitFullscreen) { |
|||
document.webkitExitFullscreen(); |
|||
} else if (document.msExitFullscreen) { |
|||
document.msExitFullscreen(); |
|||
} |
|||
}, |
|||
isEnabled: function(){ |
|||
var fullscreenElement = document.fullscreenElement || document.mozFullScreenElement || document.webkitFullscreenElement || document.msFullscreenElement; |
|||
return fullscreenElement ? true : false; |
|||
}, |
|||
toggleAll: function(){ |
|||
serviceInstance.isEnabled() ? serviceInstance.cancel() : serviceInstance.all(); |
|||
}, |
|||
isSupported: function(){ |
|||
var docElm = document.documentElement; |
|||
var requestFullscreen = docElm.requestFullScreen || docElm.mozRequestFullScreen || docElm.webkitRequestFullscreen || docElm.msRequestFullscreen; |
|||
return requestFullscreen ? true : false; |
|||
} |
|||
}; |
|||
|
|||
/* eslint-enable */ |
|||
|
|||
return serviceInstance; |
|||
} |
|||
|
|||
/*@ngInject*/ |
|||
function mdColorPicker($q, $mdDialog, mdColorPickerHistory) { |
|||
var dialog; |
|||
|
|||
/* eslint-disable angular/definedundefined */ |
|||
|
|||
return { |
|||
show: function (options) |
|||
{ |
|||
if ( options === undefined ) { |
|||
options = {}; |
|||
} |
|||
//console.log( 'DIALOG OPTIONS', options );
|
|||
// Defaults
|
|||
// Dialog Properties
|
|||
options.hasBackdrop = options.hasBackdrop === undefined ? true : options.hasBackdrop; |
|||
options.clickOutsideToClose = options.clickOutsideToClose === undefined ? true : options.clickOutsideToClose; |
|||
options.defaultValue = options.defaultValue === undefined ? '#FFFFFF' : options.defaultValue; |
|||
options.focusOnOpen = options.focusOnOpen === undefined ? false : options.focusOnOpen; |
|||
options.preserveScope = options.preserveScope === undefined ? true : options.preserveScope; |
|||
if (options.skipHide !== undefined) { |
|||
options.multiple = options.skipHide; |
|||
} |
|||
if (options.multiple === undefined) { |
|||
options.multiple = true; |
|||
} |
|||
|
|||
// mdColorPicker Properties
|
|||
options.mdColorAlphaChannel = options.mdColorAlphaChannel === undefined ? false : options.mdColorAlphaChannel; |
|||
options.mdColorSpectrum = options.mdColorSpectrum === undefined ? true : options.mdColorSpectrum; |
|||
options.mdColorSliders = options.mdColorSliders === undefined ? true : options.mdColorSliders; |
|||
options.mdColorGenericPalette = options.mdColorGenericPalette === undefined ? true : options.mdColorGenericPalette; |
|||
options.mdColorMaterialPalette = options.mdColorMaterialPalette === undefined ? true : options.mdColorMaterialPalette; |
|||
options.mdColorHistory = options.mdColorHistory === undefined ? true : options.mdColorHistory; |
|||
|
|||
|
|||
dialog = $mdDialog.show({ |
|||
templateUrl: 'mdColorPickerDialog.tpl.html', |
|||
hasBackdrop: options.hasBackdrop, |
|||
clickOutsideToClose: options.clickOutsideToClose, |
|||
|
|||
controller: ['$scope', 'options', function( $scope, options ) { |
|||
//console.log( "DIALOG CONTROLLER OPEN", Date.now() - dateClick );
|
|||
$scope.close = function close() |
|||
{ |
|||
$mdDialog.cancel(); |
|||
}; |
|||
$scope.ok = function ok() |
|||
{ |
|||
$mdDialog.hide( $scope.value ); |
|||
}; |
|||
$scope.hide = $scope.ok; |
|||
|
|||
|
|||
|
|||
$scope.value = options.value; |
|||
$scope.default = options.defaultValue; |
|||
$scope.random = options.random; |
|||
|
|||
$scope.mdColorAlphaChannel = options.mdColorAlphaChannel; |
|||
$scope.mdColorSpectrum = options.mdColorSpectrum; |
|||
$scope.mdColorSliders = options.mdColorSliders; |
|||
$scope.mdColorGenericPalette = options.mdColorGenericPalette; |
|||
$scope.mdColorMaterialPalette = options.mdColorMaterialPalette; |
|||
$scope.mdColorHistory = options.mdColorHistory; |
|||
$scope.mdColorDefaultTab = options.mdColorDefaultTab; |
|||
|
|||
}], |
|||
|
|||
locals: { |
|||
options: options, |
|||
}, |
|||
preserveScope: options.preserveScope, |
|||
multiple: options.multiple, |
|||
|
|||
targetEvent: options.$event, |
|||
focusOnOpen: options.focusOnOpen, |
|||
autoWrap: false, |
|||
onShowing: function() { |
|||
// console.log( "DIALOG OPEN START", Date.now() - dateClick );
|
|||
}, |
|||
onComplete: function() { |
|||
// console.log( "DIALOG OPEN COMPLETE", Date.now() - dateClick );
|
|||
} |
|||
}); |
|||
|
|||
dialog.then(function (value) { |
|||
mdColorPickerHistory.add(new tinycolor(value)); |
|||
}, function () { }); |
|||
|
|||
return dialog; |
|||
}, |
|||
hide: function() { |
|||
return dialog.hide(); |
|||
}, |
|||
cancel: function() { |
|||
return dialog.cancel(); |
|||
} |
|||
}; |
|||
|
|||
/* eslint-enable angular/definedundefined */ |
|||
} |
|||
|
|||
function DatePickerCtrl($scope, $mdDialog, $mdMedia, $timeout, currentDate, options) { |
|||
var self = this; |
|||
|
|||
this.date = moment(currentDate); |
|||
this.minDate = options.minDate && moment(options.minDate).isValid() ? moment(options.minDate) : null; |
|||
this.maxDate = options.maxDate && moment(options.maxDate).isValid() ? moment(options.maxDate) : null; |
|||
this.displayFormat = options.displayFormat || "ddd, MMM DD"; |
|||
this.dateFilter = angular.isFunction(options.dateFilter) ? options.dateFilter : null; |
|||
this.selectingYear = false; |
|||
|
|||
// validate min and max date
|
|||
if (this.minDate && this.maxDate) { |
|||
if (this.maxDate.isBefore(this.minDate)) { |
|||
this.maxDate = moment(this.minDate).add(1, 'days'); |
|||
} |
|||
} |
|||
|
|||
if (this.date) { |
|||
// check min date
|
|||
if (this.minDate && this.date.isBefore(this.minDate)) { |
|||
this.date = moment(this.minDate); |
|||
} |
|||
|
|||
// check max date
|
|||
if (this.maxDate && this.date.isAfter(this.maxDate)) { |
|||
this.date = moment(this.maxDate); |
|||
} |
|||
} |
|||
|
|||
this.yearItems = { |
|||
currentIndex_: 0, |
|||
PAGE_SIZE: 5, |
|||
START: (self.minDate ? self.minDate.year() : 1900), |
|||
END: (self.maxDate ? self.maxDate.year() : 0), |
|||
getItemAtIndex: function(index) { |
|||
if(this.currentIndex_ < index) |
|||
this.currentIndex_ = index; |
|||
|
|||
return this.START + index; |
|||
}, |
|||
getLength: function() { |
|||
return Math.min( |
|||
this.currentIndex_ + Math.floor(this.PAGE_SIZE / 2), |
|||
Math.abs(this.START - this.END) + 1 |
|||
); |
|||
} |
|||
}; |
|||
|
|||
$scope.$mdMedia = $mdMedia; |
|||
$scope.year = this.date.year(); |
|||
|
|||
this.selectYear = function(year) { |
|||
self.date.year(year); |
|||
$scope.year = year; |
|||
self.selectingYear = false; |
|||
self.animate(); |
|||
}; |
|||
|
|||
this.showYear = function() { |
|||
self.yearTopIndex = (self.date.year() - self.yearItems.START) + Math.floor(self.yearItems.PAGE_SIZE / 2); |
|||
self.yearItems.currentIndex_ = (self.date.year() - self.yearItems.START) + 1; |
|||
self.selectingYear = true; |
|||
}; |
|||
|
|||
this.showCalendar = function() { |
|||
self.selectingYear = false; |
|||
}; |
|||
|
|||
this.cancel = function() { |
|||
$mdDialog.cancel(); |
|||
}; |
|||
|
|||
this.confirm = function() { |
|||
var date = this.date; |
|||
|
|||
if (this.minDate && this.date.isBefore(this.minDate)) { |
|||
date = moment(this.minDate); |
|||
} |
|||
|
|||
if (this.maxDate && this.date.isAfter(this.maxDate)) { |
|||
date = moment(this.maxDate); |
|||
} |
|||
|
|||
$mdDialog.hide(date.toDate()); |
|||
}; |
|||
|
|||
this.animate = function() { |
|||
self.animating = true; |
|||
$timeout(angular.noop).then(function() { |
|||
self.animating = false; |
|||
}) |
|||
}; |
|||
} |
|||
|
|||
/*@ngInject*/ |
|||
function mdpDatePicker() { |
|||
var LABEL_OK = "OK", |
|||
LABEL_CANCEL = "Cancel", |
|||
DISPLAY_FORMAT = "ddd, MMM DD"; |
|||
|
|||
this.setDisplayFormat = function(format) { |
|||
DISPLAY_FORMAT = format; |
|||
}; |
|||
|
|||
this.setOKButtonLabel = function(label) { |
|||
LABEL_OK = label; |
|||
}; |
|||
|
|||
this.setCancelButtonLabel = function(label) { |
|||
LABEL_CANCEL = label; |
|||
}; |
|||
|
|||
/*@ngInject*/ |
|||
this.$get = function($mdDialog) { |
|||
var datePicker = function(currentDate, options) { |
|||
if (!angular.isDate(currentDate)) currentDate = Date.now(); |
|||
if (!angular.isObject(options)) options = {}; |
|||
|
|||
options.displayFormat = DISPLAY_FORMAT; |
|||
|
|||
return $mdDialog.show({ |
|||
controller: ['$scope', '$mdDialog', '$mdMedia', '$timeout', 'currentDate', 'options', DatePickerCtrl], |
|||
controllerAs: 'datepicker', |
|||
clickOutsideToClose: true, |
|||
template: '<md-dialog aria-label="" class="mdp-datepicker" ng-class="{ \'portrait\': !$mdMedia(\'gt-xs\') }">' + |
|||
'<md-dialog-content layout="row" layout-wrap>' + |
|||
'<div layout="column" layout-align="start center">' + |
|||
'<md-toolbar layout-align="start start" flex class="mdp-datepicker-date-wrapper md-hue-1 md-primary" layout="column">' + |
|||
'<span class="mdp-datepicker-year" ng-click="datepicker.showYear()" ng-class="{ \'active\': datepicker.selectingYear }">{{ datepicker.date.format(\'YYYY\') }}</span>' + |
|||
'<span class="mdp-datepicker-date" ng-click="datepicker.showCalendar()" ng-class="{ \'active\': !datepicker.selectingYear }">{{ datepicker.date.format(datepicker.displayFormat) }}</span> ' + |
|||
'</md-toolbar>' + |
|||
'</div>' + |
|||
'<div>' + |
|||
'<div class="mdp-datepicker-select-year mdp-animation-zoom" layout="column" layout-align="center start" ng-if="datepicker.selectingYear">' + |
|||
'<md-virtual-repeat-container md-auto-shrink md-top-index="datepicker.yearTopIndex">' + |
|||
'<div flex md-virtual-repeat="item in datepicker.yearItems" md-on-demand class="repeated-year">' + |
|||
'<span class="md-button" ng-click="datepicker.selectYear(item)" md-ink-ripple ng-class="{ \'md-primary current\': item == year }">{{ item }}</span>' + |
|||
'</div>' + |
|||
'</md-virtual-repeat-container>' + |
|||
'</div>' + |
|||
'<mdp-calendar ng-if="!datepicker.selectingYear" class="mdp-animation-zoom" date="datepicker.date" min-date="datepicker.minDate" date-filter="datepicker.dateFilter" max-date="datepicker.maxDate"></mdp-calendar>' + |
|||
'<md-dialog-actions layout="row">' + |
|||
'<span flex></span>' + |
|||
'<md-button ng-click="datepicker.cancel()" aria-label="' + LABEL_CANCEL + '">' + LABEL_CANCEL + '</md-button>' + |
|||
'<md-button ng-click="datepicker.confirm()" class="md-primary" aria-label="' + LABEL_OK + '">' + LABEL_OK + '</md-button>' + |
|||
'</md-dialog-actions>' + |
|||
'</div>' + |
|||
'</md-dialog-content>' + |
|||
'</md-dialog>', |
|||
targetEvent: options.targetEvent, |
|||
locals: { |
|||
currentDate: currentDate, |
|||
options: options |
|||
}, |
|||
multiple: true |
|||
}); |
|||
}; |
|||
|
|||
return datePicker; |
|||
}; |
|||
|
|||
} |
|||
|
|||
function TimePickerCtrl($scope, $mdDialog, time, autoSwitch, $mdMedia) { |
|||
var self = this; |
|||
this.VIEW_HOURS = 1; |
|||
this.VIEW_MINUTES = 2; |
|||
this.currentView = this.VIEW_HOURS; |
|||
this.time = moment(time); |
|||
this.autoSwitch = !!autoSwitch; |
|||
|
|||
this.clockHours = parseInt(this.time.format("h")); |
|||
this.clockMinutes = parseInt(this.time.minutes()); |
|||
|
|||
$scope.$mdMedia = $mdMedia; |
|||
|
|||
this.switchView = function() { |
|||
self.currentView = self.currentView == self.VIEW_HOURS ? self.VIEW_MINUTES : self.VIEW_HOURS; |
|||
}; |
|||
|
|||
this.setAM = function() { |
|||
if(self.time.hours() >= 12) |
|||
self.time.hour(self.time.hour() - 12); |
|||
}; |
|||
|
|||
this.setPM = function() { |
|||
if(self.time.hours() < 12) |
|||
self.time.hour(self.time.hour() + 12); |
|||
}; |
|||
|
|||
this.cancel = function() { |
|||
$mdDialog.cancel(); |
|||
}; |
|||
|
|||
this.confirm = function() { |
|||
$mdDialog.hide(this.time.toDate()); |
|||
}; |
|||
} |
|||
|
|||
/*@ngInject*/ |
|||
function mdpTimePicker() { |
|||
var LABEL_OK = "OK", |
|||
LABEL_CANCEL = "Cancel"; |
|||
|
|||
this.setOKButtonLabel = function(label) { |
|||
LABEL_OK = label; |
|||
}; |
|||
|
|||
this.setCancelButtonLabel = function(label) { |
|||
LABEL_CANCEL = label; |
|||
}; |
|||
|
|||
/*@ngInject*/ |
|||
this.$get = function($mdDialog) { |
|||
var timePicker = function(time, options) { |
|||
if(!angular.isDate(time)) time = Date.now(); |
|||
if (!angular.isObject(options)) options = {}; |
|||
|
|||
return $mdDialog.show({ |
|||
controller: ['$scope', '$mdDialog', 'time', 'autoSwitch', '$mdMedia', TimePickerCtrl], |
|||
controllerAs: 'timepicker', |
|||
clickOutsideToClose: true, |
|||
template: '<md-dialog aria-label="" class="mdp-timepicker" ng-class="{ \'portrait\': !$mdMedia(\'gt-xs\') }">' + |
|||
'<md-dialog-content layout-gt-xs="row" layout-wrap>' + |
|||
'<md-toolbar layout-gt-xs="column" layout-xs="row" layout-align="center center" flex class="mdp-timepicker-time md-hue-1 md-primary">' + |
|||
'<div class="mdp-timepicker-selected-time">' + |
|||
'<span ng-class="{ \'active\': timepicker.currentView == timepicker.VIEW_HOURS }" ng-click="timepicker.currentView = timepicker.VIEW_HOURS">{{ timepicker.time.format("h") }}</span>:' + |
|||
'<span ng-class="{ \'active\': timepicker.currentView == timepicker.VIEW_MINUTES }" ng-click="timepicker.currentView = timepicker.VIEW_MINUTES">{{ timepicker.time.format("mm") }}</span>' + |
|||
'</div>' + |
|||
'<div layout="column" class="mdp-timepicker-selected-ampm">' + |
|||
'<span ng-click="timepicker.setAM()" ng-class="{ \'active\': timepicker.time.hours() < 12 }">AM</span>' + |
|||
'<span ng-click="timepicker.setPM()" ng-class="{ \'active\': timepicker.time.hours() >= 12 }">PM</span>' + |
|||
'</div>' + |
|||
'</md-toolbar>' + |
|||
'<div>' + |
|||
'<div class="mdp-clock-switch-container" ng-switch="timepicker.currentView" layout layout-align="center center">' + |
|||
'<mdp-clock class="mdp-animation-zoom" auto-switch="timepicker.autoSwitch" time="timepicker.time" type="hours" ng-switch-when="1"></mdp-clock>' + |
|||
'<mdp-clock class="mdp-animation-zoom" auto-switch="timepicker.autoSwitch" time="timepicker.time" type="minutes" ng-switch-when="2"></mdp-clock>' + |
|||
'</div>' + |
|||
|
|||
'<md-dialog-actions layout="row">' + |
|||
'<span flex></span>' + |
|||
'<md-button ng-click="timepicker.cancel()" aria-label="' + LABEL_CANCEL + '">' + LABEL_CANCEL + '</md-button>' + |
|||
'<md-button ng-click="timepicker.confirm()" class="md-primary" aria-label="' + LABEL_OK + '">' + LABEL_OK + '</md-button>' + |
|||
'</md-dialog-actions>' + |
|||
'</div>' + |
|||
'</md-dialog-content>' + |
|||
'</md-dialog>', |
|||
targetEvent: options.targetEvent, |
|||
locals: { |
|||
time: time, |
|||
autoSwitch: options.autoSwitch |
|||
}, |
|||
multiple: true |
|||
}); |
|||
}; |
|||
|
|||
return timePicker; |
|||
}; |
|||
} |
|||
@ -1,991 +0,0 @@ |
|||
/* |
|||
* Copyright © 2016-2020 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
export default angular.module('thingsboard.types', []) |
|||
.constant('types', |
|||
{ |
|||
serverErrorCode: { |
|||
general: 2, |
|||
authentication: 10, |
|||
jwtTokenExpired: 11, |
|||
credentialsExpired: 15, |
|||
permissionDenied: 20, |
|||
invalidArguments: 30, |
|||
badRequestParams: 31, |
|||
itemNotFound: 32, |
|||
tooManyRequests: 33, |
|||
tooManyUpdates: 34 |
|||
}, |
|||
entryPoints: { |
|||
login: "/api/auth/login", |
|||
tokenRefresh: "/api/auth/token", |
|||
nonTokenBased: "/api/noauth" |
|||
}, |
|||
id: { |
|||
nullUid: "13814000-1dd2-11b2-8080-808080808080", |
|||
}, |
|||
aggregation: { |
|||
min: { |
|||
value: "MIN", |
|||
name: "aggregation.min" |
|||
}, |
|||
max: { |
|||
value: "MAX", |
|||
name: "aggregation.max" |
|||
}, |
|||
avg: { |
|||
value: "AVG", |
|||
name: "aggregation.avg" |
|||
}, |
|||
sum: { |
|||
value: "SUM", |
|||
name: "aggregation.sum" |
|||
}, |
|||
count: { |
|||
value: "COUNT", |
|||
name: "aggregation.count" |
|||
}, |
|||
none: { |
|||
value: "NONE", |
|||
name: "aggregation.none" |
|||
} |
|||
}, |
|||
alarmFields: { |
|||
createdTime: { |
|||
keyName: 'createdTime', |
|||
value: "createdTime", |
|||
name: "alarm.created-time", |
|||
time: true |
|||
}, |
|||
startTime: { |
|||
keyName: 'startTime', |
|||
value: "startTs", |
|||
name: "alarm.start-time", |
|||
time: true |
|||
}, |
|||
endTime: { |
|||
keyName: 'endTime', |
|||
value: "endTs", |
|||
name: "alarm.end-time", |
|||
time: true |
|||
}, |
|||
ackTime: { |
|||
keyName: 'ackTime', |
|||
value: "ackTs", |
|||
name: "alarm.ack-time", |
|||
time: true |
|||
}, |
|||
clearTime: { |
|||
keyName: 'clearTime', |
|||
value: "clearTs", |
|||
name: "alarm.clear-time", |
|||
time: true |
|||
}, |
|||
originator: { |
|||
keyName: 'originator', |
|||
value: "originatorName", |
|||
name: "alarm.originator" |
|||
}, |
|||
originatorType: { |
|||
keyName: 'originatorType', |
|||
value: "originator.entityType", |
|||
name: "alarm.originator-type" |
|||
}, |
|||
type: { |
|||
keyName: 'type', |
|||
value: "type", |
|||
name: "alarm.type" |
|||
}, |
|||
severity: { |
|||
keyName: 'severity', |
|||
value: "severity", |
|||
name: "alarm.severity" |
|||
}, |
|||
status: { |
|||
keyName: 'status', |
|||
value: "status", |
|||
name: "alarm.status" |
|||
} |
|||
}, |
|||
alarmStatus: { |
|||
activeUnack: "ACTIVE_UNACK", |
|||
activeAck: "ACTIVE_ACK", |
|||
clearedUnack: "CLEARED_UNACK", |
|||
clearedAck: "CLEARED_ACK" |
|||
}, |
|||
alarmSearchStatus: { |
|||
any: "ANY", |
|||
active: "ACTIVE", |
|||
cleared: "CLEARED", |
|||
ack: "ACK", |
|||
unack: "UNACK" |
|||
}, |
|||
alarmSeverity: { |
|||
"CRITICAL": { |
|||
name: "alarm.severity-critical", |
|||
class: "tb-critical", |
|||
color: "red" |
|||
}, |
|||
"MAJOR": { |
|||
name: "alarm.severity-major", |
|||
class: "tb-major", |
|||
color: "orange" |
|||
}, |
|||
"MINOR": { |
|||
name: "alarm.severity-minor", |
|||
class: "tb-minor", |
|||
color: "#ffca3d" |
|||
}, |
|||
"WARNING": { |
|||
name: "alarm.severity-warning", |
|||
class: "tb-warning", |
|||
color: "#abab00" |
|||
}, |
|||
"INDETERMINATE": { |
|||
name: "alarm.severity-indeterminate", |
|||
class: "tb-indeterminate", |
|||
color: "green" |
|||
} |
|||
}, |
|||
auditLogActionType: { |
|||
"ADDED": { |
|||
name: "audit-log.type-added" |
|||
}, |
|||
"DELETED": { |
|||
name: "audit-log.type-deleted" |
|||
}, |
|||
"UPDATED": { |
|||
name: "audit-log.type-updated" |
|||
}, |
|||
"ATTRIBUTES_UPDATED": { |
|||
name: "audit-log.type-attributes-updated" |
|||
}, |
|||
"ATTRIBUTES_DELETED": { |
|||
name: "audit-log.type-attributes-deleted" |
|||
}, |
|||
"RPC_CALL": { |
|||
name: "audit-log.type-rpc-call" |
|||
}, |
|||
"CREDENTIALS_UPDATED": { |
|||
name: "audit-log.type-credentials-updated" |
|||
}, |
|||
"ASSIGNED_TO_CUSTOMER": { |
|||
name: "audit-log.type-assigned-to-customer" |
|||
}, |
|||
"UNASSIGNED_FROM_CUSTOMER": { |
|||
name: "audit-log.type-unassigned-from-customer" |
|||
}, |
|||
"ACTIVATED": { |
|||
name: "audit-log.type-activated" |
|||
}, |
|||
"SUSPENDED": { |
|||
name: "audit-log.type-suspended" |
|||
}, |
|||
"CREDENTIALS_READ": { |
|||
name: "audit-log.type-credentials-read" |
|||
}, |
|||
"ATTRIBUTES_READ": { |
|||
name: "audit-log.type-attributes-read" |
|||
}, |
|||
"RELATION_ADD_OR_UPDATE": { |
|||
name: "audit-log.type-relation-add-or-update" |
|||
}, |
|||
"RELATION_DELETED": { |
|||
name: "audit-log.type-relation-delete" |
|||
}, |
|||
"RELATIONS_DELETED": { |
|||
name: "audit-log.type-relations-delete" |
|||
}, |
|||
"ALARM_ACK": { |
|||
name: "audit-log.type-alarm-ack" |
|||
}, |
|||
"ALARM_CLEAR": { |
|||
name: "audit-log.type-alarm-clear" |
|||
}, |
|||
"LOGIN": { |
|||
name: "audit-log.type-login" |
|||
}, |
|||
"LOGOUT": { |
|||
name: "audit-log.type-logout" |
|||
}, |
|||
"LOCKOUT": { |
|||
name: "audit-log.type-lockout" |
|||
} |
|||
}, |
|||
auditLogActionStatus: { |
|||
"SUCCESS": { |
|||
value: "SUCCESS", |
|||
name: "audit-log.status-success" |
|||
}, |
|||
"FAILURE": { |
|||
value: "FAILURE", |
|||
name: "audit-log.status-failure" |
|||
} |
|||
}, |
|||
auditLogMode: { |
|||
tenant: "tenant", |
|||
entity: "entity", |
|||
user: "user", |
|||
customer: "customer" |
|||
}, |
|||
aliasFilterType: { |
|||
singleEntity: { |
|||
value: 'singleEntity', |
|||
name: 'alias.filter-type-single-entity' |
|||
}, |
|||
entityList: { |
|||
value: 'entityList', |
|||
name: 'alias.filter-type-entity-list' |
|||
}, |
|||
entityName: { |
|||
value: 'entityName', |
|||
name: 'alias.filter-type-entity-name' |
|||
}, |
|||
stateEntity: { |
|||
value: 'stateEntity', |
|||
name: 'alias.filter-type-state-entity' |
|||
}, |
|||
assetType: { |
|||
value: 'assetType', |
|||
name: 'alias.filter-type-asset-type' |
|||
}, |
|||
deviceType: { |
|||
value: 'deviceType', |
|||
name: 'alias.filter-type-device-type' |
|||
}, |
|||
entityViewType: { |
|||
value: 'entityViewType', |
|||
name: 'alias.filter-type-entity-view-type' |
|||
}, |
|||
relationsQuery: { |
|||
value: 'relationsQuery', |
|||
name: 'alias.filter-type-relations-query' |
|||
}, |
|||
assetSearchQuery: { |
|||
value: 'assetSearchQuery', |
|||
name: 'alias.filter-type-asset-search-query' |
|||
}, |
|||
deviceSearchQuery: { |
|||
value: 'deviceSearchQuery', |
|||
name: 'alias.filter-type-device-search-query' |
|||
}, |
|||
entityViewSearchQuery: { |
|||
value: 'entityViewSearchQuery', |
|||
name: 'alias.filter-type-entity-view-search-query' |
|||
} |
|||
}, |
|||
direction: { |
|||
column: { |
|||
value: "column", |
|||
name: "direction.column" |
|||
}, |
|||
row: { |
|||
value: "row", |
|||
name: "direction.row" |
|||
} |
|||
}, |
|||
position: { |
|||
top: { |
|||
value: "top", |
|||
name: "position.top" |
|||
}, |
|||
bottom: { |
|||
value: "bottom", |
|||
name: "position.bottom" |
|||
}, |
|||
left: { |
|||
value: "left", |
|||
name: "position.left" |
|||
}, |
|||
right: { |
|||
value: "right", |
|||
name: "position.right" |
|||
} |
|||
}, |
|||
datasourceType: { |
|||
function: "function", |
|||
entity: "entity" |
|||
}, |
|||
dataKeyType: { |
|||
timeseries: "timeseries", |
|||
attribute: "attribute", |
|||
function: "function", |
|||
alarm: "alarm", |
|||
entityField: "entityField" |
|||
}, |
|||
contentType: { |
|||
"JSON": { |
|||
value: "JSON", |
|||
name: "content-type.json", |
|||
code: "json" |
|||
}, |
|||
"TEXT": { |
|||
value: "TEXT", |
|||
name: "content-type.text", |
|||
code: "text" |
|||
}, |
|||
"BINARY": { |
|||
value: "BINARY", |
|||
name: "content-type.binary", |
|||
code: "text" |
|||
} |
|||
}, |
|||
componentType: { |
|||
enrichment: "ENRICHMENT", |
|||
filter: "FILTER", |
|||
transformation: "TRANSFORMATION", |
|||
action: "ACTION", |
|||
external: "EXTERNAL" |
|||
}, |
|||
entityType: { |
|||
device: "DEVICE", |
|||
asset: "ASSET", |
|||
tenant: "TENANT", |
|||
customer: "CUSTOMER", |
|||
user: "USER", |
|||
dashboard: "DASHBOARD", |
|||
alarm: "ALARM", |
|||
rulechain: "RULE_CHAIN", |
|||
rulenode: "RULE_NODE", |
|||
entityView: "ENTITY_VIEW" |
|||
}, |
|||
importEntityColumnType: { |
|||
name: { |
|||
name: 'import.column-type.name', |
|||
value: 'name' |
|||
}, |
|||
type: { |
|||
name: 'import.column-type.type', |
|||
value: 'type' |
|||
}, |
|||
label: { |
|||
name: 'import.column-type.label', |
|||
value: 'label' |
|||
}, |
|||
clientAttribute: { |
|||
name: 'import.column-type.client-attribute', |
|||
value: 'CLIENT_ATTRIBUTE' |
|||
}, |
|||
sharedAttribute: { |
|||
name: 'import.column-type.shared-attribute', |
|||
value: 'SHARED_ATTRIBUTE' |
|||
}, |
|||
serverAttribute: { |
|||
name: 'import.column-type.server-attribute', |
|||
value: 'SERVER_ATTRIBUTE' |
|||
}, |
|||
timeseries: { |
|||
name: 'import.column-type.timeseries', |
|||
value: 'TIMESERIES' |
|||
}, |
|||
entityField: { |
|||
name: 'import.column-type.entity-field', |
|||
value: 'ENTITY_FIELD' |
|||
}, |
|||
accessToken: { |
|||
name: 'import.column-type.access-token', |
|||
value: 'ACCESS_TOKEN' |
|||
}, |
|||
isGateway: { |
|||
name: 'import.column-type.isgateway', |
|||
value: 'gateway' |
|||
}, |
|||
description: { |
|||
name: 'import.column-type.description', |
|||
value: 'description' |
|||
} |
|||
}, |
|||
aliasEntityType: { |
|||
current_customer: "CURRENT_CUSTOMER", |
|||
current_tenant: "CURRENT_TENANT" |
|||
}, |
|||
entityTypeTranslations: { |
|||
"DEVICE": { |
|||
type: 'entity.type-device', |
|||
typePlural: 'entity.type-devices', |
|||
list: 'entity.list-of-devices', |
|||
nameStartsWith: 'entity.device-name-starts-with' |
|||
}, |
|||
"ASSET": { |
|||
type: 'entity.type-asset', |
|||
typePlural: 'entity.type-assets', |
|||
list: 'entity.list-of-assets', |
|||
nameStartsWith: 'entity.asset-name-starts-with' |
|||
}, |
|||
"ENTITY_VIEW": { |
|||
type: 'entity.type-entity-view', |
|||
typePlural: 'entity.type-entity-views', |
|||
list: 'entity.list-of-entity-views', |
|||
nameStartsWith: 'entity.entity-view-name-starts-with' |
|||
}, |
|||
"TENANT": { |
|||
type: 'entity.type-tenant', |
|||
typePlural: 'entity.type-tenants', |
|||
list: 'entity.list-of-tenants', |
|||
nameStartsWith: 'entity.tenant-name-starts-with' |
|||
}, |
|||
"CUSTOMER": { |
|||
type: 'entity.type-customer', |
|||
typePlural: 'entity.type-customers', |
|||
list: 'entity.list-of-customers', |
|||
nameStartsWith: 'entity.customer-name-starts-with' |
|||
}, |
|||
"USER": { |
|||
type: 'entity.type-user', |
|||
typePlural: 'entity.type-users', |
|||
list: 'entity.list-of-users', |
|||
nameStartsWith: 'entity.user-name-starts-with' |
|||
}, |
|||
"DASHBOARD": { |
|||
type: 'entity.type-dashboard', |
|||
typePlural: 'entity.type-dashboards', |
|||
list: 'entity.list-of-dashboards', |
|||
nameStartsWith: 'entity.dashboard-name-starts-with' |
|||
}, |
|||
"ALARM": { |
|||
type: 'entity.type-alarm', |
|||
typePlural: 'entity.type-alarms', |
|||
list: 'entity.list-of-alarms', |
|||
nameStartsWith: 'entity.alarm-name-starts-with' |
|||
}, |
|||
"RULE_CHAIN": { |
|||
type: 'entity.type-rulechain', |
|||
typePlural: 'entity.type-rulechains', |
|||
list: 'entity.list-of-rulechains', |
|||
nameStartsWith: 'entity.rulechain-name-starts-with' |
|||
}, |
|||
"RULE_NODE": { |
|||
type: 'entity.type-rulenode', |
|||
typePlural: 'entity.type-rulenodes', |
|||
list: 'entity.list-of-rulenodes', |
|||
nameStartsWith: 'entity.rulenode-name-starts-with' |
|||
}, |
|||
"CURRENT_CUSTOMER": { |
|||
type: 'entity.type-current-customer', |
|||
list: 'entity.type-current-customer' |
|||
}, |
|||
"CURRENT_TENANT": { |
|||
type: 'entity.type-current-tenant', |
|||
list: 'entity.type-current-tenant' |
|||
} |
|||
}, |
|||
entityField: { |
|||
createdTime: { |
|||
keyName: 'createdTime', |
|||
name: 'entity-field.created-time', |
|||
value: 'createdTime', |
|||
time: true |
|||
}, |
|||
name: { |
|||
keyName: 'name', |
|||
name: 'entity-field.name', |
|||
value: 'name' |
|||
}, |
|||
type: { |
|||
keyName: 'type', |
|||
name: 'entity-field.type', |
|||
value: 'type' |
|||
}, |
|||
firstName: { |
|||
keyName: 'firstName', |
|||
name: 'entity-field.first-name', |
|||
value: 'firstName' |
|||
}, |
|||
lastName: { |
|||
keyName: 'lastName', |
|||
name: 'entity-field.last-name', |
|||
value: 'lastName' |
|||
}, |
|||
email: { |
|||
keyName: 'email', |
|||
name: 'entity-field.email', |
|||
value: 'email' |
|||
}, |
|||
title: { |
|||
keyName: 'title', |
|||
name: 'entity-field.title', |
|||
value: 'title' |
|||
}, |
|||
country: { |
|||
keyName: 'country', |
|||
name: 'entity-field.country', |
|||
value: 'country' |
|||
}, |
|||
state: { |
|||
keyName: 'state', |
|||
name: 'entity-field.state', |
|||
value: 'state' |
|||
}, |
|||
city: { |
|||
keyName: 'city', |
|||
name: 'entity-field.city', |
|||
value: 'city' |
|||
}, |
|||
address: { |
|||
keyName: 'address', |
|||
name: 'entity-field.address', |
|||
value: 'address' |
|||
}, |
|||
address2: { |
|||
keyName: 'address2', |
|||
name: 'entity-field.address2', |
|||
value: 'address2' |
|||
}, |
|||
zip: { |
|||
keyName: 'zip', |
|||
name: 'entity-field.zip', |
|||
value: 'zip' |
|||
}, |
|||
phone: { |
|||
keyName: 'phone', |
|||
name: 'entity-field.phone', |
|||
value: 'phone' |
|||
}, |
|||
label: { |
|||
keyName: 'label', |
|||
name: 'entity-field.label', |
|||
value: 'label' |
|||
} |
|||
}, |
|||
entitySearchDirection: { |
|||
from: "FROM", |
|||
to: "TO" |
|||
}, |
|||
entityRelationType: { |
|||
contains: "Contains", |
|||
manages: "Manages" |
|||
}, |
|||
eventType: { |
|||
error: { |
|||
value: "ERROR", |
|||
name: "event.type-error" |
|||
}, |
|||
lcEvent: { |
|||
value: "LC_EVENT", |
|||
name: "event.type-lc-event" |
|||
}, |
|||
stats: { |
|||
value: "STATS", |
|||
name: "event.type-stats" |
|||
} |
|||
}, |
|||
debugEventType: { |
|||
debugRuleNode: { |
|||
value: "DEBUG_RULE_NODE", |
|||
name: "event.type-debug-rule-node" |
|||
}, |
|||
debugRuleChain: { |
|||
value: "DEBUG_RULE_CHAIN", |
|||
name: "event.type-debug-rule-chain" |
|||
} |
|||
}, |
|||
extensionType: { |
|||
http: "HTTP", |
|||
mqtt: "MQTT", |
|||
opc: "OPC UA", |
|||
modbus: "MODBUS" |
|||
}, |
|||
gatewayConfigType: { |
|||
mqtt: { |
|||
value: "mqtt", |
|||
name: "MQTT" |
|||
}, |
|||
modbus: { |
|||
value: "modbus", |
|||
name: "Modbus" |
|||
}, |
|||
opcua: { |
|||
value: "opcua", |
|||
name: "OPC-UA" |
|||
}, |
|||
ble: { |
|||
value: "ble", |
|||
name: "BLE" |
|||
}, |
|||
request: { |
|||
value: "request", |
|||
name: "Request" |
|||
}, |
|||
can: { |
|||
value: "can", |
|||
name: "CAN" |
|||
}, |
|||
bacnet: { |
|||
value: "bacnet", |
|||
name: "BACnet" |
|||
}, |
|||
custom: { |
|||
value: "custom", |
|||
name: "Custom" |
|||
} |
|||
}, |
|||
gatewayLogLevel: { |
|||
none: "NONE", |
|||
critical: "CRITICAL", |
|||
error: "ERROR", |
|||
warning: "WARNING", |
|||
info: "INFO", |
|||
debug: "DEBUG" |
|||
}, |
|||
extensionValueType: { |
|||
string: 'value.string', |
|||
long: 'value.long', |
|||
double: 'value.double', |
|||
boolean: 'value.boolean' |
|||
}, |
|||
extensionTransformerType: { |
|||
toDouble: 'extension.to-double', |
|||
custom: 'extension.custom' |
|||
}, |
|||
mqttConverterTypes: { |
|||
json: 'extension.converter-json', |
|||
custom: 'extension.custom' |
|||
}, |
|||
mqttCredentialTypes: { |
|||
anonymous: { |
|||
value: "anonymous", |
|||
name: "extension.anonymous" |
|||
}, |
|||
basic: { |
|||
value: "basic", |
|||
name: "extension.basic" |
|||
}, |
|||
pem: { |
|||
value: "cert.PEM", |
|||
name: "extension.pem" |
|||
} |
|||
}, |
|||
extensionOpcSecurityTypes: { |
|||
Basic128Rsa15: "Basic128Rsa15", |
|||
Basic256: "Basic256", |
|||
Basic256Sha256: "Basic256Sha256", |
|||
None: "None" |
|||
}, |
|||
extensionIdentityType: { |
|||
anonymous: "extension.anonymous", |
|||
username: "extension.username" |
|||
}, |
|||
extensionKeystoreType: { |
|||
PKCS12: "PKCS12", |
|||
JKS: "JKS" |
|||
}, |
|||
extensionModbusFunctionCodes: { |
|||
1: "Read Coils (1)", |
|||
2: "Read Discrete Inputs (2)", |
|||
3: "Read Multiple Holding Registers (3)", |
|||
4: "Read Input Registers (4)" |
|||
}, |
|||
extensionModbusTransports: { |
|||
tcp: "TCP", |
|||
udp: "UDP", |
|||
rtu: "RTU" |
|||
}, |
|||
extensionModbusRtuParities: { |
|||
none: "none", |
|||
even: "even", |
|||
odd: "odd" |
|||
}, |
|||
extensionModbusRtuEncodings: { |
|||
ascii: "ascii", |
|||
rtu: "rtu" |
|||
}, |
|||
latestTelemetry: { |
|||
value: "LATEST_TELEMETRY", |
|||
name: "attribute.scope-latest-telemetry", |
|||
clientSide: true |
|||
}, |
|||
attributesScope: { |
|||
client: { |
|||
value: "CLIENT_SCOPE", |
|||
name: "attribute.scope-client", |
|||
clientSide: true |
|||
}, |
|||
server: { |
|||
value: "SERVER_SCOPE", |
|||
name: "attribute.scope-server", |
|||
clientSide: false |
|||
}, |
|||
shared: { |
|||
value: "SHARED_SCOPE", |
|||
name: "attribute.scope-shared", |
|||
clientSide: false |
|||
} |
|||
}, |
|||
ruleNodeTypeComponentTypes: ["FILTER", "ENRICHMENT", "TRANSFORMATION", "ACTION", "EXTERNAL"], |
|||
ruleChainNodeComponent: { |
|||
type: 'RULE_CHAIN', |
|||
name: 'rule chain', |
|||
clazz: 'tb.internal.RuleChain', |
|||
configurationDescriptor: { |
|||
nodeDefinition: { |
|||
description: "", |
|||
details: "Forwards incoming messages to specified Rule Chain", |
|||
inEnabled: true, |
|||
outEnabled: false, |
|||
relationTypes: [], |
|||
customRelations: false, |
|||
defaultConfiguration: {} |
|||
} |
|||
} |
|||
}, |
|||
unknownNodeComponent: { |
|||
type: 'UNKNOWN', |
|||
name: 'unknown', |
|||
clazz: 'tb.internal.Unknown', |
|||
configurationDescriptor: { |
|||
nodeDefinition: { |
|||
description: "", |
|||
details: "", |
|||
inEnabled: true, |
|||
outEnabled: true, |
|||
relationTypes: [], |
|||
customRelations: false, |
|||
defaultConfiguration: {} |
|||
} |
|||
} |
|||
}, |
|||
inputNodeComponent: { |
|||
type: 'INPUT', |
|||
name: 'Input', |
|||
clazz: 'tb.internal.Input' |
|||
}, |
|||
ruleNodeType: { |
|||
FILTER: { |
|||
value: "FILTER", |
|||
name: "rulenode.type-filter", |
|||
details: "rulenode.type-filter-details", |
|||
nodeClass: "tb-filter-type", |
|||
icon: "filter_list" |
|||
}, |
|||
ENRICHMENT: { |
|||
value: "ENRICHMENT", |
|||
name: "rulenode.type-enrichment", |
|||
details: "rulenode.type-enrichment-details", |
|||
nodeClass: "tb-enrichment-type", |
|||
icon: "playlist_add" |
|||
}, |
|||
TRANSFORMATION: { |
|||
value: "TRANSFORMATION", |
|||
name: "rulenode.type-transformation", |
|||
details: "rulenode.type-transformation-details", |
|||
nodeClass: "tb-transformation-type", |
|||
icon: "transform" |
|||
}, |
|||
ACTION: { |
|||
value: "ACTION", |
|||
name: "rulenode.type-action", |
|||
details: "rulenode.type-action-details", |
|||
nodeClass: "tb-action-type", |
|||
icon: "flash_on" |
|||
}, |
|||
EXTERNAL: { |
|||
value: "EXTERNAL", |
|||
name: "rulenode.type-external", |
|||
details: "rulenode.type-external-details", |
|||
nodeClass: "tb-external-type", |
|||
icon: "cloud_upload" |
|||
}, |
|||
RULE_CHAIN: { |
|||
value: "RULE_CHAIN", |
|||
name: "rulenode.type-rule-chain", |
|||
details: "rulenode.type-rule-chain-details", |
|||
nodeClass: "tb-rule-chain-type", |
|||
icon: "settings_ethernet" |
|||
}, |
|||
INPUT: { |
|||
value: "INPUT", |
|||
name: "rulenode.type-input", |
|||
details: "rulenode.type-input-details", |
|||
nodeClass: "tb-input-type", |
|||
icon: "input", |
|||
special: true |
|||
}, |
|||
UNKNOWN: { |
|||
value: "UNKNOWN", |
|||
name: "rulenode.type-unknown", |
|||
details: "rulenode.type-unknown-details", |
|||
nodeClass: "tb-unknown-type", |
|||
icon: "help_outline" |
|||
} |
|||
}, |
|||
messageType: { |
|||
'POST_ATTRIBUTES_REQUEST': { |
|||
name: 'Post attributes', |
|||
value: 'POST_ATTRIBUTES_REQUEST' |
|||
}, |
|||
'POST_TELEMETRY_REQUEST': { |
|||
name: 'Post telemetry', |
|||
value: 'POST_TELEMETRY_REQUEST' |
|||
}, |
|||
'TO_SERVER_RPC_REQUEST': { |
|||
name: 'RPC Request from Device', |
|||
value: 'TO_SERVER_RPC_REQUEST' |
|||
}, |
|||
'RPC_CALL_FROM_SERVER_TO_DEVICE': { |
|||
name: 'RPC Request to Device', |
|||
value: 'RPC_CALL_FROM_SERVER_TO_DEVICE' |
|||
}, |
|||
'ACTIVITY_EVENT': { |
|||
name: 'Activity Event', |
|||
value: 'ACTIVITY_EVENT' |
|||
}, |
|||
'INACTIVITY_EVENT': { |
|||
name: 'Inactivity Event', |
|||
value: 'INACTIVITY_EVENT' |
|||
}, |
|||
'CONNECT_EVENT': { |
|||
name: 'Connect Event', |
|||
value: 'CONNECT_EVENT' |
|||
}, |
|||
'DISCONNECT_EVENT': { |
|||
name: 'Disconnect Event', |
|||
value: 'DISCONNECT_EVENT' |
|||
}, |
|||
'ENTITY_CREATED': { |
|||
name: 'Entity Created', |
|||
value: 'ENTITY_CREATED' |
|||
}, |
|||
'ENTITY_UPDATED': { |
|||
name: 'Entity Updated', |
|||
value: 'ENTITY_UPDATED' |
|||
}, |
|||
'ENTITY_DELETED': { |
|||
name: 'Entity Deleted', |
|||
value: 'ENTITY_DELETED' |
|||
}, |
|||
'ENTITY_ASSIGNED': { |
|||
name: 'Entity Assigned', |
|||
value: 'ENTITY_ASSIGNED' |
|||
}, |
|||
'ENTITY_UNASSIGNED': { |
|||
name: 'Entity Unassigned', |
|||
value: 'ENTITY_UNASSIGNED' |
|||
}, |
|||
'ATTRIBUTES_UPDATED': { |
|||
name: 'Attributes Updated', |
|||
value: 'ATTRIBUTES_UPDATED' |
|||
}, |
|||
'ATTRIBUTES_DELETED': { |
|||
name: 'Attributes Deleted', |
|||
value: 'ATTRIBUTES_DELETED' |
|||
} |
|||
}, |
|||
valueType: { |
|||
string: { |
|||
value: "string", |
|||
name: "value.string", |
|||
icon: "mdi:format-text" |
|||
}, |
|||
integer: { |
|||
value: "integer", |
|||
name: "value.integer", |
|||
icon: "mdi:numeric" |
|||
}, |
|||
double: { |
|||
value: "double", |
|||
name: "value.double", |
|||
icon: "mdi:numeric" |
|||
}, |
|||
boolean: { |
|||
value: "boolean", |
|||
name: "value.boolean", |
|||
icon: "mdi:checkbox-marked-outline" |
|||
}, |
|||
json: { |
|||
value: "json", |
|||
name: "value.json", |
|||
icon: "mdi:json" |
|||
} |
|||
}, |
|||
widgetType: { |
|||
timeseries: { |
|||
value: "timeseries", |
|||
name: "widget.timeseries", |
|||
template: { |
|||
bundleAlias: "charts", |
|||
alias: "basic_timeseries" |
|||
} |
|||
}, |
|||
latest: { |
|||
value: "latest", |
|||
name: "widget.latest-values", |
|||
template: { |
|||
bundleAlias: "cards", |
|||
alias: "attributes_card" |
|||
} |
|||
}, |
|||
rpc: { |
|||
value: "rpc", |
|||
name: "widget.rpc", |
|||
template: { |
|||
bundleAlias: "gpio_widgets", |
|||
alias: "basic_gpio_control" |
|||
} |
|||
}, |
|||
alarm: { |
|||
value: "alarm", |
|||
name: "widget.alarm", |
|||
template: { |
|||
bundleAlias: "alarm_widgets", |
|||
alias: "alarms_table" |
|||
} |
|||
}, |
|||
static: { |
|||
value: "static", |
|||
name: "widget.static", |
|||
template: { |
|||
bundleAlias: "cards", |
|||
alias: "html_card" |
|||
} |
|||
} |
|||
}, |
|||
widgetActionSources: { |
|||
headerButton: { |
|||
name: 'widget-action.header-button', |
|||
value: 'headerButton', |
|||
multiple: true |
|||
} |
|||
}, |
|||
widgetActionTypes: { |
|||
openDashboardState: { |
|||
name: 'widget-action.open-dashboard-state', |
|||
value: 'openDashboardState' |
|||
}, |
|||
updateDashboardState: { |
|||
name: 'widget-action.update-dashboard-state', |
|||
value: 'updateDashboardState' |
|||
}, |
|||
openDashboard: { |
|||
name: 'widget-action.open-dashboard', |
|||
value: 'openDashboard' |
|||
}, |
|||
custom: { |
|||
name: 'widget-action.custom', |
|||
value: 'custom' |
|||
}, |
|||
customPretty: { |
|||
name: 'widget-action.custom-pretty', |
|||
value: 'customPretty' |
|||
} |
|||
}, |
|||
systemBundleAlias: { |
|||
charts: "charts", |
|||
cards: "cards" |
|||
}, |
|||
translate: { |
|||
customTranslationsPrefix: "custom." |
|||
} |
|||
} |
|||
).name; |
|||
@ -1,137 +0,0 @@ |
|||
/* |
|||
* Copyright © 2016-2020 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
export function utf8Encode(str) { |
|||
var result; |
|||
|
|||
if (angular.isUndefined(Uint8Array)) { // eslint-disable-line no-undef
|
|||
result = utf8ToBytes(str); |
|||
} else { |
|||
result = new Uint8Array(utf8ToBytes(str)); // eslint-disable-line no-undef
|
|||
} |
|||
|
|||
return result; |
|||
} |
|||
|
|||
export function utf8Decode(bytes) { |
|||
return utf8Slice(bytes, 0, bytes.length); |
|||
} |
|||
|
|||
function utf8Slice (buf, start, end) { |
|||
var res = '' |
|||
var tmp = '' |
|||
end = Math.min(buf.length, end || Infinity) |
|||
start = start || 0; |
|||
|
|||
for (var i = start; i < end; i++) { |
|||
if (buf[i] <= 0x7F) { |
|||
res += decodeUtf8Char(tmp) + String.fromCharCode(buf[i]) |
|||
tmp = '' |
|||
} else { |
|||
tmp += '%' + buf[i].toString(16) |
|||
} |
|||
} |
|||
|
|||
return res + decodeUtf8Char(tmp) |
|||
} |
|||
|
|||
function decodeUtf8Char (str) { |
|||
try { |
|||
return decodeURIComponent(str) |
|||
} catch (err) { |
|||
return String.fromCharCode(0xFFFD) // UTF 8 invalid char
|
|||
} |
|||
} |
|||
|
|||
function utf8ToBytes (string, units) { |
|||
units = units || Infinity |
|||
var codePoint |
|||
var length = string.length |
|||
var leadSurrogate = null |
|||
var bytes = [] |
|||
var i = 0 |
|||
|
|||
for (; i < length; i++) { |
|||
codePoint = string.charCodeAt(i) |
|||
|
|||
// is surrogate component
|
|||
if (codePoint > 0xD7FF && codePoint < 0xE000) { |
|||
// last char was a lead
|
|||
if (leadSurrogate) { |
|||
// 2 leads in a row
|
|||
if (codePoint < 0xDC00) { |
|||
if ((units -= 3) > -1) bytes.push(0xEF, 0xBF, 0xBD) |
|||
leadSurrogate = codePoint |
|||
continue |
|||
} else { |
|||
// valid surrogate pair
|
|||
codePoint = leadSurrogate - 0xD800 << 10 | codePoint - 0xDC00 | 0x10000 |
|||
leadSurrogate = null |
|||
} |
|||
} else { |
|||
// no lead yet
|
|||
|
|||
if (codePoint > 0xDBFF) { |
|||
// unexpected trail
|
|||
if ((units -= 3) > -1) bytes.push(0xEF, 0xBF, 0xBD) |
|||
continue |
|||
} else if (i + 1 === length) { |
|||
// unpaired lead
|
|||
if ((units -= 3) > -1) bytes.push(0xEF, 0xBF, 0xBD) |
|||
continue |
|||
} else { |
|||
// valid lead
|
|||
leadSurrogate = codePoint |
|||
continue |
|||
} |
|||
} |
|||
} else if (leadSurrogate) { |
|||
// valid bmp char, but last char was a lead
|
|||
if ((units -= 3) > -1) bytes.push(0xEF, 0xBF, 0xBD) |
|||
leadSurrogate = null |
|||
} |
|||
|
|||
// encode utf8
|
|||
if (codePoint < 0x80) { |
|||
if ((units -= 1) < 0) break |
|||
bytes.push(codePoint) |
|||
} else if (codePoint < 0x800) { |
|||
if ((units -= 2) < 0) break |
|||
bytes.push( |
|||
codePoint >> 0x6 | 0xC0, |
|||
codePoint & 0x3F | 0x80 |
|||
) |
|||
} else if (codePoint < 0x10000) { |
|||
if ((units -= 3) < 0) break |
|||
bytes.push( |
|||
codePoint >> 0xC | 0xE0, |
|||
codePoint >> 0x6 & 0x3F | 0x80, |
|||
codePoint & 0x3F | 0x80 |
|||
) |
|||
} else if (codePoint < 0x200000) { |
|||
if ((units -= 4) < 0) break |
|||
bytes.push( |
|||
codePoint >> 0x12 | 0xF0, |
|||
codePoint >> 0xC & 0x3F | 0x80, |
|||
codePoint >> 0x6 & 0x3F | 0x80, |
|||
codePoint & 0x3F | 0x80 |
|||
) |
|||
} else { |
|||
throw new Error('Invalid code point') |
|||
} |
|||
} |
|||
|
|||
return bytes |
|||
} |
|||
@ -1,608 +0,0 @@ |
|||
/* |
|||
* Copyright © 2016-2020 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
/* eslint-disable import/no-unresolved, import/default */ |
|||
|
|||
import materialIconsCodepoints from 'raw-loader!material-design-icons/iconfont/codepoints'; |
|||
|
|||
/* eslint-enable import/no-unresolved, import/default */ |
|||
|
|||
import tinycolor from 'tinycolor2'; |
|||
import jsonSchemaDefaults from 'json-schema-defaults'; |
|||
import base64js from 'base64-js'; |
|||
import {utf8Encode, utf8Decode} from './utf8-support'; |
|||
|
|||
import thingsboardTypes from './types.constant'; |
|||
|
|||
export default angular.module('thingsboard.utils', [thingsboardTypes]) |
|||
.factory('utils', Utils) |
|||
.name; |
|||
|
|||
const varsRegex = /\$\{([^}]*)\}/g; |
|||
|
|||
/*@ngInject*/ |
|||
function Utils($mdColorPalette, $rootScope, $window, $translate, $q, $timeout, types) { |
|||
|
|||
var predefinedFunctions = {}, |
|||
predefinedFunctionsList = [], |
|||
materialColors = [], |
|||
materialIcons = []; |
|||
|
|||
var commonMaterialIcons = [ 'more_horiz', 'more_vert', 'open_in_new', 'visibility', 'play_arrow', 'arrow_back', 'arrow_downward', |
|||
'arrow_forward', 'arrow_upwards', 'close', 'refresh', 'menu', 'show_chart', 'multiline_chart', 'pie_chart', 'insert_chart', 'people', |
|||
'person', 'domain', 'devices_other', 'now_widgets', 'dashboards', 'map', 'pin_drop', 'my_location', 'extension', 'search', |
|||
'settings', 'notifications', 'notifications_active', 'info', 'info_outline', 'warning', 'list', 'file_download', 'import_export', |
|||
'share', 'add', 'edit', 'done' ]; |
|||
|
|||
predefinedFunctions['Sin'] = "return Math.round(1000*Math.sin(time/5000));"; |
|||
predefinedFunctions['Cos'] = "return Math.round(1000*Math.cos(time/5000));"; |
|||
predefinedFunctions['Random'] = |
|||
"var value = prevValue + Math.random() * 100 - 50;\n" + |
|||
"var multiplier = Math.pow(10, 2 || 0);\n" + |
|||
"var value = Math.round(value * multiplier) / multiplier;\n" + |
|||
"if (value < -1000) {\n" + |
|||
" value = -1000;\n" + |
|||
"} else if (value > 1000) {\n" + |
|||
" value = 1000;\n" + |
|||
"}\n" + |
|||
"return value;"; |
|||
|
|||
for (var func in predefinedFunctions) { |
|||
predefinedFunctionsList.push(func); |
|||
} |
|||
|
|||
var colorPalettes = ['blue', 'green', 'red', 'amber', 'blue-grey', 'purple', 'light-green', 'indigo', 'pink', 'yellow', 'light-blue', 'orange', 'deep-purple', 'lime', 'teal', 'brown', 'cyan', 'deep-orange', 'grey']; |
|||
var colorSpectrum = ['500', 'A700', '600', '700', '800', '900', '300', '400', 'A200', 'A400']; |
|||
|
|||
angular.forEach($mdColorPalette, function (value, key) { |
|||
angular.forEach(value, function (color, label) { |
|||
if (colorSpectrum.indexOf(label) > -1) { |
|||
var rgb = 'rgb(' + color.value[0] + ',' + color.value[1] + ',' + color.value[2] + ')'; |
|||
color = tinycolor(rgb); |
|||
var isDark = color.isDark(); |
|||
var colorItem = { |
|||
value: color.toHexString(), |
|||
group: key, |
|||
label: label, |
|||
isDark: isDark |
|||
}; |
|||
materialColors.push(colorItem); |
|||
} |
|||
}); |
|||
}); |
|||
|
|||
materialColors.sort(function (colorItem1, colorItem2) { |
|||
var spectrumIndex1 = colorSpectrum.indexOf(colorItem1.label); |
|||
var spectrumIndex2 = colorSpectrum.indexOf(colorItem2.label); |
|||
var result = spectrumIndex1 - spectrumIndex2; |
|||
if (result === 0) { |
|||
var paletteIndex1 = colorPalettes.indexOf(colorItem1.group); |
|||
var paletteIndex2 = colorPalettes.indexOf(colorItem2.group); |
|||
result = paletteIndex1 - paletteIndex2; |
|||
} |
|||
return result; |
|||
}); |
|||
|
|||
var defaultDataKey = { |
|||
name: 'f(x)', |
|||
type: types.dataKeyType.function, |
|||
label: 'Sin', |
|||
color: getMaterialColor(0), |
|||
funcBody: getPredefinedFunctionBody('Sin'), |
|||
settings: {}, |
|||
_hash: Math.random() |
|||
}; |
|||
|
|||
var defaultDatasource = { |
|||
type: types.datasourceType.function, |
|||
name: types.datasourceType.function, |
|||
dataKeys: [angular.copy(defaultDataKey)] |
|||
}; |
|||
|
|||
var defaultAlarmFields = [ |
|||
types.alarmFields.createdTime.keyName, |
|||
types.alarmFields.originator.keyName, |
|||
types.alarmFields.type.keyName, |
|||
types.alarmFields.severity.keyName, |
|||
types.alarmFields.status.keyName |
|||
]; |
|||
|
|||
var defaultAlarmDataKeys = []; |
|||
|
|||
var imageAspectMap = {}; |
|||
|
|||
var service = { |
|||
getDefaultDatasource: getDefaultDatasource, |
|||
generateObjectFromJsonSchema: generateObjectFromJsonSchema, |
|||
getDefaultDatasourceJson: getDefaultDatasourceJson, |
|||
getDefaultAlarmDataKeys: getDefaultAlarmDataKeys, |
|||
getMaterialColor: getMaterialColor, |
|||
getMaterialIcons: getMaterialIcons, |
|||
getCommonMaterialIcons: getCommonMaterialIcons, |
|||
getPredefinedFunctionBody: getPredefinedFunctionBody, |
|||
getPredefinedFunctionsList: getPredefinedFunctionsList, |
|||
genMaterialColor: genMaterialColor, |
|||
objectHashCode: objectHashCode, |
|||
parseException: parseException, |
|||
processWidgetException: processWidgetException, |
|||
isDescriptorSchemaNotEmpty: isDescriptorSchemaNotEmpty, |
|||
filterSearchTextEntities: filterSearchTextEntities, |
|||
guid: guid, |
|||
cleanCopy: cleanCopy, |
|||
isLocalUrl: isLocalUrl, |
|||
validateDatasources: validateDatasources, |
|||
createKey: createKey, |
|||
createAdditionalDataKey: createAdditionalDataKey, |
|||
createLabelFromDatasource: createLabelFromDatasource, |
|||
insertVariable: insertVariable, |
|||
customTranslation: customTranslation, |
|||
objToBase64: objToBase64, |
|||
base64toObj: base64toObj, |
|||
loadImageAspect: loadImageAspect |
|||
} |
|||
|
|||
return service; |
|||
|
|||
function getPredefinedFunctionsList() { |
|||
return predefinedFunctionsList; |
|||
} |
|||
|
|||
function getPredefinedFunctionBody(func) { |
|||
return predefinedFunctions[func]; |
|||
} |
|||
|
|||
function getMaterialColor(index) { |
|||
var colorIndex = index % materialColors.length; |
|||
return materialColors[colorIndex].value; |
|||
} |
|||
|
|||
function getMaterialIcons() { |
|||
var deferred = $q.defer(); |
|||
if (materialIcons.length) { |
|||
deferred.resolve(materialIcons); |
|||
} else { |
|||
$timeout(function() { |
|||
var codepointsArray = materialIconsCodepoints.split("\n"); |
|||
codepointsArray.forEach(function (codepoint) { |
|||
if (codepoint && codepoint.length) { |
|||
var values = codepoint.split(' '); |
|||
if (values && values.length == 2) { |
|||
materialIcons.push(values[0]); |
|||
} |
|||
} |
|||
}); |
|||
deferred.resolve(materialIcons); |
|||
}); |
|||
} |
|||
return deferred.promise; |
|||
} |
|||
|
|||
function getCommonMaterialIcons() { |
|||
return commonMaterialIcons; |
|||
} |
|||
|
|||
function genMaterialColor(str) { |
|||
var hash = Math.abs(hashCode(str)); |
|||
return getMaterialColor(hash); |
|||
} |
|||
|
|||
function hashCode(str) { |
|||
var hash = 0; |
|||
var i, char; |
|||
if (str.length == 0) return hash; |
|||
for (i = 0; i < str.length; i++) { |
|||
char = str.charCodeAt(i); |
|||
hash = ((hash << 5) - hash) + char; |
|||
hash = hash & hash; // Convert to 32bit integer
|
|||
} |
|||
return hash; |
|||
} |
|||
|
|||
function objectHashCode(obj) { |
|||
var hash = 0; |
|||
if (obj) { |
|||
var str = angular.toJson(obj); |
|||
hash = hashCode(str); |
|||
} |
|||
return hash; |
|||
} |
|||
|
|||
function parseException(exception, lineOffset) { |
|||
var data = {}; |
|||
if (exception) { |
|||
if (angular.isString(exception) || exception instanceof String) { |
|||
data.message = exception; |
|||
} else { |
|||
if (exception.name) { |
|||
data.name = exception.name; |
|||
} else { |
|||
data.name = 'UnknownError'; |
|||
} |
|||
if (exception.message) { |
|||
data.message = exception.message; |
|||
} |
|||
if (exception.lineNumber) { |
|||
data.lineNumber = exception.lineNumber; |
|||
if (exception.columnNumber) { |
|||
data.columnNumber = exception.columnNumber; |
|||
} |
|||
} else if (exception.stack) { |
|||
var lineInfoRegexp = /(.*<anonymous>):(\d*)(:)?(\d*)?/g; |
|||
var lineInfoGroups = lineInfoRegexp.exec(exception.stack); |
|||
if (lineInfoGroups != null && lineInfoGroups.length >= 3) { |
|||
if (angular.isUndefined(lineOffset)) { |
|||
lineOffset = -2; |
|||
} |
|||
data.lineNumber = Number(lineInfoGroups[2]) + lineOffset; |
|||
if (lineInfoGroups.length >= 5) { |
|||
data.columnNumber = lineInfoGroups[4]; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
return data; |
|||
} |
|||
|
|||
function processWidgetException(exception) { |
|||
var parentScope = $window.parent.angular.element($window.frameElement).scope(); |
|||
var data = parseException(exception, -5); |
|||
if ($rootScope.widgetEditMode) { |
|||
parentScope.$emit('widgetException', data); |
|||
parentScope.$apply(); |
|||
} |
|||
return data; |
|||
} |
|||
|
|||
function getDefaultDatasource(dataKeySchema) { |
|||
var datasource = angular.copy(defaultDatasource); |
|||
if (angular.isDefined(dataKeySchema)) { |
|||
datasource.dataKeys[0].settings = generateObjectFromJsonSchema(dataKeySchema); |
|||
} |
|||
return datasource; |
|||
} |
|||
|
|||
function generateObjectFromJsonSchema(schema) { |
|||
var obj = jsonSchemaDefaults(schema); |
|||
deleteNullProperties(obj); |
|||
return obj; |
|||
} |
|||
|
|||
function deleteNullProperties(obj) { |
|||
if (angular.isUndefined(obj) || obj == null) { |
|||
return; |
|||
} |
|||
for (var propName in obj) { |
|||
if (obj[propName] === null || angular.isUndefined(obj[propName])) { |
|||
delete obj[propName]; |
|||
} else if (angular.isObject(obj[propName])) { |
|||
deleteNullProperties(obj[propName]); |
|||
} else if (angular.isArray(obj[propName])) { |
|||
for (var i=0;i<obj[propName].length;i++) { |
|||
deleteNullProperties(obj[propName][i]); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
function getDefaultDatasourceJson(dataKeySchema) { |
|||
return angular.toJson(getDefaultDatasource(dataKeySchema)); |
|||
} |
|||
|
|||
function initDefaultAlarmDataKeys() { |
|||
for (var i=0;i<defaultAlarmFields.length;i++) { |
|||
var name = defaultAlarmFields[i]; |
|||
var dataKey = { |
|||
name: name, |
|||
type: types.dataKeyType.alarm, |
|||
label: $translate.instant(types.alarmFields[name].name)+'', |
|||
color: getMaterialColor(i), |
|||
settings: {}, |
|||
_hash: Math.random() |
|||
}; |
|||
defaultAlarmDataKeys.push(dataKey); |
|||
} |
|||
} |
|||
|
|||
function getDefaultAlarmDataKeys() { |
|||
if (!defaultAlarmDataKeys.length) { |
|||
initDefaultAlarmDataKeys(); |
|||
} |
|||
return angular.copy(defaultAlarmDataKeys); |
|||
} |
|||
|
|||
function isDescriptorSchemaNotEmpty(descriptor) { |
|||
if (descriptor && descriptor.schema && descriptor.schema.properties) { |
|||
for(var prop in descriptor.schema.properties) { |
|||
if (Object.prototype.hasOwnProperty.call(descriptor.schema.properties, prop)) { |
|||
return true; |
|||
} |
|||
} |
|||
} |
|||
return false; |
|||
} |
|||
|
|||
function filterSearchTextEntities(entities, searchTextField, pageLink, deferred) { |
|||
var response = { |
|||
data: [], |
|||
hasNext: false, |
|||
nextPageLink: null |
|||
}; |
|||
var limit = pageLink.limit; |
|||
var textSearch = ''; |
|||
if (pageLink.textSearch) { |
|||
textSearch = pageLink.textSearch.toLowerCase(); |
|||
} |
|||
|
|||
for (var i=0;i<entities.length;i++) { |
|||
var entity = entities[i]; |
|||
var text = entity[searchTextField].toLowerCase(); |
|||
var createdTime = entity.createdTime; |
|||
if (pageLink.textOffset && pageLink.textOffset.length > 0) { |
|||
var comparison = text.localeCompare(pageLink.textOffset); |
|||
if (comparison === 0 |
|||
&& createdTime < pageLink.createdTimeOffset) { |
|||
response.data.push(entity); |
|||
if (response.data.length === limit) { |
|||
break; |
|||
} |
|||
} else if (comparison > 0 && text.startsWith(textSearch)) { |
|||
response.data.push(entity); |
|||
if (response.data.length === limit) { |
|||
break; |
|||
} |
|||
} |
|||
} else if (textSearch.length > 0) { |
|||
if (text.startsWith(textSearch)) { |
|||
response.data.push(entity); |
|||
if (response.data.length === limit) { |
|||
break; |
|||
} |
|||
} |
|||
} else { |
|||
response.data.push(entity); |
|||
if (response.data.length === limit) { |
|||
break; |
|||
} |
|||
} |
|||
} |
|||
if (response.data.length === limit) { |
|||
var lastEntity = response.data[limit-1]; |
|||
response.nextPageLink = { |
|||
limit: pageLink.limit, |
|||
textSearch: textSearch, |
|||
idOffset: lastEntity.id.id, |
|||
createdTimeOffset: lastEntity.createdTime, |
|||
textOffset: lastEntity[searchTextField].toLowerCase() |
|||
}; |
|||
response.hasNext = true; |
|||
} |
|||
deferred.resolve(response); |
|||
} |
|||
|
|||
function guid() { |
|||
function s4() { |
|||
return Math.floor((1 + Math.random()) * 0x10000) |
|||
.toString(16) |
|||
.substring(1); |
|||
} |
|||
return s4() + s4() + '-' + s4() + '-' + s4() + '-' + |
|||
s4() + '-' + s4() + s4() + s4(); |
|||
} |
|||
|
|||
function cleanCopy(object) { |
|||
var copy = angular.copy(object); |
|||
for (var prop in copy) { |
|||
if (prop && prop.startsWith('$$')) { |
|||
delete copy[prop]; |
|||
} |
|||
} |
|||
return copy; |
|||
} |
|||
|
|||
function genNextColor(datasources, initialIndex) { |
|||
var index = initialIndex || 0; |
|||
if (datasources) { |
|||
for (var i = 0; i < datasources.length; i++) { |
|||
var datasource = datasources[i]; |
|||
index += datasource.dataKeys.length; |
|||
} |
|||
} |
|||
return getMaterialColor(index); |
|||
} |
|||
|
|||
function isLocalUrl(url) { |
|||
var parser = document.createElement('a'); //eslint-disable-line
|
|||
parser.href = url; |
|||
var host = parser.hostname; |
|||
if (host === "localhost" || host === "127.0.0.1") { |
|||
return true; |
|||
} else { |
|||
return false; |
|||
} |
|||
} |
|||
|
|||
function validateDatasources(datasources) { |
|||
datasources.forEach(function (datasource) { |
|||
if (datasource.type === 'device') { |
|||
datasource.type = types.datasourceType.entity; |
|||
datasource.entityType = types.entityType.device; |
|||
if (datasource.deviceId) { |
|||
datasource.entityId = datasource.deviceId; |
|||
} else if (datasource.deviceAliasId) { |
|||
datasource.entityAliasId = datasource.deviceAliasId; |
|||
} |
|||
if (datasource.deviceName) { |
|||
datasource.entityName = datasource.deviceName; |
|||
} |
|||
} |
|||
if (datasource.type === types.datasourceType.entity && datasource.entityId) { |
|||
datasource.name = datasource.entityName; |
|||
} |
|||
}); |
|||
return datasources; |
|||
} |
|||
|
|||
function createKey(keyInfo, type, datasources) { |
|||
var label; |
|||
if (type === types.dataKeyType.alarm && !keyInfo.label) { |
|||
var alarmField = types.alarmFields[keyInfo.name]; |
|||
if (alarmField) { |
|||
label = $translate.instant(alarmField.name)+''; |
|||
} |
|||
} |
|||
if (!label) { |
|||
label = keyInfo.label || keyInfo.name; |
|||
} |
|||
var dataKey = { |
|||
name: keyInfo.name, |
|||
type: type, |
|||
label: label, |
|||
funcBody: keyInfo.funcBody, |
|||
settings: {}, |
|||
_hash: Math.random() |
|||
} |
|||
if (keyInfo.units) { |
|||
dataKey.units = keyInfo.units; |
|||
} |
|||
if (angular.isDefined(keyInfo.decimals)) { |
|||
dataKey.decimals = keyInfo.decimals; |
|||
} |
|||
if (keyInfo.color) { |
|||
dataKey.color = keyInfo.color; |
|||
} else { |
|||
dataKey.color = genNextColor(datasources); |
|||
} |
|||
if (keyInfo.postFuncBody && keyInfo.postFuncBody.length) { |
|||
dataKey.usePostProcessing = true; |
|||
dataKey.postFuncBody = keyInfo.postFuncBody; |
|||
} |
|||
return dataKey; |
|||
} |
|||
|
|||
function createAdditionalDataKey(dataKey, datasource, timeUnit, datasources, additionalKeysNumber) { |
|||
let additionalDataKey = angular.copy(dataKey); |
|||
if (dataKey.settings.comparisonSettings.comparisonValuesLabel) { |
|||
additionalDataKey.label = createLabelFromDatasource(datasource, dataKey.settings.comparisonSettings.comparisonValuesLabel); |
|||
} else { |
|||
additionalDataKey.label = dataKey.label + ' ' + $translate.instant('legend.comparison-time-ago.'+timeUnit); |
|||
} |
|||
additionalDataKey.pattern = additionalDataKey.label; |
|||
if (dataKey.settings.comparisonSettings.color) { |
|||
additionalDataKey.color = dataKey.settings.comparisonSettings.color; |
|||
} else { |
|||
additionalDataKey.color = genNextColor(datasources, additionalKeysNumber); |
|||
} |
|||
additionalDataKey._hash = Math.random(); |
|||
return additionalDataKey; |
|||
} |
|||
|
|||
function createLabelFromDatasource(datasource, pattern) { |
|||
var label = angular.copy(pattern); |
|||
var match = varsRegex.exec(pattern); |
|||
while (match !== null) { |
|||
var variable = match[0]; |
|||
var variableName = match[1]; |
|||
if (variableName === 'dsName') { |
|||
label = label.split(variable).join(datasource.name); |
|||
} else if (variableName === 'entityName') { |
|||
label = label.split(variable).join(datasource.entityName); |
|||
} else if (variableName === 'deviceName') { |
|||
label = label.split(variable).join(datasource.entityName); |
|||
} else if (variableName === 'entityLabel') { |
|||
label = label.split(variable).join(datasource.entityLabel || datasource.entityName); |
|||
} else if (variableName === 'aliasName') { |
|||
label = label.split(variable).join(datasource.aliasName); |
|||
} else if (variableName === 'entityDescription') { |
|||
label = label.split(variable).join(datasource.entityDescription); |
|||
} |
|||
match = varsRegex.exec(pattern); |
|||
} |
|||
return label; |
|||
} |
|||
|
|||
function insertVariable(pattern, name, value) { |
|||
var result = angular.copy(pattern); |
|||
var match = varsRegex.exec(pattern); |
|||
while (match !== null) { |
|||
var variable = match[0]; |
|||
var variableName = match[1]; |
|||
if (variableName === name) { |
|||
result = result.split(variable).join(value); |
|||
} |
|||
match = varsRegex.exec(pattern); |
|||
} |
|||
return result; |
|||
} |
|||
|
|||
function customTranslation(translationValue, defaultValue) { |
|||
var result = ''; |
|||
var translationId = types.translate.customTranslationsPrefix + translationValue; |
|||
var translation = $translate.instant(translationId); |
|||
if (translation != translationId) { |
|||
result = translation + ''; |
|||
} else { |
|||
result = defaultValue; |
|||
} |
|||
return result; |
|||
} |
|||
|
|||
function objToBase64(obj) { |
|||
var json = angular.toJson(obj); |
|||
var encoded = utf8Encode(json); |
|||
var b64Encoded = base64js.fromByteArray(encoded); |
|||
return b64Encoded; |
|||
} |
|||
|
|||
function base64toObj(b64Encoded) { |
|||
var encoded = base64js.toByteArray(b64Encoded); |
|||
var json = utf8Decode(encoded); |
|||
var obj = angular.fromJson(json); |
|||
return obj; |
|||
} |
|||
|
|||
function loadImageAspect(imageUrl) { |
|||
var deferred = $q.defer(); |
|||
if (imageUrl && imageUrl.length) { |
|||
var urlHashCode = hashCode(imageUrl); |
|||
var aspect = imageAspectMap[urlHashCode]; |
|||
if (angular.isUndefined(aspect)) { |
|||
var testImage = document.createElement('img'); // eslint-disable-line
|
|||
testImage.style.position = 'absolute'; |
|||
testImage.style.left = '-99999px'; |
|||
testImage.style.top = '-99999px'; |
|||
testImage.onload = function() { |
|||
aspect = testImage.width / testImage.height; |
|||
document.body.removeChild(testImage); //eslint-disable-line
|
|||
imageAspectMap[urlHashCode] = aspect; |
|||
deferred.resolve(aspect); |
|||
}; |
|||
testImage.onerror = function() { |
|||
aspect = 0; |
|||
imageAspectMap[urlHashCode] = aspect; |
|||
deferred.resolve(aspect); |
|||
}; |
|||
document.body.appendChild(testImage); //eslint-disable-line
|
|||
testImage.src = imageUrl; |
|||
} else { |
|||
deferred.resolve(aspect); |
|||
} |
|||
} else { |
|||
deferred.resolve(0); |
|||
} |
|||
return deferred.promise; |
|||
} |
|||
|
|||
} |
|||
@ -1,44 +0,0 @@ |
|||
/* |
|||
* Copyright © 2016-2020 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
export default function fixAceEditor(aceEditor) { |
|||
aceEditor.$blockScrolling = Infinity; |
|||
aceEditor.on("showGutterTooltip", function (tooltip) { |
|||
if (!tooltip.isAttachedToBody) { |
|||
document.body.appendChild(tooltip.$element); //eslint-disable-line
|
|||
tooltip.isAttachedToBody = true; |
|||
onElementRemoved(tooltip.$parentNode, () => { |
|||
if (tooltip.$element.parentNode != null) { |
|||
tooltip.$element.parentNode.removeChild(tooltip.$element); |
|||
} |
|||
}); |
|||
} |
|||
}); |
|||
} |
|||
|
|||
function onElementRemoved(element, callback) { |
|||
if (!document.body.contains(element)) { //eslint-disable-line
|
|||
callback(); |
|||
} else { |
|||
var observer; |
|||
observer = new MutationObserver(function(mutations) { //eslint-disable-line
|
|||
if (!document.body.contains(element)) { //eslint-disable-line
|
|||
callback(); |
|||
observer.disconnect(); |
|||
} |
|||
}); |
|||
observer.observe(document.body, {childList: true}); //eslint-disable-line
|
|||
} |
|||
} |
|||
@ -1,74 +0,0 @@ |
|||
/* |
|||
* Copyright © 2016-2020 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
import $ from 'jquery'; |
|||
|
|||
export default angular.module('thingsboard.directives.circularProgress', []) |
|||
.directive('tbCircularProgress', CircularProgress) |
|||
.name; |
|||
|
|||
/* eslint-disable angular/angularelement */ |
|||
|
|||
/*@ngInject*/ |
|||
function CircularProgress($compile) { |
|||
|
|||
var linker = function (scope, element) { |
|||
|
|||
var circularProgressElement = angular.element('<md-progress-circular style="margin: auto;" md-mode="indeterminate" md-diameter="20"></md-progress-circular>'); |
|||
|
|||
$compile(circularProgressElement)(scope); |
|||
|
|||
var children = null; |
|||
var cssWidth = element.prop('style')['width']; |
|||
var width = null; |
|||
if (!cssWidth) { |
|||
$(element).css('width', width + 'px'); |
|||
} |
|||
|
|||
scope.$watch('circularProgress', function (newCircularProgress, prevCircularProgress) { |
|||
if (newCircularProgress != prevCircularProgress) { |
|||
if (newCircularProgress) { |
|||
if (!cssWidth) { |
|||
$(element).css('width', ''); |
|||
width = element.prop('offsetWidth'); |
|||
$(element).css('width', width + 'px'); |
|||
} |
|||
children = $(element).children(); |
|||
$(element).empty(); |
|||
$(element).append($(circularProgressElement)); |
|||
} else { |
|||
$(element).empty(); |
|||
$(element).append(children); |
|||
if (cssWidth) { |
|||
$(element).css('width', cssWidth); |
|||
} else { |
|||
$(element).css('width', ''); |
|||
} |
|||
} |
|||
} |
|||
}); |
|||
|
|||
} |
|||
|
|||
return { |
|||
restrict: "A", |
|||
link: linker, |
|||
scope: { |
|||
circularProgress: "=tbCircularProgress" |
|||
} |
|||
}; |
|||
} |
|||
|
|||
/* eslint-enable angular/angularelement */ |
|||
@ -1,55 +0,0 @@ |
|||
/* |
|||
* Copyright © 2016-2020 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
export default angular.module('thingsboard.directives.confirmOnExit', []) |
|||
.directive('tbConfirmOnExit', ConfirmOnExit) |
|||
.name; |
|||
|
|||
/*@ngInject*/ |
|||
function ConfirmOnExit($state, $mdDialog, $window, $filter, $parse, userService) { |
|||
return { |
|||
link: function ($scope, $element, $attributes) { |
|||
$scope.confirmForm = $scope.$eval($attributes.confirmForm); |
|||
$window.onbeforeunload = function () { |
|||
if (userService.isAuthenticated() && (($scope.confirmForm && $scope.confirmForm.$dirty) || $scope.$eval($attributes.isDirty))) { |
|||
return $filter('translate')('confirm-on-exit.message'); |
|||
} |
|||
} |
|||
$scope.$on('$stateChangeStart', function (event, next, current, params) { |
|||
if (userService.isAuthenticated() && (($scope.confirmForm && $scope.confirmForm.$dirty) || $scope.$eval($attributes.isDirty))) { |
|||
event.preventDefault(); |
|||
var confirm = $mdDialog.confirm() |
|||
.title($filter('translate')('confirm-on-exit.title')) |
|||
.htmlContent($filter('translate')('confirm-on-exit.html-message')) |
|||
.ariaLabel($filter('translate')('confirm-on-exit.title')) |
|||
.cancel($filter('translate')('action.cancel')) |
|||
.ok($filter('translate')('action.ok')); |
|||
$mdDialog.show(confirm).then(function () { |
|||
if ($scope.confirmForm) { |
|||
$scope.confirmForm.$setPristine(); |
|||
} else { |
|||
var remoteSetter = $parse($attributes.isDirty).assign; |
|||
remoteSetter($scope, false); |
|||
//$scope.isDirty = false;
|
|||
} |
|||
$state.go(next.name, params); |
|||
}, function () { |
|||
}); |
|||
} |
|||
}); |
|||
}, |
|||
scope: false |
|||
}; |
|||
} |
|||
@ -1,54 +0,0 @@ |
|||
/* |
|||
* Copyright © 2016-2020 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
export default angular.module('thingsboard.filters.contactShort', []) |
|||
.filter('contactShort', ContactShort) |
|||
.name; |
|||
|
|||
/*@ngInject*/ |
|||
function ContactShort($filter) { |
|||
return function (contact) { |
|||
var contactShort = ''; |
|||
if (contact) { |
|||
if (contact.address) { |
|||
contactShort += contact.address; |
|||
contactShort += ' '; |
|||
} |
|||
if (contact.address2) { |
|||
contactShort += contact.address2; |
|||
contactShort += ' '; |
|||
} |
|||
if (contact.city) { |
|||
contactShort += contact.city; |
|||
contactShort += ' '; |
|||
} |
|||
if (contact.state) { |
|||
contactShort += contact.state; |
|||
contactShort += ' '; |
|||
} |
|||
if (contact.zip) { |
|||
contactShort += contact.zip; |
|||
contactShort += ' '; |
|||
} |
|||
if (contact.country) { |
|||
contactShort += contact.country; |
|||
} |
|||
} |
|||
if (contactShort === '') { |
|||
contactShort = $filter('translate')('contact.no-address'); |
|||
} |
|||
return contactShort; |
|||
}; |
|||
} |
|||
@ -1,323 +0,0 @@ |
|||
/* |
|||
* Copyright © 2016-2020 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
/* eslint-disable import/no-unresolved, import/default */ |
|||
|
|||
import contactTemplate from './contact.tpl.html'; |
|||
|
|||
/* eslint-enable import/no-unresolved, import/default */ |
|||
|
|||
export default angular.module('thingsboard.directives.contact', []) |
|||
.directive('tbContact', Contact) |
|||
.name; |
|||
|
|||
/*@ngInject*/ |
|||
function Contact($compile, $templateCache) { |
|||
var countries = [ |
|||
"Afghanistan", |
|||
"Åland Islands", |
|||
"Albania", |
|||
"Algeria", |
|||
"American Samoa", |
|||
"Andorra", |
|||
"Angola", |
|||
"Anguilla", |
|||
"Antarctica", |
|||
"Antigua and Barbuda", |
|||
"Argentina", |
|||
"Armenia", |
|||
"Aruba", |
|||
"Australia", |
|||
"Austria", |
|||
"Azerbaijan", |
|||
"Bahamas", |
|||
"Bahrain", |
|||
"Bangladesh", |
|||
"Barbados", |
|||
"Belarus", |
|||
"Belgium", |
|||
"Belize", |
|||
"Benin", |
|||
"Bermuda", |
|||
"Bhutan", |
|||
"Bolivia", |
|||
"Bonaire, Sint Eustatius and Saba", |
|||
"Bosnia and Herzegovina", |
|||
"Botswana", |
|||
"Bouvet Island", |
|||
"Brazil", |
|||
"British Indian Ocean Territory", |
|||
"Brunei Darussalam", |
|||
"Bulgaria", |
|||
"Burkina Faso", |
|||
"Burundi", |
|||
"Cambodia", |
|||
"Cameroon", |
|||
"Canada", |
|||
"Cape Verde", |
|||
"Cayman Islands", |
|||
"Central African Republic", |
|||
"Chad", |
|||
"Chile", |
|||
"China", |
|||
"Christmas Island", |
|||
"Cocos (Keeling) Islands", |
|||
"Colombia", |
|||
"Comoros", |
|||
"Congo", |
|||
"Congo, The Democratic Republic of the", |
|||
"Cook Islands", |
|||
"Costa Rica", |
|||
"Côte d'Ivoire", |
|||
"Croatia", |
|||
"Cuba", |
|||
"Curaçao", |
|||
"Cyprus", |
|||
"Czech Republic", |
|||
"Denmark", |
|||
"Djibouti", |
|||
"Dominica", |
|||
"Dominican Republic", |
|||
"Ecuador", |
|||
"Egypt", |
|||
"El Salvador", |
|||
"Equatorial Guinea", |
|||
"Eritrea", |
|||
"Estonia", |
|||
"Ethiopia", |
|||
"Falkland Islands (Malvinas)", |
|||
"Faroe Islands", |
|||
"Fiji", |
|||
"Finland", |
|||
"France", |
|||
"French Guiana", |
|||
"French Polynesia", |
|||
"French Southern Territories", |
|||
"Gabon", |
|||
"Gambia", |
|||
"Georgia", |
|||
"Germany", |
|||
"Ghana", |
|||
"Gibraltar", |
|||
"Greece", |
|||
"Greenland", |
|||
"Grenada", |
|||
"Guadeloupe", |
|||
"Guam", |
|||
"Guatemala", |
|||
"Guernsey", |
|||
"Guinea", |
|||
"Guinea-Bissau", |
|||
"Guyana", |
|||
"Haiti", |
|||
"Heard Island and McDonald Islands", |
|||
"Holy See (Vatican City State)", |
|||
"Honduras", |
|||
"Hong Kong", |
|||
"Hungary", |
|||
"Iceland", |
|||
"India", |
|||
"Indonesia", |
|||
"Iran, Islamic Republic of", |
|||
"Iraq", |
|||
"Ireland", |
|||
"Isle of Man", |
|||
"Israel", |
|||
"Italy", |
|||
"Jamaica", |
|||
"Japan", |
|||
"Jersey", |
|||
"Jordan", |
|||
"Kazakhstan", |
|||
"Kenya", |
|||
"Kiribati", |
|||
"Korea, Democratic People's Republic of", |
|||
"Korea, Republic of", |
|||
"Kuwait", |
|||
"Kyrgyzstan", |
|||
"Lao People's Democratic Republic", |
|||
"Latvia", |
|||
"Lebanon", |
|||
"Lesotho", |
|||
"Liberia", |
|||
"Libya", |
|||
"Liechtenstein", |
|||
"Lithuania", |
|||
"Luxembourg", |
|||
"Macao", |
|||
"Macedonia, Republic Of", |
|||
"Madagascar", |
|||
"Malawi", |
|||
"Malaysia", |
|||
"Maldives", |
|||
"Mali", |
|||
"Malta", |
|||
"Marshall Islands", |
|||
"Martinique", |
|||
"Mauritania", |
|||
"Mauritius", |
|||
"Mayotte", |
|||
"Mexico", |
|||
"Micronesia, Federated States of", |
|||
"Moldova, Republic of", |
|||
"Monaco", |
|||
"Mongolia", |
|||
"Montenegro", |
|||
"Montserrat", |
|||
"Morocco", |
|||
"Mozambique", |
|||
"Myanmar", |
|||
"Namibia", |
|||
"Nauru", |
|||
"Nepal", |
|||
"Netherlands", |
|||
"New Caledonia", |
|||
"New Zealand", |
|||
"Nicaragua", |
|||
"Niger", |
|||
"Nigeria", |
|||
"Niue", |
|||
"Norfolk Island", |
|||
"Northern Mariana Islands", |
|||
"Norway", |
|||
"Oman", |
|||
"Pakistan", |
|||
"Palau", |
|||
"Palestinian Territory, Occupied", |
|||
"Panama", |
|||
"Papua New Guinea", |
|||
"Paraguay", |
|||
"Peru", |
|||
"Philippines", |
|||
"Pitcairn", |
|||
"Poland", |
|||
"Portugal", |
|||
"Puerto Rico", |
|||
"Qatar", |
|||
"Reunion", |
|||
"Romania", |
|||
"Russian Federation", |
|||
"Rwanda", |
|||
"Saint Barthélemy", |
|||
"Saint Helena, Ascension and Tristan da Cunha", |
|||
"Saint Kitts and Nevis", |
|||
"Saint Lucia", |
|||
"Saint Martin (French Part)", |
|||
"Saint Pierre and Miquelon", |
|||
"Saint Vincent and the Grenadines", |
|||
"Samoa", |
|||
"San Marino", |
|||
"Sao Tome and Principe", |
|||
"Saudi Arabia", |
|||
"Senegal", |
|||
"Serbia", |
|||
"Seychelles", |
|||
"Sierra Leone", |
|||
"Singapore", |
|||
"Sint Maarten (Dutch Part)", |
|||
"Slovakia", |
|||
"Slovenia", |
|||
"Solomon Islands", |
|||
"Somalia", |
|||
"South Africa", |
|||
"South Georgia and the South Sandwich Islands", |
|||
"South Sudan", |
|||
"Spain", |
|||
"Sri Lanka", |
|||
"Sudan", |
|||
"Suriname", |
|||
"Svalbard and Jan Mayen", |
|||
"Swaziland", |
|||
"Sweden", |
|||
"Switzerland", |
|||
"Syrian Arab Republic", |
|||
"Taiwan", |
|||
"Tajikistan", |
|||
"Tanzania, United Republic of", |
|||
"Thailand", |
|||
"Timor-Leste", |
|||
"Togo", |
|||
"Tokelau", |
|||
"Tonga", |
|||
"Trinidad and Tobago", |
|||
"Tunisia", |
|||
"Turkey", |
|||
"Turkmenistan", |
|||
"Turks and Caicos Islands", |
|||
"Tuvalu", |
|||
"Uganda", |
|||
"Ukraine", |
|||
"United Arab Emirates", |
|||
"United Kingdom", |
|||
"United States", |
|||
"United States Minor Outlying Islands", |
|||
"Uruguay", |
|||
"Uzbekistan", |
|||
"Vanuatu", |
|||
"Venezuela", |
|||
"Viet Nam", |
|||
"Virgin Islands, British", |
|||
"Virgin Islands, U.S.", |
|||
"Wallis and Futuna", |
|||
"Western Sahara", |
|||
"Yemen", |
|||
"Zambia", |
|||
"Zimbabwe" |
|||
]; |
|||
|
|||
var postalCodePatterns = { |
|||
"United States": "(\\d{5}([\\-]\\d{4})?)", |
|||
"Australia": "[0-9]{4}", |
|||
"Austria": "[0-9]{4}", |
|||
"Belgium": "[0-9]{4}", |
|||
"Brazil": "[0-9]{5}[\\-]?[0-9]{3}", |
|||
"Canada": "^(?!.*[DFIOQU])[A-VXY][0-9][A-Z][ -]?[0-9][A-Z][0-9]$", |
|||
"Denmark": "[0-9]{3,4}", |
|||
"Faroe Islands": "[0-9]{3,4}", |
|||
"Netherlands": "[1-9][0-9]{3}\\s?[a-zA-Z]{2}", |
|||
"Germany": "[0-9]{5}", |
|||
"Hungary": "[0-9]{4}", |
|||
"Italy": "[0-9]{5}", |
|||
"Japan": "\\d{3}-\\d{4}", |
|||
"Luxembourg": "(L\\s*(-|—|–))\\s*?[\\d]{4}", |
|||
"Poland": "[0-9]{2}\\-[0-9]{3}", |
|||
"Spain": "((0[1-9]|5[0-2])|[1-4][0-9])[0-9]{3}", |
|||
"Sweden": "\\d{3}\\s?\\d{2}", |
|||
"United Kingdom": "[A-Za-z]{1,2}[0-9Rr][0-9A-Za-z]? [0-9][ABD-HJLNP-UW-Zabd-hjlnp-uw-z]{2}" |
|||
}; |
|||
|
|||
var linker = function (scope, element) { |
|||
|
|||
scope.countries = countries; |
|||
|
|||
scope.postalCodePatterns = postalCodePatterns; |
|||
|
|||
var template = $templateCache.get(contactTemplate); |
|||
|
|||
element.html(template); |
|||
|
|||
$compile(element.contents())(scope); |
|||
} |
|||
|
|||
return { |
|||
restrict: "E", |
|||
link: linker, |
|||
scope: { |
|||
contact: '=', |
|||
isEdit: '=', |
|||
theForm: '=' |
|||
} |
|||
}; |
|||
} |
|||
@ -1,59 +0,0 @@ |
|||
<!-- |
|||
|
|||
Copyright © 2016-2020 The Thingsboard Authors |
|||
|
|||
Licensed under the Apache License, Version 2.0 (the "License"); |
|||
you may not use this file except in compliance with the License. |
|||
You may obtain a copy of the License at |
|||
|
|||
http://www.apache.org/licenses/LICENSE-2.0 |
|||
|
|||
Unless required by applicable law or agreed to in writing, software |
|||
distributed under the License is distributed on an "AS IS" BASIS, |
|||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
See the License for the specific language governing permissions and |
|||
limitations under the License. |
|||
|
|||
--> |
|||
<md-input-container class="md-block"> |
|||
<label translate>contact.country</label> |
|||
<md-select ng-disabled="!isEdit" name="country" ng-model="contact.country"> |
|||
<md-option ng-repeat="country in countries" value="{{country}}"> |
|||
{{country}} |
|||
</md-option> |
|||
</md-select> |
|||
</md-input-container> |
|||
<div layout-gt-sm="row"> |
|||
<md-input-container class="md-block"> |
|||
<label translate>contact.city</label> |
|||
<input name="city" ng-model="contact.city"> |
|||
</md-input-container> |
|||
<md-input-container class="md-block"> |
|||
<label translate>contact.state</label> |
|||
<input name="state" ng-model="contact.state"> |
|||
</md-input-container> |
|||
<md-input-container class="md-block"> |
|||
<label translate>contact.postal-code</label> |
|||
<input name="zip" ng-model="contact.zip" ng-pattern="postalCodePatterns[contact.country]"> |
|||
<div ng-messages="theForm.zip.$error" role="alert" multiple> |
|||
<div translate ng-message="pattern">contact.postal-code-invalid</div> |
|||
</div> |
|||
</md-input-container> |
|||
</div> |
|||
<md-input-container class="md-block"> |
|||
<label translate>contact.address</label> |
|||
<input name="address" ng-model="contact.address"> |
|||
</md-input-container> |
|||
<md-input-container class="md-block"> |
|||
<label translate>contact.address2</label> |
|||
<input name="address2" ng-model="contact.address2"> |
|||
</md-input-container> |
|||
<md-input-container class="md-block"> |
|||
<label translate>contact.phone</label> |
|||
<input name="phone" ng-model="contact.phone"> |
|||
</md-input-container> |
|||
<md-input-container class="md-block"> |
|||
<label translate>contact.email</label> |
|||
<input name="email" type="text" ng-pattern='/^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\_\-0-9]+\.)+[a-zA-Z]{2,}))$/' ng-model="contact.email"> |
|||
</md-input-container> |
|||
|
|||
@ -1,146 +0,0 @@ |
|||
/* |
|||
* Copyright © 2016-2020 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
import './dashboard-autocomplete.scss'; |
|||
|
|||
import thingsboardApiDashboard from '../api/dashboard.service'; |
|||
import thingsboardApiUser from '../api/user.service'; |
|||
|
|||
/* eslint-disable import/no-unresolved, import/default */ |
|||
|
|||
import dashboardAutocompleteTemplate from './dashboard-autocomplete.tpl.html'; |
|||
|
|||
/* eslint-enable import/no-unresolved, import/default */ |
|||
|
|||
|
|||
export default angular.module('thingsboard.directives.dashboardAutocomplete', [thingsboardApiDashboard, thingsboardApiUser]) |
|||
.directive('tbDashboardAutocomplete', DashboardAutocomplete) |
|||
.name; |
|||
|
|||
/*@ngInject*/ |
|||
function DashboardAutocomplete($compile, $templateCache, $q, dashboardService, userService) { |
|||
|
|||
var linker = function (scope, element, attrs, ngModelCtrl) { |
|||
var template = $templateCache.get(dashboardAutocompleteTemplate); |
|||
element.html(template); |
|||
|
|||
scope.tbRequired = angular.isDefined(scope.tbRequired) ? scope.tbRequired : false; |
|||
scope.dashboard = null; |
|||
scope.dashboardSearchText = ''; |
|||
|
|||
scope.fetchDashboards = function(searchText) { |
|||
var pageLink = {limit: 50, textSearch: searchText}; |
|||
|
|||
var deferred = $q.defer(); |
|||
|
|||
var promise; |
|||
if (scope.dashboardsScope === 'customer' || userService.getAuthority() === 'CUSTOMER_USER') { |
|||
if (scope.customerId) { |
|||
promise = dashboardService.getCustomerDashboards(scope.customerId, pageLink, {ignoreLoading: true}); |
|||
} else { |
|||
promise = $q.when({data: []}); |
|||
} |
|||
} else { |
|||
if (userService.getAuthority() === 'SYS_ADMIN') { |
|||
if (scope.tenantId) { |
|||
promise = dashboardService.getTenantDashboardsByTenantId(scope.tenantId, pageLink, {ignoreLoading: true}); |
|||
} else { |
|||
promise = $q.when({data: []}); |
|||
} |
|||
} else { |
|||
promise = dashboardService.getTenantDashboards(pageLink, {ignoreLoading: true}); |
|||
} |
|||
} |
|||
|
|||
promise.then(function success(result) { |
|||
deferred.resolve(result.data); |
|||
}, function fail() { |
|||
deferred.reject(); |
|||
}); |
|||
|
|||
return deferred.promise; |
|||
} |
|||
|
|||
scope.dashboardSearchTextChanged = function() { |
|||
} |
|||
|
|||
scope.updateView = function () { |
|||
if (!scope.disabled) { |
|||
ngModelCtrl.$setViewValue(scope.dashboard ? scope.dashboard.id.id : null); |
|||
} |
|||
} |
|||
|
|||
ngModelCtrl.$render = function () { |
|||
if (ngModelCtrl.$viewValue) { |
|||
dashboardService.getDashboardInfo(ngModelCtrl.$viewValue).then( |
|||
function success(dashboard) { |
|||
scope.dashboard = dashboard; |
|||
startWatchers(); |
|||
}, |
|||
function fail() { |
|||
scope.dashboard = null; |
|||
scope.updateView(); |
|||
startWatchers(); |
|||
} |
|||
); |
|||
} else { |
|||
scope.dashboard = null; |
|||
startWatchers(); |
|||
} |
|||
} |
|||
|
|||
function startWatchers() { |
|||
scope.$watch('dashboard', function (newVal, prevVal) { |
|||
if (!angular.equals(newVal, prevVal)) { |
|||
scope.updateView(); |
|||
} |
|||
}); |
|||
scope.$watch('disabled', function (newVal, prevVal) { |
|||
if (!angular.equals(newVal, prevVal)) { |
|||
scope.updateView(); |
|||
} |
|||
}); |
|||
} |
|||
|
|||
if (scope.selectFirstDashboard) { |
|||
var pageLink = {limit: 1, textSearch: ''}; |
|||
scope.dashboardFetchFunction(pageLink).then(function success(result) { |
|||
var dashboards = result.data; |
|||
if (dashboards.length > 0) { |
|||
scope.dashboard = dashboards[0]; |
|||
scope.updateView(); |
|||
} |
|||
}, function fail() { |
|||
}); |
|||
} |
|||
|
|||
$compile(element.contents())(scope); |
|||
} |
|||
|
|||
return { |
|||
restrict: "E", |
|||
require: "^ngModel", |
|||
link: linker, |
|||
scope: { |
|||
dashboardsScope: '@', |
|||
tenantId: '=', |
|||
customerId: '=', |
|||
theForm: '=?', |
|||
tbRequired: '=?', |
|||
disabled:'=ngDisabled', |
|||
selectFirstDashboard: '=' |
|||
} |
|||
}; |
|||
} |
|||
@ -1,32 +0,0 @@ |
|||
/** |
|||
* Copyright © 2016-2020 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0 |
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
.tb-dashboard-autocomplete { |
|||
.tb-not-found { |
|||
display: block; |
|||
height: 48px; |
|||
line-height: 1.5; |
|||
} |
|||
|
|||
.tb-dashboard-item { |
|||
display: block; |
|||
height: 48px; |
|||
} |
|||
|
|||
li { |
|||
height: auto !important; |
|||
white-space: normal !important; |
|||
} |
|||
} |
|||
@ -1,43 +0,0 @@ |
|||
<!-- |
|||
|
|||
Copyright © 2016-2020 The Thingsboard Authors |
|||
|
|||
Licensed under the Apache License, Version 2.0 (the "License"); |
|||
you may not use this file except in compliance with the License. |
|||
You may obtain a copy of the License at |
|||
|
|||
http://www.apache.org/licenses/LICENSE-2.0 |
|||
|
|||
Unless required by applicable law or agreed to in writing, software |
|||
distributed under the License is distributed on an "AS IS" BASIS, |
|||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
See the License for the specific language governing permissions and |
|||
limitations under the License. |
|||
|
|||
--> |
|||
<md-autocomplete ng-required="tbRequired" |
|||
ng-disabled="disabled" |
|||
md-input-name="dashboard" |
|||
ng-model="dashboard" |
|||
md-selected-item="dashboard" |
|||
md-search-text="dashboardSearchText" |
|||
md-search-text-change="dashboardSearchTextChanged()" |
|||
md-items="item in fetchDashboards(dashboardSearchText)" |
|||
md-item-text="item.title" |
|||
md-min-length="0" |
|||
placeholder="{{ 'dashboard.select-dashboard' | translate }}" |
|||
md-menu-class="tb-dashboard-autocomplete"> |
|||
<md-item-template> |
|||
<div class="tb-dashboard-item"> |
|||
<span md-highlight-text="dashboardSearchText" md-highlight-flags="^i">{{item.title}}</span> |
|||
</div> |
|||
</md-item-template> |
|||
<md-not-found> |
|||
<div class="tb-not-found"> |
|||
<span translate translate-values='{ entity: dashboardSearchText }'>dashboard.no-dashboards-matching</span> |
|||
</div> |
|||
</md-not-found> |
|||
<div ng-messages="theForm.dashboard.$error"> |
|||
<div translate ng-message="required">dashboard.dashboard-required</div> |
|||
</div> |
|||
</md-autocomplete> |
|||
@ -1,31 +0,0 @@ |
|||
/* |
|||
* Copyright © 2016-2020 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
/*@ngInject*/ |
|||
export default function DashboardSelectPanelController(mdPanelRef, $scope, $filter, dashboards, dashboardId, onDashboardSelected) { |
|||
|
|||
var vm = this; |
|||
vm._mdPanelRef = mdPanelRef; |
|||
vm.dashboards = dashboards; |
|||
vm.dashboardId = dashboardId; |
|||
|
|||
vm.dashboardSelected = dashboardSelected; |
|||
|
|||
function dashboardSelected(dashboardId) { |
|||
if (onDashboardSelected) { |
|||
onDashboardSelected(dashboardId); |
|||
} |
|||
} |
|||
} |
|||
Some files were not shown because too many files changed in this diff
Loading…
Reference in new issue