diff options
Diffstat (limited to 'web')
-rw-r--r-- | web/app/Application.js | 7 | ||||
-rw-r--r-- | web/app/controller/Root.js | 9 | ||||
-rw-r--r-- | web/app/model/Device.js | 2 | ||||
-rw-r--r-- | web/app/model/Event.js | 3 | ||||
-rw-r--r-- | web/app/model/Geofence.js | 36 | ||||
-rw-r--r-- | web/app/store/AllGeofences.js | 28 | ||||
-rw-r--r-- | web/app/store/Geofences.js | 28 | ||||
-rw-r--r-- | web/app/view/BasePermissionsController.js (renamed from web/app/view/UserGroupsController.js) | 40 | ||||
-rw-r--r-- | web/app/view/DeviceGeofences.js | 43 | ||||
-rw-r--r-- | web/app/view/Devices.js | 8 | ||||
-rw-r--r-- | web/app/view/DevicesController.js | 20 | ||||
-rw-r--r-- | web/app/view/GroupGeofences.js | 43 | ||||
-rw-r--r-- | web/app/view/Groups.js | 11 | ||||
-rw-r--r-- | web/app/view/GroupsController.js | 21 | ||||
-rw-r--r-- | web/app/view/UserDevices.js | 5 | ||||
-rw-r--r-- | web/app/view/UserDevicesController.js | 79 | ||||
-rw-r--r-- | web/app/view/UserGeofences.js | 43 | ||||
-rw-r--r-- | web/app/view/UserGroups.js | 5 | ||||
-rw-r--r-- | web/app/view/Users.js | 5 | ||||
-rw-r--r-- | web/app/view/UsersController.js | 32 | ||||
-rw-r--r-- | web/l10n/en.json | 1 |
21 files changed, 358 insertions, 111 deletions
diff --git a/web/app/Application.js b/web/app/Application.js index e798a73e2..dbbc7a594 100644 --- a/web/app/Application.js +++ b/web/app/Application.js @@ -31,7 +31,8 @@ Ext.define('Traccar.Application', { 'Position', 'Attribute', 'Command', - 'Event' + 'Event', + 'Geofence' ], stores: [ @@ -49,7 +50,9 @@ Ext.define('Traccar.Application', { 'CommandTypes', 'TimeUnits', 'Languages', - 'Events' + 'Events', + 'Geofences', + 'AllGeofences' ], controllers: [ diff --git a/web/app/controller/Root.js b/web/app/controller/Root.js index 991a2572c..56a076edd 100644 --- a/web/app/controller/Root.js +++ b/web/app/controller/Root.js @@ -75,6 +75,7 @@ Ext.define('Traccar.controller.Root', { loadApp: function () { Ext.getStore('Groups').load(); Ext.getStore('Devices').load(); + Ext.getStore('Geofences').load(); Ext.get('attribution').remove(); if (this.isPhone) { Ext.create('widget.mainMobile'); @@ -94,7 +95,7 @@ Ext.define('Traccar.controller.Root', { }; socket.onmessage = function (event) { - var i, j, store, data, array, entity, device, typeKey, text; + var i, j, store, data, array, entity, device, typeKey, text, geofence; data = Ext.decode(event.data); @@ -147,6 +148,12 @@ Ext.define('Traccar.controller.Root', { text = typeKey; } } + if (array[i].geofenceId !== 0) { + geofence = Ext.getStore('Geofences').getById(array[i].geofenceId); + if (typeof geofence != "undefined") { + text += ' \"' + geofence.getData().name + '"'; + } + } device = Ext.getStore('Devices').getById(array[i].deviceId); if (typeof device != "undefined") { Ext.toast(text, device.getData().name); diff --git a/web/app/model/Device.js b/web/app/model/Device.js index 588d53c1f..247f72ba9 100644 --- a/web/app/model/Device.js +++ b/web/app/model/Device.js @@ -37,5 +37,7 @@ Ext.define('Traccar.model.Device', { }, { name: 'groupId', type: 'int' + }, { + name: 'geofenceIds' }] }); diff --git a/web/app/model/Event.js b/web/app/model/Event.js index 4dd3ea7ff..698ebb535 100644 --- a/web/app/model/Event.js +++ b/web/app/model/Event.js @@ -35,6 +35,9 @@ Ext.define('Traccar.model.Event', { name: 'positionId', type: 'int' }, { + name: 'geofenceId', + type: 'int' + }, { name: 'attributes' }] }); diff --git a/web/app/model/Geofence.js b/web/app/model/Geofence.js new file mode 100644 index 000000000..a832455ac --- /dev/null +++ b/web/app/model/Geofence.js @@ -0,0 +1,36 @@ +/* + * Copyright 2016 Anton Tananaev (anton.tananaev@gmail.com) + * + * 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. + */ + +Ext.define('Traccar.model.Geofence', { + extend: 'Ext.data.Model', + identifier: 'negative', + + fields: [{ + name: 'id', + type: 'int' + }, { + name: 'name', + type: 'string' + }, { + name: 'description', + type: 'string' + }, { + name: 'area', + type: 'string' + }, { + name: 'attributes' + }] +}); diff --git a/web/app/store/AllGeofences.js b/web/app/store/AllGeofences.js new file mode 100644 index 000000000..aa6f9abfc --- /dev/null +++ b/web/app/store/AllGeofences.js @@ -0,0 +1,28 @@ +/* + * Copyright 2016 Anton Tananaev (anton.tananaev@gmail.com) + * + * 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. + */ + +Ext.define('Traccar.store.AllGeofences', { + extend: 'Ext.data.Store', + model: 'Traccar.model.Geofence', + + proxy: { + type: 'rest', + url: '/api/geofences', + extraParams: { + all: true + } + } +}); diff --git a/web/app/store/Geofences.js b/web/app/store/Geofences.js new file mode 100644 index 000000000..8c5c0b3cf --- /dev/null +++ b/web/app/store/Geofences.js @@ -0,0 +1,28 @@ +/* + * Copyright 2016 Anton Tananaev (anton.tananaev@gmail.com) + * + * 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. + */ + +Ext.define('Traccar.store.Geofences', { + extend: 'Ext.data.Store', + model: 'Traccar.model.Geofence', + + proxy: { + type: 'rest', + url: '/api/geofences', + writer: { + writeAllFields: true + } + } +}); diff --git a/web/app/view/UserGroupsController.js b/web/app/view/BasePermissionsController.js index 662508f0a..713d91770 100644 --- a/web/app/view/UserGroupsController.js +++ b/web/app/view/BasePermissionsController.js @@ -14,21 +14,23 @@ * limitations under the License. */ -Ext.define('Traccar.view.UserGroupsController', { +Ext.define('Traccar.view.BasePermissionsController', { extend: 'Ext.app.ViewController', - alias: 'controller.userGroups', + alias: 'controller.basePermissionsController', init: function () { - this.userId = this.getView().user.getData().id; + var params = {}; + params[this.getView().baseObjectName] = this.getView().baseObject; + var linkStoreName = this.getView().linkStoreName; + var storeName = this.getView().storeName; + linkStoreName = (typeof linkStoreName === 'undefined') ? storeName : linkStoreName; + this.getView().setStore(Ext.getStore(storeName)); this.getView().getStore().load({ scope: this, callback: function (records, operation, success) { - var userStore = Ext.create('Traccar.store.Groups'); - - userStore.load({ - params: { - userId: this.userId - }, + var linkStore = Ext.create('Traccar.store.' + linkStoreName); + linkStore.load({ + params: params, scope: this, callback: function (records, operation, success) { var i, index; @@ -45,13 +47,13 @@ Ext.define('Traccar.view.UserGroupsController', { }, onBeforeSelect: function (object, record, index) { + var data = {}; + data[this.getView().baseObjectName] = this.getView().baseObject; + data[this.getView().linkObjectName] = record.getData().id; Ext.Ajax.request({ scope: this, - url: '/api/permissions/groups', - jsonData: { - userId: this.userId, - groupId: record.getData().id - }, + url: this.getView().urlApi, + jsonData: Ext.util.JSON.encode(data), callback: function (options, success, response) { if (!success) { Traccar.app.showError(response); @@ -61,14 +63,14 @@ Ext.define('Traccar.view.UserGroupsController', { }, onBeforeDeselect: function (object, record, index) { + var data = {}; + data[this.getView().baseObjectName] = this.getView().baseObject; + data[this.getView().linkObjectName] = record.getData().id; Ext.Ajax.request({ scope: this, method: 'DELETE', - url: '/api/permissions/groups', - jsonData: { - userId: this.userId, - groupId: record.getData().id - }, + url: this.getView().urlApi, + jsonData: Ext.util.JSON.encode(data), callback: function (options, success, response) { if (!success) { Traccar.app.showError(response); diff --git a/web/app/view/DeviceGeofences.js b/web/app/view/DeviceGeofences.js new file mode 100644 index 000000000..9e2c12a77 --- /dev/null +++ b/web/app/view/DeviceGeofences.js @@ -0,0 +1,43 @@ +/* + * Copyright 2016 Anton Tananaev (anton.tananaev@gmail.com) + * + * 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. + */ + +Ext.define('Traccar.view.DeviceGeofences', { + extend: 'Ext.grid.Panel', + xtype: 'deviceGeofencesView', + + requires: [ + 'Traccar.view.BasePermissionsController' + ], + + controller: 'basePermissionsController', + + selModel: { + selType: 'checkboxmodel', + checkOnly: true, + showHeaderCheckbox: false + }, + + listeners: { + beforedeselect: 'onBeforeDeselect', + beforeselect: 'onBeforeSelect' + }, + + columns: [{ + text: Strings.sharedName, + dataIndex: 'name', + flex: 1 + }] +}); diff --git a/web/app/view/Devices.js b/web/app/view/Devices.js index f06c2658b..1a70dfef8 100644 --- a/web/app/view/Devices.js +++ b/web/app/view/Devices.js @@ -41,6 +41,14 @@ Ext.define('Traccar.view.Devices', { tbar: { xtype: 'editToolbar', items: [{ + xtype: 'button', + disabled: true, + handler: 'onGeofencesClick', + reference: 'toolbarGeofencesButton', + glyph: 'xf21d@FontAwesome', + tooltip: Strings.sharedGeofences, + tooltipType: 'title' + }, { disabled: true, handler: 'onCommandClick', reference: 'deviceCommandButton', diff --git a/web/app/view/DevicesController.js b/web/app/view/DevicesController.js index 6b79a6804..cf31dd7bb 100644 --- a/web/app/view/DevicesController.js +++ b/web/app/view/DevicesController.js @@ -20,7 +20,8 @@ Ext.define('Traccar.view.DevicesController', { requires: [ 'Traccar.view.CommandDialog', - 'Traccar.view.DeviceDialog' + 'Traccar.view.DeviceDialog', + 'Traccar.view.DeviceGeofences' ], config: { @@ -84,6 +85,22 @@ Ext.define('Traccar.view.DevicesController', { }); }, + onGeofencesClick: function () { + var admin = Traccar.app.getUser().get('admin'); + var device = this.getView().getSelectionModel().getSelection()[0]; + Ext.create('Traccar.view.BaseWindow', { + title: Strings.sharedGeofences, + items: { + xtype: 'deviceGeofencesView', + baseObjectName: 'deviceId', + linkObjectName: 'geofenceId', + storeName: (admin) ? 'AllGeofences' : 'Geofences', + urlApi: '/api/devices/geofences', + baseObject: device.getData().id + } + }).show(); + }, + onCommandClick: function () { var device, deviceId, command, dialog, comboStore; device = this.getView().getSelectionModel().getSelection()[0]; @@ -108,6 +125,7 @@ Ext.define('Traccar.view.DevicesController', { var empty = selected.getCount() === 0; this.lookupReference('toolbarEditButton').setDisabled(empty); this.lookupReference('toolbarRemoveButton').setDisabled(empty); + this.lookupReference('toolbarGeofencesButton').setDisabled(empty); this.lookupReference('deviceCommandButton').setDisabled(empty || (selected.getLastSelected().get('status') !== 'online')); if (!empty) { this.fireEvent('selectDevice', selected.getLastSelected(), true); diff --git a/web/app/view/GroupGeofences.js b/web/app/view/GroupGeofences.js new file mode 100644 index 000000000..8ef2984ea --- /dev/null +++ b/web/app/view/GroupGeofences.js @@ -0,0 +1,43 @@ +/* + * Copyright 2016 Anton Tananaev (anton.tananaev@gmail.com) + * + * 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. + */ + +Ext.define('Traccar.view.GroupGeofences', { + extend: 'Ext.grid.Panel', + xtype: 'groupGeofencesView', + + requires: [ + 'Traccar.view.BasePermissionsController' + ], + + controller: 'basePermissionsController', + + selModel: { + selType: 'checkboxmodel', + checkOnly: true, + showHeaderCheckbox: false + }, + + listeners: { + beforedeselect: 'onBeforeDeselect', + beforeselect: 'onBeforeSelect' + }, + + columns: [{ + text: Strings.sharedName, + dataIndex: 'name', + flex: 1 + }] +}); diff --git a/web/app/view/Groups.js b/web/app/view/Groups.js index 8404c59a9..59d20df31 100644 --- a/web/app/view/Groups.js +++ b/web/app/view/Groups.js @@ -29,7 +29,16 @@ Ext.define('Traccar.view.Groups', { selType: 'rowmodel', tbar: { - xtype: 'editToolbar' + xtype: 'editToolbar', + items: [{ + xtype: 'button', + disabled: true, + handler: 'onGeofencesClick', + reference: 'toolbarGeofencesButton', + glyph: 'xf21d@FontAwesome', + tooltip: Strings.sharedGeofences, + tooltipType: 'title' + }] }, listeners: { diff --git a/web/app/view/GroupsController.js b/web/app/view/GroupsController.js index 6cc568ea2..bc713bb6d 100644 --- a/web/app/view/GroupsController.js +++ b/web/app/view/GroupsController.js @@ -18,6 +18,10 @@ Ext.define('Traccar.view.GroupsController', { extend: 'Ext.app.ViewController', alias: 'controller.groups', + requires: [ + 'Traccar.view.GroupGeofences' + ], + onAddClick: function () { var group, dialog; group = Ext.create('Traccar.model.Group'); @@ -55,9 +59,26 @@ Ext.define('Traccar.view.GroupsController', { }); }, + onGeofencesClick: function () { + var admin = Traccar.app.getUser().get('admin'); + var group = this.getView().getSelectionModel().getSelection()[0]; + Ext.create('Traccar.view.BaseWindow', { + title: Strings.sharedGeofences, + items: { + xtype: 'groupGeofencesView', + baseObjectName: 'groupId', + linkObjectName: 'geofenceId', + storeName: (admin) ? 'AllGeofences' : 'Geofences', + urlApi: '/api/groups/geofences', + baseObject: group.getData().id + } + }).show(); + }, + onSelectionChange: function (selected) { var disabled = selected.length > 0; this.lookupReference('toolbarEditButton').setDisabled(disabled); this.lookupReference('toolbarRemoveButton').setDisabled(disabled); + this.lookupReference('toolbarGeofencesButton').setDisabled(disabled); } }); diff --git a/web/app/view/UserDevices.js b/web/app/view/UserDevices.js index fe16dd93a..6a1a718aa 100644 --- a/web/app/view/UserDevices.js +++ b/web/app/view/UserDevices.js @@ -19,11 +19,10 @@ Ext.define('Traccar.view.UserDevices', { xtype: 'userDevicesView', requires: [ - 'Traccar.view.UserDevicesController' + 'Traccar.view.BasePermissionsController' ], - controller: 'userDevices', - store: 'AllDevices', + controller: 'basePermissionsController', selModel: { selType: 'checkboxmodel', diff --git a/web/app/view/UserDevicesController.js b/web/app/view/UserDevicesController.js deleted file mode 100644 index e5dbbdbb8..000000000 --- a/web/app/view/UserDevicesController.js +++ /dev/null @@ -1,79 +0,0 @@ -/* - * Copyright 2015 Anton Tananaev (anton.tananaev@gmail.com) - * - * 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. - */ - -Ext.define('Traccar.view.UserDevicesController', { - extend: 'Ext.app.ViewController', - alias: 'controller.userDevices', - - init: function () { - this.userId = this.getView().user.getData().id; - this.getView().getStore().load({ - scope: this, - callback: function (records, operation, success) { - var userStore = Ext.create('Traccar.store.Devices'); - - userStore.load({ - params: { - userId: this.userId - }, - scope: this, - callback: function (records, operation, success) { - var i, index; - if (success) { - for (i = 0; i < records.length; i++) { - index = this.getView().getStore().find('id', records[i].getData().id); - this.getView().getSelectionModel().select(index, true, true); - } - } - } - }); - } - }); - }, - - onBeforeSelect: function (object, record, index) { - Ext.Ajax.request({ - scope: this, - url: '/api/permissions/devices', - jsonData: { - userId: this.userId, - deviceId: record.getData().id - }, - callback: function (options, success, response) { - if (!success) { - Traccar.app.showError(response); - } - } - }); - }, - - onBeforeDeselect: function (object, record, index) { - Ext.Ajax.request({ - scope: this, - method: 'DELETE', - url: '/api/permissions/devices', - jsonData: { - userId: this.userId, - deviceId: record.getData().id - }, - callback: function (options, success, response) { - if (!success) { - Traccar.app.showError(response); - } - } - }); - } -}); diff --git a/web/app/view/UserGeofences.js b/web/app/view/UserGeofences.js new file mode 100644 index 000000000..03a02ff3b --- /dev/null +++ b/web/app/view/UserGeofences.js @@ -0,0 +1,43 @@ +/* + * Copyright 2016 Anton Tananaev (anton.tananaev@gmail.com) + * + * 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. + */ + +Ext.define('Traccar.view.UserGeofences', { + extend: 'Ext.grid.Panel', + xtype: 'userGeofencesView', + + requires: [ + 'Traccar.view.BasePermissionsController' + ], + + controller: 'basePermissionsController', + + selModel: { + selType: 'checkboxmodel', + checkOnly: true, + showHeaderCheckbox: false + }, + + listeners: { + beforedeselect: 'onBeforeDeselect', + beforeselect: 'onBeforeSelect' + }, + + columns: [{ + text: Strings.sharedName, + dataIndex: 'name', + flex: 1 + }] +}); diff --git a/web/app/view/UserGroups.js b/web/app/view/UserGroups.js index cb0f0bd5d..84032f021 100644 --- a/web/app/view/UserGroups.js +++ b/web/app/view/UserGroups.js @@ -19,11 +19,10 @@ Ext.define('Traccar.view.UserGroups', { xtype: 'userGroupsView', requires: [ - 'Traccar.view.UserGroupsController' + 'Traccar.view.BasePermissionsController' ], - controller: 'userGroups', - store: 'AllGroups', + controller: 'basePermissionsController', selModel: { selType: 'checkboxmodel', diff --git a/web/app/view/Users.js b/web/app/view/Users.js index 408a70885..b6301b38b 100644 --- a/web/app/view/Users.js +++ b/web/app/view/Users.js @@ -40,6 +40,11 @@ Ext.define('Traccar.view.Users', { disabled: true, handler: 'onGroupsClick', reference: 'userGroupsButton' + }, { + text: Strings.sharedGeofences, + disabled: true, + handler: 'onGeofencesClick', + reference: 'userGeofencesButton' }] }, diff --git a/web/app/view/UsersController.js b/web/app/view/UsersController.js index c48f57cf4..acba66b4d 100644 --- a/web/app/view/UsersController.js +++ b/web/app/view/UsersController.js @@ -22,6 +22,7 @@ Ext.define('Traccar.view.UsersController', { 'Traccar.view.UserDialog', 'Traccar.view.UserDevices', 'Traccar.view.UserGroups', + 'Traccar.view.UserGeofences', 'Traccar.view.BaseWindow' ], @@ -71,7 +72,12 @@ Ext.define('Traccar.view.UsersController', { title: Strings.deviceTitle, items: { xtype: 'userDevicesView', - user: user + baseObjectName: 'userId', + linkObjectName: 'deviceId', + storeName: 'AllDevices', + linkStoreName: 'Devices', + urlApi: '/api/permissions/devices', + baseObject: user.getData().id } }).show(); }, @@ -82,7 +88,28 @@ Ext.define('Traccar.view.UsersController', { title: Strings.settingsGroups, items: { xtype: 'userGroupsView', - user: user + baseObjectName: 'userId', + linkObjectName: 'groupId', + storeName: 'AllGroups', + linkStoreName: 'Groups', + urlApi: '/api/permissions/groups', + baseObject: user.getData().id + } + }).show(); + }, + + onGeofencesClick: function () { + var user = this.getView().getSelectionModel().getSelection()[0]; + Ext.create('Traccar.view.BaseWindow', { + title: Strings.sharedGeofences, + items: { + xtype: 'userGeofencesView', + baseObjectName: 'userId', + linkObjectName: 'geofenceId', + storeName: 'AllGeofences', + linkStoreName: 'Geofences', + urlApi: '/api/permissions/geofences', + baseObject: user.getData().id } }).show(); }, @@ -93,5 +120,6 @@ Ext.define('Traccar.view.UsersController', { this.lookupReference('toolbarRemoveButton').setDisabled(disabled); this.lookupReference('userDevicesButton').setDisabled(disabled); this.lookupReference('userGroupsButton').setDisabled(disabled); + this.lookupReference('userGeofencesButton').setDisabled(disabled); } }); diff --git a/web/l10n/en.json b/web/l10n/en.json index 48dc6533c..2809ea66a 100644 --- a/web/l10n/en.json +++ b/web/l10n/en.json @@ -16,6 +16,7 @@ "sharedSecond": "Second", "sharedName": "Name", "sharedSearch": "Search", + "sharedGeofences": "Geofences", "errorTitle": "Error", "errorUnknown": "Unknown error", "errorConnection": "Connection error", |