Updated package installation is now fully working
authorAlexander Ebert <ebert@woltlab.com>
Mon, 14 Apr 2014 19:39:54 +0000 (21:39 +0200)
committerAlexander Ebert <ebert@woltlab.com>
Mon, 14 Apr 2014 19:39:54 +0000 (21:39 +0200)
There are still a bunch of missing language variables and initial errors occurring during upload aren't properly handled yet. Furthermore it does not check local packages (or those downloaded via HTTP).

Additionally checks for package exclusions have been disabled for testing purposes.

wcfsetup/install/files/acp/templates/packageInstallationConfirm.tpl
wcfsetup/install/files/lib/acp/form/PackageStartInstallForm.class.php
wcfsetup/install/files/lib/acp/page/PackageInstallationConfirmPage.class.php
wcfsetup/install/files/lib/system/package/plugin/LanguagePackageInstallationPlugin.class.php
wcfsetup/install/files/lib/system/package/validation/PackageValidationArchive.class.php
wcfsetup/install/files/lib/system/package/validation/PackageValidationManager.class.php
wcfsetup/install/lang/de.xml

index 622bcafd756c90b4a467f1cdfdc834616a3fa5a7..14de2d5c5feda871856b9716f6effdd182b4f20d 100644 (file)
        <p>{$archive->getLocalizedPackageInfo('packageDescription')}</p>
 </header>
 
-{if $missingPackages > 0}
-       <p class="error">{lang}wcf.acp.package.install.error.missingRequirements{/lang}</p>
-{/if}
-
-{if $excludingPackages|count > 0}
-       <div class="error">{lang}wcf.acp.package.install.error.excludingPackages{/lang}
-               <ul>
-                       {foreach from=$excludingPackages item=excludingPackage}
-                               <li>{lang}wcf.acp.package.install.error.excludingPackages.excludingPackage{/lang}</li>
-                       {/foreach}
-               </ul>
-       </div>
-{/if}
-
-{if $excludedPackages|count > 0}
-       <div class="error">{lang}wcf.acp.package.install.error.excludedPackages{/lang}
-               <ul>
-                       {foreach from=$excludedPackages item=excludedPackage}
-                               <li>{lang}wcf.acp.package.install.error.excludedPackages.excludedPackage{/lang}</li>
-                       {/foreach}
-               </ul>
-       </div>
+{if !$validationPassed}
+       <p class="error">{lang}wcf.acp.package.validation.failed{/lang}</p>
 {/if}
 
 {if $installingImportedStyle}
        </fieldset>
 </div>
 
-{if $requiredPackages|count > 0}
+{if !$validationPassed}
        <div class="tabularBox tabularBoxTitle marginTop">
                <header>
