[BUGFIX] Prevent current extension version in update dialog
[Packages/TYPO3.CMS.git] / typo3 / sysext / extensionmanager / Classes / Domain / Repository / ExtensionRepository.php
1 <?php
2 namespace TYPO3\CMS\Extensionmanager\Domain\Repository;
3
4 /*
5 * This file is part of the TYPO3 CMS project.
6 *
7 * It is free software; you can redistribute it and/or modify it under
8 * the terms of the GNU General Public License, either version 2
9 * of the License, or any later version.
10 *
11 * For the full copyright and license information, please read the
12 * LICENSE.txt file that was distributed with this source code.
13 *
14 * The TYPO3 project - inspiring people to share!
15 */
16
17 /**
18 * A repository for extensions
19 */
20 class ExtensionRepository extends \TYPO3\CMS\Extbase\Persistence\Repository {
21
22 /**
23 * @var string
24 */
25 const TABLE_NAME = 'tx_extensionmanager_domain_model_extension';
26
27 /**
28 * @var \TYPO3\CMS\Core\Database\DatabaseConnection
29 */
30 protected $databaseConnection;
31
32 /**
33 * @var \TYPO3\CMS\Extbase\Persistence\Generic\Mapper\DataMapper
34 */
35 protected $dataMapper;
36
37 /**
38 * @param \TYPO3\CMS\Extbase\Persistence\Generic\Mapper\DataMapper $dataMapper
39 */
40 public function injectDataMapper(\TYPO3\CMS\Extbase\Persistence\Generic\Mapper\DataMapper $dataMapper) {
41 $this->dataMapper = $dataMapper;
42 }
43
44 /**
45 * Do not include pid in queries
46 *
47 * @return void
48 */
49 public function initializeObject() {
50 /** @var $defaultQuerySettings \TYPO3\CMS\Extbase\Persistence\Generic\QuerySettingsInterface */
51 $defaultQuerySettings = $this->objectManager->get(\TYPO3\CMS\Extbase\Persistence\Generic\QuerySettingsInterface::class);
52 $defaultQuerySettings->setRespectStoragePage(FALSE);
53 $this->setDefaultQuerySettings($defaultQuerySettings);
54 $this->databaseConnection = $GLOBALS['TYPO3_DB'];
55 }
56
57 /**
58 * Count all extensions
59 *
60 * @return int
61 */
62 public function countAll() {
63 $query = $this->createQuery();
64 $query = $this->addDefaultConstraints($query);
65 return $query->execute()->count();
66 }
67
68 /**
69 * Finds all extensions
70 *
71 * @return array|\TYPO3\CMS\Extbase\Persistence\QueryResultInterface
72 */
73 public function findAll() {
74 $query = $this->createQuery();
75 $query = $this->addDefaultConstraints($query);
76 $query->setOrderings(
77 array(
78 'lastUpdated' => \TYPO3\CMS\Extbase\Persistence\QueryInterface::ORDER_DESCENDING
79 )
80 );
81 return $query->execute();
82 }
83
84 /**
85 * Find an extension by extension key ordered by version
86 *
87 * @param string $extensionKey
88 * @return \TYPO3\CMS\Extbase\Persistence\QueryResultInterface
89 */
90 public function findByExtensionKeyOrderedByVersion($extensionKey) {
91 $query = $this->createQuery();
92 $query->matching($query->logicalAnd($query->equals('extensionKey', $extensionKey), $query->greaterThanOrEqual('reviewState', 0)));
93 $query->setOrderings(array('version' => \TYPO3\CMS\Extbase\Persistence\QueryInterface::ORDER_DESCENDING));
94 return $query->execute();
95 }
96
97 /**
98 * Find the current version by extension key
99 *
100 * @param string $extensionKey
101 * @return array|\TYPO3\CMS\Extbase\Persistence\QueryResultInterface
102 */
103 public function findOneByCurrentVersionByExtensionKey($extensionKey) {
104 $query = $this->createQuery();
105 $query->matching(
106 $query->logicalAnd(
107 $query->equals('extensionKey', $extensionKey),
108 $query->greaterThanOrEqual('reviewState', 0),
109 $query->equals('currentVersion', 1)
110 )
111 );
112 $query->setLimit(1);
113 return $query->execute()->getFirst();
114 }
115
116 /**
117 * Find one extension by extension key and version
118 *
119 * @param string $extensionKey
120 * @param string $version (example: 4.3.10)
121 * @return array|\TYPO3\CMS\Extbase\Persistence\QueryResultInterface
122 */
123 public function findOneByExtensionKeyAndVersion($extensionKey, $version) {
124 $query = $this->createQuery();
125 // Hint: This method must not filter out insecure extensions, if needed,
126 // it should be done on a different level, or with a helper method.
127 $query->matching($query->logicalAnd(
128 $query->equals('extensionKey', $extensionKey),
129 $query->equals('version', $version)
130 ));
131 return $query->setLimit(1)->execute()->getFirst();
132 }
133
134 /**
135 * Find an extension by title, author name or extension key
136 * This is the function used by the TER search. It is using a
137 * scoring for the matches to sort the extension with an
138 * exact key match on top
139 *
140 * @param string $searchString The string to search for extensions
141 * @return mixed
142 */
143 public function findByTitleOrAuthorNameOrExtensionKey($searchString) {
144 $quotedSearchString = $this->databaseConnection->escapeStrForLike($this->databaseConnection->quoteStr($searchString, 'tx_extensionmanager_domain_model_extension'), 'tx_extensionmanager_domain_model_extension');
145 $quotedSearchStringForLike = '\'%' . $quotedSearchString . '%\'';
146 $quotedSearchString = '\'' . $quotedSearchString . '\'';
147 $select =
148 self::TABLE_NAME . '.*, ' .
149 'CASE ' .
150 'WHEN extension_key = ' . $quotedSearchString . ' THEN 16 ' .
151 'WHEN extension_key LIKE ' . $quotedSearchStringForLike . ' THEN 8 ' .
152 'WHEN title LIKE ' . $quotedSearchStringForLike . ' THEN 4 ' .
153 'WHEN description LIKE ' . $quotedSearchStringForLike . ' THEN 2 ' .
154 'WHEN author_name LIKE ' . $quotedSearchStringForLike . ' THEN 1 ' .
155 'END AS position';
156 $where = '(
157 extension_key = ' . $quotedSearchString . ' OR
158 extension_key LIKE ' . $quotedSearchStringForLike . ' OR
159 title LIKE ' . $quotedSearchStringForLike . ' OR
160 description LIKE ' . $quotedSearchStringForLike . ' OR
161 author_name LIKE ' . $quotedSearchStringForLike . '
162 )
163 AND current_version = 1 AND review_state >= 0';
164 $order = 'position DESC';
165 $result = $this->databaseConnection->exec_SELECTgetRows($select, self::TABLE_NAME, $where, '', $order);
166 return $this->dataMapper->map(\TYPO3\CMS\Extensionmanager\Domain\Model\Extension::class, $result);
167 }
168
169 /**
170 * Find an extension between a certain version range ordered by version number
171 *
172 * @param string $extensionKey
173 * @param int $lowestVersion
174 * @param int $highestVersion
175 * @param bool $includeCurrentVersion
176 * @return \TYPO3\CMS\Extbase\Persistence\QueryResultInterface
177 */
178 public function findByVersionRangeAndExtensionKeyOrderedByVersion($extensionKey, $lowestVersion = 0, $highestVersion = 0, $includeCurrentVersion = TRUE) {
179 $query = $this->createQuery();
180 $constraint = NULL;
181 if ($lowestVersion !== 0 && $highestVersion !== 0) {
182 if ($includeCurrentVersion) {
183 $constraint = $query->logicalAnd($query->lessThanOrEqual('integerVersion', $highestVersion), $query->greaterThanOrEqual('integerVersion', $lowestVersion), $query->equals('extensionKey', $extensionKey));
184 } else {
185 $constraint = $query->logicalAnd($query->lessThanOrEqual('integerVersion', $highestVersion), $query->greaterThan('integerVersion', $lowestVersion), $query->equals('extensionKey', $extensionKey));
186 }
187 } elseif ($lowestVersion === 0 && $highestVersion !== 0) {
188 if ($includeCurrentVersion) {
189 $constraint = $query->logicalAnd($query->lessThanOrEqual('integerVersion', $highestVersion), $query->equals('extensionKey', $extensionKey));
190 } else {
191 $constraint = $query->logicalAnd($query->lessThan('integerVersion', $highestVersion), $query->equals('extensionKey', $extensionKey));
192 }
193 } elseif ($lowestVersion !== 0 && $highestVersion === 0) {
194 if ($includeCurrentVersion) {
195 $constraint = $query->logicalAnd($query->greaterThanOrEqual('integerVersion', $lowestVersion), $query->equals('extensionKey', $extensionKey));
196 } else {
197 $constraint = $query->logicalAnd($query->greaterThan('integerVersion', $lowestVersion), $query->equals('extensionKey', $extensionKey));
198 }
199 } elseif ($lowestVersion === 0 && $highestVersion === 0) {
200 $constraint = $query->equals('extensionKey', $extensionKey);
201 }
202 if ($constraint) {
203 $query->matching($query->logicalAnd($constraint, $query->greaterThanOrEqual('reviewState', 0)));
204 }
205 $query->setOrderings(array(
206 'integerVersion' => \TYPO3\CMS\Extbase\Persistence\QueryInterface::ORDER_DESCENDING
207 ));
208 return $query->execute();
209 }
210
211 /**
212 * Finds all extensions with category "distribution" not published by the TYPO3 CMS Team
213 *
214 * @return \TYPO3\CMS\Extbase\Persistence\QueryResultInterface
215 */
216 public function findAllCommunityDistributions() {
217 $query = $this->createQuery();
218 $query->matching(
219 $query->logicalAnd(
220 $query->equals('category', \TYPO3\CMS\Extensionmanager\Domain\Model\Extension::DISTRIBUTION_CATEGORY),
221 $query->equals('currentVersion', 1),
222 $query->logicalNot($query->equals('ownerusername', 'typo3v4'))
223 )
224 );
225
226 $query->setOrderings(array(
227 'alldownloadcounter' => \TYPO3\CMS\Extbase\Persistence\QueryInterface::ORDER_DESCENDING
228 ));
229
230 return $query->execute();
231 }
232
233 /**
234 * Finds all extensions with category "distribution" that are published by the TYPO3 CMS Team
235 *
236 * @return \TYPO3\CMS\Extbase\Persistence\QueryResultInterface
237 */
238 public function findAllOfficialDistributions() {
239 $query = $this->createQuery();
240 $query->matching(
241 $query->logicalAnd(
242 $query->equals('category', \TYPO3\CMS\Extensionmanager\Domain\Model\Extension::DISTRIBUTION_CATEGORY),
243 $query->equals('currentVersion', 1),
244 $query->equals('ownerusername', 'typo3v4')
245 )
246 );
247
248 $query->setOrderings(array(
249 'alldownloadcounter' => \TYPO3\CMS\Extbase\Persistence\QueryInterface::ORDER_DESCENDING
250 ));
251
252 return $query->execute();
253 }
254
255 /**
256 * Count extensions with a certain key between a given version range
257 *
258 * @param string $extensionKey
259 * @param int $lowestVersion
260 * @param int $highestVersion
261 * @return int
262 */
263 public function countByVersionRangeAndExtensionKey($extensionKey, $lowestVersion = 0, $highestVersion = 0) {
264 return $this->findByVersionRangeAndExtensionKeyOrderedByVersion($extensionKey, $lowestVersion, $highestVersion)->count();
265 }
266
267 /**
268 * Find highest version available of an extension
269 *
270 * @param string $extensionKey
271 * @return \TYPO3\CMS\Extensionmanager\Domain\Model\Extension
272 */
273 public function findHighestAvailableVersion($extensionKey) {
274 $query = $this->createQuery();
275 $query->matching($query->logicalAnd($query->equals('extensionKey', $extensionKey), $query->greaterThanOrEqual('reviewState', 0)));
276 $query->setOrderings(array(
277 'integerVersion' => \TYPO3\CMS\Extbase\Persistence\QueryInterface::ORDER_DESCENDING
278 ));
279 return $query->setLimit(1)->execute()->getFirst();
280 }
281
282 /**
283 * Updates the current_version field after update.
284 *
285 * @param int $repositoryUid
286 * @return int
287 */
288 public function insertLastVersion($repositoryUid = 1) {
289 $this->markExtensionWithMaximumVersionAsCurrent($repositoryUid);
290
291 return $this->getNumberOfCurrentExtensions();
292 }
293
294 /**
295 * Sets current_version = 1 for all extensions where the extension version is maximal.
296 *
297 * For performance reasons, the "native" TYPO3_DB is used here directly.
298 *
299 * @param int $repositoryUid
300 * @return void
301 */
302 protected function markExtensionWithMaximumVersionAsCurrent($repositoryUid) {
303 $uidsOfCurrentVersion = $this->fetchMaximalVersionsForAllExtensions($repositoryUid);
304
305 $this->databaseConnection->exec_UPDATEquery(
306 self::TABLE_NAME,
307 'uid IN (' . implode(',', $uidsOfCurrentVersion) . ')',
308 array(
309 'current_version' => 1,
310 )
311 );
312 }
313
314 /**
315 * Fetches the UIDs of all maximal versions for all extensions.
316 * This is done by doing a LEFT JOIN to itself ("a" and "b") and comparing
317 * both integer_version fields.
318 *
319 * @param int $repositoryUid
320 * @return array
321 */
322 protected function fetchMaximalVersionsForAllExtensions($repositoryUid) {
323 $queryResult = $this->databaseConnection->sql_query(
324 'SELECT a.uid AS uid ' .
325 'FROM ' . self::TABLE_NAME . ' a ' .
326 'LEFT JOIN ' . self::TABLE_NAME . ' b ON a.repository = b.repository AND a.extension_key = b.extension_key AND a.integer_version < b.integer_version ' .
327 'WHERE a.repository = ' . (int)$repositoryUid . ' AND b.extension_key IS NULL ' .
328 'ORDER BY a.uid'
329 );
330
331 $extensionUids = array();
332 while ($row = $this->databaseConnection->sql_fetch_assoc($queryResult)) {
333 $extensionUids[] = $row['uid'];
334 }
335 $this->databaseConnection->sql_free_result($queryResult);
336 return $extensionUids;
337 }
338
339 /**
340 * Returns the number of extensions that are current.
341 *
342 * @return int
343 */
344 protected function getNumberOfCurrentExtensions() {
345 return $this->databaseConnection->exec_SELECTcountRows(
346 '*',
347 self::TABLE_NAME,
348 'current_version = 1'
349 );
350 }
351
352 /**
353 * Adds default constraints to the query - in this case it
354 * enables us to always just search for the latest version of an extension
355 *
356 * @param \TYPO3\CMS\Extbase\Persistence\Generic\Query $query the query to adjust
357 * @return \TYPO3\CMS\Extbase\Persistence\Generic\Query
358 */
359 protected function addDefaultConstraints(\TYPO3\CMS\Extbase\Persistence\Generic\Query $query) {
360 if ($query->getConstraint()) {
361 $query->matching($query->logicalAnd(
362 $query->getConstraint(),
363 $query->equals('current_version', TRUE),
364 $query->greaterThanOrEqual('reviewState', 0)
365 ));
366 } else {
367 $query->matching($query->logicalAnd(
368 $query->equals('current_version', TRUE),
369 $query->greaterThanOrEqual('reviewState', 0)
370 ));
371 }
372 return $query;
373 }
374
375 }