2 namespace wcf\system\package
;
3 use wcf\data\application\Application
;
4 use wcf\data\application\ApplicationEditor
;
5 use wcf\data\language\category\LanguageCategory
;
6 use wcf\data\language\LanguageEditor
;
7 use wcf\data\language\LanguageList
;
8 use wcf\data\option\OptionEditor
;
9 use wcf\data\package\installation\queue\PackageInstallationQueue
;
10 use wcf\data\package\installation\queue\PackageInstallationQueueEditor
;
11 use wcf\data\package\Package
;
12 use wcf\data\package\PackageEditor
;
13 use wcf\system\application\ApplicationHandler
;
14 use wcf\system\cache\CacheHandler
;
15 use wcf\system\database\statement\PreparedStatement
;
16 use wcf\system\database\util\PreparedStatementConditionBuilder
;
17 use wcf\system\exception\SystemException
;
18 use wcf\system\form\container\GroupFormElementContainer
;
19 use wcf\system\form\container\MultipleSelectionFormElementContainer
;
20 use wcf\system\form\element\MultipleSelectionFormElement
;
21 use wcf\system\form\element\TextInputFormElement
;
22 use wcf\system\form\FormDocument
;
23 use wcf\system\language\LanguageFactory
;
24 use wcf\system\package\plugin\IPackageInstallationPlugin
;
25 use wcf\system\package\plugin\ObjectTypePackageInstallationPlugin
;
26 use wcf\system\package\plugin\SQLPackageInstallationPlugin
;
27 use wcf\system\request\LinkHandler
;
28 use wcf\system\request\RouteHandler
;
29 use wcf\system\setup\Installer
;
30 use wcf\system\style\StyleHandler
;
31 use wcf\system\version\VersionHandler
;
33 use wcf\util\FileUtil
;
34 use wcf\util\HeaderUtil
;
35 use wcf\util\StringUtil
;
38 * PackageInstallationDispatcher handles the whole installation process.
40 * @author Alexander Ebert
41 * @copyright 2001-2012 WoltLab GmbH
42 * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
43 * @package com.woltlab.wcf
44 * @subpackage system.package
45 * @category Community Framework
47 class PackageInstallationDispatcher
{
49 * current installation type
52 protected $action = '';
55 * instance of PackageArchive
56 * @var wcf\system\package\PackageArchive
58 public $archive = null;
61 * instance of PackageInstallationNodeBuilder
62 * @var wcf\system\package\PackageInstallationNodeBuilder
64 public $nodeBuilder = null;
68 * @var wcf\data\package\Package
70 public $package = null;
73 * instance of PackageInstallationQueue
74 * @var wcf\system\package\PackageInstallationQueue
79 * default name of the config file
82 const CONFIG_FILE
= 'config.inc.php';
85 * holds state of structuring version tables
88 protected $requireRestructureVersionTables = false;
91 * Creates a new instance of PackageInstallationDispatcher.
93 * @param wcf\data\package\installation\queue\PackageInstallationQueue $queue
95 public function __construct(PackageInstallationQueue
$queue) {
96 $this->queue
= $queue;
97 $this->nodeBuilder
= new PackageInstallationNodeBuilder($this);
99 $this->action
= $this->queue
->action
;
103 * Installs node components and returns next node.
105 * @param string $node
106 * @return wcf\system\package\PackageInstallationStep
108 public function install($node) {
109 $nodes = $this->nodeBuilder
->getNodeData($node);
111 // invoke node-specific actions
112 foreach ($nodes as $data) {
113 $nodeData = unserialize($data['nodeData']);
115 switch ($data['nodeType']) {
117 $step = $this->installPackage($nodeData);
121 $step = $this->executePIP($nodeData);
124 case 'optionalPackages':
125 $step = $this->selectOptionalPackages($node, $nodeData);
129 die("Unknown node type: '".$data['nodeType']."'");
133 if ($step->splitNode()) {
134 $this->nodeBuilder
->cloneNode($node, $data['sequenceNo']);
139 // mark node as completed
140 $this->nodeBuilder
->completeNode($node);
143 $node = $this->nodeBuilder
->getNextNode($node);
144 $step->setNode($node);
146 // perform post-install/update actions
148 // update options.inc.php
149 OptionEditor
::resetCache();
151 if ($this->action
== 'install') {
152 // save localized package infos
153 $this->saveLocalizedPackageInfos();
155 // remove all cache files after WCFSetup
157 CacheHandler
::getInstance()->clear(WCF_DIR
.'cache/', 'cache.*.php');
160 // rebuild application paths
161 ApplicationHandler
::rebuild();
162 ApplicationEditor
::setup();
165 // remove template listener cache
166 CacheHandler
::getInstance()->clear(WCF_DIR
.'cache/templateListener/', '*.php');
168 // reset language cache
169 LanguageFactory
::getInstance()->clearCache();
170 LanguageFactory
::getInstance()->deleteLanguageCache();
173 StyleHandler
::resetStylesheets();
176 if ($this->requireRestructureVersionTables
) {
177 $this->restructureVersionTables();
184 * Returns current package archive.
186 * @return wcf\system\package\PackageArchive
188 public function getArchive() {
189 if ($this->archive
=== null) {
190 $this->archive
= new PackageArchive($this->queue
->archive
, $this->getPackage());
192 if (FileUtil
::isURL($this->archive
->getArchive())) {
193 // get return value and update entry in
194 // package_installation_queue with this value
195 $archive = $this->archive
->downloadArchive();
196 $queueEditor = new PackageInstallationQueueEditor($this->queue
);
197 $queueEditor->update(array(
198 'archive' => $archive
202 $this->archive
->openArchive();
205 return $this->archive
;
209 * Installs current package.
211 * @param array $nodeData
213 protected function installPackage(array $nodeData) {
214 $installationStep = new PackageInstallationStep();
216 // check requirements
217 if (!empty($nodeData['requirements'])) {
218 foreach ($nodeData['requirements'] as $package => $requirementData) {
219 // get existing package
220 if ($requirementData['packageID']) {
221 $sql = "SELECT packageName, packageVersion
222 FROM wcf".WCF_N
."_package
223 WHERE packageID = ?";
224 $statement = WCF
::getDB()->prepareStatement($sql);
225 $statement->execute(array($requirementData['packageID']));
228 // try to find matching package
229 $sql = "SELECT packageName, packageVersion
230 FROM wcf".WCF_N
."_package
232 $statement = WCF
::getDB()->prepareStatement($sql);
233 $statement->execute(array($package));
235 $row = $statement->fetchArray();
237 // package is required but not available
238 if ($row === false) {
239 throw new SystemException("Package '".$package."' is required by '".$nodeData['packageName']."', but is neither installed nor shipped.");
242 // check version requirements
243 if ($requirementData['minVersion']) {
244 if (Package
::compareVersion($row['packageVersion'], $requirementData['minVersion']) < 0) {
245 throw new SystemException("Package '".$nodeData['packageName']."' requires the package '".$row['packageName']."' in version '".$requirementData['minVersion']."', but version '".$row['packageVersion']."'");
250 unset($nodeData['requirements']);
252 if (!$this->queue
->packageID
) {
253 // create package entry
254 $package = PackageEditor
::create($nodeData);
256 // update package id for current queue
257 $queueEditor = new PackageInstallationQueueEditor($this->queue
);
258 $queueEditor->update(array(
259 'packageID' => $package->packageID
262 // save excluded packages
263 if (count($this->getArchive()->getExcludedPackages()) > 0) {
264 $sql = "INSERT INTO wcf".WCF_N
."_package_exclusion
265 (packageID, excludedPackage, excludedPackageVersion)
267 $statement = WCF
::getDB()->prepareStatement($sql);
269 foreach ($this->getArchive()->getExcludedPackages() as $excludedPackage) {
270 $statement->execute(array($package->packageID
, $excludedPackage['name'], (!empty($excludedPackage['version']) ?
$excludedPackage['version'] : '')));
274 // if package is plugin to com.woltlab.wcf it must not have any other requirement
275 $requirements = $this->getArchive()->getRequirements();
277 // insert requirements and dependencies
278 $requirements = $this->getArchive()->getAllExistingRequirements();
279 if (!empty($requirements)) {
280 $sql = "INSERT INTO wcf".WCF_N
."_package_requirement
281 (packageID, requirement)
283 $statement = WCF
::getDB()->prepareStatement($sql);
285 foreach ($requirements as $identifier => $possibleRequirements) {
286 if (count($possibleRequirements) == 1) {
287 $requirement = array_shift($possibleRequirements);
290 $requirement = $possibleRequirements[$this->selectedRequirements
[$identifier]];
293 $statement->execute(array($package->packageID
, $requirement['packageID']));
298 $this->queue
= new PackageInstallationQueue($this->queue
->queueID
);
299 $this->package
= null;
301 if ($package->isApplication
) {
302 $host = StringUtil
::replace(RouteHandler
::getProtocol(), '', RouteHandler
::getHost());
303 $path = RouteHandler
::getPath(array('acp'));
305 // insert as application
306 ApplicationEditor
::create(array(
307 'domainName' => $host,
308 'domainPath' => $path,
309 'cookieDomain' => $host,
310 'cookiePath' => $path,
311 'packageID' => $package->packageID
316 if ($this->getPackage()->isApplication
&& $this->getPackage()->package
!= 'com.woltlab.wcf' && $this->getAction() == 'install') {
317 if (empty($this->getPackage()->packageDir
)) {
318 $document = $this->promptPackageDir();
319 if ($document !== null && $document instanceof FormDocument
) {
320 $installationStep->setDocument($document);
323 $installationStep->setSplitNode();
327 return $installationStep;
331 * Saves the localized package infos.
333 * @todo license and readme
335 protected function saveLocalizedPackageInfos() {
336 $package = new Package($this->queue
->packageID
);
338 // localize package information
339 $sql = "INSERT INTO wcf".WCF_N
."_language_item
340 (languageID, languageItem, languageItemValue, languageCategoryID, packageID)
341 VALUES (?, ?, ?, ?, ?)";
342 $statement = WCF
::getDB()->prepareStatement($sql);
345 $languageList = new LanguageList();
346 $languageList->readObjects();
348 // workaround for WCFSetup
351 FROM wcf".WCF_N
."_language_category
352 WHERE languageCategory = ?";
353 $statement2 = WCF
::getDB()->prepareStatement($sql);
354 $statement2->execute(array('wcf.acp.package'));
355 $languageCategory = $statement2->fetchObject('wcf\data\language\category\LanguageCategory');
358 $languageCategory = LanguageFactory
::getInstance()->getCategory('wcf.acp.package');
362 $this->saveLocalizedPackageInfo($statement, $languageList, $languageCategory, $package, 'packageName');
364 // save package description
365 $this->saveLocalizedPackageInfo($statement, $languageList, $languageCategory, $package, 'packageDescription');
367 // update description and name
368 $packageEditor = new PackageEditor($package);
369 $packageEditor->update(array(
370 'packageDescription' => 'wcf.acp.package.packageDescription.package'.$this->queue
->packageID
,
371 'packageName' => 'wcf.acp.package.packageName.package'.$this->queue
->packageID
376 * Saves a localized package info.
378 * @param wcf\system\database\statement\PreparedStatement $statement
379 * @param wcf\data\language\LanguageList $languageList
380 * @param wcf\data\language\category\LanguageCategory $languageCategory
381 * @param wcf\data\package\Package $package
382 * @param string $infoName
384 protected function saveLocalizedPackageInfo(PreparedStatement
$statement, $languageList, LanguageCategory
$languageCategory, Package
$package, $infoName) {
385 $infoValues = $this->getArchive()->getPackageInfo($infoName);
387 // get default value for languages without specified information
389 if (isset($infoValues['default'])) {
390 $defaultValue = $infoValues['default'];
392 else if (isset($infoValues['en'])) {
393 // fallback to English
394 $defaultValue = $infoValues['en'];
396 else if (isset($infoValues[WCF
::getLanguage()->getFixedLanguageCode()])) {
397 // fallback to the language of the current user
398 $defaultValue = $infoValues[WCF
::getLanguage()->getFixedLanguageCode()];
400 else if ($infoName == 'packageName') {
401 // fallback to the package identifier for the package name
402 $defaultValue = $this->archive
->getPackageInfo('name');
405 foreach ($languageList as $language) {
406 $value = $defaultValue;
407 if (isset($infoValues[$language->languageCode
])) {
408 $value = $infoValues[$language->languageCode
];
411 $statement->execute(array(
412 $language->languageID
,
413 'wcf.acp.package.'.$infoName.'.package'.$package->packageID
,
415 $languageCategory->languageCategoryID
,
422 * Executes a package installation plugin.
427 protected function executePIP(array $nodeData) {
428 $step = new PackageInstallationStep();
430 // fetch all pips associated with current PACKAGE_ID and include pips
431 // previously installed by current installation queue
432 $sql = "SELECT pluginName, className
433 FROM wcf".WCF_N
."_package_installation_plugin
434 WHERE pluginName = ?";
435 $statement = WCF
::getDB()->prepareStatement($sql);
436 $statement->execute(array(
439 $row = $statement->fetchArray();
442 if (!$row ||
(strcmp($nodeData['pip'], $row['pluginName']) !== 0)) {
443 throw new SystemException("unable to find package installation plugin '".$nodeData['pip']."'");
446 // valdidate class definition
447 $className = $row['className'];
448 if (!class_exists($className)) {
449 throw new SystemException("unable to find class '".$className."'");
452 $plugin = new $className($this, $nodeData);
454 if (!($plugin instanceof IPackageInstallationPlugin
)) {
455 throw new SystemException("'".$className."' does not implement 'wcf\system\package\plugin\IPackageInstallationPlugin'");
458 if ($plugin instanceof SQLPackageInstallationPlugin ||
$plugin instanceof ObjectTypePackageInstallationPlugin
) {
459 $this->requireRestructureVersionTables
= true;
464 $document = $plugin->{$this->action
}();
466 catch (SplitNodeException
$e) {
467 $step->setSplitNode();
470 if ($document !== null && ($document instanceof FormDocument
)) {
471 $step->setDocument($document);
472 $step->setSplitNode();
479 * Displays a list to select optional packages or installs selection.
481 * @param string $currentNode
482 * @param array $nodeData
483 * @return wcf\system\package\PackageInstallationStep
485 protected function selectOptionalPackages($currentNode, array $nodeData) {
486 $installationStep = new PackageInstallationStep();
488 $document = $this->promptOptionalPackages($nodeData);
489 if ($document !== null && $document instanceof FormDocument
) {
490 $installationStep->setDocument($document);
491 $installationStep->setSplitNode();
493 // insert new nodes for each package
494 else if (is_array($document)) {
495 // get target child node
496 $node = $currentNode;
497 $queue = $this->queue
;
500 foreach ($nodeData as $package) {
501 if (in_array($package['package'], $document)) {
503 $this->nodeBuilder
->shiftNodes($currentNode, 'tempNode');
507 $queue = PackageInstallationQueueEditor
::create(array(
508 'parentQueueID' => $queue->queueID
,
509 'processNo' => $this->queue
->processNo
,
510 'userID' => WCF
::getUser()->userID
,
511 'package' => $package['package'],
512 'packageName' => $package['packageName'],
513 'archive' => $package['archive'],
514 'action' => $queue->action
517 $installation = new PackageInstallationDispatcher($queue);
518 $installation->nodeBuilder
->setParentNode($node);
519 $installation->nodeBuilder
->buildNodes();
520 $node = $installation->nodeBuilder
->getCurrentNode();
526 $this->nodeBuilder
->shiftNodes('tempNode', $node);
530 return $installationStep;
534 * Extracts files from .tar(.gz) archive and installs them
536 * @param string $targetDir
537 * @param string $sourceArchive
538 * @param FileHandler $fileHandler
539 * @return wcf\system\setup\Installer
541 public function extractFiles($targetDir, $sourceArchive, $fileHandler = null) {
542 return new Installer($targetDir, $sourceArchive, $fileHandler);
546 * Returns current package.
548 * @return wcf\data\package\Package
550 public function getPackage() {
551 if ($this->package
=== null) {
552 $this->package
= new Package($this->queue
->packageID
);
555 return $this->package
;
559 * Prompts for a text input for package directory (applies for applications only)
561 * @return wcf\system\form\FormDocument
563 protected function promptPackageDir() {
564 if (!PackageInstallationFormManager
::findForm($this->queue
, 'packageDir')) {
566 $container = new GroupFormElementContainer();
567 $packageDir = new TextInputFormElement($container);
568 $packageDir->setName('packageDir');
569 $packageDir->setLabel(WCF
::getLanguage()->get('wcf.acp.package.packageDir.input'));
571 $path = RouteHandler
::getPath(array('wcf', 'acp'));
572 $defaultPath = FileUtil
::addTrailingSlash(FileUtil
::unifyDirSeperator($_SERVER['DOCUMENT_ROOT'] . $path));
573 $packageDir->setValue($defaultPath);
574 $container->appendChild($packageDir);
576 $document = new FormDocument('packageDir');
577 $document->appendContainer($container);
579 PackageInstallationFormManager
::registerForm($this->queue
, $document);
583 $document = PackageInstallationFormManager
::getForm($this->queue
, 'packageDir');
584 $document->handleRequest();
585 $packageDir = $document->getValue('packageDir');
587 if ($packageDir !== null) {
588 // validate package dir
589 if (file_exists(FileUtil
::addTrailingSlash($packageDir) . 'global.php')) {
590 $document->setError('packageDir', WCF
::getLanguage()->get('wcf.acp.package.packageDir.notAvailable'));
595 $packageEditor = new PackageEditor($this->getPackage());
596 $packageEditor->update(array(
597 'packageDir' => FileUtil
::getRelativePath(WCF_DIR
, $packageDir)
601 $domainPath = FileUtil
::getRelativePath(FileUtil
::unifyDirSeperator($_SERVER['DOCUMENT_ROOT']), FileUtil
::unifyDirSeperator($packageDir));
603 // work-around for applications installed in document root
604 if ($domainPath == './') {
608 $domainPath = FileUtil
::addLeadingSlash(FileUtil
::addTrailingSlash($domainPath));
610 // update application path
611 $application = new Application($this->getPackage()->packageID
);
612 $applicationEditor = new ApplicationEditor($application);
613 $applicationEditor->update(array(
614 'domainPath' => $domainPath,
615 'cookiePath' => $domainPath
618 // create directory and set permissions
619 @mkdir
($packageDir, 0777, true);
620 @chmod
($packageDir, 0777);
628 * Prompts a selection of optional packages.
632 protected function promptOptionalPackages(array $packages) {
633 if (!PackageInstallationFormManager
::findForm($this->queue
, 'optionalPackages')) {
634 $container = new MultipleSelectionFormElementContainer();
635 $container->setName('optionalPackages');
637 foreach ($packages as $package) {
638 $optionalPackage = new MultipleSelectionFormElement($container);
639 $optionalPackage->setName('optionalPackages');
640 $optionalPackage->setLabel($package['packageName']);
641 $optionalPackage->setValue($package['package']);
643 $container->appendChild($optionalPackage);
646 $document = new FormDocument('optionalPackages');
647 $document->appendContainer($container);
649 PackageInstallationFormManager
::registerForm($this->queue
, $document);
653 $document = PackageInstallationFormManager
::getForm($this->queue
, 'optionalPackages');
654 $document->handleRequest();
656 return $document->getValue('optionalPackages');
661 * Returns current package id.
665 public function getPackageID() {
666 return $this->queue
->packageID
;
670 * Returns current package installation type.
674 public function getAction() {
675 return $this->action
;
679 * Opens the package installation queue and
680 * starts the installation, update or uninstallation of the first entry.
682 * @param integer $parentQueueID
683 * @param integer $processNo
685 public static function openQueue($parentQueueID = 0, $processNo = 0) {
686 $conditions = new PreparedStatementConditionBuilder();
687 $conditions->add("userID = ?", array(WCF
::getUser()->userID
));
688 $conditions->add("parentQueueID = ?", array($parentQueueID));
689 if ($processNo != 0) $conditions->add("processNo = ?", array($processNo));
690 $conditions->add("done = ?", array(0));
693 FROM wcf".WCF_N
."_package_installation_queue
695 ORDER BY queueID ASC";
696 $statement = WCF
::getDB()->prepareStatement($sql);
697 $statement->execute($conditions->getParameters());
698 $packageInstallation = $statement->fetchArray();
700 if (!isset($packageInstallation['queueID'])) {
701 $url = LinkHandler
::getInstance()->getLink('PackageList');
702 HeaderUtil
::redirect($url);
706 $url = LinkHandler
::getInstance()->getLink('PackageInstallationConfirm', array(), 'action='.$packageInstallation['action'].'&queueID='.$packageInstallation['queueID']);
707 HeaderUtil
::redirect($url);
713 * Checks the package installation queue for outstanding entries.
717 public static function checkPackageInstallationQueue() {
718 $sql = "SELECT queueID
719 FROM wcf".WCF_N
."_package_installation_queue
721 AND parentQueueID = 0
723 ORDER BY queueID ASC";
724 $statement = WCF
::getDB()->prepareStatement($sql);
725 $statement->execute(array(WCF
::getUser()->userID
));
726 $row = $statement->fetchArray();
732 return $row['queueID'];
736 * Executes post-setup actions.
738 public function completeSetup() {
739 // mark queue as done
740 $queueEditor = new PackageInstallationQueueEditor($this->queue
);
741 $queueEditor->update(array(
746 $this->nodeBuilder
->purgeNodes();
748 // update package version
749 if ($this->action
== 'update') {
750 $packageEditor = new PackageEditor($this->getPackage());
751 $packageEditor->update(array(
752 'updateDate' => TIME_NOW
,
753 'packageVersion' => $this->archive
->getPackageInfo('version')
757 // clear language files once whole installation is completed
758 LanguageEditor
::deleteLanguageFiles();
761 CacheHandler
::getInstance()->clear(WCF_DIR
.'cache/', '*');
765 * Updates queue information.
767 public function updatePackage() {
768 if (empty($this->queue
->packageName
)) {
769 $queueEditor = new PackageInstallationQueueEditor($this->queue
);
770 $queueEditor->update(array(
771 'packageName' => $this->getArchive()->getLocalizedPackageInfo('packageName')
775 $this->queue
= new PackageInstallationQueue($this->queue
->queueID
);
780 * Validates specific php requirements.
782 * @param array $requirements
783 * @return array<array>
785 public static function validatePHPRequirements(array $requirements) {
788 // validate php version
789 if (isset($requirements['version'])) {
791 if (version_compare(PHP_VERSION
, $requirements['version'], '>=')) {
796 $errors['version'] = array(
797 'required' => $requirements['version'],
798 'installed' => PHP_VERSION
803 // validate extensions
804 if (isset($requirements['extensions'])) {
805 foreach ($requirements['extensions'] as $extension) {
806 $passed = (extension_loaded($extension)) ?
true : false;
809 $errors['extension'][] = array(
810 'extension' => $extension
817 if (isset($requirements['settings'])) {
818 foreach ($requirements['settings'] as $setting => $value) {
819 $iniValue = ini_get($setting);
821 $passed = self
::compareSetting($setting, $value, $iniValue);
823 $errors['setting'][] = array(
824 'setting' => $setting,
825 'required' => $value,
826 'installed' => ($iniValue === false) ?
'(unknown)' : $iniValue
832 // validate functions
833 if (isset($requirements['functions'])) {
834 foreach ($requirements['functions'] as $function) {
835 $function = StringUtil
::toLowerCase($function);
837 $passed = self
::functionExists($function);
839 $errors['function'][] = array(
840 'function' => $function
847 if (isset($requirements['classes'])) {
848 foreach ($requirements['classes'] as $class) {
851 // see: http://de.php.net/manual/en/language.oop5.basic.php
852 if (preg_match('~[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*.~', $class)) {
853 $globalClass = '\\'.$class;
855 if (class_exists($globalClass, false)) {
861 $errors['class'][] = array(
873 * Validates if an function exists and is not blacklisted by suhosin extension.
875 * @param string $function
877 * @see http://de.php.net/manual/en/function.function-exists.php#77980
879 protected static function functionExists($function) {
880 if (extension_loaded('suhosin')) {
881 $blacklist = @ini_get
('suhosin.executor.func.blacklist');
882 if (!empty($blacklist)) {
883 $blacklist = explode(',', $blacklist);
884 foreach ($blacklist as $disabledFunction) {
885 $disabledFunction = StringUtil
::toLowerCase(StringUtil
::trim($disabledFunction));
887 if ($function == $disabledFunction) {
894 return function_exists($function);
898 * Compares settings, converting values into compareable ones.
900 * @param string $setting
901 * @param string $value
902 * @param mixed $compareValue
905 protected static function compareSetting($setting, $value, $compareValue) {
906 if ($compareValue === false) return false;
908 $value = StringUtil
::toLowerCase($value);
909 $trueValues = array('1', 'on', 'true');
910 $falseValues = array('0', 'off', 'false');
912 // handle values considered as 'true'
913 if (in_array($value, $trueValues)) {
914 return ($compareValue) ?
true : false;
916 // handle values considered as 'false'
917 else if (in_array($value, $falseValues)) {
918 return (!$compareValue) ?
true : false;
920 else if (!is_numeric($value)) {
921 $compareValue = self
::convertShorthandByteValue($compareValue);
922 $value = self
::convertShorthandByteValue($value);
925 return ($compareValue >= $value) ?
true : false;
929 * Converts shorthand byte values into an integer representing bytes.
931 * @param string $value
933 * @see http://www.php.net/manual/en/faq.using.php#faq.using.shorthandbytes
935 protected static function convertShorthandByteValue($value) {
936 // convert into bytes
937 $lastCharacter = StringUtil
::substring($value, -1);
938 switch ($lastCharacter) {
941 return (int)$value * 1073741824;
946 return (int)$value * 1048576;
951 return (int)$value * 1024;
961 * Restructure version tables.
963 protected function restructureVersionTables() {
964 $objectTypes = VersionHandler
::getInstance()->getObjectTypes();
966 if (empty($objectTypes)) {
970 // base structure of version tables
971 $versionTableBaseColumns = array();
972 $versionTableBaseColumns[] = array('name' => 'versionID', 'data' => array('type' => 'INT', 'key' => 'PRIMARY', 'autoIncrement' => 'AUTO_INCREMENT'));
973 $versionTableBaseColumns[] = array('name' => 'versionUserID', 'data' => array('type' => 'INT'));
974 $versionTableBaseColumns[] = array('name' => 'versionUsername', 'data' => array('type' => 'VARCHAR', 'length' => 255));
975 $versionTableBaseColumns[] = array('name' => 'versionTime', 'data' => array('type' => 'INT'));
977 foreach ($objectTypes as $objectTypeID => $objectType) {
978 // get structure of base table
979 $baseTableColumns = WCF
::getDB()->getEditor()->getColumns($objectType::getDatabaseTableName());
980 // get structure of version table
981 $versionTableColumns = WCF
::getDB()->getEditor()->getColumns($objectType::getDatabaseVersionTableName());
983 if (empty($versionTableColumns)) {
984 $columns = array_merge($versionTableBaseColumns, $baseTableColumns);
986 WCF
::getDB()->getEditor()->createTable($objectType::getDatabaseVersionTableName(), $columns);
989 // check garbage columns in versioned table
990 foreach ($versionTableColumns as $columnData) {
991 if (!array_search($columnData['name'], $baseTableColumns, true)) {
993 WCF
::getDB()->getEditor()->dropColumn($objectType::getDatabaseVersionTableName(), $columnData['name']);
997 // check new columns for versioned table
998 foreach ($baseTableColumns as $columnData) {
999 if (!array_search($columnData['name'], $versionTableColumns, true)) {
1001 WCF
::getDB()->getEditor()->addColumn($objectType::getDatabaseVersionTableName(), $columnData['name'], $columnData['data']);