Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
d7bd1a8
Add a method to add group members directly to a conversation
nickvergessen Dec 18, 2020
db91242
Keep track of added groups
nickvergessen Dec 18, 2020
2db230b
Add and remove users from conversations when their group membership c…
nickvergessen Dec 18, 2020
b37dade
Fix constant value to plural
nickvergessen Mar 25, 2021
b532b28
Fix command integration tests
nickvergessen Mar 25, 2021
9edff03
Add expected group to output
nickvergessen Mar 25, 2021
55981db
Make sure existing self-joined users are converted to added users
nickvergessen Mar 25, 2021
4ccc537
Save and show the display name of groups
nickvergessen Mar 25, 2021
1e66602
Show groups at the end and don't allow promoting
nickvergessen Mar 25, 2021
bc4f7e5
Prevent promoting/demoting groups on API level
nickvergessen Mar 25, 2021
8608bdc
Filter out groups which are already participants
nickvergessen Mar 26, 2021
0253a69
Don't error when a user is already a member
nickvergessen Mar 26, 2021
5730ef0
Add integration tests
nickvergessen Mar 26, 2021
44a7308
Only remove users and not moderators when a group (membership) is rem…
nickvergessen Mar 31, 2021
672f06f
Ignore the read status of groups and store the displayname on adding …
nickvergessen Apr 8, 2021
807c8a0
Change misleading computed name
nickvergessen Apr 8, 2021
b48ce35
Adjust label when removing a group
nickvergessen Apr 8, 2021
269ac90
Add integration tests for multi group scenarios
nickvergessen Apr 8, 2021
4b268d0
Rename test file
nickvergessen Apr 8, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions lib/AppInfo/Application.php
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@
use OCA\Talk\Listener\BeforeUserLoggedOutListener;
use OCA\Talk\Listener\CSPListener;
use OCA\Talk\Listener\FeaturePolicyListener;
use OCA\Talk\Listener\GroupMembershipListener;
use OCA\Talk\Listener\RestrictStartingCalls as RestrictStartingCallsListener;
use OCA\Talk\Listener\UserDeletedListener;
use OCA\Talk\Listener\UserDisplayNameListener;
Expand All @@ -71,6 +72,8 @@
use OCP\AppFramework\Utility\ITimeFactory;
use OCP\Collaboration\Resources\IProviderManager;
use OCP\EventDispatcher\IEventDispatcher;
use OCP\Group\Events\UserAddedEvent;
use OCP\Group\Events\UserRemovedEvent;
use OCP\IServerContainer;
use OCP\IUser;
use OCP\Security\CSP\AddContentSecurityPolicyEvent;
Expand All @@ -95,6 +98,8 @@ public function register(IRegistrationContext $context): void {
$context->registerEventListener(AddContentSecurityPolicyEvent::class, CSPListener::class);
$context->registerEventListener(AddFeaturePolicyEvent::class, FeaturePolicyListener::class);
$context->registerEventListener(UserDeletedEvent::class, UserDeletedListener::class);
$context->registerEventListener(UserAddedEvent::class, GroupMembershipListener::class);
$context->registerEventListener(UserRemovedEvent::class, GroupMembershipListener::class);
$context->registerEventListener(BeforeUserLoggedOutEvent::class, BeforeUserLoggedOutListener::class);
$context->registerEventListener(BeforeTemplateRenderedEvent::class, PublicShareTemplateLoader::class);
$context->registerEventListener(BeforeTemplateRenderedEvent::class, PublicShareAuthTemplateLoader::class);
Expand Down
39 changes: 29 additions & 10 deletions lib/Collaboration/Collaborators/Listener.php
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
use OCA\Talk\Exceptions\ParticipantNotFoundException;
use OCA\Talk\Exceptions\RoomNotFoundException;
use OCA\Talk\Manager;
use OCA\Talk\Model\Attendee;
use OCA\Talk\Participant;
use OCA\Talk\Room;
use OCP\Collaboration\AutoComplete\AutoCompleteEvent;
Expand Down Expand Up @@ -61,7 +62,7 @@ public function __construct(Manager $manager,
public static function register(IEventDispatcher $dispatcher): void {
$dispatcher->addListener(IManager::class . '::filterResults', static function (AutoCompleteEvent $event) {
/** @var self $listener */
$listener = \OC::$server->query(self::class);
$listener = \OC::$server->get(self::class);

if ($event->getItemType() !== 'call') {
return;
Expand All @@ -82,28 +83,28 @@ protected function filterUsersAndGroupsWithoutTalk(array $results): array {
}

if (!empty($results['groups'])) {
$results['groups'] = array_filter($results['groups'], [$this, 'filterGroupResult']);
$results['groups'] = array_filter($results['groups'], [$this, 'filterBlockedGroupResult']);
}
if (!empty($results['exact']['groups'])) {
$results['exact']['groups'] = array_filter($results['exact']['groups'], [$this, 'filterGroupResult']);
$results['exact']['groups'] = array_filter($results['exact']['groups'], [$this, 'filterBlockedGroupResult']);
}

if (!empty($results['users'])) {
$results['users'] = array_filter($results['users'], [$this, 'filterUserResult']);
$results['users'] = array_filter($results['users'], [$this, 'filterBlockedUserResult']);
}
if (!empty($results['exact']['users'])) {
$results['exact']['users'] = array_filter($results['exact']['users'], [$this, 'filterUserResult']);
$results['exact']['users'] = array_filter($results['exact']['users'], [$this, 'filterBlockedUserResult']);
}

return $results;
}

protected function filterUserResult(array $result): bool {
protected function filterBlockedUserResult(array $result): bool {
$user = $this->userManager->get($result['value']['shareWith']);
return $user instanceof IUser && !$this->config->isDisabledForUser($user);
}

protected function filterGroupResult(array $result): bool {
protected function filterBlockedGroupResult(array $result): bool {
return \in_array($result['value']['shareWith'], $this->allowedGroupIds, true);
}

Expand All @@ -114,17 +115,24 @@ protected function filterExistingParticipants(string $token, array $results): ar
return $results;
}

if (!empty($results['groups'])) {
$results['groups'] = array_filter($results['groups'], [$this, 'filterParticipantGroupResult']);
}
if (!empty($results['exact']['groups'])) {
$results['exact']['groups'] = array_filter($results['exact']['groups'], [$this, 'filterParticipantGroupResult']);
}

if (!empty($results['users'])) {
$results['users'] = array_filter($results['users'], [$this, 'filterParticipantResult']);
$results['users'] = array_filter($results['users'], [$this, 'filterParticipantUserResult']);
}
if (!empty($results['exact']['users'])) {
$results['exact']['users'] = array_filter($results['exact']['users'], [$this, 'filterParticipantResult']);
$results['exact']['users'] = array_filter($results['exact']['users'], [$this, 'filterParticipantUserResult']);
}

return $results;
}

protected function filterParticipantResult(array $result): bool {
protected function filterParticipantUserResult(array $result): bool {
$userId = $result['value']['shareWith'];

try {
Expand All @@ -138,4 +146,15 @@ protected function filterParticipantResult(array $result): bool {
return true;
}
}

protected function filterParticipantGroupResult(array $result): bool {
$groupId = $result['value']['shareWith'];

try {
$this->room->getParticipantByActor(Attendee::ACTOR_GROUPS, $groupId);
return false;
} catch (ParticipantNotFoundException $e) {
return true;
}
}
}
9 changes: 1 addition & 8 deletions lib/Command/Room/TRoomCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -230,21 +230,14 @@ protected function addRoomParticipantsByGroup(Room $room, array $groupIds): void
return;
}

$users = [];
foreach ($groupIds as $groupId) {
$group = $this->groupManager->get($groupId);
if ($group === null) {
throw new InvalidArgumentException(sprintf("Group '%s' not found.", $groupId));
}

$groupUsers = array_map(function (IUser $user) {
return $user->getUID();
}, $group->getUsers());

$users = array_merge($users, array_values($groupUsers));
$this->participantService->addGroup($room, $group);
}

$this->addRoomParticipants($room, array_unique($users));
}

/**
Expand Down
32 changes: 7 additions & 25 deletions lib/Controller/RoomController.php
Original file line number Diff line number Diff line change
Expand Up @@ -673,23 +673,7 @@ protected function createGroupRoom(string $targetGroupName): DataResponse {
// Create the room
$name = $this->roomService->prepareConversationName($targetGroup->getDisplayName());
$room = $this->roomService->createConversation(Room::GROUP_CALL, $name, $currentUser);

$usersInGroup = $targetGroup->getUsers();
$participants = [];
foreach ($usersInGroup as $user) {
if ($currentUser->getUID() === $user->getUID()) {
// Owner is already added.
continue;
}

$participants[] = [
'actorType' => Attendee::ACTOR_USERS,
'actorId' => $user->getUID(),
'displayName' => $user->getDisplayName(),
];
}

$this->participantService->addUsers($room, $participants);
$this->participantService->addGroup($room, $targetGroup);

return new DataResponse($this->formatRoom($room, $room->getParticipant($currentUser->getUID(), false)), Http::STATUS_CREATED);
}
Expand Down Expand Up @@ -995,6 +979,8 @@ public function getParticipants(bool $includeStatus = false): DataResponse {
continue;
}

$result['displayName'] = $participant->getAttendee()->getDisplayName();
} elseif ($participant->getAttendee()->getActorType() === Attendee::ACTOR_GROUPS) {
$result['displayName'] = $participant->getAttendee()->getDisplayName();
}

Expand Down Expand Up @@ -1050,14 +1036,7 @@ public function addParticipantToRoom(string $newParticipant, string $source = 'u
return new DataResponse([], Http::STATUS_NOT_FOUND);
}

$usersInGroup = $group->getUsers();
foreach ($usersInGroup as $user) {
$participantsToAdd[] = [
'actorType' => Attendee::ACTOR_USERS,
'actorId' => $user->getUID(),
'displayName' => $user->getDisplayName(),
];
}
$this->participantService->addGroup($this->room, $group, $participants);
} elseif ($source === 'circles') {
if (!$this->appManager->isEnabledForUser('circles')) {
return new DataResponse([], Http::STATUS_BAD_REQUEST);
Expand Down Expand Up @@ -1458,6 +1437,9 @@ protected function changeParticipantType(int $attendeeId, bool $promote): DataR
if ($session instanceof Session && $currentSessionId === $session->getSessionId()) {
return new DataResponse([], Http::STATUS_FORBIDDEN);
}
} elseif ($attendee->getActorType() === Attendee::ACTOR_GROUPS) {
// Can not promote/demote groups
return new DataResponse([], Http::STATUS_BAD_REQUEST);
}

if ($promote === $targetParticipant->hasModeratorPermissions()) {
Expand Down
117 changes: 117 additions & 0 deletions lib/Listener/GroupMembershipListener.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
<?php

declare(strict_types=1);
/**
* @copyright Copyright (c) 2020 Joas Schilling <[email protected]>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/

namespace OCA\Talk\Listener;

use OCA\Talk\Exceptions\ParticipantNotFoundException;
use OCA\Talk\Manager;
use OCA\Talk\Model\Attendee;
use OCA\Talk\Participant;
use OCA\Talk\Room;
use OCA\Talk\Service\ParticipantService;
use OCP\EventDispatcher\Event;
use OCP\EventDispatcher\IEventListener;
use OCP\Group\Events\UserAddedEvent;
use OCP\Group\Events\UserRemovedEvent;
use OCP\IGroup;
use OCP\IGroupManager;
use OCP\IUser;

class GroupMembershipListener implements IEventListener {

/** @var IGroupManager */
private $groupManager;
/** @var Manager */
private $manager;
/** @var ParticipantService */
private $participantService;

public function __construct(IGroupManager $groupManager,
Manager $manager,
ParticipantService $participantService) {
$this->groupManager = $groupManager;
$this->manager = $manager;
$this->participantService = $participantService;
}

public function handle(Event $event): void {
if ($event instanceof UserAddedEvent) {
$this->addNewMemberToRooms($event->getGroup(), $event->getUser());
}
if ($event instanceof UserRemovedEvent) {
$this->removeFormerMemberFromRooms($event->getGroup(), $event->getUser());
}
}

protected function addNewMemberToRooms(IGroup $group, IUser $user): void {
$rooms = $this->manager->getRoomsForActor(Attendee::ACTOR_GROUPS, $group->getGID());

foreach ($rooms as $room) {
try {
$participant = $room->getParticipant($user->getUID());
if ($participant->getAttendee()->getParticipantType() === Participant::USER_SELF_JOINED) {
$this->participantService->updateParticipantType($room, $participant, Participant::USER);
}
} catch (ParticipantNotFoundException $e) {
$this->participantService->addUsers($room, [[
'actorType' => Attendee::ACTOR_USERS,
'actorId' => $user->getUID(),
'displayName' => $user->getDisplayName(),
]]);
}
}
}

protected function removeFormerMemberFromRooms(IGroup $group, IUser $user): void {
$rooms = $this->manager->getRoomsForActor(Attendee::ACTOR_GROUPS, $group->getGID());
if (empty($rooms)) {
return;
}

$userGroupIds = $this->groupManager->getUserGroupIds($user);

$furtherMemberships = [];
foreach ($userGroupIds as $groupId) {
$groupRooms = $this->manager->getRoomsForActor(Attendee::ACTOR_GROUPS, $groupId);
foreach ($groupRooms as $room) {
$furtherMemberships[$room->getId()] = true;
}
}

$rooms = array_filter($rooms, static function (Room $room) use ($furtherMemberships) {
// Only delete from rooms where the user is not member via another group
return !isset($furtherMemberships[$room->getId()]);
});

foreach ($rooms as $room) {
try {
$participant = $room->getParticipant($user->getUID());
$participantType = $participant->getAttendee()->getParticipantType();
if ($participantType === Participant::USER) {
$this->participantService->removeUser($room, $user, Room::PARTICIPANT_REMOVED);
}
} catch (ParticipantNotFoundException $e) {
}
}
}
}
17 changes: 14 additions & 3 deletions lib/Manager.php
Original file line number Diff line number Diff line change
Expand Up @@ -304,14 +304,25 @@ public function searchRoomsByToken(string $searchToken = '', int $limit = null,
* @return Room[]
*/
public function getRoomsForUser(string $userId, array $sessionIds = [], bool $includeLastMessage = false): array {
return $this->getRoomsForActor(Attendee::ACTOR_USERS, $userId, $sessionIds, $includeLastMessage);
}

/**
* @param string $actorType
* @param string $actorId
* @param array $sessionIds A list of talk sessions to consider for loading (otherwise no session is loaded)
* @param bool $includeLastMessage
* @return Room[]
*/
public function getRoomsForActor(string $actorType, string $actorId, array $sessionIds = [], bool $includeLastMessage = false): array {
$query = $this->db->getQueryBuilder();
$helper = new SelectHelper();
$helper->selectRoomsTable($query);
$helper->selectAttendeesTable($query);
$query->from('talk_rooms', 'r')
->leftJoin('r', 'talk_attendees', 'a', $query->expr()->andX(
$query->expr()->eq('a.actor_id', $query->createNamedParameter($userId)),
$query->expr()->eq('a.actor_type', $query->createNamedParameter(Attendee::ACTOR_USERS)),
$query->expr()->eq('a.actor_id', $query->createNamedParameter($actorId)),
$query->expr()->eq('a.actor_type', $query->createNamedParameter($actorType)),
$query->expr()->eq('a.room_id', 'r.id')
))
->where($query->expr()->isNotNull('a.id'));
Expand All @@ -337,7 +348,7 @@ public function getRoomsForUser(string $userId, array $sessionIds = [], bool $in
}

$room = $this->createRoomObject($row);
if ($userId !== null && isset($row['actor_id'])) {
if ($actorType === Attendee::ACTOR_USERS && isset($row['actor_id'])) {
$room->setParticipant($row['actor_id'], $this->createParticipantObject($room, $row));
}
$rooms[] = $room;
Expand Down
1 change: 1 addition & 0 deletions lib/Model/Attendee.php
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@
*/
class Attendee extends Entity {
public const ACTOR_USERS = 'users';
public const ACTOR_GROUPS = 'groups';
public const ACTOR_GUESTS = 'guests';
public const ACTOR_EMAILS = 'emails';

Expand Down
Loading