Browse Source

Update mqtt paho client version. Completely remove old ui.

pull/2775/head
Igor Kulikov 6 years ago
parent
commit
36457f8b13
  1. 28
      msa/js-executor/package-lock.json
  2. 2
      pom.xml
  3. 9
      ui/.babelrc
  4. 18
      ui/.eslintrc
  5. 2
      ui/.gitignore
  6. 13
      ui/.jshintrc
  7. 292
      ui/.stylelintrc
  8. 14229
      ui/package-lock.json
  9. 179
      ui/package.json
  10. 142
      ui/pom.xml
  11. 100
      ui/server.js
  12. 58
      ui/src/app/admin/admin.controller.js
  13. 92
      ui/src/app/admin/admin.routes.js
  14. 45
      ui/src/app/admin/general-settings.tpl.html
  15. 38
      ui/src/app/admin/index.js
  16. 107
      ui/src/app/admin/outgoing-mail-settings.tpl.html
  17. 41
      ui/src/app/admin/security-settings.controller.js
  18. 167
      ui/src/app/admin/security-settings.tpl.html
  19. 26
      ui/src/app/admin/settings-card.scss
  20. 141
      ui/src/app/alarm/alarm-details-dialog.controller.js
  21. 27
      ui/src/app/alarm/alarm-details-dialog.scss
  22. 107
      ui/src/app/alarm/alarm-details-dialog.tpl.html
  23. 39
      ui/src/app/alarm/alarm-header.directive.js
  24. 23
      ui/src/app/alarm/alarm-header.tpl.html
  25. 73
      ui/src/app/alarm/alarm-row.directive.js
  26. 37
      ui/src/app/alarm/alarm-row.tpl.html
  27. 213
      ui/src/app/alarm/alarm-table.directive.js
  28. 56
      ui/src/app/alarm/alarm-table.tpl.html
  29. 77
      ui/src/app/alarm/alarm.scss
  30. 26
      ui/src/app/alarm/index.js
  31. 99
      ui/src/app/api/admin.service.js
  32. 356
      ui/src/app/api/alarm.service.js
  33. 322
      ui/src/app/api/alias-controller.js
  34. 292
      ui/src/app/api/asset.service.js
  35. 356
      ui/src/app/api/attribute.service.js
  36. 116
      ui/src/app/api/audit-log.service.js
  37. 123
      ui/src/app/api/component-descriptor.service.js
  38. 170
      ui/src/app/api/customer.service.js
  39. 295
      ui/src/app/api/dashboard.service.js
  40. 316
      ui/src/app/api/data-aggregator.js
  41. 747
      ui/src/app/api/datasource.service.js
  42. 361
      ui/src/app/api/device.service.js
  43. 189
      ui/src/app/api/entity-relation.service.js
  44. 224
      ui/src/app/api/entity-view.service.js
  45. 1581
      ui/src/app/api/entity.service.js
  46. 50
      ui/src/app/api/event.service.js
  47. 125
      ui/src/app/api/login.service.js
  48. 40
      ui/src/app/api/queue.service.js
  49. 302
      ui/src/app/api/rule-chain.service.js
  50. 1072
      ui/src/app/api/subscription.js
  51. 362
      ui/src/app/api/telemetry-websocket.service.js
  52. 85
      ui/src/app/api/tenant.service.js
  53. 413
      ui/src/app/api/time.service.js
  54. 688
      ui/src/app/api/user.service.js
  55. 832
      ui/src/app/api/widget.service.js
  56. 174
      ui/src/app/app.config.js
  57. 163
      ui/src/app/app.js
  58. 210
      ui/src/app/app.run.js
  59. 45
      ui/src/app/asset/add-asset.tpl.html
  60. 123
      ui/src/app/asset/add-assets-to-customer.controller.js
  61. 77
      ui/src/app/asset/add-assets-to-customer.tpl.html
  62. 22
      ui/src/app/asset/asset-card.tpl.html
  63. 75
      ui/src/app/asset/asset-fieldset.tpl.html
  64. 534
      ui/src/app/asset/asset.controller.js
  65. 72
      ui/src/app/asset/asset.directive.js
  66. 72
      ui/src/app/asset/asset.routes.js
  67. 75
      ui/src/app/asset/assets.tpl.html
  68. 123
      ui/src/app/asset/assign-to-customer.controller.js
  69. 76
      ui/src/app/asset/assign-to-customer.tpl.html
  70. 41
      ui/src/app/asset/index.js
  71. 104
      ui/src/app/audit/audit-log-details-dialog.controller.js
  72. 23
      ui/src/app/audit/audit-log-details-dialog.scss
  73. 49
      ui/src/app/audit/audit-log-details-dialog.tpl.html
  74. 41
      ui/src/app/audit/audit-log-header.directive.js
  75. 24
      ui/src/app/audit/audit-log-header.tpl.html
  76. 67
      ui/src/app/audit/audit-log-row.directive.js
  77. 36
      ui/src/app/audit/audit-log-row.tpl.html
  78. 262
      ui/src/app/audit/audit-log-table.directive.js
  79. 68
      ui/src/app/audit/audit-log-table.tpl.html
  80. 44
      ui/src/app/audit/audit-log.routes.js
  81. 92
      ui/src/app/audit/audit-log.scss
  82. 23
      ui/src/app/audit/audit-logs.controller.js
  83. 22
      ui/src/app/audit/audit-logs.tpl.html
  84. 30
      ui/src/app/audit/index.js
  85. 566
      ui/src/app/common/dashboard-utils.service.js
  86. 49
      ui/src/app/common/raf.provider.js
  87. 458
      ui/src/app/common/thirdparty-fix.js
  88. 991
      ui/src/app/common/types.constant.js
  89. 137
      ui/src/app/common/utf8-support.js
  90. 608
      ui/src/app/common/utils.service.js
  91. 44
      ui/src/app/components/ace-editor-fix.js
  92. 74
      ui/src/app/components/circular-progress.directive.js
  93. 55
      ui/src/app/components/confirm-on-exit.directive.js
  94. 54
      ui/src/app/components/contact-short.filter.js
  95. 323
      ui/src/app/components/contact.directive.js
  96. 59
      ui/src/app/components/contact.tpl.html
  97. 146
      ui/src/app/components/dashboard-autocomplete.directive.js
  98. 32
      ui/src/app/components/dashboard-autocomplete.scss
  99. 43
      ui/src/app/components/dashboard-autocomplete.tpl.html
  100. 31
      ui/src/app/components/dashboard-select-panel.controller.js

