Add social share buttons for public dashboards sharing. Improve dashboard toolbar configuration. Add project version information to bottom right part of the dashboard.

This commit is contained in:
Igor Kulikov 2017-05-03 14:24:26 +03:00
parent 912c572aed
commit bfd654d84f
17 changed files with 290 additions and 29 deletions

View File

@ -33,6 +33,7 @@
"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.13.1",

View File

@ -19,6 +19,7 @@ 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';
@ -82,6 +83,7 @@ angular.module('thingsboard', [
ngMaterial,
ngMdIcons,
ngCookies,
angularSocialshare,
'pascalprecht.translate',
'mdColorPicker',
mdPickers,

View File

@ -106,7 +106,8 @@ function Utils($mdColorPalette, $rootScope, $window, $q, deviceService, types) {
isDescriptorSchemaNotEmpty: isDescriptorSchemaNotEmpty,
filterSearchTextEntities: filterSearchTextEntities,
guid: guid,
createDatasoucesFromSubscriptionsInfo: createDatasoucesFromSubscriptionsInfo
createDatasoucesFromSubscriptionsInfo: createDatasoucesFromSubscriptionsInfo,
isLocalUrl: isLocalUrl
}
return service;
@ -428,4 +429,15 @@ function Utils($mdColorPalette, $rootScope, $window, $q, deviceService, types) {
return deferred.promise;
}
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;
}
}
}

View File

@ -0,0 +1,58 @@
/*
* Copyright © 2016-2017 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 socialsharePanelTemplate from './socialshare-panel.tpl.html';
/* eslint-enable import/no-unresolved, import/default */
export default angular.module('thingsboard.directives.socialsharePanel', [])
.directive('tbSocialSharePanel', SocialsharePanel)
.name;
/*@ngInject*/
function SocialsharePanel() {
return {
restrict: "E",
scope: true,
bindToController: {
shareTitle: '@',
shareText: '@',
shareLink: '@',
shareHashTags: '@'
},
controller: SocialsharePanelController,
controllerAs: 'vm',
templateUrl: socialsharePanelTemplate
};
}
/*@ngInject*/
function SocialsharePanelController(utils) {
let vm = this;
vm.isShareLinkLocal = function() {
if (vm.shareLink && vm.shareLink.length > 0) {
return utils.isLocalUrl(vm.shareLink);
} else {
return true;
}
}
}

View File

@ -0,0 +1,62 @@
<!--
Copyright © 2016-2017 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 layout="row" ng-show="!vm.isShareLinkLocal()">
<md-button class="md-icon-button md-raised md-primary"
socialshare
socialshare-provider="facebook"
socialshare-title="{{ vm.shareTitle }}"
socialshare-text="{{ vm.shareText }}"
socialshare-url="{{ vm.shareLink }}">
<ng-md-icon icon="facebook" aria-label="Facebook"></ng-md-icon>
<md-tooltip md-direction="top">
{{ 'action.share-via' | translate:{provider:'Facebook'} }}
</md-tooltip>
</md-button>
<md-button class="md-icon-button md-raised md-primary"
socialshare
socialshare-provider="twitter"
socialshare-text="{{ vm.shareTitle }}"
socialshare-hashtags="{{ vm.shareHashTags }}"
socialshare-url="{{ vm.shareLink }}">
<ng-md-icon icon="twitter" aria-label="Twitter"></ng-md-icon>
<md-tooltip md-direction="top">
{{ 'action.share-via' | translate:{provider:'Twitter'} }}
</md-tooltip>
</md-button>
<md-button class="md-icon-button md-raised md-primary"
socialshare
socialshare-provider="linkedin"
socialshare-text="{{ vm.shareTitle }}"
socialshare-url="{{ vm.shareLink }}">
<ng-md-icon icon="linkedin" aria-label="Linkedin"></ng-md-icon>
<md-tooltip md-direction="top">
{{ 'action.share-via' | translate:{provider:'Linkedin'} }}
</md-tooltip>
</md-button>
<md-button class="md-icon-button md-raised md-primary"
socialshare
socialshare-provider="reddit"
socialshare-text="{{ vm.shareTitle }}"
socialshare-url="{{ vm.shareLink }}">
<md-icon md-svg-icon="mdi:reddit" aria-label="Reddit"></md-icon>
<md-tooltip md-direction="top">
{{ 'action.share-via' | translate:{provider:'Reddit'} }}
</md-tooltip>
</md-button>
</div>

View File

