Track the number of articles written per user
authorAlexander Ebert <ebert@woltlab.com>
Wed, 27 Jun 2018 13:40:43 +0000 (15:40 +0200)
committerAlexander Ebert <ebert@woltlab.com>
Wed, 27 Jun 2018 13:40:43 +0000 (15:40 +0200)
See #2586

com.woltlab.wcf/option.xml
com.woltlab.wcf/templates/messageSidebar.tpl
constants.php
wcfsetup/install/files/lib/data/article/ArticleAction.class.php
wcfsetup/install/files/lib/data/article/ArticleEditor.class.php
wcfsetup/install/files/lib/data/user/User.class.php
wcfsetup/install/files/lib/page/ArticleListPage.class.php
wcfsetup/install/files/lib/system/worker/UserRebuildDataWorker.class.php
wcfsetup/install/lang/de.xml
wcfsetup/install/lang/en.xml
wcfsetup/setup/db/install.sql

index 1566dba96bdf312656cf6b7735f83b51471305ef..0411cbbbf5e260d9dd328c854748b9741fa8f360 100644 (file)
@@ -1476,6 +1476,11 @@ DESC:wcf.global.sortOrder.descending</selectoptions>
                                <defaultvalue>1</defaultvalue>
                                <options>module_trophy</options>
                        </option>
+                       <option name="message_sidebar_enable_articles">
+                               <categoryname>message.sidebar</categoryname>
+                               <optiontype>boolean</optiontype>
+                               <defaultvalue>1</defaultvalue>
+                       </option>
                        <option name="message_sidebar_user_options">
                                <categoryname>message.sidebar</categoryname>
                                <optiontype>useroptions</optiontype>
