Added fail-safe mechanism for options, WCF is now an app
authorAlexander Ebert <ebert@woltlab.com>
Sun, 7 Feb 2016 17:45:05 +0000 (18:45 +0100)
committerAlexander Ebert <ebert@woltlab.com>
Sun, 7 Feb 2016 17:45:05 +0000 (18:45 +0100)
wcfsetup/install/files/acp/global.php
wcfsetup/install/files/app.config.inc.php [new file with mode: 0644]
wcfsetup/install/files/global.php
wcfsetup/install/files/lib/data/option/OptionEditor.class.php
wcfsetup/install/files/lib/data/package/Package.class.php
wcfsetup/install/files/lib/system/WCF.class.php
wcfsetup/install/files/lib/system/cache/builder/OptionCacheBuilder.class.php
wcfsetup/install/files/lib/system/package/PackageInstallationDispatcher.class.php
wcfsetup/install/files/options.inc.php

index a107b52e095bfd548643a06944c7bf22a097b8b9..5298236446e939069c5e4a0ca5f519a46ae3e9de 100644 (file)
@@ -10,8 +10,8 @@
 // This constant is already set in each package which got an own config.inc.php
 if (!defined('RELATIVE_WCF_DIR')) define('RELATIVE_WCF_DIR', '../');
 
-// define the wcf-root-dir
-define('WCF_DIR', dirname(dirname(__FILE__)).'/');
+// include config
+require_once(RELATIVE_WCF_DIR.'app.config.inc.php');
 
 // starting wcf acp
 require_once(WCF_DIR.'lib/system/WCF.class.php');
diff --git a/wcfsetup/install/files/app.config.inc.php b/wcfsetup/install/files/app.config.inc.php
new file mode 100644 (file)
index 0000000..6d4a379
--- /dev/null
@@ -0,0 +1,4 @@
+<?php
+// com.woltlab.wcf (packageID 0 during WCFSetup)
+if (!defined('WCF_DIR')) define('WCF_DIR', __DIR__.'/');
+if (!defined('PACKAGE_ID')) define('PACKAGE_ID', 0);
index 2d509c45e5c05233e1eaa5e2632c9101dc8d4c8b..6909cec234ad334529ccf4a4d88c8b202ccaad57 100644 (file)
@@ -1,22 +1,13 @@
 <?php
 /**
  * @author     Marcel Werk
- * @copyright  2001-2015 WoltLab GmbH
+ * @copyright  2001-2016 WoltLab GmbH
  * @license    GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
  * @package    com.woltlab.wcf
  * @category   Community Framework
  */
-// ignore direct access
-if (!defined('PACKAGE_ID')) {
-       define('PACKAGE_ID', 1);
-       /* TODO:
-       @header("HTTP/1.0 404 Not Found");
-       exit;
-       */
-}
-
-// define the wcf-root-dir
-define('WCF_DIR', dirname(__FILE__).'/');
+// include config
+require_once(__DIR__.'/app.config.inc.php');
 
 // initiate wcf core
 require_once(WCF_DIR.'lib/system/WCF.class.php');
index 6ccde53af3ab1c4248e459fc5601c7128b5306fd..e079e80783de6aa2e6fd03aa0a1ddf46689a027f 100644 (file)
@@ -130,6 +130,9 @@ class OptionEditor extends DatabaseObjectEditor implements IEditableCachedObject
                }
                unset($options);
                
+               // add a pseudo option that indicates that option file has been written properly
+               $writer->write("if (!defined('WCF_OPTION_INC_PHP_SUCCESS')) define('WCF_OPTION_INC_PHP_SUCCESS', true);");
+               
                // file footer
                $writer->write("\n");
                $writer->flush();
index a88a5c41331e7781a09adfe937a344dcd2fd19f9..68b191a2bdac89a56e393f7b9b7262db0b200706 100644 (file)
@@ -1,7 +1,6 @@
 <?php
 namespace wcf\data\package;
 use wcf\data\DatabaseObject;
