Skip to content

Commit

Permalink
feat(chat): Add basic handling of editing
Browse files Browse the repository at this point in the history
Signed-off-by: Joas Schilling <[email protected]>
  • Loading branch information
nickvergessen committed Jan 10, 2024
1 parent 7d3db3a commit 117a985
Show file tree
Hide file tree
Showing 4 changed files with 116 additions and 2 deletions.
7 changes: 6 additions & 1 deletion docs/chat.md
Original file line number Diff line number Diff line change
Expand Up @@ -314,12 +314,17 @@ See [OCP\RichObjectStrings\Definitions](https://github.com/nextcloud/server/blob
* Required capability: `edit-messages`
* Method: `PUT`
* Endpoint: `/chat/{token}/{messageId}`
* Data:

| field | type | Description |
|--------------------|--------|-----------------------------------|
| `message` | string | The message the user wants to say |

* Response:
- Status code:
+ `200 OK` - When editing was successful
+ `202 Accepted` - When editing was successful but Matterbridge is enabled so the message was leaked to other services
+ `400 Bad Request` The message is already older than 24 hours or another reason why deleting is not okay
+ `400 Bad Request` The message is already older than 24 hours or another reason why editing is not okay
+ `403 Forbidden` When the message is not from the current user and the user not a moderator
+ `403 Forbidden` When the conversation is read-only
+ `404 Not Found` When the conversation or chat message could not be found for the participant
Expand Down
41 changes: 41 additions & 0 deletions lib/Chat/ChatManager.php
Original file line number Diff line number Diff line change
Expand Up @@ -476,6 +476,47 @@ public function deleteMessage(Room $chat, IComment $comment, Participant $partic
);
}

/**
* @param Room $chat
* @param IComment $comment
* @param Participant $participant
* @param \DateTime $editTime
* @param string $message
* @return IComment
*/
public function editMessage(Room $chat, IComment $comment, Participant $participant, \DateTime $editTime, string $message): IComment {
// if ($comment->getVerb() === self::VERB_OBJECT_SHARED) {
// $messageData = json_decode($comment->getMessage(), true);
// $this->unshareFileOnMessageDelete($chat, $participant, $messageData);
// $this->removePollOnMessageDelete($chat, $participant, $messageData, $deletionTime);
// }

// $this->attachmentService->deleteAttachmentByMessageId((int) $comment->getId());

$metaData = $comment->getMetaData() ?? [];
$metaData['last_edited_by_type'] = $participant->getAttendee()->getActorType();
$metaData['last_edited_by_id'] = $participant->getAttendee()->getActorId();
$metaData['last_edited_time'] = $editTime->getTimestamp();
$comment->setMetaData($metaData);
$comment->setMessage($message, self::MAX_CHAT_LENGTH);
$this->commentsManager->save($comment);
$this->referenceManager->invalidateCache($chat->getToken());

// TODO update mentions/notifications

return $this->addSystemMessage(
$chat,
$participant->getAttendee()->getActorType(),
$participant->getAttendee()->getActorId(),
json_encode(['message' => 'message_edited', 'parameters' => ['message' => $comment->getId()]]),
$this->timeFactory->getDateTime(),
false,
null,
$comment,
true
);
}

