<?php
namespace wcf\system\package;
+use wcf\system\cache\builder\TemplateListenerCodeCacheBuilder;
+
+use wcf\system\cache\builder\TemplateListenerCacheBuilder;
+
use wcf\data\application\Application;
use wcf\data\application\ApplicationEditor;
use wcf\data\language\category\LanguageCategory;
use wcf\data\package\installation\queue\PackageInstallationQueueEditor;
use wcf\data\package\Package;
use wcf\data\package\PackageEditor;
+use wcf\system\application\ApplicationHandler;
use wcf\system\cache\CacheHandler;
use wcf\system\database\statement\PreparedStatement;
use wcf\system\database\util\PreparedStatementConditionBuilder;
use wcf\system\exception\SystemException;
-use wcf\system\form\container;
-use wcf\system\form\element;
+use wcf\system\form\container\GroupFormElementContainer;
+use wcf\system\form\container\MultipleSelectionFormElementContainer;
+use wcf\system\form\element\MultipleSelectionFormElement;
+use wcf\system\form\element\TextInputFormElement;
use wcf\system\form\FormDocument;
use wcf\system\language\LanguageFactory;
-use wcf\system\menu\acp\ACPMenu;
+use wcf\system\package\plugin\IPackageInstallationPlugin;
+use wcf\system\package\plugin\ObjectTypePackageInstallationPlugin;
+use wcf\system\package\plugin\SQLPackageInstallationPlugin;
use wcf\system\request\LinkHandler;
use wcf\system\request\RouteHandler;
-use wcf\system\form;
+use wcf\system\setup\Installer;
+use wcf\system\style\StyleHandler;
+use wcf\system\version\VersionHandler;
use wcf\system\WCF;
use wcf\util\FileUtil;
use wcf\util\HeaderUtil;
/**
* PackageInstallationDispatcher handles the whole installation process.
- *
+ *
* @author Alexander Ebert
- * @copyright 2001-2011 WoltLab GmbH
+ * @copyright 2001-2013 WoltLab GmbH
* @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
* @package com.woltlab.wcf
* @subpackage system.package
- * @category Community Framework
+ * @category Community Framework
*/
class PackageInstallationDispatcher {
/**
/**
* default name of the config file
- * @var string
+ * @var string
*/
const CONFIG_FILE = 'config.inc.php';
+ /**
+ * holds state of structuring version tables
+ * @var boolean
+ */
+ protected $requireRestructureVersionTables = false;
+
/**
* Creates a new instance of PackageInstallationDispatcher.
*
- * @param PackageInstallationQueue $queue
+ * @param wcf\data\package\installation\queue\PackageInstallationQueue $queue
*/
public function __construct(PackageInstallationQueue $queue) {
$this->queue = $queue;
/**
* Installs node components and returns next node.
- *
+ *
* @param string $node
- * @return PackageInstallationStep
+ * @return wcf\system\package\PackageInstallationStep
*/
public function install($node) {
$nodes = $this->nodeBuilder->getNodeData($node);
$node = $this->nodeBuilder->getNextNode($node);
$step->setNode($node);
- // update options.inc.php and save localized package infos
+ // perform post-install/update actions
if ($node == '') {
+ // update options.inc.php
OptionEditor::resetCache();
if ($this->action == 'install') {
+ // save localized package infos
$this->saveLocalizedPackageInfos();
+
+ // remove all cache files after WCFSetup
+ if (!PACKAGE_ID) {
+ CacheHandler::getInstance()->flushAll();
+ }
+
+ // rebuild application paths
+ ApplicationHandler::rebuild();
+ ApplicationEditor::setup();
}
- }
+
+ // remove template listener cache
+ TemplateListenerCacheBuilder::getInstance()->reset();
+ TemplateListenerCodeCacheBuilder::getInstance()->reset();
+
+ // reset language cache
+ LanguageFactory::getInstance()->clearCache();
+ LanguageFactory::getInstance()->deleteLanguageCache();
+
+ // reset stylesheets
+ StyleHandler::resetStylesheets();
+ }
+
+ if ($this->requireRestructureVersionTables) {
+ $this->restructureVersionTables();
+ }
return $step;
}
/**
* Returns current package archive.
- *
- * @return PackageArchive
+ *
+ * @return wcf\system\package\PackageArchive
*/
public function getArchive() {
if ($this->archive === null) {
/**
* Installs current package.
- *
+ *
* @param array $nodeData
*/
protected function installPackage(array $nodeData) {
if (count($this->getArchive()->getExcludedPackages()) > 0) {
$sql = "INSERT INTO wcf".WCF_N."_package_exclusion
(packageID, excludedPackage, excludedPackageVersion)
- VALUES (?, ?, ?)";
+ VALUES (?, ?, ?)";
$statement = WCF::getDB()->prepareStatement($sql);
foreach ($this->getArchive()->getExcludedPackages() as $excludedPackage) {
// if package is plugin to com.woltlab.wcf it must not have any other requirement
$requirements = $this->getArchive()->getRequirements();
- if ($package->parentPackageID == 1 && count($requirements)) {
- foreach ($requirements as $package => $data) {
- if ($package == 'com.woltlab.wcf') continue;
- throw new SystemException('Package '.$package->package.' is plugin of com.woltlab.wcf (WCF) but has more than one requirement.');
- }
- }
// insert requirements and dependencies
$requirements = $this->getArchive()->getAllExistingRequirements();
- if (count($requirements) > 0) {
+ if (!empty($requirements)) {
$sql = "INSERT INTO wcf".WCF_N."_package_requirement
(packageID, requirement)
VALUES (?, ?)";
$statement = WCF::getDB()->prepareStatement($sql);
foreach ($requirements as $identifier => $possibleRequirements) {
- if (count($possibleRequirements) == 1) $requirement = array_shift($possibleRequirements);
+ if (count($possibleRequirements) == 1) {
+ $requirement = array_shift($possibleRequirements);
+ }
else {
$requirement = $possibleRequirements[$this->selectedRequirements[$identifier]];
}
}
}
- // build requirement map
- Package::rebuildPackageRequirementMap($package->packageID);
-
- // rebuild dependencies
- Package::rebuildPackageDependencies($package->packageID);
- if ($this->action == 'update') {
- Package::rebuildParentPackageDependencies($package->packageID);
- }
-
// reload queue
$this->queue = new PackageInstallationQueue($this->queue->queueID);
$this->package = null;
if ($package->isApplication) {
- $host = RouteHandler::getHost();
+ $host = StringUtil::replace(RouteHandler::getProtocol(), '', RouteHandler::getHost());
$path = RouteHandler::getPath(array('acp'));
// insert as application
ApplicationEditor::create(array(
'domainName' => $host,
'domainPath' => $path,
+ 'cookieDomain' => $host,
+ 'cookiePath' => $path,
'packageID' => $package->packageID
));
}
-
- // insert dependencies on parent package if applicable
- $this->installPackageParent();
}
if ($this->getPackage()->isApplication && $this->getPackage()->package != 'com.woltlab.wcf' && $this->getAction() == 'install') {
if (empty($this->getPackage()->packageDir)) {
$document = $this->promptPackageDir();
- if ($document !== null && $document instanceof form\FormDocument) {
+ if ($document !== null && $document instanceof FormDocument) {
$installationStep->setDocument($document);
}
$installationStep->setSplitNode();
}
}
- else if ($this->getPackage()->parentPackageID) {
- $packageEditor = new PackageEditor($this->getPackage());
- $packageEditor->update(array(
- 'packageDir' => $this->getPackage()->getParentPackage()->packageDir
- ));
- }
return $installationStep;
}
// get language list
$languageList = new LanguageList();
- $languageList->sqlLimit = 0;
$languageList->readObjects();
// workaround for WCFSetup
if (isset($infoValues[$language->languageCode])) {
$value = $infoValues[$language->languageCode];
}
-
+
$statement->execute(array(
$language->languageID,
'wcf.acp.package.'.$infoName.'.package'.$package->packageID,
}
}
- /**
- * Sets parent package and rebuilds dependencies for both.
- */
- protected function installPackageParent() {
- // do not handle parent package if current package is an application or does not have a plugin tag while within installation process
- if ($this->getArchive()->getPackageInfo('isApplication') || $this->getAction() != 'install' || !$this->getArchive()->getPackageInfo('plugin')) {
- return;
- }
-
- // get parent package from requirements
- $sql = "SELECT requirement
- FROM wcf".WCF_N."_package_requirement
- WHERE packageID = ?
- AND requirement IN (
- SELECT packageID
- FROM wcf".WCF_N."_package
- WHERE package = ?
- )";
- $statement = WCF::getDB()->prepareStatement($sql);
- $statement->execute(array(
- $this->getPackage()->packageID,
- $this->getArchive()->getPackageInfo('plugin')
- ));
- $row = $statement->fetchArray();
- if (!$row || empty($row['requirement'])) {
- throw new SystemException("can not find any available installations of required parent package '".$this->getArchive()->getPackageInfo('plugin')."'");
- }
-
- // save parent package
- $packageEditor = new PackageEditor($this->getPackage());
- $packageEditor->update(array(
- 'parentPackageID' => $row['requirement']
- ));
-
- // rebuild parent package dependencies
- Package::rebuildParentPackageDependencies($this->getPackage()->packageID);
-
- // rebuild parent's parent package dependencies
- Package::rebuildParentPackageDependencies($row['requirement']);
-
- // reload package object on next request
- $this->package = null;
- }
-
/**
* Executes a package installation plugin.
- *
+ *
* @param array step
* @return boolean
*/
$plugin = new $className($this, $nodeData);
- if (!($plugin instanceof \wcf\system\package\plugin\IPackageInstallationPlugin)) {
- throw new SystemException("class '".$className."' does not implement the interface 'wcf\system\package\plugin\IPackageInstallationPlugin'");
+ if (!($plugin instanceof IPackageInstallationPlugin)) {
+ throw new SystemException("'".$className."' does not implement 'wcf\system\package\plugin\IPackageInstallationPlugin'");
+ }
+
+ if ($plugin instanceof SQLPackageInstallationPlugin || $plugin instanceof ObjectTypePackageInstallationPlugin) {
+ $this->requireRestructureVersionTables = true;
}
// execute PIP
return $step;
}
+ /**
+ * Displays a list to select optional packages or installs selection.
+ *
+ * @param string $currentNode
+ * @param array $nodeData
+ * @return wcf\system\package\PackageInstallationStep
+ */
protected function selectOptionalPackages($currentNode, array $nodeData) {
$installationStep = new PackageInstallationStep();
$document = $this->promptOptionalPackages($nodeData);
- if ($document !== null && $document instanceof form\FormDocument) {
+ if ($document !== null && $document instanceof FormDocument) {
$installationStep->setDocument($document);
$installationStep->setSplitNode();
}
}
/**
- * Extracts files from .tar (or .tar.gz) archive and installs them
- *
- * @param string $targetDir
- * @param string $sourceArchive
+ * Extracts files from .tar(.gz) archive and installs them
+ *
+ * @param string $targetDir
+ * @param string $sourceArchive
* @param FileHandler $fileHandler
- * @return Installer
+ * @return wcf\system\setup\Installer
*/
public function extractFiles($targetDir, $sourceArchive, $fileHandler = null) {
- return new \wcf\system\setup\Installer($targetDir, $sourceArchive, $fileHandler);
+ return new Installer($targetDir, $sourceArchive, $fileHandler);
}
/**
* Returns current package.
- *
- * @return Package
+ *
+ * @return wcf\data\package\Package
*/
public function getPackage() {
if ($this->package === null) {
/**
* Prompts for a text input for package directory (applies for applications only)
- *
- * @return FormDocument
+ *
+ * @return wcf\system\form\FormDocument
*/
protected function promptPackageDir() {
if (!PackageInstallationFormManager::findForm($this->queue, 'packageDir')) {
- $container = new container\GroupFormElementContainer();
- $packageDir = new element\TextInputFormElement($container);
+ $container = new GroupFormElementContainer();
+ $packageDir = new TextInputFormElement($container);
$packageDir->setName('packageDir');
$packageDir->setLabel(WCF::getLanguage()->get('wcf.acp.package.packageDir.input'));
$packageDir->setValue($defaultPath);
$container->appendChild($packageDir);
- $document = new form\FormDocument('packageDir');
+ $document = new FormDocument('packageDir');
$document->appendContainer($container);
PackageInstallationFormManager::registerForm($this->queue, $document);
// parse domain path
$domainPath = FileUtil::getRelativePath(FileUtil::unifyDirSeperator($_SERVER['DOCUMENT_ROOT']), FileUtil::unifyDirSeperator($packageDir));
+
+ // work-around for applications installed in document root
+ if ($domainPath == './') {
+ $domainPath = '';
+ }
+
$domainPath = FileUtil::addLeadingSlash(FileUtil::addTrailingSlash($domainPath));
// update application path
$application = new Application($this->getPackage()->packageID);
$applicationEditor = new ApplicationEditor($application);
$applicationEditor->update(array(
- 'domainPath' => $domainPath
+ 'domainPath' => $domainPath,
+ 'cookiePath' => $domainPath
));
// create directory and set permissions
}
}
+ /**
+ * Prompts a selection of optional packages.
+ *
+ * @return mixed
+ */
protected function promptOptionalPackages(array $packages) {
if (!PackageInstallationFormManager::findForm($this->queue, 'optionalPackages')) {
- $container = new container\MultipleSelectionFormElementContainer();
+ $container = new MultipleSelectionFormElementContainer();
$container->setName('optionalPackages');
foreach ($packages as $package) {
- $optionalPackage = new element\MultipleSelectionFormElement($container);
+ $optionalPackage = new MultipleSelectionFormElement($container);
$optionalPackage->setName('optionalPackages');
$optionalPackage->setLabel($package['packageName']);
$optionalPackage->setValue($package['package']);
$container->appendChild($optionalPackage);
}
- $document = new form\FormDocument('optionalPackages');
+ $document = new FormDocument('optionalPackages');
$document->appendContainer($container);
PackageInstallationFormManager::registerForm($this->queue, $document);
/**
* Returns current package id.
- *
+ *
* @return integer
*/
public function getPackageID() {
/**
* Returns current package installation type.
- *
+ *
* @return string
*/
public function getAction() {
/**
* Opens the package installation queue and
* starts the installation, update or uninstallation of the first entry.
- *
+ *
* @param integer $parentQueueID
- * @param integer $processNo
+ * @param integer $processNo
*/
public static function openQueue($parentQueueID = 0, $processNo = 0) {
$conditions = new PreparedStatementConditionBuilder();
exit;
}
else {
- $url = LinkHandler::getInstance()->getLink('Package', array(), 'action='.$packageInstallation['action'].'&queueID='.$packageInstallation['queueID']);
+ $url = LinkHandler::getInstance()->getLink('PackageInstallationConfirm', array(), 'action='.$packageInstallation['action'].'&queueID='.$packageInstallation['queueID']);
HeaderUtil::redirect($url);
exit;
}
}
- /**
- * Displays last confirmation before plugin installation.
- */
- public function beginInstallation() {
- // get requirements
- $requirements = $this->getArchive()->getRequirements();
- $openRequirements = $this->getArchive()->getOpenRequirements();
-
- $updatableInstances = array();
- $missingPackages = 0;
- foreach ($requirements as $key => $requirement) {
- if (isset($openRequirements[$requirement['name']])) {
- $requirements[$key]['status'] = 'missing';
- $requirements[$key]['action'] = $openRequirements[$requirement['name']]['action'];
-
- if (!isset($requirements[$key]['file'])) {
- if ($openRequirements[$requirement['name']]['action'] === 'update') {
- $requirements[$key]['status'] = 'missingVersion';
- $requirements[$key]['existingVersion'] = $openRequirements[$requirement['name']]['existingVersion'];
- }
- $missingPackages++;
- }
- else {
- $requirements[$key]['status'] = 'delivered';
- }
- }
- else {
- $requirements[$key]['status'] = 'installed';
- }
- }
-
- // get other instances
- if ($this->action == 'install') {
- $updatableInstances = $this->getArchive()->getUpdatableInstances();
- }
-
- ACPMenu::getInstance()->setActiveMenuItem('wcf.acp.menu.link.package.install');
- WCF::getTPL()->assign(array(
- 'archive' => $this->getArchive(),
- 'requiredPackages' => $requirements,
- 'missingPackages' => $missingPackages,
- 'updatableInstances' => $updatableInstances,
- 'excludingPackages' => $this->getArchive()->getConflictedExcludingPackages(),
- 'excludedPackages' => $this->getArchive()->getConflictedExcludedPackages(),
- 'queueID' => $this->queue->queueID
- ));
- WCF::getTPL()->display('packageInstallationConfirm');
- exit;
- }
-
/**
* Checks the package installation queue for outstanding entries.
- *
+ *
* @return integer
*/
public static function checkPackageInstallationQueue() {
$sql = "SELECT queueID
FROM wcf".WCF_N."_package_installation_queue
- WHERE userID = ?
+ WHERE userID = ?
AND parentQueueID = 0
AND done = 0
ORDER BY queueID ASC";
* Executes post-setup actions.
*/
public function completeSetup() {
- // rebuild dependencies
- Package::rebuildPackageDependencies($this->queue->packageID);
-
// mark queue as done
$queueEditor = new PackageInstallationQueueEditor($this->queue);
$queueEditor->update(array(
LanguageEditor::deleteLanguageFiles();
// reset all caches
- CacheHandler::getInstance()->clear(WCF_DIR.'cache/', '*');
+ CacheHandler::getInstance()->flushAll();
}
/**
* @param string $function
* @return boolean
* @see http://de.php.net/manual/en/function.function-exists.php#77980
- */
+ */
protected static function functionExists($function) {
if (extension_loaded('suhosin')) {
$blacklist = @ini_get('suhosin.executor.func.blacklist');
break;
}
}
+
+ /*
+ * Restructure version tables.
+ */
+ protected function restructureVersionTables() {
+ $objectTypes = VersionHandler::getInstance()->getObjectTypes();
+
+ if (empty($objectTypes)) {
+ return;
+ }
+
+ // base structure of version tables
+ $versionTableBaseColumns = array();
+ $versionTableBaseColumns[] = array('name' => 'versionID', 'data' => array('type' => 'INT', 'key' => 'PRIMARY', 'autoIncrement' => 'AUTO_INCREMENT'));
+ $versionTableBaseColumns[] = array('name' => 'versionUserID', 'data' => array('type' => 'INT'));
+ $versionTableBaseColumns[] = array('name' => 'versionUsername', 'data' => array('type' => 'VARCHAR', 'length' => 255));
+ $versionTableBaseColumns[] = array('name' => 'versionTime', 'data' => array('type' => 'INT'));
+
+ foreach ($objectTypes as $objectTypeID => $objectType) {
+ // get structure of base table
+ $baseTableColumns = WCF::getDB()->getEditor()->getColumns($objectType::getDatabaseTableName());
+ // get structure of version table
+ $versionTableColumns = WCF::getDB()->getEditor()->getColumns($objectType::getDatabaseVersionTableName());
+
+ if (empty($versionTableColumns)) {
+ $columns = array_merge($versionTableBaseColumns, $baseTableColumns);
+
+ WCF::getDB()->getEditor()->createTable($objectType::getDatabaseVersionTableName(), $columns);
+ }
+ else {
+ // check garbage columns in versioned table
+ foreach ($versionTableColumns as $columnData) {
+ if (!array_search($columnData['name'], $baseTableColumns, true)) {
+ // delete column
+ WCF::getDB()->getEditor()->dropColumn($objectType::getDatabaseVersionTableName(), $columnData['name']);
+ }
+ }
+
+ // check new columns for versioned table
+ foreach ($baseTableColumns as $columnData) {
+ if (!array_search($columnData['name'], $versionTableColumns, true)) {
+ // add colum
+ WCF::getDB()->getEditor()->addColumn($objectType::getDatabaseVersionTableName(), $columnData['name'], $columnData['data']);
+ }
+ }
+ }
+ }
+ }
}