Added support for embedded objects in articles
authorMarcel Werk <burntime@woltlab.com>
Thu, 23 Jun 2016 14:01:29 +0000 (16:01 +0200)
committerMarcel Werk <burntime@woltlab.com>
Thu, 23 Jun 2016 14:01:34 +0000 (16:01 +0200)
com.woltlab.wcf/objectType.xml
wcfsetup/install/files/lib/acp/form/ArticleAddForm.class.php
wcfsetup/install/files/lib/acp/form/ArticleEditForm.class.php
wcfsetup/install/files/lib/data/article/Article.class.php
wcfsetup/install/files/lib/data/article/ArticleAction.class.php
wcfsetup/install/files/lib/data/article/content/ArticleContent.class.php
wcfsetup/install/files/lib/data/article/content/ViewableArticleContentList.class.php
wcfsetup/setup/db/install.sql

index d1b6f8fe4bc77adf98649081aae291d3988a4159..aca198bf12b21643a3d9a0b77c0e5beabef816f2 100644 (file)
                        <classname>wcf\system\search\ArticleSearch</classname>
                        <searchindex>wcf1_article_search_index</searchindex>
                </type>
+               <type>
+                       <name>com.woltlab.wcf.article.content</name>
+                       <definitionname>com.woltlab.wcf.message</definitionname>
+               </type>
                <!-- /articles -->
                
                <type>
index ad437df0f7261330a639fb824b397ad5fdf2250f..41f8d5158ba72b6084f94a8173b01325bd9e9430 100644 (file)
@@ -10,6 +10,7 @@ use wcf\data\media\ViewableMediaList;
 use wcf\data\user\User;
 use wcf\form\AbstractForm;
 use wcf\system\exception\UserInputException;
+use wcf\system\html\input\HtmlInputProcessor;
 use wcf\system\language\LanguageFactory;
 use wcf\system\request\LinkHandler;
 use wcf\system\WCF;
