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