-                       <h2>{lang}wcf.acp.package.dependencies.required{/lang} <span class="badge badgeInverse">{#$requiredPackages|count}</span></h2>
+                       <h2>{lang}wcf.acp.package.validation{/lang}</h2>
                </header>
                
                <table class="table">
                                <tr>
                                        <th class="columnTitle columnPackageName">{lang}wcf.acp.package.name{/lang}</th>
                                        <th class="columnText columnPackage">{lang}wcf.acp.package.identifier{/lang}</th>
-                                       <th class="columnText columnPackageVersion">{lang}wcf.acp.package.installation.requiredVersion{/lang}</th>
                                        <th class="columnText">{lang}wcf.acp.package.installation.packageStatus{/lang}</th>
-                                       
-                                       {event name='columnHeads'}
                                </tr>
                        </thead>
-                       
                        <tbody>
-                               {foreach from=$requiredPackages item=$package}
+                               {foreach from=$packageValidationArchives item=packageValidationArchive}
+                                       {assign var=exceptionMessage value=$packageValidationArchive->getExceptionMessage()}
                                        <tr>
-                                               <td class="columnTitle columnPackageName">{if $package[package]}{$package[package]->packageName|language}{/if}</td>
-                                               <td class="columnText columnPackage">{@$package.name}</td>
-                                               <td class="columnText columnPackageVersion">{if $package.minversion|isset}<span class="badge label {if $package.status == 'installed'}green{elseif $package.status == 'delivered'}yellow{else}red{/if}">{$package.minversion}</span>{/if}</td>
-                                               <td class="columnText">{lang}wcf.acp.package.installation.packageStatus.{@$package.status}{/lang}</td>
-                                               
-                                               {event name='columns'}
+                                               <td class="columnTitle columnPackageName"><span{if $packageValidationArchive->getDepth()} style="padding-left: {@$packageValidationArchive->getDepth() * 14}px"{/if}>{$packageValidationArchive->getArchive()->getLocalizedPackageInfo('packageName')}</span></td>
+                                               <td class="columnText columnPackage">{$packageValidationArchive->getArchive()->getPackageInfo('name')}</td>
+                                               <td class="columnIcon columnStatus"><span class="icon icon16 {if $exceptionMessage}fa-times-circle red{else}fa-check-circle green{/if}"></span></td>
                                        </tr>
+                                       
+                                       {if $exceptionMessage}
+                                               <tr>
+                                                       <td colspan="3"><span{if $packageValidationArchive->getDepth()} style="padding-left: {@$packageValidationArchive->getDepth() * 14}px"{/if}>{@$exceptionMessage}</span></td>
+                                               </tr>
+                                       {/if}
                                {/foreach}
                        </tbody>
                </table>
 
 <div class="formSubmit">
        <input type="button" id="backButton" value="{lang}wcf.global.button.back{/lang}" accesskey="c" />
-       {if $missingPackages == 0 && $excludingPackages|count == 0 && $excludedPackages|count == 0}
+       {if $validationPassed}
                <input type="button" class="buttonPrimary" id="submitButton" value="{lang}wcf.global.button.next{/lang}" class="default" accesskey="s" />
        {/if}
 </div>
index 5e711b75253f05562efbe27693f4bc9f3e49163a..28f8cadafedb46a8c9dbb6efea461fe1106d9768 100755 (executable)
@@ -139,7 +139,7 @@ class PackageStartInstallForm extends AbstractForm {
                
                if (!PackageValidationManager::getInstance()->validate($this->uploadPackage['name'], false)) {
                        // TODO: do something
-                       die("validation failed");
+                       die("validation failed: " . PackageValidationManager::getInstance()->getExceptionMessage());
                }
                
                $this->package = PackageValidationManager::getInstance()->getPackageValidationArchive()->getPackage();
index 0fd64fa803fc7854338e1de894061a72d9eda73a..b86cbe5514851110bdd069f49db86c0dfc428edd 100644 (file)
@@ -1,15 +1,12 @@
 <?php
 namespace wcf\acp\page;
 use wcf\data\package\installation\queue\PackageInstallationQueue;
-use wcf\data\package\Package;
-use wcf\data\package\PackageCache;
 use wcf\page\AbstractPage;
 use wcf\system\exception\IllegalLinkException;
-use wcf\system\package\PackageArchive;
+use wcf\system\package\validation\PackageValidationManager;
 use wcf\system\package\PackageInstallationDispatcher;
 use wcf\system\WCF;
 use wcf\system\WCFACP;
-use wcf\system\package\validation\PackageValidationManager;
 
 /**
  * Shows a confirmation page prior to start installing.
@@ -27,18 +24,6 @@ class PackageInstallationConfirmPage extends AbstractPage {
         */
        public $activeMenuItem = 'wcf.acp.menu.link.package.install';
        
-       /**
-        * number of missing packages
-        * @var integer
-        */
-       public $missingPackages = 0;
-       
-       /**
-        * list of unsatisfied requirements
-        * @var array<array>
-        */
-       public $openRequirements = array();
-       
        /**
         * package installation dispatcher object
         * @var \wcf\system\package\PackageInstallationDispatcher
@@ -58,10 +43,10 @@ class PackageInstallationConfirmPage extends AbstractPage {
        public $queueID = 0;
        
        /**
-        * list of requirements
-        * @var array<array>
+        * package validation result
+        * @var boolean
         */
-       public $requirements = array();
+       public $validationPassed = false;
        
        /**
         * true if the package to be installed was uploaded via the import style
@@ -104,63 +89,7 @@ class PackageInstallationConfirmPage extends AbstractPage {
                $this->packageInstallationDispatcher = new PackageInstallationDispatcher($this->queue);
                
                // validate the package and all it's requirements
-               if (PackageValidationManager::getInstance()->validate($this->queue->archive, true)) {
-                       die("success");
-               }
-               else {
-                       /*echo "<pre>";
-                       foreach (PackageValidationManager::getInstance()->getPackageValidationArchiveList() as $archive) {
-                               echo '[' . $archive->getArchive()->getPackageInfo('name') . '] ' . $archive->getExceptionMessage() . "\n";
-                       }
-                       die("failed");*/
-                       return;
-               }
-               
-               // get requirements
-               $this->requirements = $this->packageInstallationDispatcher->getArchive()->getRequirements();
-               $this->openRequirements = $this->packageInstallationDispatcher->getArchive()->getOpenRequirements();
-               
-               foreach ($this->requirements as &$requirement) {
-                       if (isset($this->openRequirements[$requirement['name']])) {
-                               $requirement['status'] = 'missing';
-                               $requirement['action'] = $this->openRequirements[$requirement['name']]['action'];
-                               
-                               if (!isset($requirement['file'])) {
-                                       if ($requirement['action'] === 'update') {
-                                               $requirement['status'] = 'missingVersion';
-                                               $requirement['existingVersion'] = $this->openRequirements[$requirement['name']]['existingVersion'];
-                                       }
-                                       $this->missingPackages++;
-                               }
-                               else {
-                                       $requirement['status'] = 'delivered';
-                                       $packageArchive = new PackageArchive($this->packageInstallationDispatcher->getArchive()->extractTar($requirement['file']));
-                                       $packageArchive->openArchive();
-                                       
-                                       // make sure that the delivered package is correct
-                                       if ($requirement['name'] != $packageArchive->getPackageInfo('name')) {
-                                               $requirement['status'] = 'invalidDeliveredPackage';
-                                               $requirement['deliveredPackage'] = $packageArchive->getPackageInfo('name');
-                                               $this->missingPackages++;
-                                       }
-                                       else if (isset($requirement['minversion'])) {
-                                               // make sure that the delivered version is sufficient
-                                               if (Package::compareVersion($requirement['minversion'], $packageArchive->getPackageInfo('version')) > 0) {
-                                                       $requirement['deliveredVersion'] = $packageArchive->getPackageInfo('version');
-                                                       $requirement['status'] = 'missingVersion';
-                                                       $this->missingPackages++;
-                                               }
-                                       }
-                               }
-                       }
-                       else {
-                               $requirement['status'] = 'installed';
-                       }
-                       
-                       $requirement['package'] = PackageCache::getInstance()->getPackageByIdentifier($requirement['name']);
-               }
-               
-               unset($requirement);
+               $this->validationPassed = PackageValidationManager::getInstance()->validate($this->queue->archive, true);
        }
        
        /**
@@ -171,12 +100,9 @@ class PackageInstallationConfirmPage extends AbstractPage {
                
                WCF::getTPL()->assign(array(
                        'archive' => $this->packageInstallationDispatcher->getArchive(),
-                       /*'requiredPackages' => $this->requirements,
-                       'missingPackages' => $this->missingPackages,
-                       'excludingPackages' => $this->packageInstallationDispatcher->getArchive()->getConflictedExcludingPackages(),
-                       'excludedPackages' => $this->packageInstallationDispatcher->getArchive()->getConflictedExcludedPackages(),*/
                        'packageValidationArchives' => PackageValidationManager::getInstance()->getPackageValidationArchiveList(),
                        'queue' => $this->queue,