@ -36,7 +36,14 @@
<label translate>dashboard.assignedToCustomer</label>
<input ng-model="assignedCustomer.title" disabled>
</md-input-container>
<div layout="row" ng-show="!isEdit && isPublic && (dashboardScope === 'customer' || dashboardScope === 'tenant')">
<div layout="column" ng-show="!isEdit && isPublic && (dashboardScope === 'customer' || dashboardScope === 'tenant')">
<tb-social-share-panel style="padding-bottom: 10px;"
share-title="{{ 'dashboard.socialshare-title' | translate:{dashboardTitle: dashboard.title} }}"
share-text="{{ 'dashboard.socialshare-text' | translate:{dashboardTitle: dashboard.title} }}"
share-link="{{ publicLink }}"
share-hash-tags="thingsboard, iot">
</tb-social-share-panel>
<div layout="row">
<md-input-container class="md-block" flex>
<label translate>dashboard.public-link</label>
<input ng-model="publicLink" disabled>
@ -51,6 +58,7 @@
</md-tooltip>
</md-button>
</div>
</div>
<fieldset ng-disabled="loading || !isEdit">
<md-input-container class="md-block">
<label translate>dashboard.title</label>

View File

@ -31,6 +31,18 @@ export default function DashboardSettingsController($scope, $mdDialog, gridSetti
vm.gridSettings.showTitle = true;
}
if (angular.isUndefined(vm.gridSettings.showDevicesSelect)) {
vm.gridSettings.showDevicesSelect = true;
}
if (angular.isUndefined(vm.gridSettings.showDashboardTimewindow)) {
vm.gridSettings.showDashboardTimewindow = true;
}
if (angular.isUndefined(vm.gridSettings.showDashboardExport)) {
vm.gridSettings.showDashboardExport = true;
}
vm.gridSettings.backgroundColor = vm.gridSettings.backgroundColor || 'rgba(0,0,0,0)';
vm.gridSettings.titleColor = vm.gridSettings.titleColor || 'rgba(0,0,0,0.870588)';
vm.gridSettings.columns = vm.gridSettings.columns || 24;

View File

@ -48,6 +48,17 @@
md-color-history="false"
></div>
</div>
<div layout="row" layout-align="start center">
<md-checkbox flex aria-label="{{ 'dashboard.display-device-selection' | translate }}"
ng-model="vm.gridSettings.showDevicesSelect">{{ 'dashboard.display-device-selection' | translate }}
</md-checkbox>
<md-checkbox flex aria-label="{{ 'dashboard.display-dashboard-timewindow' | translate }}"
ng-model="vm.gridSettings.showDashboardTimewindow">{{ 'dashboard.display-dashboard-timewindow' | translate }}
</md-checkbox>
<md-checkbox flex aria-label="{{ 'dashboard.display-dashboard-export' | translate }}"
ng-model="vm.gridSettings.showDashboardExport">{{ 'dashboard.display-dashboard-export' | translate }}
</md-checkbox>
</div>
<md-input-container class="md-block">
<label translate>dashboard.columns-count</label>
<input required type="number" step="any" name="columns" ng-model="vm.gridSettings.columns" min="10"

View File