index 9e22ea6d0618a0260ebd7c3e9c18750ce0804659..37dd130a541e883a197d09d8f9dfdf373cdd0e5f 100644 (file)
                                                                <dd>{#$userProfile->trophyPoints}</dd>
                                                        {/if}
                                                        
+                                                       {if MESSAGE_SIDEBAR_ENABLE_ARTICLES && $userProfile->articles}
+                                                               <dt><a href="{link controller='ArticleList' userID=$userProfile->userID}{/link}" class="jsTooltip" title="{lang user=$userProfile}wcf.article.showArticlesWritten{/lang}">{lang}wcf.user.articles{/lang}</a></dt>
+                                                               <dd>{#$userProfile->articles}</dd>
+                                                       {/if}
+                                                       
                                                        {event name='userCredits'}
                                                        
                                                        {if MESSAGE_SIDEBAR_USER_OPTIONS && $userProfile->isAccessible('canViewProfile')}
index 19c0f160c1afcda3db3b16312dacc931ab5daf9b..986a884210c52c368a6248347036580e45448f25 100644 (file)
@@ -239,3 +239,4 @@ define('PAGE_LOGO_LINK_TO_APP_DEFAULT', 1);
 define('MODULE_USER_COVER_PHOTO', 1);
 define('IMAGE_ALLOW_EXTERNAL_SOURCE', 0);
 define('MESSAGE_ENABLE_TOC', 1);
+define('MESSAGE_SIDEBAR_ENABLE_ARTICLES', 1);
index 3fc74565735aacd1271183a73b5f9cf815fe8aa8..2e6e9444e6ba795875362b6025de49ac295b47f4 100644 (file)
@@ -138,6 +138,10 @@ class ArticleAction extends AbstractDatabaseObjectAction {
                        UserStorageHandler::getInstance()->resetAll('unreadArticles');
                }
                
+               if ($article->publicationStatus == Article::PUBLISHED) {
+                       ArticleEditor::updateArticleCounter([$article->userID => 1]);
+               }
+               
                return $article;
        }
        
@@ -241,6 +245,28 @@ class ArticleAction extends AbstractDatabaseObjectAction {
                if (ARTICLE_ENABLE_VISIT_TRACKING) {
                        UserStorageHandler::getInstance()->resetAll('unreadArticles');
                }
+               
+               $publicationStatus = (isset($this->parameters['data']['publicationStatus'])) ? $this->parameters['data']['publicationStatus'] : null;
+               if ($publicationStatus !== null) {
+                       $usersToArticles = [];
+                       /** @var ArticleEditor $articleEditor */
+                       foreach ($this->objects as $articleEditor) {
+                               if ($publicationStatus != $articleEditor->publicationStatus) {
+                                       // The article was published before or was now published.
+                                       if ($publicationStatus == Article::PUBLISHED || $articleEditor->publicationStatus == Article::PUBLISHED) {
+                                               if (!isset($usersToArticles[$articleEditor->userID])) {
+                                                       $usersToArticles[$articleEditor->userID] = 0;
+                                               }
+                                               
+                                               $usersToArticles[$articleEditor->userID] += ($publicationStatus == Article::PUBLISHED) ? 1 : -1;
+                                       }
+                               }
+                       }
+                       
+                       if (!empty($usersToArticles)) {
+                               ArticleEditor::updateArticleCounter($usersToArticles);
+                       }
+               }
        }
        
        /**
@@ -552,14 +578,23 @@ class ArticleAction extends AbstractDatabaseObjectAction {
         * Publishes articles.
         */
        public function publish() {
+               $usersToArticles = [];
                foreach ($this->getObjects() as $articleEditor) {
                        $articleEditor->update([
                                'time' => TIME_NOW,
                                'publicationStatus' => Article::PUBLISHED,
                                'publicationDate' => 0
                        ]);
+                       
+                       if (!isset($usersToArticles[$articleEditor->userID])) {
+                               $usersToArticles[$articleEditor->userID] = 0;
+                       }
+                       
+                       $usersToArticles[$articleEditor->userID]++;
                }
                
+               ArticleEditor::updateArticleCounter($usersToArticles);
+               
                $this->unmarkItems();
        }
        
@@ -590,10 +625,19 @@ class ArticleAction extends AbstractDatabaseObjectAction {
         * Unpublishes articles.
         */
        public function unpublish() {
+               $usersToArticles = [];
                foreach ($this->getObjects() as $articleEditor) {
                        $articleEditor->update(['publicationStatus' => Article::UNPUBLISHED]);
+                       
+                       if (!isset($usersToArticles[$articleEditor->userID])) {
+                               $usersToArticles[$articleEditor->userID] = 0;
+                       }
+                       
+                       $usersToArticles[$articleEditor->userID]--;
                }
                
+               ArticleEditor::updateArticleCounter($usersToArticles);
+               
                $this->unmarkItems();
        }
        
index 9993c673082583f7ed3dba8167ada68e479ce0ae..c48831ee734e941fb860475da86c9599834b17f2 100644 (file)
@@ -2,6 +2,7 @@
 declare(strict_types=1);
 namespace wcf\data\article;
 use wcf\data\DatabaseObjectEditor;
+use wcf\system\WCF;
 
 /**
  * Provides functions to edit cms articles.
@@ -21,4 +22,20 @@ class ArticleEditor extends DatabaseObjectEditor {
         * @inheritDoc
         */
        protected static $baseClass = Article::class;
+       
+       /**
+        * Updates the article counter of the given user ids.
+        * 
+        * @param       int[]   $users  user id => article counter increase/decrease
+        * @since       3.2
+        */
+       public static function updateArticleCounter(array $users) {
+               $sql = "UPDATE  wcf".WCF_N."_user
+                       SET     articles = articles + ?
+                       WHERE   userID = ?";
+               $statement = WCF::getDB()->prepareStatement($sql);
+               foreach ($users as $userID => $articles) {
+                       $statement->execute([$articles, $userID]);
+               }
+       }
 }
index 23946382d3581bc2c67ddb89a35c85c691208596..d2a8130937446a240be6dadb0dfcf9524e37d6a6 100644 (file)
@@ -70,6 +70,7 @@ use wcf\util\UserUtil;
  * @property-read       integer         $disableCoverPhoto              is `1` if the user's cover photo has been disabled, otherwise `0`
  * @property-read      string          $disableCoverPhotoReason        reason why the user's cover photo is disabled
  * @property-read      integer         $disableCoverPhotoExpires       timestamp at which the user's cover photo will automatically be enabled again
+ * @property-read      integer         $articles                       number of articles written by the user
  */
 final class User extends DatabaseObject implements IRouteController, IUserContent {
        /**
index ed848ae71f533b612729e4ade2050b630740ca31..430b23a48d67b9a1c22e6a794ef57d1548e60c87 100644 (file)
@@ -5,6 +5,8 @@ use wcf\data\article\category\ArticleCategory;
 use wcf\data\article\AccessibleArticleList;
 use wcf\data\label\group\ViewableLabelGroup;
 use wcf\data\object\type\ObjectTypeCache;
+use wcf\data\user\User;
+use wcf\system\exception\IllegalLinkException;
 use wcf\system\label\LabelHandler;
 use wcf\system\request\LinkHandler;
 use wcf\system\WCF;
@@ -69,6 +71,12 @@ class ArticleListPage extends MultipleLinkPage {
         */
        public $controllerParameters = ['application' => 'wcf'];
        
+       /**
+        * @var User
+        * @since 3.2
+        */
+       public $user;
+       
        /**
         * @inheritDoc
         */
@@ -96,6 +104,15 @@ class ArticleListPage extends MultipleLinkPage {
                        }
                }
                
+               if (!empty($_GET['userID'])) {
+                       $this->user = new User(intval($_GET['userID']));
+                       if (!$this->user->userID) {
+                               throw new IllegalLinkException();
+                       }
+                       
+                       $this->controllerParameters['userID'] = $this->user->userID;
+               }
+               
                if (!empty($_POST)) {
                        $labelParameters = '';
                        if (!empty($this->labelIDs)) {
@@ -108,7 +125,7 @@ class ArticleListPage extends MultipleLinkPage {
                        exit;
                }
                
-               $this->canonicalURL = LinkHandler::getInstance()->getLink('ArticleList', [], ($this->pageNo > 1 ? 'pageNo=' . $this->pageNo : ''));
+               $this->canonicalURL = LinkHandler::getInstance()->getLink('ArticleList', $this->controllerParameters, ($this->pageNo > 1 ? 'pageNo=' . $this->pageNo : ''));
        }
        
        /**
@@ -121,6 +138,10 @@ class ArticleListPage extends MultipleLinkPage {
        }
        
        protected function applyFilters() {
+               if ($this->user) {
+                       $this->objectList->getConditionBuilder()->add("article.userID = ?", [$this->user->userID]);
+               }
+               
                // filter by label
                if (!empty($this->labelIDs)) {
                        $objectTypeID = ObjectTypeCache::getInstance()->getObjectTypeByName('com.woltlab.wcf.label.object', 'com.woltlab.wcf.article')->objectTypeID;
@@ -156,7 +177,8 @@ class ArticleListPage extends MultipleLinkPage {
                        'labelGroups' => $this->labelGroups,
                        'labelIDs' => $this->labelIDs,
                        'controllerName' => $this->controllerName,
-                       'controllerObject' => null
+                       'controllerObject' => null,
+                       'user' => $this->user
                ]);
        }
 }
index 781f2f0a87e5f5ac655c7d61445c2f97c3f79f85..74ee8fa333784ff57b3b786ff6e680534c2b35a9 100644 (file)
@@ -71,10 +71,21 @@ class UserRebuildDataWorker extends AbstractRebuildDataWorker {
                        // update activity points
                        UserActivityPointHandler::getInstance()->updateUsers($userIDs);
                        
+                       // update article counter
+                       $conditionBuilder = new PreparedStatementConditionBuilder();
+                       $conditionBuilder->add('user_table.userID IN (?)', [$userIDs]);
+                       $sql = "UPDATE  wcf".WCF_N."_user user_table
+                               SET     articles = (
+                                               SELECT  COUNT(*)
+                                               FROM    wcf".WCF_N."_article
+                                               WHERE   userID = user_table.userID
+                                       )
+                               ".$conditionBuilder;
+                       $statement = WCF::getDB()->prepareStatement($sql);
+                       $statement->execute($conditionBuilder->getParameters());
+                       
                        // update like counter
                        if (MODULE_LIKE) {
-                               $conditionBuilder = new PreparedStatementConditionBuilder();
-                               $conditionBuilder->add('user_table.userID IN (?)', [$userIDs]);
                                $sql = "UPDATE  wcf".WCF_N."_user user_table
                                        SET     likesReceived = (
                                                        SELECT  COUNT(*)
@@ -89,8 +100,6 @@ class UserRebuildDataWorker extends AbstractRebuildDataWorker {
                        
                        // update trophy points
                        if (MODULE_TROPHY) {
-                               $conditionBuilder = new PreparedStatementConditionBuilder();
-                               $conditionBuilder->add('user_table.userID IN (?)', [$userIDs]);
                                $sql = "UPDATE  wcf".WCF_N."_user user_table
                                        SET     trophyPoints = (
                                                        SELECT          COUNT(*)
index 0881ea36dc991bf8c99f4914277c23b91f5deb2c..60312afed4effa3cf22b2ca4d5304cca8cf2cfb0 100644 (file)
@@ -2341,6 +2341,7 @@ Benutzerkontos nun in vollem Umfang nutzen.]]></item>
                <item name="wcf.article.sortField.time"><![CDATA[Datum]]></item>
                <item name="wcf.article.sortField.views"><![CDATA[Zugriffe]]></item>
                <item name="wcf.article.markAllAsRead"><![CDATA[Alle Artikel als gelesen markieren]]></item>
+               <item name="wcf.article.showArticlesWritten"><![CDATA[Artikel von {$user->username}]]></item>
        </category>
        
        <category name="wcf.attachment">
@@ -3692,6 +3693,7 @@ sich{/if} nicht bei uns registriert {if LANGUAGE_USE_INFORMAL_VARIANT}hast{else}
                <item name="wcf.user.sortField.registrationDate"><![CDATA[Registrierungsdatum]]></item>
                <item name="wcf.user.sortField.username"><![CDATA[Benutzername]]></item>
                <item name="wcf.user.sortField.lastActivityTime"><![CDATA[Letzte Aktivität]]></item>
+               <item name="wcf.user.articles"><![CDATA[Artikel]]></item>
        </category>
        
        <category name="wcf.user.menu">
index ded15c36fe10a6b9b1ddcacec32cb5446d49468b..817412d9517ff5842ff097f2a08c05b2a361e969 100644 (file)
@@ -2277,6 +2277,7 @@ full extend.]]></item>
                <item name="wcf.article.sortField.time"><![CDATA[Date]]></item>
                <item name="wcf.article.sortField.views"><![CDATA[Views]]></item>
                <item name="wcf.article.markAllAsRead"><![CDATA[Mark All Articles as Read]]></item>
+               <item name="wcf.article.showArticlesWritten"><![CDATA[Articles by {$user->username}]]></item>
        </category>
        
        <category name="wcf.attachment">
@@ -3686,6 +3687,7 @@ not register with us.]]></item>
                <item name="wcf.user.sortField.registrationDate"><![CDATA[Registration Date]]></item>
                <item name="wcf.user.sortField.username"><![CDATA[Username]]></item>
                <item name="wcf.user.sortField.lastActivityTime"><![CDATA[Last Activity]]></item>
+               <item name="wcf.user.articles"><![CDATA[Articles]]></item>
        </category>
        
        <category name="wcf.user.menu">
index c4f7555de58fd82736ec1a85fcdf33097aa05089..83e79596e2bb6e0e520af8bb3616fce8cb06955e 100644 (file)
@@ -1432,6 +1432,7 @@ CREATE TABLE wcf1_user (
        disableCoverPhoto TINYINT(1) NOT NULL DEFAULT 0,
        disableCoverPhotoReason TEXT,
        disableCoverPhotoExpires INT(10) NOT NULL DEFAULT 0,
+       articles INT(10) NOT NULL DEFAULT 0,
        
        KEY username (username),
        KEY email (email),