+                       'validationPassed' => $this->validationPassed,
                        'installingImportedStyle' => $this->installingImportedStyle
                ));
        }
index ca6164ddab8cd3f5034959c55c0e759ec3c432da..43b0314191e481888db8eded866940684a6f659d 100644 (file)
@@ -4,6 +4,7 @@ use wcf\data\language\LanguageEditor;
 use wcf\system\database\util\PreparedStatementConditionBuilder;
 use wcf\system\exception\SystemException;
 use wcf\system\language\LanguageFactory;
+use wcf\system\package\PackageArchive;
 use wcf\system\WCF;
 use wcf\util\XML;
 
@@ -251,4 +252,11 @@ class LanguagePackageInstallationPlugin extends AbstractXMLPackageInstallationPl
         * @see \wcf\system\package\plugin\AbstractXMLPackageInstallationPlugin::findExistingItem()
         */
        protected function findExistingItem(array $data) { }
+       
+       /**
+        * @see \wcf\system\package\plugin\IPackageInstallationPlugin::isValid()
+        */
+       public static function isValid(PackageArchive $archive, $instruction) {
+               return true;
+       }
 }
index 40587c3d10a8730bf92dc0486e8b0260b1e75378..c8a69b2247737eace6848d02ff01bc63ce3c7471 100644 (file)
@@ -1,8 +1,8 @@
 <?php
 namespace wcf\system\package\validation;
