From c139d069c92d6ecc24d739d6d8f408509b4f01be Mon Sep 17 00:00:00 2001 From: Marcel Werk Date: Tue, 31 May 2016 00:26:56 +0200 Subject: [PATCH] Added validation of custom page urls --- .../install/files/acp/templates/pageAdd.tpl | 9 +-- .../files/lib/acp/form/PageAddForm.class.php | 61 +++++++++++++++++-- .../files/lib/acp/form/PageEditForm.class.php | 16 +++++ .../files/lib/data/page/PageEditor.class.php | 54 ++++++++++++++++ 4 files changed, 130 insertions(+), 10 deletions(-) diff --git a/wcfsetup/install/files/acp/templates/pageAdd.tpl b/wcfsetup/install/files/acp/templates/pageAdd.tpl index e39174a8e0..ce614511bb 100644 --- a/wcfsetup/install/files/acp/templates/pageAdd.tpl +++ b/wcfsetup/install/files/acp/templates/pageAdd.tpl @@ -123,11 +123,11 @@ {if !$isMultilingual} - +
- {if $errorField == 'customURL'} + {if $errorField == 'customURL_0'} {if $errorType == 'empty'} {lang}wcf.global.form.error.empty{/lang} @@ -140,11 +140,12 @@ {else} {foreach from=$availableLanguages item=availableLanguage} - + {assign var='__errorFieldName' value='customURL_'|concat:$availableLanguage->languageID} +
- {if $errorField == 'customURL'} + {if $errorField == $__errorFieldName} {if $errorType == 'empty'} {lang}wcf.global.form.error.empty{/lang} diff --git a/wcfsetup/install/files/lib/acp/form/PageAddForm.class.php b/wcfsetup/install/files/lib/acp/form/PageAddForm.class.php index 0fbb2ee751..7632c0aa1b 100644 --- a/wcfsetup/install/files/lib/acp/form/PageAddForm.class.php +++ b/wcfsetup/install/files/lib/acp/form/PageAddForm.class.php @@ -4,6 +4,7 @@ use wcf\data\application\Application; use wcf\data\application\ApplicationList; use wcf\data\box\Box; use wcf\data\box\BoxList; +use wcf\data\language\Language; use wcf\data\page\Page; use wcf\data\page\PageAction; use wcf\data\page\PageEditor; @@ -96,6 +97,12 @@ class PageAddForm extends AbstractForm { */ public $availableBoxes = []; + /** + * list of available languages + * @var Language[] + */ + public $availableLanguages = []; + /** * page custom URL * @var string[] @@ -145,6 +152,9 @@ class PageAddForm extends AbstractForm { $applicationList->readObjects(); $this->availableApplications = $applicationList->getObjects(); + // get available languages + $this->availableLanguages = LanguageFactory::getInstance()->getLanguages(); + // get boxes $boxList = new BoxList(); $boxList->sqlOrderBy = 'box.name'; @@ -209,7 +219,7 @@ class PageAddForm extends AbstractForm { $this->validateApplicationPackageID(); - $this->validateCustomUrl(); + $this->validateCustomUrls(); $this->validateBoxIDs(); } @@ -271,10 +281,49 @@ class PageAddForm extends AbstractForm { * * @throws UserInputException */ - protected function validateCustomUrl() { - foreach ($this->customURL as $type => $customURL) { - if (!empty($customURL) && !RouteHandler::isValidCustomUrl($customURL)) { - throw new UserInputException('customURL_' . $type, 'invalid'); + protected function validateCustomUrls() { + if (empty($this->customURL) && $this->pageType != 'system') { + if ($this->isMultilingual) { + $language1 = reset($this->availableLanguages); + throw new UserInputException('customURL_'.$language1->languageID); + } + else { + throw new UserInputException('customURL_0'); + } + } + + foreach ($this->customURL as $languageID => $customURL) { + $this->validateCustomUrl($languageID, $customURL); + } + } + + /** + * Validates given custom url. + * + * @param integer $languageID + * @param string $customURL + * + * @throws UserInputException + */ + protected function validateCustomUrl($languageID, $customURL) { + if (empty($customURL)) { + if ($this->pageType != 'system') { + throw new UserInputException('customURL_' . $languageID, 'invalid'); + } + } + else if (!RouteHandler::isValidCustomUrl($customURL)) { + throw new UserInputException('customURL_' . $languageID, 'invalid'); + } + else { + // check whether url is already in use + if (!PageEditor::isUniqueCustomUrl($customURL, $this->applicationPackageID)) { + throw new UserInputException('customURL_' . $languageID, 'notUnique'); + } + + foreach ($this->customURL as $languageID2 => $customURL2) { + if ($languageID != $languageID2 && $customURL = $customURL2) { + throw new UserInputException('customURL_' . $languageID, 'notUnique'); + } } } } @@ -429,7 +478,7 @@ class PageAddForm extends AbstractForm { 'metaKeywords' => $this->metaKeywords, 'boxIDs' => $this->boxIDs, 'availableApplications' => $this->availableApplications, - 'availableLanguages' => LanguageFactory::getInstance()->getLanguages(), + 'availableLanguages' => $this->availableLanguages, 'availableBoxes' => $this->availableBoxes, 'pageNodeList' => (new PageNodeTree())->getNodeList() ]); diff --git a/wcfsetup/install/files/lib/acp/form/PageEditForm.class.php b/wcfsetup/install/files/lib/acp/form/PageEditForm.class.php index a8b385c3b6..749ad6f1eb 100644 --- a/wcfsetup/install/files/lib/acp/form/PageEditForm.class.php +++ b/wcfsetup/install/files/lib/acp/form/PageEditForm.class.php @@ -93,6 +93,22 @@ class PageEditForm extends PageAddForm { // type is immutable } + /** + * @inheritDoc + */ + protected function validateCustomUrl($languageID, $customURL) { + if ($this->pageType == 'system') { + if ($customURL != $this->page->controllerCustomURL) { + parent::validateCustomUrl($languageID, $customURL); + } + } + else { + if ($customURL != $this->page->getPageContent()[$languageID]) { + parent::validateCustomUrl($languageID, $customURL); + } + } + } + /** * @inheritDoc */ diff --git a/wcfsetup/install/files/lib/data/page/PageEditor.class.php b/wcfsetup/install/files/lib/data/page/PageEditor.class.php index ebb14fd42d..d75bce1e01 100644 --- a/wcfsetup/install/files/lib/data/page/PageEditor.class.php +++ b/wcfsetup/install/files/lib/data/page/PageEditor.class.php @@ -2,8 +2,12 @@ namespace wcf\data\page; use wcf\data\DatabaseObjectEditor; use wcf\data\IEditableCachedObject; +use wcf\data\package\PackageCache; use wcf\system\cache\builder\PageCacheBuilder; use wcf\system\cache\builder\RoutingCacheBuilder; +use wcf\system\request\ControllerMap; +use wcf\system\WCF; +use wcf\util\FileUtil; /** * Provides functions to edit pages. @@ -32,4 +36,54 @@ class PageEditor extends DatabaseObjectEditor implements IEditableCachedObject { RoutingCacheBuilder::getInstance()->reset(); PageCacheBuilder::getInstance()->reset(); } + + /** + * Returns true if given custom url is unique. + * + * @param string $customURL + * @param integer $packageID + * + * @return boolean + */ + public static function isUniqueCustomUrl($customURL, $packageID = 1) { + // check controller + $package = PackageCache::getInstance()->getPackage($packageID); + $packageDir = FileUtil::addTrailingSlash(FileUtil::getRealPath(WCF_DIR.$package->packageDir)); + + $files = array_merge(glob($packageDir . 'lib/action/*.php'), glob($packageDir . 'lib/form/*.php'), glob($packageDir . 'lib/page/*.php')); + foreach ($files as $file) { + $filename = preg_replace('/(Action|Page|Form)(\.class)?\.php$/', '', basename($file)); + if ($customURL == ControllerMap::transformController($filename)) { + return false; + } + } + + // check custom controller urls + $sql = "SELECT COUNT(*) AS count + FROM wcf".WCF_N."_page + WHERE controllerCustomURL = ? + AND applicationPackageID = ?"; + $statement = WCF::getDB()->prepareStatement($sql); + $statement->execute([$customURL, $packageID]); + if ($statement->fetchColumn()) { + return false; + } + + // check custom urls + $sql = "SELECT COUNT(*) AS count + FROM wcf".WCF_N."_page_content + WHERE customURL = ? + AND pageID IN ( + SELECT pageID + FROM wcf".WCF_N."_page + WHERE applicationPackageID = ? + )"; + $statement = WCF::getDB()->prepareStatement($sql); + $statement->execute([$customURL, $packageID]); + if ($statement->fetchColumn()) { + return false; + } + + return true; + } } -- 2.20.1