Added support for embedded objects in cms pages / boxes
authorMarcel Werk <burntime@woltlab.com>
Wed, 29 Jun 2016 21:42:04 +0000 (23:42 +0200)
committerMarcel Werk <burntime@woltlab.com>
Wed, 29 Jun 2016 21:42:09 +0000 (23:42 +0200)
23 files changed:
com.woltlab.wcf/objectType.xml
com.woltlab.wcf/templates/cms.tpl
wcfsetup/install/files/acp/templates/boxAdd.tpl
wcfsetup/install/files/lib/acp/form/BoxAddForm.class.php
wcfsetup/install/files/lib/acp/form/BoxEditForm.class.php
wcfsetup/install/files/lib/acp/form/PageAddForm.class.php
wcfsetup/install/files/lib/acp/form/PageEditForm.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/box/Box.class.php
wcfsetup/install/files/lib/data/box/BoxAction.class.php
wcfsetup/install/files/lib/data/box/content/BoxContent.class.php [new file with mode: 0644]
wcfsetup/install/files/lib/data/box/content/BoxContentAction.class.php [new file with mode: 0644]
wcfsetup/install/files/lib/data/box/content/BoxContentEditor.class.php [new file with mode: 0644]
wcfsetup/install/files/lib/data/box/content/BoxContentList.class.php [new file with mode: 0644]
wcfsetup/install/files/lib/data/page/Page.class.php
wcfsetup/install/files/lib/data/page/PageAction.class.php
wcfsetup/install/files/lib/data/page/content/PageContent.class.php [new file with mode: 0644]
wcfsetup/install/files/lib/data/page/content/PageContentAction.class.php [new file with mode: 0644]
wcfsetup/install/files/lib/data/page/content/PageContentEditor.class.php [new file with mode: 0644]
wcfsetup/install/files/lib/data/page/content/PageContentList.class.php [new file with mode: 0644]
wcfsetup/install/files/lib/page/CmsPage.class.php
wcfsetup/setup/db/install.sql

index 2f06ec0aca79cc65de0d0361a0e3ef14910fba82..31b9dd79b510cfe5cda9a96b919ad41c23f8b55d 100644 (file)
                </type>
                <!-- /articles -->
                
+               <type>
+                       <name>com.woltlab.wcf.page.content</name>
+                       <definitionname>com.woltlab.wcf.message</definitionname>
+               </type>
+               <type>
+                       <name>com.woltlab.wcf.box.content</name>
+                       <definitionname>com.woltlab.wcf.message</definitionname>
+               </type>
+               
                <type>
                        <name>com.woltlab.wcf.bbcode.smiley</name>
                        <definitionname>com.woltlab.wcf.category</definitionname>
index 2681ff76d531d481aa5a631945f3d0ad266308c6..f52d98158043f7951ab0151073ba267f2aef63a0 100644 (file)
@@ -1,6 +1,6 @@
 {if !$__wcf->isLandingPage()}
-       {capture assign='pageTitle'}{$content[title]}{/capture}
-       {capture assign='contentTitle'}{$content[title]}{/capture}
+       {capture assign='pageTitle'}{$content->title}{/capture}
+       {capture assign='contentTitle'}{$content->title}{/capture}
 {/if}
 
 {capture assign='headContent'}
 
 {include file='header'}
 
-{if $content[content]}
+{if $content->content}
        {if $page->pageType == 'text'}
                <section class="section cmsContent htmlContent">
-                       {@$content[content]}
+                       {@$content->getFormattedContent()}
                </section>
        {elseif $page->pageType == 'html'}
-               {@$content[content]}
+               {@$content->content}
        {elseif $page->pageType == 'tpl'}
                {include file=$page->getTplName($contentLanguageID)}
        {/if}
index 2a55a88ebad7dcd73d87286cf4cd3e64a737584e..1e6b0501ea8a49e6d1f1f23185bda86a5288b6c9 100644 (file)
                                                                {/if}
                                                                
                                                                <dl{if $errorField == 'title'|concat:$availableLanguage->languageID} class="formError"{/if}>
-                                                                       <dt><label for="title{@$availableLanguage->languageID}">{lang}wcf.acp.box.title{/lang}</label></dt>
+                                                                       <dt><label for="title{@$availableLanguage->languageID}">{lang}wcf.global.title{/lang}</label></dt>
                                                                        <dd>
                                                                                <input type="text" id="title{@$availableLanguage->languageID}" name="title[{@$availableLanguage->languageID}]" value="{if !$title[$availableLanguage->languageID]|empty}{$title[$availableLanguage->languageID]}{/if}" class="long" maxlength="255">
                                                                                {if $errorField == 'title'|concat:$availableLanguage->languageID}
index 8e3d547045140ad2a590783c21e3ae06c667abab..0705b0ae29d900fa2de18ee01eb9575388f8fc5f 100644 (file)
@@ -15,6 +15,7 @@ use wcf\system\box\IConditionBoxController;
 use wcf\system\database\util\PreparedStatementConditionBuilder;
 use wcf\system\exception\IllegalLinkException;
 use wcf\system\exception\UserInputException;
+use wcf\system\html\input\HtmlInputProcessor;
 use wcf\system\language\LanguageFactory;
 use wcf\system\page\handler\ILookupPageHandler;
 use wcf\system\request\LinkHandler;