@@ -127,6 +128,11 @@ class ArticleAddForm extends AbstractForm {
         */
        public $content = [];
        
+       /**
+        * @var HtmlInputProcessor[]
+        */
+       public $htmlInputProcessors = [];
+       
        /**
         * image ids
         * @var integer[]
@@ -278,6 +284,9 @@ class ArticleAddForm extends AbstractForm {
                                if (empty($this->content[$language->languageID])) {
                                        throw new UserInputException('content'.$language->languageID);
                                }
+                               
+                               $this->htmlInputProcessors[$language->languageID] = new HtmlInputProcessor();
+                               $this->htmlInputProcessors[$language->languageID]->process($this->content[$language->languageID]);
                        }
                }
                else {
@@ -289,6 +298,9 @@ class ArticleAddForm extends AbstractForm {
                        if (empty($this->content[0])) {
                                throw new UserInputException('content');
                        }
+                       
+                       $this->htmlInputProcessors[0] = new HtmlInputProcessor();
+                       $this->htmlInputProcessors[0]->process($this->content[0]);
                }
                
        }
@@ -307,6 +319,7 @@ class ArticleAddForm extends AbstractForm {
                                        'tags' => (!empty($this->tags[$language->languageID]) ? $this->tags[$language->languageID] : []),
                                        'teaser' => (!empty($this->teaser[$language->languageID]) ? $this->teaser[$language->languageID] : ''),
                                        'content' => (!empty($this->content[$language->languageID]) ? $this->content[$language->languageID] : ''),
+                                       'htmlInputProcessor' => (isset($this->htmlInputProcessors[$language->languageID]) ? $this->htmlInputProcessors[$language->languageID] : null),
                                        'imageID' => (!empty($this->imageID[$language->languageID]) ? $this->imageID[$language->languageID] : null)
                                ];
                        }
@@ -317,6 +330,7 @@ class ArticleAddForm extends AbstractForm {
                                'tags' => (!empty($this->tags[0]) ? $this->tags[0] : []),
                                'teaser' => (!empty($this->teaser[0]) ? $this->teaser[0] : ''),
                                'content' => (!empty($this->content[0]) ? $this->content[0] : ''),
+                               'htmlInputProcessor' => (isset($this->htmlInputProcessors[0]) ? $this->htmlInputProcessors[0] : null),
                                'imageID' => (!empty($this->imageID[0]) ? $this->imageID[0] : null)
                        ];
                }
index c89afc91d4b8a3430e21da196c2fa8d9fdaf5849..01dead1c667a36f6ee157e0224c49ed1d31be9a6 100644 (file)
@@ -71,6 +71,7 @@ class ArticleEditForm extends ArticleAddForm {
                                        'tags' => (!empty($this->tags[$language->languageID]) ? $this->tags[$language->languageID] : []),
                                        'teaser' => (!empty($this->teaser[$language->languageID]) ? $this->teaser[$language->languageID] : ''),
                                        'content' => (!empty($this->content[$language->languageID]) ? $this->content[$language->languageID] : ''),
+                                       'htmlInputProcessor' => (isset($this->htmlInputProcessors[$language->languageID]) ? $this->htmlInputProcessors[$language->languageID] : null),
                                        'imageID' => (!empty($this->imageID[$language->languageID]) ? $this->imageID[$language->languageID] : null)
                                ];
                        }
@@ -81,6 +82,7 @@ class ArticleEditForm extends ArticleAddForm {
                                'tags' => (!empty($this->tags[0]) ? $this->tags[0] : []),
                                'teaser' => (!empty($this->teaser[0]) ? $this->teaser[0] : ''),
                                'content' => (!empty($this->content[0]) ? $this->content[0] : ''),
+                               'htmlInputProcessor' => (isset($this->htmlInputProcessors[0]) ? $this->htmlInputProcessors[0] : null),
                                'imageID' => (!empty($this->imageID[0]) ? $this->imageID[0] : null)
                        ];
                }
index 2402bc291a471238ea7719fc6aaad49b87419b87..95441a6dea57991b75e0fae207b665a7577cfeaa 100644 (file)
@@ -27,7 +27,7 @@ use wcf\system\WCF;
  * @property-read       integer         $enableComments
  * @property-read       integer         $comments
  * @property-read       integer         $views
- * @todo
+ * @property-read       integer         $cumulativeLikes
  */
 class Article extends DatabaseObject implements ILinkableObject {
        /**
index c40ac0f3056718f7b19f4ab7d7f42af179520281..3236d76d7c2fbfdca30101ee2a45d672398bddcb 100644 (file)
@@ -6,6 +6,7 @@ use wcf\data\AbstractDatabaseObjectAction;
 use wcf\system\comment\CommentHandler;
 use wcf\system\language\LanguageFactory;
 use wcf\system\like\LikeHandler;
+use wcf\system\message\embedded\object\MessageEmbeddedObjectManager;
 use wcf\system\search\SearchIndexManager;
 use wcf\system\tagging\TagEngine;
 
@@ -58,6 +59,11 @@ class ArticleAction extends AbstractDatabaseObjectAction {
                // save article content
                if (!empty($this->parameters['content'])) {
                        foreach ($this->parameters['content'] as $languageID => $content) {
+                               if (!empty($content['htmlInputProcessor'])) {
+                                       /** @noinspection PhpUndefinedMethodInspection */
+                                       $content['content'] = $content['htmlInputProcessor']->getHtml();
+                               }
+                               
                                /** @var ArticleContent $articleContent */
                                $articleContent = ArticleContentEditor::create([
                                        'articleID' => $article->articleID,
@@ -67,6 +73,7 @@ class ArticleAction extends AbstractDatabaseObjectAction {
                                        'content' => $content['content'],
                                        'imageID' => $content['imageID']
                                ]);
+                               $articleContentEditor = new ArticleContentEditor($articleContent);
                                
                                // save tags
                                if (!empty($content['tags'])) {
@@ -75,6 +82,13 @@ class ArticleAction extends AbstractDatabaseObjectAction {
                                
                                // update search index
                                SearchIndexManager::getInstance()->add('com.woltlab.wcf.article', $articleContent->articleContentID, $articleContent->content, $articleContent->title, $article->time, $article->userID, $article->username, ($languageID ?: null), $articleContent->teaser);
+                               
+                               // save embedded objects
+                               if (!empty($content['htmlInputProcessor'])) {
+                                       if (MessageEmbeddedObjectManager::getInstance()->registerObjects($content['htmlInputProcessor'], 'com.woltlab.wcf.article.content', $articleContent->articleContentID)) {
+                                               $articleContentEditor->update(['hasEmbeddedObjects' => 1]);
+                                       }
+                               }
                        }
                }
                
@@ -91,11 +105,17 @@ class ArticleAction extends AbstractDatabaseObjectAction {
                if (!empty($this->parameters['content'])) {
                        foreach ($this->getObjects() as $article) {
                                foreach ($this->parameters['content'] as $languageID => $content) {
+                                       if (!empty($content['htmlInputProcessor'])) {
+                                               /** @noinspection PhpUndefinedMethodInspection */
+                                               $content['content'] = $content['htmlInputProcessor']->getHtml();
+                                       }
+                                       
                                        $articleContent = ArticleContent::getArticleContent($article->articleID, ($languageID ?: null));
+                                       $articleContentEditor = null;
                                        if ($articleContent !== null) {
                                                // update
-                                               $editor = new ArticleContentEditor($articleContent);
-                                               $editor->update([
+                                               $articleContentEditor = new ArticleContentEditor($articleContent);
+                                               $articleContentEditor->update([
                                                        'title' => $content['title'],
                                                        'teaser' => $content['teaser'],
                                                        'content' => $content['content'],
@@ -109,6 +129,7 @@ class ArticleAction extends AbstractDatabaseObjectAction {
                                                }
                                        }
                                        else {
+                                               /** @var ArticleContent $articleContent */
                                                $articleContent = ArticleContentEditor::create([
                                                        'articleID' => $article->articleID,
                                                        'languageID' => ($languageID ?: null),
@@ -117,6 +138,7 @@ class ArticleAction extends AbstractDatabaseObjectAction {
                                                        'content' => $content['content'],
                                                        'imageID' => $content['imageID']
                                                ]);
+                                               $articleContentEditor = new ArticleContentEditor($articleContent);
                                        }
                                        
                                        // save tags
@@ -126,6 +148,13 @@ class ArticleAction extends AbstractDatabaseObjectAction {
                                        
                                        // update search index
                                        SearchIndexManager::getInstance()->add('com.woltlab.wcf.article', $articleContent->articleContentID, $articleContent->content, $articleContent->title, $article->time, $article->userID, $article->username, ($languageID ?: null), $articleContent->teaser);
+                                       
+                                       // save embedded objects
+                                       if (!empty($content['htmlInputProcessor'])) {
+                                               if ($articleContent->hasEmbeddedObjects != MessageEmbeddedObjectManager::getInstance()->registerObjects($content['htmlInputProcessor'], 'com.woltlab.wcf.article.content', $articleContent->articleContentID)) {
+                                                       $articleContentEditor->update(['hasEmbeddedObjects' => ($articleContent->hasEmbeddedObjects ? 0 : 1)]);
+                                               }
+                                       }
                                }
                        }
                }
index fa6408e17df814bdd857588fd7380622fac1980f..259a13acbdb73cd4c1e14db8efb472871dd2ac55 100644 (file)
@@ -10,7 +10,6 @@ use wcf\system\message\embedded\object\MessageEmbeddedObjectManager;
 use wcf\system\request\IRouteController;
 use wcf\system\request\LinkHandler;
 use wcf\system\WCF;
-use wcf\util\MessageUtil;
 use wcf\util\StringUtil;
 
 /**
@@ -29,6 +28,7 @@ use wcf\util\StringUtil;
  * @property-read      string          $content
  * @property-read      string          $teaser
  * @property-read      integer         $imageID
+ * @property-read      integer         $hasEmbeddedObjects
  */
 class ArticleContent extends DatabaseObject implements ILinkableObject, IRouteController {
        /**
@@ -94,7 +94,7 @@ class ArticleContent extends DatabaseObject implements ILinkableObject, IRouteCo
         */
        public function getFormattedContent() {
                // assign embedded objects
-               MessageEmbeddedObjectManager::getInstance()->setActiveMessage('com.woltlab.wcf.article', $this->articleContentID);
+               MessageEmbeddedObjectManager::getInstance()->setActiveMessage('com.woltlab.wcf.article.content', $this->articleContentID);
                
                // TODO
                return (new HtmlOutputProcessor())->process($this->content);
@@ -126,6 +126,7 @@ class ArticleContent extends DatabaseObject implements ILinkableObject, IRouteCo
                return null;
        }
        
+       
        public static function getArticleContent($articleID, $languageID) {
                if ($languageID !== null) {
                        $sql = "SELECT  *
index 40d85efee3de21cb877fb352d033424b195a6848..40b56d08065b62e3ee983754d50be60bc43179ce 100644 (file)
@@ -1,6 +1,7 @@
 <?php
 namespace wcf\data\article\content;
 use wcf\data\media\ViewableMediaList;
+use wcf\system\message\embedded\object\MessageEmbeddedObjectManager;
 
 /**
  * Represents a list of viewable article contents.
@@ -23,11 +24,14 @@ class ViewableArticleContentList extends ArticleContentList {
        public function readObjects() {
                parent::readObjects();
                
-               $imageIDs = [];
+               $imageIDs = $embeddedObjectPostIDs = [];
                foreach ($this->getObjects() as $articleContent) {
                        if ($articleContent->imageID) {
                                $imageIDs[] = $articleContent->imageID;
                        }
+                       if ($articleContent->hasEmbeddedObjects) {
+                               $embeddedObjectPostIDs[] = $articleContent->articleContentID;
+                       }
                }
                
                // cache images
@@ -37,11 +41,17 @@ class ViewableArticleContentList extends ArticleContentList {
                        $mediaList->readObjects();
                        $images = $mediaList->getObjects();
                        
+                       /** @var ViewableArticleContent $articleContent */
                        foreach ($this->getObjects() as $articleContent) {
                                if ($articleContent->imageID && isset($images[$articleContent->imageID])) {
                                        $articleContent->setImage($images[$articleContent->imageID]);
                                }
                        }
                }
+               
+               // load embedded objects
+               if (!empty($embeddedObjectPostIDs)) {
+                       MessageEmbeddedObjectManager::getInstance()->loadObjects('com.woltlab.wcf.article.content', $embeddedObjectPostIDs);
+               }
        }
 }
index 4feb29cbc953392f9c1cace147588ae0775e11ca..37984080add87baffd13336071c72da04dce9d1a 100644 (file)
@@ -166,7 +166,6 @@ CREATE TABLE wcf1_article (
        comments SMALLINT(5) NOT NULL DEFAULT 0,
        views MEDIUMINT(7) NOT NULL DEFAULT 0,
        cumulativeLikes MEDIUMINT(7) NOT NULL DEFAULT 0,
-       hasEmbeddedObjects TINYINT(1) NOT NULL DEFAULT 0,
        
        KEY (time)
 );
@@ -180,6 +179,7 @@ CREATE TABLE wcf1_article_content (
        teaser TEXT,
        content MEDIUMTEXT,
        imageID INT(10),
+       hasEmbeddedObjects TINYINT(1) NOT NULL DEFAULT 0,
        
        UNIQUE KEY (articleID, languageID)
 );