Merge branch '5.3' into 5.4
authorTim Düsterhus <duesterhus@woltlab.com>
Thu, 16 Mar 2023 14:54:21 +0000 (15:54 +0100)
committerTim Düsterhus <duesterhus@woltlab.com>
Thu, 16 Mar 2023 14:54:21 +0000 (15:54 +0100)
1  2 
wcfsetup/install/files/lib/data/article/Article.class.php

index d956daec942da6eeedf989fb5a6ebaae7c342f42,ba752e661af347473393f24847926066daf47b94..f19663724e47b9cacd7cf1060b3249060e3f7c2c
@@@ -15,396 -13,381 +15,400 @@@ use wcf\system\WCF
  
  /**
   * Represents a cms article.
 - * 
 - * @author    Marcel Werk
 - * @copyright 2001-2019 WoltLab GmbH
 - * @license   GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
 - * @package   WoltLabSuite\Core\Data\Article
 - * @since     3.0
   *
 - * @property-read     integer         $articleID              unique id of the article
 - * @property-read     integer|null    $userID                 id of the user the article belongs to or `null` if the user does not exist anymore
 - * @property-read     string          $username               name of the user the article belongs to
 - * @property-read     integer         $time                   timestamp at which the comment has been written
 - * @property-read     integer         $categoryID             id of the category the article belongs to
 - * @property-read     integer         $isMultilingual         is `1` if the article is available in multiple languages, otherwise `0`
 - * @property-read     integer         $publicationStatus      publication status of the article (see `Article::UNPUBLISHED`, `Article::PUBLISHED` and `Article::DELAYED_PUBLICATION`)
 - * @property-read     integer         $publicationDate        timestamp at which the article will be automatically published or `0` if it has already been published
 - * @property-read     integer         $enableComments         is `1` if comments are enabled for the article, otherwise `0`
 - * @property-read     integer         $comments               number of comments on the article
 - * @property-read     integer         $views                  number of times the article has been viewed
 - * @property-read     integer         $cumulativeLikes        cumulative result of likes (counting `+1`) and dislikes (counting `-1`) for the article
 - * @property-read     integer         $isDeleted              is 1 if the article is in trash bin, otherwise 0
 - * @property-read     integer         $hasLabels              is `1` if labels are assigned to the article
 + * @author  Marcel Werk
 + * @copyright   2001-2019 WoltLab GmbH
 + * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
 + * @package WoltLabSuite\Core\Data\Article
 + * @since   3.0
 + *
 + * @property-read   int $articleID      unique id of the article
 + * @property-read   int|null $userID         id of the user the article belongs to or `null` if the user does not exist anymore
 + * @property-read   string $username       name of the user the article belongs to
 + * @property-read   int $time           timestamp at which the comment has been written
 + * @property-read   int $categoryID     id of the category the article belongs to
 + * @property-read   int $isMultilingual     is `1` if the article is available in multiple languages, otherwise `0`
 + * @property-read   int $publicationStatus  publication status of the article (see `Article::UNPUBLISHED`, `Article::PUBLISHED` and `Article::DELAYED_PUBLICATION`)
 + * @property-read   int $publicationDate    timestamp at which the article will be automatically published or `0` if it has already been published
 + * @property-read   int $enableComments     is `1` if comments are enabled for the article, otherwise `0`
 + * @property-read   int $comments       number of comments on the article
 + * @property-read   int $views          number of times the article has been viewed
 + * @property-read   int $cumulativeLikes    cumulative result of likes (counting `+1`) and dislikes (counting `-1`) for the article
 + * @property-read   int $isDeleted      is 1 if the article is in trash bin, otherwise 0
 + * @property-read   int $hasLabels      is `1` if labels are assigned to the article
   */
 -class Article extends DatabaseObject implements ILinkableObject, IUserContent {
 -      /**
 -       * indicates that article is unpublished
 -       */
 -      const UNPUBLISHED = 0;
 -      
 -      /**
 -       * indicates that article is published
 -       */
 -      const PUBLISHED = 1;
 -      
 -      /**
 -       * indicates that the publication of an article is delayed
 -       */
 -      const DELAYED_PUBLICATION = 2;
 -      
 -      /**
 -       * article content grouped by language id
 -       * @var ArticleContent[]
 -       */
 -      public $articleContents;
 -      
 -      /**
 -       * language links
 -       * @var ArticleContent[]
 -       */
 -      public $languageLinks;
 -      
 -      /**
 -       * article's category
 -       * @var ArticleCategory
 -       */
 -      protected $category;
 -      
 -      /**
 -       * @var IArticleDiscussionProvider
 -       * @since       5.2
 -       */
 -      protected $discussionProvider;
 -      
 -      /**
 -       * Returns true if the active user can delete this article.
 -       *
 -       * @return      boolean
 -       */
 -      public function canDelete() {
 -              if (WCF::getSession()->getPermission('admin.content.article.canManageArticle')) {
 -                      return true;
 -              }
 -              
 -              if (WCF::getSession()->getPermission('admin.content.article.canManageOwnArticles') && $this->userID == WCF::getUser()->userID) {
 -                      return true;
 -              }
 -              
 -              return false;
 -      }
 -      
 -      /**
 -       * Returns true if the active user has access to this article.
 -       *
 -       * @return      boolean
 -       */
 -      public function canRead() {
 -              if ($this->isDeleted && !WCF::getSession()->getPermission('admin.content.article.canManageArticle')) {
 -                      return false;
 -              }
 -              
 -              if ($this->publicationStatus != self::PUBLISHED) {
 -                      if (!WCF::getSession()->getPermission('admin.content.article.canManageArticle') && (!WCF::getSession()->getPermission('admin.content.article.canContributeArticle') || $this->userID != WCF::getUser()->userID)) {
 -                              return false;
 -                      }
 -              }
 -              
 -              if ($this->getCategory()) {
 -                      return $this->getCategory()->isAccessible();
 -              }
 -              
 -              return WCF::getSession()->getPermission('user.article.canRead');
 -      }
 -      
 -      /**
 -       * Returns true if the current user can edit these article.
 -       * 
 -       * @return      boolean
 -       * @since       5.2
 -       */
 -      public function canEdit() {
 -              if (!$this->canRead()) {
 -                      return false;
 -              }
 -
 -              if (WCF::getSession()->getPermission('admin.content.article.canManageArticle')) {
 -                      return true; 
 -              }
 -              
 -              if (WCF::getSession()->getPermission('admin.content.article.canManageOwnArticles') && $this->userID == WCF::getUser()->userID) {
 -                      return true;
 -              }
 -              
 -              if ($this->publicationStatus != self::PUBLISHED) {
 -                      if (WCF::getSession()->getPermission('admin.content.article.canContributeArticle') && $this->userID == WCF::getUser()->userID) {
 -                              return false;
 -                      }
 -              }
 -              
 -              return false; 
 -      }
 -      
 -      /**
 -       * Returns true if the current user can publish these article. 
 -       * 
 -       * @return      boolean
 -       * @since       5.2
 -       */
 -      public function canPublish() {
 -              if (WCF::getSession()->getPermission('admin.content.article.canManageArticle')) {
 -                      return true;
 -              }
 -              
 -              if (WCF::getSession()->getPermission('admin.content.article.canManageOwnArticles') && $this->userID == WCF::getUser()->userID) {
 -                      return true;
 -              }
 -              
 -              return false;
 -      }
 -      
 -      /**
 -       * @inheritDoc
 -       */
 -      public function getLink() {
 -              if ($this->getArticleContent() !== null) {
 -                      return $this->getArticleContent()->getLink();
 -              }
 -              
 -              return '';
 -      }
 -      
 -      /**
 -       * Returns the article's title.
 -       *
 -       * @return      string
 -       */
 -      public function getTitle() {
 -              if ($this->getArticleContent() !== null) {
 -                      return $this->getArticleContent()->getTitle();
 -              }
 -              
 -              return '';
 -      }
 -      
 -      /**
 -       * Returns the article's unformatted teaser.
 -       *
 -       * @return      string
 -       */
 -      public function getTeaser() {
 -              if ($this->getArticleContent() !== null) {
 -                      return $this->getArticleContent()->getTeaser();
 -              }
 -              
 -              return '';
 -      }
 -      
 -      /**
 -       * Returns the article's formatted teaser.
 -       *
 -       * @return      string
 -       */
 -      public function getFormattedTeaser() {
 -              if ($this->getArticleContent() !== null) {
 -                      return $this->getArticleContent()->getFormattedTeaser();
 -              }
 -              
 -              return '';
 -      }
 -      
 -      /**
 -       * Returns the article's formatted content.
 -       *
 -       * @return      string
 -       */
 -      public function getFormattedContent() {
 -              if ($this->getArticleContent() !== null) {
 -                      return $this->getArticleContent()->getFormattedContent();
 -              }
 -              
 -              return '';
 -      }
 -      
 -      /**
 -       * Returns the active content version.
 -       *
 -       * @return      ArticleContent|null
 -       */
 -      public function getArticleContent() {
 -              $this->getArticleContents();
 -              
 -              if ($this->isMultilingual) {
 -                      if (isset($this->articleContents[WCF::getLanguage()->languageID])) {
 -                              return $this->articleContents[WCF::getLanguage()->languageID];
 -                      }
 -              }
 -              else {
 -                      if (!empty($this->articleContents[0])) {
 -                              return $this->articleContents[0];
 -                      }
 -              }
 -              
 -              return null;
 -      }
 -      
 -      /**
 -       * Returns the article's content.
 -       *
 -       * @return      ArticleContent[]
 -       */
 -      public function getArticleContents() {
 -              if ($this->articleContents === null) {
 -                      $this->articleContents = [];
 -                      
 -                      $sql = "SELECT  *
 -                              FROM    wcf" . WCF_N . "_article_content
 -                              WHERE   articleID = ?";
 -                      $statement = WCF::getDB()->prepareStatement($sql);
 -                      $statement->execute([$this->articleID]);
 -                      while ($row = $statement->fetchArray()) {
 -                              $this->articleContents[$row['languageID'] ?: 0] = new ArticleContent(null, $row);
 -                      }
 -              }
 -              
 -              return $this->articleContents;
 -      }
 -      
 -      /**
 -       * Returns the article's language links.
 -       *
 -       * @return      ArticleContent[]
 -       */
 -      public function getLanguageLinks() {
 -              if ($this->languageLinks === null) {
 -                      $this->languageLinks = [];
 -                      $sql = "SELECT  articleContentID, title, languageID
 -                              FROM    wcf" . WCF_N . "_article_content
 -                              WHERE   articleID = ?";
 -                      $statement = WCF::getDB()->prepareStatement($sql);
 -                      $statement->execute([$this->articleID]);
 -                      while ($row = $statement->fetchArray()) {
 -                              $this->languageLinks[$row['languageID'] ?: 0] = new ArticleContent(null, $row);
 -                      }
 -              }
 -              
 -              return $this->languageLinks;
 -      }
 -      
 -      /**
 -       * Returns the category of the article.
 -       *
 -       * @return      ArticleCategory
 -       */
 -      public function getCategory() {
 -              if ($this->category === null && $this->categoryID) {
 -                      $this->category = ArticleCategory::getCategory($this->categoryID);
 -              }
 -              
 -              return $this->category;
 -      }
 -      
 -      /**
 -       * Sets the discussion provider for this article.
 -       * 
 -       * @param       IArticleDiscussionProvider      $discussionProvider
 -       * @since       5.2
 -       */
 -      public function setDiscussionProvider(IArticleDiscussionProvider $discussionProvider) {
 -              $this->discussionProvider = $discussionProvider;
 -      }
 -      
 -      /**
 -       * Returns the responsible discussion provider for this article.
 -       * 
 -       * @return      IArticleDiscussionProvider
 -       * @since       5.2
 -       */
 -      public function getDiscussionProvider() {
 -              if ($this->discussionProvider === null) {
 -                      foreach (self::getAllDiscussionProviders() as $discussionProvider) {
 -                              if (call_user_func([$discussionProvider, 'isResponsible'], $this)) {
 -                                      $this->setDiscussionProvider(new $discussionProvider($this));
 -                                      break;
 -                              }
 -                      }
 -                      
 -                      if ($this->discussionProvider === null) {
 -                              throw new \RuntimeException('No discussion provider has claimed to be responsible for the article #' . $this->articleID);
 -                      }
 -              }
 -              
 -              return $this->discussionProvider;
 -      }
 -      
 -      /**
 -       * Returns the list of the available discussion providers.
 -       * 
 -       * @return      string[]
 -       * @since       5.2
 -       */
 -      public static function getAllDiscussionProviders() {
 -              /** @var string[] $discussionProviders */
 -              static $discussionProviders;
 -              
 -              if ($discussionProviders === null) {
 -                      $discussionProviders = [];
 -                      
 -                      $objectTypes = ObjectTypeCache::getInstance()->getObjectTypes('com.woltlab.wcf.article.discussionProvider');
 -                      $commentProvider = '';
 -                      foreach ($objectTypes as $objectType) {
 -                              // the comment and the "void" provider should always be the last in the list
 -                              if ($objectType->className === CommentArticleDiscussionProvider::class) {
 -                                      $commentProvider = $objectType->className;
 -                                      continue;
 -                              }
 -                              
 -                              $discussionProviders[] = $objectType->className;
 -                      }
 -                      
 -                      $discussionProviders[] = $commentProvider;
 -                      $discussionProviders[] = VoidArticleDiscussionProvider::class;
 -              }
 -              
 -              return $discussionProviders;
 -      }
 -      
 -      /**
 -       * @inheritDoc
 -       * @since       5.2
 -       */
 -      public function getTime() {
 -              return $this->time;
 -      }
 -      
 -      /**
 -       * @inheritDoc
 -       * @since       5.2
 -       */
 -      public function getUserID() {
 -              return $this->userID;
 -      }
 -      
 -      /**
 -       * @inheritDoc
 -       * @since       5.2
 -       */
 -      public function getUsername() {
 -              return $this->username;
 -      }
 +class Article extends DatabaseObject implements ILinkableObject, IUserContent
 +{
 +    /**
 +     * indicates that article is unpublished
 +     */
 +    const UNPUBLISHED = 0;
 +
 +    /**
 +     * indicates that article is published
 +     */
 +    const PUBLISHED = 1;
 +
 +    /**
 +     * indicates that the publication of an article is delayed
 +     */
 +    const DELAYED_PUBLICATION = 2;
 +
 +    /**
 +     * article content grouped by language id
 +     * @var ArticleContent[]
 +     */
 +    public $articleContents;
 +
 +    /**
 +     * language links
 +     * @var ArticleContent[]
 +     */
 +    public $languageLinks;
 +
 +    /**
 +     * article's category
 +     * @var ArticleCategory
 +     */
 +    protected $category;
 +
 +    /**
 +     * @var IArticleDiscussionProvider
 +     * @since   5.2
 +     */
 +    protected $discussionProvider;
 +
 +    /**
 +     * Returns true if the active user can delete this article.
 +     *
 +     * @return  bool
 +     */
 +    public function canDelete()
 +    {
 +        if (WCF::getSession()->getPermission('admin.content.article.canManageArticle')) {
 +            return true;
 +        }
 +
 +        if (WCF::getSession()->getPermission('admin.content.article.canManageOwnArticles') && $this->userID == WCF::getUser()->userID) {
 +            return true;
 +        }
 +
 +        return false;
 +    }
 +
 +    /**
 +     * Returns true if the active user has access to this article.
 +     *
 +     * @return  bool
 +     */
 +    public function canRead()
 +    {
 +        if ($this->isDeleted && !WCF::getSession()->getPermission('admin.content.article.canManageArticle')) {
 +            return false;
 +        }
 +
 +        if ($this->publicationStatus != self::PUBLISHED) {
 +            if (!WCF::getSession()->getPermission('admin.content.article.canManageArticle') && (!WCF::getSession()->getPermission('admin.content.article.canContributeArticle') || $this->userID != WCF::getUser()->userID)) {
 +                return false;
 +            }
 +        }
 +
 +        if ($this->getCategory()) {
 +            return $this->getCategory()->isAccessible();
 +        }
 +
 +        return WCF::getSession()->getPermission('user.article.canRead');
 +    }
 +
 +    /**
 +     * Returns true if the current user can edit these article.
 +     *
 +     * @return      bool
 +     * @since       5.2
 +     */
 +    public function canEdit()
 +    {
++        if (!$this->canRead()) {
++            return false;
++        }
++
 +        if (WCF::getSession()->getPermission('admin.content.article.canManageArticle')) {
 +            return true;
 +        }
 +
 +        if (WCF::getSession()->getPermission('admin.content.article.canManageOwnArticles') && $this->userID == WCF::getUser()->userID) {
 +            return true;
 +        }
 +
 +        if ($this->publicationStatus != self::PUBLISHED) {
 +            if (WCF::getSession()->getPermission('admin.content.article.canContributeArticle') && $this->userID == WCF::getUser()->userID) {
 +                return false;
 +            }
 +        }
 +
 +        return false;
 +    }
 +
 +    /**
 +     * Returns true if the current user can publish these article.
 +     *
 +     * @return      bool
 +     * @since       5.2
 +     */
 +    public function canPublish()
 +    {
 +        if (WCF::getSession()->getPermission('admin.content.article.canManageArticle')) {
 +            return true;
 +        }
 +
 +        if (WCF::getSession()->getPermission('admin.content.article.canManageOwnArticles') && $this->userID == WCF::getUser()->userID) {
 +            return true;
 +        }
 +
 +        return false;
 +    }
 +
 +    /**
 +     * @inheritDoc
 +     */
 +    public function getLink()
 +    {
 +        if ($this->getArticleContent() !== null) {
 +            return $this->getArticleContent()->getLink();
 +        }
 +
 +        return '';
 +    }
 +
 +    /**
 +     * Returns the article's title.
 +     *
 +     * @return      string
 +     */
 +    public function getTitle()
 +    {
 +        if ($this->getArticleContent() !== null) {
 +            return $this->getArticleContent()->getTitle();
 +        }
 +
 +        return '';
 +    }
 +
 +    /**
 +     * Returns the article's unformatted teaser.
 +     *
 +     * @return      string
 +     */
 +    public function getTeaser()
 +    {
 +        if ($this->getArticleContent() !== null) {
 +            return $this->getArticleContent()->getTeaser();
 +        }
 +
 +        return '';
 +    }
 +
 +    /**
 +     * Returns the article's formatted teaser.
 +     *
 +     * @return      string
 +     */
 +    public function getFormattedTeaser()
 +    {
 +        if ($this->getArticleContent() !== null) {
 +            return $this->getArticleContent()->getFormattedTeaser();
 +        }
 +
 +        return '';
 +    }
 +
 +    /**
 +     * Returns the article's formatted content.
 +     *
 +     * @return      string
 +     */
 +    public function getFormattedContent()
 +    {
 +        if ($this->getArticleContent() !== null) {
 +            return $this->getArticleContent()->getFormattedContent();
 +        }
 +
 +        return '';
 +    }
 +
 +    /**
 +     * Returns the active content version.
 +     *
 +     * @return  ArticleContent|null
 +     */
 +    public function getArticleContent()
 +    {
 +        $this->getArticleContents();
 +
 +        if ($this->isMultilingual) {
 +            if (isset($this->articleContents[WCF::getLanguage()->languageID])) {
 +                return $this->articleContents[WCF::getLanguage()->languageID];
 +            }
 +        } else {
 +            if (!empty($this->articleContents[0])) {
 +                return $this->articleContents[0];
 +            }
 +        }
 +
 +        return null;
 +    }
 +
 +    /**
 +     * Returns the article's content.
 +     *
 +     * @return  ArticleContent[]
 +     */
 +    public function getArticleContents()
 +    {
 +        if ($this->articleContents === null) {
 +            $this->articleContents = [];
 +
 +            $sql = "SELECT  *
 +                    FROM    wcf" . WCF_N . "_article_content
 +                    WHERE   articleID = ?";
 +            $statement = WCF::getDB()->prepareStatement($sql);
 +            $statement->execute([$this->articleID]);
 +            while ($row = $statement->fetchArray()) {
 +                $this->articleContents[$row['languageID'] ?: 0] = new ArticleContent(null, $row);
 +            }
 +        }
 +
 +        return $this->articleContents;
 +    }
 +
 +    /**
 +     * Returns the article's language links.
 +     *
 +     * @return  ArticleContent[]
 +     */
 +    public function getLanguageLinks()
 +    {
 +        if ($this->languageLinks === null) {
 +            $this->languageLinks = [];
 +            $sql = "SELECT  articleContentID, title, languageID
 +                    FROM    wcf" . WCF_N . "_article_content
 +                    WHERE   articleID = ?";
 +            $statement = WCF::getDB()->prepareStatement($sql);
 +            $statement->execute([$this->articleID]);
 +            while ($row = $statement->fetchArray()) {
 +                $this->languageLinks[$row['languageID'] ?: 0] = new ArticleContent(null, $row);
 +            }
 +        }
 +
 +        return $this->languageLinks;
 +    }
 +
 +    /**
 +     * Returns the category of the article.
 +     *
 +     * @return  ArticleCategory
 +     */
 +    public function getCategory()
 +    {
 +        if ($this->category === null && $this->categoryID) {
 +            $this->category = ArticleCategory::getCategory($this->categoryID);
 +        }
 +
 +        return $this->category;
 +    }
 +
 +    /**
 +     * Sets the discussion provider for this article.
 +     *
 +     * @param IArticleDiscussionProvider $discussionProvider
 +     * @since       5.2
 +     */
 +    public function setDiscussionProvider(IArticleDiscussionProvider $discussionProvider)
 +    {
 +        $this->discussionProvider = $discussionProvider;
 +    }
 +
 +    /**
 +     * Returns the responsible discussion provider for this article.
 +     *
 +     * @return      IArticleDiscussionProvider
 +     * @since       5.2
 +     */
 +    public function getDiscussionProvider()
 +    {
 +        if ($this->discussionProvider === null) {
 +            foreach (self::getAllDiscussionProviders() as $discussionProvider) {
 +                if (\call_user_func([$discussionProvider, 'isResponsible'], $this)) {
 +                    $this->setDiscussionProvider(new $discussionProvider($this));
 +                    break;
 +                }
 +            }
 +
 +            if ($this->discussionProvider === null) {
 +                throw new \RuntimeException('No discussion provider has claimed to be responsible for the article #' . $this->articleID);
 +            }
 +        }
 +
 +        return $this->discussionProvider;
 +    }
 +
 +    /**
 +     * Returns the list of the available discussion providers.
 +     *
 +     * @return      string[]
 +     * @since       5.2
 +     */
 +    public static function getAllDiscussionProviders()
 +    {
 +        /** @var string[] $discussionProviders */
 +        static $discussionProviders;
 +
 +        if ($discussionProviders === null) {
 +            $discussionProviders = [];
 +
 +            $objectTypes = ObjectTypeCache::getInstance()->getObjectTypes('com.woltlab.wcf.article.discussionProvider');
 +            $commentProvider = '';
 +            foreach ($objectTypes as $objectType) {
 +                // the comment and the "void" provider should always be the last in the list
 +                if ($objectType->className === CommentArticleDiscussionProvider::class) {
 +                    $commentProvider = $objectType->className;
 +                    continue;
 +                }
 +
 +                $discussionProviders[] = $objectType->className;
 +            }
 +
 +            $discussionProviders[] = $commentProvider;
 +            $discussionProviders[] = VoidArticleDiscussionProvider::class;
 +        }
 +
 +        return $discussionProviders;
 +    }
 +
 +    /**
 +     * @inheritDoc
 +     * @since       5.2
 +     */
 +    public function getTime()
 +    {
 +        return $this->time;
 +    }
 +
 +    /**
 +     * @inheritDoc
 +     * @since       5.2
 +     */
 +    public function getUserID()
 +    {
 +        return $this->userID;
 +    }
 +
 +    /**
 +     * @inheritDoc
 +     * @since       5.2
 +     */
 +    public function getUsername()
 +    {
 +        return $this->username;
 +    }
  }