diff --git a/lib/Chat/Parser/SystemMessage.php b/lib/Chat/Parser/SystemMessage.php index b510242cf6a..be9e06ec677 100644 --- a/lib/Chat/Parser/SystemMessage.php +++ b/lib/Chat/Parser/SystemMessage.php @@ -143,13 +143,18 @@ protected function parseMessage(Message $chatMessage): void { $participant->getAttendee()->getActorId() === $parsedParameters['actor']['id']; } $cliIsActor = $parsedParameters['actor']['type'] === 'guest' && - 'guest/cli' === $parsedParameters['actor']['id']; + 'guest/' . Attendee::ACTOR_ID_CLI === $parsedParameters['actor']['id']; if ($message === 'conversation_created') { + $systemIsActor = $parsedParameters['actor']['type'] === 'guest' && + 'guest/' . Attendee::ACTOR_ID_SYSTEM === $parsedParameters['actor']['id']; + $parsedMessage = $this->l->t('{actor} created the conversation'); if ($currentUserIsActor) { $parsedMessage = $this->l->t('You created the conversation'); - } elseif ($cliIsActor) { + } elseif ($systemIsActor) { + $parsedMessage = $this->l->t('System created the conversation'); + }if ($cliIsActor) { $parsedMessage = $this->l->t('An administrator created the conversation'); } } elseif ($message === 'conversation_renamed') { diff --git a/lib/Chat/SystemMessage/Listener.php b/lib/Chat/SystemMessage/Listener.php index 69716b25b63..f671dff8a72 100644 --- a/lib/Chat/SystemMessage/Listener.php +++ b/lib/Chat/SystemMessage/Listener.php @@ -45,6 +45,7 @@ use OCA\Talk\Model\Session; use OCA\Talk\Participant; use OCA\Talk\Room; +use OCA\Talk\Service\NoteToSelfService; use OCA\Talk\Service\ParticipantService; use OCA\Talk\TalkSession; use OCA\Talk\Webinary; @@ -157,7 +158,11 @@ protected function sendSystemMessageAboutCallLeft(ParticipantModifiedEvent $even } protected function sendSystemMessageAboutConversationCreated(RoomCreatedEvent $event): void { - $this->sendSystemMessage($event->getRoom(), 'conversation_created'); + if ($event->getRoom()->getType() === Room::TYPE_CHANGELOG || $this->isCreatingNoteToSelfAutomatically($event)) { + $this->sendSystemMessage($event->getRoom(), 'conversation_created', forceSystemAsActor: true); + } else { + $this->sendSystemMessage($event->getRoom(), 'conversation_created'); + } } protected function sendSystemMessageAboutConversationRenamed(RoomModifiedEvent $event): void { @@ -174,6 +179,10 @@ protected function sendSystemMessageAboutConversationRenamed(RoomModifiedEvent $ protected function sendSystemMessageAboutRoomDescriptionChanges(RoomModifiedEvent $event): void { if ($event->getNewValue() !== '') { + if ($this->isCreatingNoteToSelf($event)) { + return; + } + $this->sendSystemMessage($event->getRoom(), 'description_set', [ 'newDescription' => $event->getNewValue(), ]); @@ -253,6 +262,10 @@ protected function addSystemMessageUserAdded(AttendeesAddedEvent $event, Attende return; } + if ($room->getType() === Room::TYPE_CHANGELOG) { + return; + } + $userJoinedFileRoom = $room->getObjectType() === Room::OBJECT_TYPE_FILE && $attendee->getParticipantType() !== Participant::USER_SELF_JOINED; // add a message "X joined the conversation", whenever user $userId: @@ -407,10 +420,13 @@ protected function attendeesRemovedEvent(AttendeesRemovedEvent $event): void { } } - protected function sendSystemMessage(Room $room, string $message, array $parameters = [], Participant $participant = null, bool $shouldSkipLastMessageUpdate = false, bool $silent = false): IComment { + protected function sendSystemMessage(Room $room, string $message, array $parameters = [], Participant $participant = null, bool $shouldSkipLastMessageUpdate = false, bool $silent = false, bool $forceSystemAsActor = false): IComment { if ($participant instanceof Participant) { $actorType = $participant->getAttendee()->getActorType(); $actorId = $participant->getAttendee()->getActorId(); + } elseif ($forceSystemAsActor) { + $actorType = Attendee::ACTOR_GUESTS; + $actorId = Attendee::ACTOR_ID_SYSTEM; } else { $user = $this->userSession->getUser(); if ($user instanceof IUser) { @@ -524,6 +540,10 @@ protected function getCallRecordingPrefix(RoomModifiedEvent $event): string { protected function avatarChanged(RoomModifiedEvent $event): void { if ($event->getNewValue()) { + if ($this->isCreatingNoteToSelf($event)) { + return; + } + $message = 'avatar_set'; } else { $message = 'avatar_removed'; @@ -531,4 +551,44 @@ protected function avatarChanged(RoomModifiedEvent $event): void { $this->sendSystemMessage($event->getRoom(), $message); } + + protected function isCreatingNoteToSelf(RoomModifiedEvent $event): bool { + if ($event->getRoom()->getType() !== Room::TYPE_NOTE_TO_SELF) { + return false; + } + + $exception = new \Exception(); + $trace = $exception->getTrace(); + + foreach ($trace as $step) { + if (isset($step['class']) && $step['class'] === NoteToSelfService::class && + isset($step['function']) && $step['function'] === 'initialCreateNoteToSelfForUser') { + return true; + } + if (isset($step['class']) && $step['class'] === NoteToSelfService::class && + isset($step['function']) && $step['function'] === 'ensureNoteToSelfExistsForUser') { + return true; + } + } + + return false; + } + + protected function isCreatingNoteToSelfAutomatically(RoomCreatedEvent $event): bool { + if ($event->getRoom()->getType() !== Room::TYPE_NOTE_TO_SELF) { + return false; + } + + $exception = new \Exception(); + $trace = $exception->getTrace(); + + foreach ($trace as $step) { + if (isset($step['class']) && $step['class'] === NoteToSelfService::class && + isset($step['function']) && $step['function'] === 'initialCreateNoteToSelfForUser') { + return true; + } + } + + return false; + } } diff --git a/lib/Model/Attendee.php b/lib/Model/Attendee.php index 41c27c98da2..0e0f2abd018 100644 --- a/lib/Model/Attendee.php +++ b/lib/Model/Attendee.php @@ -79,6 +79,7 @@ class Attendee extends Entity { // Special actor IDs public const ACTOR_BOT_PREFIX = 'bot-'; public const ACTOR_ID_CLI = 'cli'; + public const ACTOR_ID_SYSTEM = 'system'; public const ACTOR_ID_CHANGELOG = 'changelog'; public const PERMISSIONS_DEFAULT = 0; diff --git a/tests/integration/features/bootstrap/FeatureContext.php b/tests/integration/features/bootstrap/FeatureContext.php index 30c2ed4f455..d0bfbffe3df 100644 --- a/tests/integration/features/bootstrap/FeatureContext.php +++ b/tests/integration/features/bootstrap/FeatureContext.php @@ -171,6 +171,7 @@ public function setUp() { self::$tokenToIdentifier = []; self::$sessionIdToUser = [ 'cli' => 'cli', + 'system' => 'system', 'failed-to-get-session' => 'failed-to-get-session', ]; self::$userToSessionId = []; @@ -283,24 +284,31 @@ public function userCanFindListedRoomsWithTerm(string $user, string $term, strin } /** - * @Then /^user "([^"]*)" is participant of the following (unordered )?rooms \((v4)\)$/ + * @Then /^user "([^"]*)" is participant of the following (unordered )?(note-to-self )?rooms \((v4)\)$/ * * @param string $user * @param string $shouldOrder * @param string $apiVersion * @param TableNode|null $formData */ - public function userIsParticipantOfRooms(string $user, string $shouldOrder, string $apiVersion, TableNode $formData = null): void { + public function userIsParticipantOfRooms(string $user, string $shouldOrder, string $shouldFilter, string $apiVersion, TableNode $formData = null): void { $this->setCurrentUser($user); $this->sendRequest('GET', '/apps/spreed/api/' . $apiVersion . '/room'); $this->assertStatusCode($this->response, 200); $rooms = $this->getDataFromResponse($this->response); - $rooms = array_filter($rooms, function ($room) { - // Filter out "Talk updates" and "Note to self" conversations - return $room['type'] !== 4 && $room['type'] !== 6; - }); + if ($shouldFilter === '') { + $rooms = array_filter($rooms, function ($room) { + // Filter out "Talk updates" and "Note to self" conversations + return $room['type'] !== 4 && $room['type'] !== 6; + }); + } elseif ($shouldFilter === 'note-to-self ') { + $rooms = array_filter($rooms, function ($room) { + // Filter out "Talk updates" conversations + return $room['type'] !== 4; + }); + } if ($formData === null) { Assert::assertEmpty($rooms); @@ -912,6 +920,17 @@ public function userCreatesNoteToSelf(string $user, string $apiVersion): void { self::$tokenToIdentifier[$response['token']] = $user . '-note-to-self'; } + /** + * @Then /^user "([^"]*)" reset note-to-self preference$/ + * + * @param string $user + */ + public function userResetNoteToSelfPreference(string $user): void { + $this->setCurrentUser($user); + $this->sendRequest('DELETE', '/apps/provisioning_api/api/v1/config/users/spreed/note_to_self'); + $this->assertStatusCode($this->response, 200); + } + /** * @Then /^user "([^"]*)" creates room "([^"]*)" with (\d+) \((v4)\)$/ * diff --git a/tests/integration/features/chat/mentions.feature b/tests/integration/features/chat/mentions.feature index 77d6de42517..fcff252362e 100644 --- a/tests/integration/features/chat/mentions.feature +++ b/tests/integration/features/chat/mentions.feature @@ -627,9 +627,9 @@ Feature: chat/mentions Scenario: At-all in note-to-self broke the mention parsing And user "participant1" creates note-to-self (v4) And user "participant1" sends message "Test @all" to room "participant1-note-to-self" with 201 - And user "participant1" is participant of the following rooms (v4) -# | id | type | name | -# | participant1-note-to-self | 6 | Note to self | + And user "participant1" is participant of the following note-to-self rooms (v4) + | id | type | name | + | participant1-note-to-self | 6 | Note to self | Then user "participant1" sees the following messages in room "participant1-note-to-self" with 200 | room | actorType | actorId | actorDisplayName | message | messageParameters | | participant1-note-to-self | users | participant1 | participant1-displayname | Test {mention-call1} | "IGNORE" | diff --git a/tests/integration/features/chat/note-to-self.feature b/tests/integration/features/chat/note-to-self.feature new file mode 100644 index 00000000000..3e44131ff03 --- /dev/null +++ b/tests/integration/features/chat/note-to-self.feature @@ -0,0 +1,24 @@ +Feature: chat/note-to-self + + Background: + Given user "participant1" exists + + Scenario: Created manually via the endpoint + When user "participant1" reset note-to-self preference + When user "participant1" creates note-to-self (v4) + And user "participant1" is participant of the following note-to-self rooms (v4) + | id | type | name | + | participant1-note-to-self | 6 | Note to self | + Then user "participant1" sees the following system messages in room "participant1-note-to-self" with 200 + | room | actorType | actorId | actorDisplayName | message | messageParameters | systemMessage | + | participant1-note-to-self | users | participant1 | participant1-displayname | You created the conversation | {"actor":{"type":"user","id":"participant1","name":"participant1-displayname"}} | conversation_created | + + + Scenario: Created automatically when fetching the room list + When user "participant1" reset note-to-self preference + And user "participant1" is participant of the following note-to-self rooms (v4) + | id | type | name | + | Note to self | 6 | Note to self | + Then user "participant1" sees the following system messages in room "Note to self" with 200 + | room | actorType | actorId | actorDisplayName | message | messageParameters | systemMessage | + | Note to self | guests | system | | System created the conversation | {"actor":{"type":"guest","id":"guest\/system","name":"Guest"}} | conversation_created | diff --git a/tests/integration/spreedcheats/lib/AppInfo/Application.php b/tests/integration/spreedcheats/lib/AppInfo/Application.php index 910c0f0aa19..17636b2f3c4 100644 --- a/tests/integration/spreedcheats/lib/AppInfo/Application.php +++ b/tests/integration/spreedcheats/lib/AppInfo/Application.php @@ -23,12 +23,14 @@ namespace OCA\SpreedCheats\AppInfo; +use OCA\SpreedCheats\PreferenceListener; use OCA\SpreedCheats\SpeechToText\LoremIpsumSpeechToTextProvider; use OCA\SpreedCheats\Translation\LoremIpsumTranslationProvider; use OCP\AppFramework\App; use OCP\AppFramework\Bootstrap\IBootContext; use OCP\AppFramework\Bootstrap\IBootstrap; use OCP\AppFramework\Bootstrap\IRegistrationContext; +use OCP\Config\BeforePreferenceDeletedEvent; class Application extends App implements IBootstrap { public const APP_ID = 'spreedcheats'; @@ -40,6 +42,7 @@ public function __construct() { public function register(IRegistrationContext $context): void { $context->registerSpeechToTextProvider(LoremIpsumSpeechToTextProvider::class); $context->registerTranslationProvider(LoremIpsumTranslationProvider::class); + $context->registerEventListener(BeforePreferenceDeletedEvent::class, PreferenceListener::class); } public function boot(IBootContext $context): void { diff --git a/tests/integration/spreedcheats/lib/PreferenceListener.php b/tests/integration/spreedcheats/lib/PreferenceListener.php new file mode 100644 index 00000000000..434600ff31e --- /dev/null +++ b/tests/integration/spreedcheats/lib/PreferenceListener.php @@ -0,0 +1,43 @@ + + * + * @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 . + * + */ + +namespace OCA\SpreedCheats; + +use OCP\Config\BeforePreferenceDeletedEvent; +use OCP\EventDispatcher\Event; +use OCP\EventDispatcher\IEventListener; + +/** + * @template-implements IEventListener + */ +class PreferenceListener implements IEventListener { + public function handle(Event $event): void { + if (!$event instanceof BeforePreferenceDeletedEvent) { + return; + } + + if ($event->getAppId() === 'spreed') { + $event->setValid(true); + } + } +}