diff --git a/lib/db/api.js b/lib/db/api.js index c1a5b37273..ff0a472c22 100644 --- a/lib/db/api.js +++ b/lib/db/api.js @@ -1,5 +1,5 @@ /** -* Copyright © 2019,2023 contains code contributed by Orange SA, authors: Denis Barbaron - Licensed under the Apache license 2.0 +* Copyright © 2019-2024 contains code contributed by Orange SA, authors: Denis Barbaron - Licensed under the Apache license 2.0 **/ var r = require('rethinkdb') @@ -826,6 +826,31 @@ dbapi.updateDefaultUserGroupsQuotas = function(email, duration, number, repetiti }}}, {returnChanges: true})) } +dbapi.updateDeviceGroupName = function(serial, group) { + return db.run(r.table('devices').get(serial).update({group: { + name: + r.branch( + r.expr(apiutil.isOriginGroup(group.class)).eq(false) + , r.branch( + r.expr(group.isActive).eq(true) + , group.name + , r.row('group')('name') + ) + , r.branch( + r.row('group')('origin').eq(r.row('group')('id')) + , group.name + , r.row('group')('name') + ) + ) + , originName: + r.branch( + r.expr(apiutil.isOriginGroup(group.class)).eq(true) + , group.name + , r.row('group')('originName') + ) + }})) +} + dbapi.updateDeviceCurrentGroupFromOrigin = function(serial) { return db.run(r.table('devices').get(serial)).then(function(device) { return db.run(r.table('groups').get(device.group.origin)).then(function(group) { diff --git a/lib/units/api/controllers/groups.js b/lib/units/api/controllers/groups.js index 638669984d..cc28b80dba 100644 --- a/lib/units/api/controllers/groups.js +++ b/lib/units/api/controllers/groups.js @@ -1,5 +1,5 @@ /** -* Copyright © 2019 code initially contributed by Orange SA, authors: Denis Barbaron - Licensed under the Apache license 2.0 +* Copyright © 2019-2024 code initially contributed by Orange SA, authors: Denis Barbaron - Licensed under the Apache license 2.0 **/ const _ = require('lodash') @@ -782,6 +782,35 @@ function updateGroup(req, res) { const stop = new Date(req.body.stopTime || group.dates[0].stop) let state, isActive + if (group.state !== apiutil.PENDING) { + // only name can be updated + state = typeof req.body.state === 'undefined' ? group.state : req.body.state + + if (start.toISOString() !== group.dates[0].start.toISOString() || + stop.toISOString() !== group.dates[0].stop.toISOString() || + state !== group.state || + _class !== group.class || + repetitions !== group.repetitions) { + return apiutil.respond(res, 403, 'Forbidden (only name can be updated)') + } + + if (name === group.name) { + return apiutil.respond(res, 200, 'Unchanged (group)', {group: {}}) + } + + return dbapi.updateGroup(group.id, { + name: name + }) + .then(function(updatedGroup) { + if (updatedGroup) { + apiutil.respond(res, 200, 'Updated (group)', {group: apiutil.publishGroup(updatedGroup)}) + } + else { + throw new Error(`Group not found: ${group.id}`) + } + }) + } + if (apiutil.isOriginGroup(_class)) { state = apiutil.READY isActive = true @@ -808,7 +837,8 @@ function updateGroup(req, res) { repetitions === group.repetitions) { return apiutil.respond(res, 200, 'Unchanged (group)', {group: {}}) } - const duration = group.devices.length * (stop - start) * (repetitions + 1) + const duration = apiutil.isOriginGroup(_class) ? + 0 : group.devices.length * (stop - start) * (repetitions + 1) const dates = apiutil.computeGroupDates({start: start, stop: stop}, _class, repetitions) if (start < group.dates[0].start || diff --git a/lib/units/groups-engine/watchers/devices.js b/lib/units/groups-engine/watchers/devices.js index b3100a2899..1e8c7ac3e1 100644 --- a/lib/units/groups-engine/watchers/devices.js +++ b/lib/units/groups-engine/watchers/devices.js @@ -1,5 +1,5 @@ /** -* Copyright © 2019 code initially contributed by Orange SA, authors: Denis Barbaron - Licensed under the Apache license 2.0 +* Copyright © 2019-2024 code initially contributed by Orange SA, authors: Denis Barbaron - Licensed under the Apache license 2.0 **/ const wirerouter = require('../../../wire/router') @@ -66,7 +66,6 @@ module.exports = function(push, pushdev, channelRouter) { delete device.channel delete device.owner - delete device.group.id delete device.group.lifeTime return device } @@ -117,7 +116,7 @@ module.exports = function(push, pushdev, channelRouter) { , 'model' , 'operator' , 'manufacturer' - , {group: ['id', 'origin', 'originName', 'lifeTime']} + , {group: ['id', 'name', 'origin', 'originName', 'lifeTime', 'owner']} , {provider: ['name']} , {network: ['type', 'subtype']} , {display: ['height', 'width']} @@ -154,7 +153,9 @@ module.exports = function(push, pushdev, channelRouter) { data.new_val.network.type !== data.old_val.network.type || data.new_val.network.subtype !== data.old_val.network.subtype ) || - data.new_val.provider.name !== data.old_val.provider.name) { + data.new_val.provider.name !== data.old_val.provider.name || + data.new_val.group.name !== data.old_val.group.name || + data.new_val.group.originName !== data.old_val.group.originName) { sendDeviceChange(data.new_val, data.old_val, 'updated') } diff --git a/lib/units/groups-engine/watchers/groups.js b/lib/units/groups-engine/watchers/groups.js index 7bb7e9afdc..706d30dafd 100644 --- a/lib/units/groups-engine/watchers/groups.js +++ b/lib/units/groups-engine/watchers/groups.js @@ -1,5 +1,5 @@ /** -* Copyright © 2019 code initially contributed by Orange SA, authors: Denis Barbaron - Licensed under the Apache license 2.0 +* Copyright © 2019-2024 code initially contributed by Orange SA, authors: Denis Barbaron - Licensed under the Apache license 2.0 **/ const wirerouter = require('../../../wire/router') @@ -104,6 +104,12 @@ module.exports = function(push, pushdev, channelRouter) { }) } + function doUpdateDevicesGroupName(group) { + return Promise.map(group.devices, function(serial) { + return dbapi.updateDeviceGroupName(serial, group) + }) + } + function doUpdateDevicesCurrentGroup(group, devices) { return Promise.map(devices, function(serial) { return dbapi.updateDeviceCurrentGroup(serial, group) @@ -235,7 +241,7 @@ module.exports = function(push, pushdev, channelRouter) { .then(function(cursor) { cursor.each(function(err, data) { let users, devices, isBecomeActive, isBecomeUnactive, isActive - , isAddedUser, isAddedDevice, isUpdatedDeviceOriginGroup, isChangedDates + , isAddedUser, isAddedDevice, isUpdatedDeviceOriginGroup, isChangedDates, isChangedName if (err) { throw err @@ -281,6 +287,7 @@ module.exports = function(push, pushdev, channelRouter) { isBecomeActive = isBecomeUnactive = false isAddedUser = isAddedDevice = false isUpdatedDeviceOriginGroup = false + isChangedName = false } else { users = _.xor(data.new_val.users, data.old_val.users) @@ -300,6 +307,7 @@ module.exports = function(push, pushdev, channelRouter) { data.new_val.ticket !== null && (data.old_val.ticket === null || data.new_val.ticket.signature !== data.old_val.ticket.signature) + isChangedName = data.old_val.name !== data.new_val.name if (!isUpdatedDeviceOriginGroup) { sendGroupChange( @@ -337,6 +345,9 @@ module.exports = function(push, pushdev, channelRouter) { else if (users.length) { return treatGroupUsersChange(data.old_val, users, isActive, isAddedUser) } + else if (isChangedName) { + return doUpdateDevicesGroupName(data.new_val) + } return true }) }) diff --git a/lib/units/websocket/index.js b/lib/units/websocket/index.js index 949a3e46ee..489bd6b81a 100644 --- a/lib/units/websocket/index.js +++ b/lib/units/websocket/index.js @@ -1,5 +1,5 @@ /** -* Copyright © 2019 contains code contributed by Orange SA, authors: Denis Barbaron - Licensed under the Apache license 2.0 +* Copyright © 2019-2024 contains code contributed by Orange SA, authors: Denis Barbaron - Licensed under the Apache license 2.0 **/ var http = require('http') @@ -133,6 +133,15 @@ module.exports = function(options) { disconnectSocket(true) }) .on(wire.DeviceChangeMessage, function(channel, message) { + if (user.groups.subscribed.indexOf(message.device.group.id) > -1) { + socket.emit('device.change', { + important: true + , data: { + serial: message.device.serial + , group: message.device.group + } + }) + } if (user.groups.subscribed.indexOf(message.device.group.origin) > -1 || user.groups.subscribed.indexOf(message.oldOriginGroupId) > -1) { socket.emit('user.settings.devices.' + message.action, message) diff --git a/lib/wire/wire.proto b/lib/wire/wire.proto index bdf86b8f2d..1e45526afb 100644 --- a/lib/wire/wire.proto +++ b/lib/wire/wire.proto @@ -1,5 +1,5 @@ // -// Copyright © 2019 contains code contributed by Orange SA, authors: Denis Barbaron - Licensed under the Apache license 2.0 +// Copyright © 2019-2024 contains code contributed by Orange SA, authors: Denis Barbaron - Licensed under the Apache license 2.0 // // Message wrapper @@ -160,8 +160,11 @@ message DeviceProviderField { } message DeviceGroupField { - optional string origin = 1; - optional string originName = 2; + optional string id = 1; + optional string name = 2; + optional string origin = 3; + optional string originName = 4; + optional GroupOwnerField owner = 5; } message DeviceField { diff --git a/res/app/settings/groups/groups-controller.js b/res/app/settings/groups/groups-controller.js index 4cc953e33b..5bf0155ecd 100644 --- a/res/app/settings/groups/groups-controller.js +++ b/res/app/settings/groups/groups-controller.js @@ -771,7 +771,7 @@ module.exports = function GroupsCtrl( getAvailableGroupDevices(group) } } - if (isChangedSchedule && group.state !== 'pending') { + if (isChangedSchedule) { $scope.initTemporarySchedule(group) } if (doGetDevices) { diff --git a/res/app/settings/groups/groups.pug b/res/app/settings/groups/groups.pug index cf2b9023e5..28dc5eac9d 100644 --- a/res/app/settings/groups/groups.pug +++ b/res/app/settings/groups/groups.pug @@ -1,5 +1,5 @@ // - Copyright © 2019 code initially contributed by Orange SA, authors: Denis Barbaron - Licensed under the Apache license 2.0 + Copyright © 2019-2024 code initially contributed by Orange SA, authors: Denis Barbaron - Licensed under the Apache license 2.0 // .widget-container.fluid-height.stf-groups(ng-controller='GroupsCtrl') @@ -84,7 +84,7 @@ .group-line.group-actions i.fa.fa-object-group.fa-2x.fa-fw.group-list-icon .group-list-details.selectable - form.form-inline(name='nameForm' ng-if="group.state === 'pending' && showName") + form.form-inline(name='nameForm' ng-if="showName") input.form-control.input-sm( size='35' type='text' placeholder="Name" ng-model='groupsEnv[group.id].tmpName' @@ -92,7 +92,7 @@ uib-tooltip="{{'Regex syntax' | translate}}: {{::nameRegexStr}}" tooltip-placement='top' tooltip-popup-delay='500' - tooltip-enable="group.state === 'pending' && nameForm.$invalid" + tooltip-enable="nameForm.$invalid" required) button.btn.btn-sm.btn-primary.btn-check-name( @@ -103,7 +103,7 @@ .group-list-name( ng-bind-template='{{group.name}}' - ng-if="group.state !== 'pending' || !showName") + ng-if="!showName") .group-list-id span(translate) Identifier @@ -134,10 +134,9 @@ button.btn.btn-xs.pull-right( type='button' - ng-show="group.state === 'pending'" ng-click='initTemporaryName(group); showName = !showName' - ng-class='{"btn-primary-outline": !showName && group.state === "pending",\ - "btn-primary": showName && group.state === "pending"}') + ng-class='{"btn-primary-outline": !showName,\ + "btn-primary": showName}') i.fa.fa-tag span(translate) Name