From 18ec7e54839430503b5a73cbf2e63dfa2e234e7b Mon Sep 17 00:00:00 2001 From: Anton Tananaev Date: Tue, 8 Mar 2016 22:42:57 +1300 Subject: Do not display unknown devices --- web/app/view/MapController.js | 44 ++++++++++++++++++++++--------------------- 1 file changed, 23 insertions(+), 21 deletions(-) (limited to 'web/app/view') diff --git a/web/app/view/MapController.js b/web/app/view/MapController.js index ce420c2ec..918f81390 100644 --- a/web/app/view/MapController.js +++ b/web/app/view/MapController.js @@ -113,29 +113,31 @@ Ext.define('Traccar.view.MapController', { deviceId = position.get('deviceId'); device = Ext.getStore('Devices').findRecord('id', deviceId, 0, false, false, true); - geometry = new ol.geom.Point(ol.proj.fromLonLat([ - position.get('longitude'), - position.get('latitude') - ])); - - if (deviceId in this.latestMarkers) { - marker = this.latestMarkers[deviceId]; - marker.setGeometry(geometry); - } else { - marker = new ol.Feature(geometry); - marker.set('record', device); - this.latestMarkers[deviceId] = marker; - this.getView().getLatestSource().addFeature(marker); - - style = this.getLatestMarker(this.getDeviceColor(device)); - style.getText().setText(device.get('name')); - marker.setStyle(style); - } + if (device) { + geometry = new ol.geom.Point(ol.proj.fromLonLat([ + position.get('longitude'), + position.get('latitude') + ])); + + if (deviceId in this.latestMarkers) { + marker = this.latestMarkers[deviceId]; + marker.setGeometry(geometry); + } else { + marker = new ol.Feature(geometry); + marker.set('record', device); + this.latestMarkers[deviceId] = marker; + this.getView().getLatestSource().addFeature(marker); + + style = this.getLatestMarker(this.getDeviceColor(device)); + style.getText().setText(device.get('name')); + marker.setStyle(style); + } - marker.getStyle().getImage().setRotation(position.get('course') * Math.PI / 180); + marker.getStyle().getImage().setRotation(position.get('course') * Math.PI / 180); - if (marker === this.selectedMarker && this.followSelected()) { - this.getView().getMapView().setCenter(marker.getGeometry().getCoordinates()); + if (marker === this.selectedMarker && this.followSelected()) { + this.getView().getMapView().setCenter(marker.getGeometry().getCoordinates()); + } } } }, -- cgit v1.2.3 From c0d07811425497f10e41519e090f02cfdffb3c9a Mon Sep 17 00:00:00 2001 From: Anton Tananaev Date: Tue, 8 Mar 2016 23:19:30 +1300 Subject: Refactor device permissions resource --- .../api/resource/DevicePermissionResource.java | 52 ++++++++++++++++++++++ .../traccar/api/resource/PermissionResource.java | 52 ---------------------- src/org/traccar/web/WebServer.java | 4 +- web/app/view/UserDevicesController.js | 4 +- 4 files changed, 56 insertions(+), 56 deletions(-) create mode 100644 src/org/traccar/api/resource/DevicePermissionResource.java delete mode 100644 src/org/traccar/api/resource/PermissionResource.java (limited to 'web/app/view') diff --git a/src/org/traccar/api/resource/DevicePermissionResource.java b/src/org/traccar/api/resource/DevicePermissionResource.java new file mode 100644 index 000000000..f77375cba --- /dev/null +++ b/src/org/traccar/api/resource/DevicePermissionResource.java @@ -0,0 +1,52 @@ +/* + * Copyright 2015 - 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. + */ +package org.traccar.api.resource; + +import org.traccar.Context; +import org.traccar.api.BaseResource; +import org.traccar.model.DevicePermission; + +import javax.ws.rs.Consumes; +import javax.ws.rs.DELETE; +import javax.ws.rs.POST; +import javax.ws.rs.Path; +import javax.ws.rs.Produces; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; +import java.sql.SQLException; + +@Path("permissions/devices") +@Produces(MediaType.APPLICATION_JSON) +@Consumes(MediaType.APPLICATION_JSON) +public class DevicePermissionResource extends BaseResource { + + @POST + public Response add(DevicePermission entity) throws SQLException { + Context.getPermissionsManager().checkAdmin(getUserId()); + Context.getDataManager().linkDevice(entity.getUserId(), entity.getDeviceId()); + Context.getPermissionsManager().refresh(); + return Response.ok(entity).build(); + } + + @DELETE + public Response remove(DevicePermission entity) throws SQLException { + Context.getPermissionsManager().checkAdmin(getUserId()); + Context.getDataManager().unlinkDevice(entity.getUserId(), entity.getDeviceId()); + Context.getPermissionsManager().refresh(); + return Response.noContent().build(); + } + +} diff --git a/src/org/traccar/api/resource/PermissionResource.java b/src/org/traccar/api/resource/PermissionResource.java deleted file mode 100644 index 7cf2b4ed9..000000000 --- a/src/org/traccar/api/resource/PermissionResource.java +++ /dev/null @@ -1,52 +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. - */ -package org.traccar.api.resource; - -import org.traccar.Context; -import org.traccar.api.BaseResource; -import org.traccar.model.DevicePermission; - -import javax.ws.rs.Consumes; -import javax.ws.rs.DELETE; -import javax.ws.rs.POST; -import javax.ws.rs.Path; -import javax.ws.rs.Produces; -import javax.ws.rs.core.MediaType; -import javax.ws.rs.core.Response; -import java.sql.SQLException; - -@Path("permissions") -@Produces(MediaType.APPLICATION_JSON) -@Consumes(MediaType.APPLICATION_JSON) -public class PermissionResource extends BaseResource { - - @POST - public Response add(DevicePermission entity) throws SQLException { - Context.getPermissionsManager().checkAdmin(getUserId()); - Context.getDataManager().linkDevice(entity.getUserId(), entity.getDeviceId()); - Context.getPermissionsManager().refresh(); - return Response.ok(entity).build(); - } - - @DELETE - public Response remove(DevicePermission entity) throws SQLException { - Context.getPermissionsManager().checkAdmin(getUserId()); - Context.getDataManager().unlinkDevice(entity.getUserId(), entity.getDeviceId()); - Context.getPermissionsManager().refresh(); - return Response.noContent().build(); - } - -} diff --git a/src/org/traccar/web/WebServer.java b/src/org/traccar/web/WebServer.java index d6cd19d87..ed006f8cb 100644 --- a/src/org/traccar/web/WebServer.java +++ b/src/org/traccar/web/WebServer.java @@ -42,7 +42,7 @@ import org.traccar.api.ResourceErrorHandler; import org.traccar.api.SecurityRequestFilter; import org.traccar.api.resource.CommandResource; import org.traccar.api.resource.DeviceResource; -import org.traccar.api.resource.PermissionResource; +import org.traccar.api.resource.DevicePermissionResource; import org.traccar.api.resource.PositionResource; import org.traccar.api.resource.ServerResource; import org.traccar.api.resource.SessionResource; @@ -140,7 +140,7 @@ public class WebServer { resourceConfig.register(SecurityRequestFilter.class); resourceConfig.register(CorsResponseFilter.class); resourceConfig.registerClasses(ServerResource.class, SessionResource.class, CommandResource.class, - PermissionResource.class, DeviceResource.class, UserResource.class, PositionResource.class); + DevicePermissionResource.class, DeviceResource.class, UserResource.class, PositionResource.class); servletHandler.addServlet(new ServletHolder(new ServletContainer(resourceConfig)), "/*"); handlers.addHandler(servletHandler); diff --git a/web/app/view/UserDevicesController.js b/web/app/view/UserDevicesController.js index 4f013fd64..e5dbbdbb8 100644 --- a/web/app/view/UserDevicesController.js +++ b/web/app/view/UserDevicesController.js @@ -47,7 +47,7 @@ Ext.define('Traccar.view.UserDevicesController', { onBeforeSelect: function (object, record, index) { Ext.Ajax.request({ scope: this, - url: '/api/permissions', + url: '/api/permissions/devices', jsonData: { userId: this.userId, deviceId: record.getData().id @@ -64,7 +64,7 @@ Ext.define('Traccar.view.UserDevicesController', { Ext.Ajax.request({ scope: this, method: 'DELETE', - url: '/api/permissions', + url: '/api/permissions/devices', jsonData: { userId: this.userId, deviceId: record.getData().id -- cgit v1.2.3 From 1750a8b5353a69f3b1805757f3b31150260d45f8 Mon Sep 17 00:00:00 2001 From: Anton Tananaev Date: Wed, 9 Mar 2016 20:34:11 +1300 Subject: Add groups list window component --- src/org/traccar/web/WebServer.java | 5 ++- web/app/view/DeviceDialog.js | 2 +- web/app/view/Devices.js | 2 +- web/app/view/Groups.js | 44 ++++++++++++++++++++++ web/app/view/GroupsController.js | 67 ++++++++++++++++++++++++++++++++++ web/app/view/Register.js | 2 +- web/app/view/SettingsMenu.js | 3 ++ web/app/view/SettingsMenuController.js | 11 ++++++ web/app/view/UserDevices.js | 2 +- web/app/view/UserDialog.js | 2 +- web/app/view/Users.js | 2 +- web/l10n/en.json | 4 +- 12 files changed, 137 insertions(+), 9 deletions(-) create mode 100644 web/app/view/Groups.js create mode 100644 web/app/view/GroupsController.js (limited to 'web/app/view') diff --git a/src/org/traccar/web/WebServer.java b/src/org/traccar/web/WebServer.java index ed006f8cb..4e764d5d6 100644 --- a/src/org/traccar/web/WebServer.java +++ b/src/org/traccar/web/WebServer.java @@ -43,6 +43,8 @@ import org.traccar.api.SecurityRequestFilter; import org.traccar.api.resource.CommandResource; import org.traccar.api.resource.DeviceResource; import org.traccar.api.resource.DevicePermissionResource; +import org.traccar.api.resource.GroupPermissionResource; +import org.traccar.api.resource.GroupResource; import org.traccar.api.resource.PositionResource; import org.traccar.api.resource.ServerResource; import org.traccar.api.resource.SessionResource; @@ -140,7 +142,8 @@ public class WebServer { resourceConfig.register(SecurityRequestFilter.class); resourceConfig.register(CorsResponseFilter.class); resourceConfig.registerClasses(ServerResource.class, SessionResource.class, CommandResource.class, - DevicePermissionResource.class, DeviceResource.class, UserResource.class, PositionResource.class); + GroupPermissionResource.class, DevicePermissionResource.class, UserResource.class, + GroupResource.class, DeviceResource.class, PositionResource.class); servletHandler.addServlet(new ServletHolder(new ServletContainer(resourceConfig)), "/*"); handlers.addHandler(servletHandler); diff --git a/web/app/view/DeviceDialog.js b/web/app/view/DeviceDialog.js index c42af95d0..318ac4ffe 100644 --- a/web/app/view/DeviceDialog.js +++ b/web/app/view/DeviceDialog.js @@ -29,7 +29,7 @@ Ext.define('Traccar.view.DeviceDialog', { items: [{ xtype: 'textfield', name: 'name', - fieldLabel: Strings.deviceName, + fieldLabel: Strings.sharedName, allowBlank: false }, { xtype: 'textfield', diff --git a/web/app/view/Devices.js b/web/app/view/Devices.js index 66c4e813b..5712feb81 100644 --- a/web/app/view/Devices.js +++ b/web/app/view/Devices.js @@ -57,7 +57,7 @@ Ext.define('Traccar.view.Devices', { }, columns: [{ - text: Strings.deviceName, + text: Strings.sharedName, dataIndex: 'name', flex: 1 }, { diff --git a/web/app/view/Groups.js b/web/app/view/Groups.js new file mode 100644 index 000000000..8404c59a9 --- /dev/null +++ b/web/app/view/Groups.js @@ -0,0 +1,44 @@ +/* + * 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.Groups', { + extend: 'Ext.grid.Panel', + xtype: 'groupsView', + + requires: [ + 'Traccar.view.GroupsController', + 'Traccar.view.EditToolbar' + ], + + controller: 'groups', + store: 'Groups', + + selType: 'rowmodel', + + tbar: { + xtype: 'editToolbar' + }, + + listeners: { + selectionchange: 'onSelectionChange' + }, + + columns: [{ + text: Strings.sharedName, + dataIndex: 'name', + flex: 1 + }] +}); diff --git a/web/app/view/GroupsController.js b/web/app/view/GroupsController.js new file mode 100644 index 000000000..619e0c50e --- /dev/null +++ b/web/app/view/GroupsController.js @@ -0,0 +1,67 @@ +/* + * 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.GroupsController', { + extend: 'Ext.app.ViewController', + alias: 'controller.groups', + + init: function () { + Ext.getStore('Groups').load(); + }, + + onAddClick: function () { + /*var user, dialog; + user = Ext.create('Traccar.model.User'); + dialog = Ext.create('Traccar.view.UserDialog'); + dialog.down('form').loadRecord(user); + dialog.show();*/ + }, + + onEditClick: function () { + /*var user, dialog; + user = this.getView().getSelectionModel().getSelection()[0]; + dialog = Ext.create('Traccar.view.UserDialog'); + dialog.down('form').loadRecord(user); + dialog.show();*/ + }, + + onRemoveClick: function () { + /*var user = this.getView().getSelectionModel().getSelection()[0]; + Ext.Msg.show({ + title: Strings.settingsUser, + message: Strings.sharedRemoveConfirm, + buttons: Ext.Msg.YESNO, + buttonText: { + yes: Strings.sharedRemove, + no: Strings.sharedCancel + }, + fn: function (btn) { + var store = Ext.getStore('Users'); + if (btn === 'yes') { + store.remove(user); + store.sync(); + } + } + });*/ + }, + + onSelectionChange: function (selected) { + var disabled = selected.length > 0; + this.lookupReference('toolbarEditButton').setDisabled(disabled); + this.lookupReference('toolbarRemoveButton').setDisabled(disabled); + this.lookupReference('userDevicesButton').setDisabled(disabled); + } +}); diff --git a/web/app/view/Register.js b/web/app/view/Register.js index 7c2881d62..198e10b8f 100644 --- a/web/app/view/Register.js +++ b/web/app/view/Register.js @@ -33,7 +33,7 @@ Ext.define('Traccar.view.Register', { items: [{ xtype: 'textfield', name: 'name', - fieldLabel: Strings.userName, + fieldLabel: Strings.sharedName, allowBlank: false }, { xtype: 'textfield', diff --git a/web/app/view/SettingsMenu.js b/web/app/view/SettingsMenu.js index c828b37af..1ada94359 100644 --- a/web/app/view/SettingsMenu.js +++ b/web/app/view/SettingsMenu.js @@ -31,6 +31,9 @@ Ext.define('Traccar.view.SettingsMenu', { items: [{ text: Strings.settingsUser, handler: 'onUserClick' + }, { + text: Strings.settingsGroups, + handler: 'onGroupsClick' }, { text: Strings.settingsServer, hidden: true, diff --git a/web/app/view/SettingsMenuController.js b/web/app/view/SettingsMenuController.js index c52f0a75e..6d767e3a9 100644 --- a/web/app/view/SettingsMenuController.js +++ b/web/app/view/SettingsMenuController.js @@ -23,6 +23,7 @@ Ext.define('Traccar.view.SettingsMenuController', { 'Traccar.view.UserDialog', 'Traccar.view.ServerDialog', 'Traccar.view.Users', + 'Traccar.view.Groups', 'Traccar.view.BaseWindow' ], @@ -39,6 +40,16 @@ Ext.define('Traccar.view.SettingsMenuController', { dialog.show(); }, + onGroupsClick: function () { + Ext.create('Traccar.view.BaseWindow', { + title: Strings.settingsGroups, + modal: false, + items: { + xtype: 'groupsView' + } + }).show(); + }, + onServerClick: function () { var dialog = Ext.create('Traccar.view.ServerDialog'); dialog.down('form').loadRecord(Traccar.app.getServer()); diff --git a/web/app/view/UserDevices.js b/web/app/view/UserDevices.js index f9ab48266..f42f7c1bb 100644 --- a/web/app/view/UserDevices.js +++ b/web/app/view/UserDevices.js @@ -37,7 +37,7 @@ Ext.define('Traccar.view.UserDevices', { }, columns: [{ - text: Strings.deviceName, + text: Strings.sharedName, dataIndex: 'name', flex: 1 }, { text: Strings.deviceIdentifier, diff --git a/web/app/view/UserDialog.js b/web/app/view/UserDialog.js index 783ddd159..c1ed2fece 100644 --- a/web/app/view/UserDialog.js +++ b/web/app/view/UserDialog.js @@ -30,7 +30,7 @@ Ext.define('Traccar.view.UserDialog', { items: [{ xtype: 'textfield', name: 'name', - fieldLabel: Strings.userName + fieldLabel: Strings.sharedName }, { xtype: 'textfield', name: 'email', diff --git a/web/app/view/Users.js b/web/app/view/Users.js index f4ef332b4..684931559 100644 --- a/web/app/view/Users.js +++ b/web/app/view/Users.js @@ -43,7 +43,7 @@ Ext.define('Traccar.view.Users', { }, columns: [{ - text: Strings.userName, + text: Strings.sharedName, dataIndex: 'name', flex: 1 }, { diff --git a/web/l10n/en.json b/web/l10n/en.json index ec8018863..259d54b1a 100644 --- a/web/l10n/en.json +++ b/web/l10n/en.json @@ -13,10 +13,10 @@ "sharedHour": "Hour", "sharedMinute": "Minute", "sharedSecond": "Second", + "sharedName": "Name", "errorTitle": "Error", "errorUnknown": "Unknown error", "errorConnection": "Connection error", - "userName": "Name", "userEmail": "Email", "userPassword": "Password", "userAdmin": "Admin", @@ -30,13 +30,13 @@ "devicesAndState": "Devices and State", "deviceDialog": "Device", "deviceTitle": "Devices", - "deviceName": "Name", "deviceIdentifier": "Identifier", "deviceLastUpdate": "Last Update", "deviceCommand": "Command", "deviceFollow": "Follow", "settingsTitle": "Settings", "settingsUser": "Account", + "settingsGroups": "Groups", "settingsServer": "Server", "settingsUsers": "Users", "settingsDistanceUnit": "Distance", -- cgit v1.2.3 From c22cc6982a18eb3601fdc8fb8447b5a2ba73690a Mon Sep 17 00:00:00 2001 From: Anton Tananaev Date: Wed, 9 Mar 2016 21:48:59 +1300 Subject: Implement group editing dialog --- debug.xml | 2 +- src/org/traccar/database/PermissionsManager.java | 1 + web/app/view/GroupDialog.js | 36 ++++++++++++++++++++++++ web/app/view/GroupsController.js | 32 ++++++++++----------- web/l10n/en.json | 1 + 5 files changed, 55 insertions(+), 17 deletions(-) create mode 100644 web/app/view/GroupDialog.js (limited to 'web/app/view') diff --git a/debug.xml b/debug.xml index 2266a1eea..ce7c3190a 100644 --- a/debug.xml +++ b/debug.xml @@ -14,7 +14,7 @@ web true true - 10000 + 30000 true nominatim diff --git a/src/org/traccar/database/PermissionsManager.java b/src/org/traccar/database/PermissionsManager.java index 5d6430764..022691b1b 100644 --- a/src/org/traccar/database/PermissionsManager.java +++ b/src/org/traccar/database/PermissionsManager.java @@ -59,6 +59,7 @@ public class PermissionsManager { public final void refresh() { users.clear(); + groupPermissions.clear(); devicePermissions.clear(); try { server = dataManager.getServer(); diff --git a/web/app/view/GroupDialog.js b/web/app/view/GroupDialog.js new file mode 100644 index 000000000..a34e33aa0 --- /dev/null +++ b/web/app/view/GroupDialog.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.view.GroupDialog', { + extend: 'Traccar.view.BaseEditDialog', + + requires: [ + 'Traccar.view.BaseEditDialogController' + ], + + controller: 'baseEditDialog', + title: Strings.groupDialog, + + items: { + xtype: 'form', + items: [{ + xtype: 'textfield', + name: 'name', + fieldLabel: Strings.sharedName, + allowBlank: false + }] + } +}); diff --git a/web/app/view/GroupsController.js b/web/app/view/GroupsController.js index 619e0c50e..34b259658 100644 --- a/web/app/view/GroupsController.js +++ b/web/app/view/GroupsController.js @@ -23,25 +23,26 @@ Ext.define('Traccar.view.GroupsController', { }, onAddClick: function () { - /*var user, dialog; - user = Ext.create('Traccar.model.User'); - dialog = Ext.create('Traccar.view.UserDialog'); - dialog.down('form').loadRecord(user); - dialog.show();*/ + var group, dialog; + group = Ext.create('Traccar.model.Group'); + group.store = this.getView().getStore(); + dialog = Ext.create('Traccar.view.GroupDialog'); + dialog.down('form').loadRecord(group); + dialog.show(); }, onEditClick: function () { - /*var user, dialog; - user = this.getView().getSelectionModel().getSelection()[0]; - dialog = Ext.create('Traccar.view.UserDialog'); - dialog.down('form').loadRecord(user); - dialog.show();*/ + var group, dialog; + group = this.getView().getSelectionModel().getSelection()[0]; + dialog = Ext.create('Traccar.view.GroupDialog'); + dialog.down('form').loadRecord(group); + dialog.show(); }, onRemoveClick: function () { - /*var user = this.getView().getSelectionModel().getSelection()[0]; + var group = this.getView().getSelectionModel().getSelection()[0]; Ext.Msg.show({ - title: Strings.settingsUser, + title: Strings.groupDialog, message: Strings.sharedRemoveConfirm, buttons: Ext.Msg.YESNO, buttonText: { @@ -49,19 +50,18 @@ Ext.define('Traccar.view.GroupsController', { no: Strings.sharedCancel }, fn: function (btn) { - var store = Ext.getStore('Users'); + var store = Ext.getStore('Groups'); if (btn === 'yes') { - store.remove(user); + store.remove(group); store.sync(); } } - });*/ + }); }, onSelectionChange: function (selected) { var disabled = selected.length > 0; this.lookupReference('toolbarEditButton').setDisabled(disabled); this.lookupReference('toolbarRemoveButton').setDisabled(disabled); - this.lookupReference('userDevicesButton').setDisabled(disabled); } }); diff --git a/web/l10n/en.json b/web/l10n/en.json index 259d54b1a..715dce3a7 100644 --- a/web/l10n/en.json +++ b/web/l10n/en.json @@ -34,6 +34,7 @@ "deviceLastUpdate": "Last Update", "deviceCommand": "Command", "deviceFollow": "Follow", + "groupDialog": "Group", "settingsTitle": "Settings", "settingsUser": "Account", "settingsGroups": "Groups", -- cgit v1.2.3 From 71c5871dc0221d7a82851f2c3ca3cd2b594982f2 Mon Sep 17 00:00:00 2001 From: Anton Tananaev Date: Wed, 9 Mar 2016 22:03:30 +1300 Subject: Add window to link users and groups --- web/app/Application.js | 1 + web/app/store/AllGroups.js | 28 +++++++++++++ web/app/view/UserDevices.js | 6 ++- web/app/view/UserGroups.js | 44 ++++++++++++++++++++ web/app/view/UserGroupsController.js | 79 ++++++++++++++++++++++++++++++++++++ web/app/view/Users.js | 5 +++ web/app/view/UsersController.js | 12 ++++++ 7 files changed, 173 insertions(+), 2 deletions(-) create mode 100644 web/app/store/AllGroups.js create mode 100644 web/app/view/UserGroups.js create mode 100644 web/app/view/UserGroupsController.js (limited to 'web/app/view') diff --git a/web/app/Application.js b/web/app/Application.js index f81e87d5f..69ce8f891 100644 --- a/web/app/Application.js +++ b/web/app/Application.js @@ -36,6 +36,7 @@ Ext.define('Traccar.Application', { stores: [ 'Groups', 'Devices', + 'AllGroups', 'AllDevices', 'Positions', 'LatestPositions', diff --git a/web/app/store/AllGroups.js b/web/app/store/AllGroups.js new file mode 100644 index 000000000..880ccc8f5 --- /dev/null +++ b/web/app/store/AllGroups.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.AllGroups', { + extend: 'Ext.data.Store', + model: 'Traccar.model.Group', + + proxy: { + type: 'rest', + url: '/api/groups', + extraParams: { + all: true + } + } +}); diff --git a/web/app/view/UserDevices.js b/web/app/view/UserDevices.js index f42f7c1bb..fe16dd93a 100644 --- a/web/app/view/UserDevices.js +++ b/web/app/view/UserDevices.js @@ -38,9 +38,11 @@ Ext.define('Traccar.view.UserDevices', { columns: [{ text: Strings.sharedName, - dataIndex: 'name', flex: 1 + dataIndex: 'name', + flex: 1 }, { text: Strings.deviceIdentifier, - dataIndex: 'uniqueId', flex: 1 + dataIndex: 'uniqueId', + flex: 1 }] }); diff --git a/web/app/view/UserGroups.js b/web/app/view/UserGroups.js new file mode 100644 index 000000000..cb0f0bd5d --- /dev/null +++ b/web/app/view/UserGroups.js @@ -0,0 +1,44 @@ +/* + * 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.UserGroups', { + extend: 'Ext.grid.Panel', + xtype: 'userGroupsView', + + requires: [ + 'Traccar.view.UserGroupsController' + ], + + controller: 'userGroups', + store: 'AllGroups', + + 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/UserGroupsController.js b/web/app/view/UserGroupsController.js new file mode 100644 index 000000000..662508f0a --- /dev/null +++ b/web/app/view/UserGroupsController.js @@ -0,0 +1,79 @@ +/* + * 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.UserGroupsController', { + extend: 'Ext.app.ViewController', + alias: 'controller.userGroups', + + 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.Groups'); + + 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/groups', + jsonData: { + userId: this.userId, + groupId: 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/groups', + jsonData: { + userId: this.userId, + groupId: record.getData().id + }, + callback: function (options, success, response) { + if (!success) { + Traccar.app.showError(response); + } + } + }); + } +}); diff --git a/web/app/view/Users.js b/web/app/view/Users.js index 684931559..408a70885 100644 --- a/web/app/view/Users.js +++ b/web/app/view/Users.js @@ -35,6 +35,11 @@ Ext.define('Traccar.view.Users', { disabled: true, handler: 'onDevicesClick', reference: 'userDevicesButton' + }, { + text: Strings.settingsGroups, + disabled: true, + handler: 'onGroupsClick', + reference: 'userGroupsButton' }] }, diff --git a/web/app/view/UsersController.js b/web/app/view/UsersController.js index 3d0e813e8..6e8921026 100644 --- a/web/app/view/UsersController.js +++ b/web/app/view/UsersController.js @@ -75,10 +75,22 @@ Ext.define('Traccar.view.UsersController', { }).show(); }, + onGroupsClick: function () { + var user = this.getView().getSelectionModel().getSelection()[0]; + Ext.create('Traccar.view.BaseWindow', { + title: Strings.settingsGroups, + items: { + xtype: 'userDevicesView', + user: user + } + }).show(); + }, + onSelectionChange: function (selected) { var disabled = selected.length > 0; this.lookupReference('toolbarEditButton').setDisabled(disabled); this.lookupReference('toolbarRemoveButton').setDisabled(disabled); this.lookupReference('userDevicesButton').setDisabled(disabled); + this.lookupReference('userGroupsButton').setDisabled(disabled); } }); -- cgit v1.2.3 From 5634860cbe5c43007df354acba79d5051c1eb987 Mon Sep 17 00:00:00 2001 From: Anton Tananaev Date: Wed, 9 Mar 2016 23:13:06 +1300 Subject: Add parent field for device and group --- debug.xml | 4 ++-- src/org/traccar/model/Group.java | 2 -- web/app/controller/Root.js | 1 + web/app/model/Device.js | 3 +++ web/app/model/Group.js | 3 +++ web/app/view/DeviceDialog.js | 7 +++++++ web/app/view/GroupDialog.js | 7 +++++++ web/app/view/GroupsController.js | 4 ---- web/l10n/en.json | 1 + 9 files changed, 24 insertions(+), 8 deletions(-) (limited to 'web/app/view') diff --git a/debug.xml b/debug.xml index ce7c3190a..45c475a91 100644 --- a/debug.xml +++ b/debug.xml @@ -130,7 +130,7 @@ - UPDATE devices SET name = :name, uniqueId = :uniqueId WHERE id = :id; + UPDATE devices SET name = :name, uniqueId = :uniqueId, groupId = :groupId WHERE id = :id; @@ -158,7 +158,7 @@ - UPDATE groups SET name = :name WHERE id = :id; + UPDATE groups SET name = :name, groupId = :groupId WHERE id = :id; diff --git a/src/org/traccar/model/Group.java b/src/org/traccar/model/Group.java index 24f7973c8..00f2b2cfc 100644 --- a/src/org/traccar/model/Group.java +++ b/src/org/traccar/model/Group.java @@ -15,8 +15,6 @@ */ package org.traccar.model; -import java.util.Date; - public class Group { private long id; diff --git a/web/app/controller/Root.js b/web/app/controller/Root.js index a1026d3c5..5bd567619 100644 --- a/web/app/controller/Root.js +++ b/web/app/controller/Root.js @@ -73,6 +73,7 @@ Ext.define('Traccar.controller.Root', { }, loadApp: function () { + Ext.getStore('Groups').load(); Ext.getStore('Devices').load(); Ext.get('attribution').remove(); if (this.isPhone) { diff --git a/web/app/model/Device.js b/web/app/model/Device.js index 709d51953..588d53c1f 100644 --- a/web/app/model/Device.js +++ b/web/app/model/Device.js @@ -34,5 +34,8 @@ Ext.define('Traccar.model.Device', { name: 'lastUpdate', type: 'date', dateFormat: 'c' + }, { + name: 'groupId', + type: 'int' }] }); diff --git a/web/app/model/Group.js b/web/app/model/Group.js index 22e160315..a28897feb 100644 --- a/web/app/model/Group.js +++ b/web/app/model/Group.js @@ -24,5 +24,8 @@ Ext.define('Traccar.model.Group', { }, { name: 'name', type: 'string' + }, { + name: 'groupId', + type: 'int' }] }); diff --git a/web/app/view/DeviceDialog.js b/web/app/view/DeviceDialog.js index 318ac4ffe..4a22ca008 100644 --- a/web/app/view/DeviceDialog.js +++ b/web/app/view/DeviceDialog.js @@ -36,6 +36,13 @@ Ext.define('Traccar.view.DeviceDialog', { name: 'uniqueId', fieldLabel: Strings.deviceIdentifier, allowBlank: false + }, { + xtype: 'combobox', + name: 'groupId', + fieldLabel: Strings.groupParent, + store: 'Groups', + displayField: 'name', + valueField: 'id' }] } }); diff --git a/web/app/view/GroupDialog.js b/web/app/view/GroupDialog.js index a34e33aa0..2cca61ef5 100644 --- a/web/app/view/GroupDialog.js +++ b/web/app/view/GroupDialog.js @@ -31,6 +31,13 @@ Ext.define('Traccar.view.GroupDialog', { name: 'name', fieldLabel: Strings.sharedName, allowBlank: false + }, { + xtype: 'combobox', + name: 'groupId', + fieldLabel: Strings.groupParent, + store: 'Groups', + displayField: 'name', + valueField: 'id' }] } }); diff --git a/web/app/view/GroupsController.js b/web/app/view/GroupsController.js index 34b259658..6cc568ea2 100644 --- a/web/app/view/GroupsController.js +++ b/web/app/view/GroupsController.js @@ -18,10 +18,6 @@ Ext.define('Traccar.view.GroupsController', { extend: 'Ext.app.ViewController', alias: 'controller.groups', - init: function () { - Ext.getStore('Groups').load(); - }, - onAddClick: function () { var group, dialog; group = Ext.create('Traccar.model.Group'); diff --git a/web/l10n/en.json b/web/l10n/en.json index 715dce3a7..cabd76362 100644 --- a/web/l10n/en.json +++ b/web/l10n/en.json @@ -35,6 +35,7 @@ "deviceCommand": "Command", "deviceFollow": "Follow", "groupDialog": "Group", + "groupParent": "Group", "settingsTitle": "Settings", "settingsUser": "Account", "settingsGroups": "Groups", -- cgit v1.2.3 From eae47b154590838b58f189fda0c43ab37faebe12 Mon Sep 17 00:00:00 2001 From: Anton Tananaev Date: Fri, 11 Mar 2016 21:18:31 +1300 Subject: Link user group view to button --- web/app/view/UsersController.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'web/app/view') diff --git a/web/app/view/UsersController.js b/web/app/view/UsersController.js index 6e8921026..c48f57cf4 100644 --- a/web/app/view/UsersController.js +++ b/web/app/view/UsersController.js @@ -21,6 +21,7 @@ Ext.define('Traccar.view.UsersController', { requires: [ 'Traccar.view.UserDialog', 'Traccar.view.UserDevices', + 'Traccar.view.UserGroups', 'Traccar.view.BaseWindow' ], @@ -80,7 +81,7 @@ Ext.define('Traccar.view.UsersController', { Ext.create('Traccar.view.BaseWindow', { title: Strings.settingsGroups, items: { - xtype: 'userDevicesView', + xtype: 'userGroupsView', user: user } }).show(); -- cgit v1.2.3 From 525d5b80d792e742e6d101ce85531294f5dc5300 Mon Sep 17 00:00:00 2001 From: Anton Tananaev Date: Sat, 12 Mar 2016 01:09:25 +1300 Subject: Replace devices grid with tree view --- web/app/view/Devices.js | 45 ++++++++++++++++---------- web/app/view/DevicesController.js | 66 ++++++++++++++++++++++++++++++++++----- 2 files changed, 87 insertions(+), 24 deletions(-) (limited to 'web/app/view') diff --git a/web/app/view/Devices.js b/web/app/view/Devices.js index 5712feb81..ebe3ca195 100644 --- a/web/app/view/Devices.js +++ b/web/app/view/Devices.js @@ -1,5 +1,5 @@ /* - * Copyright 2015 Anton Tananaev (anton.tananaev@gmail.com) + * Copyright 2015 - 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. @@ -15,7 +15,7 @@ */ Ext.define('Traccar.view.Devices', { - extend: 'Ext.grid.Panel', + extend: 'Ext.tree.Panel', xtype: 'devicesView', requires: [ @@ -25,7 +25,17 @@ Ext.define('Traccar.view.Devices', { ], controller: 'devices', - store: 'Devices', + rootVisible: false, + store: { + type: 'tree', + parentIdProperty: 'groupId', + proxy: { + type: 'memory', + reader: { + type: 'json' + } + } + }, title: Strings.deviceTitle, selType: 'rowmodel', @@ -53,10 +63,12 @@ Ext.define('Traccar.view.Devices', { }, listeners: { - selectionchange: 'onSelectionChange' + selectionchange: 'onSelectionChange', + beforeselect: 'onBeforeSelect' }, columns: [{ + xtype: 'treecolumn', text: Strings.sharedName, dataIndex: 'name', flex: 1 @@ -65,19 +77,20 @@ Ext.define('Traccar.view.Devices', { dataIndex: 'lastUpdate', flex: 1, renderer: function (value, metaData, record) { - var status = record.get('status'); - switch (status) { - case 'online': - metaData.tdCls = 'status-color-online'; - break; - case 'offline': - metaData.tdCls = 'status-color-offline'; - break; - default: - metaData.tdCls = 'status-color-unknown'; - break; + if (record.get('leaf')) { + switch (record.get('status')) { + case 'online': + metaData.tdCls = 'status-color-online'; + break; + case 'offline': + metaData.tdCls = 'status-color-offline'; + break; + default: + metaData.tdCls = 'status-color-unknown'; + break; + } + return Ext.Date.format(value, Traccar.Style.dateTimeFormat); } - return Ext.Date.format(value, Traccar.Style.dateTimeFormat); } }] diff --git a/web/app/view/DevicesController.js b/web/app/view/DevicesController.js index 8c6c72725..4d8231d94 100644 --- a/web/app/view/DevicesController.js +++ b/web/app/view/DevicesController.js @@ -1,5 +1,5 @@ /* - * Copyright 2015 Anton Tananaev (anton.tananaev@gmail.com) + * Copyright 2015 - 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. @@ -30,9 +30,54 @@ Ext.define('Traccar.view.DevicesController', { selectDevice: 'selectDevice', selectReport: 'selectReport' } + }, + store: { + '#Groups': { + datachanged: 'storeUpdate', + update: 'storeUpdate' + }, + '#Devices': { + datachanged: 'storeUpdate', + update: 'storeUpdate' + } } } }, + + storeUpdate: function () { + var nodes = []; + Ext.getStore('Groups').each(function (record) { + var groupId, node = { + id: 'g' + record.get('id'), + original: record, + name: record.get('name') + }; + groupId = record.get('groupId'); + if (groupId !== 0) { + node.groupId = 'g' + groupId; + } + nodes.push(node); + }, this); + Ext.getStore('Devices').each(function (record) { + var groupId, node = { + id: 'd' + record.get('id'), + original: record, + name: record.get('name'), + status: record.get('status'), + lastUpdate: record.get('lastUpdate'), + leaf: true + }; + groupId = record.get('groupId'); + if (groupId !== 0) { + node.groupId = 'g' + groupId; + } + nodes.push(node); + }, this); + this.getView().getStore().getProxy().setData(nodes); + this.getView().getStore().load(); + this.getView().expandAll(); + }, + init: function () { var readonly = Traccar.app.getServer().get('readonly') && !Traccar.app.getUser().get('admin'); this.lookupReference('toolbarAddButton').setVisible(!readonly); @@ -43,7 +88,7 @@ Ext.define('Traccar.view.DevicesController', { onAddClick: function () { var device, dialog; device = Ext.create('Traccar.model.Device'); - device.store = this.getView().getStore(); + device.store = Ext.getStore('Devices'); dialog = Ext.create('Traccar.view.DeviceDialog'); dialog.down('form').loadRecord(device); dialog.show(); @@ -51,14 +96,14 @@ Ext.define('Traccar.view.DevicesController', { onEditClick: function () { var device, dialog; - device = this.getView().getSelectionModel().getSelection()[0]; + device = this.getView().getSelectionModel().getSelection()[0].get('original'); dialog = Ext.create('Traccar.view.DeviceDialog'); dialog.down('form').loadRecord(device); dialog.show(); }, onRemoveClick: function () { - var device = this.getView().getSelectionModel().getSelection()[0]; + var device = this.getView().getSelectionModel().getSelection()[0].get('original'); Ext.Msg.show({ title: Strings.deviceDialog, message: Strings.sharedRemoveConfirm, @@ -80,7 +125,7 @@ Ext.define('Traccar.view.DevicesController', { onCommandClick: function () { var device, command, dialog; - device = this.getView().getSelectionModel().getSelection()[0]; + device = this.getView().getSelectionModel().getSelection()[0].get('original'); command = Ext.create('Traccar.model.Command'); command.set('deviceId', device.get('id')); dialog = Ext.create('Traccar.view.CommandDialog'); @@ -89,17 +134,22 @@ Ext.define('Traccar.view.DevicesController', { }, onSelectionChange: function (selected) { - var empty = selected.getCount() === 0; + var empty = selected.getCount() === 0 || !this.getView().getSelectionModel().getSelection()[0].get('leaf'); this.lookupReference('toolbarEditButton').setDisabled(empty); this.lookupReference('toolbarRemoveButton').setDisabled(empty); this.lookupReference('deviceCommandButton').setDisabled(empty); if (!empty) { - this.fireEvent('selectDevice', selected.getLastSelected(), true); + this.fireEvent('selectDevice', selected.getLastSelected().get('original'), true); } }, + onBeforeSelect: function (row, record) { + return record.get('leaf'); + }, + selectDevice: function (device, center) { - this.getView().getSelectionModel().select([device], false, true); + var node = this.getView().getStore().getNodeById('d' + device.get('id')); + this.getView().getSelectionModel().select([node], false, true); }, selectReport: function (position) { -- cgit v1.2.3