548acdc2bd2c57dbbf3322268351e012b57f136c
[Packages/TYPO3.CMS.git] / typo3 / sysext / backend / Classes / Controller / Page / LocalizationController.php
1 <?php
2 namespace TYPO3\CMS\Backend\Controller\Page;
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 Psr\Http\Message\ResponseInterface;
18 use Psr\Http\Message\ServerRequestInterface;
19 use TYPO3\CMS\Backend\Configuration\TranslationConfigurationProvider;
20 use TYPO3\CMS\Backend\Domain\Repository\Localization\LocalizationRepository;
21 use TYPO3\CMS\Backend\Utility\BackendUtility;
22 use TYPO3\CMS\Core\DataHandling\DataHandler;
23 use TYPO3\CMS\Core\Http\JsonResponse;
24 use TYPO3\CMS\Core\Imaging\Icon;
25 use TYPO3\CMS\Core\Imaging\IconFactory;
26 use TYPO3\CMS\Core\Utility\GeneralUtility;
27 use TYPO3\CMS\Core\Versioning\VersionState;
28
29 /**
30 * LocalizationController handles the AJAX requests for record localization
31 */
32 class LocalizationController
33 {
34 /**
35 * @var string
36 */
37 const ACTION_COPY = 'copyFromLanguage';
38
39 /**
40 * @var string
41 */
42 const ACTION_LOCALIZE = 'localize';
43
44 /**
45 * @var IconFactory
46 */
47 protected $iconFactory;
48
49 /**
50 * @var LocalizationRepository
51 */
52 protected $localizationRepository;
53
54 /**
55 * Constructor
56 */
57 public function __construct()
58 {
59 $this->iconFactory = GeneralUtility::makeInstance(IconFactory::class);
60 $this->localizationRepository = GeneralUtility::makeInstance(LocalizationRepository::class);
61 }
62
63 /**
64 * Get used languages in a colPos of a page
65 *
66 * @param ServerRequestInterface $request
67 * @param ResponseInterface $response
68 * @return ResponseInterface
69 */
70 public function getUsedLanguagesInPageAndColumn(ServerRequestInterface $request, ResponseInterface $response)
71 {
72 $params = $request->getQueryParams();
73 if (!isset($params['pageId'], $params['colPos'], $params['languageId'])) {
74 $response = $response->withStatus(400);
75 return $response;
76 }
77
78 $pageId = (int)$params['pageId'];
79 $colPos = (int)$params['colPos'];
80 $languageId = (int)$params['languageId'];
81
82 /** @var TranslationConfigurationProvider $translationProvider */
83 $translationProvider = GeneralUtility::makeInstance(TranslationConfigurationProvider::class);
84 $systemLanguages = $translationProvider->getSystemLanguages($pageId);
85
86 $availableLanguages = [];
87
88 // First check whether column has localized records
89 $elementsInColumnCount = $this->localizationRepository->getLocalizedRecordCount($pageId, $colPos, $languageId);
90
91 if ($elementsInColumnCount === 0) {
92 $fetchedAvailableLanguages = $this->localizationRepository->fetchAvailableLanguages($pageId, $colPos, $languageId);
93 $availableLanguages[] = $systemLanguages[0];
94
95 foreach ($fetchedAvailableLanguages as $language) {
96 if (isset($systemLanguages[$language['uid']])) {
97 $availableLanguages[] = $systemLanguages[$language['uid']];
98 }
99 }
100 } else {
101 $result = $this->localizationRepository->fetchOriginLanguage($pageId, $colPos, $languageId);
102 $availableLanguages[] = $systemLanguages[$result['sys_language_uid']];
103 }
104
105 // Pre-render all flag icons
106 foreach ($availableLanguages as &$language) {
107 if ($language['flagIcon'] === 'empty-empty') {
108 $language['flagIcon'] = '';
109 } else {
110 $language['flagIcon'] = $this->iconFactory->getIcon($language['flagIcon'], Icon::SIZE_SMALL)->render();
111 }
112 }
113
114 return GeneralUtility::makeInstance(JsonResponse::class)->setPayload($availableLanguages);
115 }
116
117 /**
118 * Get a prepared summary of records being translated
119 *
120 * @param ServerRequestInterface $request
121 * @param ResponseInterface $response
122 * @return ResponseInterface
123 */
124 public function getRecordLocalizeSummary(ServerRequestInterface $request, ResponseInterface $response)
125 {
126 $params = $request->getQueryParams();
127 if (!isset($params['pageId'], $params['colPos'], $params['destLanguageId'], $params['languageId'])) {
128 $response = $response->withStatus(400);
129 return $response;
130 }
131
132 $records = [];
133 $result = $this->localizationRepository->getRecordsToCopyDatabaseResult(
134 $params['pageId'],
135 $params['colPos'],
136 $params['destLanguageId'],
137 $params['languageId'],
138 '*'
139 );
140
141 while ($row = $result->fetch()) {
142 BackendUtility::workspaceOL('tt_content', $row, -99, true);
143 if (!$row || VersionState::cast($row['t3ver_state'])->equals(VersionState::DELETE_PLACEHOLDER)) {
144 continue;
145 }
146 $records[] = [
147 'icon' => $this->iconFactory->getIconForRecord('tt_content', $row, Icon::SIZE_SMALL)->render(),
148 'title' => $row[$GLOBALS['TCA']['tt_content']['ctrl']['label']],
149 'uid' => $row['uid']
150 ];
151 }
152
153 return GeneralUtility::makeInstance(JsonResponse::class)->setPayload($records);
154 }
155
156 /**
157 * @param ServerRequestInterface $request
158 * @param ResponseInterface $response
159 * @return ResponseInterface
160 */
161 public function localizeRecords(ServerRequestInterface $request, ResponseInterface $response)
162 {
163 $params = $request->getQueryParams();
164 if (!isset($params['pageId'], $params['srcLanguageId'], $params['destLanguageId'], $params['action'], $params['uidList'])) {
165 $response = $response->withStatus(400);
166 return $response;
167 }
168
169 if ($params['action'] !== static::ACTION_COPY && $params['action'] !== static::ACTION_LOCALIZE) {
170 $response->getBody()->write('Invalid action "' . $params['action'] . '" called.');
171 $response = $response->withStatus(400);
172 return $response;
173 }
174
175 // Filter transmitted but invalid uids
176 $params['uidList'] = $this->filterInvalidUids(
177 (int)$params['pageId'],
178 (int)$params['colPos'],
179 (int)$params['destLanguageId'],
180 (int)$params['srcLanguageId'],
181 $params['uidList']
182 );
183
184 $this->process($params);
185
186 return GeneralUtility::makeInstance(JsonResponse::class)->setPayload([]);
187 }
188
189 /**
190 * Gets all possible UIDs of a page, colPos and language that might be processed and removes invalid UIDs that might
191 * be smuggled in.
192 *
193 * @param int $pageId
194 * @param int $colPos
195 * @param int $destLanguageId
196 * @param int $srcLanguageId
197 * @param array $transmittedUidList
198 * @return array
199 */
200 protected function filterInvalidUids(
201 int $pageId,
202 int $colPos,
203 int $destLanguageId,
204 int $srcLanguageId,
205 array $transmittedUidList
206 ): array {
207 // Get all valid uids that can be processed
208 $validUidList = $result = $this->localizationRepository->getRecordsToCopyDatabaseResult(
209 $pageId,
210 $colPos,
211 $destLanguageId,
212 $srcLanguageId,
213 'uid'
214 );
215
216 return array_intersect(array_unique($transmittedUidList), array_column($validUidList->fetchAll(), 'uid'));
217 }
218
219 /**
220 * Processes the localization actions
221 *
222 * @param array $params
223 */
224 protected function process($params)
225 {
226 $destLanguageId = (int)$params['destLanguageId'];
227
228 // Build command map
229 $cmd = [
230 'tt_content' => []
231 ];
232
233 if (isset($params['uidList']) && is_array($params['uidList'])) {
234 foreach ($params['uidList'] as $currentUid) {
235 if ($params['action'] === static::ACTION_LOCALIZE) {
236 $cmd['tt_content'][$currentUid] = [
237 'localize' => $destLanguageId
238 ];
239 } else {
240 $cmd['tt_content'][$currentUid] = [
241 'copyToLanguage' => $destLanguageId,
242 ];
243 }
244 }
245 }
246
247 $dataHandler = GeneralUtility::makeInstance(DataHandler::class);
248 $dataHandler->start([], $cmd);
249 $dataHandler->process_cmdmap();
250 }
251 }