Merge branch 'master' of https://github.com/thingsboard/thingsboard
This commit is contained in:
commit
0f24e00fea
File diff suppressed because one or more lines are too long
@ -93,7 +93,6 @@ public class ThingsboardInstallService {
|
|||||||
} else if ("3.0.1-cassandra".equals(upgradeFromVersion)) {
|
} else if ("3.0.1-cassandra".equals(upgradeFromVersion)) {
|
||||||
log.info("Migrating ThingsBoard latest timeseries data from cassandra to SQL database ...");
|
log.info("Migrating ThingsBoard latest timeseries data from cassandra to SQL database ...");
|
||||||
latestMigrateService.migrate();
|
latestMigrateService.migrate();
|
||||||
log.info("Updating system data...");
|
|
||||||
} else {
|
} else {
|
||||||
switch (upgradeFromVersion) {
|
switch (upgradeFromVersion) {
|
||||||
case "1.2.3": //NOSONAR, Need to execute gradual upgrade starting from upgradeFromVersion
|
case "1.2.3": //NOSONAR, Need to execute gradual upgrade starting from upgradeFromVersion
|
||||||
@ -182,13 +181,12 @@ public class ThingsboardInstallService {
|
|||||||
}
|
}
|
||||||
databaseEntitiesUpgradeService.upgradeDatabase("3.1.1");
|
databaseEntitiesUpgradeService.upgradeDatabase("3.1.1");
|
||||||
dataUpdateService.updateData("3.1.1");
|
dataUpdateService.updateData("3.1.1");
|
||||||
log.info("Updating system data...");
|
|
||||||
systemDataLoaderService.updateSystemWidgets();
|
|
||||||
systemDataLoaderService.createOAuth2Templates();
|
systemDataLoaderService.createOAuth2Templates();
|
||||||
break;
|
|
||||||
case "3.2.0":
|
case "3.2.0":
|
||||||
log.info("Upgrading ThingsBoard from version 3.2.0 to 3.2.1 ...");
|
log.info("Upgrading ThingsBoard from version 3.2.0 to 3.2.1 ...");
|
||||||
databaseEntitiesUpgradeService.upgradeDatabase("3.2.0");
|
databaseEntitiesUpgradeService.upgradeDatabase("3.2.0");
|
||||||
|
log.info("Updating system data...");
|
||||||
|
systemDataLoaderService.updateSystemWidgets();
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
throw new RuntimeException("Unable to upgrade ThingsBoard, unsupported fromVersion: " + upgradeFromVersion);
|
throw new RuntimeException("Unable to upgrade ThingsBoard, unsupported fromVersion: " + upgradeFromVersion);
|
||||||
|
|||||||
@ -17,7 +17,6 @@ package org.thingsboard.server.dao.audit;
|
|||||||
|
|
||||||
import com.datastax.oss.driver.api.core.uuid.Uuids;
|
import com.datastax.oss.driver.api.core.uuid.Uuids;
|
||||||
import com.fasterxml.jackson.databind.JsonNode;
|
import com.fasterxml.jackson.databind.JsonNode;
|
||||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
|
||||||
import com.fasterxml.jackson.databind.node.ArrayNode;
|
import com.fasterxml.jackson.databind.node.ArrayNode;
|
||||||
import com.fasterxml.jackson.databind.node.ObjectNode;
|
import com.fasterxml.jackson.databind.node.ObjectNode;
|
||||||
import com.google.common.collect.Lists;
|
import com.google.common.collect.Lists;
|
||||||
@ -50,6 +49,7 @@ import org.thingsboard.server.dao.device.provision.ProvisionRequest;
|
|||||||
import org.thingsboard.server.dao.entity.EntityService;
|
import org.thingsboard.server.dao.entity.EntityService;
|
||||||
import org.thingsboard.server.dao.exception.DataValidationException;
|
import org.thingsboard.server.dao.exception.DataValidationException;
|
||||||
import org.thingsboard.server.dao.service.DataValidator;
|
import org.thingsboard.server.dao.service.DataValidator;
|
||||||
|
import org.thingsboard.server.dao.util.mapping.JacksonUtil;
|
||||||
|
|
||||||
import java.io.PrintWriter;
|
import java.io.PrintWriter;
|
||||||
import java.io.StringWriter;
|
import java.io.StringWriter;
|
||||||
@ -65,8 +65,6 @@ import static org.thingsboard.server.dao.service.Validator.validateId;
|
|||||||
@ConditionalOnProperty(prefix = "audit-log", value = "enabled", havingValue = "true")
|
@ConditionalOnProperty(prefix = "audit-log", value = "enabled", havingValue = "true")
|
||||||
public class AuditLogServiceImpl implements AuditLogService {
|
public class AuditLogServiceImpl implements AuditLogService {
|
||||||
|
|
||||||
private static final ObjectMapper objectMapper = new ObjectMapper();
|
|
||||||
|
|
||||||
private static final String INCORRECT_TENANT_ID = "Incorrect tenantId ";
|
private static final String INCORRECT_TENANT_ID = "Incorrect tenantId ";
|
||||||
private static final int INSERTS_PER_ENTRY = 3;
|
private static final int INSERTS_PER_ENTRY = 3;
|
||||||
|
|
||||||
@ -159,7 +157,7 @@ public class AuditLogServiceImpl implements AuditLogService {
|
|||||||
private <E extends HasName, I extends EntityId> JsonNode constructActionData(I entityId, E entity,
|
private <E extends HasName, I extends EntityId> JsonNode constructActionData(I entityId, E entity,
|
||||||
ActionType actionType,
|
ActionType actionType,
|
||||||
Object... additionalInfo) {
|
Object... additionalInfo) {
|
||||||
ObjectNode actionData = objectMapper.createObjectNode();
|
ObjectNode actionData = JacksonUtil.newObjectNode();
|
||||||
switch (actionType) {
|
switch (actionType) {
|
||||||
case ADDED:
|
case ADDED:
|
||||||
case UPDATED:
|
case UPDATED:
|
||||||
@ -168,7 +166,7 @@ public class AuditLogServiceImpl implements AuditLogService {
|
|||||||
case RELATIONS_DELETED:
|
case RELATIONS_DELETED:
|
||||||
case ASSIGNED_TO_TENANT:
|
case ASSIGNED_TO_TENANT:
|
||||||
if (entity != null) {
|
if (entity != null) {
|
||||||
ObjectNode entityNode = objectMapper.valueToTree(entity);
|
ObjectNode entityNode = (ObjectNode) JacksonUtil.valueToTree(entity);
|
||||||
if (entityId.getEntityType() == EntityType.DASHBOARD) {
|
if (entityId.getEntityType() == EntityType.DASHBOARD) {
|
||||||
entityNode.put("configuration", "");
|
entityNode.put("configuration", "");
|
||||||
}
|
}
|
||||||
@ -177,7 +175,7 @@ public class AuditLogServiceImpl implements AuditLogService {
|
|||||||
if (entityId.getEntityType() == EntityType.RULE_CHAIN) {
|
if (entityId.getEntityType() == EntityType.RULE_CHAIN) {
|
||||||
RuleChainMetaData ruleChainMetaData = extractParameter(RuleChainMetaData.class, additionalInfo);
|
RuleChainMetaData ruleChainMetaData = extractParameter(RuleChainMetaData.class, additionalInfo);
|
||||||
if (ruleChainMetaData != null) {
|
if (ruleChainMetaData != null) {
|
||||||
ObjectNode ruleChainMetaDataNode = objectMapper.valueToTree(ruleChainMetaData);
|
ObjectNode ruleChainMetaDataNode = (ObjectNode) JacksonUtil.valueToTree(ruleChainMetaData);
|
||||||
actionData.set("metadata", ruleChainMetaDataNode);
|
actionData.set("metadata", ruleChainMetaDataNode);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -194,7 +192,7 @@ public class AuditLogServiceImpl implements AuditLogService {
|
|||||||
String scope = extractParameter(String.class, 0, additionalInfo);
|
String scope = extractParameter(String.class, 0, additionalInfo);
|
||||||
List<AttributeKvEntry> attributes = extractParameter(List.class, 1, additionalInfo);
|
List<AttributeKvEntry> attributes = extractParameter(List.class, 1, additionalInfo);
|
||||||
actionData.put("scope", scope);
|
actionData.put("scope", scope);
|
||||||
ObjectNode attrsNode = objectMapper.createObjectNode();
|
ObjectNode attrsNode = JacksonUtil.newObjectNode();
|
||||||
if (attributes != null) {
|
if (attributes != null) {
|
||||||
for (AttributeKvEntry attr : attributes) {
|
for (AttributeKvEntry attr : attributes) {
|
||||||
attrsNode.put(attr.getKey(), attr.getValueAsString());
|
attrsNode.put(attr.getKey(), attr.getValueAsString());
|
||||||
@ -225,7 +223,7 @@ public class AuditLogServiceImpl implements AuditLogService {
|
|||||||
case CREDENTIALS_UPDATED:
|
case CREDENTIALS_UPDATED:
|
||||||
actionData.put("entityId", entityId.toString());
|
actionData.put("entityId", entityId.toString());
|
||||||
DeviceCredentials deviceCredentials = extractParameter(DeviceCredentials.class, additionalInfo);
|
DeviceCredentials deviceCredentials = extractParameter(DeviceCredentials.class, additionalInfo);
|
||||||
actionData.set("credentials", objectMapper.valueToTree(deviceCredentials));
|
actionData.set("credentials", JacksonUtil.valueToTree(deviceCredentials));
|
||||||
break;
|
break;
|
||||||
case ASSIGNED_TO_CUSTOMER:
|
case ASSIGNED_TO_CUSTOMER:
|
||||||
strEntityId = extractParameter(String.class, 0, additionalInfo);
|
strEntityId = extractParameter(String.class, 0, additionalInfo);
|
||||||
@ -246,7 +244,7 @@ public class AuditLogServiceImpl implements AuditLogService {
|
|||||||
case RELATION_ADD_OR_UPDATE:
|
case RELATION_ADD_OR_UPDATE:
|
||||||
case RELATION_DELETED:
|
case RELATION_DELETED:
|
||||||
EntityRelation relation = extractParameter(EntityRelation.class, 0, additionalInfo);
|
EntityRelation relation = extractParameter(EntityRelation.class, 0, additionalInfo);
|
||||||
actionData.set("relation", objectMapper.valueToTree(relation));
|
actionData.set("relation", JacksonUtil.valueToTree(relation));
|
||||||
break;
|
break;
|
||||||
case LOGIN:
|
case LOGIN:
|
||||||
case LOGOUT:
|
case LOGOUT:
|
||||||
@ -264,7 +262,7 @@ public class AuditLogServiceImpl implements AuditLogService {
|
|||||||
case PROVISION_FAILURE:
|
case PROVISION_FAILURE:
|
||||||
ProvisionRequest request = extractParameter(ProvisionRequest.class, additionalInfo);
|
ProvisionRequest request = extractParameter(ProvisionRequest.class, additionalInfo);
|
||||||
if (request != null) {
|
if (request != null) {
|
||||||
actionData.set("provisionRequest", objectMapper.valueToTree(request));
|
actionData.set("provisionRequest", JacksonUtil.valueToTree(request));
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case TIMESERIES_UPDATED:
|
case TIMESERIES_UPDATED:
|
||||||
@ -275,7 +273,7 @@ public class AuditLogServiceImpl implements AuditLogService {
|
|||||||
updatedTimeseries.stream()
|
updatedTimeseries.stream()
|
||||||
.collect(Collectors.groupingBy(TsKvEntry::getTs))
|
.collect(Collectors.groupingBy(TsKvEntry::getTs))
|
||||||
.forEach((k, v) -> {
|
.forEach((k, v) -> {
|
||||||
ObjectNode element = objectMapper.createObjectNode();
|
ObjectNode element = JacksonUtil.newObjectNode();
|
||||||
element.put("ts", k);
|
element.put("ts", k);
|
||||||
ObjectNode values = element.putObject("values");
|
ObjectNode values = element.putObject("values");
|
||||||
v.forEach(kvEntry -> values.put(kvEntry.getKey(), kvEntry.getValueAsString()));
|
v.forEach(kvEntry -> values.put(kvEntry.getKey(), kvEntry.getValueAsString()));
|
||||||
|
|||||||
@ -66,7 +66,7 @@ public class JacksonUtil {
|
|||||||
throw new IllegalArgumentException(e);
|
throw new IllegalArgumentException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ObjectNode newObjectNode(){
|
public static ObjectNode newObjectNode(){
|
||||||
return OBJECT_MAPPER.createObjectNode();
|
return OBJECT_MAPPER.createObjectNode();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -58,7 +58,7 @@ frontend http-in
|
|||||||
|
|
||||||
acl transport_http_acl path_beg /api/v1/
|
acl transport_http_acl path_beg /api/v1/
|
||||||
acl letsencrypt_http_acl path_beg /.well-known/acme-challenge/
|
acl letsencrypt_http_acl path_beg /.well-known/acme-challenge/
|
||||||
acl tb_api_acl path_beg /api/ /swagger /webjars /v2/ /static/rulenode/ /oauth2/ /login/oauth2/
|
acl tb_api_acl path_beg /api/ /swagger /webjars /v2/ /static/rulenode/ /oauth2/ /login/oauth2/ /static/widgets/
|
||||||
|
|
||||||
redirect scheme https if !letsencrypt_http_acl !transport_http_acl { env(FORCE_HTTPS_REDIRECT) -m str true }
|
redirect scheme https if !letsencrypt_http_acl !transport_http_acl { env(FORCE_HTTPS_REDIRECT) -m str true }
|
||||||
|
|
||||||
@ -76,7 +76,7 @@ frontend https_in
|
|||||||
reqadd X-Forwarded-Proto:\ https
|
reqadd X-Forwarded-Proto:\ https
|
||||||
|
|
||||||
acl transport_http_acl path_beg /api/v1/
|
acl transport_http_acl path_beg /api/v1/
|
||||||
acl tb_api_acl path_beg /api/ /swagger /webjars /v2/ /static/rulenode/ /oauth2/ /login/oauth2/
|
acl tb_api_acl path_beg /api/ /swagger /webjars /v2/ /static/rulenode/ /oauth2/ /login/oauth2/ /static/widgets/
|
||||||
|
|
||||||
use_backend tb-http-backend if transport_http_acl
|
use_backend tb-http-backend if transport_http_acl
|
||||||
use_backend tb-api-backend if tb_api_acl
|
use_backend tb-api-backend if tb_api_acl
|
||||||
|
|||||||
@ -64,6 +64,7 @@ class ProfileState {
|
|||||||
alarmSettings.clear();
|
alarmSettings.clear();
|
||||||
alarmCreateKeys.clear();
|
alarmCreateKeys.clear();
|
||||||
alarmClearKeys.clear();
|
alarmClearKeys.clear();
|
||||||
|
entityKeys.clear();
|
||||||
if (deviceProfile.getProfileData().getAlarms() != null) {
|
if (deviceProfile.getProfileData().getAlarms() != null) {
|
||||||
alarmSettings.addAll(deviceProfile.getProfileData().getAlarms());
|
alarmSettings.addAll(deviceProfile.getProfileData().getAlarms());
|
||||||
for (DeviceProfileAlarm alarm : deviceProfile.getProfileData().getAlarms()) {
|
for (DeviceProfileAlarm alarm : deviceProfile.getProfileData().getAlarms()) {
|
||||||
|
|||||||
@ -16,7 +16,7 @@
|
|||||||
#
|
#
|
||||||
|
|
||||||
usage() {
|
usage() {
|
||||||
echo "This script generates client public/private rey pair, extracts them to a no-password RSA pem file,"
|
echo "This script generates client public/private key pair, extracts them to a no-password pem file,"
|
||||||
echo "and imports server public key to client keystore"
|
echo "and imports server public key to client keystore"
|
||||||
echo "usage: ./client.keygen.sh [-p file]"
|
echo "usage: ./client.keygen.sh [-p file]"
|
||||||
echo " -p | --props | --properties file Properties file. default value is ./keygen.properties"
|
echo " -p | --props | --properties file Properties file. default value is ./keygen.properties"
|
||||||
@ -70,6 +70,20 @@ while :
|
|||||||
done
|
done
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
OPENSSL_CMD=""
|
||||||
|
case $CLIENT_KEY_ALG in
|
||||||
|
RSA)
|
||||||
|
OPENSSL_CMD="rsa"
|
||||||
|
;;
|
||||||
|
EC)
|
||||||
|
OPENSSL_CMD="ec"
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
if [ -z "$OPENSSL_CMD" ]; then
|
||||||
|
echo "Unexpected CLIENT_KEY_ALG. Exiting."
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
echo "Generating SSL Key Pair..."
|
echo "Generating SSL Key Pair..."
|
||||||
|
|
||||||
keytool -genkeypair -v \
|
keytool -genkeypair -v \
|
||||||
@ -77,8 +91,8 @@ keytool -genkeypair -v \
|
|||||||
-keystore $CLIENT_FILE_PREFIX.jks \
|
-keystore $CLIENT_FILE_PREFIX.jks \
|
||||||
-keypass $CLIENT_KEY_PASSWORD \
|
-keypass $CLIENT_KEY_PASSWORD \
|
||||||
-storepass $CLIENT_KEYSTORE_PASSWORD \
|
-storepass $CLIENT_KEYSTORE_PASSWORD \
|
||||||
-keyalg RSA \
|
-keyalg $CLIENT_KEY_ALG \
|
||||||
-keysize 2048 \
|
-keysize $CLIENT_KEY_SIZE\
|
||||||
-validity 9999 \
|
-validity 9999 \
|
||||||
-dname "CN=$DOMAIN_SUFFIX, OU=$ORGANIZATIONAL_UNIT, O=$ORGANIZATION, L=$CITY, ST=$STATE_OR_PROVINCE, C=$TWO_LETTER_COUNTRY_CODE"
|
-dname "CN=$DOMAIN_SUFFIX, OU=$ORGANIZATIONAL_UNIT, O=$ORGANIZATION, L=$CITY, ST=$STATE_OR_PROVINCE, C=$TWO_LETTER_COUNTRY_CODE"
|
||||||
|
|
||||||
@ -110,7 +124,7 @@ keytool --importcert \
|
|||||||
-noprompt
|
-noprompt
|
||||||
|
|
||||||
echo "Exporting no-password pem certificate"
|
echo "Exporting no-password pem certificate"
|
||||||
openssl rsa -in $CLIENT_FILE_PREFIX.pem -out $CLIENT_FILE_PREFIX.nopass.pem -passin pass:$CLIENT_KEY_PASSWORD
|
openssl $OPENSSL_CMD -in $CLIENT_FILE_PREFIX.pem -out $CLIENT_FILE_PREFIX.nopass.pem -passin pass:$CLIENT_KEY_PASSWORD
|
||||||
tail -n +$(($(grep -m1 -n -e '-----BEGIN CERTIFICATE' $CLIENT_FILE_PREFIX.pem | cut -d: -f1) )) \
|
tail -n +$(($(grep -m1 -n -e '-----BEGIN CERTIFICATE' $CLIENT_FILE_PREFIX.pem | cut -d: -f1) )) \
|
||||||
$CLIENT_FILE_PREFIX.pem >> $CLIENT_FILE_PREFIX.nopass.pem
|
$CLIENT_FILE_PREFIX.pem >> $CLIENT_FILE_PREFIX.nopass.pem
|
||||||
|
|
||||||
|
|||||||
@ -26,6 +26,8 @@ SERVER_KEY_PASSWORD=server_key_password
|
|||||||
|
|
||||||
SERVER_KEY_ALIAS="serveralias"
|
SERVER_KEY_ALIAS="serveralias"
|
||||||
SERVER_FILE_PREFIX="mqttserver"
|
SERVER_FILE_PREFIX="mqttserver"
|
||||||
|
SERVER_KEY_ALG="RSA"
|
||||||
|
SERVER_KEY_SIZE="2048"
|
||||||
SERVER_KEYSTORE_DIR="/etc/thingsboard/conf"
|
SERVER_KEYSTORE_DIR="/etc/thingsboard/conf"
|
||||||
|
|
||||||
CLIENT_KEYSTORE_PASSWORD=password
|
CLIENT_KEYSTORE_PASSWORD=password
|
||||||
@ -33,4 +35,5 @@ CLIENT_KEY_PASSWORD=password
|
|||||||
|
|
||||||
CLIENT_KEY_ALIAS="clientalias"
|
CLIENT_KEY_ALIAS="clientalias"
|
||||||
CLIENT_FILE_PREFIX="mqttclient"
|
CLIENT_FILE_PREFIX="mqttclient"
|
||||||
|
CLIENT_KEY_ALG="RSA"
|
||||||
|
CLIENT_KEY_SIZE="2048"
|
||||||
|
|||||||
@ -92,8 +92,8 @@ keytool -genkeypair -v \
|
|||||||
-keystore $SERVER_FILE_PREFIX.jks \
|
-keystore $SERVER_FILE_PREFIX.jks \
|
||||||
-keypass $SERVER_KEY_PASSWORD \
|
-keypass $SERVER_KEY_PASSWORD \
|
||||||
-storepass $SERVER_KEYSTORE_PASSWORD \
|
-storepass $SERVER_KEYSTORE_PASSWORD \
|
||||||
-keyalg RSA \
|
-keyalg $SERVER_KEY_ALG \
|
||||||
-keysize 2048 \
|
-keysize $SERVER_KEY_SIZE \
|
||||||
-validity 9999
|
-validity 9999
|
||||||
|
|
||||||
status=$?
|
status=$?
|
||||||
|
|||||||
@ -26,6 +26,10 @@ const PROXY_CONFIG = {
|
|||||||
"target": ruleNodeUiforwardUrl,
|
"target": ruleNodeUiforwardUrl,
|
||||||
"secure": false,
|
"secure": false,
|
||||||
},
|
},
|
||||||
|
"/static/widgets": {
|
||||||
|
"target": forwardUrl,
|
||||||
|
"secure": false,
|
||||||
|
},
|
||||||
"/oauth2": {
|
"/oauth2": {
|
||||||
"target": forwardUrl,
|
"target": forwardUrl,
|
||||||
"secure": false,
|
"secure": false,
|
||||||
@ -34,10 +38,6 @@ const PROXY_CONFIG = {
|
|||||||
"target": forwardUrl,
|
"target": forwardUrl,
|
||||||
"secure": false,
|
"secure": false,
|
||||||
},
|
},
|
||||||
"/static": {
|
|
||||||
"target": forwardUrl,
|
|
||||||
"secure": false,
|
|
||||||
},
|
|
||||||
"/api/ws": {
|
"/api/ws": {
|
||||||
"target": wsForwardUrl,
|
"target": wsForwardUrl,
|
||||||
"ws": true,
|
"ws": true,
|
||||||
|
|||||||
@ -112,6 +112,12 @@
|
|||||||
{{ 'widget-action.open-right-layout' | translate }}
|
{{ 'widget-action.open-right-layout' | translate }}
|
||||||
</mat-checkbox>
|
</mat-checkbox>
|
||||||
</ng-template>
|
</ng-template>
|
||||||
|
<ng-template [ngSwitchCase]="widgetActionFormGroup.get('type').value === widgetActionType.openDashboard ?
|
||||||
|
widgetActionFormGroup.get('type').value : ''">
|
||||||
|
<mat-checkbox formControlName="openNewBrowserTab">
|
||||||
|
{{ 'widget-action.open-new-browser-tab' | translate }}
|
||||||
|
</mat-checkbox>
|
||||||
|
</ng-template>
|
||||||
<ng-template [ngSwitchCase]="widgetActionFormGroup.get('type').value === widgetActionType.openDashboardState ||
|
<ng-template [ngSwitchCase]="widgetActionFormGroup.get('type').value === widgetActionType.openDashboardState ||
|
||||||
widgetActionFormGroup.get('type').value === widgetActionType.updateDashboardState ||
|
widgetActionFormGroup.get('type').value === widgetActionType.updateDashboardState ||
|
||||||
widgetActionFormGroup.get('type').value === widgetActionType.openDashboard ?
|
widgetActionFormGroup.get('type').value === widgetActionType.openDashboard ?
|
||||||
|
|||||||
@ -144,6 +144,10 @@ export class WidgetActionDialogComponent extends DialogComponent<WidgetActionDia
|
|||||||
this.fb.control(action ? action.stateEntityParamName : null, [])
|
this.fb.control(action ? action.stateEntityParamName : null, [])
|
||||||
);
|
);
|
||||||
if (type === WidgetActionType.openDashboard) {
|
if (type === WidgetActionType.openDashboard) {
|
||||||
|
this.actionTypeFormGroup.addControl(
|
||||||
|
'openNewBrowserTab',
|
||||||
|
this.fb.control(action ? action.openNewBrowserTab : false, [])
|
||||||
|
);
|
||||||
this.actionTypeFormGroup.addControl(
|
this.actionTypeFormGroup.addControl(
|
||||||
'targetDashboardId',
|
'targetDashboardId',
|
||||||
this.fb.control(action ? action.targetDashboardId : null,
|
this.fb.control(action ? action.targetDashboardId : null,
|
||||||
|
|||||||
@ -30,6 +30,7 @@ import { Datasource, DatasourceData } from '@shared/models/widget.models';
|
|||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
import { mapProviderSchema, providerSets } from '@home/components/widget/lib/maps/schemes';
|
import { mapProviderSchema, providerSets } from '@home/components/widget/lib/maps/schemes';
|
||||||
import { addCondition, mergeSchemes } from '@core/schema-utils';
|
import { addCondition, mergeSchemes } from '@core/schema-utils';
|
||||||
|
import L, {Projection} from "leaflet";
|
||||||
|
|
||||||
export function getProviderSchema(mapProvider: MapProviders, ignoreImageMap = false) {
|
export function getProviderSchema(mapProvider: MapProviders, ignoreImageMap = false) {
|
||||||
const providerSchema = _.cloneDeep(mapProviderSchema);
|
const providerSchema = _.cloneDeep(mapProviderSchema);
|
||||||
@ -443,3 +444,21 @@ export function createLoadingDiv(loadingText: string): JQuery<HTMLElement> {
|
|||||||
</div>
|
</div>
|
||||||
`);
|
`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function checkLngLat(point: L.LatLng, southWest: L.LatLng, northEast: L.LatLng, offset = 0): L.LatLng {
|
||||||
|
const maxLngMap = northEast.lng - offset;
|
||||||
|
const minLngMap = southWest.lng + offset;
|
||||||
|
const maxLatMap = northEast.lat - offset;
|
||||||
|
const minLatMap = southWest.lat + offset;
|
||||||
|
if (point.lng > maxLngMap) {
|
||||||
|
point.lng = maxLngMap;
|
||||||
|
} else if (point.lng < minLngMap) {
|
||||||
|
point.lng = minLngMap;
|
||||||
|
}
|
||||||
|
if (point.lat > maxLatMap) {
|
||||||
|
point.lat = maxLatMap;
|
||||||
|
} else if (point.lat < minLatMap) {
|
||||||
|
point.lat = minLatMap;
|
||||||
|
}
|
||||||
|
return point;
|
||||||
|
}
|
||||||
|
|||||||
@ -16,12 +16,12 @@
|
|||||||
|
|
||||||
import L, {
|
import L, {
|
||||||
FeatureGroup,
|
FeatureGroup,
|
||||||
Icon,
|
Icon, LatLng,
|
||||||
LatLngBounds,
|
LatLngBounds,
|
||||||
LatLngTuple,
|
LatLngTuple,
|
||||||
markerClusterGroup,
|
markerClusterGroup,
|
||||||
MarkerClusterGroup,
|
MarkerClusterGroup,
|
||||||
MarkerClusterGroupOptions
|
MarkerClusterGroupOptions, Projection
|
||||||
} from 'leaflet';
|
} from 'leaflet';
|
||||||
import tinycolor from 'tinycolor2';
|
import tinycolor from 'tinycolor2';
|
||||||
import 'leaflet-providers';
|
import 'leaflet-providers';
|
||||||
@ -46,6 +46,7 @@ import {
|
|||||||
createTooltip,
|
createTooltip,
|
||||||
} from '@home/components/widget/lib/maps/maps-utils';
|
} from '@home/components/widget/lib/maps/maps-utils';
|
||||||
import {
|
import {
|
||||||
|
checkLngLat,
|
||||||
createLoadingDiv,
|
createLoadingDiv,
|
||||||
parseArray,
|
parseArray,
|
||||||
parseData,
|
parseData,
|
||||||
@ -79,6 +80,8 @@ export default abstract class LeafletMap {
|
|||||||
updatePending = false;
|
updatePending = false;
|
||||||
addMarkers: L.Marker[] = [];
|
addMarkers: L.Marker[] = [];
|
||||||
addPolygons: L.Polygon[] = [];
|
addPolygons: L.Polygon[] = [];
|
||||||
|
southWest = new L.LatLng(-Projection.SphericalMercator['MAX_LATITUDE'], -180);
|
||||||
|
northEast = new L.LatLng(Projection.SphericalMercator['MAX_LATITUDE'], 180);
|
||||||
|
|
||||||
protected constructor(public ctx: WidgetContext,
|
protected constructor(public ctx: WidgetContext,
|
||||||
public $container: HTMLElement,
|
public $container: HTMLElement,
|
||||||
@ -206,21 +209,30 @@ export default abstract class LeafletMap {
|
|||||||
|
|
||||||
addPolygonControl() {
|
addPolygonControl() {
|
||||||
if (this.options.showPolygon && this.options.editablePolygon) {
|
if (this.options.showPolygon && this.options.editablePolygon) {
|
||||||
let mousePositionOnMap: L.LatLng[];
|
let polygonPoints: L.LatLng[];
|
||||||
let addPolygon: L.Control;
|
let addPolygon: L.Control;
|
||||||
|
let mousePositionOnMap: LatLng;
|
||||||
this.map.on('mousemove', (e: L.LeafletMouseEvent) => {
|
this.map.on('mousemove', (e: L.LeafletMouseEvent) => {
|
||||||
const polygonOffset = this.options.provider === MapProviders.image ? 10 : 0.01;
|
mousePositionOnMap = e.latlng;
|
||||||
const latlng1 = e.latlng;
|
|
||||||
const latlng2 = L.latLng(e.latlng.lat, e.latlng.lng + polygonOffset);
|
|
||||||
const latlng3 = L.latLng(e.latlng.lat - polygonOffset, e.latlng.lng);
|
|
||||||
mousePositionOnMap = [latlng1, latlng2, latlng3];
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const dragListener = (e: L.DragEndEvent) => {
|
const dragListener = (e: L.DragEndEvent) => {
|
||||||
if (e.type === 'dragend' && mousePositionOnMap) {
|
if (e.type === 'dragend') {
|
||||||
const newPolygon = L.polygon(mousePositionOnMap).addTo(this.map);
|
const polygonOffset = this.options.provider === MapProviders.image ? 10 : 0.01;
|
||||||
|
|
||||||
|
let convert = this.convertToCustomFormat(mousePositionOnMap,polygonOffset);
|
||||||
|
mousePositionOnMap.lat = convert[this.options.latKeyName];
|
||||||
|
mousePositionOnMap.lng = convert[this.options.lngKeyName];
|
||||||
|
|
||||||
|
const latlng1 = mousePositionOnMap;
|
||||||
|
const latlng2 = L.latLng(mousePositionOnMap.lat, mousePositionOnMap.lng + polygonOffset);
|
||||||
|
const latlng3 = L.latLng(mousePositionOnMap.lat - polygonOffset, mousePositionOnMap.lng);
|
||||||
|
polygonPoints = [latlng1, latlng2, latlng3];
|
||||||
|
|
||||||
|
const newPolygon = L.polygon(polygonPoints).addTo(this.map);
|
||||||
this.addPolygons.push(newPolygon);
|
this.addPolygons.push(newPolygon);
|
||||||
const datasourcesList = document.createElement('div');
|
const datasourcesList = document.createElement('div');
|
||||||
const customLatLng = {[this.options.polygonKeyName]: this.convertToPolygonFormat(mousePositionOnMap)};
|
const customLatLng = {[this.options.polygonKeyName]: this.convertToPolygonFormat(polygonPoints)};
|
||||||
const header = document.createElement('p');
|
const header = document.createElement('p');
|
||||||
header.appendChild(document.createTextNode('Select entity:'));
|
header.appendChild(document.createTextNode('Select entity:'));
|
||||||
header.setAttribute('style', 'font-size: 14px; margin: 8px 0');
|
header.setAttribute('style', 'font-size: 14px; margin: 8px 0');
|
||||||
@ -414,12 +426,9 @@ export default abstract class LeafletMap {
|
|||||||
}).filter(el => !!el);
|
}).filter(el => !!el);
|
||||||
}
|
}
|
||||||
|
|
||||||
convertToCustomFormat(position: L.LatLng): object {
|
convertToCustomFormat(position: L.LatLng, offset = 0): object {
|
||||||
if (position.lng > 180) {
|
position = checkLngLat(position, this.southWest, this.northEast, offset);
|
||||||
position.lng = 180;
|
|
||||||
} else if (position.lng < -180) {
|
|
||||||
position.lng = -180;
|
|
||||||
}
|
|
||||||
return {
|
return {
|
||||||
[this.options.latKeyName]: position.lat,
|
[this.options.latKeyName]: position.lat,
|
||||||
[this.options.lngKeyName]: position.lng
|
[this.options.lngKeyName]: position.lng
|
||||||
@ -728,6 +737,11 @@ export default abstract class LeafletMap {
|
|||||||
if (e === undefined || (e.type !== 'editable:vertex:dragend' && e.type !== 'editable:vertex:deleted')) {
|
if (e === undefined || (e.type !== 'editable:vertex:dragend' && e.type !== 'editable:vertex:deleted')) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if(this.options.provider !== MapProviders.image) {
|
||||||
|
for (let key in e.layer._latlngs[0]) {
|
||||||
|
e.layer._latlngs[0][key] = checkLngLat(e.layer._latlngs[0][key], this.southWest, this.northEast);
|
||||||
|
}
|
||||||
|
}
|
||||||
this.savePolygonLocation({ ...data, ...this.convertPolygonToCustomFormat(e.layer._latlngs) }).subscribe();
|
this.savePolygonLocation({ ...data, ...this.convertPolygonToCustomFormat(e.layer._latlngs) }).subscribe();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -19,7 +19,12 @@ import LeafletMap from '../leaflet-map';
|
|||||||
import { MapImage, PosFuncton, UnitedMapSettings } from '../map-models';
|
import { MapImage, PosFuncton, UnitedMapSettings } from '../map-models';
|
||||||
import { Observable, ReplaySubject } from 'rxjs';
|
import { Observable, ReplaySubject } from 'rxjs';
|
||||||
import { filter, map, mergeMap } from 'rxjs/operators';
|
import { filter, map, mergeMap } from 'rxjs/operators';
|
||||||
import { aspectCache, calculateNewPointCoordinate, parseFunction } from '@home/components/widget/lib/maps/common-maps-utils';
|
import {
|
||||||
|
aspectCache,
|
||||||
|
calculateNewPointCoordinate,
|
||||||
|
checkLngLat,
|
||||||
|
parseFunction
|
||||||
|
} from '@home/components/widget/lib/maps/common-maps-utils';
|
||||||
import { WidgetContext } from '@home/models/widget-component.models';
|
import { WidgetContext } from '@home/models/widget-component.models';
|
||||||
import { DataSet, DatasourceType, widgetType } from '@shared/models/widget.models';
|
import { DataSet, DatasourceType, widgetType } from '@shared/models/widget.models';
|
||||||
import { DataKeyType } from '@shared/models/telemetry/telemetry.models';
|
import { DataKeyType } from '@shared/models/telemetry/telemetry.models';
|
||||||
@ -132,9 +137,9 @@ export class ImageMap extends LeafletMap {
|
|||||||
updateBounds(updateImage?: boolean, lastCenterPos?) {
|
updateBounds(updateImage?: boolean, lastCenterPos?) {
|
||||||
const w = this.width;
|
const w = this.width;
|
||||||
const h = this.height;
|
const h = this.height;
|
||||||
let southWest = this.pointToLatLng(0, h);
|
this.southWest = this.pointToLatLng(0, h);
|
||||||
let northEast = this.pointToLatLng(w, 0);
|
this.northEast = this.pointToLatLng(w, 0);
|
||||||
const bounds = new L.LatLngBounds(southWest, northEast);
|
const bounds = new L.LatLngBounds(this.southWest, this.northEast);
|
||||||
|
|
||||||
if (updateImage && this.imageOverlay) {
|
if (updateImage && this.imageOverlay) {
|
||||||
this.imageOverlay.remove();
|
this.imageOverlay.remove();
|
||||||
@ -147,8 +152,8 @@ export class ImageMap extends LeafletMap {
|
|||||||
this.imageOverlay = L.imageOverlay(this.imageUrl, bounds).addTo(this.map);
|
this.imageOverlay = L.imageOverlay(this.imageUrl, bounds).addTo(this.map);
|
||||||
}
|
}
|
||||||
const padding = 200 * maxZoom;
|
const padding = 200 * maxZoom;
|
||||||
southWest = this.pointToLatLng(-padding, h + padding);
|
const southWest = this.pointToLatLng(-padding, h + padding);
|
||||||
northEast = this.pointToLatLng(w + padding, -padding);
|
const northEast = this.pointToLatLng(w + padding, -padding);
|
||||||
const maxBounds = new L.LatLngBounds(southWest, northEast);
|
const maxBounds = new L.LatLngBounds(southWest, northEast);
|
||||||
this.map.setMaxBounds(maxBounds);
|
this.map.setMaxBounds(maxBounds);
|
||||||
if (lastCenterPos) {
|
if (lastCenterPos) {
|
||||||
@ -187,7 +192,7 @@ export class ImageMap extends LeafletMap {
|
|||||||
this.updateMarkers(this.markersData);
|
this.updateMarkers(this.markersData);
|
||||||
if (this.options.draggableMarker && this.addMarkers.length) {
|
if (this.options.draggableMarker && this.addMarkers.length) {
|
||||||
this.addMarkers.forEach((marker) => {
|
this.addMarkers.forEach((marker) => {
|
||||||
const prevPoint = this.convertToCustomFormat(marker.getLatLng(), prevWidth, prevHeight);
|
const prevPoint = this.convertToCustomFormat(marker.getLatLng(), null, prevWidth, prevHeight);
|
||||||
marker.setLatLng(this.convertPosition(prevPoint));
|
marker.setLatLng(this.convertPosition(prevPoint));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -257,11 +262,10 @@ export class ImageMap extends LeafletMap {
|
|||||||
return L.CRS.Simple.latLngToPoint(latLng, maxZoom - 1);
|
return L.CRS.Simple.latLngToPoint(latLng, maxZoom - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
convertToCustomFormat(position: L.LatLng, width = this.width, height = this.height): object {
|
convertToCustomFormat(position: L.LatLng, offset = 0, width = this.width, height = this.height): object {
|
||||||
const point = this.latLngToPoint(position);
|
const point = this.latLngToPoint(position);
|
||||||
const customX = calculateNewPointCoordinate(point.x, width);
|
const customX = calculateNewPointCoordinate(point.x, width);
|
||||||
const customY = calculateNewPointCoordinate(point.y, height);
|
const customY = calculateNewPointCoordinate(point.y, height);
|
||||||
|
|
||||||
if (customX === 0) {
|
if (customX === 0) {
|
||||||
point.x = 0;
|
point.x = 0;
|
||||||
} else if (customX === 1) {
|
} else if (customX === 1) {
|
||||||
@ -273,7 +277,8 @@ export class ImageMap extends LeafletMap {
|
|||||||
} else if (customY === 1) {
|
} else if (customY === 1) {
|
||||||
point.y = height;
|
point.y = height;
|
||||||
}
|
}
|
||||||
const customLatLng = this.pointToLatLng(point.x, point.y);
|
|
||||||
|
const customLatLng = checkLngLat(this.pointToLatLng(point.x, point.y), this.southWest, this.northEast, offset);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
[this.options.xPosKeyName]: customX,
|
[this.options.xPosKeyName]: customX,
|
||||||
|
|||||||
@ -1029,7 +1029,11 @@ export class WidgetComponent extends PageComponent implements OnInit, AfterViewI
|
|||||||
} else {
|
} else {
|
||||||
url = `/dashboards/${targetDashboardId}?state=${state}`;
|
url = `/dashboards/${targetDashboardId}?state=${state}`;
|
||||||
}
|
}
|
||||||
this.router.navigateByUrl(url);
|
if (descriptor.openNewBrowserTab) {
|
||||||
|
window.open(url, '_blank');
|
||||||
|
} else {
|
||||||
|
this.router.navigateByUrl(url);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case WidgetActionType.custom:
|
case WidgetActionType.custom:
|
||||||
const customFunction = descriptor.customFunction;
|
const customFunction = descriptor.customFunction;
|
||||||
|
|||||||
@ -344,6 +344,7 @@ export interface WidgetActionDescriptor extends CustomActionDescriptor {
|
|||||||
targetDashboardId?: string;
|
targetDashboardId?: string;
|
||||||
targetDashboardStateId?: string;
|
targetDashboardStateId?: string;
|
||||||
openRightLayout?: boolean;
|
openRightLayout?: boolean;
|
||||||
|
openNewBrowserTab?: boolean;
|
||||||
setEntityId?: boolean;
|
setEntityId?: boolean;
|
||||||
stateEntityParamName?: string;
|
stateEntityParamName?: string;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2257,7 +2257,8 @@
|
|||||||
"target-dashboard-state-required": "Target dashboard state is required",
|
"target-dashboard-state-required": "Target dashboard state is required",
|
||||||
"set-entity-from-widget": "Set entity from widget",
|
"set-entity-from-widget": "Set entity from widget",
|
||||||
"target-dashboard": "Target dashboard",
|
"target-dashboard": "Target dashboard",
|
||||||
"open-right-layout": "Open right dashboard layout (mobile view)"
|
"open-right-layout": "Open right dashboard layout (mobile view)",
|
||||||
|
"open-new-browser-tab": "Open in a new browser tab"
|
||||||
},
|
},
|
||||||
"widgets-bundle": {
|
"widgets-bundle": {
|
||||||
"current": "Current bundle",
|
"current": "Current bundle",
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user