74 changed files with 2280 additions and 269 deletions
@ -0,0 +1,43 @@ |
|||
/** |
|||
* 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. |
|||
*/ |
|||
package org.thingsboard.server.config; |
|||
|
|||
import org.springframework.boot.context.properties.ConfigurationProperties; |
|||
import org.springframework.context.annotation.Configuration; |
|||
import org.thingsboard.server.common.data.EntityType; |
|||
import org.thingsboard.server.common.data.audit.ActionType; |
|||
|
|||
import java.util.HashMap; |
|||
import java.util.Map; |
|||
|
|||
@Configuration |
|||
@ConfigurationProperties(prefix = "audit_log.logging_level") |
|||
public class AuditLogLevelProperties { |
|||
|
|||
private Map<String, String> mask = new HashMap<>(); |
|||
|
|||
public AuditLogLevelProperties() { |
|||
super(); |
|||
} |
|||
|
|||
public void setMask(Map<String, String> mask) { |
|||
this.mask = mask; |
|||
} |
|||
|
|||
public Map<String, String> getMask() { |
|||
return this.mask; |
|||
} |
|||
} |
|||
@ -0,0 +1,46 @@ |
|||
/** |
|||
* 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. |
|||
*/ |
|||
package org.thingsboard.server.dao.audit; |
|||
|
|||
import org.thingsboard.server.common.data.EntityType; |
|||
import org.thingsboard.server.common.data.audit.ActionType; |
|||
|
|||
import java.util.HashMap; |
|||
import java.util.Map; |
|||
|
|||
public class AuditLogLevelFilter { |
|||
|
|||
private Map<EntityType, AuditLogLevelMask> entityTypeMask = new HashMap<>(); |
|||
|
|||
public AuditLogLevelFilter(Map<String, String> mask) { |
|||
entityTypeMask.clear(); |
|||
mask.forEach((entityTypeStr, logLevelMaskStr) -> { |
|||
EntityType entityType = EntityType.valueOf(entityTypeStr.toUpperCase()); |
|||
AuditLogLevelMask logLevelMask = AuditLogLevelMask.valueOf(logLevelMaskStr.toUpperCase()); |
|||
entityTypeMask.put(entityType, logLevelMask); |
|||
}); |
|||
} |
|||
|
|||
public boolean logEnabled(EntityType entityType, ActionType actionType) { |
|||
AuditLogLevelMask logLevelMask = entityTypeMask.get(entityType); |
|||
if (logLevelMask != null) { |
|||
return actionType.isRead() ? logLevelMask.isRead() : logLevelMask.isWrite(); |
|||
} else { |
|||
return false; |
|||
} |
|||
} |
|||
|
|||
} |
|||
@ -0,0 +1,34 @@ |
|||
/** |
|||
* 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. |
|||
*/ |
|||
package org.thingsboard.server.dao.audit; |
|||
|
|||
import lombok.Getter; |
|||
|
|||
@Getter |
|||
public enum AuditLogLevelMask { |
|||
|
|||
OFF(false, false), |
|||
W(true, false), |
|||
RW(true, true); |
|||
|
|||
private final boolean write; |
|||
private final boolean read; |
|||
|
|||
AuditLogLevelMask(boolean write, boolean read) { |
|||
this.write = write; |
|||
this.read = read; |
|||
} |
|||
} |
|||
@ -0,0 +1,116 @@ |
|||
/* |
|||
* 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. |
|||
*/ |
|||
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; |
|||
} |
|||
|
|||
} |
|||
@ -0,0 +1,103 @@ |
|||
/* |
|||
* 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. |
|||
*/ |
|||
import $ from 'jquery'; |
|||
import 'brace/ext/language_tools'; |
|||
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 */ |
|||
@ -0,0 +1,23 @@ |
|||
/** |
|||
* 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. |
|||
*/ |
|||
|
|||
#tb-audit-log-action-data, #tb-audit-log-failure-details { |
|||
min-width: 400px; |
|||
min-height: 50px; |
|||
width: 100%; |
|||
height: 100%; |
|||
border: 1px solid #C0C0C0; |
|||
} |
|||
@ -0,0 +1,49 @@ |
|||
<!-- |
|||
|
|||
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. |
|||
|
|||
--> |
|||
<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> |
|||
@ -0,0 +1,41 @@ |
|||
/* |
|||
* 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 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 |
|||
}; |
|||
} |
|||
@ -0,0 +1,24 @@ |
|||
<!-- |
|||
|
|||
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 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> |
|||
@ -0,0 +1,67 @@ |
|||
/* |
|||
* 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 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, |
|||
skipHide: true, |
|||
onShowing: function(scope, element) { |
|||
onShowingCallback.onShowing(scope, element); |
|||
} |
|||
}); |
|||
} |
|||
|
|||
$compile(element.contents())(scope); |
|||
} |
|||
|
|||
return { |
|||
restrict: "A", |
|||
replace: false, |
|||
link: linker, |
|||
scope: false |
|||
}; |
|||
} |
|||
@ -0,0 +1,36 @@ |
|||
<!-- |
|||
|
|||
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 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> |
|||
@ -0,0 +1,262 @@ |
|||
/* |
|||
* 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. |
|||
*/ |
|||
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: '@?' |
|||
} |
|||
}; |
|||
} |
|||
@ -0,0 +1,68 @@ |
|||
<!-- |
|||
|
|||
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. |
|||
|
|||
--> |
|||
<md-content flex class="md-padding tb-absolute-fill" layout="column"> |
|||
<div flex layout="column" class="tb-audit-logs" ng-class="{'md-whiteframe-z1': pageMode}"> |
|||
<div layout="column" layout-gt-sm="row" layout-align-gt-sm="start center" class="tb-audit-log-toolbar" ng-class="{'md-padding': pageMode, 'tb-audit-log-margin-18px': !pageMode}"> |
|||
<tb-timewindow ng-model="timewindow" history-only as-button="true"></tb-timewindow> |
|||
<div flex layout="row" layout-align="start center"> |
|||
<md-button class="md-icon-button" aria-label="{{ 'action.search' | translate }}"> |
|||
<md-icon aria-label="{{ 'action.search' | translate }}" class="material-icons">search</md-icon> |
|||
<md-tooltip md-direction="top"> |
|||
{{'audit-log.search' | translate}} |
|||
</md-tooltip> |
|||
</md-button> |
|||
<md-input-container flex class="tb-audit-log-search-input"> |
|||
<label> </label> |
|||
<input ng-model="searchText" placeholder="{{'audit-log.search' | translate}}"/> |
|||
</md-input-container> |
|||
<md-button ng-disabled="$root.loading" class="md-icon-button" aria-label="Close" ng-click="searchText = ''"> |
|||
<md-icon aria-label="Close" class="material-icons">close</md-icon> |
|||
<md-tooltip md-direction="top"> |
|||
{{ 'audit-log.clear-search' | translate }} |
|||
</md-tooltip> |
|||
</md-button> |
|||
<md-button ng-disabled="$root.loading" |
|||
class="md-icon-button" ng-click="reload()"> |
|||
<md-icon>refresh</md-icon> |
|||
<md-tooltip md-direction="top"> |
|||
{{ 'action.refresh' | translate }} |
|||
</md-tooltip> |
|||
</md-button> |
|||
</div> |
|||
</div> |
|||
<div flex layout="column" class="tb-audit-log-container" ng-class="{'md-whiteframe-z1': !pageMode}"> |
|||
<md-list flex layout="column" class="tb-audit-log-table" ng-class="{'tb-audit-log-table-full': pageMode}"> |
|||
<md-list class="tb-row tb-header" layout="row" layout-align="start center" tb-audit-log-header audit-log-mode="{{auditLogMode}}"> |
|||
</md-list> |
|||
<md-progress-linear style="max-height: 0px;" md-mode="indeterminate" ng-disabled="!$root.loading" |
|||
ng-show="$root.loading"></md-progress-linear> |
|||
<md-divider></md-divider> |
|||
<span translate layout-align="center center" |
|||
style="margin-top: 25px;" |
|||
class="tb-prompt" ng-show="noData()">audit-log.no-audit-logs-prompt</span> |
|||
<md-virtual-repeat-container ng-show="hasData()" flex md-top-index="topIndex" tb-scope-element="repeatContainer"> |
|||
<md-list-item md-virtual-repeat="auditLog in theAuditLogs" md-on-demand flex ng-style="hasScroll() ? {'margin-right':'-15px'} : {}"> |
|||
<md-list class="tb-row" flex layout="row" layout-align="start center" tb-audit-log-row audit-log-mode="{{auditLogMode}}" audit-log="{{auditLog}}"> |
|||
</md-list> |
|||
<md-divider flex></md-divider> |
|||
</md-list-item> |
|||
</md-virtual-repeat-container> |
|||
</md-list> |
|||
</div> |
|||
</div> |
|||
</md-content> |
|||
@ -0,0 +1,44 @@ |
|||
/* |
|||
* 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 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"}' |
|||
} |
|||
}); |
|||
} |
|||
@ -0,0 +1,91 @@ |
|||
/** |
|||
* 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. |
|||
*/ |
|||
|
|||
.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: 0px; |
|||
} |
|||
} |
|||
} |
|||
|
|||
.tb-audit-log-container { |
|||
overflow-x: auto; |
|||
} |
|||
|
|||
|
|||
|
|||
md-list.tb-audit-log-table { |
|||
padding: 0px; |
|||
min-width: 700px; |
|||
&.tb-audit-log-table-full { |
|||
min-width: 900px; |
|||
} |
|||
|
|||
md-list-item { |
|||
padding: 0px; |
|||
} |
|||
|
|||
.tb-row { |
|||
height: 48px; |
|||
padding: 0px; |
|||
overflow: hidden; |
|||
} |
|||
|
|||
.tb-row:hover { |
|||
background-color: #EEEEEE; |
|||
} |
|||
|
|||
.tb-header:hover { |
|||
background: none; |
|||
} |
|||
|
|||
.tb-header { |
|||
.tb-cell { |
|||
color: rgba(0,0,0,.54); |
|||
font-size: 12px; |
|||
font-weight: 700; |
|||
white-space: nowrap; |
|||
background: none; |
|||
} |
|||
} |
|||
|
|||
.tb-cell { |
|||
padding: 0 24px; |
|||
margin: auto 0; |
|||
color: rgba(0,0,0,.87); |
|||
font-size: 13px; |
|||
vertical-align: middle; |
|||
text-align: left; |
|||
overflow: hidden; |
|||
.md-button { |
|||
padding: 0; |
|||
margin: 0; |
|||
} |
|||
} |
|||
|
|||
.tb-cell.tb-number { |
|||
text-align: right; |
|||
} |
|||
|
|||
} |
|||
@ -0,0 +1,24 @@ |
|||
/* |
|||
* 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. |
|||
*/ |
|||
|
|||
/*@ngInject*/ |
|||
export default function AuditLogsController(types) { |
|||
|
|||
var vm = this; |
|||
|
|||
vm.types = types; |
|||
|
|||
} |
|||
@ -0,0 +1,23 @@ |
|||
<!-- |
|||
|
|||
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. |
|||
|
|||
--> |
|||
|
|||
<tb-audit-log-table class="md-whiteframe-z1" |
|||
flex |
|||
audit-log-mode="{{vm.types.auditLogMode.tenant}}" |
|||
page-mode="true"> |
|||
</tb-audit-log-table> |
|||
@ -0,0 +1,31 @@ |
|||
/* |
|||
* 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. |
|||
*/ |
|||
|
|||
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; |
|||
@ -0,0 +1,128 @@ |
|||
/* |
|||
* 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. |
|||
*/ |
|||
export default angular.module('thingsboard.clipboard', []) |
|||
.factory('clipboardService', ClipboardService) |
|||
.name; |
|||
|
|||
/*@ngInject*/ |
|||
function ClipboardService($q) { |
|||
|
|||
var fakeHandler, fakeHandlerCallback, fakeElem; |
|||
|
|||
var service = { |
|||
copyToClipboard: copyToClipboard |
|||
}; |
|||
|
|||
return service; |
|||
|
|||
/* eslint-disable */ |
|||
function copyToClipboard(trigger, text) { |
|||
var deferred = $q.defer(); |
|||
const isRTL = document.documentElement.getAttribute('dir') == 'rtl'; |
|||
removeFake(); |
|||
fakeHandlerCallback = () => removeFake(); |
|||
fakeHandler = document.body.addEventListener('click', fakeHandlerCallback) || true; |
|||
fakeElem = document.createElement('textarea'); |
|||
fakeElem.style.fontSize = '12pt'; |
|||
fakeElem.style.border = '0'; |
|||
fakeElem.style.padding = '0'; |
|||
fakeElem.style.margin = '0'; |
|||
fakeElem.style.position = 'absolute'; |
|||
fakeElem.style[ isRTL ? 'right' : 'left' ] = '-9999px'; |
|||
let yPosition = window.pageYOffset || document.documentElement.scrollTop; |
|||
fakeElem.style.top = `${yPosition}px`; |
|||
fakeElem.setAttribute('readonly', ''); |
|||
fakeElem.value = text; |
|||
document.body.appendChild(fakeElem); |
|||
var selectedText = select(fakeElem); |
|||
|
|||
let succeeded; |
|||
try { |
|||
succeeded = document.execCommand('copy'); |
|||
} |
|||
catch (err) { |
|||
succeeded = false; |
|||
} |
|||
if (trigger) { |
|||
trigger.focus(); |
|||
} |
|||
window.getSelection().removeAllRanges(); |
|||
removeFake(); |
|||
if (succeeded) { |
|||
deferred.resolve(selectedText); |
|||
} else { |
|||
deferred.reject(); |
|||
} |
|||
return deferred.promise; |
|||
} |
|||
|
|||
function removeFake() { |
|||
if (fakeHandler) { |
|||
document.body.removeEventListener('click', fakeHandlerCallback); |
|||
fakeHandler = null; |
|||
fakeHandlerCallback = null; |
|||
} |
|||
if (fakeElem) { |
|||
document.body.removeChild(fakeElem); |
|||
fakeElem = null; |
|||
} |
|||
} |
|||
|
|||
function select(element) { |
|||
var selectedText; |
|||
|
|||
if (element.nodeName === 'SELECT') { |
|||
element.focus(); |
|||
|
|||
selectedText = element.value; |
|||
} |
|||
else if (element.nodeName === 'INPUT' || element.nodeName === 'TEXTAREA') { |
|||
var isReadOnly = element.hasAttribute('readonly'); |
|||
|
|||
if (!isReadOnly) { |
|||
element.setAttribute('readonly', ''); |
|||
} |
|||
|
|||
element.select(); |
|||
element.setSelectionRange(0, element.value.length); |
|||
|
|||
if (!isReadOnly) { |
|||
element.removeAttribute('readonly'); |
|||
} |
|||
|
|||
selectedText = element.value; |
|||
} |
|||
else { |
|||
if (element.hasAttribute('contenteditable')) { |
|||
element.focus(); |
|||
} |
|||
|
|||
var selection = window.getSelection(); |
|||
var range = document.createRange(); |
|||
|
|||
range.selectNodeContents(element); |
|||
selection.removeAllRanges(); |
|||
selection.addRange(range); |
|||
|
|||
selectedText = selection.toString(); |
|||
} |
|||
|
|||
return selectedText; |
|||
} |
|||
|
|||
/* eslint-enable */ |
|||
|
|||
} |
|||
Loading…
Reference in new issue