<definitionname>com.woltlab.wcf.user.objectWatch</definitionname>
<classname>wcf\system\user\object\watch\ArticleCategoryUserObjectWatch</classname>
</type>
+ <type>
+ <name>com.woltlab.wcf.article.notification</name>
+ <definitionname>com.woltlab.wcf.notification.objectType</definitionname>
+ <classname>wcf\system\user\notification\object\type\ArticleUserNotificationObjectType</classname>
+ <category>com.woltlab.wcf.page</category>
+ </type>
<!-- /articles -->
<type>
--- /dev/null
+{if $mimeType === 'text/plain'}
+{lang}{@$languageVariablePrefix}.mail.plaintext{/lang}
+
+{@$articleContent->getMailText($mimeType)} {* this line ends with a space *}
+{else}
+ {lang}{@$languageVariablePrefix}.mail.html{/lang}
+ {assign var='user' value=$event->getAuthor()}
+ {assign var='article' value=$event->getUserNotificationObject()}
+
+ {if $notificationType == 'instant'}{assign var='avatarSize' value=48}
+ {else}{assign var='avatarSize' value=32}{/if}
+ {capture assign='articleContent'}
+ <table cellpadding="0" cellspacing="0" border="0">
+ <tr>
+ <td><a href="{link controller='User' object=$user isEmail=true}{/link}" title="{$article->username}">{@$user->getAvatar()->getImageTag($avatarSize)}</a></td>
+ <td class="boxContent">
+ <div class="containerHeadline">
+ <h3>
+ {if $article->userID}
+ <a href="{link controller='User' object=$user isEmail=true}{/link}">{$article->username}</a>
+ {else}
+ {$article->username}
+ {/if}
+ ·
+ <a href="{$articleContent->getLink()}"><small>{$article->time|plainTime}</small></a>
+ </h3>
+ </div>
+ <div>
+ {@$articleContent->getMailText($mimeType)}
+ </div>
+ </td>
+ </tr>
+ </table>
+ {/capture}
+ {include file='email_paddingHelper' block=true class='box'|concat:$avatarSize content=$articleContent sandbox=true}
+{/if}
<classname>wcf\system\user\notification\event\ArticleCommentResponseOwnerUserNotificationEvent</classname>
<preset>1</preset>
</event>
+
+ <event>
+ <name>article</name>
+ <objecttype>com.woltlab.wcf.article.notification</objecttype>
+ <classname>wcf\system\user\notification\event\ArticleUserNotificationEvent</classname>
+ <preset>1</preset>
+ </event>
</import>
</data>
use wcf\system\request\LinkHandler;
use wcf\system\search\SearchIndexManager;
use wcf\system\tagging\TagEngine;
+use wcf\system\user\notification\object\ArticleUserNotificationObject;
+use wcf\system\user\notification\UserNotificationHandler;
+use wcf\system\user\object\watch\UserObjectWatchHandler;
use wcf\system\user\storage\UserStorageHandler;
use wcf\system\version\VersionTracker;
use wcf\system\visitTracker\VisitTracker;
// reset storage
if (ARTICLE_ENABLE_VISIT_TRACKING) {
UserStorageHandler::getInstance()->resetAll('unreadArticles');
+ UserStorageHandler::getInstance()->resetAll('unreadWatchedArticles');
}
if ($article->publicationStatus == Article::PUBLISHED) {
ArticleEditor::updateArticleCounter([$article->userID => 1]);
+
+ UserObjectWatchHandler::getInstance()->updateObject(
+ 'com.woltlab.wcf.article.category',
+ $article->getCategory()->categoryID,
+ 'article',
+ 'com.woltlab.wcf.article.notification',
+ new ArticleUserNotificationObject($article)
+ );
}
return $article;
$publicationStatus = (isset($this->parameters['data']['publicationStatus'])) ? $this->parameters['data']['publicationStatus'] : null;
if ($publicationStatus !== null) {
- $usersToArticles = [];
+ $usersToArticles = $resetNotifications = [];
/** @var ArticleEditor $articleEditor */
foreach ($this->objects as $articleEditor) {
if ($publicationStatus != $articleEditor->publicationStatus) {
$usersToArticles[$articleEditor->userID] += ($publicationStatus == Article::PUBLISHED) ? 1 : -1;
}
+
+ if ($publicationStatus == Article::PUBLISHED) {
+ UserObjectWatchHandler::getInstance()->updateObject(
+ 'com.woltlab.wcf.article.category',
+ $articleEditor->getCategory()->categoryID,
+ 'article',
+ 'com.woltlab.wcf.article.notification',
+ new ArticleUserNotificationObject($articleEditor->getDecoratedObject())
+ );
+ }
+ else {
+ $resetNotifications[] = $articleEditor->articleID;
+ }
}
}
+ if (!empty($resetNotifications)) {
+ // delete user notifications
+ UserNotificationHandler::getInstance()->removeNotifications('com.woltlab.wcf.article.notification', $resetNotifications);
+ }
+
if (!empty($usersToArticles)) {
ArticleEditor::updateArticleCounter($usersToArticles);
}
TagEngine::getInstance()->deleteObjects('com.woltlab.wcf.article', $articleContentIDs);
// delete entry from search index
SearchIndexManager::getInstance()->delete('com.woltlab.wcf.article', $articleContentIDs);
+ // delete user notifications
+ UserNotificationHandler::getInstance()->removeNotifications('com.woltlab.wcf.article.notification', $articleIDs);
}
$this->unmarkItems();
// reset storage
if (WCF::getUser()->userID) {
UserStorageHandler::getInstance()->reset([WCF::getUser()->userID], 'unreadArticles');
+ UserStorageHandler::getInstance()->reset([WCF::getUser()->userID], 'unreadWatchedArticles');
}
}
}
$usersToArticles[$articleEditor->userID]++;
+
+ UserObjectWatchHandler::getInstance()->updateObject(
+ 'com.woltlab.wcf.article.category',
+ $articleEditor->getCategory()->categoryID,
+ 'article',
+ 'com.woltlab.wcf.article.notification',
+ new ArticleUserNotificationObject($articleEditor->getDecoratedObject())
+ );
}
ArticleEditor::updateArticleCounter($usersToArticles);
+ // reset storage
+ if (ARTICLE_ENABLE_VISIT_TRACKING) {
+ UserStorageHandler::getInstance()->resetAll('unreadArticles');
+ UserStorageHandler::getInstance()->resetAll('unreadWatchedArticles');
+ }
+
$this->unmarkItems();
}
* Unpublishes articles.
*/
public function unpublish() {
- $usersToArticles = [];
+ $usersToArticles = $articleIDs = [];
foreach ($this->getObjects() as $articleEditor) {
$articleEditor->update(['publicationStatus' => Article::UNPUBLISHED]);
}
$usersToArticles[$articleEditor->userID]--;
+
+ $articleIDs[] = $articleEditor->articleID;
}
+ // delete user notifications
+ UserNotificationHandler::getInstance()->removeNotifications('com.woltlab.wcf.article.notification', $articleIDs);
+
ArticleEditor::updateArticleCounter($usersToArticles);
$this->unmarkItems();
return null;
}
+ /**
+ * Returns a version of this message optimized for use in emails.
+ *
+ * @param string $mimeType Either 'text/plain' or 'text/html'
+ * @return string
+ * @since 3.2
+ */
+ public function getMailText($mimeType = 'text/plain') {
+ switch ($mimeType) {
+ case 'text/plain':
+ $processor = new HtmlOutputProcessor();
+ $processor->setOutputType('text/plain');
+ $processor->process($this->content, 'com.woltlab.wcf.article.content', $this->articleContentID);
+
+ return $processor->getHtml();
+ case 'text/html':
+ // parse and return message
+ $processor = new HtmlOutputProcessor();
+ $processor->setOutputType('text/simplified-html');
+ $processor->process($this->content, 'com.woltlab.wcf.article.content', $this->articleContentID);
+
+ return $processor->getHtml();
+ }
+
+ throw new \LogicException('Unreachable');
+ }
+
/**
* Returns a certain article content or `null` if it does not exist.
*
--- /dev/null
+<?php
+namespace wcf\system\user\notification\event;
+use wcf\data\article\Article;
+use wcf\data\article\category\ArticleCategory;
+use wcf\data\user\UserProfile;
+use wcf\system\user\notification\object\ArticleUserNotificationObject;
+
+/**
+ * Notification event for receiving a user trophy.
+ *
+ * @author Joshua Ruesweg
+ * @copyright 2001-2018 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package WoltLabSuite\Core\System\User\Notification\Object\Type
+ *
+ * @method ArticleUserNotificationObject getUserNotificationObject()
+ */
+class ArticleUserNotificationEvent extends AbstractUserNotificationEvent implements ITestableUserNotificationEvent {
+ use TTestableUserNotificationEvent;
+ use TTestableArticleUserNotificationEvent;
+ use TTestableCategorizedUserNotificationEvent;
+
+ /**
+ * @inheritDoc
+ */
+ public function getTitle() {
+ return $this->getLanguage()->get('wcf.user.notification.article.title');
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function getMessage() {
+ return $this->getLanguage()->getDynamicVariable('wcf.user.notification.article.message', [
+ 'article' => $this->userNotificationObject,
+ 'author' => $this->author
+ ]);
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function getEmailMessage($notificationType = 'instant') {
+ if ($this->getUserNotificationObject()->isMultilingual) {
+ $articleContent = $this->getUserNotificationObject()->getArticleContents()[$this->getLanguage()->languageID];
+ }
+ else {
+ $articleContent = $this->getUserNotificationObject()->getArticleContents()[0];
+ }
+
+ return [
+ 'message-id' => 'com.woltlab.wcf.article/'.$this->getUserNotificationObject()->articleID,
+ 'template' => 'email_notification_article',
+ 'application' => 'wcf',
+ 'variables' => [
+ 'article' => $this->getUserNotificationObject(),
+ 'articleContent' => $articleContent,
+ 'languageVariablePrefix' => 'wcf.user.notification.article'
+ ]
+ ];
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function getLink() {
+ return $this->getUserNotificationObject()->getLink();
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function checkAccess() {
+ return $this->getUserNotificationObject()->canRead();
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public static function canBeTriggeredByGuests() {
+ return true;
+ }
+
+ /**
+ * @inheritDoc
+ * @return Article[]
+ */
+ public static function getTestObjects(UserProfile $recipient, UserProfile $author) {
+ return [new ArticleUserNotificationObject(self::getTestArticle(self::createTestCategory(ArticleCategory::OBJECT_TYPE_NAME), $author))];
+ }
+}
--- /dev/null
+<?php
+namespace wcf\system\user\notification\object;
+use wcf\data\article\Article;
+use wcf\data\DatabaseObjectDecorator;
+
+/**
+ * Represents an article user notification object.
+ *
+ * @author Joshua Ruesweg
+ * @copyright 2001-2018 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package WoltLabSuite\Core\System\User\Notification\Object
+ *
+ * @method Article getDecoratedObject()
+ * @mixin Article
+ */
+class ArticleUserNotificationObject extends DatabaseObjectDecorator implements IUserNotificationObject {
+ /**
+ * @inheritDoc
+ */
+ protected static $baseClass = Article::class;
+
+ /**
+ * @inheritDoc
+ */
+ public function getTitle() {
+ return $this->getDecoratedObject()->getTitle();
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function getURL() {
+ return $this->getDecoratedObject()->getLink();
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function getAuthorID() {
+ return $this->getDecoratedObject()->userID;
+ }
+}
--- /dev/null
+<?php
+namespace wcf\system\user\notification\object\type;
+use wcf\data\article\Article;
+use wcf\data\article\ArticleList;
+use wcf\system\user\notification\object\ArticleUserNotificationObject;
+
+/**
+ * Represents a gallery image as a notification object type.
+ *
+ * @author Joshua Ruesweg
+ * @copyright 2001-2018 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package WoltLabSuite\Core\System\User\Notification\Object\Type
+ */
+class ArticleUserNotificationObjectType extends AbstractUserNotificationObjectType {
+ /**
+ * @inheritDoc
+ */
+ protected static $decoratorClassName = ArticleUserNotificationObject::class;
+
+ /**
+ * @inheritDoc
+ */
+ protected static $objectClassName = Article::class;
+
+ /**
+ * @inheritDoc
+ */
+ protected static $objectListClassName = ArticleList::class;
+}
\ No newline at end of file
<item name="wcf.user.notification.articleComment.responseOwner.message.stacked"><![CDATA[{if $count < 4}{@$authors[0]->getAnchorTag()}{if $count != 1}{if $count == 2 && !$guestTimesTriggered} und {else}, {/if}{@$authors[1]->getAnchorTag()}{if $count == 3}{if !$guestTimesTriggered} und {else}, {/if} {@$authors[2]->getAnchorTag()}{/if}{/if}{if $guestTimesTriggered} und {if $guestTimesTriggered == 1}ein Gast{else}Gäste{/if}{/if}{else}{@$authors[0]->getAnchorTag()}{if $guestTimesTriggered},{else} und{/if} {#$others} weitere Benutzer {if $guestTimesTriggered}und {if $guestTimesTriggered == 1}ein Gast{else}Gäste{/if}{/if}{/if} haben Antworten zum Kommentar von {if $commentAuthor->userID}<a href="{link controller='User' object=$commentAuthor}{/link}" class="userLink" data-user-id="{@$commentAuthor->userID}">{$commentAuthor->username}</a>{else}{$commentAuthor->username}{/if} zu {if LANGUAGE_USE_INFORMAL_VARIANT}deinem{else}Ihrem{/if} Artikel <a href="{$article->getLink()}#comment{@$commentID}/response{@$responseID}">{$article->getTitle()}</a> verfasst.]]></item>
<item name="wcf.user.notification.articleComment.responseOwner.mail.plaintext"><![CDATA[{@$authorList} {if $count == 1 && $guestTimesTriggered < 2 && (!$event->getAuthor()->userID || $guestTimesTriggered == 0)}hat eine Antwort{else}haben Antworten{/if} zum Kommentar von {@$notificationContent[variables][commentAuthor]->username}{if $notificationContent[variables][commentAuthor]->userID} [URL:{link controller='User' object=$notificationContent[variables][commentAuthor] isEmail=true}{/link}]{/if} zu {if LANGUAGE_USE_INFORMAL_VARIANT}deinem{else}Ihrem{/if} Artikel „{@$notificationContent[variables][article]->getTitle()}“ [URL:{link controller='Article' object=$notificationContent[variables][article] isEmail=true}#comments/comment{@$commentID}/response{@$responseID}{/link}] verfasst{if $count == 1 && !$guestTimesTriggered}:{else}.{/if}]]></item>
<item name="wcf.user.notification.articleComment.responseOwner.mail.html"><![CDATA[<p>{@$authorList} {if $count == 1 && $guestTimesTriggered < 2 && (!$event->getAuthor()->userID || $guestTimesTriggered == 0)}hat eine Antwort{else}haben Antworten{/if} zum Kommentar von {if $notificationContent[variables][commentAuthor]->userID}<a href="{link controller='User' object=$notificationContent[variables][commentAuthor] isEmail=true}{/link}">{@$notificationContent[variables][commentAuthor]->username}</a>{else}{@$notificationContent[variables][commentAuthor]->username}{/if} zu {if LANGUAGE_USE_INFORMAL_VARIANT}deinem{else}Ihrem{/if} Artikel <a href="{link controller='Article' object=$notificationContent[variables][article] isEmail=true}#comments/comment{@$commentID}/response{@$responseID}{/link}">{@$notificationContent[variables][article]->getTitle()}</a> verfasst{if $count == 1 && !$guestTimesTriggered}:{else}.{/if}</p>]]></item>
+
+ <item name="wcf.user.notification.com.woltlab.wcf.article.notification.article"><![CDATA[Neuer Artikel in abonnierter Kategorie]]></item>
+ <item name="wcf.user.notification.article.title"><![CDATA[Neuer Artikel]]></item>
+ <item name="wcf.user.notification.article.message"><![CDATA[{if $author->userID}{@$author->getAnchorTag()}{else}Ein Gast{/if} hat den Artikel <a href="{$article->getLink()}">{$article->getTitle()}</a> verfasst.]]></item>
+ <item name="wcf.user.notification.article.mail.plaintext"><![CDATA[{if $event->getAuthor()->userID}{@$event->getAuthor()->username} [URL:{link controller='User' object=$event->getAuthor() isEmail=true}{/link}]{else}Ein Gast{/if} hat den Artikel „{@$notificationContent[variables][articleContent]->getTitle()}“ [URL:{link controller='Article' object=$notificationContent[variables][articleContent] isEmail=true}{/link}] verfasst:]]></item>
+ <item name="wcf.user.notification.article.mail.html"><![CDATA[<p>{if $event->getAuthor()->userID}<a href="{link controller='User' object=$event->getAuthor() isEmail=true}{/link}">{$event->getAuthor()->username}</a>{else}Ein Gast{/if} hat den Artikel <a href="{link controller='Article' object=$notificationContent[variables][articleContent] isEmail=true}{/link}">{$notificationContent[variables][articleContent]->getTitle()}</a> verfasst:</p>]]></item>
</category>
<category name="wcf.user.profile">
<item name="wcf.user.notification.articleComment.responseOwner.message.stacked"><![CDATA[{if $count < 4}{@$authors[0]->getAnchorTag()}{if $count != 1}{if $count == 2 && !$guestTimesTriggered} and {else}, {/if}{@$authors[1]->getAnchorTag()}{if $count == 3}{if !$guestTimesTriggered} and {else}, {/if} {@$authors[2]->getAnchorTag()}{/if}{/if}{if $guestTimesTriggered} and {if $guestTimesTriggered == 1}a guest{else}guests{/if}{/if}{else}{@$authors[0]->getAnchorTag()}{if $guestTimesTriggered},{else} and{/if} {#$others} other users {if $guestTimesTriggered}and {if $guestTimesTriggered == 1}a guest{else}guests{/if}{/if}{/if} replied to a comment by {if $author->userID}<a href="{link controller='User' object=$author}{/link}" class="userLink" data-user-id="{@$author->userID}">{$author->username}</a>{else}{$author->username}{/if} on your article <a href="{link controller='Article' object=$article}#comments/comment{@$commentID}{/link}">{$article->getTitle()}</a>.]]></item>
<item name="wcf.user.notification.articleComment.responseOwner.mail.plaintext"><![CDATA[{@$authorList} wrote {if $count == 1 && !$guestTimesTriggered}a reply{else}replies{/if} to {@$notificationContent[variables][commentAuthor]->username}’s{if $notificationContent[variables][commentAuthor]->userID} [URL:{link controller='User' object=$notificationContent[variables][commentAuthor] isEmail=true}{/link}]{/if} comment on your article “{@$article->getTitle()}” [URL:{link controller='Article' object=$article isEmail=true}#comments/comment{@$commentID}/response{@$responseID}{/link}]{if $count == 1 && !$guestTimesTriggered}:{else}.{/if}]]></item>
<item name="wcf.user.notification.articleComment.responseOwner.mail.html"><![CDATA[<p>{@$authorList} wrote {if $count == 1 && !$guestTimesTriggered}a reply{else}replies{/if} to {if $notificationContent[variables][commentAuthor]->userID}<a href="{link controller='User' object=$notificationContent[variables][commentAuthor] isEmail=true}{/link}">{$notificationContent[variables][commentAuthor]->username}</a>{else}{$notificationContent[variables][commentAuthor]->username}{/if}’s comment on your article <a href="{link controller='Article' object=$article isEmail=true}#comments/comment{@$commentID}/response{@$responseID}{/link}">{$article->getTitle()}</a>:</p>]]></item>
+
+ <item name="wcf.user.notification.com.woltlab.wcf.article.notification.article"><![CDATA[Notify me of new articles in watched categories]]></item>
+ <item name="wcf.user.notification.article.title"><![CDATA[New Article]]></item>
+ <item name="wcf.user.notification.article.message"><![CDATA[{if $author->userID}{@$author->getAnchorTag()}{else}A guest{/if} wrote the article <a href="{$article->getLink()}">{$article->getTitle()}</a>.]]></item>
+ <item name="wcf.user.notification.article.mail.plaintext"><![CDATA[{if $event->getAuthor()->userID}{@$event->getAuthor()->username} [URL:{link controller='User' object=$event->getAuthor() isEmail=true}{/link}]{else}A guest{/if} wrote the article “{@$notificationContent[variables][articleContent]->getTitle()}” [URL:{link controller='Article' object=$notificationContent[variables][articleContent] isEmail=true}{/link}]:]]></item>
+ <item name="wcf.user.notification.article.mail.html"><![CDATA[<p>{if $event->getAuthor()->userID}<a href="{link controller='User' object=$event->getAuthor() isEmail=true}{/link}">{$event->getAuthor()->username}</a>{else}A guest{/if} wrote the article <a href="{link controller='Article' object=$notificationContent[variables][articleContent] isEmail=true}{/link}">{$notificationContent[variables][articleContent]->getTitle()}</a>:</p>]]></item>
</category>
<category name="wcf.user.profile">