'nullable_type_declaration_for_default_null_value' => true,
'static_lambda' => true,
- 'fully_qualified_strict_types' => true,
+ 'fully_qualified_strict_types' => ['leading_backslash_in_global_namespace' => true],
'no_unused_imports' => true,
'ordered_imports' => true,
use wcf\data\user\group\UserGroup;
use wcf\data\user\ignore\UserIgnore;
use wcf\data\user\UserProfile;
+use wcf\system\cache\runtime\ConversationMessageRuntimeCache;
use wcf\system\cache\runtime\UserProfileRuntimeCache;
use wcf\system\conversation\ConversationHandler;
use wcf\system\database\util\PreparedStatementConditionBuilder;
*/
protected $canAddUnrestricted;
- /**
- * first message object
- * @var ConversationMessage
- */
- protected $firstMessage;
-
/**
* true if the current user is an active participant of this conversation
* @var bool
return $this->canAddUnrestricted;
}
- /**
- * Returns the first message in this conversation.
- *
- * @return ConversationMessage
- */
- public function getFirstMessage()
- {
- if ($this->firstMessage === null) {
- $this->firstMessage = new ConversationMessage($this->firstMessageID);
- }
-
- return $this->firstMessage;
- }
-
- /**
- * Sets the first message.
- *
- * @param ConversationMessage $message
- */
- public function setFirstMessage(ConversationMessage $message)
+ public function getFirstMessage(): ?ConversationMessage
{
- $this->firstMessage = $message;
+ return ConversationMessageRuntimeCache::getInstance()->getObject($this->firstMessageID);
}
/**
'firstMessageID' => $resultValues['returnValues']->messageID,
]);
- $conversation->setFirstMessage($resultValues['returnValues']);
if (!$conversation->isDraft) {
// fire notification event
$notificationRecipients = \array_merge(
UserNotificationHandler::getInstance()->fireEvent(
'conversation',
'com.woltlab.wcf.conversation.notification',
- new ConversationUserNotificationObject($conversation),
+ new ConversationUserNotificationObject(new Conversation($conversation->conversationID)),
$notificationRecipients
);
}
*
* @method Conversation getDecoratedObject()
* @mixin Conversation
+ * @deprecated 6.1
*/
class FeedConversation extends DatabaseObjectDecorator implements IFeedEntry
{
* @method FeedConversation|null getSingleObject()
* @method FeedConversation|null search($objectID)
* @property FeedConversation[] $objects
+ * @deprecated 6.1
*/
class FeedConversationList extends ConversationList
{
use wcf\data\conversation\label\ConversationLabel;
use wcf\data\conversation\label\ConversationLabelList;
+use wcf\system\cache\runtime\ConversationMessageRuntimeCache;
use wcf\system\cache\runtime\UserProfileRuntimeCache;
use wcf\system\database\util\PreparedStatementConditionBuilder;
use wcf\system\WCF;
$labels = $this->loadLabelAssignments();
- $userIDs = [];
+ $userIDs = $messageIDs = [];
foreach ($this->objects as $conversationID => $conversation) {
if (isset($labels[$conversationID])) {
foreach ($labels[$conversationID] as $label) {
if ($conversation->lastPosterID) {
$userIDs[] = $conversation->lastPosterID;
}
+
+ if ($conversation->firstMessageID) {
+ $messageIDs[] = $conversation->firstMessageID;
+ }
}
- if (!empty($userIDs)) {
+ if ($userIDs !== []) {
UserProfileRuntimeCache::getInstance()->cacheObjectIDs($userIDs);
}
+ if ($messageIDs !== []) {
+ ConversationMessageRuntimeCache::getInstance()->cacheObjectIDs($userIDs);
+ }
}
}
* @author Alexander Ebert
* @copyright 2001-2019 WoltLab GmbH
* @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @deprecated 6.1 use `ConversationRssFeedPage` instead
*/
class ConversationFeedPage extends AbstractFeedPage
{
*/
public $loginRequired = true;
+ /**
+ * @inheritDoc
+ */
+ public function readParameters()
+ {
+ parent::readParameters();
+
+ $this->redirectToNewPage(ConversationRssFeedPage::class);
+ }
+
/**
* @inheritDoc
*/
--- /dev/null
+<?php
+
+namespace wcf\page;
+
+use wcf\data\conversation\UserConversationList;
+use wcf\system\rssFeed\RssFeed;
+use wcf\system\rssFeed\RssFeedItem;
+use wcf\system\WCF;
+
+/**
+ * Outputs a list of recent conversations as an rss feed.
+ *
+ * @author Marcel Werk
+ * @copyright 2001-2024 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ */
+class ConversationRssFeedPage extends AbstractRssFeedPage
+{
+ /**
+ * @inheritDoc
+ */
+ public $loginRequired = true;
+
+ protected UserConversationList $conversations;
+
+ #[\Override]
+ public function readData()
+ {
+ parent::readData();
+
+ $this->conversations = new UserConversationList(WCF::getUser()->userID);
+ $this->conversations->sqlLimit = 20;
+ $this->conversations->sqlOrderBy = 'conversation.lastPostTime DESC';
+ $this->conversations->readObjects();
+ }
+
+ #[\Override]
+ protected function getRssFeed(): RssFeed
+ {
+ $feed = new RssFeed();
+ $channel = $this->getDefaultChannel();
+ $channel->title(WCF::getLanguage()->get('wcf.conversation.conversations'));
+
+ if ($this->conversations->valid()) {
+ $channel->lastBuildDateFromTimestamp($this->conversations->current()->lastPostTime);
+ }
+ $feed->channel($channel);
+
+ foreach ($this->conversations as $conversation) {
+ $item = new RssFeedItem();
+ $item
+ ->title($conversation->getTitle())
+ ->link($conversation->getLink())
+ ->description($conversation->getFirstMessage()->getExcerpt())
+ ->pubDateFromTimestamp($conversation->lastPostTime)
+ ->creator($conversation->lastPoster)
+ ->guid($conversation->getLink())
+ ->contentEncoded($conversation->getFirstMessage()->getSimplifiedFormattedMessage())
+ ->slashComments($conversation->replies);
+
+ $channel->item($item);
+ }
+
+ return $feed;
+ }
+}
--- /dev/null
+<?php
+
+namespace wcf\system\cache\runtime;
+
+use wcf\data\conversation\message\ConversationMessage;
+use wcf\data\conversation\message\ConversationMessageList;
+
+/**
+ * Runtime cache implementation for conversation messages.
+ *
+ * @author Matthias Schmidt, Marcel Werk
+ * @copyright 2001-2024 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @since 6.1
+ *
+ * @method ConversationMessage[] getCachedObjects()
+ * @method ConversationMessage getObject($objectID)
+ * @method ConversationMessage[] getObjects(array $objectIDs)
+ */
+class ConversationMessageRuntimeCache extends AbstractRuntimeCache
+{
+ /**
+ * @inheritDoc
+ */
+ protected $listClassName = ConversationMessageList::class;
+}
{/capture}
{capture assign='headContent'}
- <link rel="alternate" type="application/rss+xml" title="{lang}wcf.global.button.rss{/lang}" href="{link controller='ConversationFeed'}at={@$__wcf->getUser()->userID}-{@$__wcf->getUser()->accessToken}{/link}">
+ <link rel="alternate" type="application/rss+xml" title="{lang}wcf.global.button.rss{/lang}" href="{link controller='ConversationRssFeed'}at={@$__wcf->getUser()->userID}-{@$__wcf->getUser()->accessToken}{/link}">
{/capture}
{capture assign='sidebarRight'}
{/capture}
{capture assign='contentInteractionDropdownItems'}
- <li><a rel="alternate" href="{link controller='ConversationFeed'}at={@$__wcf->getUser()->userID}-{@$__wcf->getUser()->accessToken}{/link}">{lang}wcf.global.button.rss{/lang}</a></li>
+ <li><a rel="alternate" href="{link controller='ConversationRssFeed'}at={@$__wcf->getUser()->userID}-{@$__wcf->getUser()->accessToken}{/link}">{lang}wcf.global.button.rss{/lang}</a></li>
{/capture}
{include file='header'}