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)) {
|
||||
log.info("Migrating ThingsBoard latest timeseries data from cassandra to SQL database ...");
|
||||
latestMigrateService.migrate();
|
||||
log.info("Updating system data...");
|
||||
} else {
|
||||
switch (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");
|
||||
dataUpdateService.updateData("3.1.1");
|
||||
log.info("Updating system data...");
|
||||
systemDataLoaderService.updateSystemWidgets();
|
||||
systemDataLoaderService.createOAuth2Templates();
|
||||
break;
|
||||
case "3.2.0":
|
||||
log.info("Upgrading ThingsBoard from version 3.2.0 to 3.2.1 ...");
|
||||
databaseEntitiesUpgradeService.upgradeDatabase("3.2.0");
|
||||
log.info("Updating system data...");
|
||||
systemDataLoaderService.updateSystemWidgets();
|
||||
break;
|
||||
default:
|
||||
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.fasterxml.jackson.databind.JsonNode;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.fasterxml.jackson.databind.node.ArrayNode;
|
||||
import com.fasterxml.jackson.databind.node.ObjectNode;
|
||||
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.exception.DataValidationException;
|
||||
import org.thingsboard.server.dao.service.DataValidator;
|
||||
import org.thingsboard.server.dao.util.mapping.JacksonUtil;
|
||||
|
||||
import java.io.PrintWriter;
|
||||
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")
|
||||
public class AuditLogServiceImpl implements AuditLogService {
|
||||
|
||||
private static final ObjectMapper objectMapper = new ObjectMapper();
|
||||
|
||||
private static final String INCORRECT_TENANT_ID = "Incorrect tenantId ";
|
||||
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,
|
||||
ActionType actionType,
|
||||
Object... additionalInfo) {
|
||||
ObjectNode actionData = objectMapper.createObjectNode();
|
||||
ObjectNode actionData = JacksonUtil.newObjectNode();
|
||||
switch (actionType) {
|
||||
case ADDED:
|
||||
case UPDATED:
|
||||
@ -168,7 +166,7 @@ public class AuditLogServiceImpl implements AuditLogService {
|
||||
case RELATIONS_DELETED:
|
||||
case ASSIGNED_TO_TENANT:
|
||||
if (entity != null) {
|
||||
ObjectNode entityNode = objectMapper.valueToTree(entity);
|
||||
ObjectNode entityNode = (ObjectNode) JacksonUtil.valueToTree(entity);
|
||||
if (entityId.getEntityType() == EntityType.DASHBOARD) {
|
||||
entityNode.put("configuration", "");
|
||||
}
|
||||
@ -177,7 +175,7 @@ public class AuditLogServiceImpl implements AuditLogService {
|
||||
if (entityId.getEntityType() == EntityType.RULE_CHAIN) {
|
||||
RuleChainMetaData ruleChainMetaData = extractParameter(RuleChainMetaData.class, additionalInfo);
|
||||
if (ruleChainMetaData != null) {
|
||||
ObjectNode ruleChainMetaDataNode = objectMapper.valueToTree(ruleChainMetaData);
|
||||
ObjectNode ruleChainMetaDataNode = (ObjectNode) JacksonUtil.valueToTree(ruleChainMetaData);
|
||||
actionData.set("metadata", ruleChainMetaDataNode);
|
||||
}
|
||||
}
|
||||
@ -194,7 +192,7 @@ public class AuditLogServiceImpl implements AuditLogService {
|
||||
String scope = extractParameter(String.class, 0, additionalInfo);
|
||||
List<AttributeKvEntry> attributes = extractParameter(List.class, 1, additionalInfo);
|
||||
actionData.put("scope", scope);
|
||||
ObjectNode attrsNode = objectMapper.createObjectNode();
|
||||
ObjectNode attrsNode = JacksonUtil.newObjectNode();
|
||||
if (attributes != null) {
|
||||
for (AttributeKvEntry attr : attributes) {
|
||||
attrsNode.put(attr.getKey(), attr.getValueAsString());
|
||||
@ -225,7 +223,7 @@ public class AuditLogServiceImpl implements AuditLogService {
|
||||
case CREDENTIALS_UPDATED:
|
||||
actionData.put("entityId", entityId.toString());
|
||||
DeviceCredentials deviceCredentials = extractParameter(DeviceCredentials.class, additionalInfo);
|
||||
actionData.set("credentials", objectMapper.valueToTree(deviceCredentials));
|
||||
actionData.set("credentials", JacksonUtil.valueToTree(deviceCredentials));
|
||||
break;
|
||||
case ASSIGNED_TO_CUSTOMER:
|
||||
strEntityId = extractParameter(String.class, 0, additionalInfo);
|
||||
@ -246,7 +244,7 @@ public class AuditLogServiceImpl implements AuditLogService {
|
||||
case RELATION_ADD_OR_UPDATE:
|
||||
case RELATION_DELETED:
|
||||
EntityRelation relation = extractParameter(EntityRelation.class, 0, additionalInfo);
|
||||
actionData.set("relation", objectMapper.valueToTree(relation));
|
||||
actionData.set("relation", JacksonUtil.valueToTree(relation));
|
||||
break;
|
||||
case LOGIN:
|
||||
case LOGOUT:
|
||||
@ -264,7 +262,7 @@ public class AuditLogServiceImpl implements AuditLogService {
|
||||
case PROVISION_FAILURE:
|
||||
ProvisionRequest request = extractParameter(ProvisionRequest.class, additionalInfo);
|
||||
if (request != null) {
|
||||
actionData.set("provisionRequest", objectMapper.valueToTree(request));
|
||||
actionData.set("provisionRequest", JacksonUtil.valueToTree(request));
|
||||
}
|
||||
break;
|
||||
case TIMESERIES_UPDATED:
|
||||
@ -275,7 +273,7 @@ public class AuditLogServiceImpl implements AuditLogService {
|
||||
updatedTimeseries.stream()
|
||||
.collect(Collectors.groupingBy(TsKvEntry::getTs))
|
||||
.forEach((k, v) -> {
|
||||
ObjectNode element = objectMapper.createObjectNode();
|
||||
ObjectNode element = JacksonUtil.newObjectNode();
|
||||
element.put("ts", k);
|
||||
ObjectNode values = element.putObject("values");
|
||||
v.forEach(kvEntry -> values.put(kvEntry.getKey(), kvEntry.getValueAsString()));
|
||||
|
||||
@ -58,7 +58,7 @@ frontend http-in
|
||||
|
||||
acl transport_http_acl path_beg /api/v1/
|
||||
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 }
|
||||
|
||||
@ -76,7 +76,7 @@ frontend https_in
|
||||
reqadd X-Forwarded-Proto:\ https
|
||||
|
||||
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-api-backend if tb_api_acl
|
||||
|
||||
@ -64,6 +64,7 @@ class ProfileState {
|
||||
alarmSettings.clear();
|
||||
alarmCreateKeys.clear();
|
||||
alarmClearKeys.clear();
|
||||
entityKeys.clear();
|
||||
if (deviceProfile.getProfileData().getAlarms() != null) {
|
||||
alarmSettings.addAll(deviceProfile.getProfileData().getAlarms());
|
||||
for (DeviceProfileAlarm alarm : deviceProfile.getProfileData().getAlarms()) {
|
||||
|
||||
@ -16,7 +16,7 @@
|
||||
#
|
||||
|
||||
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 "usage: ./client.keygen.sh [-p file]"
|
||||
echo " -p | --props | --properties file Properties file. default value is ./keygen.properties"
|
||||
@ -70,6 +70,20 @@ while :
|
||||
done
|
||||
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..."
|
||||
|
||||
keytool -genkeypair -v \
|
||||
@ -77,8 +91,8 @@ keytool -genkeypair -v \
|
||||
-keystore $CLIENT_FILE_PREFIX.jks \
|
||||
-keypass $CLIENT_KEY_PASSWORD \
|
||||
-storepass $CLIENT_KEYSTORE_PASSWORD \
|
||||
-keyalg RSA \
|
||||
-keysize 2048 \
|
||||
-keyalg $CLIENT_KEY_ALG \
|
||||
-keysize $CLIENT_KEY_SIZE\
|
||||
-validity 9999 \
|
||||
-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
|
||||
|
||||
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) )) \
|
||||
$CLIENT_FILE_PREFIX.pem >> $CLIENT_FILE_PREFIX.nopass.pem
|
||||
|
||||
|
||||
@ -26,6 +26,8 @@ SERVER_KEY_PASSWORD=server_key_password
|
||||
|
||||
SERVER_KEY_ALIAS="serveralias"
|
||||
SERVER_FILE_PREFIX="mqttserver"
|
||||
SERVER_KEY_ALG="RSA"
|
||||
SERVER_KEY_SIZE="2048"
|
||||
SERVER_KEYSTORE_DIR="/etc/thingsboard/conf"
|
||||
|
||||
CLIENT_KEYSTORE_PASSWORD=password
|
||||
@ -33,4 +35,5 @@ CLIENT_KEY_PASSWORD=password
|
||||
|
||||
CLIENT_KEY_ALIAS="clientalias"
|
||||
CLIENT_FILE_PREFIX="mqttclient"
|
||||
|
||||
CLIENT_KEY_ALG="RSA"
|
||||
CLIENT_KEY_SIZE="2048"
|
||||
|
||||
@ -92,8 +92,8 @@ keytool -genkeypair -v \
|
||||
-keystore $SERVER_FILE_PREFIX.jks \
|
||||
-keypass $SERVER_KEY_PASSWORD \
|
||||
-storepass $SERVER_KEYSTORE_PASSWORD \
|
||||
-keyalg RSA \
|
||||
-keysize 2048 \
|
||||
-keyalg $SERVER_KEY_ALG \
|
||||
-keysize $SERVER_KEY_SIZE \
|
||||
-validity 9999
|
||||
|
||||
status=$?
|
||||
|
||||
@ -26,6 +26,10 @@ const PROXY_CONFIG = {
|
||||
"target": ruleNodeUiforwardUrl,
|
||||
"secure": false,
|
||||
},
|
||||
"/static/widgets": {
|
||||
"target": forwardUrl,
|
||||
"secure": false,
|
||||
},
|
||||
"/oauth2": {
|
||||
"target": forwardUrl,
|
||||
"secure": false,
|
||||
@ -34,10 +38,6 @@ const PROXY_CONFIG = {
|
||||
"target": forwardUrl,
|
||||
"secure": false,
|
||||
},
|
||||
"/static": {
|
||||
"target": forwardUrl,
|
||||
"secure": false,
|
||||
},
|
||||
"/api/ws": {
|
||||
"target": wsForwardUrl,
|
||||
"ws": true,
|
||||
|
||||
@ -112,6 +112,12 @@
|
||||
{{ 'widget-action.open-right-layout' | translate }}
|
||||
</mat-checkbox>
|
||||
</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 ||
|
||||
widgetActionFormGroup.get('type').value === widgetActionType.updateDashboardState ||
|
||||
widgetActionFormGroup.get('type').value === widgetActionType.openDashboard ?
|
||||
|
||||
@ -144,6 +144,10 @@ export class WidgetActionDialogComponent extends DialogComponent<WidgetActionDia
|
||||
this.fb.control(action ? action.stateEntityParamName : null, [])
|
||||
);
|
||||
if (type === WidgetActionType.openDashboard) {
|
||||
this.actionTypeFormGroup.addControl(
|
||||
'openNewBrowserTab',
|
||||
this.fb.control(action ? action.openNewBrowserTab : false, [])
|
||||
);
|
||||
this.actionTypeFormGroup.addControl(
|
||||
'targetDashboardId',
|
||||
this.fb.control(action ? action.targetDashboardId : null,
|
||||
|
||||
@ -30,6 +30,7 @@ import { Datasource, DatasourceData } from '@shared/models/widget.models';
|
||||
import _ from 'lodash';
|
||||
import { mapProviderSchema, providerSets } from '@home/components/widget/lib/maps/schemes';
|
||||
import { addCondition, mergeSchemes } from '@core/schema-utils';
|
||||
import L, {Projection} from "leaflet";
|
||||
|
||||
export function getProviderSchema(mapProvider: MapProviders, ignoreImageMap = false) {
|
||||
const providerSchema = _.cloneDeep(mapProviderSchema);
|
||||
@ -443,3 +444,21 @@ export function createLoadingDiv(loadingText: string): JQuery<HTMLElement> {
|
||||
</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, {
|
||||
FeatureGroup,
|
||||
Icon,
|
||||
Icon, LatLng,
|
||||
LatLngBounds,
|
||||
LatLngTuple,
|
||||
markerClusterGroup,
|
||||
MarkerClusterGroup,
|
||||
MarkerClusterGroupOptions
|
||||
MarkerClusterGroupOptions, Projection
|
||||
} from 'leaflet';
|
||||
import tinycolor from 'tinycolor2';
|
||||
import 'leaflet-providers';
|
||||
@ -46,6 +46,7 @@ import {
|
||||
createTooltip,
|
||||
} from '@home/components/widget/lib/maps/maps-utils';
|
||||
import {
|
||||
checkLngLat,
|
||||
createLoadingDiv,
|
||||
parseArray,
|
||||
parseData,
|
||||
@ -79,6 +80,8 @@ export default abstract class LeafletMap {
|
||||
updatePending = false;
|
||||
addMarkers: L.Marker[] = [];
|
||||
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,
|
||||
public $container: HTMLElement,
|
||||
@ -206,21 +209,30 @@ export default abstract class LeafletMap {
|
||||
|
||||
addPolygonControl() {
|
||||
if (this.options.showPolygon && this.options.editablePolygon) {
|
||||
let mousePositionOnMap: L.LatLng[];
|
||||
let polygonPoints: L.LatLng[];
|
||||
let addPolygon: L.Control;
|
||||
let mousePositionOnMap: LatLng;
|
||||
this.map.on('mousemove', (e: L.LeafletMouseEvent) => {
|
||||
const polygonOffset = this.options.provider === MapProviders.image ? 10 : 0.01;
|
||||
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];
|
||||
mousePositionOnMap = e.latlng;
|
||||
});
|
||||
|
||||
const dragListener = (e: L.DragEndEvent) => {
|
||||
if (e.type === 'dragend' && mousePositionOnMap) {
|
||||
const newPolygon = L.polygon(mousePositionOnMap).addTo(this.map);
|
||||
if (e.type === 'dragend') {
|
||||
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);
|
||||
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');
|
||||
header.appendChild(document.createTextNode('Select entity:'));
|
||||
header.setAttribute('style', 'font-size: 14px; margin: 8px 0');
|
||||
@ -414,12 +426,9 @@ export default abstract class LeafletMap {
|
||||
}).filter(el => !!el);
|
||||
}
|
||||
|
||||
convertToCustomFormat(position: L.LatLng): object {
|
||||
if (position.lng > 180) {
|
||||
position.lng = 180;
|
||||
} else if (position.lng < -180) {
|
||||
position.lng = -180;
|
||||
}
|
||||
convertToCustomFormat(position: L.LatLng, offset = 0): object {
|
||||
position = checkLngLat(position, this.southWest, this.northEast, offset);
|
||||
|
||||
return {
|
||||
[this.options.latKeyName]: position.lat,
|
||||
[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')) {
|
||||
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();
|
||||
}
|
||||
|
||||
|
||||
@ -19,7 +19,12 @@ import LeafletMap from '../leaflet-map';
|
||||
import { MapImage, PosFuncton, UnitedMapSettings } from '../map-models';
|
||||
import { Observable, ReplaySubject } from 'rxjs';
|
||||
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 { DataSet, DatasourceType, widgetType } from '@shared/models/widget.models';
|
||||
import { DataKeyType } from '@shared/models/telemetry/telemetry.models';
|
||||
@ -132,9 +137,9 @@ export class ImageMap extends LeafletMap {
|
||||
updateBounds(updateImage?: boolean, lastCenterPos?) {
|
||||
const w = this.width;
|
||||
const h = this.height;
|
||||
let southWest = this.pointToLatLng(0, h);
|
||||
let northEast = this.pointToLatLng(w, 0);
|
||||
const bounds = new L.LatLngBounds(southWest, northEast);
|
||||
this.southWest = this.pointToLatLng(0, h);
|
||||
this.northEast = this.pointToLatLng(w, 0);
|
||||
const bounds = new L.LatLngBounds(this.southWest, this.northEast);
|
||||
|
||||
if (updateImage && this.imageOverlay) {
|
||||
this.imageOverlay.remove();
|
||||
@ -147,8 +152,8 @@ export class ImageMap extends LeafletMap {
|
||||
this.imageOverlay = L.imageOverlay(this.imageUrl, bounds).addTo(this.map);
|
||||
}
|
||||
const padding = 200 * maxZoom;
|
||||
southWest = this.pointToLatLng(-padding, h + padding);
|
||||
northEast = this.pointToLatLng(w + padding, -padding);
|
||||
const southWest = this.pointToLatLng(-padding, h + padding);
|
||||
const northEast = this.pointToLatLng(w + padding, -padding);
|
||||
const maxBounds = new L.LatLngBounds(southWest, northEast);
|
||||
this.map.setMaxBounds(maxBounds);
|
||||
if (lastCenterPos) {
|
||||
@ -187,7 +192,7 @@ export class ImageMap extends LeafletMap {
|
||||
this.updateMarkers(this.markersData);
|
||||
if (this.options.draggableMarker && this.addMarkers.length) {
|
||||
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));
|
||||
});
|
||||
}
|
||||
@ -257,11 +262,10 @@ export class ImageMap extends LeafletMap {
|
||||
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 customX = calculateNewPointCoordinate(point.x, width);
|
||||
const customY = calculateNewPointCoordinate(point.y, height);
|
||||
|
||||
if (customX === 0) {
|
||||
point.x = 0;
|
||||
} else if (customX === 1) {
|
||||
@ -273,7 +277,8 @@ export class ImageMap extends LeafletMap {
|
||||
} else if (customY === 1) {
|
||||
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 {
|
||||
[this.options.xPosKeyName]: customX,
|
||||
|
||||
@ -1029,7 +1029,11 @@ export class WidgetComponent extends PageComponent implements OnInit, AfterViewI
|
||||
} else {
|
||||
url = `/dashboards/${targetDashboardId}?state=${state}`;
|
||||
}
|
||||
if (descriptor.openNewBrowserTab) {
|
||||
window.open(url, '_blank');
|
||||
} else {
|
||||
this.router.navigateByUrl(url);
|
||||
}
|
||||
break;
|
||||
case WidgetActionType.custom:
|
||||
const customFunction = descriptor.customFunction;
|
||||
|
||||
@ -344,6 +344,7 @@ export interface WidgetActionDescriptor extends CustomActionDescriptor {
|
||||
targetDashboardId?: string;
|
||||
targetDashboardStateId?: string;
|
||||
openRightLayout?: boolean;
|
||||
openNewBrowserTab?: boolean;
|
||||
setEntityId?: boolean;
|
||||
stateEntityParamName?: string;
|
||||
}
|
||||
|
||||
@ -2257,7 +2257,8 @@
|
||||
"target-dashboard-state-required": "Target dashboard state is required",
|
||||
"set-entity-from-widget": "Set entity from widget",
|
||||
"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": {
|
||||
"current": "Current bundle",
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user