cc4142aab4a6299d7bfd611f4452dc0be2eb48ca
[Packages/TYPO3.CMS.git] / typo3 / sysext / backend / Classes / Domain / Repository / Localization / LocalizationRepository.php
1 <?php
2 declare(strict_types = 1);
3
4 namespace TYPO3\CMS\Backend\Domain\Repository\Localization;
5
6 /*
7 * This file is part of the TYPO3 CMS project.
8 *
9 * It is free software; you can redistribute it and/or modify it under
10 * the terms of the GNU General Public License, either version 2
11 * of the License, or any later version.
12 *
13 * For the full copyright and license information, please read the
14 * LICENSE.txt file that was distributed with this source code.
15 *
16 * The TYPO3 project - inspiring people to share!
17 */
18
19 use Doctrine\DBAL\Driver\Statement;
20 use TYPO3\CMS\Core\Authentication\BackendUserAuthentication;
21 use TYPO3\CMS\Core\Database\Connection;
22 use TYPO3\CMS\Core\Database\ConnectionPool;
23 use TYPO3\CMS\Core\Database\Query\QueryBuilder;
24 use TYPO3\CMS\Core\Database\Query\Restriction\BackendWorkspaceRestriction;
25 use TYPO3\CMS\Core\Database\Query\Restriction\DeletedRestriction;
26 use TYPO3\CMS\Core\Utility\GeneralUtility;
27
28 /**
29 * Repository for record localizations
30 *
31 * @internal
32 */
33 class LocalizationRepository
34 {
35 /**
36 * Fetch the language from which the records in a certain language were initially localized
37 *
38 * @param int $pageId
39 * @param int $localizedLanguage
40 * @return array
41 */
42 public function fetchOriginLanguage(int $pageId, int $localizedLanguage): array
43 {
44 $queryBuilder = $this->getQueryBuilderWithWorkspaceRestriction('tt_content');
45
46 $constraints = [
47 $queryBuilder->expr()->eq(
48 'tt_content.pid',
49 $queryBuilder->createNamedParameter($pageId, \PDO::PARAM_INT)
50 ),
51 $queryBuilder->expr()->eq(
52 'tt_content.sys_language_uid',
53 $queryBuilder->createNamedParameter($localizedLanguage, \PDO::PARAM_INT)
54 ),
55 ];
56 $constraints += $this->getAllowedLanguageConstraintsForBackendUser();
57
58 $queryBuilder->select('tt_content_orig.sys_language_uid')
59 ->from('tt_content')
60 ->join(
61 'tt_content',
62 'tt_content',
63 'tt_content_orig',
64 $queryBuilder->expr()->eq(
65 'tt_content.l10n_source',
66 $queryBuilder->quoteIdentifier('tt_content_orig.uid')
67 )
68 )
69 ->join(
70 'tt_content_orig',
71 'sys_language',
72 'sys_language',
73 $queryBuilder->expr()->eq(
74 'tt_content_orig.sys_language_uid',
75 $queryBuilder->quoteIdentifier('sys_language.uid')
76 )
77 )
78 ->where(...$constraints)
79 ->groupBy('tt_content_orig.sys_language_uid');
80
81 return $queryBuilder->execute()->fetch() ?: [];
82 }
83
84 /**
85 * Returns number of localized records in given page and language
86 * Records which were added to the language directly (not through translation) are not counted.
87 *
88 * @param int $pageId
89 * @param int $languageId
90 * @return int
91 */
92 public function getLocalizedRecordCount(int $pageId, int $languageId): int
93 {
94 $queryBuilder = $this->getQueryBuilderWithWorkspaceRestriction('tt_content');
95
96 $rowCount = $queryBuilder->count('uid')
97 ->from('tt_content')
98 ->where(
99 $queryBuilder->expr()->eq(
100 'tt_content.sys_language_uid',
101 $queryBuilder->createNamedParameter($languageId, \PDO::PARAM_INT)
102 ),
103 $queryBuilder->expr()->eq(
104 'tt_content.pid',
105 $queryBuilder->createNamedParameter($pageId, \PDO::PARAM_INT)
106 ),
107 $queryBuilder->expr()->neq(
108 'tt_content.l10n_source',
109 $queryBuilder->createNamedParameter(0, \PDO::PARAM_INT)
110 )
111 )
112 ->execute()
113 ->fetchColumn(0);
114
115 return (int)$rowCount;
116 }
117
118 /**
119 * Fetch all available languages
120 *
121 * @param int $pageId
122 * @param int $languageId
123 * @return array
124 */
125 public function fetchAvailableLanguages(int $pageId, int $languageId): array
126 {
127 $queryBuilder = $this->getQueryBuilderWithWorkspaceRestriction('tt_content');
128
129 $constraints = [
130 $queryBuilder->expr()->eq(
131 'tt_content.sys_language_uid',
132 $queryBuilder->quoteIdentifier('sys_language.uid')
133 ),
134 $queryBuilder->expr()->eq(
135 'tt_content.pid',
136 $queryBuilder->createNamedParameter($pageId, \PDO::PARAM_INT)
137 ),
138 $queryBuilder->expr()->neq(
139 'sys_language.uid',
140 $queryBuilder->createNamedParameter($languageId, \PDO::PARAM_INT)
141 )
142 ];
143 $constraints += $this->getAllowedLanguageConstraintsForBackendUser();
144
145 $queryBuilder->select('sys_language.uid')
146 ->from('tt_content')
147 ->from('sys_language')
148 ->where(...$constraints)
149 ->groupBy('sys_language.uid', 'sys_language.sorting')
150 ->orderBy('sys_language.sorting');
151
152 $result = $queryBuilder->execute()->fetchAll();
153
154 return $result;
155 }
156
157 /**
158 * Builds additional query constraints to exclude hidden languages and
159 * limit a backend user to its allowed languages (unless the user is an admin)
160 *
161 * @return array
162 */
163 protected function getAllowedLanguageConstraintsForBackendUser(): array
164 {
165 $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('sys_language');
166 $constraints = [];
167
168 $backendUser = $this->getBackendUser();
169 if (!$backendUser->isAdmin()) {
170 if (!empty($GLOBALS['TCA']['sys_language']['ctrl']['enablecolumns']['disabled'])) {
171 $constraints[] = $queryBuilder->expr()->eq(
172 'sys_language.' . $GLOBALS['TCA']['sys_language']['ctrl']['enablecolumns']['disabled'],
173 $queryBuilder->createNamedParameter(0, \PDO::PARAM_INT)
174 );
175 }
176
177 if (!empty($backendUser->user['allowed_languages'])) {
178 $constraints[] = $queryBuilder->expr()->in(
179 'sys_language.uid',
180 $queryBuilder->createNamedParameter(
181 GeneralUtility::intExplode(',', $backendUser->user['allowed_languages'], true),
182 Connection::PARAM_INT_ARRAY
183 )
184 );
185 }
186 }
187
188 return $constraints;
189 }
190
191 /**
192 * Get records for copy process
193 *
194 * @param int $pageId
195 * @param int $destLanguageId
196 * @param int $languageId
197 * @param string $fields
198 * @return Statement
199 */
200 public function getRecordsToCopyDatabaseResult(int $pageId, int $destLanguageId, int $languageId, string $fields = '*'): Statement
201 {
202 $originalUids = [];
203
204 // Get original uid of existing elements triggered language
205 $queryBuilder = $this->getQueryBuilderWithWorkspaceRestriction('tt_content');
206
207 $originalUidsStatement = $queryBuilder
208 ->select('l10n_source')
209 ->from('tt_content')
210 ->where(
211 $queryBuilder->expr()->eq(
212 'sys_language_uid',
213 $queryBuilder->createNamedParameter($destLanguageId, \PDO::PARAM_INT)
214 ),
215 $queryBuilder->expr()->eq(
216 'tt_content.pid',
217 $queryBuilder->createNamedParameter($pageId, \PDO::PARAM_INT)
218 )
219 )
220 ->execute();
221
222 while ($origUid = $originalUidsStatement->fetchColumn(0)) {
223 $originalUids[] = (int)$origUid;
224 }
225
226 $queryBuilder->select(...GeneralUtility::trimExplode(',', $fields, true))
227 ->from('tt_content')
228 ->where(
229 $queryBuilder->expr()->eq(
230 'tt_content.sys_language_uid',
231 $queryBuilder->createNamedParameter($languageId, \PDO::PARAM_INT)
232 ),
233 $queryBuilder->expr()->eq(
234 'tt_content.pid',
235 $queryBuilder->createNamedParameter($pageId, \PDO::PARAM_INT)
236 )
237 )
238 ->orderBy('tt_content.sorting');
239
240 if (!empty($originalUids)) {
241 $queryBuilder
242 ->andWhere(
243 $queryBuilder->expr()->notIn(
244 'tt_content.uid',
245 $queryBuilder->createNamedParameter($originalUids, Connection::PARAM_INT_ARRAY)
246 )
247 );
248 }
249
250 return $queryBuilder->execute();
251 }
252
253 /**
254 * Returns the current BE user.
255 *
256 * @return BackendUserAuthentication
257 */
258 protected function getBackendUser(): BackendUserAuthentication
259 {
260 return $GLOBALS['BE_USER'];
261 }
262
263 /**
264 * Get a QueryBuilder for the given table with preconfigured restrictions
265 * to not retrieve workspace placeholders or deleted records.
266 *
267 * @param string $tableName
268 * @return \TYPO3\CMS\Core\Database\Query\QueryBuilder
269 */
270 protected function getQueryBuilderWithWorkspaceRestriction(string $tableName): QueryBuilder
271 {
272 $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable($tableName);
273 $queryBuilder->getRestrictions()
274 ->removeAll()
275 ->add(GeneralUtility::makeInstance(DeletedRestriction::class))
276 ->add(GeneralUtility::makeInstance(BackendWorkspaceRestriction::class));
277
278 return $queryBuilder;
279 }
280 }