[BUGFIX] Ensure correct sorting when using the localization wizard
[Packages/TYPO3.CMS.git] / typo3 / sysext / backend / Classes / Domain / Repository / Localization / LocalizationRepository.php
1 <?php
2 namespace TYPO3\CMS\Backend\Domain\Repository\Localization;
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 use TYPO3\CMS\Backend\Utility\BackendUtility;
18
19 /**
20 * Repository for record localizations
21 */
22 class LocalizationRepository
23 {
24 /**
25 * Fetch the language from which the records of a colPos in a certain language were initially localized
26 *
27 * @param int $pageId
28 * @param int $colPos
29 * @param int $localizedLanguage
30 * @return array|false|null
31 */
32 public function fetchOriginLanguage($pageId, $colPos, $localizedLanguage)
33 {
34 $record = $this->getDatabaseConnection()->exec_SELECTgetSingleRow(
35 'tt_content_orig.sys_language_uid',
36 'tt_content,tt_content AS tt_content_orig',
37 'tt_content.colPos = ' . (int)$colPos
38 . ' AND tt_content.pid = ' . (int)$pageId
39 . ' AND tt_content.sys_language_uid = ' . (int)$localizedLanguage
40 . ' AND tt_content.t3_origuid = tt_content_orig.uid'
41 . $this->getExcludeQueryPart()
42 . $this->getAllowedLanguagesForBackendUser(),
43 'tt_content_orig.sys_language_uid'
44 );
45
46 return $record;
47 }
48
49 /**
50 * @param $pageId
51 * @param $colPos
52 * @param $languageId
53 * @return int
54 */
55 public function getLocalizedRecordCount($pageId, $colPos, $languageId)
56 {
57 $rows = (int)$this->getDatabaseConnection()->exec_SELECTcountRows(
58 'uid',
59 'tt_content',
60 'tt_content.sys_language_uid=' . (int)$languageId
61 . ' AND tt_content.colPos = ' . (int)$colPos
62 . ' AND tt_content.pid=' . (int)$pageId
63 . ' AND tt_content.t3_origuid <> 0'
64 . $this->getExcludeQueryPart()
65 );
66
67 return $rows;
68 }
69
70 /**
71 * Fetch all available languages
72 *
73 * @param int $pageId
74 * @param int $colPos
75 * @param int $languageId
76 * @return array
77 */
78 public function fetchAvailableLanguages($pageId, $colPos, $languageId)
79 {
80 $result = $this->getDatabaseConnection()->exec_SELECTgetRows(
81 'sys_language.uid',
82 'tt_content,sys_language',
83 'tt_content.sys_language_uid=sys_language.uid'
84 . ' AND tt_content.colPos = ' . (int)$colPos
85 . ' AND tt_content.pid = ' . (int)$pageId
86 . ' AND sys_language.uid <> ' . (int)$languageId
87 . $this->getExcludeQueryPart()
88 . $this->getAllowedLanguagesForBackendUser(),
89 'sys_language.uid',
90 'sys_language.title'
91 );
92
93 return $result;
94 }
95
96 /**
97 * Builds an additional where clause to exclude deleted records and setting the versioning placeholders
98 *
99 * @return string
100 */
101 public function getExcludeQueryPart()
102 {
103 return BackendUtility::deleteClause('tt_content')
104 . BackendUtility::versioningPlaceholderClause('tt_content');
105 }
106
107 /**
108 * Builds an additional where clause to exclude hidden languages and limit a backend user to its allowed languages,
109 * if the user is not an admin.
110 *
111 * @return string
112 */
113 public function getAllowedLanguagesForBackendUser()
114 {
115 $backendUser = $this->getBackendUser();
116 $additionalWhere = '';
117 if (!$backendUser->isAdmin()) {
118 $additionalWhere .= ' AND sys_language.hidden=0';
119
120 if (!empty($backendUser->user['allowed_languages'])) {
121 $additionalWhere .= ' AND sys_language.uid IN(' . $this->getDatabaseConnection()->cleanIntList($backendUser->user['allowed_languages']) . ')';
122 }
123 }
124
125 return $additionalWhere;
126 }
127
128 /**
129 * Get records for copy process
130 *
131 * @param int $pageId
132 * @param int $colPos
133 * @param int $destLanguageId
134 * @param int $languageId
135 * @param string $fields
136 * @return bool|\mysqli_result|object
137 */
138 public function getRecordsToCopyDatabaseResult($pageId, $colPos, $destLanguageId, $languageId, $fields = '*')
139 {
140 $db = $this->getDatabaseConnection();
141
142 // Get original uid of existing elements triggered language / colpos
143 $originalUids = $db->exec_SELECTgetRows(
144 't3_origuid',
145 'tt_content',
146 'sys_language_uid=' . (int)$destLanguageId
147 . ' AND tt_content.colPos = ' . (int)$colPos
148 . ' AND tt_content.pid=' . (int)$pageId
149 . $this->getExcludeQueryPart(),
150 '',
151 '',
152 '',
153 't3_origuid'
154 );
155 $originalUidList = $db->cleanIntList(implode(',', array_keys($originalUids)));
156
157 $res = $db->exec_SELECTquery(
158 $fields,
159 'tt_content',
160 'tt_content.sys_language_uid=' . (int)$languageId
161 . ' AND tt_content.colPos = ' . (int)$colPos
162 . ' AND tt_content.pid=' . (int)$pageId
163 . ' AND tt_content.uid NOT IN (' . $originalUidList . ')'
164 . $this->getExcludeQueryPart(),
165 '',
166 'tt_content.sorting'
167 );
168
169 return $res;
170 }
171
172 /**
173 * Fetches the localization for a given record.
174 *
175 * @FIXME: This method is a clone of BackendUtility::getRecordLocalization, using origUid instead of transOrigPointerField
176 *
177 * @param string $table Table name present in $GLOBALS['TCA']
178 * @param int $uid The uid of the record
179 * @param int $language The uid of the language record in sys_language
180 * @param string $andWhereClause Optional additional WHERE clause (default: '')
181 * @return mixed Multidimensional array with selected records; if none exist, FALSE is returned
182 */
183 public function getRecordLocalization($table, $uid, $language, $andWhereClause = '')
184 {
185 $recordLocalization = false;
186
187 // Check if translations are stored in other table
188 if (isset($GLOBALS['TCA'][$table]['ctrl']['transForeignTable'])) {
189 $table = $GLOBALS['TCA'][$table]['ctrl']['transForeignTable'];
190 }
191
192 if (BackendUtility::isTableLocalizable($table)) {
193 $tcaCtrl = $GLOBALS['TCA'][$table]['ctrl'];
194
195 if (isset($tcaCtrl['origUid'])) {
196 $recordLocalization = BackendUtility::getRecordsByField(
197 $table,
198 $tcaCtrl['origUid'],
199 $uid,
200 'AND ' . $tcaCtrl['languageField'] . '=' . (int)$language . ($andWhereClause ? ' ' . $andWhereClause : ''),
201 '',
202 '',
203 '1'
204 );
205 }
206 }
207 return $recordLocalization;
208 }
209
210 /**
211 * Returning uid of previous localized record, if any, for tables with a "sortby" column
212 * Used when new localized records are created so that localized records are sorted in the same order as the default language records
213 *
214 * @FIXME: This method is a clone of DataHandler::getPreviousLocalizedRecordUid which is protected there and uses
215 * BackendUtility::getRecordLocalization which we also needed to clone in this class. Also, this method takes two
216 * language arguments.
217 *
218 * @param string $table Table name
219 * @param int $uid Uid of default language record
220 * @param int $pid Pid of default language record
221 * @param int $sourceLanguage Language of origin
222 * @param int $destinationLanguage Language of localization
223 * @return int uid of record after which the localized record should be inserted
224 */
225 public function getPreviousLocalizedRecordUid($table, $uid, $pid, $sourceLanguage, $destinationLanguage)
226 {
227 $previousLocalizedRecordUid = $uid;
228 if ($GLOBALS['TCA'][$table] && $GLOBALS['TCA'][$table]['ctrl']['sortby']) {
229 $sortRow = $GLOBALS['TCA'][$table]['ctrl']['sortby'];
230 $select = $sortRow . ',pid,uid';
231 // For content elements, we also need the colPos
232 if ($table === 'tt_content') {
233 $select .= ',colPos';
234 }
235 // Get the sort value of the default language record
236 $row = BackendUtility::getRecord($table, $uid, $select);
237 if (is_array($row)) {
238 // Find the previous record in default language on the same page
239 $where = 'pid=' . (int)$pid . ' AND ' . 'sys_language_uid=' . (int)$sourceLanguage . ' AND ' . $sortRow . '<' . (int)$row[$sortRow];
240 // Respect the colPos for content elements
241 if ($table === 'tt_content') {
242 $where .= ' AND colPos=' . (int)$row['colPos'];
243 }
244 $res = $this->getDatabaseConnection()->exec_SELECTquery(
245 $select,
246 $table,
247 $where . BackendUtility::deleteClause($table),
248 '',
249 $sortRow . ' DESC',
250 '1'
251 );
252 // If there is an element, find its localized record in specified localization language
253 if ($previousRow = $this->getDatabaseConnection()->sql_fetch_assoc($res)) {
254 $previousLocalizedRecord = $this->getRecordLocalization($table, $previousRow['uid'], $destinationLanguage);
255 if (is_array($previousLocalizedRecord[0])) {
256 $previousLocalizedRecordUid = $previousLocalizedRecord[0]['uid'];
257 }
258 }
259 $this->getDatabaseConnection()->sql_free_result($res);
260 }
261 }
262 return $previousLocalizedRecordUid;
263 }
264
265 /**
266 * Returns the current BE user.
267 *
268 * @return \TYPO3\CMS\Core\Authentication\BackendUserAuthentication
269 */
270 protected function getBackendUser()
271 {
272 return $GLOBALS['BE_USER'];
273 }
274
275 /**
276 * Returns the database connection
277 *
278 * @return \TYPO3\CMS\Core\Database\DatabaseConnection
279 */
280 protected function getDatabaseConnection()
281 {
282 return $GLOBALS['TYPO3_DB'];
283 }
284 }