@ -48,6 +48,8 @@ export default function DashboardController(types, widgetService, userService,
vm.isToolbarOpened = false;
vm.thingsboardVersion = THINGSBOARD_VERSION; //eslint-disable-line
vm.currentDashboardId = $stateParams.dashboardId;
if ($stateParams.customerId) {
vm.currentCustomerId = $stateParams.customerId;
@ -105,6 +107,9 @@ export default function DashboardController(types, widgetService, userService,
vm.onRevertWidgetEdit = onRevertWidgetEdit;
vm.helpLinkIdForWidgetType = helpLinkIdForWidgetType;
vm.displayTitle = displayTitle;
vm.displayExport = displayExport;
vm.displayDashboardTimewindow = displayDashboardTimewindow;
vm.displayDevicesSelect = displayDevicesSelect;
vm.widgetsBundle;
@ -565,6 +570,33 @@ export default function DashboardController(types, widgetService, userService,
}
}
function displayExport() {
if (vm.dashboard && vm.dashboard.configuration.gridSettings &&
angular.isDefined(vm.dashboard.configuration.gridSettings.showDashboardExport)) {
return vm.dashboard.configuration.gridSettings.showDashboardExport;
} else {
return true;
}
}
function displayDashboardTimewindow() {
if (vm.dashboard && vm.dashboard.configuration.gridSettings &&
angular.isDefined(vm.dashboard.configuration.gridSettings.showDashboardTimewindow)) {
return vm.dashboard.configuration.gridSettings.showDashboardTimewindow;
} else {
return true;
}
}
function displayDevicesSelect() {
if (vm.dashboard && vm.dashboard.configuration.gridSettings &&
angular.isDefined(vm.dashboard.configuration.gridSettings.showDevicesSelect)) {
return vm.dashboard.configuration.gridSettings.showDevicesSelect;
} else {
return true;
}
}
function onRevertWidgetEdit(widgetForm) {
if (widgetForm.$dirty) {
widgetForm.$setPristine();

View File

@ -49,16 +49,21 @@
</md-button>
<tb-user-menu ng-if="!vm.isPublicUser() && forceFullscreen" display-user-info="true">
</tb-user-menu>
<md-button aria-label="{{ 'action.export' | translate }}" class="md-icon-button"
<md-button ng-show="vm.isEdit || vm.displayExport()"
aria-label="{{ 'action.export' | translate }}" class="md-icon-button"
ng-click="vm.exportDashboard($event)">
<md-tooltip md-direction="bottom">
{{ 'dashboard.export' | translate }}
</md-tooltip>
<md-icon aria-label="{{ 'action.export' | translate }}" class="material-icons">file_download</md-icon>
</md-button>
<tb-timewindow is-toolbar direction="left" tooltip-direction="bottom" aggregation ng-model="vm.dashboardConfiguration.timewindow">
<tb-timewindow ng-show="vm.isEdit || vm.displayDashboardTimewindow()"
is-toolbar
direction="left"
tooltip-direction="bottom" aggregation
ng-model="vm.dashboardConfiguration.timewindow">
</tb-timewindow>
<tb-aliases-device-select ng-show="!vm.isEdit"
<tb-aliases-device-select ng-show="!vm.isEdit && vm.displayDevicesSelect()"
tooltip-direction="bottom"
ng-model="vm.aliasesInfo.deviceAliases"
device-aliases-info="vm.aliasesInfo.deviceAliasesInfo">
@ -304,6 +309,6 @@
</section>
</section>
<section class="tb-powered-by-footer" ng-style="{'color': vm.dashboard.configuration.gridSettings.titleColor}">
<span>Powered by <a href="https://thingsboard.io" target="_blank">Thingsboard</a></span>
<span>Powered by <a href="https://thingsboard.io" target="_blank">Thingsboard v.{{ vm.thingsboardVersion }}</a></span>
</section>
</md-content>

View File

@ -224,7 +224,7 @@ export function DashboardsController(userService, dashboardService, customerServ
onAction: function ($event, item) {
unassignFromCustomer($event, item, true);
},
name: function() { return $translate.instant('action.unshare') },
name: function() { return $translate.instant('action.make-private') },
details: function() { return $translate.instant('dashboard.make-private') },
icon: "reply",
isEnabled: function(dashboard) {
@ -329,7 +329,7 @@ export function DashboardsController(userService, dashboardService, customerServ
onAction: function ($event, item) {
unassignFromCustomer($event, item, true);
},
name: function() { return $translate.instant('action.unshare') },
name: function() { return $translate.instant('action.make-private') },
details: function() { return $translate.instant('dashboard.make-private') },
icon: "reply",
isEnabled: function(dashboard) {
@ -404,7 +404,28 @@ export function DashboardsController(userService, dashboardService, customerServ
}
function saveDashboard(dashboard) {
return dashboardService.saveDashboard(dashboard);
var deferred = $q.defer();
dashboardService.saveDashboard(dashboard).then(
function success(savedDashboard) {
var dashboards = [ savedDashboard ];
customerService.applyAssignedCustomersInfo(dashboards).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 assignToCustomer($event, dashboardIds) {

View File

@ -30,6 +30,7 @@ import thingsboardDashboardSelect from '../components/dashboard-select.directive
import thingsboardDashboard from '../components/dashboard.directive';
import thingsboardExpandFullscreen from '../components/expand-fullscreen.directive';
import thingsboardWidgetsBundleSelect from '../components/widgets-bundle-select.directive';
import thingsboardSocialsharePanel from '../components/socialshare-panel.directive';
import thingsboardTypes from '../common/types.constant';
import thingsboardItemBuffer from '../services/item-buffer.service';
import thingsboardImportExport from '../import-export';
@ -64,7 +65,8 @@ export default angular.module('thingsboard.dashboard', [
thingsboardDashboardSelect,
thingsboardDashboard,
thingsboardExpandFullscreen,
thingsboardWidgetsBundleSelect
thingsboardWidgetsBundleSelect,
thingsboardSocialsharePanel
])
.config(DashboardRoutes)
.controller('DashboardsController', DashboardsController)

View File

@ -43,6 +43,12 @@
</md-button>
</div>
<div class="tb-notice" translate>dashboard.public-dashboard-notice</div>
<tb-social-share-panel style="padding-top: 15px;"
share-title="{{ 'dashboard.socialshare-title' | translate:{dashboardTitle:vm.dashboard.title} }}"
share-text="{{ 'dashboard.socialshare-text' | translate:{dashboardTitle:vm.dashboard.title} }}"
share-link="{{ vm.publicLink }}"
share-hash-tags="thingsboard, iot">
</tb-social-share-panel>
</md-content>
</div>
</md-dialog-content>

View File

@ -185,7 +185,7 @@ export function DeviceController(userService, deviceService, customerService, $s
onAction: function ($event, item) {
unassignFromCustomer($event, item, true);
},
name: function() { return $translate.instant('action.unshare') },
name: function() { return $translate.instant('action.make-private') },
details: function() { return $translate.instant('device.make-private') },
icon: "reply",
isEnabled: function(device) {
@ -271,7 +271,7 @@ export function DeviceController(userService, deviceService, customerService, $s
onAction: function ($event, item) {
unassignFromCustomer($event, item, true);
},
name: function() { return $translate.instant('action.unshare') },
name: function() { return $translate.instant('action.make-private') },
details: function() { return $translate.instant('device.make-private') },
icon: "reply",
isEnabled: function(device) {
@ -364,8 +364,29 @@ export function DeviceController(userService, deviceService, customerService, $s
return device ? device.name : '';
}
function saveDevice (device) {
return deviceService.saveDevice(device);
function saveDevice(device) {
var deferred = $q.defer();
deviceService.saveDevice(device).then(
function success(savedDevice) {
var devices = [ savedDevice ];
customerService.applyAssignedCustomersInfo(devices).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() {

View File

@ -44,7 +44,7 @@ export default angular.module('thingsboard.locale', [])
"assign": "Assign",
"unassign": "Unassign",
"share": "Share",
"unshare": "Unshare",
"make-private": "Make private",
"apply": "Apply",
"apply-changes": "Apply changes",
"edit-mode": "Edit mode",
@ -63,7 +63,8 @@ export default angular.module('thingsboard.locale', [])
"copy": "Copy",
"paste": "Paste",
"import": "Import",
"export": "Export"
"export": "Export",
"share-via": "Share via {{provider}}"
},
"aggregation": {
"aggregation": "Aggregation",
@ -233,6 +234,8 @@ export default angular.module('thingsboard.locale', [])
"make-private-dashboard-title": "Are you sure you want to make the dashboard '{{dashboardTitle}}' private?",
"make-private-dashboard-text": "After the confirmation the dashboard will be made private and won't be accessible by others.",
"make-private-dashboard": "Make dashboard private",
"socialshare-text": "'{{dashboardTitle}}' powered by ThingsBoard",
"socialshare-title": "'{{dashboardTitle}}' powered by ThingsBoard",
"select-dashboard": "Select dashboard",
"no-dashboards-matching": "No dashboards matching '{{dashboard}}' were found.",
"dashboard-required": "Dashboard is required.",
@ -262,6 +265,9 @@ export default angular.module('thingsboard.locale', [])
"max-vertical-margin-message": "Only 50 is allowed as maximum vertical margin value.",
"display-title": "Display dashboard title",
"title-color": "Title color",
"display-device-selection": "Display device selection",
"display-dashboard-timewindow": "Display timewindow",
"display-dashboard-export": "Display export",
"import": "Import dashboard",
"export": "Export dashboard",
"export-failed-error": "Unable to export dashboard: {{error}}",

View File

@ -60,6 +60,7 @@ module.exports = {
allChunks: true,
}),
new webpack.DefinePlugin({
THINGSBOARD_VERSION: JSON.stringify(require('./package.json').version),
'__DEVTOOLS__': false,
'process.env': {
NODE_ENV: JSON.stringify('development'),

View File

@ -58,6 +58,7 @@ module.exports = {
allChunks: true,
}),
new webpack.DefinePlugin({
THINGSBOARD_VERSION: JSON.stringify(require('./package.json').version),
'__DEVTOOLS__': false,
'process.env': {
NODE_ENV: JSON.stringify('production'),