-use wcf\system\io\File;
 use wcf\system\package\PackageInstallationDispatcher;
 use wcf\system\WCF;
 use wcf\util\FileUtil;
@@ -10,7 +9,7 @@ use wcf\util\FileUtil;
  * Represents a package.
  * 
  * @author     Alexander Ebert
- * @copyright  2001-2015 WoltLab GmbH
+ * @copyright  2001-2016 WoltLab GmbH
  * @license    GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
  * @package    com.woltlab.wcf
  * @subpackage data.package
@@ -346,23 +345,28 @@ class Package extends DatabaseObject {
        public static function writeConfigFile($packageID) {
                $package = new Package($packageID);
                $packageDir = FileUtil::addTrailingSlash(FileUtil::getRealPath(WCF_DIR.$package->packageDir));
-               $file = new File($packageDir.PackageInstallationDispatcher::CONFIG_FILE);
-               $file->write("<?php\n");
+               
                $prefix = strtoupper(self::getAbbreviation($package->package));
                
-               $file->write("// ".$package->package." (packageID ".$package->packageID.")\n");
-               $file->write("if (!defined('".$prefix."_DIR')) define('".$prefix."_DIR', dirname(__FILE__).'/');\n");
-               $file->write("if (!defined('RELATIVE_".$prefix."_DIR')) define('RELATIVE_".$prefix."_DIR', '');\n");
-               $file->write("\n");
+               $content = "<?php\n";
+               $content .= "// {$package->package} (packageID {$packageID})\n";
+               $content .= "if (!defined('{$prefix}_DIR')) define('{$prefix}_DIR', __DIR__.'/');\n";
+               $content .= "if (!defined('PACKAGE_ID')) define('PACKAGE_ID', {$packageID});\n";
+               $content .= "if (!defined('PACKAGE_NAME')) define('PACKAGE_NAME', '" . addcslashes($package->getName(), "'") . "');\n";
+               $content .= "if (!defined('PACKAGE_VERSION')) define('PACKAGE_VERSION', '{$package->packageVersion}');\n";
+               
+               if ($packageID != 1) {
+                       $content .= "\n";
+                       $content .= "// helper constants for applications\n";
+                       $content .= "if (!defined('RELATIVE_{$prefix}_DIR')) define('RELATIVE_{$prefix}_DIR', '');\n";
+                       $content .= "if (!defined('RELATIVE_WCF_DIR')) define('RELATIVE_WCF_DIR', RELATIVE_{$prefix}_DIR.'" . FileUtil::getRelativePath($packageDir, WCF_DIR) . "');\n";
+               }
                
-               // write general information
-               $file->write("// general info\n");
-               $file->write("if (!defined('RELATIVE_WCF_DIR')) define('RELATIVE_WCF_DIR', RELATIVE_".$prefix."_DIR.'".FileUtil::getRelativePath($packageDir, WCF_DIR)."');\n");
-               $file->write("if (!defined('PACKAGE_ID')) define('PACKAGE_ID', ".$packageID.");\n");
-               $file->write("if (!defined('PACKAGE_NAME')) define('PACKAGE_NAME', '".str_replace("'", "\'", $package->getName())."');\n");
-               $file->write("if (!defined('PACKAGE_VERSION')) define('PACKAGE_VERSION', '".$package->packageVersion."');\n");
+               file_put_contents($packageDir . PackageInstallationDispatcher::CONFIG_FILE, $content);
                
-               // write end
-               $file->close();
+               // add legacy config.inc.php file for backward compatibility
+               if ($packageID != 1 && !file_exists($packageDir.'config.inc.php')) {
+                       file_put_contents($packageDir.'config.inc.php', "<?php" . "\n" . "require_once('".PackageInstallationDispatcher::CONFIG_FILE."');\n");
+               }
        }
 }
index 3e1744fb70f6da58eee2d675cf5d2857f2cbd190..f58f7851feecdf9d500a2627bde6cf078ddc5d4b 100644 (file)
@@ -305,6 +305,24 @@ class WCF {
                        OptionEditor::rebuild();
                }
                require_once($filename);
