[BUGFIX] add empty Classes folder
[TYPO3CMS/Extensions/direct_mail_subscription.git] / fe_adminLib.inc
1 <?php
2 /***************************************************************
3 *  Copyright notice
4 *
5 *  (c) 1999-2009 Kasper Skårhøj (kasperYYYY@typo3.com)
6 *  All rights reserved
7 *
8 *  This script is part of the TYPO3 project. The TYPO3 project is
9 *  free software; you can redistribute it and/or modify
10 *  it under the terms of the GNU General Public License as published by
11 *  the Free Software Foundation; either version 2 of the License, or
12 *  (at your option) any later version.
13 *
14 *  The GNU General Public License can be found at
15 *  http://www.gnu.org/copyleft/gpl.html.
16 *  A copy is found in the textfile GPL.txt and important notices to the license
17 *  from the author is found in LICENSE.txt distributed with these scripts.
18 *
19 *
20 *  This script is distributed in the hope that it will be useful,
21 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
22 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
23 *  GNU General Public License for more details.
24 *
25 *  This copyright notice MUST APPEAR in all copies of the script!
26 ***************************************************************/
27 use TYPO3\CMS\Core\Utility\ArrayUtility;
28 use TYPO3\CMS\Core\Utility\DebugUtility;
29 use TYPO3\CMS\Core\Utility\ExtensionManagementUtility;
30 use TYPO3\CMS\Core\Utility\GeneralUtility;
31 use TYPO3\CMS\Core\Utility\MathUtility;
32
33 /**
34  * FE admin lib.
35  *
36  * $Id: fe_adminLib.inc 10317 2011-01-26 00:56:49Z baschny $
37  * Revised for TYPO3 3.6 June/2003 by Kasper Skårhøj
38  *
39  * @author Kasper Skårhøj <kasperYYYY@typo3.com>
40  */
41
42 /**
43  * This library provides a HTML-template file based framework for Front End creating/editing/deleting records authenticated by email or fe_user login.
44  * It is used in the extensions "direct_mail_subscription" and "feuser_admin" (and the deprecated(!) static template "plugin.feadmin.dmailsubscription" and "plugin.feadmin.fe_users" which are the old versions of these two extensions)
45  * Further the extensions "t3consultancies" and "t3references" also uses this library but contrary to the "direct_mail_subscription" and "feuser_admin" extensions which relies on external HTML templates which must be adapted these two extensions delivers the HTML template code from inside.
46  * Generally the fe_adminLib appears to be hard to use. Personally I feel turned off by all the template-file work involved and since it is very feature rich (and for that sake pretty stable!) there are lots of things that can go wrong - you feel. Therefore I like the concept used by "t3consultancies"/"t3references" since those extensions uses the library by supplying the HTML-template code automatically.
47  * Suggestions for improvement and streamlining is welcome so this powerful class could be used more and effectively.
48  *
49  * @author Kasper Skårhøj <kasperYYYY@typo3.com>
50  */
51 class user_feAdmin
52 {
53     // External, static:
54     /*
55      * If true, values from the record put into markers going out into HTML will be passed through htmlspecialchars()!
56      *
57      * @var bool
58      */
59     public $recInMarkersHSC = true;
60
61     public $dataArr = array();
62     public $failureMsg = array();
63     public $theTable = '';
64     public $thePid = 0;
65     public $markerArray = array();
66     public $templateCode = '';
67
68     /* @var $cObj \TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer */
69     public $cObj;
70
71     public $conf = array();
72
73     public $cmd;
74     public $preview;
75     public $backURL;
76     public $recUid;
77
78     /*
79      * Is set if data did not have the required fields set.
80      *
81      * @var int
82      */
83     public $failure = 0;
84     public $error = '';
85
86     /*
87      * is set if data is saved
88      *
89      * @var int
90      */
91     public $saved = 0;
92     public $requiredArr;
93     public $currentArr = array();
94     public $previewLabel = '';
95
96     /*
97      * '&no_cache=1' if you want that parameter sent.
98      *
99      * @var string
100      */
101     public $nc = '';
102     public $additionalUpdateFields = '';
103     public $emailMarkPrefix = 'EMAIL_TEMPLATE_';
104     public $codeLength;
105     public $cmdKey;
106
107     /*
108      * Set to a basic_filefunc object
109      *
110      * @var string
111      */
112     public $fileFunc = '';
113
114     /*
115      * This array will hold the names of files transferred to
116      * the uploads folder if any. If the records are NOT saved,
117      * these files should be deleted!! Currently this is not working!
118      *
119      * @var array
120      */
121     public $filesStoredInUploadFolders = array();
122
123         // Internal vars, dynamic:
124     /*
125      * Is loaded with all temporary filenames used
126      * for upload which should be deleted before exit...
127      *
128      * @var array
129      */
130     public $unlinkTempFiles = array();
131
132     /**
133      * Main function. Called from TypoScript.
134      * This
135      * - initializes internal variables,
136      * - fills in the markerArray with default substitution string
137      * - saves/emails if such commands are sent
138      * - calls functions for display of the screen for editing/creation/deletion etc.
139      *
140      * @param string $content Empty string, ignore.
141      * @param array  $conf    TypoScript properties following the USER_INT object which uses this library
142      *
143      * @return string HTML content
144      */
145     public function init($content, $conf)
146     {
147         $this->conf = $conf;
148
149             // template file is fetched.
150         $this->templateCode = $this->conf['templateContent'] ? $this->conf['templateContent'] : $this->cObj->fileResource($this->conf['templateFile']);
151
152             // Getting the cmd var
153         $this->cmd = (string) GeneralUtility::_GP('cmd');
154             // Getting the preview var
155         $this->preview = (string) GeneralUtility::_GP('preview');
156             // backURL is a given URL to return to when login is performed
157         $this->backURL = GeneralUtility::_GP('backURL');
158
159         if (strstr($this->backURL, '"') ||
160             strstr($this->backURL, "'") ||
161             preg_match('/(javascript|vbscript):/i', $this->backURL) ||
162             stristr($this->backURL, 'fromcharcode') ||
163             strstr($this->backURL, '<') ||
164             strstr($this->backURL, '>')
165             ) {
166             // Clear backURL if it seems to contain XSS code - only URLs are allowed
167             $this->backURL = '';
168         }
169             // Remove host from URL: Make sure that $this->backURL maps to the current site
170         $this->backURL = preg_replace('|[A-Za-z]+://[^/]+|', '', $this->backURL);
171             // Uid to edit:
172         $this->recUid = GeneralUtility::_GP('rU');
173             // Authentication code:
174         $this->authCode = GeneralUtility::_GP('aC');
175             // get table
176         $this->theTable = $this->conf['table'];
177             // link configuration
178         $linkConf = is_array($this->conf['formurl.']) ? $this->conf['formurl.'] : array();
179             // pid
180         $this->thePid = intval($this->conf['pid']) ? intval($this->conf['pid']) : $GLOBALS['TSFE']->id;
181             //
182         $this->codeLength = intval($this->conf['authcodeFields.']['codeLength']) ? intval($this->conf['authcodeFields.']['codeLength']) : 8;
183
184             // Setting the hardcoded lists of fields allowed for editing and creation.
185         $this->fieldList = implode(',', GeneralUtility::trimExplode(',', $GLOBALS['TCA'][$this->theTable]['feInterface']['fe_admin_fieldList'], 1));
186
187             // globally substituted markers, fonts and colors.
188         $splitMark = md5(microtime());
189         list($this->markerArray['###GW1B###'], $this->markerArray['###GW1E###']) = explode($splitMark, $this->cObj->stdWrap($splitMark, $this->conf['wrap1.']));
190         list($this->markerArray['###GW2B###'], $this->markerArray['###GW2E###']) = explode($splitMark, $this->cObj->stdWrap($splitMark, $this->conf['wrap2.']));
191
192         $this->markerArray['###GC1###'] = $this->cObj->stdWrap($this->conf['color1'], $this->conf['color1.']);
193         $this->markerArray['###GC2###'] = $this->cObj->stdWrap($this->conf['color2'], $this->conf['color2.']);
194         $this->markerArray['###GC3###'] = $this->cObj->stdWrap($this->conf['color3'], $this->conf['color3.']);
195
196         if (intval($this->conf['no_cache']) &&
197                 !isset($linkConf['no_cache'])
198         ) {
199             // needed for backwards compatibility
200             $linkConf['no_cache'] = 1;
201         }
202         if (!$linkConf['parameter']) {
203             $linkConf['parameter'] = $GLOBALS['TSFE']->id;
204         }
205         if (!$linkConf['additionalParams']) {
206             // needed for backwards compatibility
207             $linkConf['additionalParams'] = $this->conf['addParams'];
208         }
209
210         $linkConf['forceAbsoluteUrl'] = '1';
211
212         $formUrl = $this->cObj->typoLink_URL($linkConf);
213
214         if (!strstr($formUrl, '?')) {
215             $formUrl .= '?';
216         }
217
218             // Initialize markerArray, setting FORM_URL and HIDDENFIELDS
219         $this->markerArray['###FORM_URL###'] = $formUrl;
220         $this->markerArray['###FORM_URL_ENC###'] = rawurlencode($this->markerArray['###FORM_URL###']);
221         $this->markerArray['###FORM_URL_HSC###'] = htmlspecialchars($this->markerArray['###FORM_URL###']);
222
223         $this->markerArray['###BACK_URL###'] = $this->backURL;
224         $this->markerArray['###BACK_URL_ENC###'] = rawurlencode($this->markerArray['###BACK_URL###']);
225         $this->markerArray['###BACK_URL_HSC###'] = htmlspecialchars($this->markerArray['###BACK_URL###']);
226
227         $this->markerArray['###THE_PID###'] = $this->thePid;
228         $this->markerArray['###REC_UID###'] = $this->recUid;
229         $this->markerArray['###AUTH_CODE###'] = $this->authCode;
230         $this->markerArray['###THIS_ID###'] = $GLOBALS['TSFE']->id;
231         // emtpying this marker, since the FORM_URL is absolute URL => typolink with forceAbsoluteUrl
232         $this->markerArray['###THIS_URL###'] = '';
233         $this->markerArray['###HIDDENFIELDS###'] = ($this->cmd ? '<input type="hidden" name="cmd" value="'.htmlspecialchars($this->cmd).'" />' : '').
234             ($this->authCode ? '<input type="hidden" name="aC" value="'.htmlspecialchars($this->authCode).'" />' : '').
235             ($this->backURL ? '<input type="hidden" name="backURL" value="'.htmlspecialchars($this->backURL).'" />' : '');
236
237             // Setting cmdKey which is either 'edit' or 'create'
238         switch ($this->cmd) {
239             case 'edit':
240                 $this->cmdKey = 'edit';
241                 break;
242             default:
243                 $this->cmdKey = 'create';
244         }
245
246             // Setting requiredArr to the fields in 'required' intersected field the total field list in order to remove invalid fields.
247         $this->requiredArr = array_intersect(
248             GeneralUtility::trimExplode(',', $this->conf[$this->cmdKey.'.']['required'], 1),
249             GeneralUtility::trimExplode(',', $this->conf[$this->cmdKey.'.']['fields'], 1)
250         );
251
252             // Setting incoming data. Non-stripped
253         $fe = GeneralUtility::_GP('FE');
254
255         // Incoming data.
256         $this->dataArr = $fe[$this->theTable];
257
258             // Checking template file and table value
259         if (!$this->templateCode) {
260             $content = 'No template file found: '.$this->conf['templateFile'];
261
262             return $content;
263         }
264
265         if (!$this->theTable || !$this->fieldList) {
266             $content = 'Wrong table: '.$this->theTable;
267             // Not listed or editable table!
268             return $content;
269         }
270
271         // *****************
272         // If data is submitted, we take care of it here.
273         // *******************
274         if ($this->cmd == 'delete' && !$this->preview && !GeneralUtility::_GP('doNotSave')) {
275             // Delete record if delete command is sent + the preview flag is NOT set.
276             $this->deleteRecord();
277         }
278             // If incoming data is seen...
279         if (is_array($this->dataArr)){
280             if (GeneralUtility::compat_version('7.0')) {
281                 $countDataArray = ArrayUtility::removeArrayEntryByValue(array_keys($this->dataArr), 'captcha');
282             } else {
283                 $countDataArray = GeneralUtility::removeArrayEntryByValue(array_keys($this->dataArr), 'captcha');
284             }
285         }
286
287         if (is_array($this->dataArr) && count($countDataArray)) {
288             // Evaluation of data:
289             $this->parseValues();
290             $this->overrideValues();
291             $this->evalValues();
292             if ($this->conf['evalFunc']) {
293                 $this->dataArr = $this->userProcess('evalFunc', $this->dataArr);
294             }
295
296                 // if not preview and no failures, then set data...
297             if (!$this->failure && !$this->preview && !GeneralUtility::_GP('doNotSave')) {
298                 // doNotSave is a global var (eg a 'Cancel' submit button) that prevents the data from being processed
299                 $this->save();
300             } else {
301                 if ($this->conf['debug']) {
302                     DebugUtility::debug($this->failure);
303                 }
304             }
305         } else {
306             // If no incoming data, this will set the default values.
307             $this->defaultValues();
308             // No preview if data is not received
309             $this->preview = 0;
310         }
311
312         if ($this->failure) {
313             // No preview flag if a evaluation failure has occured
314             $this->preview = 0;
315         }
316
317         // Setting preview label prefix.
318         $this->previewLabel = $this->preview ? '_PREVIEW' : '';
319
320             // *********************
321             // DISPLAY FORMS:
322             // ***********************
323         if ($this->saved) {
324             // Clear page cache
325             $this->clearCacheIfSet();
326             $this->setNoCacheHeader();
327
328                 // Displaying the page here that says, the record has been saved. You're able to include the saved values by markers.
329             switch ($this->cmd) {
330                 case 'delete':
331                     $key = 'DELETE';
332                     break;
333                 case 'edit':
334                     $key = 'EDIT';
335                     break;
336                 default:
337                     $key = 'CREATE';
338             }
339                 // Output message
340             $templateCode = $this->cObj->getSubpart($this->templateCode, '###TEMPLATE_'.$key.'_SAVED###');
341             $this->setCObjects($templateCode, $this->currentArr);
342             $markerArray = $this->cObj->fillInMarkerArray($this->markerArray, $this->currentArr, '', true, 'FIELD_', $this->recInMarkersHSC);
343             $content = $this->cObj->substituteMarkerArray($templateCode, $markerArray);
344
345                 // email message:
346             $this->compileMail(
347                 $key.'_SAVED',
348                 array($this->currentArr),
349                 $this->currentArr[$this->conf['email.']['field']],
350                 $this->conf['setfixed.']
351             );
352         } elseif ($this->error) {
353             // If there was an error, we return the template-subpart with the error message
354             $templateCode = $this->cObj->getSubpart($this->templateCode, $this->error);
355             $this->setCObjects($templateCode);
356             $content = $this->cObj->substituteMarkerArray($templateCode, $this->markerArray);
357         } else {
358             // Finally, if there has been no attempt to save. That is either preview or just displaying and empty or not correctly filled form:
359             if (!$this->cmd) {
360                 $this->cmd = $this->conf['defaultCmd'];
361             }
362
363             if ($this->conf['debug']) {
364                 DebugUtility::debug('Display form: '.$this->cmd, 1);
365             }
366
367             switch ($this->cmd) {
368                 case 'setfixed':
369                     $content = $this->procesSetFixed();
370                     break;
371                 case 'infomail':
372                     $content = $this->sendInfoMail();
373                     break;
374                 case 'delete':
375                     $content = $this->displayDeleteScreen();
376                     break;
377                 case 'edit':
378                     $content = $this->displayEditScreen();
379                     break;
380                 case 'create':
381                     $content = $this->displayCreateScreen();
382                     break;
383             }
384         }
385
386             // Delete temp files:
387         foreach ($this->unlinkTempFiles as $tempFileName) {
388             GeneralUtility::unlink_tempfile($tempFileName);
389         }
390
391             // Return content:
392         return $content;
393     }
394
395     /*****************************************
396      *
397      * Data processing
398      *
399      *****************************************/
400
401     /**
402      * Performs processing on the values found in the input data array, $this->dataArr.
403      * The processing is done according to configuration found in TypoScript
404      * Examples of this could be to force a value to an integer,
405      * remove all non-alphanumeric characters, trimming a value, upper/lowercase it,
406      * or process it due to special types like files submitted etc.
407      * Called from init() if the $this->dataArr is found to be an array.
408      *
409      * @see init()
410      */
411     public function parseValues()
412     {
413         if (is_array($this->conf['parseValues.'])) {
414             foreach ($this->conf['parseValues.'] as $theField => $theValue) {
415                 $listOfCommands = GeneralUtility::trimExplode(',', $theValue, 1);
416                 foreach ($listOfCommands as $cmd) {
417                     // Point is to enable parameters after each command enclosed in brackets [..]. These will be in position 1 in the array.
418                     $cmdParts = preg_split('/\[|\]/', $cmd);
419                     $theCmd = trim($cmdParts[0]);
420                     switch ($theCmd) {
421                         case 'int':
422                             $this->dataArr[$theField] = intval($this->dataArr[$theField]);
423                             break;
424                         case 'lower':
425                             // see upper
426                         case 'upper':
427                             $this->dataArr[$theField] = $this->cObj->caseshift($this->dataArr[$theField], $theCmd);
428                             break;
429                         case 'nospace':
430                             $this->dataArr[$theField] = str_replace(' ', '', $this->dataArr[$theField]);
431                             break;
432                         case 'alpha':
433                             $this->dataArr[$theField] = preg_replace('/[^a-zA-Z]/', '', $this->dataArr[$theField]);
434                             break;
435                         case 'num':
436                             $this->dataArr[$theField] = preg_replace('/[^0-9]/', '', $this->dataArr[$theField]);
437                             break;
438                         case 'alphanum':
439                             $this->dataArr[$theField] = preg_replace('/[^a-zA-Z0-9]/', '', $this->dataArr[$theField]);
440                             break;
441                         case 'alphanum_x':
442                             $this->dataArr[$theField] = preg_replace('/[^a-zA-Z0-9_-]/', '', $this->dataArr[$theField]);
443                             break;
444                         case 'trim':
445                             $this->dataArr[$theField] = trim($this->dataArr[$theField]);
446                             break;
447                         case 'random':
448                             $this->dataArr[$theField] = substr(md5(uniqid(microtime(), 1)), 0, intval($cmdParts[1]));
449                             break;
450                         case 'files':
451                             if ($this->cmdKey == 'create' && !GeneralUtility::_GP('doNotSave')) {
452                                 $this->processFiles($cmdParts, $theField);
453                             } else {
454                                 // Fields with files cannot be edited - only created.
455                                 unset($this->dataArr[$theField]);
456                             }
457                             break;
458                         case 'setEmptyIfAbsent':
459                             if (!isset($this->dataArr[$theField])) {
460                                 $this->dataArr[$theField] = '';
461                             }
462                             break;
463                         case 'multiple':
464                             if (is_array($this->dataArr[$theField])) {
465                                 $this->dataArr[$theField] = implode(',', $this->dataArr[$theField]);
466                             }
467                             break;
468                         case 'checkArray':
469                             if (is_array($this->dataArr[$theField])) {
470                                 $val = 0;
471                                 foreach ($this->dataArr[$theField] as $kk => $vv) {
472                                     $kk = MathUtility::forceIntegerInRange($kk, 0);
473
474                                     if ($kk <= 30) {
475                                         if ($vv) {
476                                             $val |= pow(2, $kk);
477                                         }
478                                     }
479                                 }
480                                 $this->dataArr[$theField] = $val;
481                             } else {
482                                 $this->dataArr[$theField] = 0;
483                             }
484                             break;
485                         case 'uniqueHashInt':
486                             $otherFields = GeneralUtility::trimExplode(';', $cmdParts[1], 1);
487                             $hashArray = array();
488                             foreach ($otherFields as $fN) {
489                                 $vv = $this->dataArr[$fN];
490                                 $vv = preg_replace('/[[:space:]]/', '', $vv);
491                                 $vv = preg_replace('/[^[:alnum:]]/', '', $vv);
492                                 $vv = strtolower($vv);
493                                 $hashArray[] = $vv;
494                             }
495                             $this->dataArr[$theField] = hexdec(substr(md5(serialize($hashArray)), 0, 8));
496                             break;
497                         default:
498                     }
499                 }
500             }
501         }
502     }
503
504     /**
505      * Processing of files.
506      * NOTICE: for now files can be handled only on creation of records.
507      * But a more advanced feature is that PREVIEW of files is handled.
508      *
509      * @param array  $cmdParts Array with cmd-parts (from parseValues()). This will for example contain information about allowed file extensions and max size of uploaded files.
510      * @param string $theField The fieldname with the files.
511      *
512      * @see parseValues()
513      */
514     protected function processFiles($cmdParts, $theField)
515     {
516
517             // First, make an array with the filename and file reference, whether the file is just uploaded or a preview
518         $filesArr = array();
519
520         if (is_string($this->dataArr[$theField])) {
521             // files from preview.
522             $tmpArr = explode(',', $this->dataArr[$theField]);
523             foreach ($tmpArr as $val) {
524                 $valParts = explode('|', $val);
525                 $filesArr[] = array(
526                     'name' => $valParts[1],
527                     'tmp_name' => PATH_site.'typo3temp/'.$valParts[0],
528                 );
529             }
530         } elseif (is_array($_FILES['FE'][$this->theTable][$theField]['name'])) {
531             // Files from upload
532             foreach ($_FILES['FE'][$this->theTable][$theField]['name'] as $kk => $vv) {
533                 if ($vv) {
534                     $tmpFile = GeneralUtility::upload_to_tempfile($_FILES['FE'][$this->theTable][$theField]['tmp_name'][$kk]);
535                     if ($tmpFile) {
536                         $this->unlinkTempFiles[] = $tmpFile;
537                         $filesArr[] = array(
538                             'name' => $vv,
539                             'tmp_name' => $tmpFile,
540                         );
541                     }
542                 }
543             }
544         } elseif (is_array($_FILES['FE']['name'][$this->theTable][$theField])) {
545             // Files from upload
546             foreach ($_FILES['FE']['name'][$this->theTable][$theField] as $kk => $vv) {
547                 if ($vv) {
548                     $tmpFile = GeneralUtility::upload_to_tempfile($_FILES['FE']['tmp_name'][$this->theTable][$theField][$kk]);
549                     if ($tmpFile) {
550                         $this->unlinkTempFiles[] = $tmpFile;
551                         $filesArr[] = array(
552                             'name' => $vv,
553                             'tmp_name' => $tmpFile,
554                         );
555                     }
556                 }
557             }
558         }
559
560             // Then verify the files in that array; check existence, extension and size
561         $this->dataArr[$theField] = '';
562         $finalFilesArr = array();
563         if (count($filesArr)) {
564             $extArray = GeneralUtility::trimExplode(';', strtolower($cmdParts[1]), 1);
565             $maxSize = intval($cmdParts[3]);
566             foreach ($filesArr as $infoArr) {
567                 $fI = pathinfo($infoArr['name']);
568                 if (GeneralUtility::verifyFilenameAgainstDenyPattern($fI['name'])) {
569                     if (!count($extArray) || in_array(strtolower($fI['extension']), $extArray)) {
570                         $tmpFile = $infoArr['tmp_name'];
571                         if (@is_file($tmpFile)) {
572                             if (!$maxSize || filesize($tmpFile) < $maxSize * 1024) {
573                                 $finalFilesArr[] = $infoArr;
574                             } elseif ($this->conf['debug']) {
575                                 debug('Size is beyond '.$maxSize.' kb ('.filesize($tmpFile).' bytes) and the file cannot be saved.');
576                             }
577                         } elseif ($this->conf['debug']) {
578                             debug('Surprisingly there was no file for '.$vv.' in '.$tmpFile);
579                         }
580                     } elseif ($this->conf['debug']) {
581                         debug('Extension "'.$fI['extension'].'" not allowed');
582                     }
583                 } elseif ($this->conf['debug']) {
584                     debug('Filename matched illegal pattern.');
585                 }
586             }
587         }
588             // Copy the files in the resulting array to the proper positions based on preview/non-preview.
589         $fileNameList = array();
590         foreach ($finalFilesArr as $infoArr) {
591             if ($this->isPreview()) {        // If the form is a preview form (and data is therefore not going into the database...) do this.
592                 $this->createFileFuncObj();
593                 $fI = pathinfo($infoArr['name']);
594                 $tmpFilename = $this->theTable . '_' . GeneralUtility::shortmd5(uniqid($infoArr['name'])) . '.' . $fI['extension'];
595                 $theDestFile = $this->fileFunc->getUniqueName($this->fileFunc->cleanFileName($tmpFilename), PATH_site . 'typo3temp/');
596                 GeneralUtility::upload_copy_move($infoArr['tmp_name'], $theDestFile);
597                     // Setting the filename in the list
598                 $fI2 = pathinfo($theDestFile);
599                 $fileNameList[] = $fI2['basename'].'|'.$infoArr['name'];
600             } else {
601                 $this->createFileFuncObj();
602                 $GLOBALS['TSFE']->includeTCA();
603                 GeneralUtility::loadTCA($this->theTable);
604                 if (is_array($GLOBALS['TCA'][$this->theTable]['columns'][$theField])) {
605                     $uploadPath = $GLOBALS['TCA'][$this->theTable]['columns'][$theField]['config']['uploadfolder'];
606                 }
607                 if ($uploadPath) {
608                     $theDestFile = $this->fileFunc->getUniqueName($this->fileFunc->cleanFileName($infoArr['name']), PATH_site . $uploadPath);
609                     GeneralUtility::upload_copy_move($infoArr['tmp_name'], $theDestFile);
610                         // Setting the filename in the list
611                     $fI2 = pathinfo($theDestFile);
612                     $fileNameList[] = $fI2['basename'];
613                     $this->filesStoredInUploadFolders[] = $theDestFile;
614                 }
615             }
616                 // Implode the list of filenames
617             $this->dataArr[$theField] = implode(',', $fileNameList);
618         }
619     }
620
621     /**
622      * Overriding values in $this->dataArr if configured for that in TypoScript ([edit/create].overrideValues).
623      *
624      * @see init()
625      */
626     public function overrideValues()
627     {
628         // Addition of overriding values
629         if (is_array($this->conf[$this->cmdKey.'.']['overrideValues.'])) {
630             foreach ($this->conf[$this->cmdKey.'.']['overrideValues.'] as $theField => $theValue) {
631                 $this->dataArr[$theField] = $theValue;
632             }
633         }
634     }
635
636     /**
637      * Called if there is no input array in $this->dataArr. Then this function sets the default values configured in TypoScript.
638      *
639      * @see init()
640      */
641     public function defaultValues()
642     {
643         // Addition of default values
644         if (is_array($this->conf[$this->cmdKey.'.']['defaultValues.'])) {
645             foreach ($this->conf[$this->cmdKey.'.']['defaultValues.'] as $theField => $theValue) {
646                 $this->dataArr[$theField] = $theValue;
647             }
648         }
649     }
650
651     /**
652      * This will evaluate the input values from $this->dataArr to see if they conforms with the requirements configured in TypoScript per field.
653      * For example this could be checking if a field contains a valid email address, a unique value, a value within a certain range etc.
654      * It will populate arrays like $this->failure and $this->failureMsg with error messages (which can later be displayed in the template). Mostly it does NOT alter $this->dataArr (such parsing of values was done by parseValues())
655      * Works based on configuration in TypoScript key [create/edit].evalValues.
656      *
657      * @see init(), parseValues()
658      */
659     public function evalValues()
660     {
661         // Check required, set failure if not ok.
662         $tempArr = array();
663         foreach ($this->requiredArr as $theField) {
664             if (!trim($this->dataArr[$theField])) {
665                 $tempArr[] = $theField;
666             }
667         }
668
669         // Evaluate: This evaluates for more advanced things than 'required' does. But it returns the same error code, so you must let the required-message tell, if further evaluation has failed!
670         $recExist = 0;
671         if (is_array($this->conf[$this->cmdKey.'.']['evalValues.'])) {
672             switch ($this->cmd) {
673                 case 'edit':
674                     if (isset($this->dataArr['pid'])) {            // 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.
675                         $recordTestPid = intval($this->dataArr['pid']);
676                     } else {
677                         $tempRecArr = $GLOBALS['TSFE']->sys_page->getRawRecord($this->theTable, $this->dataArr['uid']);
678                         $recordTestPid = intval($tempRecArr['pid']);
679                     }
680                     $recExist = 1;
681                     break;
682                 default:
683                     $pid = MathUtility::convertToPositiveInteger($this->dataArr['pid']);
684                     $recordTestPid = $this->thePid ? $this->thePid : $pid;
685                     break;
686             }
687
688             foreach ($this->conf[$this->cmdKey.'.']['evalValues.'] as $theField => $theValue) {
689                 $listOfCommands = GeneralUtility::trimExplode(',', $theValue, 1);
690                 foreach ($listOfCommands as $cmd) {
691                     $cmdParts = preg_split('/\[|\]/', $cmd);    // Point is to enable parameters after each command enclosed in brackets [..]. These will be in position 1 in the array.
692                     $theCmd = trim($cmdParts[0]);
693                     switch ($theCmd) {
694                         case 'uniqueGlobal':
695                             if ($DBrows = $GLOBALS['TSFE']->sys_page->getRecordsByField($this->theTable, $theField, $this->dataArr[$theField], '', '', '', '1')) {
696                                 if (!$recExist || $DBrows[0]['uid'] != $this->dataArr['uid']) {    // 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.
697                                     $tempArr[] = $theField;
698                                     $this->failureMsg[$theField][] = $this->getFailure($theField, $theCmd, 'The value existed already. Enter a new value.');
699                                 }
700                             }
701                             break;
702                         case 'uniqueLocal':
703                             if ($DBrows = $GLOBALS['TSFE']->sys_page->getRecordsByField($this->theTable, $theField, $this->dataArr[$theField], 'AND pid IN ('.$recordTestPid.')', '', '', '1')) {
704                                 if (!$recExist || $DBrows[0]['uid'] != $this->dataArr['uid']) {    // 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.
705                                     $tempArr[] = $theField;
706                                     $this->failureMsg[$theField][] = $this->getFailure($theField, $theCmd, 'The value existed already. Enter a new value.');
707                                 }
708                             }
709                             break;
710                         case 'twice':
711                             if (strcmp($this->dataArr[$theField], $this->dataArr[$theField.'_again'])) {
712                                 $tempArr[] = $theField;
713                                 $this->failureMsg[$theField][] = $this->getFailure($theField, $theCmd, 'You must enter the same value twice');
714                             }
715                             break;
716                         case 'email':
717                             if (!GeneralUtility::validEmail($this->dataArr[$theField])) {
718                                 $tempArr[] = $theField;
719                                 $this->failureMsg[$theField][] = $this->getFailure($theField, $theCmd, 'You must enter a valid email address');
720                             }
721                             break;
722                         case 'required':
723                             if (!trim($this->dataArr[$theField])) {
724                                 $tempArr[] = $theField;
725                                 $this->failureMsg[$theField][] = $this->getFailure($theField, $theCmd, 'You must enter a value!');
726                             }
727                             break;
728                         case 'atLeast':
729                             $chars = intval($cmdParts[1]);
730                             if (strlen($this->dataArr[$theField]) < $chars) {
731                                 $tempArr[] = $theField;
732                                 $this->failureMsg[$theField][] = sprintf($this->getFailure($theField, $theCmd, 'You must enter at least %s characters!'), $chars);
733                             }
734                             break;
735                         case 'atMost':
736                             $chars = intval($cmdParts[1]);
737                             if (strlen($this->dataArr[$theField]) > $chars) {
738                                 $tempArr[] = $theField;
739                                 $this->failureMsg[$theField][] = sprintf($this->getFailure($theField, $theCmd, 'You must enter at most %s characters!'), $chars);
740                             }
741                             break;
742                         case 'inBranch':
743                             $pars = explode(';', $cmdParts[1]);
744                             if (intval($pars[0])) {
745                                 $pid_list = $this->cObj->getTreeList(
746                                     intval($pars[0]),
747                                     intval($pars[1]) ? intval($pars[1]) : 999,
748                                     intval($pars[2])
749                                 );
750                                 if (!$pid_list || !GeneralUtility::inList($pid_list, $this->dataArr[$theField])) {
751                                     $tempArr[] = $theField;
752                                     $this->failureMsg[$theField][] = sprintf($this->getFailure($theField, $theCmd, 'The value was not a valid valud from this list: %s'), $pid_list);
753                                 }
754                             }
755                             break;
756                         case 'unsetEmpty':
757                             if (!$this->dataArr[$theField]) {
758                                 $hash = array_flip($tempArr);
759                                 unset($hash[$theField]);
760                                 $tempArr = array_keys($hash);
761                                 unset($this->failureMsg[$theField]);
762                                 // This should prevent the field from entering the database.
763                                 unset($this->dataArr[$theField]);
764                             }
765                             break;
766                     }
767                 }
768                 $this->markerArray['###EVAL_ERROR_FIELD_'.$theField.'###'] = is_array($this->failureMsg[$theField]) ? implode('<br />', $this->failureMsg[$theField]) : '';
769             }
770             $tempArr[] = $this->checkCaptcha();
771         }
772
773         $this->failure = implode(',', $tempArr);     //$failure will show which fields were not OK
774     }
775
776     /**
777      * check captcha.
778      *
779      * @return bool @captcha:TRUE if captcha not loaded or captcha is correct, FALSE on wrong captcha
780      */
781     public function checkCaptcha()
782     {
783         /* CAPTCHA */
784         $captcha = true;
785
786         if (ExtensionManagementUtility::isLoaded('captcha') && isset($this->dataArr['captcha'])) {
787             session_start();
788             $captchaStr = $_SESSION['tx_captcha_string'];
789             $_SESSION['tx_captcha_string'] = '';
790
791             if (empty($captchaStr) || ($this->dataArr['captcha'] !== $captchaStr)) {
792                 $captcha = false;
793                 $theField = 'captcha';
794                 $this->failureMsg[$theField][] = $this->getFailure($theField, 'captcha', 'Wrong captcha!');
795                 $errorMsg = is_array($this->failureMsg[$theField]) ? implode('<br />', $this->failureMsg[$theField]) : '';
796                 $this->markerArray['###CAPTCHA###'] = $this->getCaptcha($errorMsg);
797             }
798         }
799
800         if (isset($theField)) {
801             return $theField;
802         }
803     }
804
805     /**
806      * Preforms user processing of input array -
807      * triggered right after the function call to evalValues() IF TypoScript property "evalFunc" was set.
808      *
809      * @param string $mConfKeyKey Pointing to the property in TypoScript holding the configuration for this processing (here: "evalFunc.*"). Well: at least its safe to say that "parentObj" in this array passed to the function is a reference back to this object.
810      * @param array $passVar The $this->dataArr passed for processing
811      *
812      * @return array The processed $passVar ($this->dataArr)
813      *
814      * @see init(), evalValues()
815      */
816     public function userProcess($mConfKey, $passVar)
817     {
818         if ($this->conf[$mConfKey]) {
819             $funcConf = $this->conf[$mConfKey.'.'];
820             $funcConf['parentObj'] = $this;
821             $passVar = $GLOBALS['TSFE']->cObj->callUserFunction($this->conf[$mConfKey], $funcConf, $passVar);
822         }
823
824         return $passVar;
825     }
826
827     /**
828      * User processing of contnet.
829      *
830      * @param   string  $confVal Value of the TypoScript object triggering the processing.
831      * @param   array   $confArr Properties of the TypoScript object triggering the processing.
832      * The key "parentObj" in this array is passed to the function as a reference back to this object.
833      * @param   mixed   $passVar Input variable to process
834      *
835      * @return mixed Processed input variable, $passVar
836      *
837      * @see userProcess(), save(), modifyDataArrForFormUpdate()
838      */
839     public function userProcess_alt($confVal, $confArr, $passVar)
840     {
841         if ($confVal) {
842             $funcConf = $confArr;
843             $funcConf['parentObj'] = $this;
844             $passVar = $GLOBALS['TSFE']->cObj->callUserFunction($confVal, $funcConf, $passVar);
845         }
846
847         return $passVar;
848     }
849
850     /*****************************************
851      *
852      * Database manipulation functions
853      *
854      *****************************************/
855
856     /**
857      * Performs the saving of records, either edited or created.
858      *
859      * @see init()
860      */
861     public function save()
862     {
863         switch ($this->cmd) {
864             case 'edit':
865                 $theUid = $this->dataArr['uid'];
866                 // Fetches the original record to check permissions
867                 $origArr = $GLOBALS['TSFE']->sys_page->getRawRecord($this->theTable, $theUid);
868                 if ($this->conf['edit'] && ($GLOBALS['TSFE']->loginUser || $this->aCAuth($origArr))) {
869                     // Must be logged in in order to edit  (OR be validated by email)
870                     $newFieldList = implode(',', array_intersect(explode(',', $this->fieldList), GeneralUtility::trimExplode(',', $this->conf['edit.']['fields'], 1)));
871                     if ($this->aCAuth($origArr) || $this->cObj->DBmayFEUserEdit($this->theTable, $origArr, $GLOBALS['TSFE']->fe_user->user, $this->conf['allowedGroups'], $this->conf['fe_userEditSelf'])) {
872
873                         $this->cObj->DBgetUpdate($this->theTable, $theUid, $this->dataArr, $newFieldList, true);
874                         $this->currentArr = $GLOBALS['TSFE']->sys_page->getRawRecord($this->theTable, $theUid);
875                         $this->userProcess_alt($this->conf['edit.']['userFunc_afterSave'], $this->conf['edit.']['userFunc_afterSave.'], array('rec' => $this->currentArr, 'origRec' => $origArr));
876                         $this->saved = 1;
877                     } else {
878                         $this->error = '###TEMPLATE_NO_PERMISSIONS###';
879                     }
880                 }
881                 break;
882             default:
883                 if ($this->conf['create']) {
884                     $newFieldList = implode(',', array_intersect(explode(',', $this->fieldList), GeneralUtility::trimExplode(',', $this->conf['create.']['fields'], 1)));
885                     $this->cObj->DBgetInsert($this->theTable, $this->thePid, $this->dataArr, $newFieldList, true);
886                     $newId = $GLOBALS['TYPO3_DB']->sql_insert_id();
887
888                     if ($this->theTable == 'fe_users' && $this->conf['fe_userOwnSelf']) {
889                         // enables users, creating logins, to own them self.
890                         $extraList = '';
891                         $dataArr = array();
892                         if ($GLOBALS['TCA'][$this->theTable]['ctrl']['fe_cruser_id']) {
893                             $field = $GLOBALS['TCA'][$this->theTable]['ctrl']['fe_cruser_id'];
894                             $dataArr[$field] = $newId;
895                             $extraList .= ','.$field;
896                         }
897                         if ($GLOBALS['TCA'][$this->theTable]['ctrl']['fe_crgroup_id']) {
898                             $field = $GLOBALS['TCA'][$this->theTable]['ctrl']['fe_crgroup_id'];
899                             list($dataArr[$field]) = explode(',', $this->dataArr['usergroup']);
900                             $dataArr[$field] = intval($dataArr[$field]);
901                             $extraList .= ','.$field;
902                         }
903                         if (count($dataArr)) {
904                             $this->cObj->DBgetUpdate($this->theTable, $newId, $dataArr, $extraList, true);
905                         }
906                     }
907
908                     $this->currentArr = $GLOBALS['TSFE']->sys_page->getRawRecord($this->theTable, $newId);
909                     $this->userProcess_alt($this->conf['create.']['userFunc_afterSave'], $this->conf['create.']['userFunc_afterSave.'], array('rec' => $this->currentArr));
910
911                     // reloading the currentArr from the DB so that any DB changes in userFunc is taken into account
912                     unset($this->currentArr);
913                     $this->currentArr = $GLOBALS['TSFE']->sys_page->getRawRecord($this->theTable, $newId);
914
915                     $this->saved = 1;
916                 }
917                 break;
918         }
919     }
920
921     /**
922      * Deletes the record from table/uid, $this->theTable/$this->recUid, IF the fe-user has permission to do so.
923      * If the deleted flag should just be set, then it is done so. Otherwise the record truely is deleted along with any attached files.
924      * Called from init() if "cmd" was set to "delete" (and some other conditions).
925      *
926      * @return string void
927      *
928      * @see init()
929      */
930     public function deleteRecord()
931     {
932         if ($this->conf['delete']) {    // If deleting is enabled
933             $origArr = $GLOBALS['TSFE']->sys_page->getRawRecord($this->theTable, $this->recUid);
934             if ($GLOBALS['TSFE']->loginUser || $this->aCAuth($origArr)) {
935                 // Must be logged in OR be authenticated by the aC code in order to delete
936                     // If the recUid selects a record.... (no check here)
937                 if (is_array($origArr)) {
938                     if ($this->aCAuth($origArr) || $this->cObj->DBmayFEUserEdit($this->theTable, $origArr, $GLOBALS['TSFE']->fe_user->user, $this->conf['allowedGroups'], $this->conf['fe_userEditSelf'])) {
939                         // Display the form, if access granted.
940                         if (!$GLOBALS['TCA'][$this->theTable]['ctrl']['delete']) {
941                             // If the record is fully deleted... then remove the image (or any file) attached.
942                             $this->deleteFilesFromRecord($this->recUid);
943                         }
944                         $this->cObj->DBgetDelete($this->theTable, $this->recUid, true);
945                         $this->currentArr = $origArr;
946                         $this->saved = 1;
947                     } else {
948                         $this->error = '###TEMPLATE_NO_PERMISSIONS###';
949                     }
950                 }
951             }
952         }
953     }
954
955     /**
956      * Deletes the files attached to a record and updates the record.
957      * Table/uid is $this->theTable/$uid.
958      *
959      * @param int   $uid Uid number of the record to delete from $this->theTable
960      *
961      * @see deleteRecord()
962      */
963     public function deleteFilesFromRecord($uid)
964     {
965         $table = $this->theTable;
966         $rec = $GLOBALS['TSFE']->sys_page->getRawRecord($table, $uid);
967
968         $GLOBALS['TSFE']->includeTCA();
969         GeneralUtility::loadTCA($table);
970         foreach ($GLOBALS['TCA'][$table]['columns'] as $field => $conf) {
971             if ($conf['config']['type'] == 'group' && $conf['config']['internal_type'] == 'file') {
972                 $GLOBALS['TYPO3_DB']->exec_UPDATEquery($table, 'uid=' . intval($uid), array($field => ''));
973
974                 $delFileArr = explode(',', $rec[$field]);
975                 foreach ($delFileArr as $n) {
976                     if ($n) {
977                         $fpath = $conf['config']['uploadfolder'] . '/' . $n;
978                         unlink($fpath);
979                     }
980                 }
981             }
982         }
983     }
984
985     /*****************************************
986      *
987      * Command "display" functions
988      *
989      *****************************************/
990
991     /**
992      * Creates the preview display of delete actions.
993      *
994      * @return string HTML content
995      *
996      * @see init()
997      */
998     public function displayDeleteScreen()
999     {
1000         if ($this->conf['delete']) {
1001             // If deleting is enabled
1002             $origArr = $GLOBALS['TSFE']->sys_page->getRawRecord($this->theTable, $this->recUid);
1003             if ($GLOBALS['TSFE']->loginUser || $this->aCAuth($origArr)) {
1004                 // Must be logged in OR be authenticated by the aC code in order to delete
1005                 // If the recUid selects a record.... (no check here)
1006                 if (is_array($origArr)) {
1007                     if ($this->aCAuth($origArr) || $this->cObj->DBmayFEUserEdit($this->theTable, $origArr, $GLOBALS['TSFE']->fe_user->user, $this->conf['allowedGroups'], $this->conf['fe_userEditSelf'])) {
1008                         // Display the form, if access granted.
1009                         $this->markerArray['###HIDDENFIELDS###'] .= '<input type="hidden" name="rU" value="'.$this->recUid.'" />';
1010                         $content = $this->getPlainTemplate('###TEMPLATE_DELETE_PREVIEW###', $origArr);
1011                     } else {    // Else display error, that you could not edit that particular record...
1012                         $content = $this->getPlainTemplate('###TEMPLATE_NO_PERMISSIONS###');
1013                     }
1014                 }
1015             } else {
1016                 // Finally this is if there is no login user. This must tell that you must login.
1017                 // Perhaps link to a page with create-user or login information.
1018                 $content = $this->getPlainTemplate('###TEMPLATE_AUTH###');
1019             }
1020         } else {
1021             $content = 'Delete-option is not set in TypoScript';
1022         }
1023
1024         return $content;
1025     }
1026
1027     /**
1028      * Returns a JavaScript <script> section with some function calls to JavaScript functions from "typo3/js/jsfunc.updateform.js" (which is also included by setting a reference in $GLOBALS['TSFE']->additionalHeaderData['JSincludeFormupdate'])
1029      * The JavaScript codes simply transfers content into form fields of a form which is probably used for editing information by frontend users. Used by fe_adminLib.inc.
1030      *
1031      * @param array  $dataArray Data array which values to load into the form fields from $formName (only field names found in $fieldList)
1032      * @param string $formName  The form name
1033      * @param string $arrPrefix A prefix for the data array
1034      * @param string $fieldList The list of fields which are loaded
1035      *
1036      * @return string
1037      *
1038      * @see user_feAdmin::displayCreateScreen()
1039      *
1040      * @todo internal; comes from ContentObjectRenderer.php from TYPO3 CMS 6.2 (not included in 7.3 anymore)
1041      */
1042     public function getUpdateJS($dataArray, $formName, $arrPrefix, $fieldList)
1043     {
1044         $JSPart = '';
1045         $updateValues = GeneralUtility::trimExplode(',', $fieldList);
1046         foreach ($updateValues as $fKey) {
1047             $value = $dataArray[$fKey];
1048             if (is_array($value)) {
1049                 foreach ($value as $Nvalue) {
1050                     $JSPart .= '
1051         updateForm(\''.$formName.'\',\''.$arrPrefix.'['.$fKey.'][]\','.GeneralUtility::quoteJSvalue($Nvalue, true).');';
1052                 }
1053             } else {
1054                 $JSPart .= '
1055         updateForm(\''.$formName.'\',\''.$arrPrefix.'['.$fKey.']\','.GeneralUtility::quoteJSvalue($value, true).');';
1056             }
1057         }
1058         $JSPart = '<script type="text/javascript">
1059         /*<![CDATA[*/ '.$JSPart.'
1060         /*]]>*/
1061 </script>
1062 ';
1063         $GLOBALS['TSFE']->additionalHeaderData['JSincludeFormupdate'] = '<script type="text/javascript" src="'.GeneralUtility::createVersionNumberedFilename($GLOBALS['TSFE']->absRefPrefix. ExtensionManagementUtility::extRelPath('direct_mail_subscription').'jsfunc.updateform.js').'"></script>';
1064
1065         return $JSPart;
1066     }
1067
1068     /**
1069      * Creates the "create" screen for records.
1070      *
1071      * @return string HTML content
1072      *
1073      * @see init()
1074      */
1075     public function displayCreateScreen()
1076     {
1077         if ($this->conf['create']) {
1078             $templateCode = $this->cObj->getSubpart($this->templateCode, ((!$GLOBALS['TSFE']->loginUser || $this->conf['create.']['noSpecialLoginForm']) ? '###TEMPLATE_CREATE'.$this->previewLabel.'###' : '###TEMPLATE_CREATE_LOGIN'.$this->previewLabel.'###'));
1079             $failure = GeneralUtility::_GP('noWarnings') ? '' : $this->failure;
1080             if (!$failure) {
1081                 $templateCode = $this->cObj->substituteSubpart($templateCode, '###SUB_REQUIRED_FIELDS_WARNING###', '');
1082             }
1083
1084             $templateCode = $this->removeRequired($templateCode, $failure);
1085             $this->setCObjects($templateCode);
1086
1087             if (!is_array($this->dataArr)) {
1088                 $this->dataArr = array();
1089             }
1090
1091             $markerArray = $this->cObj->fillInMarkerArray($this->markerArray, $this->dataArr, '', true, 'FIELD_', $this->recInMarkersHSC);
1092             if ($this->conf['create.']['preview'] && !$this->previewLabel) {
1093                 $markerArray['###HIDDENFIELDS###'] .= '<input type="hidden" name="preview" value="1" />';
1094             }
1095
1096             /* CAPTCHA */
1097             if (!$this->markerArray['###CAPTCHA###']) {
1098                 $markerArray['###CAPTCHA###'] = $this->getCaptcha();
1099             }
1100
1101             $content = $this->cObj->substituteMarkerArray($templateCode, $markerArray);
1102             $content .= $this->getUpdateJS($this->modifyDataArrForFormUpdate($this->dataArr), $this->theTable.'_form', 'FE['.$this->theTable.']', $this->fieldList.$this->additionalUpdateFields);
1103         }
1104
1105         return $content;
1106     }
1107
1108     /**
1109      * Creates the edit-screen for records.
1110      *
1111      * @return string HTML content
1112      *
1113      * @see init()
1114      */
1115     public function displayEditScreen()
1116     {
1117         if ($this->conf['edit']) {    // If editing is enabled
1118             $origArr = $GLOBALS['TSFE']->sys_page->getRawRecord($this->theTable, $this->dataArr['uid'] ? $this->dataArr['uid'] : $this->recUid);
1119
1120             if ($GLOBALS['TSFE']->loginUser || $this->aCAuth($origArr)) {    // Must be logged in OR be authenticated by the aC code in order to edit
1121                     // If the recUid selects a record.... (no check here)
1122                 if (is_array($origArr)) {
1123                     if ($this->aCAuth($origArr) || $this->cObj->DBmayFEUserEdit($this->theTable, $origArr, $GLOBALS['TSFE']->fe_user->user, $this->conf['allowedGroups'], $this->conf['fe_userEditSelf'])) {
1124                         // Display the form, if access granted.
1125                         $content = $this->displayEditForm($origArr);
1126                     } else {    // Else display error, that you could not edit that particular record...
1127                         $content = $this->getPlainTemplate('###TEMPLATE_NO_PERMISSIONS###');
1128                     }
1129                 } elseif ($GLOBALS['TSFE']->loginUser) {
1130                     // If the recUid did not select a record, we display a menu of records. (eg. if no recUid)
1131                     $lockPid = $this->conf['edit.']['menuLockPid'] ? ' AND pid='.intval($this->thePid) : '';
1132
1133                     $res = $GLOBALS['TYPO3_DB']->exec_SELECTquery('*', $this->theTable, '1 '.$lockPid.$this->cObj->DBmayFEUserEditSelect($this->theTable, $GLOBALS['TSFE']->fe_user->user, $this->conf['allowedGroups'], $this->conf['fe_userEditSelf']).$GLOBALS['TSFE']->sys_page->deleteClause($this->theTable));
1134
1135                     if ($GLOBALS['TYPO3_DB']->sql_num_rows($res)) {    // If there are menu-items ...
1136                         $templateCode = $this->getPlainTemplate('###TEMPLATE_EDITMENU###');
1137                         $out = '';
1138                         $itemCode = $this->cObj->getSubpart($templateCode, '###ITEM###');
1139                         while ($menuRow = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($res)) {
1140                             $markerArray = $this->cObj->fillInMarkerArray(array(), $menuRow, '', true, 'FIELD_', $this->recInMarkersHSC);
1141                             $markerArray = $this->setCObjects($itemCode, $menuRow, $markerArray, 'ITEM_');
1142                             $out .= $this->cObj->substituteMarkerArray($itemCode, $markerArray);
1143                         }
1144                         $content = $this->cObj->substituteSubpart($templateCode, '###ALLITEMS###', $out);
1145                     } else {    // If there are not menu items....
1146                         $content = $this->getPlainTemplate('###TEMPLATE_EDITMENU_NOITEMS###');
1147                     }
1148                 } else {
1149                     $content = $this->getPlainTemplate('###TEMPLATE_AUTH###');
1150                 }
1151             } else {
1152                 // Finally this is if there is no login user. This must tell that you must login. Perhaps link to a page with create-user or login information.
1153                 $content = $this->getPlainTemplate('###TEMPLATE_AUTH###');
1154             }
1155         } else {
1156             $content = 'Edit-option is not set in TypoScript';
1157         }
1158
1159         return $content;
1160     }
1161
1162     /**
1163      * Subfunction for displayEditScreen(); Takes a record and creates an edit form based on the template code for it.
1164      * This function is called if the user is editing a record and permitted to do so. Checked in displayEditScreen().
1165      *
1166      * @param   array   $origArr    The array with the record to edit
1167      *
1168      * @return string HTML content
1169      *
1170      * @see displayEditScreen()
1171      */
1172     public function displayEditForm($origArr)
1173     {
1174         $currentArr = is_array($this->dataArr) ? $this->dataArr + $origArr : $origArr;
1175
1176         if ($this->conf['debug']) {
1177             debug('displayEditForm(): '.'###TEMPLATE_EDIT'.$this->previewLabel.'###', 1);
1178         }
1179         $templateCode = $this->cObj->getSubpart($this->templateCode, '###TEMPLATE_EDIT'.$this->previewLabel.'###');
1180         $failure = GeneralUtility::_GP('noWarnings') ? '' : $this->failure;
1181
1182         if (!$failure) {
1183             $templateCode = $this->cObj->substituteSubpart($templateCode, '###SUB_REQUIRED_FIELDS_WARNING###', '');
1184         }
1185
1186         $templateCode = $this->removeRequired($templateCode, $failure);
1187
1188         $this->setCObjects($templateCode, $currentArr);
1189
1190         $markerArray = $this->cObj->fillInMarkerArray($this->markerArray, $currentArr, '', true, 'FIELD_', $this->recInMarkersHSC);
1191
1192         $markerArray['###HIDDENFIELDS###'] .= '<input type="hidden" name="FE['.$this->theTable.'][uid]" value="'.$currentArr['uid'].'" />';
1193         if ($this->conf['edit.']['preview'] && !$this->previewLabel) {
1194             $markerArray['###HIDDENFIELDS###'] .= '<input type="hidden" name="preview" value="1" />';
1195         }
1196
1197         /* CAPTCHA */
1198         if (!$this->markerArray['###CAPTCHA###']) {
1199             $markerArray['###CAPTCHA###'] = $this->getCaptcha();
1200         }
1201
1202         $content = $this->cObj->substituteMarkerArray($templateCode, $markerArray);
1203         $content .= $this->getUpdateJS($this->modifyDataArrForFormUpdate($currentArr), $this->theTable.'_form', 'FE['.$this->theTable.']', $this->fieldList.$this->additionalUpdateFields);
1204
1205         return $content;
1206     }
1207
1208     /**
1209      * Processes socalled "setfixed" commands. These are commands setting a certain field in a certain record to a certain value. Like a link you can click in an email which will unhide a record to enable something. Or likewise a link which can delete a record by a single click.
1210      * The idea is that only some allowed actions like this is allowed depending on the configured TypoScript.
1211      *
1212      * @return string HTML content displaying the status of the action
1213      */
1214     public function procesSetFixed()
1215     {
1216         if ($this->conf['setfixed']) {
1217             $theUid = intval($this->recUid);
1218             $origArr = $GLOBALS['TSFE']->sys_page->getRawRecord($this->theTable, $theUid);
1219             $fD = GeneralUtility::_GP('fD');
1220             $sFK = GeneralUtility::_GP('sFK');
1221
1222             $valuesConfiguredInTypoScript = isset($this->conf['setfixed.'][$sFK.'.']) ? $this->conf['setfixed.'][$sFK.'.'] : array();
1223             $fields = $valuesConfiguredInTypoScript;
1224             unset($fields['_FIELDLIST']);
1225             $fields = array_keys($fields);
1226             if (isset($valuesConfiguredInTypoScript['_FIELDLIST'])) {
1227                 $fields = array_merge($fields, GeneralUtility::trimExplode(',', $valuesConfiguredInTypoScript['_FIELDLIST']));
1228             }
1229             $valuesConfiguredInTypoScript['_FIELDLIST'] = implode(',', array_unique($fields));
1230
1231             $fieldArr = array();
1232             if (!empty($valuesConfiguredInTypoScript) || $sFK == 'DELETE') {
1233                 foreach ($valuesConfiguredInTypoScript as $field => $value) {
1234                     $origArr[$field] = $value;
1235                     $fieldArr[] = $field;
1236                 }
1237                 $theCode = $this->setfixedHash($origArr, $origArr['_FIELDLIST']);
1238                 if (!strcmp($this->authCode, $theCode)) {
1239                     if ($sFK == 'DELETE') {
1240                         $this->cObj->DBgetDelete($this->theTable, $theUid, true);
1241                     } else {
1242                         $newFieldList = implode(',', array_intersect(GeneralUtility::trimExplode(',', $this->fieldList), GeneralUtility::trimExplode(',', implode($fieldArr, ','), 1)));
1243                         unset($valuesConfiguredInTypoScript['_FIELDLIST']);
1244                         $this->cObj->DBgetUpdate($this->theTable, $theUid, $valuesConfiguredInTypoScript, $newFieldList, true);
1245
1246                         $this->currentArr = $GLOBALS['TSFE']->sys_page->getRawRecord($this->theTable, $theUid);
1247                         $this->userProcess_alt($this->conf['setfixed.']['userFunc_afterSave'], $this->conf['setfixed.']['userFunc_afterSave.'], array('rec' => $this->currentArr, 'origRec' => $origArr));
1248                     }
1249
1250                         // Outputting template
1251                     $this->markerArray = $this->cObj->fillInMarkerArray($this->markerArray, $origArr, '', true, 'FIELD_', $this->recInMarkersHSC);
1252                     $content = $this->getPlainTemplate('###TEMPLATE_SETFIXED_OK_'.$sFK.'###');
1253                     if (!$content) {
1254                         $content = $this->getPlainTemplate('###TEMPLATE_SETFIXED_OK###');
1255                     }
1256
1257                         // Compiling email
1258                     $this->compileMail(
1259                         'SETFIXED_'.$sFK,
1260                         array($origArr),
1261                         $origArr[$this->conf['email.']['field']],
1262                         $this->conf['setfixed.']
1263                     );
1264                         // Clearing cache if set:
1265                     $this->clearCacheIfSet();
1266                     $this->setNoCacheHeader();
1267                 } else {
1268                     $content = $this->getPlainTemplate('###TEMPLATE_SETFIXED_FAILED###');
1269                 }
1270             } else {
1271                 $content = $this->getPlainTemplate('###TEMPLATE_SETFIXED_FAILED###');
1272             }
1273         }
1274
1275         return $content;
1276     }
1277
1278     /*****************************************
1279      *
1280      * Template processing functions
1281      *
1282      *****************************************/
1283
1284     /**
1285      * Remove required parts from template code string
1286      * Works like this:
1287      *  - You insert subparts like this ###SUB_REQUIRED_FIELD_'.$theField.'### in the template that tells what is required for the field, if it's not correct filled in.
1288      *  - These subparts are all removed, except if the field is listed in $failure string!
1289      *
1290      * Only fields that are found in $this->requiredArr is processed.
1291      *
1292      * @param   string  $templateCode   The template HTML code
1293      * @param   string  $failure    Comma list of fields which has errors (and therefore should not be removed)
1294      *
1295      * @return string The processed template HTML code
1296      */
1297     public function removeRequired($templateCode, $failure)
1298     {
1299         foreach ($this->requiredArr as $theField) {
1300             if (!GeneralUtility::inList($failure, $theField)) {
1301                 $templateCode = $this->cObj->substituteSubpart($templateCode, '###SUB_REQUIRED_FIELD_'.$theField.'###', '');
1302             }
1303         }
1304
1305         return $templateCode;
1306     }
1307
1308     /**
1309      * Returns template subpart HTML code for the key given.
1310      *
1311      * @param   string  $key    Subpart marker to return subpart for.
1312      * @param   array   $r  Optional data record array.
1313      *  If set, then all fields herein will also be substituted if found as markers in the template
1314      *
1315      * @return string The subpart with all markers found in current $this->markerArray substituted.
1316      *
1317      * @see \TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer::fillInMarkerArray()
1318      */
1319     public function getPlainTemplate($key, $r = array())
1320     {
1321         if ($this->conf['debug']) {
1322             debug('getPlainTemplate(): '.$key, 1);
1323         }
1324         $templateCode = $this->cObj->getSubpart($this->templateCode, $key);
1325         $this->setCObjects($templateCode, is_array($r) ? $r : array());
1326
1327         /* CAPTCHA */
1328         if (!$this->markerArray['###CAPTCHA###']) {
1329             $this->markerArray['###CAPTCHA###'] = $this->getCaptcha();
1330         }
1331
1332         $markerArray = is_array($r) ? $this->cObj->fillInMarkerArray($this->markerArray, $r, '', true, 'FIELD_', $this->recInMarkersHSC) : $this->markerArray;
1333
1334         $content = $this->cObj->substituteMarkerArray($templateCode, $markerArray);
1335
1336         return $content;
1337     }
1338
1339     /**
1340      * Modifies input array for passing on to
1341      * \TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer::getUpdateJS() which produces some JavaScript for form evaluation or the like.
1342      *
1343      * @param   array   $inputArr   The data array
1344      *
1345      * @return array The processed input array
1346      *
1347      * @see displayCreateScreen(), displayEditForm(), \TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer::getUpdateJS()
1348      */
1349     public function modifyDataArrForFormUpdate($inputArr)
1350     {
1351         if (is_array($this->conf[$this->cmdKey.'.']['evalValues.'])) {
1352             foreach ($this->conf[$this->cmdKey.'.']['evalValues.'] as $theField => $theValue) {
1353                 $listOfCommands = GeneralUtility::trimExplode(',', $theValue, 1);
1354                 foreach ($listOfCommands as $cmd) {
1355                     // Point is to enable parameters after each command enclosed in brackets [..].
1356                     // These will be in position 1 in the array.
1357                     $cmdParts = preg_split('/\[|\]/', $cmd);
1358                     $theCmd = trim($cmdParts[0]);
1359                     switch ($theCmd) {
1360                         case 'twice':
1361                             if (isset($inputArr[$theField])) {
1362                                 if (!isset($inputArr[$theField.'_again'])) {
1363                                     $inputArr[$theField.'_again'] = $inputArr[$theField];
1364                                 }
1365                                 $this->additionalUpdateFields .= ','.$theField.'_again';
1366                             }
1367                             break;
1368                     }
1369                 }
1370             }
1371         }
1372         if (is_array($this->conf['parseValues.'])) {
1373             foreach ($this->conf['parseValues.'] as $theField => $theValue) {
1374                 $listOfCommands = GeneralUtility::trimExplode(',', $theValue, 1);
1375                 foreach ($listOfCommands as $cmd) {
1376                     // Point is to enable parameters after each command enclosed in brackets [..].
1377                     // These will be in position 1 in the array.
1378                     $cmdParts = preg_split('/\[|\]/', $cmd);
1379                     $theCmd = trim($cmdParts[0]);
1380                     switch ($theCmd) {
1381                         case 'multiple':
1382                             if (isset($inputArr[$theField]) && !$this->isPreview()) {
1383                                 $inputArr[$theField] = explode(',', $inputArr[$theField]);
1384                             }
1385                             break;
1386                         case 'checkArray':
1387                             if ($inputArr[$theField] && !$this->isPreview()) {
1388                                 for ($a = 0; $a <= 30; ++$a) {
1389                                     if ($inputArr[$theField] & pow(2, $a)) {
1390                                         $alt_theField = $theField.']['.$a;
1391                                         $inputArr[$alt_theField] = 1;
1392                                         $this->additionalUpdateFields .= ','.$alt_theField;
1393                                     }
1394                                 }
1395                             }
1396                             break;
1397                     }
1398                 }
1399             }
1400         }
1401
1402         $inputArr = $this->userProcess_alt(
1403             $this->conf['userFunc_updateArray'],
1404             $this->conf['userFunc_updateArray.'],
1405             $inputArr
1406         );
1407
1408         return $this->escapeHTML($inputArr);
1409     }
1410
1411     /**
1412      * Will render TypoScript cObjects (configured in $this->conf['cObjects.']) and
1413      * add their content to keys in a markerArray, either the array passed to the function or the internal one ($this->markerArray) if the input $markerArray is not set.
1414      *
1415      * @param   string  $templateCode   The current template code string. Is used to check if the marker string is found and if not, the content object is not rendered!
1416      * @param   array   $currentArr An alternative data record array (if empty then   $this->dataArr       is used)
1417      * @param   mixed   $markerArray    An alternative markerArray to fill in (instead of $this->markerArray). If you want to set the cobjects in the internal $this->markerArray, then just set this to non-array value.
1418      * @param   string  $specialPrefix  Optional prefix to set for the marker strings.
1419      *
1420      * @return array The processed $markerArray (if given).
1421      */
1422     public function setCObjects($templateCode, $currentArr = array(), $markerArray = '', $specialPrefix = '')
1423     {
1424         if (is_array($this->conf['cObjects.'])) {
1425             foreach ($this->conf['cObjects.'] as $theKey => $theConf) {
1426                 if (!strstr($theKey, '.')) {
1427                     if (strstr($templateCode, '###'.$specialPrefix.'CE_'.$theKey.'###')) {
1428                         $cObjCode = $this->cObj->cObjGetSingle($this->conf['cObjects.'][$theKey], $this->conf['cObjects.'][$theKey.'.'], 'cObjects.'.$theKey);
1429
1430                         if (!is_array($markerArray)) {
1431                             $this->markerArray['###'.$specialPrefix.'CE_'.$theKey.'###'] = $cObjCode;
1432                         } else {
1433                             $markerArray['###'.$specialPrefix.'CE_'.$theKey.'###'] = $cObjCode;
1434                         }
1435                     }
1436                     if (strstr($templateCode, '###'.$specialPrefix.'PCE_'.$theKey.'###')) {
1437                         /** @var $local_cObj \TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer */
1438                         $local_cObj = GeneralUtility::makeInstance('TYPO3\\CMS\\Frontend\\ContentObject\\ContentObjectRenderer');
1439                         $local_cObj->start(count($currentArr) ? $currentArr : $this->dataArr, $this->theTable);
1440                         $cObjCode = $local_cObj->cObjGetSingle($this->conf['cObjects.'][$theKey], $this->conf['cObjects.'][$theKey.'.'], 'cObjects.'.$theKey);
1441
1442                         if (!is_array($markerArray)) {
1443                             $this->markerArray['###'.$specialPrefix.'PCE_'.$theKey.'###'] = $cObjCode;
1444                         } else {
1445                             $markerArray['###'.$specialPrefix.'PCE_'.$theKey.'###'] = $cObjCode;
1446                         }
1447                     }
1448                 }
1449             }
1450         }
1451
1452         return $markerArray;
1453     }
1454
1455     /**
1456      * return the captcha code. Uses the TEMPLATE_CAPTCHA subpart to.
1457      *
1458      * @param string $errorMsg The captcha error message
1459      *
1460      * @return html $captcha: the captcha code;
1461      */
1462     public function getCaptcha($errorMsg = '')
1463     {
1464         if (ExtensionManagementUtility::isLoaded('captcha')) {
1465             $templateCodeCaptcha = $this->cObj->getSubpart($this->templateCode, '###TEMPLATE_CAPTCHA###');
1466             $markerArrayCaptcha['###CAPTCHA_IMG###'] = '<img src="/' . ExtensionManagementUtility::siteRelPath('captcha').'captcha/captcha.php" alt="" />';
1467
1468             if (!empty($errorMsg)) {
1469                 $markerArrayCaptcha['###EVAL_ERROR_FIELD_captcha###'] = $errorMsg;
1470             } else {
1471                 $templateCodeCaptcha = $this->cObj->substituteSubpart($templateCodeCaptcha, '###SUB_REQUIRED_FIELD_captcha###', '');
1472             }
1473
1474             $captcha = $this->cObj->substituteMarkerArray($templateCodeCaptcha, $markerArrayCaptcha);
1475         } else {
1476             $captcha = '';
1477         }
1478
1479         return $captcha;
1480     }
1481
1482     /*****************************************
1483      *
1484      * Emailing
1485      *
1486      *****************************************/
1487
1488     /**
1489      * Sends info mail to user.
1490      *
1491      * @return string HTML content message
1492      *
1493      * @see init(),compileMail(), sendMail()
1494      */
1495     public function sendInfoMail()
1496     {
1497         if ($this->conf['infomail'] && $this->conf['email.']['field']) {
1498             $fetch = GeneralUtility::_GP('fetch');
1499
1500             if ($fetch) {
1501                 $this->evalValues();
1502             }
1503
1504             //check the failureMsg array, since evalValues is called
1505             $captcha = true;
1506             if (is_array($this->failureMsg['captcha'])) {
1507                 $captcha = false;
1508             }
1509
1510             if ($fetch && $captcha) {
1511                 // Getting infomail config.
1512                 $key = trim(GeneralUtility::_GP('key'));
1513                 if (is_array($this->conf['infomail.'][$key.'.'])) {
1514                     $config = $this->conf['infomail.'][$key.'.'];
1515                 } else {
1516                     $config = $this->conf['infomail.']['default.'];
1517                 }
1518                 $pidLock = '';
1519                 if (!$config['dontLockPid']) {
1520                     $pidLock = 'AND pid IN ('.$this->thePid.') ';
1521                 }
1522
1523                 //get PID recursively
1524                 if ($this->conf['pidRecursive']) {
1525                     $recursivePidList = $this->cObj->getTreeList($this->thePid, 100);
1526                     if ($recursivePidList) {
1527                         $pidList = $recursivePidList . ',' . $this->thePid;
1528                     } else {
1529                         $pidList = $this->thePid;
1530                     }
1531                     $pidLock = 'AND pid IN (' .  $pidList . ')';
1532                 }
1533
1534                     // Getting records
1535                 $fetchInt = MathUtility::canBeInterpretedAsInteger($fetch);
1536
1537                 if ($fetchInt) {
1538                     $DBrows = $GLOBALS['TSFE']->sys_page->getRecordsByField($this->theTable, 'uid', $fetch, $pidLock, '', '', '1');
1539                 } elseif ($fetch) {    // $this->conf['email.']['field'] must be a valid field in the table!
1540                     $DBrows = $GLOBALS['TSFE']->sys_page->getRecordsByField($this->theTable, $this->conf['email.']['field'], $fetch, $pidLock, '', '', '100');
1541                 }
1542
1543                     // Processing records
1544                 if (is_array($DBrows)) {
1545                     $recipient = $DBrows[0][$this->conf['email.']['field']];
1546                     $this->compileMail($config['label'], $DBrows, $recipient, $this->conf['setfixed.']);
1547                     $content = $this->getPlainTemplate('###TEMPLATE_INFOMAIL_SENT###');
1548                 } else {
1549                     $content = $this->getPlainTemplate('###TEMPLATE_INFOMAIL_NORECORD###');
1550                 }
1551             } else {
1552                 $content = $this->getPlainTemplate('###TEMPLATE_INFOMAIL###');
1553                 $content = $this->removeRequired($content, $this->failure);
1554             }
1555         } else {
1556             $content = 'Error: infomail option is not available or emailField is not setup in TypoScript';
1557         }
1558
1559         return $content;
1560     }
1561
1562     /**
1563      * Compiles and sends a mail based on input values + template parts. Looks for a normal and an "-admin" template and may send both kinds of emails. See documentation in TSref.
1564      *
1565      * @param   string  $key    A key which together with $this->emailMarkPrefix will identify the part from the template code to use for the email.
1566      * @param   array   $DBrows An array of records which fields are substituted in the templates
1567      * @param   mixed   $recipient  Mail recipient. If string then its supposed to be an email address. If integer then its a uid of a fe_users record which is looked up and the email address from here is used for sending the mail.
1568      * @param   array   $setFixedConfig Additional fields to set in the markerArray used in the substitution process
1569      */
1570     public function compileMail($key, $DBrows, $recipient, $setFixedConfig = array())
1571     {
1572         $this->getTimeTracker()->push('compileMail');
1573         $mailContent = '';
1574         $key = $this->emailMarkPrefix.$key;
1575
1576         $userContent['all'] = trim($this->cObj->getSubpart($this->templateCode, '###'.$key.'###'));
1577         $adminContent['all'] = trim($this->cObj->getSubpart($this->templateCode, '###'.$key.'-ADMIN###'));
1578         $userContent['rec'] = $this->cObj->getSubpart($userContent['all'], '###SUB_RECORD###');
1579         $adminContent['rec'] = $this->cObj->getSubpart($adminContent['all'], '###SUB_RECORD###');
1580
1581         foreach ($DBrows as $r) {
1582             $markerArray = $this->cObj->fillInMarkerArray($this->markerArray, $r, '', 0);
1583             $markerArray = $this->setCObjects($userContent['rec'].$adminContent['rec'], $r, $markerArray, 'ITEM_');
1584             $markerArray['###SYS_AUTHCODE###'] = $this->authCode($r);
1585             $markerArray = $this->setfixed($markerArray, $setFixedConfig, $r);
1586
1587             if ($userContent['rec']) {
1588                 $userContent['accum'] .= $this->cObj->substituteMarkerArray($userContent['rec'], $markerArray);
1589             }
1590             if ($adminContent['rec']) {
1591                 $adminContent['accum'] .= $this->cObj->substituteMarkerArray($adminContent['rec'], $markerArray);
1592             }
1593         }
1594
1595         if ($userContent['all']) {
1596             $userContent['final'] .= $this->cObj->substituteSubpart($userContent['all'], '###SUB_RECORD###', $userContent['accum']);
1597         }
1598         if ($adminContent['all']) {
1599             $adminContent['final'] .= $this->cObj->substituteSubpart($adminContent['all'], '###SUB_RECORD###', $adminContent['accum']);
1600         }
1601
1602         $recipientID = MathUtility::canBeInterpretedAsInteger($recipient);
1603         if ($recipientID) {
1604             $fe_userRec = $GLOBALS['TSFE']->sys_page->getRawRecord('fe_users', $recipient);
1605             $recipient = $fe_userRec['email'];
1606         }
1607
1608         $this->getTimeTracker()->setTSlogMessage('Template key: ###'.$key.'###, userContentLength: '.strlen($userContent['final']).', adminContentLength: '.strlen($adminContent['final']));
1609
1610         //TODO: add optional Swiftmailer see #13129
1611         // send to admin, if set
1612         if ($this->conf['email.']['admin'] && $adminContent['final']) {
1613             $this->sendMail($this->conf['email.']['admin'], $adminContent['final']);
1614         }
1615
1616         // send to recipient
1617         if ($userContent['final']) {
1618             $this->sendMail($recipient, $userContent['final']);
1619         }
1620
1621         $this->getTimeTracker()->pull();
1622     }
1623
1624     /**
1625      * Actually sends the requested mails (through $this->cObj->sendNotifyEmail or through $this->sendHTMLMail).
1626      * As of TYPO3 v4.3 with autoloader, a check for $GLOBALS['TSFE']->config['config']['incT3Lib_htmlmail'] has been included for backwards compatibility.
1627      *
1628      * @param   string  $recipient  Recipient email address (or list)
1629      * @param   string  $content    Content for the regular email to user
1630      *
1631      * @see compileMail(), sendInfoMail()
1632      */
1633     public function sendMail($recipient, $content = '')
1634     {
1635         // Prepare the Mailer instance
1636         // init the swiftmailer object
1637         /** @var $mailer \TYPO3\CMS\Core\Mail\MailMessage */
1638         $mailer = GeneralUtility::makeInstance('TYPO3\\CMS\\Core\\Mail\\MailMessage');
1639         $mailer->setFrom(array($this->conf['email.']['from'] => $this->conf['email.']['fromName']));
1640         $mailer->setReplyTo(array($this->conf['email.']['from'] => $this->conf['email.']['fromName']));
1641         $mailer->setTo(array($recipient));
1642
1643         if ($this->isHTMLContent($content)) {
1644             //set HTML Subject
1645             $parts = preg_split('/<title>|<\/title>/i', $content, 3);
1646             $subject = trim($parts[1]) ? trim($parts[1]) : 'TYPO3 FE Admin message';
1647             $mailer->setSubject($subject);
1648
1649             //set HTML Content
1650             $mailer->setBody($content, 'text/html');
1651         } else {
1652             // set subject from plain
1653             $parts = explode(LF, trim($content), 2); // First line is subject
1654             $subject = trim($parts[0]);
1655             $plain_message = trim($parts[1]);
1656             $mailer->setSubject($subject);
1657
1658             // set plain text
1659             $mailer->setBody($plain_message, 'text/plain');
1660         }
1661
1662
1663         $mailer->send();
1664     }
1665
1666     /**
1667      * Detects if content is HTML (looking for <html> tag as first and last in string).
1668      *
1669      * @param   string  $c  Content string to test
1670      *
1671      * @return bool Returns true if the content begins and ends with <html></html>-tags
1672      */
1673     public function isHTMLContent($c)
1674     {
1675         $c = trim($c);
1676         $first = strtolower(substr($c, 0, 6));
1677         $last = strtolower(substr($c, -7));
1678         if ($first.$last == '<html></html>') {
1679             return 1;
1680         }
1681     }
1682
1683     /*****************************************
1684      *
1685      * Various helper functions
1686      *
1687      *****************************************/
1688
1689     /**
1690      * Returns true if authentication is OK based on the "aC" code which is a GET parameter set from outside with a hash string which must match some internal hash string.
1691      * This allows to authenticate editing without having a fe_users login
1692      * Uses $this->authCode which is set in init() by "GeneralUtility::_GP('aC');".
1693      *
1694      * @param   array   $r  The data array for which to evaluate authentication
1695      *
1696      * @return bool True if authenticated OK
1697      *
1698      * @see authCode(), init()
1699      */
1700     public function aCAuth($r)
1701     {
1702         if ($this->authCode && !strcmp($this->authCode, $this->authCode($r))) {
1703             return true;
1704         }
1705     }
1706
1707     /**
1708      * Creating authentication hash string based on input record and the fields listed in TypoScript property "authcodeFields".
1709      *
1710      * @param   array   $r  The data record
1711      * @param   string  $extra  Additional string to include in the hash
1712      *
1713      * @return string Hash string of $this->codeLength (if TypoScript "authcodeFields" was set)
1714      *
1715      * @see aCAuth()
1716      */
1717     public function authCode($r, $extra = '')
1718     {
1719         $l = $this->codeLength;
1720         if ($this->conf['authcodeFields']) {
1721             $fieldArr = GeneralUtility::trimExplode(',', $this->conf['authcodeFields'], 1);
1722             $value = '';
1723             foreach ($fieldArr as $field) {
1724                 $value .= $r[$field].'|';
1725             }
1726             $value .= $extra.'|'.$this->conf['authcodeFields.']['addKey'];
1727             if ($this->conf['authcodeFields.']['addDate']) {
1728                 $value .= '|'.date($this->conf['authcodeFields.']['addDate']);
1729             }
1730             $value .= $GLOBALS['TYPO3_CONF_VARS']['SYS']['encryptionKey'];
1731
1732             return substr(md5($value), 0, $l);
1733         }
1734     }
1735
1736     /**
1737      * Adding keys to the marker array with "setfixed" GET parameters.
1738      *
1739      * @param   array   $markerArray    Marker-array to modify/add a key to.
1740      * @param   array   $setfixed   TypoScript properties configuring "setfixed" for the plugin. Basically this is $this->conf['setfixed.'] passed along.
1741      * @param   array   $r  The data record
1742      *
1743      * @return array Processed $markerArray
1744      *
1745      * @see compileMail()
1746      */
1747     public function setfixed($markerArray, $setfixed, $r)
1748     {
1749         if (is_array($setfixed)) {
1750             foreach ($setfixed as $theKey => $data) {
1751                 if (!strcmp($theKey, 'DELETE')) {
1752                     $data = $setfixed['DELETE.'] ?: array();
1753                     $recCopy = $r;
1754                     $string = '&cmd=setfixed&sFK='.rawurlencode($theKey).'&rU='.$r['uid'];
1755                     $string .= '&aC='.$this->setfixedHash($recCopy, $data['_FIELDLIST']);
1756                     $markerArray['###SYS_SETFIXED_DELETE###'] = $string;
1757                     $markerArray['###SYS_SETFIXED_HSC_DELETE###'] = htmlspecialchars($string);
1758                 } elseif (strstr($theKey, '.')) {
1759                     $fields = $data;
1760                     unset($fields['_FIELDLIST']);
1761                     $fields = array_keys($fields);
1762                     if (isset($data['_FIELDLIST'])) {
1763                         $fields = array_merge($fields, GeneralUtility::trimExplode(',', $data['_FIELDLIST']));
1764                     }
1765                     $data['_FIELDLIST'] = implode(',', array_unique($fields));
1766
1767                     $theKey = substr($theKey, 0, -1);
1768                     if (is_array($data)) {
1769                         $recCopy = $r;
1770                         $string = '&cmd=setfixed&sFK='.rawurlencode($theKey).'&rU='.$r['uid'];
1771                         foreach ($data as $fieldName => $fieldValue) {
1772                             $string .= '&fD%5B'.$fieldName.'%5D='.rawurlencode($fieldValue);
1773                             $recCopy[$fieldName] = $fieldValue;
1774                         }
1775                         $string .= '&aC='.$this->setfixedHash($recCopy, $data['_FIELDLIST']);
1776                         $markerArray['###SYS_SETFIXED_'.$theKey.'###'] = $string;
1777                         $markerArray['###SYS_SETFIXED_HSC_'.$theKey.'###'] = htmlspecialchars($string);
1778                     }
1779                 }
1780             }
1781         }
1782
1783         return $markerArray;
1784     }
1785
1786     /**
1787      * Creating hash string for setFixed. Much similar to authCode().
1788      *
1789      * @param array  $recCopy The data record
1790      * @param string $fields  List of fields to use
1791      *
1792      * @return string Hash string of $this->codeLength (if TypoScript "authcodeFields" was set)
1793      *
1794      * @see setfixed(),authCode()
1795      */
1796     public function setfixedHash($recCopy, $fields = '')
1797     {
1798         if ($fields) {
1799             $fieldArr = GeneralUtility::trimExplode(',', $fields, 1);
1800             foreach ($fieldArr as $k => $v) {
1801                 $recCopy_temp[$k] = $recCopy[$v];
1802             }
1803         } else {
1804             $recCopy_temp = $recCopy;
1805         }
1806         $encStr = implode('|', $recCopy_temp).'|'.$this->conf['authcodeFields.']['addKey'].'|'.$GLOBALS['TYPO3_CONF_VARS']['SYS']['encryptionKey'];
1807         $hash = substr(md5($encStr), 0, $this->codeLength);
1808
1809         return $hash;
1810     }
1811
1812     /**
1813      * Returns true if preview display is on.
1814      *
1815      * @return bool
1816      */
1817     public function isPreview()
1818     {
1819         return $this->conf[$this->cmdKey.'.']['preview'] && $this->preview;
1820     }
1821
1822     /**
1823      * Creates an instance of class "\TYPO3\CMS\Core\Utility\File\BasicFileUtility" in $this->fileFunc (if not already done).
1824      */
1825     public function createFileFuncObj()
1826     {
1827         if (!$this->fileFunc) {
1828             $this->fileFunc = GeneralUtility::makeInstance('TYPO3\\CMS\\Core\\Utility\\File\\BasicFileUtility');
1829         }
1830     }
1831
1832     /**
1833      * If TypoScript property clearCacheOfPages is set then all page ids in this value will have their cache cleared.
1834      */
1835     public function clearCacheIfSet()
1836     {
1837         if ($this->conf['clearCacheOfPages']) {
1838             $cc_pidList = $GLOBALS['TYPO3_DB']->cleanIntList($this->conf['clearCacheOfPages']);
1839             $GLOBALS['TSFE']->clearPageCacheContent_pidList($cc_pidList);
1840         }
1841     }
1842
1843     /**
1844      * Set http header, so content won't be cached or indexed by search engine.
1845      */
1846     protected function setNoCacheHeader()
1847     {
1848         //send no-cache header
1849         // HTTP 1.1
1850         header('Cache-Control: no-cache, must-revalidate');
1851         // HTTP 1.0
1852         header('Pragma: no-cache');
1853         // robots
1854         header('X-Robots-Tag: noindex, nofollow');
1855     }
1856
1857     /**
1858      * Returns an error message for the field/command combination inputted. The error message is looked up in the TypoScript properties (evalErrors.[fieldname].[command]) and if empty then the $label value is returned.
1859      *
1860      * @param   string  $theField   Field name
1861      * @param   string  $theCmd Command identifier string
1862      * @param   string  $label  Alternative label, shown if no other error string was found
1863      *
1864      * @return string The error message string
1865      */
1866     public function getFailure($theField, $theCmd, $label)
1867     {
1868         return isset($this->conf['evalErrors.'][$theField.'.'][$theCmd]) ? $this->conf['evalErrors.'][$theField.'.'][$theCmd] : $label;
1869     }
1870
1871     /**
1872      * Will escape HTML-tags.
1873      *
1874      * @param   mixed   $var    The unescaped data
1875      *
1876      * @return mixed The processed input data
1877      */
1878     public function escapeHTML($var)
1879     {
1880         if (is_array($var)) {
1881             foreach ($var as $k => $value) {
1882                 $var[$k] = $this->escapeHTML($var[$k]);
1883             }
1884         } else {
1885             $var = htmlspecialchars($var, ENT_NOQUOTES);
1886         }
1887
1888         return $var;
1889     }
1890
1891     /**
1892      * @return object TYPO3\CMS\Core\TimeTracker
1893      */
1894     protected function getTimeTracker()
1895     {
1896         return $GLOBALS['TT'];
1897     }
1898 }
1899
1900 if (defined('TYPO3_MODE') && isset($GLOBALS['TYPO3_CONF_VARS'][TYPO3_MODE]['XCLASS']['media/scripts/fe_adminLib.inc'])) {
1901     include_once $GLOBALS['TYPO3_CONF_VARS'][TYPO3_MODE]['XCLASS']['media/scripts/fe_adminLib.inc'];
1902 }