28
msa/js-executor/package-lock.json

@ -1944,12 +1944,14 @@
"balanced-match": {
"version": "1.0.0",
"bundled": true,
"dev": true
"dev": true,
"optional": true
},
"brace-expansion": {
"version": "1.1.11",
"bundled": true,
"dev": true,
"optional": true,
"requires": {
"balanced-match": "^1.0.0",
"concat-map": "0.0.1"
@ -1964,17 +1966,20 @@
"code-point-at": {
"version": "1.1.0",
"bundled": true,
"dev": true
"dev": true,
"optional": true
},
"concat-map": {
"version": "0.0.1",
"bundled": true,
"dev": true
"dev": true,
"optional": true
},
"console-control-strings": {
"version": "1.1.0",
"bundled": true,
"dev": true
"dev": true,
"optional": true
},
"core-util-is": {
"version": "1.0.2",
@ -2091,7 +2096,8 @@
"inherits": {
"version": "2.0.4",
"bundled": true,
"dev": true
"dev": true,
"optional": true
},
"ini": {
"version": "1.3.5",
@ -2103,6 +2109,7 @@
"version": "1.0.0",
"bundled": true,
"dev": true,
"optional": true,
"requires": {
"number-is-nan": "^1.0.0"
}
@ -2117,6 +2124,7 @@
"version": "3.0.4",
"bundled": true,
"dev": true,
"optional": true,
"requires": {
"brace-expansion": "^1.1.7"
}
@ -2124,12 +2132,14 @@
"minimist": {
"version": "0.0.8",
"bundled": true,
"dev": true
"dev": true,
"optional": true
},
"minipass": {
"version": "2.9.0",
"bundled": true,
"dev": true,
"optional": true,
"requires": {
"safe-buffer": "^5.1.2",
"yallist": "^3.0.0"
@ -2148,6 +2158,7 @@
"version": "0.5.1",
"bundled": true,
"dev": true,
"optional": true,
"requires": {
"minimist": "0.0.8"
}
@ -2237,7 +2248,8 @@
"number-is-nan": {
"version": "1.0.1",
"bundled": true,
"dev": true
"dev": true,
"optional": true
},
"object-assign": {
"version": "4.1.1",
@ -2249,6 +2261,7 @@
"version": "1.4.0",
"bundled": true,
"dev": true,
"optional": true,
"requires": {
"wrappy": "1"
}
@ -2370,6 +2383,7 @@
"version": "1.0.2",
"bundled": true,
"dev": true,
"optional": true,
"requires": {
"code-point-at": "^1.0.0",
"is-fullwidth-code-point": "^1.0.0",

2
pom.xml

@ -69,7 +69,7 @@
<protobuf.version>3.11.4</protobuf.version>
<grpc.version>1.22.1</grpc.version>
<lombok.version>1.16.18</lombok.version>
<paho.client.version>1.1.0</paho.client.version>
<paho.client.version>1.2.4</paho.client.version>
<netty.version>4.1.49.Final</netty.version>
<os-maven-plugin.version>1.5.0</os-maven-plugin.version>
<rabbitmq.version>4.8.0</rabbitmq.version>

9
ui/.babelrc

@ -1,9 +0,0 @@
{
"presets": [
"@babel/preset-react",
"@babel/preset-env"
],
"plugins": [
"react-hot-loader/babel"
]
}

18
ui/.eslintrc

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

2
ui/.gitignore

@ -1,2 +0,0 @@
node_modules
.tern-project

13
ui/.jshintrc

@ -1,13 +0,0 @@
{
"globalstrict": true,
"globals": {
"angular": false,
"describe": false,
"it": false,
"expect": false,
"beforeEach": false,
"afterEach": false,
"module": false,
"inject": false
}
}

292
ui/.stylelintrc

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

14229
ui/package-lock.json

File diff suppressed because it is too large

179
ui/package.json

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

142
ui/pom.xml

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

100
ui/server.js

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

58
ui/src/app/admin/admin.controller.js

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

92
ui/src/app/admin/admin.routes.js

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

45
ui/src/app/admin/general-settings.tpl.html

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

38
ui/src/app/admin/index.js

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

107
ui/src/app/admin/outgoing-mail-settings.tpl.html

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

41
ui/src/app/admin/security-settings.controller.js

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

167
ui/src/app/admin/security-settings.tpl.html

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

26
ui/src/app/admin/settings-card.scss

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

141
ui/src/app/alarm/alarm-details-dialog.controller.js

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

27
ui/src/app/alarm/alarm-details-dialog.scss

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

107
ui/src/app/alarm/alarm-details-dialog.tpl.html

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

39
ui/src/app/alarm/alarm-header.directive.js

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

23
ui/src/app/alarm/alarm-header.tpl.html

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

73
ui/src/app/alarm/alarm-row.directive.js

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

37
ui/src/app/alarm/alarm-row.tpl.html

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

213
ui/src/app/alarm/alarm-table.directive.js

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

56
ui/src/app/alarm/alarm-table.tpl.html

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

77
ui/src/app/alarm/alarm.scss

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

26
ui/src/app/alarm/index.js

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

99
ui/src/app/api/admin.service.js

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

356
ui/src/app/api/alarm.service.js

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

322
ui/src/app/api/alias-controller.js

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

292
ui/src/app/api/asset.service.js

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

356
ui/src/app/api/attribute.service.js

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

116
ui/src/app/api/audit-log.service.js

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

123
ui/src/app/api/component-descriptor.service.js

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

170
ui/src/app/api/customer.service.js

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

295
ui/src/app/api/dashboard.service.js

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

316
ui/src/app/api/data-aggregator.js

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

747
ui/src/app/api/datasource.service.js

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

361
ui/src/app/api/device.service.js

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

189
ui/src/app/api/entity-relation.service.js

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

224
ui/src/app/api/entity-view.service.js

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

1581
ui/src/app/api/entity.service.js

File diff suppressed because it is too large

50
ui/src/app/api/event.service.js

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

125
ui/src/app/api/login.service.js

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

40
ui/src/app/api/queue.service.js

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

302
ui/src/app/api/rule-chain.service.js

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

1072
ui/src/app/api/subscription.js

File diff suppressed because it is too large

362
ui/src/app/api/telemetry-websocket.service.js

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

85
ui/src/app/api/tenant.service.js

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

413
ui/src/app/api/time.service.js

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

688
ui/src/app/api/user.service.js

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

832
ui/src/app/api/widget.service.js

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

174
ui/src/app/app.config.js

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

163
ui/src/app/app.js

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

210
ui/src/app/app.run.js

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

45
ui/src/app/asset/add-asset.tpl.html

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

123
ui/src/app/asset/add-assets-to-customer.controller.js

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

77
ui/src/app/asset/add-assets-to-customer.tpl.html

@ -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>&nbsp;</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>

22
ui/src/app/asset/asset-card.tpl.html

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

75
ui/src/app/asset/asset-fieldset.tpl.html

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

534
ui/src/app/asset/asset.controller.js

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

72
ui/src/app/asset/asset.directive.js

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

72
ui/src/app/asset/asset.routes.js

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

75
ui/src/app/asset/assets.tpl.html

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

123
ui/src/app/asset/assign-to-customer.controller.js

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

76
ui/src/app/asset/assign-to-customer.tpl.html

@ -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>&nbsp;</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>

41
ui/src/app/asset/index.js

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

104
ui/src/app/audit/audit-log-details-dialog.controller.js

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

23
ui/src/app/audit/audit-log-details-dialog.scss

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

49
ui/src/app/audit/audit-log-details-dialog.tpl.html

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

41
ui/src/app/audit/audit-log-header.directive.js

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

24
ui/src/app/audit/audit-log-header.tpl.html

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

67
ui/src/app/audit/audit-log-row.directive.js

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

36
ui/src/app/audit/audit-log-row.tpl.html

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

262
ui/src/app/audit/audit-log-table.directive.js

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

68
ui/src/app/audit/audit-log-table.tpl.html

@ -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>&nbsp;</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>

44
ui/src/app/audit/audit-log.routes.js

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

92
ui/src/app/audit/audit-log.scss

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

23
ui/src/app/audit/audit-logs.controller.js

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

22
ui/src/app/audit/audit-logs.tpl.html

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

30
ui/src/app/audit/index.js

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

566
ui/src/app/common/dashboard-utils.service.js

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

49
ui/src/app/common/raf.provider.js

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

458
ui/src/app/common/thirdparty-fix.js

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

991
ui/src/app/common/types.constant.js

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

137
ui/src/app/common/utf8-support.js

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

608
ui/src/app/common/utils.service.js

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

44
ui/src/app/components/ace-editor-fix.js

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

74
ui/src/app/components/circular-progress.directive.js

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

55
ui/src/app/components/confirm-on-exit.directive.js

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

54
ui/src/app/components/contact-short.filter.js

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

323
ui/src/app/components/contact.directive.js

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

59
ui/src/app/components/contact.tpl.html

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

146
ui/src/app/components/dashboard-autocomplete.directive.js

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

32
ui/src/app/components/dashboard-autocomplete.scss

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

43
ui/src/app/components/dashboard-autocomplete.tpl.html

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

31
ui/src/app/components/dashboard-select-panel.controller.js

@ -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…
Cancel
Save