/**
	 * Returns a result list of a search for installable packages.
	 * 
	 * @return	array
	 */
	public function search() {
		$conditions = new PreparedStatementConditionBuilder();
		if (!empty($this->parameters['package'])) {
			$conditions->add("package_update.package LIKE ?", array('%'.$this->parameters['package'].'%'));
		}
		if (!empty($this->parameters['packageDescription'])) {
			$conditions->add("package_update.packageDescription LIKE ?", array('%'.$this->parameters['packageDescription'].'%'));
		}
		if (!empty($this->parameters['packageName'])) {
			$conditions->add("package_update.packageName LIKE ?", array('%'.$this->parameters['packageName'].'%'));
		}
		
		// find matching packages
		$sql = "SELECT		package_update.packageUpdateID
			FROM		wcf".WCF_N."_package_update package_update
			".$conditions."
			ORDER BY	package_update.packageName ASC";
		$statement = WCF::getDB()->prepareStatement($sql, 1000);
		$statement->execute($conditions->getParameters());
		$packageUpdateIDs = array();
		while ($row = $statement->fetchArray()) {
			$packageUpdateIDs[] = $row['packageUpdateID'];
		}
		
		// no matches found
		if (empty($packageUpdateIDs)) {
			WCF::getTPL()->assign(array(
				'packageUpdates' => array()
			));
			
			return array(
				'count' => 0,
				'pageCount' => 0,
				'searchID' => 0,
				'template' => WCF::getTPL()->fetch('packageSearchResultList')
			);
		}
		
		// filter by version
		$conditions = new PreparedStatementConditionBuilder();
		$conditions->add("puv.packageUpdateID IN (?)", array($packageUpdateIDs));
		$sql = "SELECT		pu.package, puv.packageUpdateVersionID, puv.packageUpdateID, puv.packageVersion, puv.isAccessible
			FROM		wcf".WCF_N."_package_update_version puv
			LEFT JOIN	wcf".WCF_N."_package_update pu
			ON		(pu.packageUpdateID = puv.packageUpdateID)
			".$conditions;
		$statement = WCF::getDB()->prepareStatement($sql);
		$statement->execute($conditions->getParameters());
		$packageVersions = array();
		while ($row = $statement->fetchArray()) {
			$package = $row['package'];
			if (!isset($packageVersions[$package])) {
				$packageVersions[$package] = array();
			}
			
			$packageUpdateID = $row['packageUpdateID'];
			if (!isset($packageVersions[$package][$packageUpdateID])) {
				$packageVersions[$package][$packageUpdateID] = array(
					'accessible' => array(),
					'existing' => array()
				);
			}
			
			if ($row['isAccessible']) {
				$packageVersions[$package][$packageUpdateID]['accessible'][$row['packageUpdateVersionID']] = $row['packageVersion'];
			}
			$packageVersions[$package][$packageUpdateID]['existing'][$row['packageUpdateVersionID']] = $row['packageVersion'];
		}
		
		// determine highest versions
		$packageUpdates = array();
		foreach ($packageVersions as $package => $versionData) {
			$accessible = $existing = $versions = array();
			
			foreach ($versionData as $packageUpdateID => $versionTypes) {
				// ignore unaccessible packages
				if (empty($versionTypes['accessible'])) {
					continue;
				}
				
				uasort($versionTypes['accessible'], array('wcf\data\package\Package', 'compareVersion'));
				uasort($versionTypes['existing'], array('wcf\data\package\Package', 'compareVersion'));
				
				$accessibleVersion = array_slice($versionTypes['accessible'], -1, 1, true);
				$existingVersion = array_slice($versionTypes['existing'], -1, 1, true);
				
				$ak = key($accessibleVersion);
				$av = current($accessibleVersion);
				$ek = key($existingVersion);
				$ev = current($existingVersion);
				
				$accessible[$av] = $ak;
				$existing[$ev] = $ek;
				$versions[$ak] = $packageUpdateID;
				$versions[$ek] = $packageUpdateID;
			}
			
			uksort($accessible, array('wcf\data\package\Package', 'compareVersion'));
			uksort($existing, array('wcf\data\package\Package', 'compareVersion'));
			
			$accessible = array_pop($accessible);
			$existing = array_pop($existing);
			$packageUpdates[$versions[$accessible]] = array(
				'accessible' => $accessible,
				'existing' => $existing
			);
		}
		
		$search = SearchEditor::create(array(
			'userID' => WCF::getUser()->userID,
			'searchData' => serialize($packageUpdates),
			'searchTime' => TIME_NOW,
			'searchType' => 'acpPackageSearch'
		));
		
		// forward call to build the actual result list
		$updateAction = new PackageUpdateAction(array(), 'getResultList', array(
			'pageNo' => 1,
			'search' => $search
		));
		
		$returnValues = $updateAction->executeAction();
		return $returnValues['returnValues'];
	}
 /**
  * Returns a result list of a search for installable packages.
  * 
  * @return	array
  */
 public function search()
 {
     PackageUpdateDispatcher::getInstance()->refreshPackageDatabase();
     $availableUpdateServers = PackageUpdateServer::getActiveUpdateServers();
     // there are no available package update servers
     if (empty($availableUpdateServers)) {
         WCF::getTPL()->assign(array('packageUpdates' => array()));
         return array('count' => 0, 'pageCount' => 0, 'searchID' => 0, 'template' => WCF::getTPL()->fetch('packageSearchResultList'));
     }
     $conditions = new PreparedStatementConditionBuilder();
     $conditions->add("package_update.packageUpdateServerID IN (?)", array(array_keys($availableUpdateServers)));
     if (!empty($this->parameters['package'])) {
         $conditions->add("package_update.package LIKE ?", array('%' . $this->parameters['package'] . '%'));
     }
     if (!empty($this->parameters['packageDescription'])) {
         $conditions->add("package_update.packageDescription LIKE ?", array('%' . $this->parameters['packageDescription'] . '%'));
     }
     if (!empty($this->parameters['packageName'])) {
         $conditions->add("package_update.packageName LIKE ?", array('%' . $this->parameters['packageName'] . '%'));
     }
     $conditions->add("package.packageID IS NULL");
     // find matching packages
     $sql = "SELECT\t\tpackage_update.packageUpdateID\n\t\t\tFROM\t\twcf" . WCF_N . "_package_update package_update\n\t\t\tLEFT JOIN\twcf" . WCF_N . "_package package\n\t\t\tON\t\t(package.package = package_update.package)\n\t\t\t" . $conditions . "\n\t\t\tORDER BY\tpackage_update.packageName ASC";
     $statement = WCF::getDB()->prepareStatement($sql, 1000);
     $statement->execute($conditions->getParameters());
     $packageUpdateIDs = array();
     while ($row = $statement->fetchArray()) {
         $packageUpdateIDs[] = $row['packageUpdateID'];
     }
     // no matches found
     if (empty($packageUpdateIDs)) {
         WCF::getTPL()->assign(array('packageUpdates' => array()));
         return array('count' => 0, 'pageCount' => 0, 'searchID' => 0, 'template' => WCF::getTPL()->fetch('packageSearchResultList'));
     }
     // get excluded packages
     $sql = "SELECT\t*\n\t\t\tFROM\twcf" . WCF_N . "_package_update_exclusion";
     $statement = WCF::getDB()->prepareStatement($sql);
     $statement->execute();
     $excludedPackages = array();
     while ($row = $statement->fetchArray()) {
         $package = $row['excludedPackage'];
         $packageVersion = $row['excludedPackageVersion'];
         $packageUpdateVersionID = $row['packageUpdateVersionID'];
         if (!isset($excludedPackages[$packageUpdateVersionID][$package])) {
             $excludedPackages[$packageUpdateVersionID][$package] = $packageVersion;
         } else {
             if (Package::compareVersion($excludedPackages[$packageUpdateVersionID][$package], $packageVersion) == 1) {
                 $excludedPackages[$packageUpdateVersionID][$package] = $packageVersion;
             }
         }
     }
     // get installed packages
     $sql = "SELECT\tpackage, packageVersion\n\t\t\tFROM\twcf" . WCF_N . "_package";
     $statement = WCF::getDB()->prepareStatement($sql);
     $statement->execute();
     $installedPackages = array();
     while ($row = $statement->fetchArray()) {
         $installedPackages[$row['package']] = $row['packageVersion'];
     }
     // filter by version
     $conditions = new PreparedStatementConditionBuilder();
     $conditions->add("puv.packageUpdateID IN (?)", array($packageUpdateIDs));
     $sql = "SELECT\t\tpu.package, puv.packageUpdateVersionID, puv.packageUpdateID, puv.packageVersion, puv.isAccessible\n\t\t\tFROM\t\twcf" . WCF_N . "_package_update_version puv\n\t\t\tLEFT JOIN\twcf" . WCF_N . "_package_update pu\n\t\t\tON\t\t(pu.packageUpdateID = puv.packageUpdateID)\n\t\t\t" . $conditions;
     $statement = WCF::getDB()->prepareStatement($sql);
     $statement->execute($conditions->getParameters());
     $packageVersions = array();
     while ($row = $statement->fetchArray()) {
         $package = $row['package'];
         $packageVersion = $row['packageVersion'];
         $packageUpdateVersionID = $row['packageUpdateVersionID'];
         // check excluded packages
         if (isset($excludedPackages[$packageUpdateVersionID])) {
             $isExcluded = false;
             foreach ($excludedPackages[$packageUpdateVersionID] as $excludedPackage => $excludedPackageVersion) {
                 if (isset($installedPackages[$excludedPackage]) && Package::compareVersion($excludedPackageVersion, $installedPackages[$excludedPackage]) <= 0) {
                     // excluded, ignore
                     $isExcluded = true;
                     break;
                 }
             }
             if ($isExcluded) {
                 continue;
             }
         }
         if (!isset($packageVersions[$package])) {
             $packageVersions[$package] = array();
         }
         $packageUpdateID = $row['packageUpdateID'];
         if (!isset($packageVersions[$package][$packageUpdateID])) {
             $packageVersions[$package][$packageUpdateID] = array('accessible' => array(), 'existing' => array());
         }
         if ($row['isAccessible']) {
             $packageVersions[$package][$packageUpdateID]['accessible'][$row['packageUpdateVersionID']] = $packageVersion;
         }
         $packageVersions[$package][$packageUpdateID]['existing'][$row['packageUpdateVersionID']] = $packageVersion;
     }
     // all found versions are excluded
     if (empty($packageVersions)) {
         WCF::getTPL()->assign(array('packageUpdates' => array()));
         return array('count' => 0, 'pageCount' => 0, 'searchID' => 0, 'template' => WCF::getTPL()->fetch('packageSearchResultList'));
     }
     // determine highest versions
     $packageUpdates = array();
     foreach ($packageVersions as $package => $versionData) {
         $accessible = $existing = $versions = array();
         foreach ($versionData as $packageUpdateID => $versionTypes) {
             // ignore unaccessible packages
             if (empty($versionTypes['accessible'])) {
                 continue;
             }
             uasort($versionTypes['accessible'], array('wcf\\data\\package\\Package', 'compareVersion'));
             uasort($versionTypes['existing'], array('wcf\\data\\package\\Package', 'compareVersion'));
             $accessibleVersion = array_slice($versionTypes['accessible'], -1, 1, true);
             $existingVersion = array_slice($versionTypes['existing'], -1, 1, true);
             $ak = key($accessibleVersion);
             $av = current($accessibleVersion);
             $ek = key($existingVersion);
             $ev = current($existingVersion);
             $accessible[$av] = $ak;
             $existing[$ev] = $ek;
             $versions[$ak] = $packageUpdateID;
             $versions[$ek] = $packageUpdateID;
         }
         // ignore packages without accessible versions
         if (empty($accessible)) {
             continue;
         }
         uksort($accessible, array('wcf\\data\\package\\Package', 'compareVersion'));
         uksort($existing, array('wcf\\data\\package\\Package', 'compareVersion'));
         $accessible = array_pop($accessible);
         $existing = array_pop($existing);
         $packageUpdates[$versions[$accessible]] = array('accessible' => $accessible, 'existing' => $existing);
     }
     // no found packages is accessible
     if (empty($packageUpdates)) {
         WCF::getTPL()->assign(array('packageUpdates' => array()));
         return array('count' => 0, 'pageCount' => 0, 'searchID' => 0, 'template' => WCF::getTPL()->fetch('packageSearchResultList'));
     }
     $search = SearchEditor::create(array('userID' => WCF::getUser()->userID, 'searchData' => serialize($packageUpdates), 'searchTime' => TIME_NOW, 'searchType' => 'acpPackageSearch'));
     // forward call to build the actual result list
     $updateAction = new PackageUpdateAction(array(), 'getResultList', array('pageNo' => 1, 'search' => $search));
     $returnValues = $updateAction->executeAction();
     return $returnValues['returnValues'];
 }