TYPO3 API  SVNRelease
class.tx_em_repository_utility.php
Go to the documentation of this file.
00001 <?php
00002 /***************************************************************
00003  *  Copyright notice
00004  *
00005  *  (c) 2010 Marcus Krause <marcus#exp2010@t3sec.info>
00006  *         Steffen Kamper <info@sk-typo3.de>
00007  *  All rights reserved
00008  *
00009  *  This script is part of the TYPO3 project. The TYPO3 project is
00010  *  free software; you can redistribute it and/or modify
00011  *  it under the terms of the GNU General Public License as published by
00012  *  the Free Software Foundation; either version 2 of the License, or
00013  *  (at your option) any later version.
00014  *
00015  *  The GNU General Public License can be found at
00016  *  http://www.gnu.org/copyleft/gpl.html.
00017  *
00018  *  This script is distributed in the hope that it will be useful,
00019  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00020  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00021  *  GNU General Public License for more details.
00022  *
00023  *  This copyright notice MUST APPEAR in all copies of the script!
00024  ***************************************************************/
00025 /**
00026  * class.tx_em_repository_utility.php
00027  *
00028  * Module: Extension manager - Central repository utility functions
00029  *
00030  * $Id: class.tx_em_repository_utility.php 2082 2010-03-21 17:19:42Z steffenk $
00031  *
00032  * @author  Marcus Krause <marcus#exp2010@t3sec.info>
00033  * @author  Steffen Kamper <info@sk-typo3.de>
00034  */
00035 
00036 
00037 /**
00038  * Central utility class for repository handling.
00039  *
00040  * @author    Marcus Krause <marcus#exp2010@t3sec.info>
00041  * @author    Steffen Kamper <info@sk-typo3.de>
00042  *
00043  * @since      2010-02-18
00044  * @package  TYPO3
00045  * @subpackage  EM
00046  */
00047 class tx_em_Repository_Utility implements t3lib_Singleton {
00048 
00049 
00050     /**
00051      * ##########################################
00052      * Problem constants - to be used in bitmasks
00053      * ##########################################
00054      */
00055     /**
00056      * Type of problem: extension file not existing in file system.
00057      *
00058      * @var  integer
00059      */
00060     const PROBLEM_EXTENSION_FILE_NOT_EXISTING = 1;
00061 
00062     /**
00063      * Type of problem: wrong hash indicates outdated extension file.
00064      *
00065      * @var  integer
00066      */
00067     const PROBLEM_EXTENSION_HASH_CHANGED = 2;
00068 
00069     /**
00070      * Type of problem: no version records in database.
00071      *
00072      * @var  integer
00073      */
00074     const PROBLEM_NO_VERSIONS_IN_DATABASE = 4;
00075 
00076 
00077     /**
00078      * Keeps instance of repository class.
00079      *
00080      * @var em_repository
00081      */
00082     protected $repository = NULL;
00083 
00084 
00085     /**
00086      * Class constructor.
00087      *
00088      * @access  public
00089      * @param   object  &$repository  (optional) instance of {@link em_repository repository} class
00090      * @return  void
00091      */
00092     function __construct(&$repository = NULL) {
00093         if ($repository !== NULL && is_object($repository)
00094                 && $repository instanceof tx_em_Repository) {
00095             $this->setRepository($repository);
00096         }
00097     }
00098 
00099     /**
00100      * Method provides a wrapper for throwing an exception.
00101      *
00102      * @access  protected
00103      * @see  tx_em_ConnectionException
00104      * @param   string   $message  the exception message to throw.
00105      * @param   integer $code  the exception code.
00106      * @return  void
00107      */
00108     protected function throwConnectionException($message = "", $code = 0) {
00109         throw new tx_em_ConnectionException(get_class($this) . ': ' . $message, $code);
00110     }
00111 
00112     /**
00113      * Method registers required repository instance to work with.
00114      *
00115      * Repository instance is passed by reference.
00116      *
00117      * @access  public
00118      * @param   em_repository  &$repository  instance of {@link em_repository repository} class
00119      * @return  void
00120      * @see  $repository
00121      */
00122     public function setRepository(tx_em_Repository &$repository) {
00123         $this->repository = $repository;
00124     }
00125 
00126     /**
00127      * Method fetches extension list file from remote server.
00128      *
00129      * Delegates to {@link fetchFile()}.
00130      *
00131      * @access  public
00132      * @return  void
00133      * @see  fetchFile()
00134      */
00135     public function fetchExtListFile() {
00136         $this->fetchFile($this->getRemoteExtListFile(), $this->getLocalExtListFile());
00137     }
00138 
00139     /**
00140      * Method fetches mirror list file from remote server.
00141      *
00142      * Delegates to {@link fetchFile()}.
00143      *
00144      * @access  public
00145      * @return  void
00146      * @see  fetchFile()
00147      */
00148     public function fetchMirrorListFile() {
00149         $this->fetchFile($this->getRemoteMirrorListFile(), $this->getLocalMirrorListFile());
00150     }
00151 
00152     /**
00153      * Method fetches contents from remote server and
00154      * writes them into a file in the local file system.
00155      *
00156      * @access  protected
00157      * @param   string  $remoteRessource  remote ressource to read contents from
00158      * @param   string  $localRessource   local ressource (absolute file path) to store retrieved contents to
00159      * @return  void
00160      * @see  t3lib_div::getURL(), t3lib_div::writeFile()
00161      * @throws  tx_em_ConnectionException
00162      */
00163     protected function fetchFile($remoteRessource, $localRessource) {
00164         if (is_string($remoteRessource) && is_string($localRessource)
00165                 && !empty($remoteRessource) && !empty($localRessource)) {
00166             $fileContent = t3lib_div::getURL($remoteRessource, 0, array(TYPO3_user_agent));
00167             if ($fileContent !== false) {
00168                 t3lib_div::writeFile($localRessource, $fileContent) || $this->throwConnectionException(sprintf('Could not write to file %s.', htmlspecialchars($localRessource)));
00169             } else {
00170                 $this->throwConnectionException(sprintf('Could not access remote ressource %s.', htmlspecialchars($remoteRessource)));
00171             }
00172         }
00173     }
00174 
00175     /**
00176      * Method returns location of local extension list file.
00177      *
00178      * @access  public
00179      * @return  string  local location of file
00180      * @see  getRemoteExtListFile()
00181      */
00182     public function getLocalExtListFile() {
00183         $absFilePath = PATH_site . 'typo3temp/'
00184                 . intval($this->repository->getId())
00185                 . '.extensions.xml.gz';
00186         return $absFilePath;
00187     }
00188 
00189     /**
00190      * Method returns location of remote extension list file.
00191      *
00192      * @access  public
00193      * @return  string  remote location of file
00194      * @see  getLocalExtListFile()
00195      */
00196     public function getRemoteExtListFile() {
00197         $mirror = $this->getMirrors(TRUE)->getMirror();
00198         $filePath = 'http://' . $mirror['host'] . $mirror['path']
00199                 . 'extensions.xml.gz';
00200         return $filePath;
00201     }
00202 
00203     /**
00204      * Method returns location of remote file containing
00205      * the extension checksum hash.
00206      *
00207      * @access  public
00208      * @return  string  remote location of file
00209      */
00210     public function getRemoteExtHashFile() {
00211         $mirror = $this->getMirrors(TRUE)->getMirror();
00212         $filePath = 'http://' . $mirror['host'] . $mirror['path']
00213                 . 'extensions.md5';
00214         return $filePath;
00215     }
00216 
00217     /**
00218      * Method returns location of local mirror list file.
00219      *
00220      * @access  public
00221      * @return  string  local location of file
00222      * @see  getRemoteMirrorListFile()
00223      */
00224     public function getLocalMirrorListFile() {
00225         $absFilePath = PATH_site . 'typo3temp/'
00226                 . intval($this->repository->getId())
00227                 . '.mirrors.xml.gz';
00228         return $absFilePath;
00229     }
00230 
00231     /**
00232      * Method returns location of remote mirror list file.
00233      *
00234      * @access  public
00235      * @return  string  remote location of file
00236      * @see  getLocalMirrorListFile()
00237      */
00238     public function getRemoteMirrorListFile() {
00239         $filePath = $this->repository->getMirrorListUrl();
00240         return $filePath;
00241     }
00242 
00243     /**
00244      * Method returns available mirrors for registered repository.
00245      *
00246      * If there are no mirrors registered to the repository,
00247      * the method will retrieve them from file system or remote
00248      * server.
00249      *
00250      * @access  public
00251      * @param   boolean  $forcedUpdateFromRemote  if boolean true, mirror configuration will always retrieved from remote server
00252      * @return  em_repository_mirrors  instance of repository mirrors class
00253      */
00254     public function getMirrors($forcedUpdateFromRemote = TRUE) {
00255         $assignedMirror = $this->repository->getMirrors();
00256         if ($forcedUpdateFromRemote || is_null($assignedMirror) || !is_object($assignedMirror)) {
00257             if ($forcedUpdateFromRemote || !is_file($this->getLocalMirrorListFile())) {
00258                 $this->fetchMirrorListFile();
00259             }
00260             $objMirrorListImporter = t3lib_div::makeInstance('tx_em_Import_MirrorListImporter');
00261             $this->repository->addMirrors($objMirrorListImporter->getMirrors($this->getLocalMirrorListFile()));
00262         }
00263         return $this->repository->getMirrors();
00264     }
00265 
00266     /**
00267      * Method returns information if currently available
00268      * extension list might be outdated.
00269      *
00270      * @access  public
00271      * @see  em_repository_utility::PROBLEM_NO_VERSIONS_IN_DATABASE,
00272      *        em_repository_utility::PROBLEM_EXTENSION_FILE_NOT_EXISTING,
00273      *        em_repository_utility::PROBLEM_EXTENSION_HASH_CHANGED
00274      * @return  integer  integer "0" if everything is perfect, otherwise bitmask with occured problems
00275      * @see  updateExtList()
00276      */
00277     public function isExtListUpdateNecessary() {
00278         $updateNecessity = 0;
00279 
00280         if (tx_em_Database::getExtensionCountFromRepository($this->getRepositoryUID(TRUE)) <= 0) {
00281             $updateNecessity |= self::PROBLEM_NO_VERSIONS_IN_DATABASE;
00282         }
00283 
00284         if (!is_file($this->getLocalExtListFile())) {
00285             $updateNecessity |= self::PROBLEM_EXTENSION_FILE_NOT_EXISTING;
00286         } else {
00287             $remotemd5 = t3lib_div::getURL($this->getRemoteExtHashFile(), 0, array(TYPO3_user_agent));
00288 
00289             if ($remotemd5 !== false) {
00290                 $localmd5 = md5_file($this->getLocalExtListFile());
00291                 if ($remotemd5 !== $localmd5) {
00292                     $updateNecessity |= self::PROBLEM_EXTENSION_HASH_CHANGED;
00293                 }
00294             } else {
00295                 $this->throwConnectionException('Could not retrieve extension hash file from remote server.');
00296             }
00297         }
00298         return $updateNecessity;
00299     }
00300 
00301     /**
00302      * Method returns UID of the current repository.
00303      *
00304      * @access  public
00305      * @param   boolean $insertIfMissing  creates repository record in DB if set to TRUE
00306      * @return  integer
00307      */
00308     public function getRepositoryUID($insertIfMissing = FALSE) {
00309         $uid = $this->repository->getId();
00310         $repository = tx_em_Database::getRepositoryByUID($uid);
00311         if (empty($repository) && $insertIfMissing) {
00312             $uid = tx_em_Database::insertRepository($this->repository);
00313         } else {
00314             $uid = intval($repository['uid']);
00315         }
00316 
00317         return $uid;
00318     }
00319 
00320     /**
00321      * Method updates TYPO3 database with up-to-date
00322      * extension version records.
00323      *
00324      * @access  public
00325      * @param   boolean  $renderFlashMessage  if method should return flashmessage or raw integer
00326      * @return  mixed   either sum of imported extensions or instance of t3lib_FlashMessage
00327      * @see  isExtListUpdateNecessary()
00328      */
00329     public function updateExtList($renderFlashMessage = FALSE) {
00330 
00331         if ($renderFlashMessage) {
00332             /* @var $flashMessage t3lib_FlashMessage */
00333             $flashMessage = t3lib_div::makeInstance('t3lib_FlashMessage',
00334                 $GLOBALS['LANG']->getLL('ext_import_list_unchanged_header'),
00335                 $GLOBALS['LANG']->getLL('ext_import_list_unchanged'),
00336                 t3lib_FlashMessage::INFO
00337             );
00338         }
00339         $sumRecords = 0;
00340 
00341         $updateNecessity = $this->isExtListUpdateNecessary();
00342 
00343         if ($updateNecessity !== 0) {
00344             // retrieval of file necessary
00345             $tmpBitmask = (self::PROBLEM_EXTENSION_FILE_NOT_EXISTING | self::PROBLEM_EXTENSION_HASH_CHANGED);
00346             if (($tmpBitmask & $updateNecessity) > 0) {
00347                 $this->fetchExtListFile();
00348                 $updateNecessity &= ~$tmpBitmask;
00349             }
00350 
00351                 // database table cleanup
00352             if (($updateNecessity & self::PROBLEM_NO_VERSIONS_IN_DATABASE)) {
00353                 $updateNecessity &= ~self::PROBLEM_NO_VERSIONS_IN_DATABASE;
00354             } else {
00355                 $GLOBALS['TYPO3_DB']->exec_DELETEquery('cache_extensions', 'repository=' . $this->getRepositoryUID());
00356             }
00357 
00358                 // no further problems - start of import process
00359             if ($updateNecessity === 0) {
00360                 $uid = $this->getRepositoryUID(TRUE);
00361                 /* @var $objExtListImporter tx_em_Import_ExtensionListImporter */
00362                 $objExtListImporter = t3lib_div::makeInstance('tx_em_Import_ExtensionListImporter');
00363                 $objExtListImporter->import($this->getLocalExtListFile(), $uid);
00364                 $sumRecords = tx_em_Database::getExtensionCountFromRepository($uid);
00365                 if ($renderFlashMessage) {
00366                     $flashMessage->setTitle($GLOBALS['LANG']->getLL('ext_import_extlist_updated_header'));
00367                     $flashMessage->setMessage(sprintf($GLOBALS['LANG']->getLL('ext_import_extlist_updated'), tx_em_Database::getExtensionCountFromRepository()));
00368                     $flashMessage->setSeverity(t3lib_FlashMessage::OK);
00369                 }
00370             }
00371         }
00372         return $renderFlashMessage ? $flashMessage : $sumRecords;
00373     }
00374 }
00375 
00376 if (defined('TYPO3_MODE') && isset($GLOBALS['TYPO3_CONF_VARS'][TYPO3_MODE]['XCLASS']['typo3/sysext/em/classes/repository/class.tx_em_repository_utility.php'])) {
00377     include_once($GLOBALS['TYPO3_CONF_VARS'][TYPO3_MODE]['XCLASS']['typo3/sysext/em/classes/repository/class.tx_em_repository_utility.php']);
00378 }
00379 
00380 ?>