[BUGFIX] Enable removing all selectable user groups
[TYPO3CMS/Extensions/sr_feuser_register.git] / Classes / Domain / Data.php
1 <?php
2 namespace SJBR\SrFeuserRegister\Domain;
3
4 /*
5 * Copyright notice
6 *
7 * (c) 2007-2018 Stanislas Rolland <typo3(arobas)sjbr.ca>
8 * All rights reserved
9 *
10 * This script is part of the TYPO3 project. The TYPO3 project is
11 * free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
15 *
16 * The GNU General Public License can be found at
17 * http://www.gnu.org/copyleft/gpl.html.
18 * This script is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * GNU General Public License for more details.
21 *
22 * This copyright notice MUST APPEAR in all copies of the script!
23 */
24
25 use SJBR\SrFeuserRegister\Captcha\CaptchaManager;
26 use SJBR\SrFeuserRegister\Request\Parameters;
27 use SJBR\SrFeuserRegister\Security\Authentication;
28 use SJBR\SrFeuserRegister\Security\SecuredData;
29 use SJBR\SrFeuserRegister\Security\SessionData;
30 use SJBR\SrFeuserRegister\Security\StorageSecurity;
31 use SJBR\SrFeuserRegister\Utility\LocalizationUtility;
32 use SJBR\SrFeuserRegister\View\AbstractView;
33 use TYPO3\CMS\Core\Utility\ExtensionManagementUtility;
34 use TYPO3\CMS\Core\Utility\GeneralUtility;
35 use TYPO3\CMS\Core\Utility\MathUtility;
36 use TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer;
37 use TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController;
38
39 /**
40 * Data store functions
41 */
42 class Data
43 {
44 /**
45 * Extension key
46 *
47 * @var string
48 */
49 protected $extensionKey;
50
51 /**
52 * Extension name
53 *
54 * @var string Extension name
55 */
56 protected $extensionName;
57
58 /**
59 * Prefix used for CSS classes and variables
60 *
61 * @var string
62 */
63 protected $prefixId;
64
65 /**
66 * The table being used
67 *
68 * @var string
69 */
70 protected $theTable;
71
72 /**
73 * The plugin configuration
74 *
75 * @var array
76 */
77 protected $conf;
78
79 /**
80 * Content object
81 *
82 * @var ContentObjectRenderer
83 */
84 protected $cObj;
85
86 /**
87 * The request parameters object
88 *
89 * @var Parameters
90 */
91 protected $parameters;
92
93 /**
94 * The Static Info object
95 *
96 * @var \SJBR\StaticInfoTables\PiBaseApi
97 */
98 protected $staticInfoObj = null;
99
100 /**
101 * The usergroup hook object
102 *
103 */
104 protected $userGroupObj = null;
105
106 /**
107 * List of fields allowed for editing and creation
108 *
109 * @var string
110 */
111 protected $fieldList = '';
112
113 /**
114 * List of fields reserved as administration fields
115 *
116 * @var string
117 */
118 protected $adminFieldList = '';
119
120 /**
121 * List of special fields like captcha
122 *
123 * @var string
124 */
125 protected $specialFieldList = '';
126
127 /**
128 * Array of required field names per command key
129 *
130 * @var array
131 */
132 protected $requiredFieldsArray = array();
133
134 /**
135 * The array of incoming data
136 *
137 * @var array
138 */
139 protected $dataArray = array();
140
141 /**
142 * The array of incoming data
143 *
144 * @var array
145 */
146 protected $origArray = array();
147
148 /**
149 * The list of fields that ar in error
150 *
151 * @var string
152 */
153 protected $failure = '';
154
155 protected $evalErrors = array();
156
157 /**
158 * True when data was saved
159 *
160 * @var bool
161 */
162 protected $saved = false;
163
164 public $addTableArray = array();
165
166 public $error;
167 public $additionalUpdateFields = '';
168
169 /**
170 * The uid of the current record
171 *
172 * @var int
173 */
174 protected $recUid = 0;
175
176 public $missing = array(); // array of required missing fields
177 public $inError = array(); // array of fields with eval errors other than absence
178
179 /**
180 * Constructor
181 *
182 * @param string $extensionKey: the extension key
183 * @param string $prefixId: the prefixId
184 * @param string $theTable: the name of the table in use
185 * @param array $conf: the plugin configuration
186 * @param ContentObjectRenderer $cObj: the content object
187 * @param Parameters $parameters: the request parameters object
188 * @param string $adminFieldList: list of table fields that are considered reserved for administration purposes
189 * @return void
190 */
191 public function __construct(
192 $extensionKey,
193 $prefixId,
194 $theTable,
195 array &$conf,
196 ContentObjectRenderer $cObj,
197 Parameters $parameters,
198 $adminFieldList
199 ) {
200 $this->extensionKey = $extensionKey;
201 $this->extensionName = GeneralUtility::underscoredToUpperCamelCase($this->extensionKey);
202 $this->prefixId = $prefixId;
203 $this->theTable = $theTable;
204 $this->conf =& $conf;
205 $this->cObj = $cObj;
206 $this->parameters = $parameters;
207 if (ExtensionManagementUtility::isLoaded('static_info_tables')) {
208 $this->staticInfoObj = GeneralUtility::makeInstance('SJBR\\StaticInfoTables\\PiBaseApi');
209 if ($this->staticInfoObj->needsInit()) {
210 $this->staticInfoObj->init();
211 }
212 }
213 // Usergroup hook object
214 if ($this->theTable === 'fe_users') {
215 $hookClassArray = is_array($GLOBALS['TYPO3_CONF_VARS']['EXTCONF'][$this->extensionKey][$this->prefixId][$this->theTable]['usergroup']) ? $GLOBALS['TYPO3_CONF_VARS']['EXTCONF'][$this->extensionKey][$this->prefixId][$this->theTable]['usergroup'] : array();
216 foreach ($hookClassArray as $classRef) {
217 $this->userGroupObj = GeneralUtility::makeInstance($classRef);
218 if (is_object($this->userGroupObj)) {
219 break;
220 }
221 }
222 }
223 // Set the lists of admin fields
224 $this->setAdminFieldList($adminFieldList);
225 // Get the incoming data
226 $this->getIncomingData();
227 }
228
229 /**
230 * Get the data entered in the displayed form
231 *
232 * @return void
233 */
234 protected function getIncomingData()
235 {
236 // Get POST parameters
237 if ($this->parameters->isTokenValid()) {
238 $fe = GeneralUtility::_POST('FE');
239 if (isset($fe) && is_array($fe)) {
240 $feDataArray = $fe[$this->theTable];
241 SecuredData::secureInput($feDataArray, false);
242 $this->modifyRow($feDataArray, false);
243 SessionData::securePassword($this->extensionKey, $feDataArray);
244 $feDataArray = array_merge($feDataArray, SessionData::readPassword($this->extensionKey));
245 $this->setDataArray($feDataArray);
246 }
247 }
248 }
249
250 /**
251 * Set the incoming data array
252 *
253 * @param array $dataArray: the incoming data array
254 * @return void
255 */
256 public function setDataArray(array $dataArray)
257 {
258 $this->dataArray = $dataArray;
259 }
260
261 /**
262 * Set configuration
263 *
264 * @param array $conf: the configuration array
265 * @return void
266 */
267 public function setConfiguration(array $conf)
268 {
269 $this->conf = $conf;
270 }
271
272 /**
273 * Get the incoming data array
274 *
275 * @return array the incoming data array
276 */
277 public function getDataArray()
278 {
279 return $this->dataArray;
280 }
281
282 /**
283 * Resets the incoming data array
284 *
285 * @return void
286 */
287 public function resetDataArray()
288 {
289 $this->dataArray = array();
290 }
291
292 /**
293 * Get the list of fields that are in error
294 *
295 * @return string the list of fields that are in error
296 */
297 public function getFailure()
298 {
299 return $this->failure;
300 }
301
302 /**
303 * Set the list of fields that are in error
304 *
305 * @param string the list of fields that are in error
306 * @return void
307 */
308 protected function setFailure($failure)
309 {
310 $this->failure = $failure;
311 }
312
313 protected function setError($error) {
314 $this->error = $error;
315 }
316
317 public function getError () {
318 return $this->error;
319 }
320
321 public function setSaved($value)
322 {
323 $this->saved = $value;
324 }
325
326 public function getSaved()
327 {
328 return $this->saved;
329 }
330
331 /**
332 * Get the list of fields allowed for editing and creation
333 *
334 * @return string a list of fields allowed for editing and creation
335 */
336 public function getFieldList()
337 {
338 if (empty($this->fieldList)) {
339 $excludeFields = array('felogin_forgotHash', 'felogin_redirectPid', 'lastlogin', 'lockToDomain', 'starttime', 'endtime', 'token', 'TSconfig');
340 $this->fieldList = implode(',', array_diff(array_keys($GLOBALS['TCA'][$this->theTable]['columns']), $excludeFields));
341 }
342 return $this->fieldList;
343 }
344
345 /**
346 * Get the array of required fields names
347 *
348 * @return array required fields names
349 */
350 public function getRequiredFieldsArray($cmdKey)
351 {
352 if (!isset($this->requiredFieldsArray[$cmdKey])) {
353 $this->requiredFieldsArray[$cmdKey] = array();
354 if (isset($this->conf[$cmdKey . '.']['required']) && isset($this->conf[$cmdKey . '.']['fields'])) {
355 $this->requiredFieldsArray[$cmdKey] = array_intersect(GeneralUtility::trimExplode(',', $this->conf[$cmdKey . '.']['required'], true), GeneralUtility::trimExplode(',', $this->conf[$cmdKey . '.']['fields'], true));
356 }
357 }
358 return $this->requiredFieldsArray[$cmdKey];
359 }
360
361 /**
362 * Set the list of reserved administration fields
363 *
364 * @param string a list of reserved fields
365 * @return void
366 */
367 protected function setAdminFieldList($adminFieldList)
368 {
369 if (!$adminFieldList) {
370 $adminFieldList = 'username,password,name,disable,usergroup,by_invitation,tx_srfeuserregister_password';
371 }
372 if (trim($this->conf['addAdminFieldList'])) {
373 $adminFieldList .= ',' . trim($this->conf['addAdminFieldList']);
374 }
375 // Honour Address List (tt_address) configuration settings
376 if ($this->theTable === 'tt_address' && ExtensionManagementUtility::isLoaded('tt_address')) {
377 $settings = \TYPO3\TtAddress\Utility\SettingsUtility::getSettings();
378 if (!$settings->isStoreBackwardsCompatName()) {
379 // Remove name from adminFieldList
380 $adminFieldList = GeneralUtility::rmFromList('name', $adminFieldList);
381 }
382 }
383 $this->adminFieldList = implode(',', array_intersect(explode(',', $this->getFieldList()), GeneralUtility::trimExplode(',', $adminFieldList, true)));
384 }
385
386 /**
387 * Get the list of reserved administration fields
388 *
389 * @return string a list of reserved fields
390 */
391 public function getAdminFieldList()
392 {
393 return $this->adminFieldList;
394 }
395
396 /**
397 * Get the list of special fields like captcha
398 *
399 * @return string a list of special fields
400 */
401 public function getSpecialFieldList()
402 {
403 if (empty($this->specialFieldList)) {
404 if (CaptchaManager::isLoaded($this->extensionKey)) {
405 $this->specialFieldList = 'captcha_response';
406 }
407 }
408 return $this->specialFieldList;
409 }
410
411 public function getAdditionalUpdateFields()
412 {
413 return $this->additionalUpdateFields;
414 }
415
416
417 public function setAdditionalUpdateFields ($additionalUpdateFields) {
418 $this->additionalUpdateFields = $additionalUpdateFields;
419 }
420
421
422 public function setRecUid($uid)
423 {
424 $this->recUid = (int) $uid;
425 }
426
427
428 public function getRecUid()
429 {
430 return $this->recUid;
431 }
432
433
434 public function getAddTableArray () {
435 return $this->addTableArray;
436 }
437
438
439 public function addTableArray ($table) {
440 if (!in_array($table, $this->addTableArray)) {
441 $this->addTableArray[] = $table;
442 }
443 }
444
445
446 public function setOrigArray(array $origArray)
447 {
448 $this->origArray = $origArray;
449 }
450
451
452 public function getOrigArray()
453 {
454 return $this->origArray;
455 }
456
457
458 public function bNewAvailable () {
459 $dataArray = $this->getDataArray();
460 $rc = ($dataArray['username'] != '' || $dataArray['email'] != '');
461 return $rc;
462 }
463
464 /**
465 * Overrides field values as specified by TS setup
466 *
467 * @param array $dataArray: the incoming data array
468 * @param string $cmdKey: the command being processed
469 * @return void all overriding done directly on array $dataArray
470 */
471 public function overrideValues(array &$dataArray, $cmdKey)
472 {
473 if (is_array($this->conf[$cmdKey . '.']['overrideValues.'])) {
474 foreach ($this->conf[$cmdKey . '.']['overrideValues.'] as $theField => $theValue) {
475 if ($theField === 'usergroup' && $this->theTable === 'fe_users' && $this->conf[$cmdKey . '.']['allowUserGroupSelection']) {
476 $overrideArray = GeneralUtility::trimExplode(',', $theValue, true);
477 if (is_array($dataArray[$theField])) {
478 $dataValue = array_merge($dataArray[$theField], $overrideArray);
479 } else {
480 $dataValue = $overrideArray;
481 }
482 $dataValue = array_unique($dataValue);
483 } else {
484 $stdWrap = $this->conf[$cmdKey . '.']['overrideValues.'][$theField . '.'];
485 if ($stdWrap) {
486 $dataValue = $this->cObj->stdWrap($theValue, $stdWrap);
487 } else if (isset($this->conf[$cmdKey . '.']['overrideValues.'][$theField])) {
488 $dataValue = $this->conf[$cmdKey . '.']['overrideValues.'][$theField];
489 } else {
490 $dataValue = $theValue;
491 }
492 }
493 $dataArray[$theField] = $dataValue;
494 }
495 }
496 }
497
498 /**
499 * Fetches default field values as specified by TS setup
500 *
501 * @param string $cmdKey: the command being processed
502 * @return array a data row with default key/value pairs
503 */
504 public function defaultValues($cmdKey)
505 {
506 $dataArray = array();
507 if (is_array($this->conf[$cmdKey . '.']['defaultValues.'])) {
508 foreach ($this->conf[$cmdKey . '.']['defaultValues.'] as $theField => $theValue) {
509 $dataArray[$theField] = $theValue;
510 }
511 }
512 return $dataArray;
513 }
514
515 /**
516 * Gets the error message to be displayed
517 *
518 * @param string $theField: the name of the field being validated
519 * @param string $theRule: the name of the validation rule being evaluated
520 * @param string $label: a default error message provided by the invoking function
521 * @param string $param: parameter for the error message
522 * @param string $extensionName: name of the extension
523 * @return string the error message to be displayed
524 */
525 protected function getFailureText($theField, $theRule, $label, $param = '', $extensionName)
526 {
527 $failureLabel = '';
528 if ($theRule) {
529 $failureLabel = LocalizationUtility::translate('evalErrors_' . $theRule . '_' . $theField, $extensionName);
530 $failureLabel = $failureLabel ?: LocalizationUtility::translate('evalErrors_' . $theRule, $extensionName);
531 }
532 if (!$failureLabel) {
533 $failureLabel = LocalizationUtility::translate($label, $extensionName);
534 }
535 if ($param) {
536 $failureLabel = sprintf($failureLabel, $param);
537 }
538 return $failureLabel;
539 }
540
541 /**
542 * Applies validation rules specified in TS setup
543 *
544 * @param array $dataArray: the incoming data array
545 * @param string $cmdKey: the command being processed
546 * @param string $mode: the current mode (normal or preview)
547 * @return void on return, the parameters failure will contain the list of fields which were not ok
548 */
549 public function evalValues(array &$dataArray, array $origArray, $markerObj, $cmdKey, $mode = AbstractView::MODE_NORMAL)
550 {
551 $failureArray = array();
552 $failureMsg = array();
553 $markerArray = array();
554 $displayFieldArray = GeneralUtility::trimExplode(',', $this->conf[$cmdKey.'.']['fields'], true);
555 if (CaptchaManager::useCaptcha($cmdKey, $this->conf, $this->extensionKey)) {
556 $displayFieldArray = array_merge($displayFieldArray, array('captcha_response'));
557 }
558 // Check required fields, set failure if missing.
559 $requiredArray = $this->getRequiredFieldsArray($cmdKey);
560 foreach ($requiredArray as $theField) {
561 $value = $dataArray[$theField];
562 if ($theField === 'usergroup' && is_object($this->userGroupObj) && is_array($value)) {
563 $value = $this->userGroupObj->restrictToSelectableValues($value, $this->conf, $cmdKey);
564 }
565 $isMissing = empty($value);
566 $fieldConfig = $GLOBALS['TCA'][$this->theTable]['columns'][$theField]['config'];
567 if ($isMissing && $fieldConfig['type'] === 'radio') {
568 foreach ($fieldConfig['items'] as $k => $item) {
569 if ($value == $item[1]) {
570 $isMissing = false;
571 break;
572 }
573 }
574 }
575 if ($isMissing) {
576 $failureArray[] = $theField;
577 $this->missing[$theField] = true;
578 }
579 }
580 $pid = $dataArray['pid'];
581
582 // Evaluate: This evaluates for more advanced things than "required" does.
583 // But it returns the same error code, so you must let the required-message, if further evaluation has failed!
584 if (is_array($this->conf[$cmdKey . '.']['evalValues.'])) {
585 $cmd = $this->parameters->getCmd();
586 if ($cmd === 'edit' || $cmdKey === 'edit') {
587 if ((int)$pid) {
588 // This may be tricked if the input has the pid-field set but the edit-field list does NOT allow the pid to be edited. Then the pid may be false.
589 $recordTestPid = (int)$pid;
590 } else {
591 $tempRecArr = $GLOBALS['TSFE']->sys_page->getRawRecord($this->theTable, $dataArray['uid']);
592 $recordTestPid = (int) $tempRecArr['pid'];
593 }
594 } else {
595 $thePid = $this->parameters->getPid();
596 $recordTestPid = $thePid ? $thePid : $pid;
597 }
598 foreach ($this->conf[$cmdKey.'.']['evalValues.'] as $theField => $theValue) {
599 $this->evalErrors[$theField] = array();
600 $failureMsg[$theField] = array();
601 $listOfCommands = GeneralUtility::trimExplode(',', $theValue, true);
602 // Unset the incoming value is empty and unsetEmpty is specified
603 if (array_search('unsetEmpty', $listOfCommands) !== false) {
604 if (isset($dataArray[$theField]) && empty($dataArray[$theField]) && trim($dataArray[$theField]) !== '0') {
605 unset($dataArray[$theField]);
606 }
607 if (isset($dataArray[$theField . '_again']) && empty($dataArray[$theField . '_again']) && trim($dataArray[$theField . '_again']) !== '0') {
608 unset($dataArray[$theField . '_again']);
609 }
610 }
611 if (isset($dataArray[$theField]) || isset($dataArray[$theField . '_again']) || !count($origArray) || !isset($origArray[$theField])) {
612 foreach ($listOfCommands as $k => $cmd) {
613 // Enable parameters after each command enclosed in brackets [..]
614 $cmdParts = preg_split('/\[|\]/', $cmd);
615 $theCmd = trim($cmdParts[0]);
616 switch($theCmd) {
617 case 'uniqueGlobal':
618 case 'uniqueDeletedGlobal':
619 case 'uniqueLocal':
620 case 'uniqueDeletedLocal':
621 if (class_exists('TYPO3\\CMS\\Core\\Database\\ConnectionPool')) {
622 $queryBuilder = GeneralUtility::makeInstance(\TYPO3\CMS\Core\Database\ConnectionPool::class)
623 ->getQueryBuilderForTable($this->theTable);
624 $queryBuilder
625 ->getRestrictions()
626 ->removeAll();
627 if ($theCmd === 'uniqueLocal' || $theCmd === 'uniqueGlobal') {
628 $queryBuilder
629 ->getRestrictions()
630 ->add(GeneralUtility::makeInstance(\TYPO3\CMS\Core\Database\Query\Restriction\DeletedRestriction::class));
631 }
632 $queryBuilder
633 ->select('uid', $theField)
634 ->from($this->theTable)
635 ->where(
636 $queryBuilder->expr()->eq($theField, $queryBuilder->createNamedParameter($dataArray[$theField]), \PDO::PARAM_STR)
637 )
638 ->setMaxResults(1);
639 if ($dataArray['uid']) {
640 $queryBuilder
641 ->andWhere(
642 $queryBuilder->expr()->neq('uid', $queryBuilder->createNamedParameter((int)$dataArray['uid']), \PDO::PARAM_INT)
643 );
644 }
645 if ($theCmd === 'uniqueLocal' || $theCmd === 'uniqueDeletedLocal') {
646 $queryBuilder
647 ->andWhere(
648 $queryBuilder->expr()->in('pid', GeneralUtility::intExplode(',', $recordTestPid, true))
649 );
650 }
651 $DBrows = $queryBuilder
652 ->execute()
653 ->fetchAll();
654 } else {
655 // TYPO3 CMS 7 LTS
656 $where = $theField . '=' . $GLOBALS['TYPO3_DB']->fullQuoteStr($dataArray[$theField], $this->theTable);
657 if ($dataArray['uid']) {
658 $where .= ' AND uid != ' . (int)$dataArray['uid'];
659 }
660 if ($theCmd === 'uniqueLocal' || $theCmd === 'uniqueGlobal') {
661 $where .= $GLOBALS['TSFE']->sys_page->deleteClause($this->theTable);
662 }
663 if ($theCmd === 'uniqueLocal' || $theCmd === 'uniqueDeletedLocal') {
664 $where .= ' AND pid IN (' . $recordTestPid.')';
665 }
666 $DBrows = $GLOBALS['TYPO3_DB']->exec_SELECTgetRows('uid,' . $theField, $this->theTable, $where, '', '', '1');
667 }
668 if (
669 !is_array($dataArray[$theField]) &&
670 trim($dataArray[$theField]) != '' &&
671 isset($DBrows) &&
672 is_array($DBrows) &&
673 isset($DBrows[0]) &&
674 is_array($DBrows[0])
675 ) {
676 // Only issue an error if the record is not existing (if new...) and if the record with the false value selected was not our self.
677 $failureArray[] = $theField;
678 $this->inError[$theField] = true;
679 $this->evalErrors[$theField][] = $theCmd;
680 $failureMsg[$theField][] = $this->getFailureText($theField, $theCmd, 'evalErrors_existed_already', '', $this->extensionName);
681 }
682 break;
683 case 'twice':
684 $fieldValue = strval($dataArray[$theField]);
685 $fieldAgainValue = strval($dataArray[$theField . '_again']);
686 if (strcmp($fieldValue, $fieldAgainValue)) {
687 $failureArray[] = $theField;
688 $this->inError[$theField] = true;
689 $this->evalErrors[$theField][] = $theCmd;
690 $failureMsg[$theField][] = $this->getFailureText($theField, $theCmd, 'evalErrors_same_twice', '', $this->extensionName);
691 }
692 break;
693 case 'email':
694 if (!is_array($dataArray[$theField]) && trim($dataArray[$theField]) && !GeneralUtility::validEmail($dataArray[$theField])) {
695 $failureArray[] = $theField;
696 $this->inError[$theField] = true;
697 $this->evalErrors[$theField][] = $theCmd;
698 $failureMsg[$theField][] = $this->getFailureText($theField, $theCmd, 'evalErrors_valid_email', '', $this->extensionName);
699 }
700 break;
701 case 'required':
702 $value = $dataArray[$theField];
703 if ($theField === 'usergroup' && is_object($this->userGroupObj) && is_array($value)) {
704 $value = $this->userGroupObj->restrictToSelectableValues($value, $this->conf, $cmdKey);
705 }
706 if (empty($value) && $dataArray[$theField] !== '0') {
707 $failureArray[] = $theField;
708 $this->inError[$theField] = true;
709 $this->evalErrors[$theField][] = $theCmd;
710 $failureMsg[$theField][] = $this->getFailureText($theField, $theCmd, 'evalErrors_required', '', $this->extensionName);
711 }
712 break;
713 case 'atLeast':
714 $chars = intval($cmdParts[1]);
715 if (!is_array($dataArray[$theField]) && strlen($dataArray[$theField]) < $chars) {
716 $failureArray[] = $theField;
717 $this->inError[$theField] = true;
718 $this->evalErrors[$theField][] = $theCmd;
719 $failureMsg[$theField][] = $this->getFailureText($theField, $theCmd, 'evalErrors_atleast_characters', $chars, $this->extensionName);
720 }
721 break;
722 case 'atMost':
723 $chars = intval($cmdParts[1]);
724 if (!is_array($dataArray[$theField]) && strlen($dataArray[$theField]) > $chars) {
725 $failureArray[] = $theField;
726 $this->inError[$theField] = true;
727 $this->evalErrors[$theField][] = $theCmd;
728 $failureMsg[$theField][] = $this->getFailureText($theField, $theCmd, 'evalErrors_atmost_characters', $chars, $this->extensionName);
729 }
730 break;
731 case 'inBranch':
732 $pars = explode(';', $cmdParts[1]);
733 if (intval($pars[0])) {
734 $pid_list = $this->cObj->getTreeList(
735 intval($pars[0]),
736 intval($pars[1]) ? intval($pars[1]) : 999,
737 intval($pars[2])
738 );
739
740 if (!$pid_list || !GeneralUtility::inList($pid_list, $dataArray[$theField])) {
741 $failureArray[] = $theField;
742 $this->inError[$theField] = true;
743 $this->evalErrors[$theField][] = $theCmd;
744 $failureMsg[$theField][] = $this->getFailureText($theField, $theCmd, 'evalErrors_unvalid_list', $pid_list, $this->extensionName);
745 }
746 }
747 break;
748 case 'wwwURL':
749 if ($dataArray[$theField]) {
750 $urlParts = parse_url($dataArray[$theField]);
751 if (
752 $urlParts === false
753 || !GeneralUtility::isValidUrl($dataArray[$theField])
754 || ($urlParts['scheme'] != 'http' && $urlParts['scheme'] != 'https')
755 || $urlParts['user']
756 || $urlParts['pass']
757 ) {
758 $failureArray[] = $theField;
759 $this->inError[$theField] = true;
760 $this->evalErrors[$theField][] = $theCmd;
761 $failureMsg[$theField][] = $this->getFailureText($theField, $theCmd, 'evalErrors_unvalid_url', '', $this->extensionName);
762 }
763 }
764 break;
765 case 'date':
766 if (
767 !is_array($dataArray[$theField])
768 && $dataArray[$theField]
769 && !$this->evalDate($dataArray[$theField])
770 ) {
771 $failureArray[] = $theField;
772 $this->inError[$theField] = true;
773 $this->evalErrors[$theField][] = $theCmd;
774 $failureMsg[$theField][] = $this->getFailureText($theField, $theCmd, 'evalErrors_unvalid_date', '', $this->extensionName);
775 }
776 break;
777 case 'preg':
778 if (!is_array($dataArray[$theField]) && !empty($dataArray[$theField]) && $dataArray[$theField] !== '0') {
779 $pattern = str_replace('preg[', '', $cmd);
780 $pattern = substr($pattern, 0, strlen($pattern) - 1);
781 $matches = array();
782 $test = preg_match($pattern, $dataArray[$theField], $matches);
783 if (count($matches) === 0) {
784 $failureArray[] = $theField;
785 $this->inError[$theField] = true;
786 $this->evalErrors[$theField][] = $theCmd;
787 $failureMsg[$theField][] = $this->getFailureText($theField, $theCmd, 'evalErrors_' . $theCmd, $cmd, $this->extensionName);
788 }
789 }
790 break;
791 case 'hook':
792 default:
793 $hookClassArray = is_array($GLOBALS['TYPO3_CONF_VARS']['EXTCONF'][$this->extensionKey][$this->prefixId]['model']) ? $GLOBALS['TYPO3_CONF_VARS']['EXTCONF'][$this->extensionKey][$this->prefixId]['model'] : array();
794 // The captcha cannot be checked twice
795 if (is_array($GLOBALS['TYPO3_CONF_VARS']['EXTCONF'][$this->extensionKey]['captcha']) && ($mode == AbstractView::MODE_PREVIEW || !$this->conf[$cmdKey . '.']['preview'])) {
796 $hookClassArray = array_merge($hookClassArray, $GLOBALS['TYPO3_CONF_VARS']['EXTCONF'][$this->extensionKey]['captcha']);
797 }
798 foreach ($hookClassArray as $classRef) {
799 $hookObj = GeneralUtility::makeInstance($classRef);
800 if (is_object($hookObj) && method_exists($hookObj, 'evalValues')) {
801 $errorField = $hookObj->evalValues($this->theTable, $dataArray, $theField, $cmdKey, $cmdParts, $this->extensionName);
802 if (is_array($errorField)) {
803 if (!empty($errorField)) {
804 $failureArray[] = $theField;
805 $this->evalErrors[$theField][] = $theCmd;
806 $this->inError[$theField] = true;
807 $failureMsg[$theField] = array_merge($failureMsg[$theField], $errorField);
808 }
809 } else if ($errorField !== '') {
810 $failureArray[] = $errorField;
811 $this->evalErrors[$theField][] = $theCmd;
812 $this->inError[$theField] = true;
813 $failureMsg[$theField][] = $this->getFailureText($theField, $theCmd, 'evalErrors_' . $theCmd, $cmd, $this->extensionName);
814 }
815 }
816 }
817 break;
818 }
819 }
820 }
821
822 if (in_array($theField, $displayFieldArray) || in_array($theField, $failureArray)) {
823 if (!empty($failureMsg[$theField])) {
824 if ($markerArray['###EVAL_ERROR_saved###']) {
825 $markerArray['###EVAL_ERROR_saved###'] .= '<br />';
826 }
827 $errorMsg = implode($failureMsg[$theField], '<br />');
828 $markerArray['###EVAL_ERROR_saved###'] .= $errorMsg;
829 } else {
830 $errorMsg = '';
831 }
832 $markerArray['###EVAL_ERROR_FIELD_' . $theField . '###'] = ($errorMsg != '' ? $errorMsg : '<!--no error-->');
833 }
834 }
835 }
836
837 if (empty($markerArray['###EVAL_ERROR_saved###'])) {
838 $markerArray['###EVAL_ERROR_saved###'] = '';
839 }
840
841 if ($this->staticInfoObj !== null && $this->missing['zone']) {
842 // empty zone if there is no zone for the provided country
843 $zoneArray = $this->staticInfoObj->initCountrySubdivisions($dataArray['static_info_country']);
844 if (empty($zoneArray)) {
845 unset($this->missing['zone']);
846 $k = array_search('zone', $failureArray);
847 unset($failureArray[$k]);
848 }
849 }
850
851 $failure = implode($failureArray, ',');
852 $this->setFailure($failure);
853 $markerObj->addEvalValuesMarkers($markerArray);
854 return $this->evalErrors;
855 }
856
857 /**
858 * Transforms fields into certain things...
859 *
860 * @param array $dataArray: the incoming data array
861 * @param array $origArray: the original data array
862 * @param string $cmdKey: the comman key being processed
863 * @return void all parsing done directly on input array $dataArray
864 */
865 public function parseValues(array &$dataArray, array $origArray, $cmdKey)
866 {
867 if (is_array($this->conf['parseValues.'])) {
868 $parsedOrigArray = $this->parseIncomingData($origArray);
869 foreach ($this->conf['parseValues.'] as $theField => $theValue) {
870 $listOfCommands = GeneralUtility::trimExplode(',', $theValue, true);
871 if (in_array('setEmptyIfAbsent', $listOfCommands)) {
872 $this->setEmptyIfAbsent($theField, $dataArray);
873 }
874 $fieldConfig = $GLOBALS['TCA'][$this->theTable]['columns'][$theField]['config'];
875 if (isset($dataArray[$theField]) || isset($origArray[$theField]) || $fieldConfig['internal_type'] === 'file' || $fieldConfig['foreign_table'] === 'sys_file_reference') {
876 foreach ($listOfCommands as $cmd) {
877 // Enable parameters after each command enclosed in brackets [..].
878 $cmdParts = preg_split('/\[|\]/', $cmd);
879 $theCmd = trim($cmdParts[0]);
880 $bValueAssigned = true;
881 if (($theField === 'password' || $theField === 'password_again') && !isset($dataArray[$theField])) {
882 $bValueAssigned = false;
883 }
884 $dataValue = (isset($dataArray[$theField]) ? $dataArray[$theField] : $parsedOrigArray[$theField]);
885 switch ($theCmd) {
886 case 'int':
887 $dataValue = (int) $dataValue;
888 break;
889 case 'lower':
890 case 'upper':
891 $dataValue = $this->cObj->caseshift($dataValue, $theCmd);
892 break;
893 case 'nospace':
894 $dataValue = str_replace(' ', '', $dataValue);
895 break;
896 case 'alpha':
897 $dataValue = preg_replace('/[^a-zA-Z]/', '', $dataValue);
898 break;
899 case 'num':
900 $dataValue = preg_replace('/[^0-9]/', '', $dataValue);
901 break;
902 case 'alphanum':
903 $dataValue = preg_replace('/[^a-zA-Z0-9]/', '', $dataValue);
904 break;
905 case 'alphanum_x':
906 $dataValue = preg_replace('/[^a-zA-Z0-9_-]/', '', $dataValue);
907 break;
908 case 'trim':
909 $dataValue = trim($dataValue);
910 break;
911 case 'random':
912 $dataValue = substr(md5(uniqid(microtime(), 1)), 0, intval($cmdParts[1]));
913 break;
914 case 'files':
915 $hookClassArray = is_array($GLOBALS['TYPO3_CONF_VARS']['EXTCONF'][$this->extensionKey][$this->prefixId]['model']) ? $GLOBALS['TYPO3_CONF_VARS']['EXTCONF'][$this->extensionKey][$this->prefixId]['model'] : array();
916 foreach ($hookClassArray as $classRef) {
917 $hookObj = GeneralUtility::makeInstance($classRef);
918 if (is_object($hookObj) && method_exists($hookObj, 'parseValues')) {
919 $dataValue = $hookObj->parseValues($this->theTable, $dataArray, $dataValue, $theField, $cmdKey, $cmdParts);
920 }
921 }
922 break;
923 case 'multiple':
924 $fieldDataArray = [];
925 if (!empty($dataArray[$theField]) && !empty($dataValue)) {
926 if (is_array($dataValue)) {
927 $fieldDataArray = $dataValue;
928 } else if (is_string($dataValue)) {
929 $fieldDataArray = GeneralUtility::trimExplode(',', $dataValue, true);
930 }
931 }
932 $dataValue = $fieldDataArray;
933 break;
934 case 'checkArray':
935 if (is_array($dataValue)) {
936 $newDataValue = 0;
937 foreach ($dataValue as $kk => $vv) {
938 $kk = MathUtility::forceIntegerInRange($kk, 0);
939 if ($kk <= 30) {
940 if ($vv) {
941 $newDataValue|= pow(2, $kk);
942 }
943 }
944 }
945 $dataValue = $newDataValue;
946 }
947 break;
948 case 'uniqueHashInt':
949 $otherFields = GeneralUtility::trimExplode(';', $cmdParts[1], true);
950 $hashArray = array();
951 foreach($otherFields as $fN) {
952 $vv = $dataArray[$fN];
953 $vv = preg_replace('/\s+/', '', $vv);
954 $vv = preg_replace('/[^a-zA-Z0-9]/', '', $vv);
955 $vv = strtolower($vv);
956 $hashArray[] = $vv;
957 }
958 $dataValue = hexdec(substr(md5(serialize($hashArray)), 0, 8));
959 break;
960 case 'wwwURL':
961 if ($dataValue) {
962 $urlParts = parse_url($dataValue);
963 if ($urlParts !== false) {
964 if (!$urlParts['scheme']) {
965 $urlParts['scheme'] = 'http';
966 $dataValue = $urlParts['scheme'] . '://' . $dataValue;
967 }
968 if (GeneralUtility::isValidUrl($dataValue)) {
969 $dataValue = $urlParts['scheme'] . '://' .
970 $urlParts['host'] .
971 $urlParts['path'] .
972 ($urlParts['query'] ? '?' . $urlParts['query'] : '') .
973 ($urlParts['fragment'] ? '#' . $urlParts['fragment'] : '');
974 }
975 }
976 }
977 break;
978 case 'date':
979 if ($dataValue && $this->evalDate($dataValue)) {
980 $dateArray = $this->fetchDate($dataValue);
981 $dataValue = $dateArray['y'] . '-' . $dateArray['m'] . '-'.$dateArray['d'];
982 $translateArray = array(
983 'd' => ($dateArray['d'] < 10 ? '0'.$dateArray['d'] : $dateArray['d']),
984 'j' => $dateArray['d'],
985 'm' => ($dateArray['m'] < 10 ? '0'.$dateArray['m'] : $dateArray['m']),
986 'n' => $dateArray['m'],
987 'y' => $dateArray['y'],
988 'Y' => $dateArray['y']
989 );
990 $searchArray = array_keys($translateArray);
991 $replaceArray = array_values($translateArray);
992 $dataValue = str_replace($searchArray, $replaceArray, 'Y-m-d');
993 } else if (!isset($dataArray[$theField])) {
994 $bValueAssigned = false;
995 }
996 break;
997 default:
998 break;
999 }
1000 if ($bValueAssigned) {
1001 $dataArray[$theField] = $dataValue;
1002 }
1003 }
1004 }
1005 }
1006 }
1007 }
1008
1009 /**
1010 * Saves the data into the database
1011 *
1012 * @param array $dataArray: the incoming data array
1013 * @param array $origArray: the original data array
1014 * @return void sets $this->saved
1015 */
1016 public function save(array $dataArray, array $origArray, $token, array &$newRow, $cmd, $cmdKey, $pid)
1017 {
1018 $rc = 0;
1019 switch ($cmdKey) {
1020 case 'edit':
1021 case 'password':
1022 $theUid = (int) $origArray['uid'];
1023 $rc = $theUid;
1024 $aCAuth = Authentication::aCAuth($this->parameters->getAuthCode(), $origArray, $this->conf, $this->conf['setfixed.']['EDIT.']['_FIELDLIST']);
1025 // Fetch the original record to check permissions
1026 if ($this->conf['edit'] && ($GLOBALS['TSFE']->loginUser || $aCAuth)) {
1027 // Must be logged in in order to edit (OR be validated by email)
1028 $newFieldList = implode(',', array_intersect(explode(',', $this->getFieldList()), GeneralUtility::trimExplode(',', $this->conf[$cmdKey . '.']['fields'], true)));
1029 $newFieldArray = array_unique( array_merge (explode(',', $newFieldList), explode(',', $this->getAdminFieldList())));
1030 $fieldArray = GeneralUtility::trimExplode(',', $this->conf[$cmdKey . '.']['fields'], true);
1031 // Do not reset the name if we have no new value
1032 if (!in_array('name', $fieldArray) && !in_array('first_name', $fieldArray) && !in_array('last_name', $fieldArray)) {
1033 $newFieldArray = array_diff($newFieldArray, array('name'));
1034 }
1035 // Do not reset the username if we have no new value
1036 if (!in_array('username', $fieldArray) && $dataArray['username'] == '') {
1037 $newFieldArray = array_diff($newFieldArray, array('username'));
1038 }
1039
1040 if ($aCAuth || $this->DBmayFEUserEdit($this->theTable, $origArray, $GLOBALS['TSFE']->fe_user->user, $this->conf['allowedGroups'], $this->conf['fe_userEditSelf'])) {
1041 $outGoingData = $this->parseOutgoingData($cmdKey, $pid, $dataArray, $origArray);
1042 // Do not set the outgoing password if the incoming password was unset
1043 if ($this->theTable === 'fe_users' && !empty($dataArray['password'])) {
1044 $outGoingData['password'] = SessionData::readPasswordForStorage($this->extensionKey);
1045 }
1046 $newFieldList = implode (',', $newFieldArray);
1047 if (isset($GLOBALS['TCA'][$this->theTable]['ctrl']['token'])) {
1048 // Save token in record
1049 $outGoingData['token'] = $token;
1050 // Could be set conditional to adminReview or user confirm
1051 $newFieldList .= ',token';
1052 }
1053 $res = $this->updateRecord($theUid, $outGoingData, $newFieldList);
1054 $this->updateMMRelations($dataArray);
1055 $this->setSaved(true);
1056
1057 $newRow = $GLOBALS['TSFE']->sys_page->getRawRecord($this->theTable, $theUid);
1058 $newRow = $this->parseIncomingData($newRow);
1059 $this->modifyRow($newRow, true);
1060
1061 // Call all afterSaveEdit hooks after the record has been edited and saved
1062 if (is_array ($GLOBALS['TYPO3_CONF_VARS']['EXTCONF'][$this->extensionKey][$this->prefixId]['registrationProcess'])) {
1063 foreach ($GLOBALS['TYPO3_CONF_VARS']['EXTCONF'][$this->extensionKey][$this->prefixId]['registrationProcess'] as $classRef) {
1064 $hookObj = GeneralUtility::makeInstance($classRef);
1065 if (method_exists($hookObj, 'registrationProcess_afterSaveEdit')) {
1066 $hookObj->registrationProcess_afterSaveEdit(
1067 $this->theTable,
1068 $dataArray,
1069 $origArray,
1070 $token,
1071 $newRow,
1072 $cmd,
1073 $cmdKey,
1074 $pid,
1075 $newFieldList,
1076 $this
1077 );
1078 }
1079 }
1080 }
1081 } else {
1082 $this->setError('###TEMPLATE_NO_PERMISSIONS###');
1083 }
1084 }
1085 break;
1086 default:
1087 if (is_array($this->conf[$cmdKey.'.'])) {
1088 // Allow to override values for fields that are not on the form
1089 $newFieldArray = array_merge(GeneralUtility::trimExplode(',', $this->conf[$cmdKey . '.']['fields'], true), array_keys($this->conf[$cmdKey . '.']['overrideValues.']));
1090 $newFieldArray = array_intersect(explode(',', $this->getFieldList()), $newFieldArray);
1091 $newFieldArray = array_unique(array_merge($newFieldArray, explode(',', $this->getAdminFieldList())));
1092 $newFieldList = implode(',', $newFieldArray);
1093 $parsedArray = $this->parseOutgoingData($cmdKey, $pid, $dataArray, $origArray);
1094 if ($this->theTable === 'fe_users') {
1095 $parsedArray['password'] = SessionData::readPasswordForStorage($this->extensionKey);
1096 }
1097 if (isset($GLOBALS['TCA'][$this->theTable]['ctrl']['token'])) {
1098 $parsedArray['token'] = $token;
1099 $newFieldList .= ',token';
1100 }
1101 $newId = $this->insertRecord((int)$pid, $parsedArray, $newFieldList);
1102 $rc = $newId;
1103 // Enable users to own themselves.
1104 if ($this->theTable === 'fe_users' && $this->conf['fe_userOwnSelf']) {
1105 $extraList = '';
1106 $tmpDataArray = array();
1107 if ($GLOBALS['TCA'][$this->theTable]['ctrl']['fe_cruser_id']) {
1108 $field = $GLOBALS['TCA'][$this->theTable]['ctrl']['fe_cruser_id'];
1109 $dataArray[$field] = $newId;
1110 $tmpDataArray[$field] = $newId;
1111 $extraList .= ',' . $field;
1112 }
1113 if ($GLOBALS['TCA'][$this->theTable]['ctrl']['fe_crgroup_id']) {
1114 $field = $GLOBALS['TCA'][$this->theTable]['ctrl']['fe_crgroup_id'];
1115 if (is_array($dataArray['usergroup'])) {
1116 list($tmpDataArray[$field]) = $dataArray['usergroup'];
1117 } else {
1118 $tmpArray = explode(',', $dataArray['usergroup']);
1119 list($tmpDataArray[$field]) = $tmpArray;
1120 }
1121 $tmpDataArray[$field] = intval($tmpDataArray[$field]);
1122 $extraList .= ',' . $field;
1123 }
1124 if (!empty($tmpDataArray)) {
1125 $res = $this->updateRecord($newId, $tmpDataArray, $extraList);
1126 }
1127 }
1128 $dataArray['uid'] = $newId;
1129 $this->updateMMRelations($dataArray);
1130 $hookClassArray = is_array($GLOBALS['TYPO3_CONF_VARS']['EXTCONF'][$this->extensionKey][$this->prefixId]['model']) ? $GLOBALS['TYPO3_CONF_VARS']['EXTCONF'][$this->extensionKey][$this->prefixId]['model'] : array();
1131 foreach ($hookClassArray as $classRef) {
1132 $hookObj = GeneralUtility::makeInstance($classRef);
1133 if (is_object($hookObj) && method_exists($hookObj, 'afterSave')) {
1134 $dataArray = $hookObj->afterSave($this->theTable, $cmdKey, $dataArray);
1135 }
1136 }
1137 $this->setSaved(true);
1138
1139 $newRow = $GLOBALS['TSFE']->sys_page->getRawRecord($this->theTable, $newId);
1140 if (is_array($newRow)) {
1141 $newRow = $this->parseIncomingData($newRow);
1142 $this->modifyRow($newRow, true);
1143 // Call all afterSaveCreate hooks after the record has been created and saved
1144 if (is_array ($GLOBALS['TYPO3_CONF_VARS']['EXTCONF'][$this->extensionKey][$this->prefixId]['registrationProcess'])) {
1145 foreach ($GLOBALS['TYPO3_CONF_VARS']['EXTCONF'][$this->extensionKey][$this->prefixId]['registrationProcess'] as $classRef) {
1146 $hookObj = GeneralUtility::makeInstance($classRef);
1147 if (method_exists($hookObj, 'registrationProcess_afterSaveCreate')) {
1148 $hookObj->registrationProcess_afterSaveCreate(
1149 $this->theTable,
1150 $dataArray,
1151 $origArray,
1152 $token,
1153 $newRow,
1154 $cmd,
1155 $cmdKey,
1156 $pid,
1157 $extraList,
1158 $this
1159 );
1160 }
1161 }
1162 }
1163 } else {
1164 $newRow = array();
1165 $this->setError('###TEMPLATE_NO_PERMISSIONS###');
1166 $this->setSaved(false);
1167 $rc = 0;
1168 }
1169 }
1170 break;
1171 }
1172 return $rc;
1173 }
1174
1175 /**
1176 * Checks if a frontend user is allowed to edit a certain record
1177 *
1178 * @param string $table The table name, found in $GLOBALS['TCA']
1179 * @param array $row The record data array for the record in question
1180 * @param array $feUserRow The array of the fe_user which is evaluated, typ. $GLOBALS['TSFE']->fe_user->user
1181 * @param string $allowedGroups Commalist of the only fe_groups uids which may edit the record. If not set, then the usergroup field of the fe_user is used.
1182 * @param bool|int $feEditSelf TRUE, if the fe_user may edit his own fe_user record.
1183 * @return bool
1184 */
1185 public function DBmayFEUserEdit($table, $row, $feUserRow, $allowedGroups = '', $feEditSelf = 0)
1186 {
1187 if ($allowedGroups) {
1188 $groupList = implode(
1189 ',',
1190 array_intersect(
1191 GeneralUtility::trimExplode(',', $feUserRow['usergroup'], true),
1192 GeneralUtility::trimExplode(',', $allowedGroups, true)
1193 )
1194 );
1195 } else {
1196 $groupList = $feUserRow['usergroup'];
1197 }
1198 $ok = 0;
1199 // Points to the field that allows further editing from frontend if not set. If set the record is locked.
1200 if (!$GLOBALS['TCA'][$table]['ctrl']['fe_admin_lock'] || !$row[$GLOBALS['TCA'][$table]['ctrl']['fe_admin_lock']]) {
1201 // Points to the field (int) that holds the fe_users-id of the creator fe_user
1202 if ($GLOBALS['TCA'][$table]['ctrl']['fe_cruser_id']) {
1203 $rowFEUser = (int)$row[$GLOBALS['TCA'][$table]['ctrl']['fe_cruser_id']];
1204 if ($rowFEUser && $rowFEUser === (int)$feUserRow['uid']) {
1205 $ok = 1;
1206 }
1207 }
1208 // If $feEditSelf is set, fe_users may always edit them selves...
1209 if ($feEditSelf && $table === 'fe_users' && (int)$feUserRow['uid'] === (int)$row['uid']) {
1210 $ok = 1;
1211 }
1212 // Points to the field (int) that holds the fe_group-id of the creator fe_user's first group
1213 if ($GLOBALS['TCA'][$table]['ctrl']['fe_crgroup_id']) {
1214 $rowFEUser = (int)$row[$GLOBALS['TCA'][$table]['ctrl']['fe_crgroup_id']];
1215 if ($rowFEUser) {
1216 if (GeneralUtility::inList($groupList, $rowFEUser)) {
1217 $ok = 1;
1218 }
1219 }
1220 }
1221 }
1222 return $ok;
1223 }
1224
1225 /**
1226 * Insert a record
1227 * @param int $pid The PID value for the record to insert
1228 * @param array $dataArr The data array where key/value pairs are fieldnames/values for the record to insert
1229 * @param string $fieldList Comma list of fieldnames which are allowed to be set. Only values from the data record for fields in this list will be set!!
1230 * @return string The uid of the inserted record
1231 * @return void
1232 */
1233 protected function insertRecord($pid, $dataArr, $fieldList)
1234 {
1235 if (class_exists('TYPO3\\CMS\\Core\\Database\\ConnectionPool')) {
1236 $extraList = 'pid';
1237 if ($GLOBALS['TCA'][$this->theTable]['ctrl']['tstamp']) {
1238 $field = $GLOBALS['TCA'][$this->theTable]['ctrl']['tstamp'];
1239 $dataArr[$field] = $GLOBALS['EXEC_TIME'];
1240 $extraList .= ',' . $field;
1241 }
1242 if ($GLOBALS['TCA'][$this->theTable]['ctrl']['crdate']) {
1243 $field = $GLOBALS['TCA'][$this->theTable]['ctrl']['crdate'];
1244 $dataArr[$field] = $GLOBALS['EXEC_TIME'];
1245 $extraList .= ',' . $field;
1246 }
1247 if ($GLOBALS['TCA'][$this->theTable]['ctrl']['cruser_id']) {
1248 $field = $GLOBALS['TCA'][$this->theTable]['ctrl']['cruser_id'];
1249 $dataArr[$field] = 0;
1250 $extraList .= ',' . $field;
1251 }
1252 if ($GLOBALS['TCA'][$this->theTable]['ctrl']['fe_cruser_id']) {
1253 $field = $GLOBALS['TCA'][$this->theTable]['ctrl']['fe_cruser_id'];
1254 $dataArr[$field] = (int)$this->getTypoScriptFrontendController()->fe_user->user['uid'];
1255 $extraList .= ',' . $field;
1256 }
1257 if ($GLOBALS['TCA'][$this->theTable]['ctrl']['fe_crgroup_id']) {
1258 $field = $GLOBALS['TCA'][$this->theTable]['ctrl']['fe_crgroup_id'];
1259 list($dataArr[$field]) = explode(',', $this->getTypoScriptFrontendController()->fe_user->user['usergroup']);
1260 $dataArr[$field] = (int)$dataArr[$field];
1261 $extraList .= ',' . $field;
1262 }
1263 unset($dataArr['uid']);
1264 if ($pid >= 0) {
1265 $dataArr['pid'] = $pid;
1266 }
1267 $fieldList = implode(',', GeneralUtility::trimExplode(',', $fieldList . ',' . $extraList, true));
1268 $insertFields = [];
1269 foreach ($dataArr as $f => $v) {
1270 if (GeneralUtility::inList($fieldList, $f)) {
1271 $insertFields[$f] = $v;
1272 }
1273 }
1274 $connection = GeneralUtility::makeInstance(\TYPO3\CMS\Core\Database\ConnectionPool::class)->getConnectionForTable($this->theTable);
1275 $queryBuilder = $connection->createQueryBuilder()
1276 ->insert($this->theTable)
1277 ->values($insertFields)
1278 ->execute();
1279 return $connection->lastInsertId($this->theTable);
1280 } else {
1281 // TYPO3 CMS 7 LTS
1282 $this->cObj->DBgetInsert($this->theTable, (int)$pid, $dataArr, $fieldList, true);
1283 return $GLOBALS['TYPO3_DB']->sql_insert_id();
1284 }
1285 }
1286
1287 /**
1288 * Update a record
1289 *
1290 * @param integer $uid The UID of the record from $table which we are going to update
1291 * @param array $dataArr The data array where key/value pairs are fieldnames/values for the record to update.
1292 * @param string $fieldList Comma list of fieldnames which are allowed to be updated. Only values from the data record for fields in this list will be updated!!
1293 * @return bool false
1294 */
1295 public function updateRecord($uid, array $dataArr, $fieldList)
1296 {
1297 if (class_exists('TYPO3\\CMS\\Core\\Database\\ConnectionPool')) {
1298 if ($uid) {
1299 $fields = GeneralUtility::trimExplode(',', $fieldList, true);
1300 $queryBuilder = GeneralUtility::makeInstance(\TYPO3\CMS\Core\Database\ConnectionPool::class)->getQueryBuilderForTable($this->theTable);
1301 $queryBuilder
1302 ->update($this->theTable)
1303 ->where(
1304 $queryBuilder->expr()->eq('uid', $queryBuilder->createNamedParameter((int)$uid, \PDO::PARAM_INT))
1305 );
1306 foreach ($dataArr as $field => $value) {
1307 if (in_array($field, $fields)) {
1308 $queryBuilder->set($field, $value);
1309 }
1310 }
1311 if ($GLOBALS['TCA'][$this->theTable]['ctrl']['tstamp']) {
1312 $queryBuilder->set($GLOBALS['TCA'][$this->theTable]['ctrl']['tstamp'], (int)$GLOBALS['EXEC_TIME']);
1313 }
1314 $queryBuilder->execute();
1315 }
1316 return false;
1317 } else {
1318 // TYPO3 CMS 7 LTS
1319 return $this->cObj->DBgetUpdate($this->theTable, $uid, $dataArr, $fieldList, true);
1320 }
1321 }
1322
1323 /**
1324 * Delete a record
1325 *
1326 * @param integer $uid The UID of the record from $table which we are going to delete
1327 * @return string false.
1328 * @return void
1329 */
1330 public function deleteRecordByUid($uid)
1331 {
1332 if (class_exists('TYPO3\\CMS\\Core\\Database\\ConnectionPool')) {
1333 $uid = (int)$uid;
1334 if (!$uid) {
1335 return false;
1336 }
1337 $connectionPool = GeneralUtility::makeInstance(\TYPO3\CMS\Core\Database\ConnectionPool::class);
1338 if ($GLOBALS['TCA'][$this->theTable]['ctrl']['delete']) {
1339 $queryBuilder = $connectionPool->getQueryBuilderForTable($this->theTable);
1340 $queryBuilder
1341 ->update($this->theTable)
1342 ->where(
1343 $queryBuilder->expr()->eq('uid', $queryBuilder->createNamedParameter($uid, \PDO::PARAM_INT))
1344 )
1345 ->set($GLOBALS['TCA'][$this->theTable]['ctrl']['delete'], 1);
1346 if ($GLOBALS['TCA'][$this->theTable]['ctrl']['tstamp']) {
1347 $queryBuilder->set($GLOBALS['TCA'][$this->theTable]['ctrl']['tstamp'], (int)$GLOBALS['EXEC_TIME']);
1348 }
1349 $queryBuilder->execute();
1350 } else {
1351 $connectionPool->getConnectionForTable($this->theTable)
1352 ->delete(
1353 $this->theTable,
1354 ['uid' => $uid]
1355 );
1356 }
1357 return false;
1358 } else {
1359 // TYPO3 CMS 7 LTS
1360 return $this->cObj->DBgetDelete($this->theTable, $uid, true);
1361 }
1362 }
1363
1364 /**
1365 * Processes a record deletion request
1366 *
1367 * @return void (sets $this->saved)
1368 */
1369 public function deleteRecord(array &$origArray, array &$dataArray)
1370 {
1371 if ($this->conf['delete']) {
1372 // If deleting is enabled
1373 $aCAuth = Authentication::aCAuth($this->parameters->getAuthCode(), $origArray, $this->conf, $this->conf['setfixed.']['DELETE.']['_FIELDLIST']);
1374 if ($GLOBALS['TSFE']->loginUser || $aCAuth) {
1375 // Must be logged in OR be authenticated by the aC code in order to delete
1376 // If the recUid selects a record.... (no check here)
1377 if (is_array($origArray)) {
1378 if ($aCAuth || $this->DBmayFEUserEdit($this->theTable, $origArray, $GLOBALS['TSFE']->fe_user->user, $this->conf['allowedGroups'], $this->conf['fe_userEditSelf'])) {
1379 // Delete the record and display form, if access granted.
1380 // Call all beforeSaveDelete hooks BEFORE the record is deleted
1381 if (is_array ($GLOBALS['TYPO3_CONF_VARS']['EXTCONF'][$this->extensionKey][$this->prefixId]['registrationProcess'])) {
1382 foreach ($GLOBALS['TYPO3_CONF_VARS']['EXTCONF'][$this->extensionKey][$this->prefixId]['registrationProcess'] as $classRef) {
1383 $hookObj = GeneralUtility::makeInstance($classRef);
1384 if (method_exists($hookObj, 'registrationProcess_beforeSaveDelete')) {
1385 $hookObj->registrationProcess_beforeSaveDelete($origArray, $this);
1386 }
1387 }
1388 }
1389 if (!$GLOBALS['TCA'][$this->theTable]['ctrl']['delete'] || $this->conf['forceFileDelete']) {
1390 // If the record is being fully deleted... then remove the images or files attached.
1391 $this->deleteFilesFromRecord($this->getRecUid());
1392 }
1393 $res = $this->deleteRecordByUid($this->getRecUid());
1394 $this->deleteMMRelations($this->getRecUid(), $origArray);
1395 $dataArray = $origArray;
1396 $this->setSaved(true);
1397 } else {
1398 $this->setError('###TEMPLATE_NO_PERMISSIONS###');
1399 }
1400 }
1401 }
1402 }
1403 }
1404
1405 /**
1406 * Delete the files associated with a deleted record
1407 *
1408 * @param string $uid: record id
1409 * @return void
1410 */
1411 public function deleteFilesFromRecord($uid)
1412 {
1413 $rec = $GLOBALS['TSFE']->sys_page->getRawRecord($this->theTable, $uid);
1414 $updateFields = array();
1415 foreach ($GLOBALS['TCA'][$this->theTable]['columns'] as $field => $fieldConf) {
1416 if ($fieldConf['config']['type'] === 'group' && $fieldConf['config']['internal_type'] === 'file') {
1417 $updateFields[$field] = '';
1418 $res = $this->updateRecord($uid, $updateFields, $field);
1419 unset($updateFields[$field]);
1420 $delFileArr = explode(',', $rec[$field]);
1421 foreach ($delFileArr as $n) {
1422 if ($n) {
1423 $fpath = PATH_site . $fieldConf['config']['uploadfolder'] . '/' . $n;
1424 if (@is_file($fpath)) {
1425 @unlink($fpath);
1426 }
1427 }
1428 }
1429 }
1430 }
1431 $hookClassArray = is_array($GLOBALS['TYPO3_CONF_VARS']['EXTCONF'][$this->extensionKey][$this->prefixId]['model']) ? $GLOBALS['TYPO3_CONF_VARS']['EXTCONF'][$this->extensionKey][$this->prefixId]['model'] : array();
1432 foreach ($hookClassArray as $classRef) {
1433 $hookObj = GeneralUtility::makeInstance($classRef);
1434 if (is_object($hookObj) && method_exists($hookObj, 'deleteFileReferences')) {
1435 $dataArray = $hookObj->deleteFileReferences($this->theTable, $uid);
1436 }
1437 }
1438 }
1439
1440 /**
1441 * Check if the value is a correct date in format yyyy-mm-dd
1442 */
1443 protected function fetchDate($value, $dateFormat = 'Y-m-d')
1444 {
1445 $rcArray = array('m' => '', 'd' => '', 'y' => '');
1446 $dateValue = trim($value);
1447 $split = '/-/';
1448 $dateFormatArray = preg_split($split, $dateFormat);
1449 $dateValueArray = preg_split($split, $dateValue);
1450
1451 $max = sizeof($dateFormatArray);
1452 $yearOffset = 0;
1453 for ($i=0; $i < $max; $i++) {
1454
1455 switch($dateFormatArray[$i]) {
1456 // day
1457 // d - day of the month, 2 digits with leading zeros; i.e. "01" to "31"
1458 // j - day of the month without leading zeros; i.e. "1" to "31"
1459 case 'd':
1460 case 'j':
1461 $rcArray['d'] = intval($dateValueArray[$i]);
1462 break;
1463 // month
1464 // m - month; i.e. "01" to "12"
1465 // n - month without leading zeros; i.e. "1" to "12"
1466 case 'm':
1467 case 'n':
1468 $rcArray['m'] = intval($dateValueArray[$i]);
1469 break;
1470 // M - month, textual, 3 letters; e.g. "Jan"
1471 // F - month, textual, long; e.g. "January"
1472 // case 'M','F': ...to be written ;break;
1473 // year
1474
1475 // Y - year, 4 digits; e.g. "1999"
1476 case 'Y':
1477 $rcArray['y'] = intval($dateValueArray[$i]);
1478 break;
1479 // y - year, 2 digits; e.g. "99"
1480 case 'y':
1481 $yearVal = intval($dateValueArray[$i]);
1482 if($yearVal <= 11) {
1483 $rcArray['y'] = '20' . $yearVal;
1484 } else {
1485 $rcArray['y'] = '19' . $yearVal;
1486 }
1487 break;
1488 }
1489 }
1490 return $rcArray;
1491 }
1492
1493
1494 /** evalDate($value)
1495 *
1496 * Check if the value is a correct date in format yyyy-mm-dd
1497 */
1498 protected function evalDate($value, $dateFormat = 'Y-m-d') {
1499 if( !$value) {
1500 return false;
1501 }
1502 $dateArray = $this->fetchDate($value);
1503
1504 if(is_numeric($dateArray['y']) && is_numeric($dateArray['m']) && is_numeric($dateArray['d'])) {
1505 $rc = checkdate($dateArray['m'], $dateArray['d'], $dateArray['y']);
1506 } else {
1507 $rc = false;
1508 }
1509 return $rc;
1510 }
1511
1512 /**
1513 * Update MM relations
1514 *
1515 * @param array $row
1516 * @return void
1517 */
1518 public function updateMMRelations(array $row)
1519 {
1520 $fieldsList = array_keys($row);
1521 if (class_exists('TYPO3\\CMS\\Core\\Database\\ConnectionPool')) {
1522 $connectionPool = GeneralUtility::makeInstance(\TYPO3\CMS\Core\Database\ConnectionPool::class);
1523 }
1524 foreach ($GLOBALS['TCA'][$this->theTable]['columns'] as $colName => $colSettings) {
1525 if (in_array($colName, $fieldsList) && $colSettings['config']['type'] === 'select' && $colSettings['config']['MM']) {
1526 $valuesArray = $row[$colName];
1527 $side = isset($colSettings['config']['MM_opposite_field']) ? 'foreign' : 'local';
1528 $oppositeSide = ($side === 'foreign') ? 'local' : 'foreign';
1529 if (isset($valuesArray) && is_array($valuesArray)) {
1530 if (is_object($connectionPool)) {
1531 $connection = $connectionPool->getConnectionForTable($colSettings['config']['MM']);
1532 $connection->delete(
1533 $colSettings['config']['MM'],
1534 ['uid_' . $side => (int)$row['uid']]
1535 );
1536 if (isset($colSettings['config']['MM_match_fields'])) {
1537 $tablenames = $colSettings['config']['MM_match_fields']['tablenames'];
1538 $fieldname = $colSettings['config']['MM_match_fields']['fieldname'];
1539 }
1540 $insertFields = [
1541 'uid_' . $side => (int)$row['uid'],
1542 'sorting' . ($side === 'foreign' ? '_' . $side : '') => 0
1543 ];
1544 if (isset($tablenames)) {
1545 $insertFields['tablenames'] = $tablenames ?: '';
1546 }
1547 if (isset($fieldname)) {
1548 $insertFields['fieldname'] = $fieldname ?: '';
1549 }
1550 foreach ($valuesArray as $theValue) {
1551 $insertFields['uid_' . $oppositeSide] = (int)$theValue;
1552 $insertFields['sorting' . ($side === 'foreign' ? '_' . $side : '')]++;
1553 $connection->insert(
1554 $colSettings['config']['MM'],
1555 $insertFields
1556 );
1557 }
1558 } else {
1559 // TYPO3 CMS 7 LTS
1560 $res = $GLOBALS['TYPO3_DB']->exec_DELETEquery(
1561 $colSettings['config']['MM'],
1562 'uid_' . $side . '=' . (int)$row['uid']
1563 );
1564 if (isset($colSettings['config']['MM_match_fields'])) {
1565 $tablenames = $colSettings['config']['MM_match_fields']['tablenames'];
1566 $fieldname = $colSettings['config']['MM_match_fields']['fieldname'];
1567 }
1568 $insertFields = [
1569 'uid_' . $side => (int)$row['uid'],
1570 'sorting' . ($side === 'foreign' ? '_' . $side : '') => 0
1571 ];
1572 if (isset($tablenames)) {
1573 $insertFields['tablenames'] = $tablenames ?: '';
1574 }
1575 if (isset($fieldname)) {
1576 $insertFields['fieldname'] = $fieldname ?: '';
1577 }
1578 foreach ($valuesArray as $theValue) {
1579 $insertFields['uid_' . $oppositeSide] = (int)$theValue;
1580 $insertFields['sorting' . ($side === 'foreign' ? '_' . $side : '')]++;
1581 $res = $GLOBALS['TYPO3_DB']->exec_INSERTquery(
1582 $colSettings['config']['MM'],
1583 $insertFields
1584 );
1585 }
1586 }
1587 }
1588 }
1589 }
1590 }
1591
1592 /**
1593 * Delete MM relations
1594 *
1595 * @return void
1596 */
1597 public function deleteMMRelations($uid, array $row = array())
1598 {
1599 $fieldsList = array_keys($row);
1600 if (class_exists('TYPO3\\CMS\\Core\\Database\\ConnectionPool')) {
1601 $connectionPool = GeneralUtility::makeInstance(\TYPO3\CMS\Core\Database\ConnectionPool::class);
1602 }
1603 foreach ($GLOBALS['TCA'][$this->theTable]['columns'] as $colName => $colSettings) {
1604 if (in_array($colName, $fieldsList) && $colSettings['config']['type'] === 'select' && $colSettings['config']['MM']) {
1605 $side = isset($colSettings['config']['MM_opposite_field']) ? 'foreign' : 'local';
1606 if (is_object($connectionPool)) {
1607 $connectionPool
1608 ->getConnectionForTable($colSettings['config']['MM'])
1609 ->delete(
1610 $colSettings['config']['MM'],
1611 ['uid_' . $side => (int)$uid]
1612 );
1613 } else {
1614 // TYPO3 CMS 7 LTS
1615 $res = $GLOBALS['TYPO3_DB']->exec_DELETEquery(
1616 $colSettings['config']['MM'],
1617 'uid_' . $side . '=' . (int)$uid
1618 );
1619 }
1620 }
1621 }
1622 }
1623
1624 /**
1625 * Updates the input array from preview
1626 *
1627 * @param array $inputArr: new values
1628 * @return array updated array
1629 */
1630 public function modifyDataArrForFormUpdate(array $inputArr, $cmdKey)
1631 {
1632 if (is_array($this->conf[$cmdKey.'.']['evalValues.'])) {
1633 foreach ($this->conf[$cmdKey.'.']['evalValues.'] as $theField => $theValue) {
1634 $listOfCommands = GeneralUtility::trimExplode(',', $theValue, true);
1635 foreach ($listOfCommands as $k => $cmd) {
1636 // Parameters after each command are enclosed in brackets [..]
1637 $cmdParts = preg_split('/\[|\]/', $cmd);
1638 $theCmd = trim($cmdParts[0]);
1639 switch ($theCmd) {
1640 case 'twice':
1641 if (isset($inputArr[$theField])) {
1642 if (!isset($inputArr[$theField . '_again'])) {
1643 $inputArr[$theField . '_again'] = $inputArr[$theField];
1644 }
1645 $this->setAdditionalUpdateFields($this->getAdditionalUpdateFields() . ',' . $theField . '_again');
1646 }
1647 break;
1648 }
1649 }
1650 }
1651 }
1652 if (is_array($this->conf['parseValues.'])) {
1653 foreach ($this->conf['parseValues.'] as $theField => $theValue) {
1654 $listOfCommands = GeneralUtility::trimExplode(',', $theValue, true);
1655 foreach ($listOfCommands as $k => $cmd) {
1656 // Parameters after each command are enclosed in brackets [..]
1657 $cmdParts = preg_split('/\[|\]/', $cmd);
1658 $theCmd = trim($cmdParts[0]);
1659 switch ($theCmd) {
1660 case 'multiple':
1661 if (isset($inputArr[$theField])) {
1662 unset($inputArr[$theField]);
1663 }
1664 break;
1665 case 'checkArray':
1666 if ($inputArr[$theField] && !$this->parameters->isPreview()) {
1667 for ($a = 0; $a <= 50; $a++) {
1668 if ($inputArr[$theField] & pow(2, $a)) {
1669 $alt_theField = $theField . '][' . $a;
1670 $inputArr[$alt_theField] = 1;
1671 $this->setAdditionalUpdateFields($this->getAdditionalUpdateFields() . ',' . $alt_theField);
1672 }
1673 }
1674 }
1675 break;
1676 }
1677 }
1678 }
1679 }
1680 foreach ($inputArr as $field => $value) {
1681 if (is_array($value)) {
1682 $value = implode(',', $value);
1683 }
1684 $inputArr[$field] = $value;
1685 }
1686 SecuredData::secureInput($inputArr);
1687 return $inputArr;
1688 }
1689
1690 /**
1691 * Moves first, middle and last name into name
1692 *
1693 * @param array $dataArray: incoming array
1694 * @param string $cmdKey: the command key
1695 * @return void done directly on $dataArray passed by reference
1696 */
1697 public function setName(array &$dataArray, $cmdKey)
1698 {
1699 if (
1700 in_array('name', explode(',', $this->getFieldList()))
1701 && !in_array('name', GeneralUtility::trimExplode(',', $this->conf[$cmdKey . '.']['fields'], true))
1702 && in_array('first_name', GeneralUtility::trimExplode(',', $this->conf[$cmdKey . '.']['fields'], true))
1703 && in_array('last_name', GeneralUtility::trimExplode(',', $this->conf[$cmdKey . '.']['fields'], true))
1704 ) {
1705 // Honour Address List (tt_address) configuration settings
1706 $nameFormat = '';
1707 if ($this->theTable === 'tt_address' && ExtensionManagementUtility::isLoaded('tt_address')) {
1708 $settings = \TYPO3\TtAddress\Utility\SettingsUtility::getSettings();
1709 $nameFormat = $settings->getBackwardsCompatFormat();
1710 }
1711 if (!empty($nameFormat)) {
1712 $dataArray['name'] = sprintf($nameFormat, $dataArray['first_name'], $dataArray['middle_name'], $dataArray['last_name']);
1713 } else {
1714 $dataArray['name'] = trim(trim($dataArray['first_name'])
1715 . ((in_array('middle_name', GeneralUtility::trimExplode(',', $this->conf[$cmdKey . '.']['fields'], true)) && trim($dataArray['middle_name']) != '') ? ' ' . trim($dataArray['middle_name']) : '' )
1716 . ' ' . trim($dataArray['last_name']));
1717 }
1718 }
1719 }
1720
1721 /**
1722 * Moves email into username if useEmailAsUsername is set
1723 *
1724 * @param array $dataArray: the data array
1725 * @param string $cmdKey: the command being processed
1726 * @return void (done directly on array $dataArray)
1727 */
1728 public function setUsername(array &$dataArray, $cmdKey)
1729 {
1730 if ($this->conf[$cmdKey.'.']['useEmailAsUsername'] && $this->theTable === 'fe_users' && GeneralUtility::inList($this->getFieldList(), 'username') && empty($this->evalErrors['email'])) {
1731 $dataArray['username'] = trim($dataArray['email']);
1732 }
1733 }
1734
1735 /**
1736 * Sets the password
1737 *
1738 * @param array $dataArray: the data array
1739 * @param string $cmdKey: the command being processed
1740 * @return void (done directly on array $dataArray)
1741 */
1742 public function setPassword(array &$dataArray, $cmdKey)
1743 {
1744 if ($this->theTable === 'fe_users') {
1745 StorageSecurity::initializeAutoLoginPassword($dataArray);
1746 // We generate an interim password in the case of an invitation
1747 if ($cmdKey === 'invite') {
1748 SessionData::generatePassword($this->extensionKey, $dataArray);
1749 }
1750 // If inviting or if auto login will be required on confirmation, we store an encrypted version of the password
1751 if ($cmdKey === 'invite' || ($cmdKey === 'create' && $this->conf['enableAutoLoginOnConfirmation'] && !$this->conf['enableAutoLoginOnCreate'])) {
1752 StorageSecurity::encryptPasswordForAutoLogin($dataArray);
1753 }
1754 }
1755 }
1756
1757 /**
1758 * Transforms incoming timestamps into dates
1759 *
1760 * @return array parsedArray
1761 */
1762 public function parseIncomingData(array $origArray, $bUnsetZero = true)
1763 {
1764 $parsedArray = $origArray;
1765 if (is_array($this->conf['parseFromDBValues.'])) {
1766 foreach($this->conf['parseFromDBValues.'] as $theField => $theValue) {
1767 $listOfCommands = GeneralUtility::trimExplode(',', $theValue, true);
1768 if (is_array($listOfCommands)) {
1769 foreach ($listOfCommands as $k2 => $cmd) {
1770 // Enable parameters after each command enclosed in brackets [..]
1771 $cmdParts = preg_split('/\[|\]/', $cmd);
1772 $theCmd = trim($cmdParts[0]);
1773 switch($theCmd) {
1774 case 'date':
1775 case 'adodb_date':
1776 if ($origArray[$theField]) {
1777 $parsedArray[$theField] = date('Y-m-d', $origArray[$theField]);
1778 }
1779 if (!$parsedArray[$theField]) {
1780 if ($bUnsetZero) {
1781 unset($parsedArray[$theField]);
1782 } else {
1783 $parsedArray[$theField] = '';
1784 }
1785 }
1786 break;
1787 }
1788 }
1789 }
1790 }
1791 }
1792 return $parsedArray;
1793 }
1794
1795 /**
1796 * Processes data before entering the database
1797 * 1. Transforms outgoing dates into timestamps
1798 * 2. Modifies the select fields into the count if mm tables are used.
1799 * 3. Deletes de-referenced files
1800 *
1801 * @return parsedArray
1802 */
1803 protected function parseOutgoingData($cmdKey, $pid, array $dataArray, array $origArray)
1804 {
1805 $parsedArray = $dataArray;
1806
1807 if (is_array($this->conf['parseToDBValues.'])) {
1808 foreach ($this->conf['parseToDBValues.'] as $theField => $theValue) {
1809 $listOfCommands = GeneralUtility::trimExplode(',', $theValue, true);
1810 foreach($listOfCommands as $k2 => $cmd) {
1811 // Enable parameters after each command enclosed in brackets [..]
1812 $cmdParts = preg_split('/\[|\]/', $cmd);
1813 $theCmd = trim($cmdParts[0]);
1814 if (($theCmd == 'date' || $theCmd == 'adodb_date') && $dataArray[$theField]) {
1815 if (strlen($dataArray[$theField]) == 8) {
1816 $parsedArray[$theField] = substr($dataArray[$theField], 0, 4) . '-' . substr($dataArray[$theField], 4, 2) . '-' . substr($dataArray[$theField], 6, 2);
1817 } else {
1818 $parsedArray[$theField] = $dataArray[$theField];
1819 }
1820 $dateArray = $this->fetchDate($parsedArray[$theField]);
1821 }
1822 switch ($theCmd) {
1823 case 'date':
1824 case 'adodb_date':
1825 if ($dataArray[$theField]) {
1826 $parsedArray[$theField] = mktime(0, 0, 0, $dateArray['m'], $dateArray['d'], $dateArray['y']);
1827 }
1828 break;
1829 case 'deleteUnreferencedFiles':
1830 $fieldConfig = $GLOBALS['TCA'][$this->theTable]['columns'][$theField]['config'];
1831 if (is_array($fieldConfig) && $fieldConfig['type'] === 'group' && $fieldConfig['internal_type'] === 'file' && $fieldConfig['uploadfolder']) {
1832 $uploadPath = $fieldConfig['uploadfolder'];
1833 $origFiles = array();
1834 if (is_array($origArray[$theField])) {
1835 $origFiles = $origArray[$theField];
1836 } else if ($origArray[$theField]) {
1837 $origFiles = GeneralUtility::trimExplode(',', $origArray[$theField], true);
1838 }
1839 $updatedFiles = array();
1840 if (is_array($dataArray[$theField])) {
1841 $updatedFiles = $dataArray[$theField];
1842 } else if ($dataArray[$theField]) {
1843 $updatedFiles = GeneralUtility::trimExplode(',', $dataArray[$theField], true);
1844 }
1845 $unReferencedFiles = array_diff($origFiles, $updatedFiles);
1846 foreach ($unReferencedFiles as $file) {
1847 if(@is_file(PATH_site . $uploadPath . '/' . $file)) {
1848 @unlink(PATH_site . $uploadPath . '/' . $file);
1849 }
1850 }
1851 }
1852 break;
1853 }
1854 }
1855 }
1856 }
1857
1858 $fieldsList = array_keys($parsedArray);
1859 // Invoke the hooks for additional parsing
1860 foreach ($GLOBALS['TCA'][$this->theTable]['columns'] as $colName => $colSettings) {
1861 if (isset($parsedArray[$colName]) || isset($origArray[$colName])) {
1862 $foreignTable = $GLOBALS['TCA'][$this->theTable]['columns'][$colName]['config']['foreign_table'] ?: '';
1863 $hookClassArray = is_array($GLOBALS['TYPO3_CONF_VARS']['EXTCONF'][$this->extensionKey][$this->prefixId][$this->theTable][$colName]) ? $GLOBALS['TYPO3_CONF_VARS']['EXTCONF'][$this->extensionKey][$this->prefixId][$this->theTable][$colName] : array();
1864 foreach ($hookClassArray as $classRef) {
1865 $hookObject = GeneralUtility::makeInstance($classRef);
1866 if (is_object($hookObject) && method_exists($hookObject, 'parseOutgoingData')) {
1867 $hookObject->parseOutgoingData($this->theTable, $colName, $foreignTable, $cmdKey, $pid, $this->conf, $dataArray, $origArray, $parsedArray);
1868 }
1869 }
1870 }
1871 }
1872 // Update the MM relation count field
1873 foreach ($GLOBALS['TCA'][$this->theTable]['columns'] as $colName => $colSettings) {
1874 if (isset($parsedArray[$colName])) {
1875 if (is_array ($parsedArray[$colName])) {
1876 if (
1877 in_array($colName, $fieldsList) &&
1878 $colSettings['config']['type'] == 'select' &&
1879 $colSettings['config']['MM']
1880 ) {
1881 // set the count instead of the comma separated list
1882 if ($parsedArray[$colName]) {
1883 $parsedArray[$colName] = count($parsedArray[$colName]);
1884 } else {
1885 $parsedArray[$colName] = '';
1886 }
1887 } else {
1888 $parsedArray[$colName] = implode (',', $parsedArray[$colName]);
1889 }
1890 }
1891 }
1892 }
1893 return $parsedArray;
1894 }
1895
1896 public function getInError()
1897 {
1898 return $this->inError;
1899 }
1900
1901 /**
1902 * Sets the index $theField of the incoming data array to empty value depending on type of $theField
1903 * as defined in the TCA for $theTable
1904 *
1905 * @param string $theTable: the name of the table
1906 * @param string $theField: the name of the field
1907 * @param array $dataArray: the incoming data array
1908 * @return void
1909 */
1910 protected function setEmptyIfAbsent($theField, array &$dataArray) {
1911 if (!isset($dataArray[$theField])) {
1912 $fieldConfig = $GLOBALS['TCA'][$this->theTable]['columns'][$theField]['config'];
1913 if (is_array($fieldConfig)) {
1914 $type = $fieldConfig['type'];
1915 switch ($type) {
1916 case 'check':
1917 case 'radio':
1918 $dataArray[$theField] = 0;
1919 break;
1920 case 'input':
1921 $eval = $fieldConfig['eval'];
1922 switch ($eval) {
1923 case 'int':
1924 case 'date':
1925 case 'datetime':
1926 case 'time':
1927 case 'timesec':
1928 $dataArray[$theField] = 0;
1929 break;
1930 default:
1931 $dataArray[$theField] = '';
1932 break;
1933 }
1934 break;
1935 default:
1936 $dataArray[$theField] = '';
1937 break;
1938 }
1939 } else {
1940 $dataArray[$theField] = '';
1941 }
1942 }
1943 }
1944
1945 /**
1946 * Adds the fields coming from other tables via MM tables
1947 *
1948 * @param array $dataArray: the record array
1949 * @return array the modified data array
1950 */
1951 public function modifyTcaMMfields(array $dataArray, &$modArray)
1952 {
1953 $rcArray = $dataArray;
1954 if (class_exists('TYPO3\\CMS\\Core\\Database\\ConnectionPool')) {
1955 $connectionPool = GeneralUtility::makeInstance(\TYPO3\CMS\Core\Database\ConnectionPool::class);
1956 }
1957 foreach ($GLOBALS['TCA'][$this->theTable]['columns'] as $colName => $colSettings) {
1958 $colConfig = $colSettings['config'];
1959 switch ($colConfig['type']) {
1960 case 'select':
1961 if ($colConfig['MM'] && $colConfig['foreign_table']) {
1962 $side = isset($colConfig['MM_opposite_field']) ? 'foreign' : 'local';
1963 $oppositeSide = ($side === 'foreign') ? 'local' : 'foreign';
1964 $valueArray = [];
1965 if (class_exists('TYPO3\\CMS\\Core\\Database\\ConnectionPool')) {
1966 $queryBuilder = $connectionPool->getQueryBuilderForTable($colConfig['MM']);
1967 $query = $queryBuilder
1968 ->select('uid_' . $oppositeSide)
1969 ->from($colConfig['MM'])
1970 ->where(
1971 $queryBuilder->expr()->eq('uid_' . $side, $queryBuilder->createNamedParameter((int)$dataArray['uid'], \PDO::PARAM_INT))
1972 )
1973 ->execute();
1974 while ($row = $query->fetch()) {
1975 $valueArray[] = $row['uid_' . $oppositeSide];
1976 }
1977 } else {
1978 // TYPO3 CMS 7 LTS
1979 $where = 'uid_' . $side . ' = ' . (int)$dataArray['uid'];
1980 $res = $GLOBALS['TYPO3_DB']->exec_SELECTquery('uid_' . $oppositeSide, $colConfig['MM'], $where);
1981 while ($row = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($res)) {
1982 $valueArray[] = $row['uid_' . $oppositeSide];
1983 }
1984 }
1985 $rcArray[$colName] = implode(',', $valueArray);
1986 $modArray[$colName] = $rcArray[$colName];
1987 }
1988 break;
1989 }
1990 }
1991 return $rcArray;
1992 }
1993
1994 /**
1995 * Modifies the incoming data row
1996 * Adds checkboxes which have been unset. This means that no field will be present for them.
1997 * Fetches the former values of select boxes
1998 *
1999 * @param array $dataArray: the input data array will be changed
2000 * @return void
2001 */
2002 public function modifyRow(array &$dataArray, $bColumnIsCount = true)
2003 {
2004 $fieldsList = array_keys($dataArray);
2005 foreach ($GLOBALS['TCA'][$this->theTable]['columns'] as $colName => $colSettings) {
2006 $colConfig = $colSettings['config'];
2007 if (!$colConfig || !is_array($colConfig)) {
2008 continue;
2009 }
2010 if ($colConfig['maxitems'] > 1) {
2011 $bMultipleValues = true;
2012 } else {
2013 $bMultipleValues = false;
2014 }
2015 switch ($colConfig['type']) {
2016 case 'group':
2017 $bMultipleValues = true;
2018 break;
2019 case 'inline':
2020 if ($colConfig['foreign_table'] === 'sys_file_reference' && isset($dataArray[$colName]) && !is_array($dataArray[$colName])) {
2021 $dataArray[$colName] = unserialize($dataArray[$colName]);
2022 }
2023 break;
2024 case 'select':
2025 $value = $dataArray[$colName];
2026 // checkbox from which nothing has been selected
2027 if ($value == 'Array') {
2028 $dataArray[$colName] = $value = '';
2029 }
2030 if (in_array($colName, $fieldsList) && $colConfig['MM'] && isset($value)) {
2031 if ($value == '' || is_array($value)) {
2032 // the values from the mm table are already available as an array
2033 } else if ($bColumnIsCount) {
2034 $side = isset($colConfig['MM_opposite_field']) ? 'foreign' : 'local';
2035 $oppositeSide = ($side === 'foreign') ? 'local' : 'foreign';
2036 $valuesArray = [];
2037 if (class_exists('TYPO3\\CMS\\Core\\Database\\ConnectionPool')) {
2038 $queryBuilder = GeneralUtility::makeInstance(\TYPO3\CMS\Core\Database\ConnectionPool::class)
2039 ->getQueryBuilderForTable($colConfig['MM']);
2040 $queryBuilder->getRestrictions()->removeAll();
2041 $query = $queryBuilder
2042 ->select('uid_local','uid_foreign', 'sorting' . ($side === 'foreign' ? '_' . $side : ''))
2043 ->from($colConfig['MM'])
2044 ->where(
2045 $queryBuilder->expr()->eq('uid_' . $side, $queryBuilder->createNamedParameter((int)$dataArray['uid'], \PDO::PARAM_INT))
2046 )
2047 ->orderBy('sorting' . ($side === 'foreign' ? '_' . $side : ''))
2048 ->execute();
2049 while ($row = $query->fetch()) {
2050 $valuesArray[] = $row['uid_' . $oppositeSide];
2051 }
2052 } else {
2053 // TYPO3 CMS 7 LTS
2054 $res = $GLOBALS['TYPO3_DB']->exec_SELECTquery(
2055 'uid_local,uid_foreign,sorting' . ($side === 'foreign' ? '_' . $side : ''),
2056 $colConfig['MM'],
2057 'uid_' . $side . '=' . (int)$dataArray['uid'],
2058 '',
2059 'sorting' . ($side === 'foreign' ? '_' . $side : '')
2060 );
2061 while ($row = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($res)) {
2062 $valuesArray[] = $row['uid_' . $oppositeSide];
2063 }
2064 }
2065 $dataArray[$colName] = $valuesArray;
2066 } else {
2067 $dataArray[$colName] = GeneralUtility::trimExplode (',', $value, true);
2068 }
2069 }
2070 break;
2071 case 'check':
2072 if (is_array($colConfig['items'])) {
2073 $value = $dataArray[$colName];
2074 if(is_array($value)) {
2075 $dataArray[$colName] = 0;
2076 // Combine values to one hexidecimal number
2077 foreach ($value AS $dec) {
2078 $dataArray[$colName] |= (1 << $dec);
2079 }
2080 }
2081 } else if (isset($dataArray[$colName])) {
2082 if ($dataArray[$colName] != '0') {
2083 $dataArray[$colName] = '1';
2084 } else {
2085 $dataArray[$colName] = '0';
2086 }
2087 } else {
2088 $dataArray[$colName] = '0';
2089 }
2090 break;
2091 default:
2092 break;
2093 }
2094 if ($bMultipleValues) {
2095 $value = $dataArray[$colName];
2096 if (isset($value) && !is_array($value)) {
2097 $dataArray[$colName] = GeneralUtility::trimExplode (',', $value, true);
2098 }
2099 }
2100 }
2101 if ($this->staticInfoObj !== null && $dataArray['static_info_country']) {
2102 // empty zone if it does not fit to the provided country
2103 $zoneArray = $this->staticInfoObj->initCountrySubdivisions($dataArray['static_info_country']);
2104 if (!isset($zoneArray[$dataArray['zone']])) {
2105 $dataArray['zone'] = '';
2106 }
2107 }
2108 }
2109
2110 /**
2111 * Get the extension key
2112 *
2113 * @return string the extension key
2114 */
2115 public function getExtensionKey()
2116 {
2117 return $this->extensionKey;
2118 }
2119
2120 /**
2121 * Get the prefix id
2122 *
2123 * @return string the prefix id
2124 */
2125 public function getPrefixId()
2126 {
2127 return $this->prefixId;
2128 }
2129
2130 /**
2131 * Get the table in use
2132 *
2133 * @return string the table in use
2134 */
2135 public function getTable()
2136 {
2137 return $this->theTable;
2138 }
2139
2140 /**
2141 * @return TypoScriptFrontendController
2142 */
2143 protected function getTypoScriptFrontendController()
2144 {
2145 return $GLOBALS['TSFE'];
2146 }
2147 }