@@ -175,6 +176,11 @@ class BoxAddForm extends AbstractForm {
         */
        public $aclValues = [];
        
+       /**
+        * @var HtmlInputProcessor[]
+        */
+       public $htmlInputProcessors = [];
+       
        /**
         * @inheritDoc
         */
@@ -358,6 +364,19 @@ class BoxAddForm extends AbstractForm {
                if ($this->boxController && $this->boxController->getProcessor() instanceof IConditionBoxController) {
                        $this->boxController->getProcessor()->validateConditions();
                }
+               
+               if ($this->boxType == 'text') {
+                       if ($this->isMultilingual) {
+                               foreach (LanguageFactory::getInstance()->getLanguages() as $language) {
+                                       $this->htmlInputProcessors[$language->languageID] = new HtmlInputProcessor();
+                                       $this->htmlInputProcessors[$language->languageID]->process((!empty($this->content[$language->languageID]) ? $this->content[$language->languageID] : ''), 'com.woltlab.wcf.box.content');
+                               }
+                       }
+                       else {
+                               $this->htmlInputProcessors[0] = new HtmlInputProcessor();
+                               $this->htmlInputProcessors[0]->process((!empty($this->content[0]) ? $this->content[0] : ''), 'com.woltlab.wcf.box.content');
+                       }
+               }
        }
        
        /**
@@ -384,6 +403,7 @@ class BoxAddForm extends AbstractForm {
                                $content[$language->languageID] = [
                                        'title' => (!empty($this->title[$language->languageID]) ? $this->title[$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)
                                ];
                        }
@@ -392,6 +412,7 @@ class BoxAddForm extends AbstractForm {
                        $content[0] = [
                                'title' => (!empty($this->title[0]) ? $this->title[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 37ba3739dadc758a612ff4d170c42a9446177931..6bd2acbddfe1a92299c898b9d6c636698a18b988 100644 (file)
@@ -81,16 +81,18 @@ class BoxEditForm extends BoxAddForm {
                if ($this->boxType == 'system' || $this->isMultilingual) {
                        foreach (LanguageFactory::getInstance()->getLanguages() as $language) {
                                $content[$language->languageID] = [
-                                       'title' => (!empty($_POST['title'][$language->languageID]) ? $_POST['title'][$language->languageID] : ''),
-                                       'content' => (!empty($_POST['content'][$language->languageID]) ? $_POST['content'][$language->languageID] : ''),
+                                       'title' => (!empty($this->title[$language->languageID]) ? $this->title[$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)
                                ];
                        }
                }
                else {
                        $content[0] = [
-                               'title' => (!empty($_POST['title'][0]) ? $_POST['title'][0] : ''),
-                               'content' => (!empty($_POST['content'][0]) ? $_POST['content'][0] : ''),
+                               'title' => (!empty($this->title[0]) ? $this->title[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)
                        ];
                }
@@ -134,8 +136,8 @@ class BoxEditForm extends BoxAddForm {
         */
        public function readData() {
                if (!empty($_POST) && !WCF::getSession()->getPermission('admin.content.cms.canUseMedia')) {
-                       foreach ($this->box->getBoxContent() as $languageID => $content) {
-                               $this->imageID[$languageID] = $content['imageID'];
+                       foreach ($this->box->getBoxContents() as $languageID => $content) {
+                               $this->imageID[$languageID] = $content->imageID;
                        }
                        
                        $this->readBoxImages();
@@ -161,10 +163,10 @@ class BoxEditForm extends BoxAddForm {
                        if ($this->linkPageID) $this->linkType = 'internal';
                        if ($this->externalURL) $this->linkType = 'external';
                        
-                       foreach ($this->box->getBoxContent() as $languageID => $content) {
-                               $this->title[$languageID] = $content['title'];
-                               $this->content[$languageID] = $content['content'];
-                               $this->imageID[$languageID] = $content['imageID'];
+                       foreach ($this->box->getBoxContents() as $languageID => $content) {
+                               $this->title[$languageID] = $content->title;
+                               $this->content[$languageID] = $content->content;
+                               $this->imageID[$languageID] = $content->imageID;
                        }
                        
                        if ($this->boxControllerID) {
index 0f60f33e2681ec34f2d5042c4064746a4eed2d95..4f80e76e35d5b32e70de39d3adfe8fef481ee9f8 100644 (file)
@@ -144,6 +144,11 @@ class PageAddForm extends AbstractForm {
         */
        public $aclValues = [];
        
+       /**
+        * @var HtmlInputProcessor[]
+        */
+       public $htmlInputProcessors = [];
+       
        /**
         * @inheritDoc
         */
@@ -231,6 +236,19 @@ class PageAddForm extends AbstractForm {
                $this->validateCustomUrls();
                
                $this->validateBoxIDs();
+               
+               if ($this->pageType == 'text') {
+                       if ($this->isMultilingual) {
+                               foreach (LanguageFactory::getInstance()->getLanguages() as $language) {
+                                       $this->htmlInputProcessors[$language->languageID] = new HtmlInputProcessor();
+                                       $this->htmlInputProcessors[$language->languageID]->process((!empty($this->content[$language->languageID]) ? $this->content[$language->languageID] : ''), 'com.woltlab.wcf.page.content');
+                               }
+                       }
+                       else {
+                               $this->htmlInputProcessors[0] = new HtmlInputProcessor();
+                               $this->htmlInputProcessors[0]->process((!empty($this->content[0]) ? $this->content[0] : ''), 'com.woltlab.wcf.page.content');
+                       }
+               }
        }
        
        /**
@@ -330,7 +348,7 @@ class PageAddForm extends AbstractForm {
                        }
                        
                        foreach ($this->customURL as $languageID2 => $customURL2) {
-                               if ($languageID != $languageID2 && $customURL = $customURL2) {
+                               if ($languageID != $languageID2 && $customURL == $customURL2) {
                                        throw new UserInputException('customURL_' . $languageID, 'notUnique');
                                }
                        }
@@ -385,36 +403,28 @@ class PageAddForm extends AbstractForm {
        public function save() {
                parent::save();
                
-               $parseHTML = function($content) {
-                       if ($this->pageType == 'text') {
-                               $htmlInputProcessor = new HtmlInputProcessor();
-                               $htmlInputProcessor->process($content);
-                               $content = $htmlInputProcessor->getHtml();
-                       }
-                       
-                       return $content;
-               };
-               
                // prepare page content
                $content = [];
                if ($this->isMultilingual) {
                        foreach (LanguageFactory::getInstance()->getLanguages() as $language) {
                                $content[$language->languageID] = [
-                                       'customURL' => (!empty($_POST['customURL'][$language->languageID]) ? $_POST['customURL'][$language->languageID] : ''),
-                                       'title' => (!empty($_POST['title'][$language->languageID]) ? $_POST['title'][$language->languageID] : ''),
-                                       'content' => (!empty($_POST['content'][$language->languageID]) ? $parseHTML($_POST['content'][$language->languageID]) : ''),
-                                       'metaDescription' => (!empty($_POST['metaDescription'][$language->languageID]) ? $_POST['metaDescription'][$language->languageID] : ''),
-                                       'metaKeywords' => (!empty($_POST['metaKeywords'][$language->languageID]) ? $_POST['metaKeywords'][$language->languageID] : '')
+                                       'customURL' => (!empty($this->customURL[$language->languageID]) ? $this->customURL[$language->languageID] : ''),
+                                       'title' => (!empty($this->title[$language->languageID]) ? $this->title[$language->languageID] : ''),
+                                       'content' => (!empty($this->content[$language->languageID]) ? $this->content[$language->languageID] : ''),
+                                       'htmlInputProcessor' => (isset($this->htmlInputProcessors[$language->languageID]) ? $this->htmlInputProcessors[$language->languageID] : null),
+                                       'metaDescription' => (!empty($this->metaDescription[$language->languageID]) ? $this->metaDescription[$language->languageID] : ''),
+                                       'metaKeywords' => (!empty($this->metaKeywords[$language->languageID]) ? $this->metaKeywords[$language->languageID] : '')
                                ];
                        }
                }
                else {
                        $content[0] = [
-                               'customURL' => (!empty($_POST['customURL'][0]) ? $_POST['customURL'][0] : ''),
-                               'title' => (!empty($_POST['title'][0]) ? $_POST['title'][0] : ''),
-                               'content' => (!empty($_POST['content'][0]) ? $parseHTML($_POST['content'][0]) : ''),
-                               'metaDescription' => (!empty($_POST['metaDescription'][0]) ? $_POST['metaDescription'][0] : ''),
-                               'metaKeywords' => (!empty($_POST['metaKeywords'][0]) ? $_POST['metaKeywords'][0] : '')
+                               'customURL' => (!empty($this->customURL[0]) ? $this->customURL[0] : ''),
+                               'title' => (!empty($this->title[0]) ? $this->title[0] : ''),
+                               'content' => (!empty($this->content[0]) ? $this->content[0] : ''),
+                               'htmlInputProcessor' => (isset($this->htmlInputProcessors[0]) ? $this->htmlInputProcessors[0] : null),
+                               'metaDescription' => (!empty($this->metaDescription[0]) ? $this->metaDescription[0] : ''),
+                               'metaKeywords' => (!empty($this->metaKeywords[0]) ? $this->metaKeywords[0] : '')
                        ];
                }
                
index b067afbe208b54d575115bffb5928a20dffd86d4..4ae43fd0e2cf1e9413aac1ef05b06f9a4614724c 100644 (file)
@@ -102,7 +102,7 @@ class PageEditForm extends PageAddForm {
                        }
                }
                else {
-                       if (mb_strtolower($customURL) != mb_strtolower($this->page->getPageContent()[$languageID]['customURL'])) {
+                       if (mb_strtolower($customURL) != mb_strtolower($this->page->getPageContents()[$languageID]->customURL)) {
                                parent::validateCustomUrl($languageID, $customURL);
                        }
                }
@@ -127,14 +127,14 @@ class PageEditForm extends PageAddForm {
                        foreach (LanguageFactory::getInstance()->getLanguages() as $language) {
                                $content[$language->languageID] = [
                                        'customURL' => '',
-                                       'title' => (!empty($_POST['title'][$language->languageID]) ? $_POST['title'][$language->languageID] : ''),
+                                       'title' => (!empty($this->title[$language->languageID]) ? $this->title[$language->languageID] : ''),
                                        'content' => '',
                                        'metaDescription' => '',
                                        'metaKeywords' => ''
                                ];
                        }
                        
-                       $data['controllerCustomURL'] = (!empty($_POST['customURL'][0]) ? $_POST['customURL'][0] : '');
+                       $data['controllerCustomURL'] = (!empty($this->customURL[0]) ? $this->customURL[0] : '');
                        $this->objectAction = new PageAction([$this->page], 'update', [
                                'data' => array_merge($this->additionalFields, $data),
                                'boxToPage' => $this->getBoxToPage(),
@@ -147,21 +147,23 @@ class PageEditForm extends PageAddForm {
                        if ($this->page->isMultilingual) {
                                foreach (LanguageFactory::getInstance()->getLanguages() as $language) {
                                        $content[$language->languageID] = [
-                                               'customURL' => (!empty($_POST['customURL'][$language->languageID]) ? $_POST['customURL'][$language->languageID] : ''),
-                                               'title' => (!empty($_POST['title'][$language->languageID]) ? $_POST['title'][$language->languageID] : ''),
-                                               'content' => (!empty($_POST['content'][$language->languageID]) ? $_POST['content'][$language->languageID] : ''),
-                                               'metaDescription' => (!empty($_POST['metaDescription'][$language->languageID]) ? $_POST['metaDescription'][$language->languageID] : ''),
-                                               'metaKeywords' => (!empty($_POST['metaKeywords'][$language->languageID]) ? $_POST['metaKeywords'][$language->languageID] : '')
+                                               'customURL' => (!empty($this->customURL[$language->languageID]) ? $this->customURL[$language->languageID] : ''),
+                                               'title' => (!empty($this->title[$language->languageID]) ? $this->title[$language->languageID] : ''),
+                                               'content' => (!empty($this->content[$language->languageID]) ? $this->content[$language->languageID] : ''),
+                                               'htmlInputProcessor' => (isset($this->htmlInputProcessors[$language->languageID]) ? $this->htmlInputProcessors[$language->languageID] : null),
+                                               'metaDescription' => (!empty($this->metaDescription[$language->languageID]) ? $this->metaDescription[$language->languageID] : ''),
+                                               'metaKeywords' => (!empty($this->metaKeywords[$language->languageID]) ? $this->metaKeywords[$language->languageID] : '')
                                        ];
                                }
                        }
                        else {
                                $content[0] = [
-                                       'customURL' => (!empty($_POST['customURL'][0]) ? $_POST['customURL'][0] : ''),
-                                       'title' => (!empty($_POST['title'][0]) ? $_POST['title'][0] : ''),
-                                       'content' => (!empty($_POST['content'][0]) ? $_POST['content'][0] : ''),
-                                       'metaDescription' => (!empty($_POST['metaDescription'][0]) ? $_POST['metaDescription'][0] : ''),
-                                       'metaKeywords' => (!empty($_POST['metaKeywords'][0]) ? $_POST['metaKeywords'][0] : '')
+                                       'customURL' => (!empty($this->customURL[0]) ? $this->customURL[0] : ''),
+                                       'title' => (!empty($this->title[0]) ? $this->title[0] : ''),
+                                       'content' => (!empty($this->content[0]) ? $this->content[0] : ''),
+                                       'htmlInputProcessor' => (isset($this->htmlInputProcessors[0]) ? $this->htmlInputProcessors[0] : null),
+                                       'metaDescription' => (!empty($this->metaDescription[0]) ? $this->metaDescription[0] : ''),
+                                       'metaKeywords' => (!empty($this->metaKeywords[0]) ? $this->metaKeywords[0] : '')
                                ];
                        }
                        
@@ -202,14 +204,14 @@ class PageEditForm extends PageAddForm {
                        $this->applicationPackageID = $this->page->applicationPackageID;
                        if ($this->page->controllerCustomURL) $this->customURL[0] = $this->page->controllerCustomURL;
                        if ($this->page->isLandingPage) $this->isLandingPage = 1;
-                       if ($this->page->isDiabled) $this->isDisabled = 1;
+                       if ($this->page->isDisabled) $this->isDisabled = 1;
                        
-                       foreach ($this->page->getPageContent() as $languageID => $content) {
-                               $this->title[$languageID] = $content['title'];
-                               $this->content[$languageID] = $content['content'];
-                               $this->metaDescription[$languageID] = $content['metaDescription'];
-                               $this->metaKeywords[$languageID] = $content['metaKeywords'];
-                               $this->customURL[$languageID] = $content['customURL'];
+                       foreach ($this->page->getPageContents() as $languageID => $content) {
+                               $this->title[$languageID] = $content->title;
+                               $this->content[$languageID] = $content->content;
+                               $this->metaDescription[$languageID] = $content->metaDescription;
+                               $this->metaKeywords[$languageID] = $content->metaKeywords;
+                               $this->customURL[$languageID] = $content->customURL;
                        }
                        
                        $this->boxIDs = [];
index 027349bbfcdf524572c0a165f97b83bdfb38d903..a43096e058a28669f03c90e8286e1abe7a40dddb 100644 (file)
@@ -85,6 +85,8 @@ class ArticleAction extends AbstractDatabaseObjectAction {
                                
                                // save embedded objects
                                if (!empty($content['htmlInputProcessor'])) {
+                                       /** @noinspection PhpUndefinedMethodInspection */
+                                       $content['htmlInputProcessor']->setObjectID($articleContent->articleContentID);
                                        if (MessageEmbeddedObjectManager::getInstance()->registerObjects($content['htmlInputProcessor'])) {
                                                $articleContentEditor->update(['hasEmbeddedObjects' => 1]);
                                        }
@@ -151,6 +153,8 @@ class ArticleAction extends AbstractDatabaseObjectAction {
                                        
                                        // save embedded objects
                                        if (!empty($content['htmlInputProcessor'])) {
+                                               /** @noinspection PhpUndefinedMethodInspection */
+                                               $content['htmlInputProcessor']->setObjectID($articleContent->articleContentID);
                                                if ($articleContent->hasEmbeddedObjects != MessageEmbeddedObjectManager::getInstance()->registerObjects($content['htmlInputProcessor'])) {
                                                        $articleContentEditor->update(['hasEmbeddedObjects' => ($articleContent->hasEmbeddedObjects ? 0 : 1)]);
                                                }
index b89798d69a9bbe361042d7beb8f182a5963862fe..55dcc24e6ce14da2c3a925d4460b01b7576c6467 100644 (file)
@@ -128,7 +128,13 @@ class ArticleContent extends DatabaseObject implements ILinkableObject, IRouteCo
                return null;
        }
        
-       
+       /**
+        * Returns a certain article content.
+        * 
+        * @param       integer         $articleID
+        * @param       integer         $languageID
+        * @return      ArticleContent|null
+        */
        public static function getArticleContent($articleID, $languageID) {
                if ($languageID !== null) {
                        $sql = "SELECT  *
index ce4c1e508947ff900817295f2f135498f7cf5010..66f3cc5688eaed6cd9f69ef655bc2791cbf4c144 100644 (file)
@@ -1,5 +1,6 @@
 <?php
 namespace wcf\data\box;
+use wcf\data\box\content\BoxContent;
 use wcf\data\condition\Condition;
 use wcf\data\media\ViewableMedia;
 use wcf\data\menu\Menu;
@@ -13,6 +14,8 @@ use wcf\data\page\Page;
 use wcf\data\page\PageCache;
 use wcf\data\DatabaseObject;
 use wcf\system\exception\SystemException;
+use wcf\system\html\output\HtmlOutputProcessor;
+use wcf\system\message\embedded\object\MessageEmbeddedObjectManager;
 use wcf\system\page\handler\ILookupPageHandler;
 use wcf\system\page\handler\IMenuPageHandler;
 use wcf\system\WCF;
@@ -47,12 +50,6 @@ use wcf\util\StringUtil;
  * @property-read      mixed[]         $additionalData
  */
 class Box extends DatabaseObject {
-       /**
-        * box content grouped by language id
-        * @var string[][]
-        */
-       protected $boxContent;
-       
        /**
         * image media object
         * @var ViewableMedia
@@ -105,6 +102,12 @@ class Box extends DatabaseObject {
         */
        protected $controller;
        
+       /**
+        * box content grouped by language id
+        * @var BoxContent[]
+        */
+       public $boxContents;
+       
        /**
         * @inheritDoc
         */
@@ -156,13 +159,13 @@ class Box extends DatabaseObject {
        }
        
        /**
-        * Returns the box content.
-        * 
-        * @return      string[][]
+        * Returns the box's content.
+        *
+        * @return      BoxContent[]
         */
-       public function getBoxContent() {
-               if ($this->boxContent === null) {
-                       $this->boxContent = [];
+       public function getBoxContents() {
+               if ($this->boxContents === null) {
+                       $this->boxContents = [];
                        
                        $sql = "SELECT  *
                                FROM    wcf" . WCF_N . "_box_content
@@ -170,15 +173,11 @@ class Box extends DatabaseObject {
                        $statement = WCF::getDB()->prepareStatement($sql);
                        $statement->execute([$this->boxID]);
                        while ($row = $statement->fetchArray()) {
-                               $this->boxContent[($row['languageID'] ?: 0)] = [
-                                       'title' => $row['title'],
-                                       'content' => $row['content'],
-                                       'imageID' => $row['imageID']
-                               ];
+                               $this->boxContents[($row['languageID'] ?: 0)] = new BoxContent(null, $row);
                        }
                }
                
-               return $this->boxContent;
+               return $this->boxContents;
        }
        
        /**
@@ -187,14 +186,14 @@ class Box extends DatabaseObject {
         * @return      string
         */
        public function getBoxContentTitle() {
-               $boxContent = $this->getBoxContent();
+               $this->getBoxContents();
                if ($this->isMultilingual || $this->boxType == 'system') {
-                       if (isset($boxContent[WCF::getLanguage()->languageID])) {
-                               return $boxContent[WCF::getLanguage()->languageID]['title'];
+                       if (isset($this->boxContents[WCF::getLanguage()->languageID])) {
+                               return $this->boxContents[WCF::getLanguage()->languageID]->title;
                        }
                }
-               else if (isset($boxContent[0])) {
-                       return $boxContent[0]['title'];
+               else if (isset($this->boxContents[0])) {
+                       return $this->boxContents[0]->title;
                }
                
                return '';
@@ -229,21 +228,29 @@ class Box extends DatabaseObject {
                        return WCF::getTPL()->fetch($this->getTplName(WCF::getLanguage()->languageID), 'wcf', [], true);
                }
                
-               $boxContent = $this->getBoxContent();
-               $content = '';
+               $this->getBoxContents();
+               $boxContent = null;
                if ($this->isMultilingual) {
-                       if (isset($boxContent[WCF::getLanguage()->languageID])) $content = $boxContent[WCF::getLanguage()->languageID]['content'];
+                       if (isset($this->boxContents[WCF::getLanguage()->languageID])) $boxContent = $this->boxContents[WCF::getLanguage()->languageID];
                }
                else {
-                       if (isset($boxContent[0])) $content = $boxContent[0]['content'];
+                       if (isset($this->boxContents[0])) $boxContent = $this->boxContents[0];
                }
                
-               if ($this->boxType == 'text') {
-                       // @todo parse text
-                       $content = StringUtil::encodeHTML($content);
+               if ($boxContent !== null) {
+                       if ($this->boxType == 'text') {
+                               // assign embedded objects
+                               MessageEmbeddedObjectManager::getInstance()->setActiveMessage('com.woltlab.wcf.box.content', $boxContent->boxContentID);
+                               
+                               $processor = new HtmlOutputProcessor();
+                               $processor->process($boxContent->content, 'com.woltlab.wcf.box.content', $boxContent->boxContentID);
+                               
+                               return $processor->getHtml();
+                       }
+                       
+                       return $boxContent->content;
                }
-               
-               return $content;
+               return '';
        }
        
        /**
@@ -273,13 +280,13 @@ class Box extends DatabaseObject {
                        return $this->getMenu()->hasContent();
                }
                
-               $boxContent = $this->getBoxContent();
+               $this->getBoxContents();
                $content = '';
                if ($this->isMultilingual) {
-                       if (isset($boxContent[WCF::getLanguage()->languageID])) $content = $boxContent[WCF::getLanguage()->languageID]['content'];
+                       if (isset($this->boxContents[WCF::getLanguage()->languageID])) $content = $this->boxContents[WCF::getLanguage()->languageID]->content;
                }
                else {
-                       if (isset($boxContent[0])) $content = $boxContent[0]['content'];
+                       if (isset($this->boxContents[0])) $content = $this->boxContents[0]->content;
                }
                
                return !empty($content);
@@ -331,14 +338,14 @@ class Box extends DatabaseObject {
                        return $this->image;
                }
                
-               $boxContent = $this->getBoxContent();
+               $this->getBoxContents();
                if ($this->isMultilingual) {
-                       if (isset($boxContent[WCF::getLanguage()->languageID]) && $boxContent[WCF::getLanguage()->languageID]['imageID']) {
-                               $this->image = ViewableMedia::getMedia($boxContent[WCF::getLanguage()->languageID]['imageID']);
+                       if (isset($this->boxContents[WCF::getLanguage()->languageID]) && $this->boxContents[WCF::getLanguage()->languageID]->imageID) {
+                               $this->image = ViewableMedia::getMedia($this->boxContents[WCF::getLanguage()->languageID]->imageID);
                        }
                }
-               else if (isset($boxContent[0]) && $boxContent[0]['imageID']) {
-                       $this->image = ViewableMedia::getMedia($boxContent[0]['imageID']);
+               else if (isset($this->boxContents[0]) && $this->boxContents[0]->imageID) {
+                       $this->image = ViewableMedia::getMedia($this->boxContents[0]->imageID);
                }
                
                $this->image->setLinkParameters(['boxID' => $this->boxID]);
@@ -359,12 +366,12 @@ class Box extends DatabaseObject {
                        return false;
                }
                
-               $boxContent = $this->getBoxContent();
+               $this->getBoxContents();
                if ($this->isMultilingual) {
-                       return (isset($boxContent[WCF::getLanguage()->languageID]) && $boxContent[WCF::getLanguage()->languageID]['imageID']);
+                       return (isset($this->boxContents[WCF::getLanguage()->languageID]) && $this->boxContents[WCF::getLanguage()->languageID]->imageID);
                }
                
-               return (isset($boxContent[0]) && $boxContent[0]['imageID']);
+               return (isset($this->boxContents[0]) && $this->boxContents[0]->imageID);
        }
        
        /**
index 04f24daef8229b571936eef25d8a94d87e900a10..3c6f7f9af99070752ade2f91b136f644a68efa1a 100644 (file)
@@ -1,11 +1,14 @@
 <?php
 namespace wcf\data\box;
+use wcf\data\box\content\BoxContent;
+use wcf\data\box\content\BoxContentEditor;
 use wcf\data\object\type\ObjectType;
 use wcf\data\object\type\ObjectTypeCache;
 use wcf\data\AbstractDatabaseObjectAction;
 use wcf\system\box\IConditionBoxController;
 use wcf\system\exception\PermissionDeniedException;
 use wcf\system\exception\UserInputException;
+use wcf\system\message\embedded\object\MessageEmbeddedObjectManager;
 use wcf\system\WCF;
 
 /**
@@ -62,19 +65,30 @@ class BoxAction extends AbstractDatabaseObjectAction {
        
                // save box content
                if (!empty($this->parameters['content'])) {
-                       $sql = "INSERT INTO     wcf".WCF_N."_box_content
-                                               (boxID, languageID, title, content, imageID)
-                               VALUES          (?, ?, ?, ?, ?)";
-                       $statement = WCF::getDB()->prepareStatement($sql);
-                       
                        foreach ($this->parameters['content'] as $languageID => $content) {
-                               $statement->execute([
-                                       $box->boxID,
-                                       ($languageID ?: null),
-                                       $content['title'],
-                                       $content['content'],
-                                       $content['imageID']
+                               if (!empty($content['htmlInputProcessor'])) {
+                                       /** @noinspection PhpUndefinedMethodInspection */
+                                       $content['content'] = $content['htmlInputProcessor']->getHtml();
+                               }
+                               
+                               /** @var BoxContent $boxContent */
+                               $boxContent = BoxContentEditor::create([
+                                       'boxID' => $box->boxID,
+                                       'languageID' => ($languageID ?: null),
+                                       'title' => $content['title'],
+                                       'content' => $content['content'],
+                                       'imageID' => $content['imageID']
                                ]);
+                               $boxContentEditor = new BoxContentEditor($boxContent);
+                               
+                               // save embedded objects
+                               if (!empty($content['htmlInputProcessor'])) {
+                                       /** @noinspection PhpUndefinedMethodInspection */
+                                       $content['htmlInputProcessor']->setObjectID($boxContent->boxContentID);
+                                       if (MessageEmbeddedObjectManager::getInstance()->registerObjects($content['htmlInputProcessor'])) {
+                                               $boxContentEditor->update(['hasEmbeddedObjects' => 1]);
+                                       }
+                               }
                        }
                }
                
@@ -114,26 +128,44 @@ class BoxAction extends AbstractDatabaseObjectAction {
                
                // update box content
                if (!empty($this->parameters['content'])) {
-                       $sql = "DELETE FROM     wcf".WCF_N."_box_content
-                               WHERE           boxID = ?";
-                       $deleteStatement = WCF::getDB()->prepareStatement($sql);
-                       
-                       $sql = "INSERT INTO     wcf".WCF_N."_box_content
-                                               (boxID, languageID, title, content, imageID)
-                               VALUES          (?, ?, ?, ?, ?)";
-                       $insertStatement = WCF::getDB()->prepareStatement($sql);
-                       
                        foreach ($this->getObjects() as $box) {
-                               $deleteStatement->execute([$box->boxID]);
-                               
                                foreach ($this->parameters['content'] as $languageID => $content) {
-                                       $insertStatement->execute([
-                                               $box->boxID,
-                                               ($languageID ?: null),
-                                               $content['title'],
-                                               $content['content'],
-                                               $content['imageID']
-                                       ]);
+                                       if (!empty($content['htmlInputProcessor'])) {
+                                               /** @noinspection PhpUndefinedMethodInspection */
+                                               $content['content'] = $content['htmlInputProcessor']->getHtml();
+                                       }
+                                       
+                                       $boxContent = BoxContent::getBoxContent($box->boxID, ($languageID ?: null));
+                                       $boxContentEditor = null;
+                                       if ($boxContent !== null) {
+                                               // update
+                                               $boxContentEditor = new BoxContentEditor($boxContent);
+                                               $boxContentEditor->update([
+                                                       'title' => $content['title'],
+                                                       'content' => $content['content'],
+                                                       'imageID' => $content['imageID']
+                                               ]);
+                                       }
+                                       else {
+                                               /** @var BoxContent $boxContent */
+                                               $boxContent = BoxContentEditor::create([
+                                                       'boxID' => $box->boxID,
+                                                       'languageID' => ($languageID ?: null),
+                                                       'title' => $content['title'],
+                                                       'content' => $content['content'],
+                                                       'imageID' => $content['imageID']
+                                               ]);
+                                               $boxContentEditor = new BoxContentEditor($boxContent);
+                                       }
+                                       
+                                       // save embedded objects
+                                       if (!empty($content['htmlInputProcessor'])) {
+                                               /** @noinspection PhpUndefinedMethodInspection */
+                                               $content['htmlInputProcessor']->setObjectID($boxContent->boxContentID);
+                                               if ($boxContent->hasEmbeddedObjects != MessageEmbeddedObjectManager::getInstance()->registerObjects($content['htmlInputProcessor'])) {
+                                                       $boxContentEditor->update(['hasEmbeddedObjects' => ($boxContent->hasEmbeddedObjects ? 0 : 1)]);
+                                               }
+                                       }
                                }
                                
                                // save template
diff --git a/wcfsetup/install/files/lib/data/box/content/BoxContent.class.php b/wcfsetup/install/files/lib/data/box/content/BoxContent.class.php
new file mode 100644 (file)
index 0000000..3685c57
--- /dev/null
@@ -0,0 +1,65 @@
+<?php
+namespace wcf\data\box\content;
+use wcf\data\DatabaseObject;
+use wcf\system\WCF;
+
+/**
+ * Represents a box content.
+ * 
+ * @author     Marcel Werk
+ * @copyright  2001-2016 WoltLab GmbH
+ * @license    GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package    WoltLabSuite\Core\Data\Box\Content
+ * @since      3.0
+ *
+ * @property-read      integer         $boxContentID
+ * @property-read      integer         $boxID
+ * @property-read      integer         $languageID
+ * @property-read      string          $title
+ * @property-read      string          $content
+ * @property-read      integer         $imageID
+ * @property-read      integer         $hasEmbeddedObjects
+ */
+class BoxContent extends DatabaseObject {
+       /**
+        * @inheritDoc
+        */
+       protected static $databaseTableName = 'box_content';
+       
+       /**
+        * @inheritDoc
+        */
+       protected static $databaseTableIndexName = 'boxContentID';
+       
+       /**
+        * Returns a certain box content.
+        *
+        * @param       integer         $boxID
+        * @param       integer         $languageID
+        * @return      BoxContent|null
+        */
+       public static function getBoxContent($boxID, $languageID) {
+               if ($languageID !== null) {
+                       $sql = "SELECT  *
+                               FROM    wcf" . WCF_N . "_box_content
+                               WHERE   boxID = ?
+                                       AND languageID = ?";
+                       $statement = WCF::getDB()->prepareStatement($sql);
+                       $statement->execute([$boxID, $languageID]);
+               }
+               else {
+                       $sql = "SELECT  *
+                               FROM    wcf" . WCF_N . "_box_content
+                               WHERE   boxID = ?
+                                       AND languageID IS NULL";
+                       $statement = WCF::getDB()->prepareStatement($sql);
+                       $statement->execute([$boxID]);
+               }
+               
+               if (($row = $statement->fetchSingleRow()) !== false) {
+                       return new BoxContent(null, $row);
+               }
+               
+               return null;
+       }
+}
diff --git a/wcfsetup/install/files/lib/data/box/content/BoxContentAction.class.php b/wcfsetup/install/files/lib/data/box/content/BoxContentAction.class.php
new file mode 100644 (file)
index 0000000..eaac6b1
--- /dev/null
@@ -0,0 +1,22 @@
+<?php
+namespace wcf\data\box\content;
+use wcf\data\AbstractDatabaseObjectAction;
+
+/**
+ * Executes box content related actions.
+ * 
+ * @author     Marcel Werk
+ * @copyright  2001-2016 WoltLab GmbH
+ * @license    GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package    WoltLabSuite\Core\Data\Box\Content
+ * @since      3.0
+ * 
+ * @method     BoxContentEditor[]      getObjects()
+ * @method     BoxContentEditor        getSingleObject()
+ */
+class BoxContentAction extends AbstractDatabaseObjectAction {
+       /**
+        * @inheritDoc
+        */
+       protected $className = BoxContentEditor::class;
+}
diff --git a/wcfsetup/install/files/lib/data/box/content/BoxContentEditor.class.php b/wcfsetup/install/files/lib/data/box/content/BoxContentEditor.class.php
new file mode 100644 (file)
index 0000000..114e034
--- /dev/null
@@ -0,0 +1,22 @@
+<?php
+namespace wcf\data\box\content;
+use wcf\data\DatabaseObjectEditor;
+
+/**
+ * Provides functions to edit box content.
+ * 
+ * @author     Marcel Werk
+ * @copyright  2001-2016 WoltLab GmbH
+ * @license    GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package    WoltLabSuite\Core\Data\Box\Content
+ * @since      3.0
+ * 
+ * @method     BoxContent      getDecoratedObject()
+ * @mixin      BoxContent
+ */
+class BoxContentEditor extends DatabaseObjectEditor {
+       /**
+        * @inheritDoc
+        */
+       protected static $baseClass = BoxContent::class;
+}
diff --git a/wcfsetup/install/files/lib/data/box/content/BoxContentList.class.php b/wcfsetup/install/files/lib/data/box/content/BoxContentList.class.php
new file mode 100644 (file)
index 0000000..abdbafb
--- /dev/null
@@ -0,0 +1,24 @@
+<?php
+namespace wcf\data\box\content;
+use wcf\data\DatabaseObjectList;
+
+/**
+ * Represents a list of box content.
+ * 
+ * @author     Marcel Werk
+ * @copyright  2001-2016 WoltLab GmbH
+ * @license    GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package    WoltLabSuite\Core\Data\Box\Content
+ * @since      3.0
+ *
+ * @method     BoxContent              current()
+ * @method     BoxContent[]            getObjects()
+ * @method     BoxContent|null         search($objectID)
+ * @property   BoxContent[]            $objects
+ */
+class BoxContentList extends DatabaseObjectList {
+       /**
+        * @inheritDoc
+        */
+       public $className = BoxContent::class;
+}
index 4945b770a61559a930890749fecbe00a0aa49d26..82011ef131a09f160401ef91d75fa727e19c37b4 100644 (file)
@@ -3,6 +3,7 @@ namespace wcf\data\page;
 use wcf\data\DatabaseObject;
 use wcf\data\ILinkableObject;
 use wcf\data\ITitledObject;
+use wcf\data\page\content\PageContent;
 use wcf\data\TDatabaseObjectOptions;
 use wcf\data\TDatabaseObjectPermissions;
 use wcf\system\acl\simple\SimpleAclResolver;
@@ -71,6 +72,12 @@ class Page extends DatabaseObject implements ILinkableObject, ITitledObject {
         */
        protected $boxIDs;
        
+       /**
+        * page content grouped by language id
+        * @var PageContent[]
+        */
+       public $pageContents;
+       
        /**
         * Returns true if the active user can delete this page.
         * 
@@ -98,29 +105,25 @@ class Page extends DatabaseObject implements ILinkableObject, ITitledObject {
        }
        
        /**
-        * Returns the page content.
-        * 
-        * @return      array           content data
+        * Returns the page's content.
+        *
+        * @return      PageContent[]
         */
-       public function getPageContent() {
-               $content = [];
-               
-               $sql = "SELECT  *
-                       FROM    wcf".WCF_N."_page_content
-                       WHERE   pageID = ?";
-               $statement = WCF::getDB()->prepareStatement($sql);
-               $statement->execute([$this->pageID]);
-               while ($row = $statement->fetchArray()) {
-                       $content[($row['languageID'] ?: 0)] = [
-                               'title' => $row['title'],
-                               'content' => $row['content'],
-                               'metaDescription' => $row['metaDescription'],
-                               'metaKeywords' => $row['metaKeywords'],
-                               'customURL' => $row['customURL']
-                       ];
+       public function getPageContents() {
+               if ($this->pageContents === null) {
+                       $this->pageContents = [];
+                       
+                       $sql = "SELECT  *
+                               FROM    wcf" . WCF_N . "_page_content
+                               WHERE   pageID = ?";
+                       $statement = WCF::getDB()->prepareStatement($sql);
+                       $statement->execute([$this->pageID]);
+                       while ($row = $statement->fetchArray()) {
+                               $this->pageContents[($row['languageID'] ?: 0)] = new PageContent(null, $row);
+                       }
                }
                
-               return $content;
+               return $this->pageContents;
        }
        
        /**
@@ -128,7 +131,7 @@ class Page extends DatabaseObject implements ILinkableObject, ITitledObject {
         * for multilingual pages.
         * 
         * @param       integer         $languageID     language id or `null` if there are no localized versions
-        * @return      string[]        page content data
+        * @return      PageContent|null                page content data
         */
        public function getPageContentByLanguage($languageID = null) {
                $conditions = new PreparedStatementConditionBuilder();
@@ -142,8 +145,9 @@ class Page extends DatabaseObject implements ILinkableObject, ITitledObject {
                $statement = WCF::getDB()->prepareStatement($sql, 1);
                $statement->execute($conditions->getParameters());
                $row = $statement->fetchSingleRow();
+               if ($row !== false) return new PageContent(null, $row);
                
-               return $row ?: [];
+               return null;
        }
        
        /**
index f82e4467989ef9fe80ec98bbf8161598f9b55644..80adeb5e5250c75de09e0f6d0319ac59da506644 100644 (file)
@@ -3,8 +3,11 @@ namespace wcf\data\page;
 use wcf\data\AbstractDatabaseObjectAction;
 use wcf\data\ISearchAction;
 use wcf\data\IToggleAction;
+use wcf\data\page\content\PageContent;
+use wcf\data\page\content\PageContentEditor;
 use wcf\system\exception\PermissionDeniedException;
 use wcf\system\exception\UserInputException;
+use wcf\system\message\embedded\object\MessageEmbeddedObjectManager;
 use wcf\system\page\handler\ILookupPageHandler;
 use wcf\system\WCF;
 
@@ -61,21 +64,32 @@ class PageAction extends AbstractDatabaseObjectAction implements ISearchAction,
                
                // save page content
                if (!empty($this->parameters['content'])) {
-                       $sql = "INSERT INTO     wcf".WCF_N."_page_content
-                                               (pageID, languageID, title, content, metaDescription, metaKeywords, customURL)
-                               VALUES          (?, ?, ?, ?, ?, ?, ?)";
-                       $statement = WCF::getDB()->prepareStatement($sql);
-                       
                        foreach ($this->parameters['content'] as $languageID => $content) {
-                               $statement->execute([
-                                       $page->pageID,
-                                       ($languageID ?: null),
-                                       $content['title'],
-                                       $content['content'],
-                                       $content['metaDescription'],
-                                       $content['metaKeywords'],
-                                       $content['customURL']
+                               if (!empty($content['htmlInputProcessor'])) {
+                                       /** @noinspection PhpUndefinedMethodInspection */
+                                       $content['content'] = $content['htmlInputProcessor']->getHtml();
+                               }
+                               
+                               /** @var PageContent $pageContent */
+                               $pageContent = PageContentEditor::create([
+                                       'pageID' => $page->pageID,
+                                       'languageID' => ($languageID ?: null),
+                                       'title' => $content['title'],
+                                       'content' => $content['content'],
+                                       'metaDescription' => $content['metaDescription'],
+                                       'metaKeywords' => $content['metaKeywords'],
+                                       'customURL' => $content['customURL']
                                ]);
+                               $pageContentEditor = new PageContentEditor($pageContent);
+                               
+                               // save embedded objects
+                               if (!empty($content['htmlInputProcessor'])) {
+                                       /** @noinspection PhpUndefinedMethodInspection */
+                                       $content['htmlInputProcessor']->setObjectID($pageContent->pageContentID);
+                                       if (MessageEmbeddedObjectManager::getInstance()->registerObjects($content['htmlInputProcessor'])) {
+                                               $pageContentEditor->update(['hasEmbeddedObjects' => 1]);
+                                       }
+                               }
                        }
                }
                
@@ -115,28 +129,48 @@ class PageAction extends AbstractDatabaseObjectAction implements ISearchAction,
        
                // update page content
                if (!empty($this->parameters['content'])) {
-                       $sql = "DELETE FROM     wcf".WCF_N."_page_content
-                               WHERE           pageID = ?";
-                       $deleteStatement = WCF::getDB()->prepareStatement($sql);
-                       
-                       $sql = "INSERT INTO     wcf".WCF_N."_page_content
-                                               (pageID, languageID, title, content, metaDescription, metaKeywords, customURL)
-                               VALUES          (?, ?, ?, ?, ?, ?, ?)";
-                       $insertStatement = WCF::getDB()->prepareStatement($sql);
-                       
                        foreach ($this->getObjects() as $page) {
-                               $deleteStatement->execute([$page->pageID]);
-                               
                                foreach ($this->parameters['content'] as $languageID => $content) {
-                                       $insertStatement->execute([
-                                               $page->pageID,
-                                               ($languageID ?: null),
-                                               $content['title'],
-                                               $content['content'],
-                                               $content['metaDescription'],
-                                               $content['metaKeywords'],
-                                               $content['customURL']
-                                       ]);
+                                       if (!empty($content['htmlInputProcessor'])) {
+                                               /** @noinspection PhpUndefinedMethodInspection */
+                                               $content['content'] = $content['htmlInputProcessor']->getHtml();
+                                       }
+                                       
+                                       $pageContent = PageContent::getPageContent($page->pageID, ($languageID ?: null));
+                                       $pageContentEditor = null;
+                                       if ($pageContent !== null) {
+                                               // update
+                                               $pageContentEditor = new PageContentEditor($pageContent);
+                                               $pageContentEditor->update([
+                                                       'title' => $content['title'],
+                                                       'content' => $content['content'],
+                                                       'metaDescription' => $content['metaDescription'],
+                                                       'metaKeywords' => $content['metaKeywords'],
+                                                       'customURL' => $content['customURL']
+                                               ]);
+                                       }
+                                       else {
+                                               /** @var PageContent $pageContent */
+                                               $pageContent = PageContentEditor::create([
+                                                       'pageID' => $page->pageID,
+                                                       'languageID' => ($languageID ?: null),
+                                                       'title' => $content['title'],
+                                                       'content' => $content['content'],
+                                                       'metaDescription' => $content['metaDescription'],
+                                                       'metaKeywords' => $content['metaKeywords'],
+                                                       'customURL' => $content['customURL']
+                                               ]);
+                                               $pageContentEditor = new PageContentEditor($pageContent);
+                                       }
+                                       
+                                       // save embedded objects
+                                       if (!empty($content['htmlInputProcessor'])) {
+                                               /** @noinspection PhpUndefinedMethodInspection */
+                                               $content['htmlInputProcessor']->setObjectID($pageContent->pageContentID);
+                                               if ($pageContent->hasEmbeddedObjects != MessageEmbeddedObjectManager::getInstance()->registerObjects($content['htmlInputProcessor'])) {
+                                                       $pageContentEditor->update(['hasEmbeddedObjects' => ($pageContent->hasEmbeddedObjects ? 0 : 1)]);
+                                               }
+                                       }
                                }
                                
                                // save template
@@ -234,7 +268,7 @@ class PageAction extends AbstractDatabaseObjectAction implements ISearchAction,
        public function delete() {
                foreach ($this->getObjects() as $page) {
                        if ($page->pageType == 'tpl') {
-                               foreach ($page->getPageContent() as $languageID => $content) {
+                               foreach ($page->getPageContents() as $languageID => $content) {
                                        $file = WCF_DIR . 'templates/' . $page->getTplName(($languageID ?: null)) . '.tpl';
                                        if (file_exists($file)) {
                                                @unlink($file);
diff --git a/wcfsetup/install/files/lib/data/page/content/PageContent.class.php b/wcfsetup/install/files/lib/data/page/content/PageContent.class.php
new file mode 100644 (file)
index 0000000..9c30258
--- /dev/null
@@ -0,0 +1,84 @@
+<?php
+namespace wcf\data\page\content;
+use wcf\data\DatabaseObject;
+use wcf\system\html\output\HtmlOutputProcessor;
+use wcf\system\message\embedded\object\MessageEmbeddedObjectManager;
+use wcf\system\WCF;
+
+/**
+ * Represents a page content.
+ * 
+ * @author     Marcel Werk
+ * @copyright  2001-2016 WoltLab GmbH
+ * @license    GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package    WoltLabSuite\Core\Data\Page\Content
+ * @since      3.0
+ *
+ * @property-read      integer         $pageContentID
+ * @property-read      integer         $pageID
+ * @property-read      integer         $languageID
+ * @property-read      string          $title
+ * @property-read      string          $content
+ * @property-read      string          $metaDescription
+ * @property-read      string          $metaKeywords
+ * @property-read      string          $customURL
+ * @property-read      integer         $hasEmbeddedObjects
+ */
+class PageContent extends DatabaseObject {
+       /**
+        * @inheritDoc
+        */
+       protected static $databaseTableName = 'page_content';
+       
+       /**
+        * @inheritDoc
+        */
+       protected static $databaseTableIndexName = 'pageContentID';
+       
+       /**
+        * Returns the page's formatted content.
+        *
+        * @return      string
+        */
+       public function getFormattedContent() {
+               // assign embedded objects
+               MessageEmbeddedObjectManager::getInstance()->setActiveMessage('com.woltlab.wcf.page.content', $this->pageContentID);
+               
+               $processor = new HtmlOutputProcessor();
+               $processor->process($this->content, 'com.woltlab.wcf.page.content', $this->pageContentID);
+               
+               return $processor->getHtml();
+       }
+       
+       /**
+        * Returns a certain page content.
+        *
+        * @param       integer         $pageID
+        * @param       integer         $languageID
+        * @return      PageContent|null
+        */
+       public static function getPageContent($pageID, $languageID) {
+               if ($languageID !== null) {
+                       $sql = "SELECT  *
+                               FROM    wcf" . WCF_N . "_page_content
+                               WHERE   pageID = ?
+                                       AND languageID = ?";
+                       $statement = WCF::getDB()->prepareStatement($sql);
+                       $statement->execute([$pageID, $languageID]);
+               }
+               else {
+                       $sql = "SELECT  *
+                               FROM    wcf" . WCF_N . "_page_content
+                               WHERE   pageID = ?
+                                       AND languageID IS NULL";
+                       $statement = WCF::getDB()->prepareStatement($sql);
+                       $statement->execute([$pageID]);
+               }
+               
+               if (($row = $statement->fetchSingleRow()) !== false) {
+                       return new PageContent(null, $row);
+               }
+               
+               return null;
+       }
+}
diff --git a/wcfsetup/install/files/lib/data/page/content/PageContentAction.class.php b/wcfsetup/install/files/lib/data/page/content/PageContentAction.class.php
new file mode 100644 (file)
index 0000000..bdad409
--- /dev/null
@@ -0,0 +1,22 @@
+<?php
+namespace wcf\data\page\content;
+use wcf\data\AbstractDatabaseObjectAction;
+
+/**
+ * Executes page content related actions.
+ * 
+ * @author     Marcel Werk
+ * @copyright  2001-2016 WoltLab GmbH
+ * @license    GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package    WoltLabSuite\Core\Data\Page\Content
+ * @since      3.0
+ * 
+ * @method     PageContentEditor[]     getObjects()
+ * @method     PageContentEditor       getSingleObject()
+ */
+class PageContentAction extends AbstractDatabaseObjectAction {
+       /**
+        * @inheritDoc
+        */
+       protected $className = PageContentEditor::class;
+}
diff --git a/wcfsetup/install/files/lib/data/page/content/PageContentEditor.class.php b/wcfsetup/install/files/lib/data/page/content/PageContentEditor.class.php
new file mode 100644 (file)
index 0000000..f0f9ae0
--- /dev/null
@@ -0,0 +1,22 @@
+<?php
+namespace wcf\data\page\content;
+use wcf\data\DatabaseObjectEditor;
+
+/**
+ * Provides functions to edit page content.
+ * 
+ * @author     Marcel Werk
+ * @copyright  2001-2016 WoltLab GmbH
+ * @license    GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package    WoltLabSuite\Core\Data\Page\Content
+ * @since      3.0
+ * 
+ * @method     PageContent     getDecoratedObject()
+ * @mixin      PageContent
+ */
+class PageContentEditor extends DatabaseObjectEditor {
+       /**
+        * @inheritDoc
+        */
+       protected static $baseClass = PageContent::class;
+}
diff --git a/wcfsetup/install/files/lib/data/page/content/PageContentList.class.php b/wcfsetup/install/files/lib/data/page/content/PageContentList.class.php
new file mode 100644 (file)
index 0000000..975c5df
--- /dev/null
@@ -0,0 +1,24 @@
+<?php
+namespace wcf\data\page\content;
+use wcf\data\DatabaseObjectList;
+
+/**
+ * Represents a list of page content.
+ * 
+ * @author     Marcel Werk
+ * @copyright  2001-2016 WoltLab GmbH
+ * @license    GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package    WoltLabSuite\Core\Data\Page\Content
+ * @since      3.0
+ *
+ * @method     PageContent             current()
+ * @method     PageContent[]           getObjects()
+ * @method     PageContent|null        search($objectID)
+ * @property   PageContent[]           $objects
+ */
+class PageContentList extends DatabaseObjectList {
+       /**
+        * @inheritDoc
+        */
+       public $className = PageContent::class;
+}
index 148badb5bb8df4376e6f9e289b1caea9204ad014..2645e74a5b1fd593438df4e18e5b681de8c38e67 100644 (file)
@@ -1,5 +1,6 @@
 <?php
 namespace wcf\page;
+use wcf\data\page\content\PageContent;
 use wcf\data\page\Page;
 use wcf\system\exception\IllegalLinkException;
 use wcf\system\language\LanguageFactory;
@@ -19,7 +20,7 @@ use wcf\system\WCF;
  */
 class CmsPage extends AbstractPage {
        /**
-        * @var string[]
+        * @var PageContent
         */
        public $content;
        
@@ -69,7 +70,7 @@ class CmsPage extends AbstractPage {
                }
                
                $this->content = $this->page->getPageContentByLanguage($this->languageID);
-               if (empty($this->content)) {
+               if ($this->content === null) {
                        throw new IllegalLinkException();
                }
                
@@ -88,13 +89,13 @@ class CmsPage extends AbstractPage {
                parent::readData();
                
                // add meta/og tags
-               MetaTagHandler::getInstance()->addTag('og:title', 'og:title', $this->content['title'] . ' - ' . WCF::getLanguage()->get(PAGE_TITLE), true);
+               MetaTagHandler::getInstance()->addTag('og:title', 'og:title', $this->content->title . ' - ' . WCF::getLanguage()->get(PAGE_TITLE), true);
                MetaTagHandler::getInstance()->addTag('og:url', 'og:url', $this->canonicalURL, true);
                MetaTagHandler::getInstance()->addTag('og:type', 'og:type', 'website', true);
-               if ($this->content['metaDescription']) {
-                       MetaTagHandler::getInstance()->addTag('og:description', 'og:description', $this->content['metaDescription'], true);
+               if ($this->content->metaDescription) {
+                       MetaTagHandler::getInstance()->addTag('og:description', 'og:description', $this->content->metaDescription, true);
                }
-               if ($this->content['metaKeywords']) {
+               if ($this->content->metaKeywords) {
                        MetaTagHandler::getInstance()->addTag('keywords', 'keywords', $this->content['metaKeywords']);
                }
        }
index 37984080add87baffd13336071c72da04dce9d1a..25aa7447f5ce1a7f87a49e9a0cacb121cabcd32d 100644 (file)
@@ -290,13 +290,15 @@ CREATE TABLE wcf1_box (
 
 DROP TABLE IF EXISTS wcf1_box_content;
 CREATE TABLE wcf1_box_content (
+       boxContentID INT(10) NOT NULL AUTO_INCREMENT PRIMARY KEY,
        boxID INT(10) NOT NULL,
        languageID INT(10),
        title VARCHAR(255) NOT NULL,
        content MEDIUMTEXT,
        imageID INT(10),
+       hasEmbeddedObjects TINYINT(1) NOT NULL DEFAULT 0,
        
-       KEY (boxID, languageID)
+       UNIQUE KEY (boxID, languageID)
 );
 
 DROP TABLE IF EXISTS wcf1_box_to_page;
@@ -968,6 +970,7 @@ CREATE TABLE wcf1_page (
 
 DROP TABLE IF EXISTS wcf1_page_content;
 CREATE TABLE wcf1_page_content (
+       pageContentID INT(10) NOT NULL AUTO_INCREMENT PRIMARY KEY,
        pageID INT(10) NOT NULL,
        languageID INT(10),
        title VARCHAR(255) NOT NULL,
@@ -975,7 +978,8 @@ CREATE TABLE wcf1_page_content (
        metaDescription TEXT,
        metaKeywords TEXT,
        customURL VARCHAR(255) NOT NULL,
-
+       hasEmbeddedObjects TINYINT(1) NOT NULL DEFAULT 0,
+       
        UNIQUE KEY (pageID, languageID)
 );