+use wcf\data\package\Package;
 use wcf\data\package\PackageCache;
 use wcf\system\package\PackageArchive;
-use wcf\data\package\Package;
 
 /**
  * Recursively validates the package archive and it's delivered requirements.
@@ -97,7 +97,9 @@ class PackageValidationArchive implements \RecursiveIterator {
                                PackageValidationManager::getInstance()->addVirtualPackage($this->archive->getPackageInfo('name'), $this->archive->getPackageInfo('version'));
                                
                                // check for exclusions
-                               if (true || WCF_VERSION != '2.1.0 Alpha 1 (Typhoon)') {
+                               // TODO: exclusions are not checked for testing purposes
+                               //       REMOVE THIS BEFORE *ANY* PUBLIC RELEASE
+                               if (WCF_VERSION != '2.1.0 Alpha 1 (Typhoon)') {
                                        $this->validateExclusion();
                                }
                                
@@ -138,6 +140,15 @@ class PackageValidationArchive implements \RecursiveIterator {
                
        }
        
+       /**
+        * Validates if the package has suitable install or update instructions. Setting $deepInspection
+        * to true will cause every single instruction to be validated against the corresponding PIP.
+        * 
+        * Please be aware that unknown PIPs will be silently ignored and will not cause any error!
+        * 
+        * @param       string          $requiredVersion
+        * @param       boolean         $deepInspection
+        */
        protected function validateInstructions($requiredVersion, $deepInspection) {
                $package = $this->getPackage();
                
@@ -157,7 +168,9 @@ class PackageValidationArchive implements \RecursiveIterator {
                                throw new PackageValidationException(PackageValidationException::NO_INSTALL_PATH, array('packageName' => $this->archive->getPackageInfo('name')));
                        }
                        
-                       $this->validatePackageInstallationPlugins('install', $instructions);
+                       if ($deepInspection) {
+                               $this->validatePackageInstallationPlugins('install', $instructions);
+                       }
                }
                else {
                        // package is already installed, check update path
@@ -169,11 +182,18 @@ class PackageValidationArchive implements \RecursiveIterator {
                                ));
                        }
                        
-                       $this->validatePackageInstallationPlugins('update', $this->archive->getUpdateInstructions());
+                       if ($deepInspection) {
+                               $this->validatePackageInstallationPlugins('update', $this->archive->getUpdateInstructions());
+                       }
                }
-               exit;
        }
        
