Add watched articles page
authorJoshua Rüsweg <josh@bastelstu.be>
Wed, 29 Aug 2018 13:04:54 +0000 (15:04 +0200)
committerJoshua Rüsweg <josh@bastelstu.be>
Wed, 29 Aug 2018 13:04:54 +0000 (15:04 +0200)
See #2642

com.woltlab.wcf/menuItem.xml
com.woltlab.wcf/page.xml
com.woltlab.wcf/templates/watchedArticleList.tpl [new file with mode: 0644]
wcfsetup/install/files/lib/data/article/ViewableArticle.class.php
wcfsetup/install/files/lib/page/WatchedArticleListPage.class.php [new file with mode: 0644]
wcfsetup/install/files/lib/system/page/handler/WatchedArticleListPageHandler.class.php [new file with mode: 0644]

index 7944df241ca69df283515f22501318e94a0bc330..8b46b4c481711b4a7edace3500a7060f3e293559 100644 (file)
                        <title language="en">Articles</title>
                        <page>com.woltlab.wcf.ArticleList</page>
                </item>
-               
+               <item identifier="com.woltlab.wcf.WatchedArticleList">
+                       <parent>com.woltlab.wcf.ArticleList</parent>
+                       <title language="de">Abonnierte Artikel</title>
+                       <title language="en">Watched Articles</title>
+                       <page>com.woltlab.wcf.WatchedArticleList</page>
+               </item>
                <item identifier="com.woltlab.wcf.MembersList">
                        <menu>com.woltlab.wcf.MainMenu</menu>
                        <title language="de">Mitglieder</title>
index 8ae5f9c88642ae7ebb718c3d490ce387cfa18c1c..a8b1cf596a99a631cd4c3795b76eeaeaa385ff25 100644 (file)
                                <title>Artikel</title>
                        </content>
                </page>
+               <page identifier="com.woltlab.wcf.WatchedArticleList">
+                       <pageType>system</pageType>
+                       <controller>wcf\page\WatchedArticleListPage</controller>
+                       <handler>wcf\system\page\handler\WatchedArticleListPageHandler</handler>
+                       <name language="de">Abonnierte Artikel</name>
+                       <name language="en">Watched Articles</name>
+                       <options>module_article</options>
+                       <parent>com.woltlab.wcf.ArticleList</parent>
+                       
+                       <content language="en">
+                               <title>Watched Articles</title>
+                       </content>
+                       <content language="de">
+                               <title>Abonnierte Artikel</title>
+                       </content>
+               </page>
                <page identifier="com.woltlab.wcf.CategoryArticleList">
                        <pageType>system</pageType>
                        <controller>wcf\page\CategoryArticleListPage</controller>
diff --git a/com.woltlab.wcf/templates/watchedArticleList.tpl b/com.woltlab.wcf/templates/watchedArticleList.tpl
new file mode 100644 (file)
index 0000000..51fa6ad
--- /dev/null
@@ -0,0 +1,117 @@
+{capture assign='headContent'}
+       {if $pageNo < $pages}
+               <link rel="next" href="{link controller='WatchedArticleList'}pageNo={@$pageNo+1}{/link}">
+       {/if}
+       {if $pageNo > 1}
+               <link rel="prev" href="{link controller='WatchedArticleList'}{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='WatchedArticleList' 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'}
index 0d48d4d22a15baf7de02052cd55be08bc1f8c926..dd231f5ec8325577faf664e94b7efc9598879b80 100644 (file)
@@ -52,6 +52,12 @@ class ViewableArticle extends DatabaseObjectDecorator {
         */
        protected static $unreadArticles;
        
+       /**
+        * number of unread articles in watched categories
+        * @var integer
+        */
+       protected static $unreadWatchedArticles;
+       
        /**
         * list of assigned labels
         * @var Label[]
@@ -237,4 +243,51 @@ class ViewableArticle extends DatabaseObjectDecorator {
                
                return self::$unreadArticles;
        }
+       
+       /**
+        * Returns the number of unread articles in watched categories.
+        *
+        * @return      integer
+        */
+       public static function getWatchedUnreadArticles() {
+               if (self::$unreadWatchedArticles === null) {
+                       self::$unreadWatchedArticles = 0;
+                       
+                       if (WCF::getUser()->userID) {
+                               $unreadArticles = UserStorageHandler::getInstance()->getField('unreadWatchedArticles');
+                               
+                               // cache does not exist or is outdated
+                               if ($unreadArticles === null) {
+                                       $categoryIDs = ArticleCategory::getSubscribedCategoryIDs();
+                                       if (!empty($categoryIDs)) {
+                                               $conditionBuilder = new PreparedStatementConditionBuilder();
+                                               $conditionBuilder->add('article.categoryID IN (?)', [$categoryIDs]);
+                                               $conditionBuilder->add('article.time > ?', [VisitTracker::getInstance()->getVisitTime('com.woltlab.wcf.article')]);
+                                               $conditionBuilder->add('article.isDeleted = ?', [0]);
+                                               $conditionBuilder->add('article.publicationStatus = ?', [Article::PUBLISHED]);
+                                               $conditionBuilder->add('(article.time > tracked_visit.visitTime OR tracked_visit.visitTime IS NULL)');
+                                               
+                                               $sql = "SELECT          COUNT(*)
+                                                       FROM            wcf".WCF_N."_article article
+                                                       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.")
+                                                       ".$conditionBuilder;
+                                               $statement = WCF::getDB()->prepareStatement($sql);
+                                               $statement->execute($conditionBuilder->getParameters());
+                                               self::$unreadWatchedArticles = $statement->fetchSingleColumn();
+                                       }
+                                       
+                                       // update storage unreadEntries
+                                       UserStorageHandler::getInstance()->update(WCF::getUser()->userID, 'unreadWatchedArticles', self::$unreadWatchedArticles);
+                               }
+                               else {
+                                       self::$unreadWatchedArticles = $unreadArticles;
+                               }
+                       }
+               }
+               
+               return self::$unreadWatchedArticles;
+       }
 }
