diff --git a/files/lib/data/conversation/message/ConversationMessage.class.php b/files/lib/data/conversation/message/ConversationMessage.class.php
index bfb720ea..f75683c9 100644
--- a/files/lib/data/conversation/message/ConversationMessage.class.php
+++ b/files/lib/data/conversation/message/ConversationMessage.class.php
@@ -225,4 +225,26 @@ public function getUserProfile(): UserProfile
{
return $this->getCollection()->getUserProfile($this);
}
+
+ /**
+ * Checks if the current user can read this particular message.
+ */
+ public function canRead(): bool
+ {
+ $conversation = $this->getConversation();
+ if ($conversation === null) {
+ return false;
+ }
+
+ $participant = $conversation->getParticipant();
+ if ($participant === null) {
+ return false;
+ }
+
+ if ($participant->hasJoinedAfter($this->time) || $participant->hasLeftBefore($this->time)) {
+ return false;
+ }
+
+ return true;
+ }
}
diff --git a/files/lib/data/conversation/participant/ConversationParticipant.class.php b/files/lib/data/conversation/participant/ConversationParticipant.class.php
index b531f67a..ec288e2d 100644
--- a/files/lib/data/conversation/participant/ConversationParticipant.class.php
+++ b/files/lib/data/conversation/participant/ConversationParticipant.class.php
@@ -37,6 +37,32 @@ class ConversationParticipant extends DatabaseObject
*/
protected static $databaseTableIndexName = 'conversationParticipantID';
+ /**
+ * Returns true if the user has joined the conversation after the provided
+ * timestamp.
+ */
+ public function hasJoinedAfter(int $timestamp): bool
+ {
+ if ($this->joinedAt === 0) {
+ return false;
+ }
+
+ return $this->joinedAt > $timestamp;
+ }
+
+ /**
+ * Returns true if the user has left the conversation before the provided
+ * timestamp.
+ */
+ public function hasLeftBefore(int $timestamp): bool
+ {
+ if ($this->leftAt === 0) {
+ return false;
+ }
+
+ return $this->leftAt < $timestamp;
+ }
+
public static function getParticipant(int $conversationID, int $userID): ?static
{
$sql = "SELECT * FROM wcf1_conversation_to_user WHERE conversationID = ? AND userID = ?";
diff --git a/files/lib/system/message/quote/ConversationMessageQuoteHandler.class.php b/files/lib/system/message/quote/ConversationMessageQuoteHandler.class.php
new file mode 100644
index 00000000..718af831
--- /dev/null
+++ b/files/lib/system/message/quote/ConversationMessageQuoteHandler.class.php
@@ -0,0 +1,35 @@
+
+ */
+final class ConversationMessageQuoteHandler extends AbstractMessageQuoteHandler
+{
+ #[\Override]
+ public function getMessage(int $objectID): ?IMessage
+ {
+ $message = new ConversationMessage($objectID);
+ if (!$message->messageID) {
+ return null;
+ }
+
+ if (!$message->canRead()) {
+ return null;
+ }
+
+ if ($message->hasEmbeddedObjects) {
+ $message->getCollection()->loadEmbeddedObjects();
+ }
+
+ return $message;
+ }
+}
diff --git a/objectType.xml b/objectType.xml
index 60e28252..23b9ea24 100644
--- a/objectType.xml
+++ b/objectType.xml
@@ -32,6 +32,7 @@
com.woltlab.wcf.conversation.message
com.woltlab.wcf.message.quote
+ wcf\system\message\quote\ConversationMessageQuoteHandler
com.woltlab.wcf.conversation.message
diff --git a/templates/conversation.tpl b/templates/conversation.tpl
index 490b3e11..e18b8334 100644
--- a/templates/conversation.tpl
+++ b/templates/conversation.tpl
@@ -84,7 +84,7 @@
], ({ UiConversationMessageInlineEditor }, { registerContainer }, { getParticipantList }) => {
new UiConversationMessageInlineEditor({$conversation->conversationID});
- registerContainer(".message", ".messageBody", "wcf\\data\\conversation\\message\\ConversationMessage", "com.woltlab.wcf.conversation.message");
+ registerContainer(".message", ".messageBody", "com.woltlab.wcf.conversation.message");
const contextMenu = document.getElementById('{unsafe:$interactionContextMenu->getContainerID()|encodeJS}')
console.log(contextMenu);