[TASK] Speed up updating the extension list
[Packages/TYPO3.CMS.git] / typo3 / sysext / extensionmanager / Classes / Domain / Repository / ExtensionRepository.php
1 <?php
2 namespace TYPO3\CMS\Extensionmanager\Domain\Repository;
3
4 /***************************************************************
5 * Copyright notice
6 *
7 * (c) 2012-2013 Susanne Moog, <typo3@susannemoog.de>
8 * All rights reserved
9 *
10 * This script is part of the TYPO3 project. The TYPO3 project is
11 * free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
15 *
16 * The GNU General Public License can be found at
17 * http://www.gnu.org/copyleft/gpl.html.
18 *
19 * This script is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 * GNU General Public License for more details.
23 *
24 * This copyright notice MUST APPEAR in all copies of the script!
25 ***************************************************************/
26 /**
27 * A repository for extensions
28 *
29 * @author Susanne Moog <typo3@susannemoog.de>
30 */
31 class ExtensionRepository extends \TYPO3\CMS\Extbase\Persistence\Repository {
32 /**
33 * @var string
34 */
35 const TABLE_NAME = 'tx_extensionmanager_domain_model_extension';
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 return $query->execute();
81 }
82
83 /**
84 * Find an extension by extension key ordered by version
85 *
86 * @param string $extensionKey
87 * @return \TYPO3\CMS\Extbase\Persistence\QueryResultInterface
88 */
89 public function findByExtensionKeyOrderedByVersion($extensionKey) {
90 $query = $this->createQuery();
91 $query->matching($query->logicalAnd($query->equals('extensionKey', $extensionKey), $query->greaterThanOrEqual('reviewState', 0)));
92 $query->setOrderings(array('version' => \TYPO3\CMS\Extbase\Persistence\QueryInterface::ORDER_DESCENDING));
93 return $query->execute();
94 }
95
96 /**
97 * Find the current version by extension key
98 *
99 * @param string $extensionKey
100 * @return array|\TYPO3\CMS\Extbase\Persistence\QueryResultInterface
101 */
102 public function findOneByCurrentVersionByExtensionKey($extensionKey) {
103 $query = $this->createQuery();
104 $query->matching(
105 $query->logicalAnd(
106 $query->equals('extensionKey', $extensionKey),
107 $query->greaterThanOrEqual('reviewState', 0),
108 $query->equals('currentVersion', 1)
109 )
110 );
111 $query->setLimit(1);
112 return $query->execute()->getFirst();
113 }
114
115 /**
116 * Find one extension by extension key and version
117 *
118 * @param string $extensionKey
119 * @param string $version (example: 4.3.10)
120 * @return array|\TYPO3\CMS\Extbase\Persistence\QueryResultInterface
121 */
122 public function findOneByExtensionKeyAndVersion($extensionKey, $version) {
123 $query = $this->createQuery();
124 // Hint: This method must not filter out insecure extensions, if needed,
125 // it should be done on a different level, or with a helper method.
126 $query->matching($query->logicalAnd(
127 $query->equals('extensionKey', $extensionKey),
128 $query->equals('version', $version)
129 ));
130 return $query->setLimit(1)->execute()->getFirst();
131 }
132
133 /**
134 * Find an extension by title, author name or extension key
135 * This is the function used by the TER search. It is using a
136 * scoring for the matches to sort the extension with an
137 * exact key match on top
138 *
139 * @param string $searchString The string to search for extensions
140 * @return mixed
141 */
142 public function findByTitleOrAuthorNameOrExtensionKey($searchString) {
143 $quotedSearchString = $this->databaseConnection->escapeStrForLike($this->databaseConnection->quoteStr($searchString, 'tx_extensionmanager_domain_model_extension'), 'tx_extensionmanager_domain_model_extension');
144 $quotedSearchStringForLike = '\'%' . $quotedSearchString . '%\'';
145 $quotedSearchString = '\'' . $quotedSearchString . '\'';
146 $select = 'tx_extensionmanager_domain_model_extension.*,
147 (
148 (extension_key like ' . $quotedSearchString . ') * 8 +
149 (extension_key like ' . $quotedSearchStringForLike . ') * 4 +
150 (title like ' . $quotedSearchStringForLike . ') * 2 +
151 (author_name like ' . $quotedSearchStringForLike . ')
152 ) as position';
153 $from = 'tx_extensionmanager_domain_model_extension';
154 $where = '(
155 extension_key = ' . $quotedSearchString . '
156 OR
157 extension_key LIKE ' . $quotedSearchStringForLike . '
158 OR
159 title LIKE ' . $quotedSearchStringForLike . '
160 OR
161 description LIKE ' . $quotedSearchStringForLike . '
162 OR
163 author_name LIKE ' . $quotedSearchStringForLike . '
164 )
165 AND current_version=1 AND review_state >= 0
166 HAVING position > 0';
167 $order = 'position desc';
168 $result = $this->databaseConnection->exec_SELECTgetRows($select, $from, $where, '', $order);
169 return $this->dataMapper->map('TYPO3\\CMS\\Extensionmanager\\Domain\\Model\\Extension', $result);
170 }
171
172 /**
173 * Find an extension between a certain version range ordered by version number
174 *
175 * @param string $extensionKey
176 * @param integer $lowestVersion
177 * @param integer $highestVersion
178 * @return \TYPO3\CMS\Extbase\Persistence\QueryResultInterface
179 */
180 public function findByVersionRangeAndExtensionKeyOrderedByVersion($extensionKey, $lowestVersion = 0, $highestVersion = 0) {
181 $query = $this->createQuery();
182 $constraint = NULL;
183 if ($lowestVersion !== 0 && $highestVersion !== 0) {
184 $constraint = $query->logicalAnd($query->lessThanOrEqual('integerVersion', $highestVersion), $query->greaterThanOrEqual('integerVersion', $lowestVersion), $query->equals('extensionKey', $extensionKey));
185 } elseif ($lowestVersion === 0 && $highestVersion !== 0) {
186 $constraint = $query->logicalAnd($query->lessThanOrEqual('integerVersion', $highestVersion), $query->equals('extensionKey', $extensionKey));
187 } elseif ($lowestVersion !== 0 && $highestVersion === 0) {
188 $constraint = $query->logicalAnd($query->greaterThanOrEqual('integerVersion', $lowestVersion), $query->equals('extensionKey', $extensionKey));
189 } elseif ($lowestVersion === 0 && $highestVersion === 0) {
190 $constraint = $query->equals('extensionKey', $extensionKey);
191 }
192 if ($constraint) {
193 $query->matching($query->logicalAnd($constraint, $query->greaterThanOrEqual('reviewState', 0)));
194 }
195 $query->setOrderings(array(
196 'integerVersion' => \TYPO3\CMS\Extbase\Persistence\QueryInterface::ORDER_DESCENDING
197 ));
198 return $query->execute();
199 }
200
201 /**
202 * Finds all extensions with category "distribution"
203 *
204 * @return \TYPO3\CMS\Extbase\Persistence\QueryResultInterface
205 */
206 public function findAllDistributions() {
207 $query = $this->createQuery();
208 $query->matching(
209 $query->logicalAnd(
210 $query->equals('category', \TYPO3\CMS\Extensionmanager\Domain\Model\Extension::DISTRIBUTION_CATEGORY),
211 $query->equals('currentVersion', 1)
212 )
213 );
214 return $query->execute();
215 }
216
217 /**
218 * Count extensions with a certain key between a given version range
219 *
220 * @param string $extensionKey
221 * @param integer $lowestVersion
222 * @param integer $highestVersion
223 * @return integer
224 */
225 public function countByVersionRangeAndExtensionKey($extensionKey, $lowestVersion = 0, $highestVersion = 0) {
226 return $this->findByVersionRangeAndExtensionKeyOrderedByVersion($extensionKey, $lowestVersion, $highestVersion)->count();
227 }
228
229 /**
230 * Find highest version available of an extension
231 *
232 * @param string $extensionKey
233 * @return object
234 */
235 public function findHighestAvailableVersion($extensionKey) {
236 $query = $this->createQuery();
237 $query->matching($query->logicalAnd($query->equals('extensionKey', $extensionKey), $query->greaterThanOrEqual('reviewState', 0)));
238 $query->setOrderings(array(
239 'integerVersion' => \TYPO3\CMS\Extbase\Persistence\QueryInterface::ORDER_DESCENDING
240 ));
241 return $query->setLimit(1)->execute()->getFirst();
242 }
243
244 /**
245 * Updates the current_version field after update.
246 *
247 * @param int $repositoryUid
248 * @return int
249 */
250 public function insertLastVersion($repositoryUid = 1) {
251 $this->markExtensionWithMaximumVersionAsCurrent($repositoryUid);
252
253 return $this->getNumberOfCurrentExtensions();
254 }
255
256 /**
257 * Sets current_version = 1 for all extensions where the extension version is maximal.
258 *
259 * For performance reasons, the "native" TYPO3_DB is used here directly.
260 *
261 * @param int $repositoryUid
262 * @return void
263 */
264 protected function markExtensionWithMaximumVersionAsCurrent($repositoryUid) {
265 $uidsOfCurrentVersion = $this->fetchMaximalVersionsForAllExtensions($repositoryUid);
266
267 $this->databaseConnection->exec_UPDATEquery(
268 self::TABLE_NAME,
269 'uid IN (' . implode(',', $uidsOfCurrentVersion) . ')',
270 array(
271 'current_version' => 1,
272 )
273 );
274 }
275
276 /**
277 * Fetches the UIDs of all maximal versions for all extensions.
278 * This is done by doing a subselect in the WHERE clause to get all
279 * max versions and then the UID of that record in the outer select.
280 *
281 * @param int $repositoryUid
282 * @return array
283 */
284 protected function fetchMaximalVersionsForAllExtensions($repositoryUid) {
285 $extensionUids = $this->databaseConnection->exec_SELECTgetRows(
286 'a.uid AS uid',
287 self::TABLE_NAME . ' a',
288 'integer_version=(' .
289 $this->databaseConnection->SELECTquery(
290 'MAX(integer_version)',
291 self::TABLE_NAME . ' b',
292 'b.repository=' . (int)$repositoryUid . ' AND a.extension_key=b.extension_key'
293 ) .
294 ') AND repository=' . (int)$repositoryUid,
295 '', '', '', 'uid'
296 );
297 return array_keys($extensionUids);
298 }
299
300 /**
301 * Returns the number of extensions that are current.
302 *
303 * @return int
304 */
305 protected function getNumberOfCurrentExtensions() {
306 return $this->databaseConnection->exec_SELECTcountRows(
307 '*',
308 self::TABLE_NAME,
309 'current_version = 1'
310 );
311 }
312
313 /**
314 * Adds default constraints to the query - in this case it
315 * enables us to always just search for the latest version of an extension
316 *
317 * @param \TYPO3\CMS\Extbase\Persistence\Generic\Query $query the query to adjust
318 * @return \TYPO3\CMS\Extbase\Persistence\Generic\Query
319 */
320 protected function addDefaultConstraints(\TYPO3\CMS\Extbase\Persistence\Generic\Query $query) {
321 if ($query->getConstraint()) {
322 $query->matching($query->logicalAnd(
323 $query->getConstraint(),
324 $query->equals('current_version', TRUE),
325 $query->greaterThanOrEqual('reviewState', 0)
326 ));
327 } else {
328 $query->matching($query->logicalAnd(
329 $query->equals('current_version', TRUE),
330 $query->greaterThanOrEqual('reviewState', 0)
331 ));
332 }
333 return $query;
334 }
335 }