diff --git a/wcfsetup/install/files/lib/page/WatchedArticleListPage.class.php b/wcfsetup/install/files/lib/page/WatchedArticleListPage.class.php
new file mode 100644 (file)
index 0000000..0f365e4
--- /dev/null
@@ -0,0 +1,49 @@
+<?php
+namespace wcf\page;
+use wcf\data\article\category\ArticleCategory;
+use wcf\data\article\CategoryArticleList;
+use wcf\system\exception\IllegalLinkException;
+use wcf\system\request\LinkHandler;
+
+/**
+ * Shows a list of articles in watched categories.
+ *
+ * @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 WatchedArticleListPage extends ArticleListPage {
+       /**
+        * @inheritDoc
+        */
+       public $loginRequired = true;
+       
+       /**
+        * @inheritDoc
+        */
+       public $controllerName = 'WatchedArticleList';
+       
+       /**
+        * @inheritDoc
+        */
+       public function readParameters() {
+               parent::readParameters();
+               
+               if (empty(ArticleCategory::getSubscribedCategoryIDs())) {
+                       throw new IllegalLinkException();
+               }
+               
+               $this->canonicalURL = LinkHandler::getInstance()->getLink('WatchedArticleList', $this->controllerParameters, ($this->pageNo > 1 ? 'pageNo=' . $this->pageNo : ''));
+       }
+       
+       /**
+        * @inheritDoc
+        */
+       protected function initObjectList() {
+               $this->objectList = new CategoryArticleList(ArticleCategory::getSubscribedCategoryIDs());
+               
+               $this->applyFilters();
+       }
+}
diff --git a/wcfsetup/install/files/lib/system/page/handler/WatchedArticleListPageHandler.class.php b/wcfsetup/install/files/lib/system/page/handler/WatchedArticleListPageHandler.class.php
new file mode 100644 (file)
index 0000000..1559744
--- /dev/null
@@ -0,0 +1,31 @@
+<?php
+namespace wcf\system\page\handler;
+use wcf\data\article\category\ArticleCategory;
+use wcf\data\article\ViewableArticle;
+
+/**
+ * Page handler implementation for the page showing the list of articles in watched categories.
+ *
+ * @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 WatchedArticleListPageHandler extends AbstractMenuPageHandler {
+       /** @noinspection PhpMissingParentCallCommonInspection */
+       /**
+        * @inheritDoc
+        */
+       public function getOutstandingItemCount(/** @noinspection PhpUnusedParameterInspection */$objectID = null) {
+               return ARTICLE_ENABLE_VISIT_TRACKING ? ViewableArticle::getWatchedUnreadArticles() : 0;
+       }
+       
+       /** @noinspection PhpMissingParentCallCommonInspection */
+       /**
+        * @inheritDoc
+        */
+       public function isVisible($objectID = null) {
+               return !empty(ArticleCategory::getSubscribedCategoryIDs());
+       }
+}