<title language="en">Articles</title>
<page>com.woltlab.wcf.ArticleList</page>
</item>
+ <item identifier="com.woltlab.wcf.UnreadArticleList">
+ <parent>com.woltlab.wcf.ArticleList</parent>
+ <title language="de">Ungelesene Artikel</title>
+ <title language="en">Unread Articles</title>
+ <page>com.woltlab.wcf.UnreadArticleList</page>
+ </item>
<item identifier="com.woltlab.wcf.WatchedArticleList">
<parent>com.woltlab.wcf.ArticleList</parent>
<title language="de">Abonnierte Artikel</title>
<title>Artikel</title>
</content>
</page>
+ <page identifier="com.woltlab.wcf.UnreadArticleList">
+ <pageType>system</pageType>
+ <controller>wcf\page\UnreadArticleListPage</controller>
+ <handler>wcf\system\page\handler\UnreadArticleListPageHandler</handler>
+ <name language="de">Ungelesene Artikel</name>
+ <name language="en">Unread Articles</name>
+ <options>module_article</options>
+ <parent>com.woltlab.wcf.ArticleList</parent>
+
+ <content language="en">
+ <title>Unread Articles</title>
+ </content>
+ <content language="de">
+ <title>Ungelesene Artikel</title>
+ </content>
+ </page>
<page identifier="com.woltlab.wcf.WatchedArticleList">
<pageType>system</pageType>
<controller>wcf\page\WatchedArticleListPage</controller>
--- /dev/null
+{capture assign='headContent'}
+ {if $pageNo < $pages}
+ <link rel="next" href="{link controller='UnreadArticleList'}pageNo={@$pageNo+1}{/link}">
+ {/if}
+ {if $pageNo > 1}
+ <link rel="prev" href="{link controller='UnreadArticleList'}{if $pageNo > 2}pageNo={@$pageNo-1}{/if}{/link}">
+ {/if}
+{/capture}
+
+{capture assign='headerNavigation'}
+ {if ARTICLE_ENABLE_VISIT_TRACKING}
+ <li class="jsOnly"><a href="#" title="{lang}wcf.article.markAllAsRead{/lang}" class="markAllAsReadButton jsTooltip"><span class="icon icon16 fa-check"></span> <span class="invisible">{lang}wcf.article.markAllAsRead{/lang}</span></a></li>
+ {/if}
+{/capture}
+
+{capture assign='sidebarRight'}
+ {if !$labelGroups|empty}
+ <form id="sidebarForm" method="post" action="{link application='wcf' controller=$controllerName object=$controllerObject}{/link}">
+ <section class="box">
+ <h2 class="boxTitle">{lang}wcf.label.label{/lang}</h2>
+
+ <div class="boxContent">
+ <dl>
+ {foreach from=$labelGroups item=labelGroup}
+ {if $labelGroup|count}
+ <dt>{$labelGroup->getTitle()}</dt>
+ <dd>
+ <ul class="labelList jsOnly">
+ <li class="dropdown labelChooser" id="labelGroup{@$labelGroup->groupID}" data-group-id="{@$labelGroup->groupID}">
+ <div class="dropdownToggle" data-toggle="labelGroup{@$labelGroup->groupID}"><span class="badge label">{lang}wcf.label.none{/lang}</span></div>
+ <div class="dropdownMenu">
+ <ul class="scrollableDropdownMenu">
+ {foreach from=$labelGroup item=label}
+ <li data-label-id="{@$label->labelID}"><span><span class="badge label{if $label->getClassNames()} {@$label->getClassNames()}{/if}">{lang}{$label->label}{/lang}</span></span></li>
+ {/foreach}
+ </ul>
+ </div>
+ </li>
+ </ul>
+ <noscript>
+ {foreach from=$labelGroups item=labelGroup}
+ <select name="labelIDs[{@$labelGroup->groupID}]">
+ <option value="0">{lang}wcf.label.none{/lang}</option>
+ <option value="-1">{lang}wcf.label.withoutSelection{/lang}</option>
+ {foreach from=$labelGroup item=label}
+ <option value="{@$label->labelID}"{if $labelIDs[$labelGroup->groupID]|isset && $labelIDs[$labelGroup->groupID] == $label->labelID} selected{/if}>{lang}{$label->label}{/lang}</option>
+ {/foreach}
+ </select>
+ {/foreach}
+ </noscript>
+ </dd>
+ {/if}
+ {/foreach}
+ </dl>
+ <div class="formSubmit">
+ <input type="submit" value="{lang}wcf.global.button.submit{/lang}" accesskey="s">
+ </div>
+ </div>
+ </section>
+ </form>
+
+ <script data-relocate="true">
+ $(function() {
+ WCF.Language.addObject({
+ 'wcf.label.none': '{lang}wcf.label.none{/lang}',
+ 'wcf.label.withoutSelection': '{lang}wcf.label.withoutSelection{/lang}'
+ });
+
+ new WCF.Label.Chooser({ {implode from=$labelIDs key=groupID item=labelID}{@$groupID}: {@$labelID}{/implode} }, '#sidebarForm', undefined, true);
+ });
+ </script>
+ {/if}
+{/capture}
+
+{include file='header'}
+
+{hascontent}
+ <div class="paginationTop">
+ {content}
+ {pages print=true assign='pagesLinks' controller='UnreadArticleList' link="pageNo=%d"}
+ {/content}
+ </div>
+{/hascontent}
+
+{if $objects|count}
+ <div class="section">
+ {include file='articleListItems'}
+ </div>
+{else}
+ <p class="info">{lang}wcf.global.noItems{/lang}</p>
+{/if}
+
+<footer class="contentFooter">
+ {hascontent}
+ <div class="paginationBottom">
+ {content}{@$pagesLinks}{/content}
+ </div>
+ {/hascontent}
+
+ {hascontent}
+ <nav class="contentFooterNavigation">
+ <ul>
+ {content}{event name='contentFooterNavigation'}{/content}
+ </ul>
+ </nav>
+ {/hascontent}
+</footer>
+
+{if ARTICLE_ENABLE_VISIT_TRACKING}
+ <script data-relocate="true">
+ require(['WoltLabSuite/Core/Ui/Article/MarkAllAsRead'], function(UiArticleMarkAllAsRead) {
+ UiArticleMarkAllAsRead.init();
+ });
+ </script>
+{/if}
+
+{include file='footer'}
--- /dev/null
+<?php
+namespace wcf\page;
+use wcf\system\request\LinkHandler;
+use wcf\system\visitTracker\VisitTracker;
+use wcf\system\WCF;
+
+/**
+ * Shows a list of unread articles.
+ *
+ * @author Joshua Ruesweg
+ * @copyright 2001-2018 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package WoltLabSuite\Core\Page
+ * @since 3.2
+ */
+class UnreadArticleListPage extends ArticleListPage {
+ /**
+ * @inheritDoc
+ */
+ public $loginRequired = true;
+
+ /**
+ * @inheritDoc
+ */
+ public $neededModules = ['ARTICLE_ENABLE_VISIT_TRACKING', 'MODULE_ARTICLE'];
+
+ /**
+ * @inheritDoc
+ */
+ public $controllerName = 'UnreadArticleList';
+
+ /**
+ * @inheritDoc
+ */
+ public function readParameters() {
+ parent::readParameters();
+
+ $this->canonicalURL = LinkHandler::getInstance()->getLink('UnreadArticleList', $this->controllerParameters, ($this->pageNo > 1 ? 'pageNo=' . $this->pageNo : ''));
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function initObjectList() {
+ parent::initObjectList();
+
+ $this->objectList->getConditionBuilder()->add('article.time > ?', [VisitTracker::getInstance()->getVisitTime('com.woltlab.wcf.article')]);
+
+ if (WCF::getUser()->userID) {
+ $this->objectList->sqlConditionJoins = "LEFT JOIN wcf".WCF_N."_tracked_visit tracked_visit ON (tracked_visit.objectTypeID = ".VisitTracker::getInstance()->getObjectTypeID('com.woltlab.wcf.article')." AND tracked_visit.objectID = article.articleID AND tracked_visit.userID = ".WCF::getUser()->userID.")";
+ $this->objectList->getConditionBuilder()->add("(article.time > tracked_visit.visitTime OR tracked_visit.visitTime IS NULL)");
+ }
+ }
+}
--- /dev/null
+<?php
+namespace wcf\system\page\handler;
+use wcf\data\article\ViewableArticle;
+
+/**
+ * Page handler implementation for the page showing the list of unread articles.
+ *
+ * @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\Page\Handler
+ * @since 3.2
+ */
+class UnreadArticleListPageHandler extends AbstractMenuPageHandler {
+ /** @noinspection PhpMissingParentCallCommonInspection */
+ /**
+ * @inheritDoc
+ */
+ public function getOutstandingItemCount(/** @noinspection PhpUnusedParameterInspection */$objectID = null) {
+ return ViewableArticle::getUnreadArticles();
+ }
+
+ /** @noinspection PhpMissingParentCallCommonInspection */
+ /**
+ * @inheritDoc
+ */
+ public function isVisible(/** @noinspection PhpUnusedParameterInspection */$objectID = null) {
+ return ARTICLE_ENABLE_VISIT_TRACKING && !empty(ViewableArticle::getUnreadArticles());
+ }
+}