public function clearHistory(Room $chat, string $actorType, string $actorId): IComment {
$this->commentsManager->deleteCommentsAtObject('chat', (string) $chat->getId());

Expand Down
60 changes: 59 additions & 1 deletion lib/Controller/ChatController.php
Original file line number Diff line number Diff line change
Expand Up @@ -751,6 +751,7 @@ public function deleteMessage(int $messageId): DataResponse {
* Edit a chat message
*
* @param int $messageId ID of the message
* @param string $message the message to send
* @psalm-param non-negative-int $messageId
* @return DataResponse<Http::STATUS_OK|Http::STATUS_ACCEPTED, TalkChatMessageWithParent, array{X-Chat-Last-Common-Read?: numeric-string}>|DataResponse<Http::STATUS_BAD_REQUEST|Http::STATUS_FORBIDDEN|Http::STATUS_NOT_FOUND|Http::STATUS_METHOD_NOT_ALLOWED, array<empty>, array{}>
*
Expand All @@ -766,7 +767,64 @@ public function deleteMessage(int $messageId): DataResponse {
#[RequireParticipant]
#[RequirePermission(permission: RequirePermission::CHAT)]
#[RequireReadWriteConversation]
public function editMessage(int $messageId): DataResponse {
public function editMessage(int $messageId, string $message): DataResponse {
try {
$comment = $this->chatManager->getComment($this->room, (string) $messageId);
} catch (NotFoundException $e) {
return new DataResponse([], Http::STATUS_NOT_FOUND);
}

$attendee = $this->participant->getAttendee();
$isOwnMessage = $comment->getActorType() === $attendee->getActorType()
&& $comment->getActorId() === $attendee->getActorId();

// Special case for if the message is a bridged message, then the message is the bridge bot's message.
$isOwnMessage = $isOwnMessage || ($comment->getActorType() === Attendee::ACTOR_BRIDGED && $attendee->getActorId() === MatterbridgeManager::BRIDGE_BOT_USERID);
if (!$isOwnMessage
&& (!$this->participant->hasModeratorPermissions(false)
|| $this->room->getType() === Room::TYPE_ONE_TO_ONE
|| $this->room->getType() === Room::TYPE_ONE_TO_ONE_FORMER)) {
// Actor is not a moderator or not the owner of the message
return new DataResponse([], Http::STATUS_FORBIDDEN);
}

if ($comment->getVerb() !== ChatManager::VERB_MESSAGE && $comment->getVerb() !== ChatManager::VERB_OBJECT_SHARED) {
// System message (since the message is not parsed, it has type "system")
return new DataResponse([], Http::STATUS_METHOD_NOT_ALLOWED);
}

$maxAge = $this->timeFactory->getDateTime();
$maxAge->sub(new \DateInterval('P1D'));
if ($comment->getCreationDateTime() < $maxAge) {
// Message is too old
return new DataResponse([], Http::STATUS_BAD_REQUEST);
}

$systemMessageComment = $this->chatManager->editMessage(
$this->room,
$comment,
$this->participant,
$this->timeFactory->getDateTime(),
$message
);

$systemMessage = $this->messageParser->createMessage($this->room, $this->participant, $systemMessageComment, $this->l);
$this->messageParser->parseMessage($systemMessage);

$comment = $this->chatManager->getComment($this->room, (string) $messageId);
$parseMessage = $this->messageParser->createMessage($this->room, $this->participant, $comment, $this->l);
$this->messageParser->parseMessage($parseMessage);

$data = $systemMessage->toArray($this->getResponseFormat());
$data['parent'] = $parseMessage->toArray($this->getResponseFormat());

$bridge = $this->matterbridgeManager->getBridgeOfRoom($this->room);

$headers = [];
if ($this->participant->getAttendee()->getReadPrivacy() === Participant::PRIVACY_PUBLIC) {
$headers = ['X-Chat-Last-Common-Read' => (string) $this->chatManager->getLastCommonReadMessage($this->room)];
}
return new DataResponse($data, $bridge['enabled'] ? Http::STATUS_ACCEPTED : Http::STATUS_OK, $headers);
}

/**
Expand Down
10 changes: 10 additions & 0 deletions lib/Model/Message.php
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,16 @@ public function toArray(string $format): array {
'markdown' => $this->getMessageType() === ChatManager::VERB_SYSTEM ? false : true,
];

$metaData = $this->getComment()->getMetaData();
if (!empty($metaData)) {
if (isset($metaData['last_edited_by_type'], $metaData['last_edited_by_id'], $metaData['last_edited_time'])) {
$data['lastEditActorDisplayName'] = $metaData['last_edited_by_id']; // FIXME @see MessageParser::setActor
$data['lastEditActorId'] = $metaData['last_edited_by_id'];
$data['lastEditActorType'] = $metaData['last_edited_by_type'];
$data['lastEditTimestamp'] = $metaData['last_edited_time'];
}
}

if ($this->getMessageType() === ChatManager::VERB_MESSAGE_DELETED) {
$data['deleted'] = true;
}
Expand Down

0 comments on commit 117a985

Please sign in to comment.