Added validation of custom page urls
authorMarcel Werk <burntime@woltlab.com>
Mon, 30 May 2016 22:26:56 +0000 (00:26 +0200)
committerMarcel Werk <burntime@woltlab.com>
Mon, 30 May 2016 22:27:32 +0000 (00:27 +0200)
wcfsetup/install/files/acp/templates/pageAdd.tpl
wcfsetup/install/files/lib/acp/form/PageAddForm.class.php
wcfsetup/install/files/lib/acp/form/PageEditForm.class.php
wcfsetup/install/files/lib/data/page/PageEditor.class.php

index e39174a8e03f2a19eaf7af7b20e068a5f59ffb75..ce614511bbbb552330430907a25ae54a28670451 100644 (file)
                                </dl>
                                
                                {if !$isMultilingual}
-                                       <dl{if $errorField == 'customURL'} class="formError"{/if}>
+                                       <dl{if $errorField == 'customURL_0'} class="formError"{/if}>
                                                <dt><label for="customURL">{lang}wcf.acp.page.customURL{/lang}</label></dt>
                                                <dd>
                                                        <input type="text" id="customURL" name="customURL[0]" value="{if !$customURL[0]|empty}{$customURL[0]}{/if}" class="long" />
-                                                       {if $errorField == 'customURL'}
+                                                       {if $errorField == 'customURL_0'}
                                                                <small class="innerError">
                                                                        {if $errorType == 'empty'}
                                                                                {lang}wcf.global.form.error.empty{/lang}
                                        </dl>
                                {else}
                                        {foreach from=$availableLanguages item=availableLanguage}
-                                               <dl{if $errorField == 'customURL'} class="formError"{/if}>
+                                               {assign var='__errorFieldName' value='customURL_'|concat:$availableLanguage->languageID}
+                                               <dl{if $errorField == $__errorFieldName} class="formError"{/if}>
                                                        <dt><label for="customURL{@$availableLanguage->languageID}">{lang}wcf.acp.page.customURL{/lang} ({$availableLanguage->languageName})</label></dt>
                                                        <dd>
                                                                <input type="text" id="customURL{@$availableLanguage->languageID}" name="customURL[{@$availableLanguage->languageID}]" value="{if !$customURL[$availableLanguage->languageID]|empty}{$customURL[$availableLanguage->languageID]}{/if}" class="long" />
-                                                               {if $errorField == 'customURL'}
+                                                               {if $errorField == $__errorFieldName}
                                                                        <small class="innerError">
                                                                                {if $errorType == 'empty'}
                                                                                        {lang}wcf.global.form.error.empty{/lang}
index 0fbb2ee7519e12a1270933c8d12f6afc652d6cc2..7632c0aa1ba98099e661c8c123f74b8307eff268 100644 (file)
@@ -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()
                ]);
index a8b385c3b6276e012832687f1820dc73b92fc88c..749ad6f1eb8475cddb264a361e3bd78e8880bf3c 100644 (file)
@@ -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
         */
index ebb14fd42d17abb349672e313346b7d43475a54e..d75bce1e01a3f41707509bd4b2d1787859e7f8ea 100644 (file)
@@ -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;
+       }
 }