+       /**
+        * Validates install or update instructions against the corresponding PIP, unknown PIPs will be silently ignored.
+        * 
+        * @param       string          $type
+        * @param       array<array>    $instructions
+        */
        protected function validatePackageInstallationPlugins($type, array $instructions) {
                for ($i = 0, $length = count($instructions); $i < $length; $i++) {
                        $instruction = $instructions[$i];
@@ -187,6 +207,9 @@ class PackageValidationArchive implements \RecursiveIterator {
                }
        }
        
+       /**
+        * Validates if an installed package excludes the current package and vice versa.
+        */
        protected function validateExclusion() {
                $excludingPackages = $this->archive->getConflictedExcludingPackages();
                if (!empty($excludingPackages)) {
@@ -216,10 +239,21 @@ class PackageValidationArchive implements \RecursiveIterator {
                return $this->exception->getMessage();
        }
        
+       /**
+        * Returns the package archive object.
+        * 
+        * @return      \wcf\system\package\PackageArchive
+        */
        public function getArchive() {
                return $this->archive;
        }
        
+       /**
+        * Returns the package object based on the package archive's package identifier or null
+        * if the package isn't already installed.
+        * 
+        * @return      \wcf\data\package\Package
+        */
        public function getPackage() {
                if ($this->package === null) {
                        $this->package = PackageCache::getInstance()->getPackageByIdentifier($this->archive->getPackageInfo('name'));
index c599971b119c0aded52b2122aaa69adfef654e5c..1e68507e9a1f06d385078a10069fcfe4067f85d7 100644 (file)
@@ -1,9 +1,9 @@
 <?php
 namespace wcf\system\package\validation;
-use wcf\data\package\Package;
-use wcf\system\SingletonFactory;
 use wcf\data\package\installation\plugin\PackageInstallationPluginList;
+use wcf\data\package\Package;
 use wcf\system\package\PackageArchive;
+use wcf\system\SingletonFactory;
 
 /**
  * Manages recursive validation of package archives.
@@ -117,11 +117,36 @@ class PackageValidationManager extends SingletonFactory {
                return new \RecursiveIteratorIterator($packageValidationArchive, \RecursiveIteratorIterator::SELF_FIRST);
        }
        
+       /**
+        * Recursively traverses the package validation archives and returns the first exception message.
+        * 
+        * @return      string
+        */
+       public function getExceptionMessage() {
+               foreach ($this->getPackageValidationArchiveList() as $packageArchive) {
+                       if ($packageArchive->getExceptionMessage()) {
+                               return $packageArchive->getExceptionMessage();
+                       }
+               }
+               
+               return '';
+       }
+       
+       /**
+        * Validates an instruction against the corresponding package installation plugin.
+        * 
+        * Please be aware that unknown PIPs will silently ignored and cause no error.
+        *  
+        * @param       \wcf\data\package\PackageArchive        $archive
+        * @param       string                                  $pip
+        * @param       string                                  $instruction
+        * @return      boolean
+        */
        public function validatePackageInstallationPluginInstruction(PackageArchive $archive, $pip, $instruction) {
                if (isset($this->packageInstallationPlugins[$pip])) {
                        return call_user_func(array($this->packageInstallationPlugins[$pip], 'isValid'), $archive, $instruction);
                }
-               echo "(default success)\n";
+               
                return true;
        }
 }
index faff0f42615d0e27448b0a0fef467ea985255a90..bc825abfec179b5becae527f358589408e83a4fe 100644 (file)
@@ -992,6 +992,9 @@ GmbH=Gesellschaft mit beschränkter Haftung]]></item>
                <item name="wcf.acp.package.error.downloadFailed"><![CDATA[Das Herunterladen des Paketes{if $__downloadPackage|isset} „{$__downloadPackage}“{/if} ist fehlgeschlagen.]]></item>
                <item name="wcf.acp.package.newVersion"><![CDATA[Neue Version]]></item>
                
+               <item name="wcf.acp.package.validation"><![CDATA[Prüfungsergebnis]]></item>
+               <item name="wcf.acp.package.validation.failed"><![CDATA[Das hochgeladene Paket kann nicht installiert werden, bitte beachten Sie das unten stehende Prüfungsergebnis.]]></item>
+               
                <!-- TODO: most error codes are still missing, they will be added during testing -->
                <item name="wcf.acp.package.validation.errorCode.8"><![CDATA[Dieses Paket ist inkompatibel mit den folgenden, installierten Paketen: <ul class="nativeList">{foreach from=$packages item=package}<li>„{$package}“ ({$package->package})</li>{/foreach}</ul>]]></item>
                <item name="wcf.acp.package.validation.errorCode.10"><![CDATA[Benötigt das Paket „{$packageName}“ in Version „{$packageVersion}“ oder höher, dies ist aber weder installiert noch wird es mitgeliefert.]]></item>