7fb415e978e72db32c7a3b4fb134946576f1c86c
[Packages/TYPO3.CMS.git] / typo3 / sysext / backend / Classes / Form / Element / GroupElement.php
1 <?php
2 namespace TYPO3\CMS\Backend\Form\Element;
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 use TYPO3\CMS\Backend\Utility\IconUtility;
19 use TYPO3\CMS\Core\Messaging\FlashMessage;
20 use TYPO3\CMS\Core\Messaging\FlashMessageService;
21 use TYPO3\CMS\Core\Resource\ProcessedFile;
22 use TYPO3\CMS\Core\Resource\ResourceFactory;
23 use TYPO3\CMS\Core\Utility\GeneralUtility;
24 use TYPO3\CMS\Core\Utility\MathUtility;
25 use TYPO3\CMS\Core\Authentication\BackendUserAuthentication;
26
27 /**
28 * Generation of TCEform elements of the type "group"
29 */
30 class GroupElement extends AbstractFormElement {
31
32 /**
33 * This will render a selector box into which elements from either
34 * the file system or database can be inserted. Relations.
35 *
36 * @return array As defined in initializeResultArray() of AbstractNode
37 */
38 public function render() {
39 $table = $this->data['tableName'];
40 $fieldName = $this->data['fieldName'];
41 $row = $this->data['databaseRow'];
42 $parameterArray = $this->data['parameterArray'];
43 $config = $parameterArray['fieldConf']['config'];
44 $show_thumbs = $config['show_thumbs'];
45 $resultArray = $this->initializeResultArray();
46
47 $size = isset($config['size']) ? (int)$config['size'] : $this->minimumInputWidth;
48 $maxitems = MathUtility::forceIntegerInRange($config['maxitems'], 0);
49 if (!$maxitems) {
50 $maxitems = 100000;
51 }
52 $minitems = MathUtility::forceIntegerInRange($config['minitems'], 0);
53 $thumbnails = array();
54 $allowed = GeneralUtility::trimExplode(',', $config['allowed'], TRUE);
55 $disallowed = GeneralUtility::trimExplode(',', $config['disallowed'], TRUE);
56 $disabled = $config['readOnly'];
57 $info = array();
58 $parameterArray['itemFormElID_file'] = $parameterArray['itemFormElID'] . '_files';
59
60 // whether the list and delete controls should be disabled
61 $noList = isset($config['disable_controls']) && GeneralUtility::inList($config['disable_controls'], 'list');
62 $noDelete = isset($config['disable_controls']) && GeneralUtility::inList($config['disable_controls'], 'delete');
63
64 // "Extra" configuration; Returns configuration for the field based on settings found in the "types" fieldlist.
65 $specConf = BackendUtility::getSpecConfParts($parameterArray['fieldConf']['defaultExtras']);
66
67 // Register properties in required elements / validation
68 $attributes['data-formengine-validation-rules'] = htmlspecialchars(
69 $this->getValidationDataAsJsonString(
70 array(
71 'minitems' => $minitems,
72 'maxitems' => $maxitems
73 )
74 )
75 );
76
77 // If maxitems==1 then automatically replace the current item (in list and file selector)
78 if ($maxitems === 1) {
79 $resultArray['additionalJavaScriptPost'][] =
80 'TBE_EDITOR.clearBeforeSettingFormValueFromBrowseWin[' . GeneralUtility::quoteJSvalue($parameterArray['itemFormElName']) . '] = {
81 itemFormElID_file: ' . GeneralUtility::quoteJSvalue($parameterArray['itemFormElID_file']) . '
82 }';
83 $parameterArray['fieldChangeFunc']['TBE_EDITOR_fieldChanged'] = 'setFormValueManipulate(' . GeneralUtility::quoteJSvalue($parameterArray['itemFormElName'])
84 . ', \'Remove\'); ' . $parameterArray['fieldChangeFunc']['TBE_EDITOR_fieldChanged'];
85 } elseif ($noList) {
86 // If the list controls have been removed and the maximum number is reached, remove the first entry to avoid "write once" field
87 $parameterArray['fieldChangeFunc']['TBE_EDITOR_fieldChanged'] = 'setFormValueManipulate(' . GeneralUtility::quoteJSvalue($parameterArray['itemFormElName'])
88 . ', \'RemoveFirstIfFull\', ' . GeneralUtility::quoteJSvalue($maxitems) . '); ' . $parameterArray['fieldChangeFunc']['TBE_EDITOR_fieldChanged'];
89 }
90
91 $html = '<input type="hidden" class="t3js-group-hidden-field" data-formengine-input-name="' . htmlspecialchars($parameterArray['itemFormElName']) . '" value="' . ($config['multiple'] ? 1 : 0) . '"' . $disabled . ' />';
92
93 // Define parameters for all types below
94 $commonParameters = array(
95 'size' => $size,
96 'dontShowMoveIcons' => isset($config['hideMoveIcons']) || $maxitems <= 1,
97 'autoSizeMax' => MathUtility::forceIntegerInRange($config['autoSizeMax'], 0),
98 'maxitems' => $maxitems,
99 'style' => isset($config['selectedListStyle'])
100 ? ' style="' . htmlspecialchars($config['selectedListStyle']) . '"'
101 : '',
102 'readOnly' => $disabled,
103 'noBrowser' => $noList || isset($config['disable_controls']) && GeneralUtility::inList($config['disable_controls'], 'browser'),
104 'noList' => $noList,
105 );
106
107 // Acting according to either "file" or "db" type:
108 switch ((string)$config['internal_type']) {
109 case 'file_reference':
110 $config['uploadfolder'] = '';
111 // Fall through
112 case 'file':
113 // Creating string showing allowed types:
114 if (empty($allowed)) {
115 $allowed = array('*');
116 }
117 // Making the array of file items:
118 $itemArray = GeneralUtility::trimExplode(',', $parameterArray['itemFormElValue'], TRUE);
119 $fileFactory = ResourceFactory::getInstance();
120 // Correct the filename for the FAL items
121 foreach ($itemArray as &$fileItem) {
122 list($fileUid, $fileLabel) = explode('|', $fileItem);
123 if (MathUtility::canBeInterpretedAsInteger($fileUid)) {
124 $fileObject = $fileFactory->getFileObject($fileUid);
125 $fileLabel = $fileObject->getName();
126 }
127 $fileItem = $fileUid . '|' . $fileLabel;
128 }
129 // Showing thumbnails:
130 if ($show_thumbs) {
131 foreach ($itemArray as $imgRead) {
132 $imgP = explode('|', $imgRead);
133 $imgPath = rawurldecode($imgP[0]);
134 // FAL icon production
135 if (MathUtility::canBeInterpretedAsInteger($imgP[0])) {
136 $fileObject = $fileFactory->getFileObject($imgP[0]);
137 if ($fileObject->isMissing()) {
138 $thumbnails[] = array(
139 'message' => \TYPO3\CMS\Core\Resource\Utility\BackendUtility::getFlashMessageForMissingFile($fileObject)->render()
140 );
141 } elseif (GeneralUtility::inList($GLOBALS['TYPO3_CONF_VARS']['GFX']['imagefile_ext'], $fileObject->getExtension())) {
142 $thumbnails[] = array(
143 'name' => htmlspecialchars($fileObject->getName()),
144 'image' => $fileObject->process(ProcessedFile::CONTEXT_IMAGEPREVIEW, array())->getPublicUrl(TRUE)
145 );
146 } else {
147 // Icon
148 $thumbnails[] = array(
149 'name' => htmlspecialchars($fileObject->getName()),
150 'image' => IconUtility::getSpriteIconForResource($fileObject, array('title' => $fileObject->getName()))
151 );
152 }
153 } else {
154 $rowCopy = array();
155 $rowCopy[$fieldName] = $imgPath;
156 try {
157 $thumbnails[] = array(
158 'name' => $imgPath,
159 'image' => BackendUtility::thumbCode(
160 $rowCopy,
161 $table,
162 $fieldName,
163 '',
164 '',
165 $config['uploadfolder'],
166 0,
167 ' align="middle"'
168 )
169 );
170 } catch (\Exception $exception) {
171 /** @var $flashMessage FlashMessage */
172 $message = $exception->getMessage();
173 $flashMessage = GeneralUtility::makeInstance(
174 FlashMessage::class,
175 htmlspecialchars($message), '', FlashMessage::ERROR, TRUE
176 );
177 /** @var $flashMessageService FlashMessageService */
178 $flashMessageService = GeneralUtility::makeInstance(FlashMessageService::class);
179 $defaultFlashMessageQueue = $flashMessageService->getMessageQueueByIdentifier();
180 $defaultFlashMessageQueue->enqueue($flashMessage);
181 $logMessage = $message . ' (' . $table . ':' . $row['uid'] . ')';
182 GeneralUtility::sysLog($logMessage, 'core', GeneralUtility::SYSLOG_SEVERITY_WARNING);
183 }
184 }
185 }
186 }
187 // Creating the element:
188 $params = array_merge($commonParameters, array(
189 'allowed' => $allowed,
190 'disallowed' => $disallowed,
191 'thumbnails' => $thumbnails,
192 'noDelete' => $noDelete
193 ));
194 $html .= $this->dbFileIcons(
195 $parameterArray['itemFormElName'],
196 'file',
197 implode(',', $allowed),
198 $itemArray,
199 '',
200 $params,
201 $parameterArray['onFocus'],
202 '',
203 '',
204 '',
205 $config);
206 if (!$disabled && !(isset($config['disable_controls']) && GeneralUtility::inList($config['disable_controls'], 'upload'))) {
207 // Adding the upload field:
208 $isDirectFileUploadEnabled = (bool)$this->getBackendUserAuthentication()->uc['edit_docModuleUpload'];
209 if ($isDirectFileUploadEnabled && $config['uploadfolder']) {
210 // Insert the multiple attribute to enable HTML5 multiple file upload
211 $multipleAttribute = '';
212 $multipleFilenameSuffix = '';
213 if (isset($config['maxitems']) && $config['maxitems'] > 1) {
214 $multipleAttribute = ' multiple="multiple"';
215 $multipleFilenameSuffix = '[]';
216 }
217 $html .= '
218 <div id="' . $parameterArray['itemFormElID_file'] . '">
219 <input type="file"' . $multipleAttribute . '
220 name="data_files' . $this->data['elementBaseName'] . $multipleFilenameSuffix . '"
221 size="35" onchange="' . implode('', $parameterArray['fieldChangeFunc']) . '"
222 />
223 </div>';
224 }
225 }
226 break;
227 case 'folder':
228 // If the element is of the internal type "folder":
229 // Array of folder items:
230 $itemArray = GeneralUtility::trimExplode(',', $parameterArray['itemFormElValue'], TRUE);
231 // Creating the element:
232 $params = $commonParameters;
233 $html .= $this->dbFileIcons(
234 $parameterArray['itemFormElName'],
235 'folder',
236 '',
237 $itemArray,
238 '',
239 $params,
240 $parameterArray['onFocus']
241 );
242 break;
243 case 'db':
244 // If the element is of the internal type "db":
245 // Creating string showing allowed types:
246 $onlySingleTableAllowed = FALSE;
247 $languageService = $this->getLanguageService();
248
249 $allowedTables = array();
250 if ($allowed[0] === '*') {
251 $allowedTables = array(
252 'name' => htmlspecialchars($languageService->sL('LLL:EXT:lang/locallang_core.xlf:labels.allTables'))
253 );
254 } elseif ($allowed) {
255 $onlySingleTableAllowed = count($allowed) === 1;
256 foreach ($allowed as $allowedTable) {
257 $allowedTables[] = array(
258 // @todo: access to globals!
259 'name' => htmlspecialchars($languageService->sL($GLOBALS['TCA'][$allowedTable]['ctrl']['title'])),
260 'icon' => IconUtility::getSpriteIconForRecord($allowedTable, array()),
261 'onClick' => 'setFormValueOpenBrowser(\'db\', ' . GeneralUtility::quoteJSvalue($parameterArray['itemFormElName'] . '|||' . $allowedTable) . '); return false;'
262 );
263 }
264 }
265 $perms_clause = $this->getBackendUserAuthentication()->getPagePermsClause(1);
266 $itemArray = array();
267
268 // Thumbnails:
269 // @todo: this is data processing - must be extracted
270 $temp_itemArray = GeneralUtility::trimExplode(',', $parameterArray['itemFormElValue'], TRUE);
271 foreach ($temp_itemArray as $dbRead) {
272 $recordParts = explode('|', $dbRead);
273 list($this_table, $this_uid) = BackendUtility::splitTable_Uid($recordParts[0]);
274 // For the case that no table was found and only a single table is defined to be allowed, use that one:
275 if (!$this_table && $onlySingleTableAllowed) {
276 $this_table = $allowed;
277 }
278 $itemArray[] = array('table' => $this_table, 'id' => $this_uid);
279 if (!$disabled && $show_thumbs) {
280 $rr = BackendUtility::getRecordWSOL($this_table, $this_uid);
281 $thumbnails[] = array(
282 'name' => BackendUtility::getRecordTitle($this_table, $rr, TRUE),
283 'image' => IconUtility::getSpriteIconForRecord($this_table, $rr),
284 'path' => BackendUtility::getRecordPath($rr['pid'], $perms_clause, 15),
285 'uid' => $rr['uid'],
286 'table' => $this_table
287 );
288 }
289 }
290 // Creating the element:
291 $params = array_merge($commonParameters, array(
292 'info' => $info,
293 'allowedTables' => $allowedTables,
294 'thumbnails' => $thumbnails,
295 ));
296 $html .= $this->dbFileIcons(
297 $parameterArray['itemFormElName'],
298 'db',
299 implode(',', $allowed),
300 $itemArray,
301 '',
302 $params,
303 $parameterArray['onFocus'],
304 $table,
305 $fieldName,
306 $row['uid'],
307 $config
308 );
309 break;
310 }
311 // Wizards:
312 if (!$disabled) {
313 $html = $this->renderWizards(
314 array($html),
315 $config['wizards'],
316 $table,
317 $row,
318 $fieldName,
319 $parameterArray,
320 $parameterArray['itemFormElName'],
321 $specConf
322 );
323 }
324 $resultArray['html'] = $html;
325 return $resultArray;
326 }
327
328 /**
329 * @return BackendUserAuthentication
330 */
331 protected function getBackendUserAuthentication() {
332 return $GLOBALS['BE_USER'];
333 }
334
335 }