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