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