From 445241cc29f0227665721b2ba8841e21399e8e7c Mon Sep 17 00:00:00 2001 From: =?utf8?q?Joshua=20R=C3=BCsweg?= Date: Wed, 29 Aug 2018 16:47:18 +0200 Subject: [PATCH] Add method to determine unread articles per category See #2648 --- .../lib/data/article/ArticleAction.class.php | 7 ++ .../data/article/ViewableArticle.class.php | 95 +++++++++++++++++++ 2 files changed, 102 insertions(+) diff --git a/wcfsetup/install/files/lib/data/article/ArticleAction.class.php b/wcfsetup/install/files/lib/data/article/ArticleAction.class.php index 4a86e8ac68..ee7eb976f8 100644 --- a/wcfsetup/install/files/lib/data/article/ArticleAction.class.php +++ b/wcfsetup/install/files/lib/data/article/ArticleAction.class.php @@ -140,6 +140,7 @@ class ArticleAction extends AbstractDatabaseObjectAction { if (ARTICLE_ENABLE_VISIT_TRACKING) { UserStorageHandler::getInstance()->resetAll('unreadArticles'); UserStorageHandler::getInstance()->resetAll('unreadWatchedArticles'); + UserStorageHandler::getInstance()->resetAll('unreadArticlesByCategory'); } if ($article->publicationStatus == Article::PUBLISHED) { @@ -257,6 +258,7 @@ class ArticleAction extends AbstractDatabaseObjectAction { if (ARTICLE_ENABLE_VISIT_TRACKING) { UserStorageHandler::getInstance()->resetAll('unreadArticles'); UserStorageHandler::getInstance()->resetAll('unreadWatchedArticles'); + UserStorageHandler::getInstance()->resetAll('unreadArticlesByCategory'); } $publicationStatus = (isset($this->parameters['data']['publicationStatus'])) ? $this->parameters['data']['publicationStatus'] : null; @@ -400,6 +402,7 @@ class ArticleAction extends AbstractDatabaseObjectAction { if (ARTICLE_ENABLE_VISIT_TRACKING) { UserStorageHandler::getInstance()->resetAll('unreadArticles'); UserStorageHandler::getInstance()->resetAll('unreadWatchedArticles'); + UserStorageHandler::getInstance()->resetAll('unreadArticlesByCategory'); } return ['objectIDs' => $this->objectIDs]; @@ -428,6 +431,7 @@ class ArticleAction extends AbstractDatabaseObjectAction { if (ARTICLE_ENABLE_VISIT_TRACKING) { UserStorageHandler::getInstance()->resetAll('unreadArticles'); UserStorageHandler::getInstance()->resetAll('unreadWatchedArticles'); + UserStorageHandler::getInstance()->resetAll('unreadArticlesByCategory'); } return ['objectIDs' => $this->objectIDs]; @@ -529,6 +533,7 @@ class ArticleAction extends AbstractDatabaseObjectAction { if (WCF::getUser()->userID) { UserStorageHandler::getInstance()->reset([WCF::getUser()->userID], 'unreadArticles'); UserStorageHandler::getInstance()->reset([WCF::getUser()->userID], 'unreadWatchedArticles'); + UserStorageHandler::getInstance()->reset([WCF::getUser()->userID], 'unreadArticlesByCategory'); } } @@ -542,6 +547,7 @@ class ArticleAction extends AbstractDatabaseObjectAction { if (WCF::getUser()->userID) { UserStorageHandler::getInstance()->reset([WCF::getUser()->userID], 'unreadArticles'); UserStorageHandler::getInstance()->reset([WCF::getUser()->userID], 'unreadWatchedArticles'); + UserStorageHandler::getInstance()->reset([WCF::getUser()->userID], 'unreadArticlesByCategory'); } } @@ -650,6 +656,7 @@ class ArticleAction extends AbstractDatabaseObjectAction { if (ARTICLE_ENABLE_VISIT_TRACKING) { UserStorageHandler::getInstance()->resetAll('unreadArticles'); UserStorageHandler::getInstance()->resetAll('unreadWatchedArticles'); + UserStorageHandler::getInstance()->resetAll('unreadArticlesByCategory'); } $this->unmarkItems(); diff --git a/wcfsetup/install/files/lib/data/article/ViewableArticle.class.php b/wcfsetup/install/files/lib/data/article/ViewableArticle.class.php index dd231f5ec8..7b74c1cc27 100644 --- a/wcfsetup/install/files/lib/data/article/ViewableArticle.class.php +++ b/wcfsetup/install/files/lib/data/article/ViewableArticle.class.php @@ -55,9 +55,17 @@ class ViewableArticle extends DatabaseObjectDecorator { /** * number of unread articles in watched categories * @var integer + * @since 3.2 */ protected static $unreadWatchedArticles; + /** + * number of unread articles ordered by categories + * @var integer + * @since 3.2 + */ + protected static $unreadArticlesByCategory; + /** * list of assigned labels * @var Label[] @@ -244,10 +252,97 @@ class ViewableArticle extends DatabaseObjectDecorator { return self::$unreadArticles; } + /** + * Returns the number of unread articles for a specific category. + * + * @param integer $articleCategoryID + * @return integer + * @since 3.2 + */ + public static function getUnreadArticlesForCategory($articleCategoryID) { + if (self::$unreadArticlesByCategory === null) { + self::$unreadArticlesByCategory = []; + + if (WCF::getUser()->userID) { + $unreadArticlesByCategory = UserStorageHandler::getInstance()->getField('unreadArticlesByCategory'); + + // cache does not exist or is outdated + if ($unreadArticlesByCategory === null) { + self::$unreadArticlesByCategory[$articleCategoryID] = self::fetchUnreadArticlesForCategory($articleCategoryID); + + // update storage unreadEntries + UserStorageHandler::getInstance()->update(WCF::getUser()->userID, 'unreadArticlesByCategory', serialize(self::$unreadArticlesByCategory)); + } + else { + $unreadArticlesByCategory = unserialize($unreadArticlesByCategory); + + if (isset($unreadArticlesByCategory[$articleCategoryID])) { + self::$unreadArticlesByCategory = $unreadArticlesByCategory; + } + else { + self::$unreadArticlesByCategory[$articleCategoryID] = self::fetchUnreadArticlesForCategory($articleCategoryID); + + // update storage unreadEntries + UserStorageHandler::getInstance()->update(WCF::getUser()->userID, 'unreadArticlesByCategory', serialize(self::$unreadArticlesByCategory)); + } + } + } + } + + return self::$unreadArticlesByCategory[$articleCategoryID]; + } + + /** + * Returns the unread article count for a specific category. + * + * @param integer $articleCategoryID + * @return integer + * @since 3.2 + */ + private static function fetchUnreadArticlesForCategory($articleCategoryID) { + $accessibleCategoryIDs = ArticleCategory::getAccessibleCategoryIDs(); + + if (!in_array($articleCategoryID, $accessibleCategoryIDs)) { + // the category is not accessible + return 0; + } + + $category = ArticleCategory::getCategory($articleCategoryID); + + if ($category === null) { + throw new \InvalidArgumentException('The given article category id "'.$articleCategoryID.'" is not valid.'); + } + + $categoryIDs = array_intersect(array_merge(array_map(function ($category) { + /** @var ArticleCategory $category */ + return $category->categoryID; + }, $category->getChildCategories()), [$articleCategoryID]), $accessibleCategoryIDs); + + $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()); + + return $statement->fetchSingleColumn(); + } + /** * Returns the number of unread articles in watched categories. * * @return integer + * @since 3.2 */ public static function getWatchedUnreadArticles() { if (self::$unreadWatchedArticles === null) { -- 2.20.1