[TASK] Call explicit render() on icon objects
[Packages/TYPO3.CMS.git] / typo3 / sysext / setup / Classes / Controller / SetupModuleController.php
1 <?php
2 namespace TYPO3\CMS\Setup\Controller;
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\Backend\Avatar\DefaultAvatarProvider;
20 use TYPO3\CMS\Backend\Utility\BackendUtility;
21 use TYPO3\CMS\Core\Imaging\Icon;
22 use TYPO3\CMS\Core\Imaging\IconFactory;
23 use TYPO3\CMS\Core\Authentication\BackendUserAuthentication;
24 use TYPO3\CMS\Core\Database\DatabaseConnection;
25 use TYPO3\CMS\Core\DataHandling\DataHandler;
26 use TYPO3\CMS\Core\FormProtection\FormProtectionFactory;
27 use TYPO3\CMS\Core\Messaging\FlashMessage;
28 use TYPO3\CMS\Core\Resource\Exception\FileDoesNotExistException;
29 use TYPO3\CMS\Core\Resource\ResourceFactory;
30 use TYPO3\CMS\Core\Utility\GeneralUtility;
31
32 /**
33 * Script class for the Setup module
34 */
35 class SetupModuleController {
36
37 const PASSWORD_NOT_UPDATED = 0;
38 const PASSWORD_UPDATED = 1;
39 const PASSWORD_NOT_THE_SAME = 2;
40 const PASSWORD_OLD_WRONG = 3;
41
42 /**
43 * @var array
44 */
45 public $MOD_MENU = array();
46
47 /**
48 * @var array
49 */
50 public $MOD_SETTINGS = array();
51
52 /**
53 * @var \TYPO3\CMS\Backend\Template\DocumentTemplate
54 */
55 public $doc;
56
57 /**
58 * @var string
59 */
60 public $content;
61
62 /**
63 * @var array
64 */
65 public $overrideConf;
66
67 /**
68 * backend user object, set during simulate-user operation
69 *
70 * @var \TYPO3\CMS\Core\Authentication\BackendUserAuthentication
71 */
72 public $OLD_BE_USER;
73
74 /**
75 * @var bool
76 */
77 public $languageUpdate;
78
79 /**
80 * @var bool
81 */
82 protected $pagetreeNeedsRefresh = FALSE;
83
84 /**
85 * @var bool
86 */
87 protected $isAdmin;
88
89 /**
90 * @var array
91 */
92 protected $tsFieldConf;
93
94 /**
95 * @var bool
96 */
97 protected $saveData = FALSE;
98
99 /**
100 * @var int
101 */
102 protected $passwordIsUpdated = self::PASSWORD_NOT_UPDATED;
103
104 /**
105 * @var bool
106 */
107 protected $passwordIsSubmitted = FALSE;
108
109 /**
110 * @var bool
111 */
112 protected $setupIsUpdated = FALSE;
113
114 /**
115 * @var bool
116 */
117 protected $tempDataIsCleared = FALSE;
118
119 /**
120 * @var bool
121 */
122 protected $settingsAreResetToDefault = FALSE;
123
124 /**
125 * Form protection instance
126 *
127 * @var \TYPO3\CMS\Core\FormProtection\BackendFormProtection
128 */
129 protected $formProtection;
130
131 /**
132 * @var string
133 */
134 protected $simulateSelector = '';
135
136 /**
137 * @var string
138 */
139 protected $simUser = '';
140
141 /**
142 * The name of the module
143 *
144 * @var string
145 */
146 protected $moduleName = 'user_setup';
147
148 /**
149 * Instantiate the form protection before a simulated user is initialized.
150 */
151 public function __construct() {
152 $this->formProtection = FormProtectionFactory::get();
153 }
154
155 /**
156 * Getter for the form protection instance.
157 *
158 * @return \TYPO3\CMS\Core\FormProtection\BackendFormProtection
159 */
160 public function getFormProtection() {
161 return $this->formProtection;
162 }
163
164 /**
165 * If settings are submitted to _POST[DATA], store them
166 * NOTICE: This method is called before the \TYPO3\CMS\Backend\Template\DocumentTemplate
167 * is included. See bottom of document.
168 *
169 * @see \TYPO3\CMS\Backend\Template\DocumentTemplate
170 */
171 public function storeIncomingData() {
172 // First check if something is submitted in the data-array from POST vars
173 $d = GeneralUtility::_POST('data');
174 $columns = $GLOBALS['TYPO3_USER_SETTINGS']['columns'];
175 $beUser = $this->getBackendUser();
176 $beUserId = $beUser->user['uid'];
177 $storeRec = array();
178 $fieldList = $this->getFieldsFromShowItem();
179 if (is_array($d) && $this->formProtection->validateToken((string)GeneralUtility::_POST('formToken'), 'BE user setup', 'edit')) {
180 // UC hashed before applying changes
181 $save_before = md5(serialize($beUser->uc));
182 // PUT SETTINGS into the ->uc array:
183 // Reload left frame when switching BE language
184 if (isset($d['lang']) && $d['lang'] != $beUser->uc['lang']) {
185 $this->languageUpdate = TRUE;
186 }
187 // Reload pagetree if the title length is changed
188 if (isset($d['titleLen']) && $d['titleLen'] !== $beUser->uc['titleLen']) {
189 $this->pagetreeNeedsRefresh = TRUE;
190 }
191 if ($d['setValuesToDefault']) {
192 // If every value should be default
193 $beUser->resetUC();
194 $this->settingsAreResetToDefault = TRUE;
195 } elseif ($d['clearSessionVars']) {
196 foreach ($beUser->uc as $key => $value) {
197 if (!isset($columns[$key])) {
198 unset($beUser->uc[$key]);
199 }
200 }
201 $this->tempDataIsCleared = TRUE;
202 } elseif ($d['save']) {
203 // Save all submitted values if they are no array (arrays are with table=be_users) and exists in $GLOBALS['TYPO3_USER_SETTINGS'][columns]
204 foreach ($columns as $field => $config) {
205 if (!in_array($field, $fieldList)) {
206 continue;
207 }
208 if ($config['table']) {
209 if ($config['table'] === 'be_users' && !in_array($field, array('password', 'password2', 'passwordCurrent', 'email', 'realName', 'admin', 'avatar'))) {
210 if (!isset($config['access']) || $this->checkAccess($config) && $beUser->user[$field] !== $d['be_users'][$field]) {
211 if ($config['type'] === 'check') {
212 $fieldValue = isset($d['be_users'][$field]) ? 1 : 0;
213 } else {
214 $fieldValue = $d['be_users'][$field];
215 }
216 $storeRec['be_users'][$beUserId][$field] = $fieldValue;
217 $beUser->user[$field] = $fieldValue;
218 }
219 }
220 }
221 if ($config['type'] === 'check') {
222 $beUser->uc[$field] = isset($d[$field]) ? 1 : 0;
223 } else {
224 $beUser->uc[$field] = htmlspecialchars($d[$field]);
225 }
226 }
227 // Personal data for the users be_user-record (email, name, password...)
228 // If email and name is changed, set it in the users record:
229 $be_user_data = $d['be_users'];
230 // Possibility to modify the transmitted values. Useful to do transformations, like RSA password decryption
231 if (is_array($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['ext/setup/mod/index.php']['modifyUserDataBeforeSave'])) {
232 foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['ext/setup/mod/index.php']['modifyUserDataBeforeSave'] as $function) {
233 $params = array('be_user_data' => &$be_user_data);
234 GeneralUtility::callUserFunction($function, $params, $this);
235 }
236 }
237 $this->passwordIsSubmitted = (string)$be_user_data['password'] !== '';
238 $passwordIsConfirmed = $this->passwordIsSubmitted && $be_user_data['password'] === $be_user_data['password2'];
239 // Update the real name:
240 if ($be_user_data['realName'] !== $beUser->user['realName']) {
241 $beUser->user['realName'] = ($storeRec['be_users'][$beUserId]['realName'] = substr($be_user_data['realName'], 0, 80));
242 }
243 // Update the email address:
244 if ($be_user_data['email'] !== $beUser->user['email']) {
245 $beUser->user['email'] = ($storeRec['be_users'][$beUserId]['email'] = substr($be_user_data['email'], 0, 80));
246 }
247 // Update the password:
248 if ($passwordIsConfirmed) {
249 $currentPasswordHashed = $GLOBALS['BE_USER']->user['password'];
250 $saltFactory = \TYPO3\CMS\Saltedpasswords\Salt\SaltFactory::getSaltingInstance($currentPasswordHashed);
251 if ($saltFactory->checkPassword($be_user_data['passwordCurrent'], $currentPasswordHashed)) {
252 $this->passwordIsUpdated = self::PASSWORD_UPDATED;
253 $storeRec['be_users'][$beUserId]['password'] = $be_user_data['password'];
254 } else {
255 $this->passwordIsUpdated = self::PASSWORD_OLD_WRONG;
256 }
257 } else {
258 $this->passwordIsUpdated = self::PASSWORD_NOT_THE_SAME;
259 }
260
261 $this->setAvatarFileUid($beUserId, $be_user_data['avatar'], $storeRec);
262
263 $this->saveData = TRUE;
264 }
265 // Inserts the overriding values.
266 $beUser->overrideUC();
267 $save_after = md5(serialize($beUser->uc));
268 // If something in the uc-array of the user has changed, we save the array...
269 if ($save_before != $save_after) {
270 $beUser->writeUC($beUser->uc);
271 $beUser->writelog(254, 1, 0, 1, 'Personal settings changed', array());
272 $this->setupIsUpdated = TRUE;
273 }
274 // If the temporary data has been cleared, lets make a log note about it
275 if ($this->tempDataIsCleared) {
276 $beUser->writelog(254, 1, 0, 1, $this->getLanguageService()->getLL('tempDataClearedLog'), array());
277 }
278 // Persist data if something has changed:
279 if (!empty($storeRec) && $this->saveData) {
280 // Make instance of TCE for storing the changes.
281 /** @var DataHandler $dataHandler */
282 $dataHandler = GeneralUtility::makeInstance(DataHandler::class);
283 $dataHandler->stripslashes_values = FALSE;
284 // This is so the user can actually update his user record.
285 $isAdmin = $beUser->user['admin'];
286 $beUser->user['admin'] = 1;
287 $dataHandler->start($storeRec, array(), $beUser);
288 // This is to make sure that the users record can be updated even if in another workspace. This is tolerated.
289 $dataHandler->bypassWorkspaceRestrictions = TRUE;
290 $dataHandler->process_datamap();
291 unset($tce);
292 if ($this->passwordIsUpdated === self::PASSWORD_NOT_UPDATED || count($storeRec['be_users'][$beUserId]) > 1) {
293 $this->setupIsUpdated = TRUE;
294 }
295 // Restore admin status after processing
296 $beUser->user['admin'] = $isAdmin;
297 }
298 }
299 }
300
301 /******************************
302 *
303 * Rendering module
304 *
305 ******************************/
306 /**
307 * Initializes the module for display of the settings form.
308 *
309 * @return void
310 */
311 public function init() {
312 $this->getLanguageService()->includeLLFile('EXT:setup/Resources/Private/Language/locallang.xlf');
313
314 // Returns the script user - that is the REAL logged in user! ($GLOBALS[BE_USER] might be another user due to simulation!)
315 $scriptUser = $this->getRealScriptUserObj();
316
317 $this->isAdmin = $scriptUser->isAdmin();
318 // Getting the 'override' values as set might be set in User TSconfig
319 $this->overrideConf = $this->getBackendUser()->getTSConfigProp('setup.override');
320 // Getting the disabled fields might be set in User TSconfig (eg setup.fields.password.disabled=1)
321 $this->tsFieldConf = $this->getBackendUser()->getTSConfigProp('setup.fields');
322 // id password is disabled, disable repeat of password too (password2)
323 if (isset($this->tsFieldConf['password.']) && $this->tsFieldConf['password.']['disabled']) {
324 $this->tsFieldConf['password2.']['disabled'] = 1;
325 $this->tsFieldConf['passwordCurrent.']['disabled'] = 1;
326 }
327 // Create instance of object for output of data
328 $this->doc = GeneralUtility::makeInstance(\TYPO3\CMS\Backend\Template\DocumentTemplate::class);
329 $this->doc->setModuleTemplate('EXT:setup/Resources/Private/Templates/setup.html');
330 $this->doc->form = '<form action="' . BackendUtility::getModuleUrl('user_setup') . '" method="post" name="usersetup" enctype="multipart/form-data">';
331 $this->doc->JScode .= $this->getJavaScript();
332 }
333
334 /**
335 * Generate necessary JavaScript
336 *
337 * @return string
338 */
339 protected function getJavaScript() {
340 $javaScript = '';
341 if (is_array($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['ext/setup/mod/index.php']['setupScriptHook'])) {
342 foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['ext/setup/mod/index.php']['setupScriptHook'] as $function) {
343 $params = array();
344 $javaScript .= GeneralUtility::callUserFunction($function, $params, $this);
345 }
346 }
347 return $javaScript;
348 }
349
350 /**
351 * Generate the main settings form:
352 *
353 * @return void
354 */
355 public function main() {
356 if ($this->languageUpdate) {
357 $this->doc->JScodeArray['languageUpdate'] .= '
358 if (top.refreshMenu) {
359 top.refreshMenu();
360 } else {
361 top.TYPO3ModuleMenu.refreshMenu();
362 }
363 ';
364 }
365 if ($this->pagetreeNeedsRefresh) {
366 BackendUtility::setUpdateSignal('updatePageTree');
367 }
368 // Start page:
369 $this->doc->loadJavascriptLib('sysext/backend/Resources/Public/JavaScript/md5.js');
370 // Use a wrapper div
371 $this->content .= '<div id="user-setup-wrapper">';
372 // Load available backend modules
373 $this->loadModules = GeneralUtility::makeInstance(\TYPO3\CMS\Backend\Module\ModuleLoader::class);
374 $this->loadModules->observeWorkspaces = TRUE;
375 $this->loadModules->load($GLOBALS['TBE_MODULES']);
376 $this->content .= $this->doc->header($this->getLanguageService()->getLL('UserSettings'));
377 // Show if setup was saved
378 if ($this->setupIsUpdated && !$this->tempDataIsCleared && !$this->settingsAreResetToDefault) {
379 $flashMessage = GeneralUtility::makeInstance(FlashMessage::class, $this->getLanguageService()->getLL('setupWasUpdated'), $this->getLanguageService()->getLL('UserSettings'));
380 $this->content .= $flashMessage->render();
381 }
382
383 // Show if temporary data was cleared
384 if ($this->tempDataIsCleared) {
385 $flashMessage = GeneralUtility::makeInstance(FlashMessage::class, $this->getLanguageService()->getLL('tempDataClearedFlashMessage'), $this->getLanguageService()->getLL('tempDataCleared'));
386 $this->content .= $flashMessage->render();
387 }
388
389 // Show if temporary data was cleared
390 if ($this->settingsAreResetToDefault) {
391 $flashMessage = GeneralUtility::makeInstance(FlashMessage::class, $this->getLanguageService()->getLL('settingsAreReset'), $this->getLanguageService()->getLL('resetConfiguration'));
392 $this->content .= $flashMessage->render();
393 }
394
395 // Notice
396 if ($this->setupIsUpdated || $this->settingsAreResetToDefault) {
397 $flashMessage = GeneralUtility::makeInstance(FlashMessage::class, $this->getLanguageService()->getLL('activateChanges'), '', FlashMessage::INFO);
398 $this->content .= $flashMessage->render();
399 }
400
401 // If password is updated, output whether it failed or was OK.
402 if ($this->passwordIsSubmitted) {
403 $flashMessage = NULL;
404 switch ($this->passwordIsUpdated) {
405 case self::PASSWORD_OLD_WRONG:
406 $flashMessage = GeneralUtility::makeInstance(FlashMessage::class, $this->getLanguageService()->getLL('oldPassword_failed'), $this->getLanguageService()->getLL('newPassword'), FlashMessage::ERROR);
407 break;
408 case self::PASSWORD_NOT_THE_SAME:
409 $flashMessage = GeneralUtility::makeInstance(FlashMessage::class, $this->getLanguageService()->getLL('newPassword_failed'), $this->getLanguageService()->getLL('newPassword'), FlashMessage::ERROR);
410 break;
411 case self::PASSWORD_UPDATED:
412 $flashMessage = GeneralUtility::makeInstance(FlashMessage::class, $this->getLanguageService()->getLL('newPassword_ok'), $this->getLanguageService()->getLL('newPassword'));
413 break;
414 }
415 if ($flashMessage) {
416 $this->content .= $flashMessage->render();
417 }
418 }
419
420 // Render user switch
421 $this->content .= $this->renderSimulateUserSelectAndLabel();
422
423 // Render the menu items
424 $menuItems = $this->renderUserSetup();
425 $this->content .= $this->doc->getDynamicTabMenu($menuItems, 'user-setup', 1, FALSE, FALSE);
426 $formToken = $this->formProtection->generateToken('BE user setup', 'edit');
427 $this->content .= $this->doc->section('', '<input type="hidden" name="simUser" value="' . $this->simUser . '" />
428 <input type="hidden" name="formToken" value="' . $formToken . '" />
429 <input type="hidden" value="1" name="data[save]" />
430 <input type="hidden" name="data[setValuesToDefault]" value="0" id="setValuesToDefault" />
431 <input type="hidden" name="data[clearSessionVars]" value="0" id="clearSessionVars" />');
432 // End of wrapper div
433 $this->content .= '</div>';
434 // Setting up the buttons and markers for docheader
435 $docHeaderButtons = $this->getButtons();
436 $markers['CSH'] = $docHeaderButtons['csh'];
437 $markers['CONTENT'] = $this->content;
438 // Build the <body> for the module
439 $this->content = $this->doc->moduleBody($this->pageinfo, $docHeaderButtons, $markers);
440 // Renders the module page
441 $this->content = $this->doc->render($this->getLanguageService()->getLL('UserSettings'), $this->content);
442 }
443
444
445 /**
446 * Injects the request object for the current request or subrequest
447 * Simply calls main() and init() and writes the content to the response
448 *
449 * @param ServerRequestInterface $request the current request
450 * @param ResponseInterface $response
451 * @return ResponseInterface the response with the content
452 */
453 public function mainAction(ServerRequestInterface $request, ResponseInterface $response) {
454 $GLOBALS['SOBE'] = $this;
455 $this->simulateUser();
456 $this->storeIncomingData();
457 $this->init();
458 $this->main();
459
460 $response->getBody()->write($this->content);
461 return $response;
462 }
463
464 /**
465 * Prints the content / ends page
466 *
467 * @return void
468 * @deprecated since TYPO3 CMS 7, will be removed in TYPO3 CMS 8
469 */
470 public function printContent() {
471 GeneralUtility::logDeprecatedFunction();
472 echo $this->content;
473 }
474
475 /**
476 * Create the panel of buttons for submitting the form or otherwise perform operations.
477 *
478 * @return array All available buttons as an assoc. array
479 */
480 protected function getButtons() {
481 /** @var IconFactory $iconFactory */
482 $iconFactory = GeneralUtility::makeInstance(IconFactory::class);
483 $buttons = array(
484 'csh' => '',
485 'save' => '',
486 'shortcut' => ''
487 );
488 $buttons['csh'] = BackendUtility::cshItem('_MOD_user_setup', '');
489 $buttons['save'] = '<button class="c-inputButton" name="data[save]" title="' . $this->getLanguageService()->sL('LLL:EXT:lang/locallang_core.xlf:rm.saveDoc', TRUE) . '">'
490 . $iconFactory->getIcon('actions-document-save', Icon::SIZE_SMALL)->render()
491 . '</button>';
492 if ($this->getBackendUser()->mayMakeShortcut()) {
493 $buttons['shortcut'] = $this->doc->makeShortcutIcon('', '', $this->moduleName);
494 }
495 return $buttons;
496 }
497
498 /******************************
499 *
500 * Render module
501 *
502 ******************************/
503
504 /**
505 * renders the data for all tabs in the user setup and returns
506 * everything that is needed with tabs and dyntab menu
507 *
508 * @return array Ready to use for the dyntabmenu itemarray
509 */
510 protected function renderUserSetup() {
511 $result = array();
512 $firstTabLabel = '';
513 $code = array();
514 $i = 0;
515 $fieldArray = $this->getFieldsFromShowItem();
516 $tabLabel = '';
517 foreach ($fieldArray as $fieldName) {
518 $more = '';
519 if (substr($fieldName, 0, 8) === '--div--;') {
520 if ($firstTabLabel === '') {
521 // First tab
522 $tabLabel = $this->getLabel(substr($fieldName, 8), '', FALSE);
523 $firstTabLabel = $tabLabel;
524 } else {
525 $result[] = array(
526 'label' => $tabLabel,
527 'content' => count($code) ? implode(LF, $code) : ''
528 );
529 $tabLabel = $this->getLabel(substr($fieldName, 8), '', FALSE);
530 $i = 0;
531 $code = array();
532 }
533 continue;
534 }
535 $config = $GLOBALS['TYPO3_USER_SETTINGS']['columns'][$fieldName];
536
537 // Field my be disabled in setup.fields
538 if (isset($this->tsFieldConf[$fieldName . '.']['disabled']) && $this->tsFieldConf[$fieldName . '.']['disabled'] == 1) {
539 continue;
540 }
541 if (isset($config['access']) && !$this->checkAccess($config)) {
542 continue;
543 }
544 $label = $this->getLabel($config['label'], $fieldName);
545 $label = $this->getCSH($config['csh'] ?: $fieldName, $label);
546 $type = $config['type'];
547 $class = $config['class'];
548
549 if ($type !== 'check') {
550 $class .= ' form-control';
551 }
552
553 $style = $config['style'];
554 if ($class) {
555 $more .= ' class="' . $class . '"';
556 }
557 if ($style) {
558 $more .= ' style="' . $style . '"';
559 }
560 if (isset($this->overrideConf[$fieldName])) {
561 $more .= ' disabled="disabled"';
562 }
563 $value = $config['table'] === 'be_users' ? $this->getBackendUser()->user[$fieldName] : $this->getBackendUser()->uc[$fieldName];
564 if (!$value && isset($config['default'])) {
565 $value = $config['default'];
566 }
567 $dataAdd = '';
568 if ($config['table'] === 'be_users') {
569 $dataAdd = '[be_users]';
570 }
571
572 switch ($type) {
573 case 'text':
574 case 'email':
575 case 'password':
576 $noAutocomplete = '';
577 if ($type === 'password') {
578 $value = '';
579 $noAutocomplete = 'autocomplete="off" ';
580 $more .= ' data-rsa-encryption=""';
581 }
582 $html = '<input id="field_' . $fieldName . '"
583 type="' . $type . '"
584 name="data' . $dataAdd . '[' . $fieldName . ']" ' .
585 $noAutocomplete .
586 'value="' . htmlspecialchars($value) . '" ' .
587 $more .
588 ' />';
589 break;
590 case 'check':
591 $html = $label . '<div class="checkbox"><label><input id="field_' . $fieldName . '"
592 type="checkbox"
593 name="data' . $dataAdd . '[' . $fieldName . ']"' .
594 ($value ? ' checked="checked"' : '') .
595 $more .
596 ' /></label></div>';
597 $label = '';
598 break;
599 case 'select':
600 if ($config['itemsProcFunc']) {
601 $html = GeneralUtility::callUserFunction($config['itemsProcFunc'], $config, $this, '');
602 } else {
603 $html = '<select id="field_' . $fieldName . '"
604 name="data' . $dataAdd . '[' . $fieldName . ']"' .
605 $more . '>' . LF;
606 foreach ($config['items'] as $key => $optionLabel) {
607 $html .= '<option value="' . $key . '"' . ($value == $key ? ' selected="selected"' : '') . '>' . $this->getLabel($optionLabel, '', FALSE) . '</option>' . LF;
608 }
609 $html .= '</select>';
610 }
611 break;
612 case 'user':
613 $html = GeneralUtility::callUserFunction($config['userFunc'], $config, $this, '');
614 break;
615 case 'button':
616 if ($config['onClick']) {
617 $onClick = $config['onClick'];
618 if ($config['onClickLabels']) {
619 foreach ($config['onClickLabels'] as $key => $labelclick) {
620 $config['onClickLabels'][$key] = $this->getLabel($labelclick, '', FALSE);
621 }
622 $onClick = vsprintf($onClick, $config['onClickLabels']);
623 }
624 $html = '<br><input class="btn btn-default" type="button"
625 value="' . $this->getLabel($config['buttonlabel'], '', FALSE) . '"
626 onclick="' . $onClick . '" />';
627 }
628 break;
629 case 'avatar':
630 // Get current avatar image
631 $html = '<br>';
632 $avatarFileUid = $this->getAvatarFileUid($this->getBackendUser()->user['uid']);
633
634 if ($avatarFileUid) {
635 $defaultAvatarProvider = GeneralUtility::makeInstance(DefaultAvatarProvider::class);
636 $avatarImage = $defaultAvatarProvider->getImage($this->getBackendUser()->user, 32);
637 if ($avatarImage) {
638 $icon = '<span class="avatar"><span class="avatar-image">' .
639 '<img src="' . htmlspecialchars($avatarImage->getUrl(TRUE)) . '"' .
640 'width="' . (int)$avatarImage->getWidth() . '" ' .
641 'height="' . (int)$avatarImage->getHeight() . '" />' .
642 '</span></span>';
643 $html .= '<span class="pull-left" style="padding-right: 10px" id="image_' . htmlspecialchars($fieldName) . '">' . $icon . ' </span>';
644 }
645 }
646 $html .= '<input id="field_' . htmlspecialchars($fieldName) . '" type="hidden" ' .
647 'name="data' . $dataAdd . '[' . htmlspecialchars($fieldName) . ']"' . $more .
648 ' value="' . $avatarFileUid . '" />';
649
650 $html .= '<div class="btn-group">';
651 if ($avatarFileUid) {
652 $html .= '<a id="clear_button_' . htmlspecialchars($fieldName) . '" onclick="clearExistingImage(); return false;" class="btn btn-default"><span class="t3-icon fa t3-icon fa fa-remove"> </span></a>';
653 }
654 $html .= '<a id="add_button_' . htmlspecialchars($fieldName) . '" class="btn btn-default btn-add-avatar" onclick="openFileBrowser();return false;"><span class="t3-icon t3-icon-actions t3-icon-actions-insert t3-icon-insert-record"> </span></a>' .
655 '</div>';
656
657 $this->addAvatarButtonJs($fieldName);
658 break;
659 default:
660 $html = '';
661 }
662
663 $code[] = '<div class="form-section"><div class="form-group">' .
664 $label .
665 $html .
666 '</div></div>';
667 }
668
669 $result[] = array(
670 'label' => $tabLabel,
671 'content' => count($code) ? implode(LF, $code) : ''
672 );
673 return $result;
674 }
675
676 /******************************
677 *
678 * Helper functions
679 *
680 ******************************/
681 /**
682 * Returns the backend user object, either the global OR the $this->OLD_BE_USER which is set during simulate-user operation.
683 * Anyway: The REAL user is returned - the one logged in.
684 *
685 * @return BackendUserAuthentication The REAL user is returned - the one logged in.
686 */
687 protected function getRealScriptUserObj() {
688 return is_object($this->OLD_BE_USER) ? $this->OLD_BE_USER : $this->getBackendUser();
689 }
690
691 /**
692 * Return a select with available languages
693 *
694 * @return string Complete select as HTML string or warning box if something went wrong.
695 */
696 public function renderLanguageSelect($params, $pObj) {
697 $languageOptions = array();
698 // Compile the languages dropdown
699 $langDefault = $this->getLanguageService()->getLL('lang_default', TRUE);
700 $languageOptions[$langDefault] = '<option value=""' . ($this->getBackendUser()->uc['lang'] === '' ? ' selected="selected"' : '') . '>' . $langDefault . '</option>';
701 // Traverse the number of languages
702 /** @var $locales \TYPO3\CMS\Core\Localization\Locales */
703 $locales = GeneralUtility::makeInstance(\TYPO3\CMS\Core\Localization\Locales::class);
704 $languages = $locales->getLanguages();
705 foreach ($languages as $locale => $name) {
706 if ($locale !== 'default') {
707 $defaultName = isset($GLOBALS['LOCAL_LANG']['default']['lang_' . $locale]) ? $GLOBALS['LOCAL_LANG']['default']['lang_' . $locale][0]['source'] : $name;
708 $localizedName = $this->getLanguageService()->getLL('lang_' . $locale, TRUE);
709 if ($localizedName === '') {
710 $localizedName = htmlspecialchars($name);
711 }
712 $localLabel = ' - [' . htmlspecialchars($defaultName) . ']';
713 $available = is_dir(PATH_typo3conf . 'l10n/' . $locale);
714 if ($available) {
715 $languageOptions[$defaultName] = '<option value="' . $locale . '"' . ($this->getBackendUser()->uc['lang'] === $locale ? ' selected="selected"' : '') . '>' . $localizedName . $localLabel . '</option>';
716 }
717 }
718 }
719 ksort($languageOptions);
720 $languageCode = '
721 <select id="field_lang" name="data[lang]" class="form-control">' . implode('', $languageOptions) . '
722 </select>';
723 if ($this->getBackendUser()->uc['lang'] && !@is_dir((PATH_typo3conf . 'l10n/' . $this->getBackendUser()->uc['lang']))) {
724 $languageUnavailableWarning = 'The selected language "' . $this->getLanguageService()->getLL(('lang_' . $this->getBackendUser()->uc['lang']), TRUE) . '" is not available before the language files are installed.<br />' . ($this->getBackendUser()->isAdmin() ? 'You can use the Language module to easily download new language files.' : 'Please ask your system administrator to do this.');
725 $languageUnavailableMessage = GeneralUtility::makeInstance(FlashMessage::class, $languageUnavailableWarning, '', FlashMessage::WARNING);
726 $languageCode = $languageUnavailableMessage->render() . $languageCode;
727 }
728 return $languageCode;
729 }
730
731 /**
732 * Returns a select with all modules for startup
733 *
734 * @return string Complete select as HTML string
735 */
736 public function renderStartModuleSelect($params, $pObj) {
737 $startModuleSelect = '<option value="">' . $this->getLanguageService()->getLL('startModule.firstInMenu', TRUE) . '</option>';
738 foreach ($pObj->loadModules->modules as $mainMod => $modData) {
739 if (!empty($modData['sub']) && is_array($modData['sub'])) {
740 $modules = '';
741 foreach ($modData['sub'] as $subData) {
742 $modName = $subData['name'];
743 $modules .= '<option value="' . htmlspecialchars($modName) . '"';
744 $modules .= $this->getBackendUser()->uc['startModule'] === $modName ? ' selected="selected"' : '';
745 $modules .= '>' . $this->getLanguageService()->moduleLabels['tabs'][$modName . '_tab'] . '</option>';
746 }
747 $groupLabel = $this->getLanguageService()->moduleLabels['tabs'][$mainMod . '_tab'];
748 $startModuleSelect .= '<optgroup label="' . htmlspecialchars($groupLabel) . '">' . $modules . '</optgroup>';
749 }
750 }
751 return '<select id="field_startModule" name="data[startModule]" class="form-control">' . $startModuleSelect . '</select>';
752 }
753
754 /**
755 * Will make the simulate-user selector if the logged in user is administrator.
756 * It will also set the GLOBAL(!) BE_USER to the simulated user selected if any (and set $this->OLD_BE_USER to logged in user)
757 *
758 * @return void
759 */
760 public function simulateUser() {
761 // If admin, allow simulation of another user
762 $this->simUser = 0;
763 $this->simulateSelector = '';
764 unset($this->OLD_BE_USER);
765 if ($this->getBackendUser()->isAdmin()) {
766 $this->simUser = (int)GeneralUtility::_GP('simUser');
767 // Make user-selector:
768 $db = $this->getDatabaseConnection();
769 $where = 'AND username NOT LIKE ' . $db->fullQuoteStr($db->escapeStrForLike('_cli_', 'be_users') . '%', 'be_users');
770 $where .= ' AND uid <> ' . (int)$this->getBackendUser()->user['uid'] . BackendUtility::BEenableFields('be_users');
771 $users = BackendUtility::getUserNames('username,usergroup,usergroup_cached_list,uid,realName', $where);
772 $opt = array();
773 foreach ($users as $rr) {
774 $label = htmlspecialchars(($rr['username'] . ($rr['realName'] ? ' (' . $rr['realName'] . ')' : '')));
775 $opt[] = '<option value="' . $rr['uid'] . '"' . ($this->simUser == $rr['uid'] ? ' selected="selected"' : '') . '>' . $label . '</option>';
776 }
777 if (!empty($opt)) {
778 $this->simulateSelector = '<select id="field_simulate" name="simulateUser" onchange="window.location.href=' . GeneralUtility::quoteJSvalue(BackendUtility::getModuleUrl('user_setup') . '&simUser=') . '+this.options[this.selectedIndex].value;"><option></option>' . implode('', $opt) . '</select>';
779 }
780 }
781 // This can only be set if the previous code was executed.
782 if ($this->simUser > 0) {
783 // Save old user...
784 $this->OLD_BE_USER = $this->getBackendUser();
785 unset($GLOBALS['BE_USER']);
786 // Unset current
787 // New backend user object
788 $BE_USER = GeneralUtility::makeInstance(BackendUserAuthentication::class);
789 $BE_USER->setBeUserByUid($this->simUser);
790 $BE_USER->fetchGroupData();
791 $BE_USER->backendSetUC();
792 // Must do this, because unsetting $BE_USER before apparently unsets the reference to the global variable by this name!
793 $GLOBALS['BE_USER'] = $BE_USER;
794 }
795 }
796
797 /**
798 * Render simulate user select and label
799 *
800 * @return string
801 */
802 protected function renderSimulateUserSelectAndLabel() {
803 if ($this->simulateSelector === '') {
804 return '';
805 }
806
807 return '<p>' .
808 '<label for="field_simulate" style="margin-right: 20px;">' .
809 $this->getLanguageService()->sL('LLL:EXT:setup/Resources/Private/Language/locallang.xlf:simulate') .
810 '</label>' .
811 $this->simulateSelector .
812 '</p>';
813 }
814
815 /**
816 * Returns access check (currently only "admin" is supported)
817 *
818 * @param array $config Configuration of the field, access mode is defined in key 'access'
819 * @return bool Whether it is allowed to modify the given field
820 */
821 protected function checkAccess(array $config) {
822 $access = $config['access'];
823
824 if (isset($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['setup']['accessLevelCheck'][$access])) {
825 if (class_exists($access)) {
826 $accessObject = GeneralUtility::makeInstance($access);
827 if (method_exists($accessObject, 'accessLevelCheck')) {
828 // Initialize vars. If method fails, $set will be set to FALSE
829 return $accessObject->accessLevelCheck($config);
830 }
831 }
832 } elseif ($access == 'admin') {
833 return $this->isAdmin;
834 }
835
836 return FALSE;
837 }
838
839 /**
840 * Returns the label $str from getLL() and grays out the value if the $str/$key is found in $this->overrideConf array
841 *
842 * @param string $str Locallang key
843 * @param string $key Alternative override-config key
844 * @param bool $addLabelTag Defines whether the string should be wrapped in a <label> tag.
845 * @param string $altLabelTagId Alternative id for use in "for" attribute of <label> tag. By default the $str key is used prepended with "field_".
846 * @return string HTML output.
847 */
848 protected function getLabel($str, $key = '', $addLabelTag = TRUE, $altLabelTagId = '') {
849 if (substr($str, 0, 4) === 'LLL:') {
850 $out = $this->getLanguageService()->sL($str);
851 } else {
852 $out = htmlspecialchars($str);
853 }
854 if (isset($this->overrideConf[$key ?: $str])) {
855 $out = '<span style="color:#999999">' . $out . '</span>';
856 }
857 if ($addLabelTag) {
858 $out = '<label for="' . ($altLabelTagId ?: 'field_' . $key) . '">' . $out . '</label>';
859 }
860 return $out;
861 }
862
863 /**
864 * Returns the CSH Icon for given string
865 *
866 * @param string $str Locallang key
867 * @param string $label The label to be used, that should be wrapped in help
868 * @return string HTML output.
869 */
870 protected function getCSH($str, $label) {
871 $context = '_MOD_user_setup';
872 $field = $str;
873 $strParts = explode(':', $str);
874 if (count($strParts) > 1) {
875 // Setting comes from another extension
876 $context = $strParts[0];
877 $field = $strParts[1];
878 } elseif (!GeneralUtility::inList('language,simuser,reset', $str)) {
879 $field = 'option_' . $str;
880 }
881 return BackendUtility::wrapInHelp($context, $field, $label);
882 }
883
884 /**
885 * Returns array with fields defined in $GLOBALS['TYPO3_USER_SETTINGS']['showitem']
886 *
887 * @return array Array with fieldnames visible in form
888 */
889 protected function getFieldsFromShowItem() {
890 return GeneralUtility::trimExplode(',', $GLOBALS['TYPO3_USER_SETTINGS']['showitem'], TRUE);
891 }
892
893 /**
894 * Get Avatar fileUid
895 *
896 * @param int $beUserId
897 * @return int
898 */
899 protected function getAvatarFileUid($beUserId) {
900 $file = $this->getDatabaseConnection()->exec_SELECTgetSingleRow(
901 'uid_local',
902 'sys_file_reference',
903 'tablenames = \'be_users\' AND fieldname = \'avatar\' AND ' .
904 'table_local = \'sys_file\' AND uid_foreign = ' . (int)$beUserId .
905 BackendUtility::BEenableFields('sys_file_reference') . BackendUtility::deleteClause('sys_file_reference')
906 );
907 return $file ? $file['uid_local'] : 0;
908 }
909
910 /**
911 * Set avatar fileUid for backend user
912 *
913 * @param int $beUserId
914 * @param int $fileUid
915 * @param array $storeRec
916 */
917 protected function setAvatarFileUid($beUserId, $fileUid, array &$storeRec) {
918
919 // Update is only needed when new fileUid is set
920 if ((int)$fileUid === $this->getAvatarFileUid($beUserId)) {
921 return;
922 }
923
924 // Delete old file reference
925 $this->getDatabaseConnection()->exec_DELETEquery(
926 'sys_file_reference',
927 'tablenames = \'be_users\' AND fieldname = \'avatar\' AND ' .
928 'table_local = \'sys_file\' AND uid_foreign = ' . (int)$beUserId
929 );
930
931 // Create new reference
932 if ($fileUid) {
933
934 // Get file object
935 try {
936 $file = ResourceFactory::getInstance()->getFileObject($fileUid);
937 } catch (FileDoesNotExistException $e) {
938 $file = FALSE;
939 }
940
941 // Check if user is allowed to use the image (only when not in simulation mode)
942 if ($file && $this->simUser === 0 && !$file->getStorage()->checkFileActionPermission('read', $file)) {
943 $file = FALSE;
944 }
945
946 // Check if extension is allowed
947 if ($file && GeneralUtility::inList($GLOBALS['TYPO3_CONF_VARS']['GFX']['imagefile_ext'], $file->getExtension())) {
948
949 // Create new file reference
950 $storeRec['sys_file_reference']['NEW1234'] = array(
951 'uid_local' => $fileUid,
952 'uid_foreign' => $beUserId,
953 'tablenames' => 'be_users',
954 'fieldname' => 'avatar',
955 'pid' => 0,
956 'table_local' => 'sys_file',
957 );
958 $storeRec['be_users'][(int)$beUserId]['avatar'] = 'NEW1234';
959 }
960 }
961 }
962
963 /**
964 * Add JavaScript to for browse files button
965 *
966 * @param sting $fieldName
967 */
968 protected function addAvatarButtonJs($fieldName) {
969 $this->doc->JScodeArray['avatar-button'] = '
970 var browserWin="";
971
972 function openFileBrowser() {
973 var url = ' . GeneralUtility::quoteJSvalue(BackendUtility::getModuleUrl('wizard_element_browser', ['mode' => 'file', 'bparams' => '||||dummy|setFileUid'])) .';
974 browserWin = window.open(url,"Typo3WinBrowser","height=650,width=800,status=0,menubar=0,resizable=1,scrollbars=1");
975 browserWin.focus();
976 }
977
978 function clearExistingImage() {
979 TYPO3.jQuery(\'#image_' . htmlspecialchars($fieldName) . '\').hide();
980 TYPO3.jQuery(\'#clear_button_' . htmlspecialchars($fieldName) . '\').hide();
981 TYPO3.jQuery(\'#field_' . htmlspecialchars($fieldName) . '\').val(\'\');
982 }
983
984 function setFileUid(field, value, fileUid) {
985 clearExistingImage();
986 TYPO3.jQuery(\'#field_' . htmlspecialchars($fieldName) . '\').val(fileUid);
987 TYPO3.jQuery(\'#add_button_' . htmlspecialchars($fieldName) . '\').removeClass(\'btn-default\').addClass(\'btn-info\');
988
989 browserWin.close();
990 }
991 ';
992
993 }
994
995 /**
996 * Returns the current BE user.
997 *
998 * @return \TYPO3\CMS\Core\Authentication\BackendUserAuthentication
999 */
1000 protected function getBackendUser() {
1001 return $GLOBALS['BE_USER'];
1002 }
1003
1004 /**
1005 * Returns LanguageService
1006 *
1007 * @return \TYPO3\CMS\Lang\LanguageService
1008 */
1009 protected function getLanguageService() {
1010 return $GLOBALS['LANG'];
1011 }
1012
1013 /**
1014 * @return DatabaseConnection
1015 */
1016 protected function getDatabaseConnection() {
1017 return $GLOBALS['TYPO3_DB'];
1018 }
1019 }
1020