diff options
Diffstat (limited to 'lib')
24 files changed, 871 insertions, 60 deletions
diff --git a/lib/Chat/ChatManager.php b/lib/Chat/ChatManager.php index a55dd9848..6257b73f4 100644 --- a/lib/Chat/ChatManager.php +++ b/lib/Chat/ChatManager.php @@ -764,7 +764,7 @@ class ChatManager { } private function searchIsPartOfConversationNameOrAtAll(string $search, string $roomDisplayName): bool { - if (stripos($roomDisplayName, $search) === 0) { + if (stripos($roomDisplayName, $search) !== false) { return true; } if (strpos('all', $search) === 0) { @@ -779,4 +779,45 @@ class ChatManager { public function deleteExpiredMessages(): void { $this->commentsManager->deleteCommentsExpiredAtObject('chat', ''); } + + public function fileOfMessageExists(string $message): bool { + $parameters = $this->getParametersFromMessage($message); + try { + $this->shareProvider->getShareById($parameters['share']); + } catch (ShareNotFound $e) { + return false; + } + return true; + } + + public function isSharedFile(string $message): bool { + $parameters = $this->getParametersFromMessage($message); + return !empty($parameters['share']); + } + + protected function getParametersFromMessage(string $message): array { + $data = json_decode($message, true); + if (!\is_array($data) || !array_key_exists('parameters', $data) || !is_array($data['parameters'])) { + return []; + } + return $data['parameters']; + } + + /** + * When receive a list of comments, filter the comments, + * removing all that have shares of file that no more exists + * + * @param IComment[] $comments + * @return IComment[] + */ + public function filterCommentsWithNonExistingFiles(array $comments): array { + return array_filter($comments, function (IComment $comment) { + if ($this->isSharedFile($comment->getMessage())) { + if (!$this->fileOfMessageExists($comment->getMessage())) { + return false; + } + } + return true; + }); + } } diff --git a/lib/Chat/MessageParser.php b/lib/Chat/MessageParser.php index 766463c00..376af71f4 100644 --- a/lib/Chat/MessageParser.php +++ b/lib/Chat/MessageParser.php @@ -53,8 +53,8 @@ class MessageParser { IUserManager $userManager, ParticipantService $participantService) { $this->dispatcher = $dispatcher; - $this->userManager = $userManager; $this->participantService = $participantService; + $this->userManager = $userManager; } public function createMessage(Room $room, Participant $participant, IComment $comment, IL10N $l): Message { diff --git a/lib/Chat/Parser/SystemMessage.php b/lib/Chat/Parser/SystemMessage.php index cefb79ed0..673d7121f 100644 --- a/lib/Chat/Parser/SystemMessage.php +++ b/lib/Chat/Parser/SystemMessage.php @@ -507,6 +507,16 @@ class SystemMessage { if ($currentUserIsActor) { $parsedMessage = $this->l->t('You cleared the history of the conversation'); } + } elseif ($message === 'avatar_set') { + $parsedMessage = $this->l->t('{actor} set the conversation avatar'); + if ($currentUserIsActor) { + $parsedMessage = $this->l->t('You set the conversation avatar'); + } + } elseif ($message === 'avatar_removed') { + $parsedMessage = $this->l->t('{actor} removed the conversation avatar'); + if ($currentUserIsActor) { + $parsedMessage = $this->l->t('You removed the conversation avatar'); + } } elseif ($message === 'poll_closed') { $parsedParameters['poll'] = $parameters['poll']; $parsedParameters['poll']['id'] = (string) $parsedParameters['poll']['id']; @@ -514,6 +524,26 @@ class SystemMessage { if ($currentUserIsActor) { $parsedMessage = $this->l->t('You ended the poll {poll}'); } + } elseif ($message === 'recording_started') { + $parsedMessage = $this->l->t('{actor} started the video recording'); + if ($currentUserIsActor) { + $parsedMessage = $this->l->t('You started the video recording'); + } + } elseif ($message === 'recording_stopped') { + $parsedMessage = $this->l->t('{actor} stopped the video recording'); + if ($currentUserIsActor) { + $parsedMessage = $this->l->t('You stopped the video recording'); + } + } elseif ($message === 'audio_recording_started') { + $parsedMessage = $this->l->t('{actor} started the audio recording'); + if ($currentUserIsActor) { + $parsedMessage = $this->l->t('You started the audio recording'); + } + } elseif ($message === 'audio_recording_stopped') { + $parsedMessage = $this->l->t('{actor} stopped the audio recording'); + if ($currentUserIsActor) { + $parsedMessage = $this->l->t('You stopped the audio recording'); + } } elseif ($message === 'poll_voted') { $parsedParameters['poll'] = $parameters['poll']; $parsedParameters['poll']['id'] = (string) $parsedParameters['poll']['id']; diff --git a/lib/Chat/SystemMessage/Listener.php b/lib/Chat/SystemMessage/Listener.php index 2cc7f15ed..fecc384eb 100644 --- a/lib/Chat/SystemMessage/Listener.php +++ b/lib/Chat/SystemMessage/Listener.php @@ -100,6 +100,8 @@ class Listener implements IEventListener { $dispatcher->addListener('OCP\Share::postShare', self::class . '::fixMimeTypeOfVoiceMessage'); $dispatcher->addListener(RoomShareProvider::EVENT_SHARE_FILE_AGAIN, self::class . '::fixMimeTypeOfVoiceMessage'); $dispatcher->addListener(Room::EVENT_AFTER_SET_MESSAGE_EXPIRATION, self::class . '::afterSetMessageExpiration'); + $dispatcher->addListener(Room::EVENT_AFTER_SET_CALL_RECORDING, self::class . '::setCallRecording'); + $dispatcher->addListener(Room::EVENT_AFTER_AVATAR_SET, self::class . '::avatarChanged'); } public static function sendSystemMessageAboutBeginOfCall(ModifyParticipantEvent $event): void { @@ -493,4 +495,42 @@ class Listener implements IEventListener { ] ); } + + public static function setCallRecording(ModifyRoomEvent $event): void { + $prefix = self::getCallRecordingPrefix($event); + $suffix = self::getCallRecordingSuffix($event); + $systemMessage = $prefix . 'recording_' . $suffix; + + $listener = Server::get(self::class); + $listener->sendSystemMessage($event->getRoom(), $systemMessage); + } + + private static function getCallRecordingSuffix(ModifyRoomEvent $event): string { + $newStatus = $event->getNewValue(); + $startStatus = [ + Room::RECORDING_VIDEO, + Room::RECORDING_AUDIO, + ]; + $suffix = in_array($newStatus, $startStatus) ? 'started' : 'stopped'; + return $suffix; + } + + private static function getCallRecordingPrefix(ModifyRoomEvent $event): string { + $newValue = $event->getNewValue(); + $oldValue = $event->getOldValue(); + $isAudioStatus = $newValue === Room::RECORDING_AUDIO + || $oldValue === Room::RECORDING_AUDIO; + return $isAudioStatus ? 'audio_' : ''; + } + + public static function avatarChanged(ModifyRoomEvent $event): void { + if ($event->getNewValue()) { + $message = 'avatar_set'; + } else { + $message = 'avatar_removed'; + } + + $listener = Server::get(self::class); + $listener->sendSystemMessage($event->getRoom(), $message); + } } diff --git a/lib/Command/User/TransferOwnership.php b/lib/Command/User/TransferOwnership.php new file mode 100644 index 000000000..b26f1f852 --- /dev/null +++ b/lib/Command/User/TransferOwnership.php @@ -0,0 +1,175 @@ +<?php + +declare(strict_types=1); +/** + * @copyright Copyright (c) 2022, Joas Schilling <coding@schilljs.com> + * + * @author Joas Schilling <coding@schilljs.com> + * + * @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\Command\User; + +use OC\Core\Command\Base; +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 OCA\Talk\Service\RoomService; +use OCP\IUserManager; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Output\OutputInterface; + +class TransferOwnership extends Base { + private RoomService $roomService; + private ParticipantService $participantService; + private Manager $manager; + private IUserManager $userManager; + + public function __construct(ParticipantService $participantService, + Manager $manager, + IUserManager $userManager) { + parent::__construct(); + $this->participantService = $participantService; + $this->manager = $manager; + $this->userManager = $userManager; + } + + protected function configure(): void { + $this + ->setName('talk:user:transfer-ownership') + ->setDescription('Adds the destination-user with the same participant type to all (not one-to-one) conversations of source-user') + ->addArgument( + 'source-user', + InputArgument::REQUIRED, + 'Owner of conversations which shall be moved' + ) + ->addArgument( + 'destination-user', + InputArgument::REQUIRED, + 'User who will be the new owner of the conversations' + ) + ->addOption( + 'include-non-moderator', + null, + InputOption::VALUE_NONE, + 'Also include conversations where the source-user is a normal user' + ) + ->addOption( + 'remove-source-user', + null, + InputOption::VALUE_NONE, + 'Remove the source-user from the conversations' + ) + ; + } + + protected function execute(InputInterface $input, OutputInterface $output): int { + $sourceUID = $input->getArgument('source-user'); + $destinationUID = $input->getArgument('destination-user'); + + $destinationUser = $this->userManager->get($destinationUID); + if ($destinationUser === null) { + $output->writeln('<error>Destination user could not be found.</error>'); + return 1; + } + + $includeNonModeratorRooms = $input->getOption('include-non-moderator'); + $removeSourceUser = $input->getOption('remove-source-user'); + + $modified = 0; + $rooms = $this->manager->getRoomsForActor(Attendee::ACTOR_USERS, $sourceUID); + foreach ($rooms as $room) { + if ($room->getType() !== Room::TYPE_GROUP && $room->getType() !== Room::TYPE_PUBLIC) { + // Skip one-to-one, changelog and any other room types + continue; + } + + $sourceParticipant = $this->participantService->getParticipantByActor($room, Attendee::ACTOR_USERS, $sourceUID); + + if ($sourceParticipant->getAttendee()->getParticipantType() === Participant::USER_SELF_JOINED) { + continue; + } + + if (!$includeNonModeratorRooms && !$sourceParticipant->hasModeratorPermissions()) { + continue; + } + + try { + $destinationParticipant = $this->participantService->getParticipantByActor($room, Attendee::ACTOR_USERS, $destinationUser->getUID()); + + $targetType = $this->shouldUpdateParticipantType($sourceParticipant->getAttendee()->getParticipantType(), $destinationParticipant->getAttendee()->getParticipantType()); + + if ($targetType !== null) { + $this->participantService->updateParticipantType( + $room, + $destinationParticipant, + $sourceParticipant->getAttendee()->getParticipantType() + ); + $modified++; + } + } catch (ParticipantNotFoundException $e) { + $this->participantService->addUsers($room, [ + [ + 'actorType' => Attendee::ACTOR_USERS, + 'actorId' => $destinationUser->getUID(), + 'displayName' => $destinationUser->getDisplayName(), + 'participantType' => $sourceParticipant->getAttendee()->getParticipantType(), + ] + ]); + $modified++; + } + + if ($removeSourceUser) { + $this->participantService->removeAttendee($room, $sourceParticipant, Room::PARTICIPANT_REMOVED); + } + } + + $output->writeln('<info>Added or promoted user ' . $destinationUser->getUID() . ' in ' . $modified . ' rooms.</info>'); + return 0; + } + + protected function shouldUpdateParticipantType(int $sourceParticipantType, int $destinationParticipantType): ?int { + if ($sourceParticipantType === Participant::OWNER) { + if ($destinationParticipantType === Participant::OWNER) { + return null; + } + return $sourceParticipantType; + } + + if ($sourceParticipantType === Participant::MODERATOR) { + if ($destinationParticipantType === Participant::OWNER || $destinationParticipantType === Participant::MODERATOR) { + return null; + } + return $sourceParticipantType; + } + + if ($sourceParticipantType === Participant::USER) { + if ($destinationParticipantType !== Participant::USER_SELF_JOINED) { + return null; + } + return $sourceParticipantType; + } + + return null; + } +} diff --git a/lib/Config.php b/lib/Config.php index 9f63cec65..14882d0ae 100644 --- a/lib/Config.php +++ b/lib/Config.php @@ -34,7 +34,6 @@ use OCP\IURLGenerator; use OCP\IUser; use OCP\IUserManager; use OCP\Security\ISecureRandom; -use OCP\Server; class Config { public const SIGNALING_INTERNAL = 'internal'; @@ -138,11 +137,28 @@ class Config { return $this->canEnableSIP[$user->getUID()]; } + public function isSignalingDev(): bool { + return $this->config->getAppValue('spreed', 'signaling_dev', 'no') === 'yes'; + } + public function isRecordingEnabled(): bool { $isSignalingInternal = $this->getSignalingMode() === self::SIGNALING_INTERNAL; - $recordingAllowed = $this->config->getAppValue('spreed', 'call_recording', 'yes') === 'yes'; + $isSignalingDev = $this->isSignalingDev(); + $isSignalingOk = $isSignalingDev || !$isSignalingInternal; - return !$isSignalingInternal && $recordingAllowed; + $callRecordingConfig = $this->config->getAppValue('spreed', 'call_recording', 'yes'); + $recordingEnabled = $callRecordingConfig === 'yes'; + + return $isSignalingOk && $recordingEnabled; + } + + public function getRecordingFolder(string $userId): string { + return $this->config->getUserValue( + $userId, + 'spreed', + 'recording_folder', + $this->getAttachmentFolder($userId) . '/Recording' + ); } public function isDisabledForUser(IUser $user): bool { @@ -335,7 +351,7 @@ class Config { } if ($numSignalingServers === 1 && $cleanExternalSignaling - && $this->config->getAppValue('spreed', 'signaling_dev', 'no') === 'no') { + && !$this->isSignalingDev()) { return self::SIGNALING_EXTERNAL; } @@ -431,6 +447,7 @@ class Config { if (substr($alg, 0, 2) === 'ES') { $privKey = openssl_pkey_new([ 'curve_name' => 'prime256v1', + 'private_key_bits' => 2048, 'private_key_type' => OPENSSL_KEYTYPE_EC, ]); $pubKey = openssl_pkey_get_details($privKey); diff --git a/lib/Controller/ChatController.php b/lib/Controller/ChatController.php index c3c80df14..99cd22b4a 100644 --- a/lib/Controller/ChatController.php +++ b/lib/Controller/ChatController.php @@ -466,12 +466,19 @@ class ChatController extends AEnvironmentAwareController { $this->preloadShares($comments); |