+               
+               // check if option file is complete and writable
+               if (PACKAGE_ID) {
+                       if (!is_writable($filename)) {
+                               FileUtil::makeWritable($filename);
+                               
+                               if (!is_writable($filename)) {
+                                       throw new SystemException("The option file '" . $filename . "' is not writable.");
+                               }
+                       }
+                       
+                       // check if a previous write operation was incomplete and force rebuilding
+                       if (!defined('WCF_OPTION_INC_PHP_SUCCESS')) {
+                               OptionEditor::rebuild();
+                               
+                               require_once($filename);
+                       }
+               }
        }
        
        /**
@@ -500,6 +518,12 @@ class WCF {
                if (class_exists($className) && is_subclass_of($className, 'wcf\system\application\IApplication')) {
                        // include config file
                        $configPath = $packageDir . PackageInstallationDispatcher::CONFIG_FILE;
+                       
+                       // TODO: this should be done during update instead, remove this before any public release
+                       if (!file_exists($configPath)) {
+                               Package::writeConfigFile($package->packageID);
+                       }
+                       
                        if (file_exists($configPath)) {
                                require_once($configPath);
                        }
index 3d8e1ad51709b16201a375fca468cf788b3b2c1a..2a11d1831887dbcd08add365a2aaee1494a6ebcc 100644 (file)
@@ -8,7 +8,7 @@ use wcf\system\WCF;
  * Caches options and option categories
  * 
  * @author     Marcel Werk
- * @copyright  2001-2015 WoltLab GmbH
+ * @copyright  2001-2016 WoltLab GmbH
  * @license    GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
  * @package    com.woltlab.wcf
  * @subpackage system.cache.builder
@@ -34,81 +34,44 @@ class OptionCacheBuilder extends AbstractCacheBuilder {
        protected $application = 'wcf'; 
        
        /**
-        * @see \wcf\system\cache\builder\AbstractCacheBuilder::rebuild()
+        * @inheritDoc
         */
        public function rebuild(array $parameters) {
-               $data = array(
-                       'categories' => array(),
-                       'options' => array(),
-                       'categoryStructure' => array(),
-                       'optionToCategories' => array()
-               );
+               $data = [
+                       'categories' => [],
+                       'options' => [],
+                       'categoryStructure' => [],
+                       'optionToCategories' => []
+               ];
                
                // option categories
-               // get all option categories and sort categories by priority
-               $sql = "SELECT  categoryName, categoryID
-                       FROM    ".$this->application.WCF_N."_".$this->tableName."_category";
+               $sql = "SELECT          *
+                       FROM            ".$this->application.WCF_N."_".$this->tableName."_category
+                       ORDER BY        showOrder";
                $statement = WCF::getDB()->prepareStatement($sql);
                $statement->execute();
-               $optionCategories = array();
-               while ($row = $statement->fetchArray()) {
-                       $optionCategories[$row['categoryName']] = $row['categoryID'];
-               }
-               
-               if (!empty($optionCategories)) {
-                       // get needed option categories
-                       $conditions = new PreparedStatementConditionBuilder();
-                       $conditions->add("categoryID IN (?)", array($optionCategories));
-                       
-                       $sql = "SELECT          option_category.*, package.packageDir
-                               FROM            ".$this->application.WCF_N."_".$this->tableName."_category option_category
-                               LEFT JOIN       wcf".WCF_N."_package package
-                               ON              (package.packageID = option_category.packageID)
-                               ".$conditions."
-                               ORDER BY        showOrder ASC";
-                       $statement = WCF::getDB()->prepareStatement($sql);
-                       $statement->execute($conditions->getParameters());
-                       while ($row = $statement->fetchArray()) {
-                               $data['categories'][$row['categoryName']] = new OptionCategory(null, $row);
-                               if (!isset($data['categoryStructure'][$row['parentCategoryName']])) {
-                                       $data['categoryStructure'][$row['parentCategoryName']] = array();
-                               }
-                               
-                               $data['categoryStructure'][$row['parentCategoryName']][] = $row['categoryName'];
+               while ($category = $statement->fetchObject(OptionCategory::class)) {
+                       $data['categories'][$category->categoryName] = $category;
+                       if (!isset($data['categoryStructure'][$category->parentCategoryName])) {
+                               $data['categoryStructure'][$category->parentCategoryName] = [];
                        }
+                       
+                       $data['categoryStructure'][$category->parentCategoryName][] = $category->categoryName;
                }
                
                // options
-               // get all options and sort options by priority
-               $optionIDs = array();
-               $sql = "SELECT          optionName, optionID
-                       FROM            ".$this->application.WCF_N."_".$this->tableName;
+               $sql = "SELECT          *
+                       FROM            ".$this->application.WCF_N."_".$this->tableName."
+                       ORDER BY        showOrder";
                $statement = WCF::getDB()->prepareStatement($sql);
                $statement->execute();
-               while ($row = $statement->fetchArray()) {
-                       $optionIDs[$row['optionName']] = $row['optionID'];
-               }
-               
-               if (!empty($optionIDs)) {
-                       // get needed options
-                       $conditions = new PreparedStatementConditionBuilder();
-                       $conditions->add("optionID IN (?)", array($optionIDs));
-                       
-                       $sql = "SELECT          *
-                               FROM            ".$this->application.WCF_N."_".$this->tableName."
-                               ".$conditions."
-                               ORDER BY        showOrder ASC";
-                       $statement = WCF::getDB()->prepareStatement($sql);
-                       $statement->execute($conditions->getParameters());
-                       $optionClassName = $this->optionClassName;
-                       while ($row = $statement->fetchArray()) {
-                               $data['options'][$row['optionName']] = new $optionClassName(null, $row);
-                               if (!isset($data['optionToCategories'][$row['categoryName']])) {
-                                       $data['optionToCategories'][$row['categoryName']] = array();
-                               }
-                               
-                               $data['optionToCategories'][$row['categoryName']][] = $row['optionName'];
+               while ($option = $statement->fetchObject($this->optionClassName)) {
+                       $data['options'][$option->optionName] = $option;
+                       if (!isset($data['optionToCategories'][$option->categoryName])) {
+                               $data['optionToCategories'][$option->categoryName] = [];
                        }
+                       
+                       $data['optionToCategories'][$option->categoryName][] = $option->optionName;
                }
                
                return $data;
index a5384b6f38686b8ee3eb1adfb5a570fbffcde937..3c64393e893beab4031c439eeb91a7dea810cd63 100644 (file)
@@ -81,7 +81,7 @@ class PackageInstallationDispatcher {
         * default name of the config file
         * @var string
         */
-       const CONFIG_FILE = 'config.inc.php';
+       const CONFIG_FILE = 'app.config.inc.php';
        
        /**
         * data of previous package in queue
@@ -237,12 +237,10 @@ class PackageInstallationDispatcher {
                                                wcf".WCF_N."_package package
                                WHERE           queue.processNo = ?
                                                AND package.packageID = queue.packageID
-                                               AND package.packageID <> ?
                                                AND package.isApplication = ?";
                        $statement = WCF::getDB()->prepareStatement($sql);
                        $statement->execute([
                                $this->queue->processNo,
-                               1,
                                1
                        ]);
                        while ($row = $statement->fetchArray()) {
index bef82c4cf6b6e4cd373d856cc5d34fc83a3bea9d..c841ca222c92c4d17d5ccab93b24e5d339584a0c 100644 (file)
@@ -6,7 +6,6 @@
  * @copyright  2001-2015 WoltLab GmbH
  * @license    GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
  */
-define('PACKAGE_ID', 0);
 define('LAST_UPDATE_TIME', TIME_NOW);
 
 define('COOKIE_PREFIX', 'wcf_');
@@ -37,3 +36,5 @@ define('EXTERNAL_LINK_TARGET_BLANK', 0);
 define('URL_LEGACY_MODE', 0);
 define('URL_TO_LOWERCASE', 1);
 define('SEARCH_ENGINE', 'mysql');
+
+define('WCF_OPTION_INC_PHP_SUCCESS', true);