From: Marcel Werk Date: Wed, 29 Jun 2016 21:42:04 +0000 (+0200) Subject: Added support for embedded objects in cms pages / boxes X-Git-Tag: 3.0.0_Beta_1~1305 X-Git-Url: https://git.stricted.de/?a=commitdiff_plain;h=2f2738395506c43338bbc733006482e0e290112a;p=GitHub%2FWoltLab%2FWCF.git Added support for embedded objects in cms pages / boxes --- diff --git a/com.woltlab.wcf/objectType.xml b/com.woltlab.wcf/objectType.xml index 2f06ec0aca..31b9dd79b5 100644 --- a/com.woltlab.wcf/objectType.xml +++ b/com.woltlab.wcf/objectType.xml @@ -66,6 +66,15 @@ + + com.woltlab.wcf.page.content + com.woltlab.wcf.message + + + com.woltlab.wcf.box.content + com.woltlab.wcf.message + + com.woltlab.wcf.bbcode.smiley com.woltlab.wcf.category diff --git a/com.woltlab.wcf/templates/cms.tpl b/com.woltlab.wcf/templates/cms.tpl index 2681ff76d5..f52d981580 100644 --- a/com.woltlab.wcf/templates/cms.tpl +++ b/com.woltlab.wcf/templates/cms.tpl @@ -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'} @@ -40,13 +40,13 @@ {include file='header'} -{if $content[content]} +{if $content->content} {if $page->pageType == 'text'}
- {@$content[content]} + {@$content->getFormattedContent()}
{elseif $page->pageType == 'html'} - {@$content[content]} + {@$content->content} {elseif $page->pageType == 'tpl'} {include file=$page->getTplName($contentLanguageID)} {/if} diff --git a/wcfsetup/install/files/acp/templates/boxAdd.tpl b/wcfsetup/install/files/acp/templates/boxAdd.tpl index 2a55a88eba..1e6b0501ea 100644 --- a/wcfsetup/install/files/acp/templates/boxAdd.tpl +++ b/wcfsetup/install/files/acp/templates/boxAdd.tpl @@ -342,7 +342,7 @@ {/if} languageID} class="formError"{/if}> -
+
{if $errorField == 'title'|concat:$availableLanguage->languageID} diff --git a/wcfsetup/install/files/lib/acp/form/BoxAddForm.class.php b/wcfsetup/install/files/lib/acp/form/BoxAddForm.class.php index 8e3d547045..0705b0ae29 100644 --- a/wcfsetup/install/files/lib/acp/form/BoxAddForm.class.php +++ b/wcfsetup/install/files/lib/acp/form/BoxAddForm.class.php @@ -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) ]; } diff --git a/wcfsetup/install/files/lib/acp/form/BoxEditForm.class.php b/wcfsetup/install/files/lib/acp/form/BoxEditForm.class.php index 37ba3739da..6bd2acbddf 100644 --- a/wcfsetup/install/files/lib/acp/form/BoxEditForm.class.php +++ b/wcfsetup/install/files/lib/acp/form/BoxEditForm.class.php @@ -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) { diff --git a/wcfsetup/install/files/lib/acp/form/PageAddForm.class.php b/wcfsetup/install/files/lib/acp/form/PageAddForm.class.php index 0f60f33e26..4f80e76e35 100644 --- a/wcfsetup/install/files/lib/acp/form/PageAddForm.class.php +++ b/wcfsetup/install/files/lib/acp/form/PageAddForm.class.php @@ -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] : '') ]; } diff --git a/wcfsetup/install/files/lib/acp/form/PageEditForm.class.php b/wcfsetup/install/files/lib/acp/form/PageEditForm.class.php index b067afbe20..4ae43fd0e2 100644 --- a/wcfsetup/install/files/lib/acp/form/PageEditForm.class.php +++ b/wcfsetup/install/files/lib/acp/form/PageEditForm.class.php @@ -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 = []; diff --git a/wcfsetup/install/files/lib/data/article/ArticleAction.class.php b/wcfsetup/install/files/lib/data/article/ArticleAction.class.php index 027349bbfc..a43096e058 100644 --- a/wcfsetup/install/files/lib/data/article/ArticleAction.class.php +++ b/wcfsetup/install/files/lib/data/article/ArticleAction.class.php @@ -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)]); } diff --git a/wcfsetup/install/files/lib/data/article/content/ArticleContent.class.php b/wcfsetup/install/files/lib/data/article/content/ArticleContent.class.php index b89798d69a..55dcc24e6c 100644 --- a/wcfsetup/install/files/lib/data/article/content/ArticleContent.class.php +++ b/wcfsetup/install/files/lib/data/article/content/ArticleContent.class.php @@ -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 * diff --git a/wcfsetup/install/files/lib/data/box/Box.class.php b/wcfsetup/install/files/lib/data/box/Box.class.php index ce4c1e5089..66f3cc5688 100644 --- a/wcfsetup/install/files/lib/data/box/Box.class.php +++ b/wcfsetup/install/files/lib/data/box/Box.class.php @@ -1,5 +1,6 @@ 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); } /** diff --git a/wcfsetup/install/files/lib/data/box/BoxAction.class.php b/wcfsetup/install/files/lib/data/box/BoxAction.class.php index 04f24daef8..3c6f7f9af9 100644 --- a/wcfsetup/install/files/lib/data/box/BoxAction.class.php +++ b/wcfsetup/install/files/lib/data/box/BoxAction.class.php @@ -1,11 +1,14 @@ 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 index 0000000000..3685c5716f --- /dev/null +++ b/wcfsetup/install/files/lib/data/box/content/BoxContent.class.php @@ -0,0 +1,65 @@ + + * @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 index 0000000000..eaac6b19de --- /dev/null +++ b/wcfsetup/install/files/lib/data/box/content/BoxContentAction.class.php @@ -0,0 +1,22 @@ + + * @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 index 0000000000..114e0342e9 --- /dev/null +++ b/wcfsetup/install/files/lib/data/box/content/BoxContentEditor.class.php @@ -0,0 +1,22 @@ + + * @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 index 0000000000..abdbafbba0 --- /dev/null +++ b/wcfsetup/install/files/lib/data/box/content/BoxContentList.class.php @@ -0,0 +1,24 @@ + + * @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; +} diff --git a/wcfsetup/install/files/lib/data/page/Page.class.php b/wcfsetup/install/files/lib/data/page/Page.class.php index 4945b770a6..82011ef131 100644 --- a/wcfsetup/install/files/lib/data/page/Page.class.php +++ b/wcfsetup/install/files/lib/data/page/Page.class.php @@ -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; } /** diff --git a/wcfsetup/install/files/lib/data/page/PageAction.class.php b/wcfsetup/install/files/lib/data/page/PageAction.class.php index f82e446798..80adeb5e52 100644 --- a/wcfsetup/install/files/lib/data/page/PageAction.class.php +++ b/wcfsetup/install/files/lib/data/page/PageAction.class.php @@ -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 index 0000000000..9c30258822 --- /dev/null +++ b/wcfsetup/install/files/lib/data/page/content/PageContent.class.php @@ -0,0 +1,84 @@ + + * @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 index 0000000000..bdad4094f0 --- /dev/null +++ b/wcfsetup/install/files/lib/data/page/content/PageContentAction.class.php @@ -0,0 +1,22 @@ + + * @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 index 0000000000..f0f9ae0ef0 --- /dev/null +++ b/wcfsetup/install/files/lib/data/page/content/PageContentEditor.class.php @@ -0,0 +1,22 @@ + + * @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 index 0000000000..975c5dfcd2 --- /dev/null +++ b/wcfsetup/install/files/lib/data/page/content/PageContentList.class.php @@ -0,0 +1,24 @@ + + * @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; +} diff --git a/wcfsetup/install/files/lib/page/CmsPage.class.php b/wcfsetup/install/files/lib/page/CmsPage.class.php index 148badb5bb..2645e74a5b 100644 --- a/wcfsetup/install/files/lib/page/CmsPage.class.php +++ b/wcfsetup/install/files/lib/page/CmsPage.class.php @@ -1,5 +1,6 @@ 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']); } } diff --git a/wcfsetup/setup/db/install.sql b/wcfsetup/setup/db/install.sql index 37984080ad..25aa7447f5 100644 --- a/wcfsetup/setup/db/install.sql +++ b/wcfsetup/setup/db/install.sql @@ -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) );