[BUGFIX] Throw Exception if typo3 extension repository is not defined
[Packages/TYPO3.CMS.git] / typo3 / sysext / extensionmanager / Classes / Utility / Repository / Helper.php
1 <?php
2 namespace TYPO3\CMS\Extensionmanager\Utility\Repository;
3
4 /***************************************************************
5 * Copyright notice
6 *
7 * (c) 2010-2013 Marcus Krause <marcus#exp2010@t3sec.info>
8 * Steffen Kamper <info@sk-typo3.de>
9 * All rights reserved
10 *
11 * This script is part of the TYPO3 project. The TYPO3 project is
12 * free software; you can redistribute it and/or modify
13 * it under the terms of the GNU General Public License as published by
14 * the Free Software Foundation; either version 2 of the License, or
15 * (at your option) any later version.
16 *
17 * The GNU General Public License can be found at
18 * http://www.gnu.org/copyleft/gpl.html.
19 *
20 * This script is distributed in the hope that it will be useful,
21 * but WITHOUT ANY WARRANTY; without even the implied warranty of
22 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23 * GNU General Public License for more details.
24 *
25 * This copyright notice MUST APPEAR in all copies of the script!
26 ***************************************************************/
27 /**
28 * Central utility class for repository handling.
29 *
30 * @author Marcus Krause <marcus#exp2010@t3sec.info>
31 * @author Steffen Kamper <info@sk-typo3.de>
32 */
33 class Helper implements \TYPO3\CMS\Core\SingletonInterface {
34
35 /**
36 * ##########################################
37 * Problem constants - to be used in bitmasks
38 * ##########################################
39 */
40 /**
41 * Type of problem: extension file not existing in file system.
42 *
43 * @var integer
44 */
45 const PROBLEM_EXTENSION_FILE_NOT_EXISTING = 1;
46 /**
47 * Type of problem: wrong hash indicates outdated extension file.
48 *
49 * @var integer
50 */
51 const PROBLEM_EXTENSION_HASH_CHANGED = 2;
52 /**
53 * Type of problem: no version records in database.
54 *
55 * @var integer
56 */
57 const PROBLEM_NO_VERSIONS_IN_DATABASE = 4;
58 /**
59 * Keeps instance of repository class.
60 *
61 * @var \TYPO3\CMS\Extensionmanager\Domain\Model\Repository
62 */
63 protected $repository = NULL;
64
65 /**
66 * @var \TYPO3\CMS\Extensionmanager\Domain\Repository\RepositoryRepository
67 */
68 protected $repositoryRepository;
69
70 /**
71 * @var \TYPO3\CMS\Extensionmanager\Domain\Repository\ExtensionRepository
72 */
73 protected $extensionRepository;
74
75 /**
76 * Class constructor.
77 *
78 * @access public
79 */
80 public function __construct() {
81 /** @var $objectManager \TYPO3\CMS\Extbase\Object\ObjectManager */
82 $this->objectManager = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance('TYPO3\\CMS\\Extbase\\Object\\ObjectManager');
83 $repositoryRepository = $this->objectManager->get('TYPO3\\CMS\\Extensionmanager\\Domain\\Repository\\RepositoryRepository');
84 $this->extensionRepository = $this->objectManager->get('TYPO3\\CMS\\Extensionmanager\\Domain\\Repository\\ExtensionRepository');
85 $repository = $repositoryRepository->findByUid(1);
86
87 if(!is_object($repository)) {
88 throw new \TYPO3\CMS\Extensionmanager\Exception\ExtensionManagerException('The typo3.org extension repository was not found. Please go to the "Update Wizard" in the install tool and run the task "Add the default Extension Manager database tables".', 1365192900);
89 }
90
91 $this->setRepository($repository);
92 }
93
94 /**
95 * Method registers required repository instance to work with.
96 *
97 * Repository instance is passed by reference.
98 *
99 * @access public
100 * @param \TYPO3\CMS\Extensionmanager\Domain\Model\Repository $repository
101 * @return void
102 * @see $repository
103 */
104 public function setRepository(\TYPO3\CMS\Extensionmanager\Domain\Model\Repository $repository) {
105 $this->repository = $repository;
106 }
107
108 /**
109 * Method fetches extension list file from remote server.
110 *
111 * Delegates to {@link fetchFile()}.
112 *
113 * @access public
114 * @return void
115 * @see fetchFile()
116 */
117 public function fetchExtListFile() {
118 $this->fetchFile($this->getRemoteExtListFile(), $this->getLocalExtListFile());
119 }
120
121 /**
122 * Method fetches mirror list file from remote server.
123 *
124 * Delegates to {@link fetchFile()}.
125 *
126 * @access public
127 * @return void
128 * @see fetchFile()
129 */
130 public function fetchMirrorListFile() {
131 $this->fetchFile($this->getRemoteMirrorListFile(), $this->getLocalMirrorListFile());
132 }
133
134 /**
135 * Method fetches contents from remote server and
136 * writes them into a file in the local file system.
137 *
138 * @param string $remoteResource remote resource to read contents from
139 * @param string $localResource local resource (absolute file path) to store retrieved contents to
140 * @return void
141 * @see \TYPO3\CMS\Core\Utility\GeneralUtility::getUrl(), \TYPO3\CMS\Core\Utility\GeneralUtility::writeFile()
142 * @throws \TYPO3\CMS\Extensionmanager\Exception\ExtensionManagerException
143 */
144 protected function fetchFile($remoteResource, $localResource) {
145 if (is_string($remoteResource) && is_string($localResource) && !empty($remoteResource) && !empty($localResource)) {
146 $fileContent = \TYPO3\CMS\Core\Utility\GeneralUtility::getUrl($remoteResource, 0, array(TYPO3_user_agent));
147 if ($fileContent !== FALSE) {
148 if (\TYPO3\CMS\Core\Utility\GeneralUtility::writeFile($localResource, $fileContent) === FALSE) {
149 throw new \TYPO3\CMS\Extensionmanager\Exception\ExtensionManagerException(sprintf('Could not write to file %s.', htmlspecialchars($localResource)), 1342635378);
150 }
151 } else {
152 throw new \TYPO3\CMS\Extensionmanager\Exception\ExtensionManagerException(sprintf('Could not access remote resource %s.', htmlspecialchars($remoteResource)), 1342635425);
153 }
154 }
155 }
156
157 /**
158 * Method returns location of local extension list file.
159 *
160 * @access public
161 * @return string local location of file
162 * @see getRemoteExtListFile()
163 */
164 public function getLocalExtListFile() {
165 $absFilePath = PATH_site . 'typo3temp/' . intval($this->repository->getUid()) . '.extensions.xml.gz';
166 return $absFilePath;
167 }
168
169 /**
170 * Method returns location of remote extension list file.
171 *
172 * @access public
173 * @return string remote location of file
174 * @see getLocalExtListFile()
175 */
176 public function getRemoteExtListFile() {
177 $mirror = $this->getMirrors(TRUE)->getMirror();
178 $filePath = 'http://' . $mirror['host'] . $mirror['path'] . 'extensions.xml.gz';
179 return $filePath;
180 }
181
182 /**
183 * Method returns location of remote file containing
184 * the extension checksum hash.
185 *
186 * @access public
187 * @return string remote location of file
188 */
189 public function getRemoteExtHashFile() {
190 $mirror = $this->getMirrors(TRUE)->getMirror();
191 $filePath = 'http://' . $mirror['host'] . $mirror['path'] . 'extensions.md5';
192 return $filePath;
193 }
194
195 /**
196 * Method returns location of local mirror list file.
197 *
198 * @access public
199 * @return string local location of file
200 * @see getRemoteMirrorListFile()
201 */
202 public function getLocalMirrorListFile() {
203 $absFilePath = PATH_site . 'typo3temp/' . intval($this->repository->getUid()) . '.mirrors.xml.gz';
204 return $absFilePath;
205 }
206
207 /**
208 * Method returns location of remote mirror list file.
209 *
210 * @access public
211 * @return string remote location of file
212 * @see getLocalMirrorListFile()
213 */
214 public function getRemoteMirrorListFile() {
215 $filePath = $this->repository->getMirrorListUrl();
216 return $filePath;
217 }
218
219 /**
220 * Method returns available mirrors for registered repository.
221 *
222 * If there are no mirrors registered to the repository,
223 * the method will retrieve them from file system or remote
224 * server.
225 *
226 * @access public
227 * @param boolean $forcedUpdateFromRemote if boolean TRUE, mirror configuration will always retrieved from remote server
228 * @return \TYPO3\CMS\Extensionmanager\Domain\Model\Mirrors instance of repository mirrors class
229 */
230 public function getMirrors($forcedUpdateFromRemote = TRUE) {
231 $assignedMirror = $this->repository->getMirrors();
232 if ($forcedUpdateFromRemote || is_null($assignedMirror) || !is_object($assignedMirror)) {
233 if ($forcedUpdateFromRemote || !is_file($this->getLocalMirrorListFile())) {
234 $this->fetchMirrorListFile();
235 }
236 /** @var $objMirrorListImporter \TYPO3\CMS\Extensionmanager\Utility\Importer\MirrorListUtility */
237 $objMirrorListImporter = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance('TYPO3\\CMS\\Extensionmanager\\Utility\\Importer\\MirrorListUtility');
238 $this->repository->addMirrors($objMirrorListImporter->getMirrors($this->getLocalMirrorListFile()));
239 }
240 return $this->repository->getMirrors();
241 }
242
243 /**
244 * Method returns information if currently available
245 * extension list might be outdated.
246 *
247 * @access public
248 * @see Tx_Extensionmanager_Utility_Repository_Helper::PROBLEM_NO_VERSIONS_IN_DATABASE,
249 * @throws \TYPO3\CMS\Extensionmanager\Exception\ExtensionManagerException
250 * @return integer "0" if everything is perfect, otherwise bitmask with problems
251 */
252 public function isExtListUpdateNecessary() {
253 $updateNecessity = 0;
254 if ($this->extensionRepository->countByRepository($this->repository->getUid()) <= 0) {
255 $updateNecessity |= self::PROBLEM_NO_VERSIONS_IN_DATABASE;
256 }
257 if (!is_file($this->getLocalExtListFile())) {
258 $updateNecessity |= self::PROBLEM_EXTENSION_FILE_NOT_EXISTING;
259 } else {
260 $remotemd5 = \TYPO3\CMS\Core\Utility\GeneralUtility::getUrl($this->getRemoteExtHashFile(), 0, array(TYPO3_user_agent));
261 if ($remotemd5 !== FALSE) {
262 $localmd5 = md5_file($this->getLocalExtListFile());
263 if ($remotemd5 !== $localmd5) {
264 $updateNecessity |= self::PROBLEM_EXTENSION_HASH_CHANGED;
265 }
266 } else {
267 throw new \TYPO3\CMS\Extensionmanager\Exception\ExtensionManagerException('Could not retrieve extension hash file from remote server.', 1342635016);
268 }
269 }
270 return $updateNecessity;
271 }
272
273 /**
274 * Method updates TYPO3 database with up-to-date
275 * extension version records.
276 *
277 * @return boolean TRUE if the extension list was successfully update, FALSE if no update necessary
278 * @see isExtListUpdateNecessary()
279 */
280 public function updateExtList() {
281 $updated = FALSE;
282 $updateNecessity = $this->isExtListUpdateNecessary();
283 if ($updateNecessity !== 0) {
284 // retrieval of file necessary
285 $tmpBitmask = self::PROBLEM_EXTENSION_FILE_NOT_EXISTING | self::PROBLEM_EXTENSION_HASH_CHANGED;
286 if (($tmpBitmask & $updateNecessity) > 0) {
287 $this->fetchExtListFile();
288 $updateNecessity &= ~$tmpBitmask;
289 }
290 // database table cleanup
291 if ($updateNecessity & self::PROBLEM_NO_VERSIONS_IN_DATABASE) {
292 $updateNecessity &= ~self::PROBLEM_NO_VERSIONS_IN_DATABASE;
293 } else {
294 // Use straight query as extbase "remove" is too slow here
295 // This truncates the whole table. It would be more correct to remove only rows of a specific
296 // repository, but multiple repository handling is not implemented, and truncate is quicker.
297 $this->getDatabaseConnection()->exec_TRUNCATEquery('tx_extensionmanager_domain_model_extension');
298 }
299 // no further problems - start of import process
300 if ($updateNecessity === 0) {
301 $uid = $this->repository->getUid();
302 /* @var $objExtListImporter \TYPO3\CMS\Extensionmanager\Utility\Importer\ExtensionListUtility */
303 $objExtListImporter = $this->objectManager->get('TYPO3\\CMS\\Extensionmanager\\Utility\\Importer\\ExtensionListUtility');
304 $objExtListImporter->import($this->getLocalExtListFile(), $uid);
305 $updated = TRUE;
306 }
307 }
308 return $updated;
309 }
310
311 /**
312 * Get database connection
313 *
314 * @return \TYPO3\CMS\Core\Database\DatabaseConnection
315 */
316 protected function getDatabaseConnection() {
317 return $GLOBALS['TYPO3_DB'];
318 }
319
320 }
321
322
323 ?>