Report.php 45.1 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<?php
/***************************************************************
 *  Copyright notice
 *
 *  (c) 2010 Glowbase GmbH <support@glowbase.com>
 *  All rights reserved
 *
 ***************************************************************/

namespace qfq;

//use qfq;

require_once(__DIR__ . '/Define.php');
require_once(__DIR__ . '/Variables.php');
require_once(__DIR__ . '/Error.php');
17
require_once(__DIR__ . '/../database/Database.php');
18
19
require_once(__DIR__ . '/Link.php');
require_once(__DIR__ . '/Sendmail.php');
20
require_once(__DIR__ . '/../exceptions/UserReportExtension.php');
21
require_once(__DIR__ . '/../Evaluate.php');
22
require_once(__DIR__ . '/../helper/KeyValueStringParser.php');
23

24
25
const DEFAULT_QUESTION = 'question';
const DEFAULT_ICON = 'icon';
26
const DEFAULT_BOOTSTRAP_BUTTON = 'bootstrapButton';
27
28
29

class Report {

Carsten  Rose's avatar
Carsten Rose committed
30
31
32
33
34
    /**
     * @var SIP
     */
    private $sip = null;

Carsten  Rose's avatar
Carsten Rose committed
35
36
37
38
39
    /**
     * @var Link
     */
    private $link = null;

40
41
42
43
44
    /**
     * @var Store
     */
    private $store = null;

45
46
47
48
49
50
51
    /**
     * @var string
     */
    private $dbAlias = '';

    // frArray[10.50.5.sql][select ...]
    private $frArray = array();
52

53
54
    // $indexArray[10][50][5]   one entry per 'sql' statement
    private $indexArray = array();
55

56
57
58
59
    // TODO to explain
//	private $resultArray = array();
    private $levelCount = 0;
    //private $counter = 0;
60

61
62
63
64
    /**
     * @var Variables
     */
    private $variables = null;
65

66
    /**
67
     * @var Database
68
69
     */
    private $db = null;
70

71
72
73
74
    /**
     * @var array
     */
    private $pageDefaults = array();
75

76
    /**
Carsten  Rose's avatar
Carsten Rose committed
77
78
     * @var array - Emulate global variable: will be set much earlier in other functions. Will be shown in error
     *      messages.
79
     */
80
81
    private $fr_error = array('uid' => '', 'pid' => '', 'row' => '', 'debug_level' => '0', 'full_level' => '');

82
83
    private $phpUnit = false;

84
    private $showDebugInfoFlag = false;
85

86
    /**
87
     * Report constructor.
88
     *
Carsten  Rose's avatar
Carsten Rose committed
89
     * @param array $t3data
90
     * @param Evaluate $eval
Carsten  Rose's avatar
Carsten Rose committed
91
     * @param bool $phpUnit
92
     */
93
    public function __construct(array $t3data, Evaluate $eval, $phpUnit = false) {
94
95

        $this->phpUnit = $phpUnit;
96

97
98
        Support::setIfNotSet($t3data, "uid", 0);

99
        $this->sip = new Sip($phpUnit);
100
101
        if ($phpUnit) {
            $this->sip->sipUniqId('badcaffee1234');
102
103
            //TODO Webserver Umgebung besser faken
            $_SERVER['REQUEST_URI'] = 'localhost';
104
105
        }

106
        $this->link = new Link($this->sip, $phpUnit);
Carsten  Rose's avatar
Carsten Rose committed
107

108
        $this->store = Store::getInstance();
109

110
        $this->showDebugInfoFlag = (Support::findInSet(SYSTEM_SHOW_DEBUG_INFO_YES, $this->store->getVar(SYSTEM_SHOW_DEBUG_INFO, STORE_SYSTEM)));
111

112
113
        $this->checkUpdateSqlLog();

114
115
        $this->pageDefaults[DEFAULT_QUESTION]["pagec"] = "Please confirm!:info";
        $this->pageDefaults[DEFAULT_QUESTION]["paged"] = "Do you really want to delete the record?:warning";
116

117
118
119
120
121
122
        $this->pageDefaults[DEFAULT_ICON]["paged"] = TOKEN_DELETE;
        $this->pageDefaults[DEFAULT_ICON]["pagee"] = TOKEN_EDIT;
        $this->pageDefaults[DEFAULT_ICON]["pageh"] = TOKEN_HELP;
        $this->pageDefaults[DEFAULT_ICON]["pagei"] = TOKEN_INFO;
        $this->pageDefaults[DEFAULT_ICON]["pagen"] = TOKEN_NEW;
        $this->pageDefaults[DEFAULT_ICON]["pages"] = TOKEN_SHOW;
123

124
125
126
127
128
129
        $this->pageDefaults[DEFAULT_BOOTSTRAP_BUTTON]["pagec"] = TOKEN_BOOTSTRAP_BUTTON;
        $this->pageDefaults[DEFAULT_BOOTSTRAP_BUTTON]["paged"] = TOKEN_BOOTSTRAP_BUTTON;
        $this->pageDefaults[DEFAULT_BOOTSTRAP_BUTTON]["pagee"] = TOKEN_BOOTSTRAP_BUTTON;
        $this->pageDefaults[DEFAULT_BOOTSTRAP_BUTTON]["pagen"] = TOKEN_BOOTSTRAP_BUTTON;
        $this->pageDefaults[DEFAULT_BOOTSTRAP_BUTTON]["pages"] = TOKEN_BOOTSTRAP_BUTTON;

130
        $this->db = new Database();
131
        $this->variables = new Variables($eval, $t3data["uid"]);
132

133
        // Set static values, which won't change during this run.
134
        $this->fr_error["pid"] = isset($this->variables->resultArray['global.']['page_id']) ? $this->variables->resultArray['global.']['page_id'] : 0;
135
        $this->fr_error["uid"] = $t3data['uid'];
136
        $this->fr_error["debug_level"] = 0;
137

Carsten  Rose's avatar
Carsten Rose committed
138
        // Sanitize function for POST and GET Parameters.
139
        // Merged URL-Parameter (key1, id etc...) in resultArray.
140
        $this->variables->resultArray = array_merge($this->variables->resultArray, array("global." => $this->variables->collectGlobalVariables()));
141
142
143

    }

144
    /**
Carsten  Rose's avatar
Carsten Rose committed
145
146
     * If a variable 'sqlLog' is given in STORE_TYPO3 (=Bodytext) make them relative to SYSTEM_PATH_EXT and copy it to
     * STORE_SYSTEM
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
     */
    private function checkUpdateSqlLog() {

        $sqlLog = $this->store->getVar(TYPO3_SQL_LOG, STORE_TYPO3);
        if (false !== $sqlLog) {
            if ($sqlLog != '' && $sqlLog[0] !== '/') {
                $sqlLog = $this->store->getVar(SYSTEM_PATH_EXT, STORE_SYSTEM) . '/' . $sqlLog;
            }

            $this->store->setVar(SYSTEM_SQL_LOG, $sqlLog, STORE_SYSTEM);
        }

        $sqlLogMode = $this->store->getVar(TYPO3_SQL_LOG_MODE, STORE_TYPO3);
        if (false !== $sqlLogMode) {
            $this->store->setVar(SYSTEM_SQL_LOG_MODE, $sqlLogMode, STORE_SYSTEM);
        }
    }

165
166
167
168
169
    /**
     * Main function. Parses bodytext and iterates over all queries.
     *
     * @return string
     */
170
    public function process($bodyText) {
171

172
173
174
175
176
        //phpUnit Test: clean environment
        $this->frArray = array();
        $this->indexArray = array();
        $this->levelCount = 0;

177
        // Iteration over Bodytext
178
        $ttLineArray = explode("\n", $bodyText);
179

180
181
182
        foreach ($ttLineArray as $index => $line) {
            // Fill $frArray, $indexArray, $resultArray
            $this->parseFRLine($line);
183
        }
184

185
186
187
188
189
190
        // Sort array
        $this->sortIndexArray($this->indexArray, $this->generateSortArg());

        // Report
        $content = $this->triggerReport();

191
        return $content;
192
    }
193
194
195
196
197
198

    /**
     * Split line in level, command, content and fill 'frArray', 'levelCount', 'indexArray'
     * Example: 10.50.5.sql = select * from person
     *
     * @param    string $ttLine : line to split in level, command, content
Carsten  Rose's avatar
Carsten Rose committed
199
     *
200
     * @throws SyntaxReportException
201
202
203
204
205
206
207
     * @return    void
     */
    private function parseFRLine($ttLine) {

        // 10.50.5.sql = select ...
        $arr = explode("=", trim($ttLine), 2);

208
        // no elements or only one: do nothing
209
        if (count($arr) < 2) {
210
            return;
211
        }
212

213
214
215
216
        // 10.50.5.sql
        $key = strtolower(trim($arr[0]));

        // comment ?
217
        if (empty($key) || $key[0] === "#") {
218
            return;
219
        }
220
221
222
223
224
225
226
227
228
229

        // select ...
        $value = trim($arr[1]);

        // 10.50.5.sql
        $arrKey = explode('.', $key);

        // frCmd = "sql"
        $frCmd = $arrKey[count($arrKey) - 1];

230
231
232
233
        if (strpos('|' . strtolower(TOKEN_VALID_LIST) . '|', '|' . $frCmd . '|') === false) {
            throw new SyntaxReportException ("Unknown token: $frCmd in Line '$ttLine''", ERROR_UNKNOWN_TOKEN, null, __FILE__, __LINE__, $this->fr_error);
        }

234
235
236
237
238
239
240
241
242
243
244
        // remove last item (cmd)
        unset($arrKey[count($arrKey) - 1]);

        // save elements only if there is a level specified
        if (count($arrKey)) {
            // level = "10.50.5"
            $level = implode(".", $arrKey);

            // fill Array
            $this->setLine($level, $frCmd, $value);
        }
245
    }
246

247
248
249
250
251
    /**
     * @param $level
     * @param $frCmd
     * @param $value
     */
252
253
254
255
256
257
258
259
    private function setLine($level, $frCmd, $value) {

        // store complete line reformatted in frArray
        $this->frArray[$level . "." . $frCmd] = $value;

        // per sql command
        //pro sql cmd wir der Indexarray abgefüllt. Dieser wird später verwendet um auf den $frArray zuzugreifen
        //if(preg_match("/^sql/i", $frCmd) == 1){
260
        if ($frCmd === TOKEN_FORM || $frCmd === TOKEN_SQL) {
261
262
263
264
265
            // Remember max level
            $this->levelCount = max(substr_count($level, '.') + 1, $this->levelCount);
            // $indexArray[10][50][5]
            $this->indexArray[] = explode(".", $level);
        }
266
267
268

        // set defaults
        if ($frCmd === TOKEN_SQL) {
269
270
            $arr = explode('|', TOKEN_VALID_LIST);
            foreach ($arr as $key) {
271
                if (!isset($this->frArray[$level . "." . $key])) {
272
                    $this->frArray[$level . "." . $key] = '';
273
                }
274
275
            }
        }
276
    }
277
278
279
280

    /**
     * Sorts the associative array.
     *
Carsten  Rose's avatar
Carsten Rose committed
281
     * @param array $ary : The unsorted Level Array
282
     * @param string $clause : the sort argument 0 ASC, 1 ASC... according to the number of columns
283
     * @param bool|true $ascending
284
     */
285
    private function sortIndexArray(array &$ary, $clause, $ascending = true) {
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329

        $clause = str_ireplace('order by', '', $clause);
        $clause = preg_replace('/\s+/', ' ', $clause);
        $keys = explode(',', $clause);
        $dirMap = array('desc' => 1, 'asc' => -1);
        $def = $ascending ? -1 : 1;

        $keyAry = array();
        $dirAry = array();
        foreach ($keys as $key) {
            $key = explode(' ', trim($key));
            $keyAry[] = trim($key[0]);
            if (isset($key[1])) {
                $dir = strtolower(trim($key[1]));
                $dirAry[] = $dirMap[$dir] ? $dirMap[$dir] : $def;
            } else {
                $dirAry[] = $def;
            }
        }
        $fnBody = '';
        for ($i = count($keyAry) - 1; $i >= 0; $i--) {
            $k = $keyAry[$i];
            $t = $dirAry[$i];
            $f = -1 * $t;
            $aStr = '$a[\'' . $k . '\']';
            $bStr = '$b[\'' . $k . '\']';

            if (strpos($k, '(') !== false) {
                $aStr = '$a->' . $k;
                $bStr = '$b->' . $k;
            }

            if ($fnBody == '') {
                $fnBody .= "if({$aStr} == {$bStr}) { return 0; }\n";
                $fnBody .= "return ({$aStr} < {$bStr}) ? {$t} : {$f};\n";
            } else {
                $fnBody = "if({$aStr} == {$bStr}) {\n" . $fnBody;
                $fnBody .= "}\n";
                $fnBody .= "return ({$aStr} < {$bStr}) ? {$t} : {$f};\n";
            }
        }

        if ($fnBody) {
            $sortFn = create_function('$a,$b', $fnBody);
330
331
332
333
334

            // TODO: at the moment, $sortFn() triggers some E_NOTICE warnings. We stop these here for a short time.
            $errorSet = error_reporting();
            error_reporting($errorSet & ~E_NOTICE);

335
            usort($ary, $sortFn);
336
337

            error_reporting($errorSet);
338
        }
339
    }
340

341
342
343
344
345
    /**
     * generateSortArg
     *
     * @return string
     */
346
347
348
349
350
351
352
    private function generateSortArg() {

        $sortArg = "";

        for ($i = 0; $i < $this->levelCount; $i++) {
            $sortArg = $sortArg . $i . " ASC, ";
        }
353

354
        $sortArg = substr($sortArg, 0, strlen($sortArg) - 2);
355

356
        return $sortArg;
357
    }
358
359
360
361
362
363
364
365
366
367
368
369
370
371

    /**
     * Executes the queries recursive. This Method is called for each Sublevel.
     *
     * ROOTLEVEL
     * This method is called once from the main method.
     * For the first call the method executes the rootlevels
     *
     * SUBLEVEL
     * For each rootlevel the method calls it self whith the levelmode 0
     * If the next Level is a Sublevel it will be executed and $this->counter will be added by 1
     * The sublevel calls the method again for a following sublevel
     *
     * @param int $cur_level Which level it will call [10] = level 1, [10.10] = level 2 ...
372
     * @param string $super_level_array The Value-Array of the indexarray [0=>10, 1=>50]
Carsten  Rose's avatar
Carsten Rose committed
373
     * @param int $counter The outer numeric Arraykey of indexarray
Carsten  Rose's avatar
Carsten Rose committed
374
     *
375
376
377
378
379
380
381
     * @return string                       The content that is displayed on the website
     * @throws codeException
     * @throws SqlReportException
     * @throws SyntaxReportException
     * @throws UserReportException
     */

382
383
    private function triggerReport($cur_level = 1, array $super_level_array = array(), $counter = 0) {
        $keys = array();
384
        $stat = array();
385
386
387
388
389
390

        $lineDebug = 0;
        $content = "";
        $rowTotal = 0;

        // CurrentLevel "10.10.50"
391
392
393
394
395
396
        if (isset($this->indexArray[$counter]) && is_array($this->indexArray[$counter])) {
            $full_level = implode(".", $this->indexArray[$counter]);
        } else {
            $full_level = '';
        }

397
        // Superlevel "10.10"
398
399
400
401
402
        if (isset($super_level_array) && is_array($super_level_array)) {
            $full_super_level = implode(".", $super_level_array);
        } else {
            $full_super_level = '';
        }
403
404
405
406
407
408
409

        //condition1: indexArray
        //condition2: full_level == Superlevel (but at the length of the Superlevel)
        while ($counter < count($this->indexArray) && $full_super_level == substr($full_level, 0, strlen($full_super_level))) {

            //True: The cur_level is a subquery -> continue
            if ($cur_level != count($this->indexArray[$counter])) {
410
411
412
413
414
415
                ++$counter;
                if (isset($this->indexArray[$counter]) && is_array($this->indexArray[$counter])) {
                    $full_level = implode(".", $this->indexArray[$counter]);
                } else {
                    $full_level = '';
                }
416
417
418
419
                continue;
            }

            // Set debug, if one is specified else keep the parent one.
420
            $lineDebug = $this->getValueParentDefault(TOKEN_DEBUG, $full_super_level, $full_level, $cur_level, 0);
421
422

            // Prepare Error reporting
423
            $this->store->setVar(SYSTEM_SQL_RAW, $this->frArray[$full_level . "." . TOKEN_SQL], STORE_SYSTEM);
424
            $this->store->setVar(SYSTEM_REPORT_FULL_LEVEL, $full_level, STORE_SYSTEM);
425

426
427
428
            // Prepare SQL: replace variables. Actual 'line.total' or 'line.count' will recalculated: don't replace them now!
            unset($this->variables->resultArray[$full_level . ".line."]["total"]);
            unset($this->variables->resultArray[$full_level . ".line."]["count"]);
429
            $sql = $this->variables->doVariables($this->frArray[$full_level . "." . TOKEN_SQL]);
430

431
            $this->store->setVar(SYSTEM_SQL_FINAL, $sql, STORE_SYSTEM);
432
433
434

            //Execute SQL. All errors have been already catched.
            unset($result);
435
            $result = $this->db->sql($sql, ROW_KEYS, array(), '', $keys, $stat);
436
437
438

            // If an array is returned, $sql was a query, otherwise an 'insert', 'update', 'delete', ...
            // Query: total nummber of rows
439
440
            // insert, delete, update: number of affected rows
            $rowTotal = isset($stat[DB_NUM_ROWS]) ? $stat[DB_NUM_ROWS] : $stat[DB_AFFECTED_ROWS];
441
442

            $this->variables->resultArray[$full_level . ".line."]["total"] = $rowTotal;
443
444
445
            if (isset($stat[DB_INSERT_ID])) {
                $this->variables->resultArray[$full_level . ".line."]["insertId"] = $stat[DB_INSERT_ID];
            }
446
447

            // HEAD: If there is at least one record, do 'head'.
448
            if ($rowTotal > 0) {
449
                $content .= $this->variables->doVariables($this->frArray[$full_level . "." . TOKEN_HEAD]);
450
            }
451
452

            // Prepare row alteration
453
454
455
456
457
            $arrRbgd = explode("|", $this->frArray[$full_level . "." . TOKEN_RBGD]);
            if (count($arrRbgd) < 2) {
                $arrRbgd[] = '';
                $arrRbgd[] = '';
            }
458

459
460
461
462
463
464
465
466
467
468
469
470
471
472
            if (is_array($result)) {
                //---------------------------------
                // Process each row of resultset
                $columnValueSeperator = "";
                $rowIndex = 0;
                foreach ($result as $row) {
                    // record number counter
                    $this->variables->resultArray[$full_level . ".line."]["count"] = ++$rowIndex;

                    // replace {{<level>.line.count}} and {{<level>.line.total}} in __result__, if the variables specify their own full_level. This can't be replaced before firing the query.
                    for ($ii = 0; $ii < count($row); $ii++) {
                        $row[$ii] = str_replace("{{" . $full_level . ".line.count}}", $rowIndex, $row[$ii]);
                        $row[$ii] = str_replace("{{" . $full_level . ".line.total}}", $rowTotal, $row[$ii]);
                    }
473

474
475
                    // SEP set seperator (empty on first run)
                    $content .= $columnValueSeperator;
476
                    $columnValueSeperator = $this->variables->doVariables($this->frArray[$full_level . "." . TOKEN_RSEP]);
477

478
                    // RBGD: even/odd rows
479
                    $content .= str_replace(TOKEN_RBGD, $arrRbgd[$rowIndex % 2], $this->frArray[$full_level . "." . TOKEN_RBEG]);
480

481
482
483
                    //-----------------------------
                    // COLUMNS: Collect all columns
                    $content .= $this->collectRow($row, $keys, $full_level, $rowIndex);
484

485
                    // REND
486
                    $content .= $this->variables->doVariables($this->frArray[$full_level . "." . TOKEN_REND]);
487

488
489
                    // Trigger subqueries of this level
                    $content .= $this->triggerReport($cur_level + 1, $this->indexArray[$counter], $counter + 1);
490

491
                    // RENR
492
                    $content .= $this->variables->doVariables($this->frArray[$full_level . "." . TOKEN_RENR]);
493

494
                }
495
496
497
498
            }

            //Print althead or tail
            if ($rowTotal > 0) {
499
                $content .= $this->variables->doVariables($this->frArray[$full_level . "." . TOKEN_TAIL]);
500
            } else {
501
                $content .= $this->variables->doVariables($this->frArray[$full_level . "." . TOKEN_ALT_HEAD]);
502
503
            }

504
505
506
507
508
509
            ++$counter;
            if (isset($this->indexArray[$counter]) && is_array($this->indexArray[$counter])) {
                $full_level = implode(".", $this->indexArray[$counter]);
            } else {
                $full_level = '';
            }
510
511
512
        }

        return $content;
513
    }
514
515
516
517
518
519
520
521

    /**
     * Determine value:
     * 1) if one specified in line: take it
     * 2) if one specified in upper level: take it
     * 3) if none above take default
     * Set value on $full_level
     *
Carsten  Rose's avatar
Carsten Rose committed
522
     * @param    string $level_key - 'db' or 'debug'
523
524
     * @param    string $full_super_level - f.e.: 10.10.
     * @param    string $full_level - f.e.: 10.10.10.
Carsten  Rose's avatar
Carsten Rose committed
525
526
     * @param    string $cur_level - f.e.: 2
     * @param    string $default - f.e.: 0
Carsten  Rose's avatar
Carsten Rose committed
527
     *
528
529
530
531
     * @return   string  The calculated value.
     */
    private function getValueParentDefault($level_key, $full_super_level, $full_level, $cur_level, $default) {

532
        if (!empty($this->frArray[$full_level . "." . $level_key])) {
533
534
535
536
537
538
539
540
541
542
543
            $value = $this->frArray[$full_level . "." . $level_key];
        } else {
            if ($cur_level == 1) {
                $value = $default;
            } else {
                $value = $this->variables->resultArray[$full_super_level . ".line."][$level_key];
            }
        }
        $this->variables->resultArray[$full_level . ".line."][$level_key] = $value;

        return ($value);
544
    }
545
546
547
548

    /**
     * Steps through 'row' and collects all columns
     *
Carsten  Rose's avatar
Carsten Rose committed
549
550
     * @param array $row Recent row fetch from sql resultset.
     * @param array $keys List of all columnnames
551
552
     * @param string $full_level Recent position to work on.
     * @param string $rowIndex Index of recent row in resultset.
Carsten  Rose's avatar
Carsten Rose committed
553
     *
554
555
556
557
558
     * @return string               Collected content of all printable columns
     * @throws SyntaxReportException
     */
    private function collectRow(array $row, array $keys, $full_level, $rowIndex) {
        $content = "";
559
        $assoc = array();
560

561
        $fsep = '';
562
563
        for ($ii = 0; $ii < count($keys); $ii++) {

564
            // Debugging
565
566
567
            $this->store->setVar(SYSTEM_REPORT_COLUMN_INDEX, $ii + 1, STORE_SYSTEM);
            $this->store->setVar(SYSTEM_REPORT_COLUMN_NAME, $keys[$ii], STORE_SYSTEM);
            $this->store->setVar(SYSTEM_REPORT_COLUMN_VALUE, $row[$ii], STORE_SYSTEM);
568

569
570
            $flagOutput = false;
            $renderedColumn = $this->renderColumn($ii, $keys[$ii], $row[$ii], $full_level, $rowIndex, $flagOutput);
571

572
            $keyAssoc = Support::stripFirstCharIf(TOKEN_COLUMN_CTRL, $keys[$ii]);
573
574
575
576
            if ($keyAssoc != '') {
                $assoc[$keyAssoc] = $row[$ii];
            }

577
            if ($flagOutput) {
578
                //prints
579
                $content .= $this->variables->doVariables($fsep);
580
                $content .= $this->variables->doVariables($this->frArray[$full_level . "." . TOKEN_FBEG]);
581
                $content .= $renderedColumn;
582
583
                $content .= $this->variables->doVariables($this->frArray[$full_level . "." . TOKEN_FEND]);
                $fsep = $this->frArray[$full_level . "." . TOKEN_FSEP];
584
585
            }
        }
Carsten  Rose's avatar
Carsten Rose committed
586

587
588
        $this->store->appendToStore(STORE_RECORD, $assoc);

589
        return ($content);
590
    }
591
592
593
594

    /**
     * Renders column depending of column name (if name is a reserved column name)
     *
595
596
597
598
599
     * @param string $columnIndex
     * @param string $columnName
     * @param string $columnValue
     * @param string $full_level
     * @param string $rowIndex
Carsten  Rose's avatar
Carsten Rose committed
600
     *
601
602
     * @return string rendered column
     * @throws SyntaxReportException
603
     */
604
    private function renderColumn($columnIndex, $columnName, $columnValue, $full_level, $rowIndex, &$flagOutput) {
605
        $content = "";
606
607
        $flagControl = false;
        $flagOutput = true;
Carsten  Rose's avatar
Carsten Rose committed
608
        $dummy = false;
609

Carsten  Rose's avatar
Carsten Rose committed
610
        // Empty columnnames are allowed: check with isset
611
        if (isset($columnName[0]) && $columnName[0] === TOKEN_COLUMN_CTRL) {
612
            $flagControl = true;
613
614
            $columnName = substr($columnName, 1);
        }
615

616
        //TODO: reserved names,not starting with '_' will be still accepted - stop this!
617
618
        switch ($columnName) {
            case "link":
Carsten  Rose's avatar
Carsten Rose committed
619
                $content .= $this->link->renderLink($columnValue);
620
                break;
621

622
623
624
625
            case "exec":
                $content .= $this->myExec($columnValue);
                break;

626
627
628
629
630
631
632
633
634
            // Uppercase 'P'
            case COLUMN_PPAGE:
            case COLUMN_PPAGEC:
            case COLUMN_PPAGED:
            case COLUMN_PPAGEE:
            case COLUMN_PPAGEH:
            case COLUMN_PPAGEI:
            case COLUMN_PPAGEN:
            case COLUMN_PPAGES:
635
            $lowerColumnName = strtolower($columnName);
636
                $tokenizedValue = $this->doFixColPosPage($columnName, $columnValue);
637
            $linkValue = $this->doPage($lowerColumnName, $tokenizedValue);
Carsten  Rose's avatar
Carsten Rose committed
638
            $content .= $this->link->renderLink($linkValue);
639
640
                break;

641
642
643
644
645
646
647
648
649
            // Lowercase 'P'
            case COLUMN_PAGE:
            case COLUMN_PAGEC:
            case COLUMN_PAGED:
            case COLUMN_PAGEE:
            case COLUMN_PAGEH:
            case COLUMN_PAGEI:
            case COLUMN_PAGEN:
            case COLUMN_PAGES:
650
                $linkValue = $this->doPage($columnName, $columnValue);
Carsten  Rose's avatar
Carsten Rose committed
651
            $content .= $this->link->renderLink($linkValue);
652
653
                break;

654
655
656
657
            case COLUMN_PPDF:
            case COLUMN_FFILE:
            case COLUMN_ZZIP:
                $lowerColumnName = strtolower($columnName);
Carsten  Rose's avatar
Carsten Rose committed
658
                $tokenizedValue = $this->doFixColPosDownload($columnValue);
659
                $linkValue = $this->doDownload($lowerColumnName, $tokenizedValue);
Carsten  Rose's avatar
Carsten Rose committed
660
                $content .= $this->link->renderLink($linkValue);
Carsten  Rose's avatar
Carsten Rose committed
661
662
                break;

663
664
665
666
            case COLUMN_PDF:
            case COLUMN_FILE:
            case COLUMN_ZIP:
                $linkValue = $this->doDownload($columnName, $columnValue);
Carsten  Rose's avatar
Carsten Rose committed
667
                $content .= $this->link->renderLink($linkValue);
Carsten  Rose's avatar
Carsten Rose committed
668
669
                break;

670
671
672
673
674
675
676
677
678
679
680
681
682
            case COLUMN_NL2BR:
                $content .= nl2br($columnValue);
                break;

            case COLUMN_HTMLENTITIES:
                $content .= htmlentities($columnValue);
                break;

            case COLUMN_STRIPTAGS:
                $content .= strip_tags($columnValue);
                break;


683
            case "bullet":
684
                if ($columnValue === '') {
685
                    break;
686
                }
687

Carsten  Rose's avatar
Carsten Rose committed
688
689
                // r:3|B:
                $linkValue = TOKEN_RENDER . ":3|" . TOKEN_BULLET . ":" . $columnValue;
Carsten  Rose's avatar
Carsten Rose committed
690
                $content .= $this->link->renderLink($linkValue);
691
692
693
                break;

            case "check":
694
                if ($columnValue === '') {
695
                    break;
696
                }
697

Carsten  Rose's avatar
Carsten Rose committed
698
699
                // "r:3|C:
                $linkValue = TOKEN_RENDER . ":3|" . TOKEN_CHECK . ":" . $columnValue;
Carsten  Rose's avatar
Carsten Rose committed
700
                $content .= $this->link->renderLink($linkValue);
701
702
                break;

703
704
            case "img":
                // "<path to image>|[alttext]|[text behind]" renders to: <img src="<path to image>" alt="[alttext]">[text behind]
705
                if (empty($columnValue)) {
Carsten  Rose's avatar
Carsten Rose committed
706
                    break;
707
                }
Carsten  Rose's avatar
Carsten Rose committed
708

709
                $mailConfig = explode("|", $columnValue, 3);
710
711

                // Fake values for tmp[1], tmp[2] to suppress access errors.
712
713
                $mailConfig[] = '';
                $mailConfig[] = '';
714

715
                if (empty($mailConfig[0])) {
716
                    break;
717
                }
718
719
                $attribute = Support::doAttribute('src', $mailConfig[0]);
                $attribute .= Support::doAttribute('alt', $mailConfig[1]);
720

721
                $content .= '<img ' . $attribute . '>' . $mailConfig[2];
722
723
724
725
                break;

            case "mailto":
                // "<email address>|[Real Name]"  renders to (encrypted via JS): <a href="mailto://<email address>"><email address></a> OR <a href="mailto://<email address>">[Real Name]</a>
726
727
                $mailConfig = explode("|", $columnValue, 2);
                if (empty($mailConfig[0])) {
728
                    break;
729
                }
730

731
                $t1 = explode("@", $mailConfig[0], 2);
732
                $content .= "<script language=javascript><!--" . chr(10);
733
734
                if (empty($mailConfig[1])) {
                    $mailConfig[1] = $mailConfig[0];
735
                }
736

737
738
                $content .= 'var contact = "' . substr($mailConfig[1], 0, 2) . '"' . chr(10);
                $content .= 'var contact1 = "' . substr($mailConfig[1], 2) . '"' . chr(10);
739
740
741
742
743
744
745
746
747
                $content .= 'var email = "' . $t1[0] . '"' . chr(10);
                $content .= 'var emailHost = "' . $t1[1] . '"' . chr(10);

                $content .= 'document.write("<a href=" + "mail" + "to:" + email + "@" + emailHost+ ">" + contact + "</a>")' . chr(10);
                $content .= 'document.write("<a href=" + "mail" + "to:" + email + "@" + emailHost+ ">" + contact1 + "</a>")' . chr(10);
                $content .= '//--></script>';
                break;

            case "sendmail":
748
                // '<receiver1>,<receiver2>,...|<sender>|<subject>|<body>|<reply-to>|<flag autosubmit: on /off>'
749
//                $mailarr = explode("|", $columnValue);
750
751
                $mailConfig = $this->sendmailConvertToken($columnValue);
                if (count($mailConfig) < 4) {
752
                    throw new SyntaxReportException ("Too few parameter for sendmail: $columnValue", ERROR_TOO_FEW_PARAMETER_FOR_SENDMAIL, null, __FILE__, __LINE__, $this->fr_error);
753
                }
754

755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
//                if (!isset($mailarr[SENDMAIL_IDX_REPLY_TO])) {
//                    $mailarr[SENDMAIL_IDX_REPLY_TO] = '';
//                }
//
//                if (!isset($mailarr[SENDMAIL_IDX_FLAG_AUTO_SUBMIT])) {
//                    $mailarr[SENDMAIL_IDX_FLAG_AUTO_SUBMIT] = 'on';
//                }
//
//                if (!isset($mailarr[SENDMAIL_IDX_GR_ID])) {
//                    $mailarr[SENDMAIL_IDX_GR_ID] = '0';
//                }
//
//                if (!isset($mailarr[SENDMAIL_IDX_X_ID])) {
//                    $mailarr[SENDMAIL_IDX_X_ID] = '0';
//                }
//
//                if (!isset($mailarr[SENDMAIL_IDX_RECEIVER_CC])) {
//                    $mailarr[SENDMAIL_IDX_RECEIVER_CC] = '';
//                }
//
//                if (!isset($mailarr[SENDMAIL_IDX_RECEIVER_BCC])) {
//                    $mailarr[SENDMAIL_IDX_RECEIVER_BCC] = '';
//                }
//
//                if (!isset($mailarr[SENDMAIL_IDX_X_ID])) {
//                    $mailarr[SENDMAIL_IDX_X_ID] = '0';
//                }
//
//                if (!isset($mailarr[SENDMAIL_IDX_X_ID2])) {
//                    $mailarr[SENDMAIL_IDX_X_ID] = '0';
//                }
//
//                if (!isset($mailarr[SENDMAIL_IDX_X_ID3])) {
//                    $mailarr[SENDMAIL_IDX_X_ID] = '0';
//                }

                $mailConfig[SENDMAIL_IDX_SRC] = "Report: T3 pageId=" . $this->store->getVar('pageId', STORE_TYPO3) .
792
793
                    ", T3 ttcontentId=" . $this->store->getVar('ttcontentUid', STORE_TYPO3) .
                    ", Level=" . $full_level;
794

795
                new Sendmail($mailConfig);
796
797
798
                break;

            case "vertical":
799
                // '<Text>|[angle]|[width]|[height]|[tag]'   , width and height needs a unit (px, em ,...), 'tag' might be 'div', 'span', ...
800
801
802
803
804
805
806
                $arr = explode("|", $columnValue, 5);

                # angle
                $angle = $arr[1] ? $arr[1] : 270;

                # width
                $width = $arr[2] ? $arr[2] : "1em";
807
                $extra = "width:$width; ";
808
809

                # height
810
                if ($arr[3]) {
811
                    $extra .= "height:" . $arr[3] . "; ";
812
                }
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832

                # tag
                if ($arr[4]) {
                    $tag = explode(" ", trim($arr[4]), 2);
                    $tag_open = "<" . $arr[4] . " ";
                    $tag_close = "</" . $tag[0] . ">";
                } else {
                    $tag_open = "<div ";
                    $tag_close = "</div>";
                }

                # http://scottgale.com/blog/css-vertical-text/2010/03/01/
                #$style = "writing-mode:tb-rl; -webkit-transform:rotate(270deg); -moz-transform:rotate(270deg); -o-transform: rotate(270deg); white-space:nowrap; width:1em;  display: block;";
                $style = "width:1em;  filter: flipv fliph; transform: rotate(" . $angle . "deg) translate(-10em,0); transform-origin: 0 0; writing-mode:tb-rl; -webkit-transform:rotate(" . $angle . "deg); -moz-transform:rotate(" . $angle . "deg); -o-transform: rotate(" . $angle . "deg); white-space:nowrap; display: block;";

                #$style = "line-height: 1.5em; background:#eee; display: block; white-space: nowrap; padding-left: 3px; writing-mode: tb-rl; filter: flipv fliph; transform: rotate(270deg) translate(-10em,0); transform-origin: 0 0; -moz-transform: rotate(270deg) translate(-10em,0); -moz-transform-origin: 0 0; -webkit-transform: rotate(270deg) translate(-10em,0); -webkit-transform-origin: 0 0;";
                $content = $tag_open . 'style="' . $style . '">' . $arr[0] . $tag_close;
                break;

            default :
833
                if ($flagControl) {
834
835
836
837
838
839
840

                    // Columnnames starting with '+' will wrap the content. EG "SELECT 'apple' AS '+h1 class=best'" >> <h1 class=best>apple</h1>
                    if (isset($columnName[0]) && $columnName[0] == COLUMN_WRAP_TOKEN && isset($columnName[1])) {
                        $content = Support::wrapTag('<' . substr($columnName, 1) . '>', $columnValue);
                        break;
                    }

841
842
                    $flagOutput = false;
                } else {
843
                    // No special Columnname: just add the column value.
844
845
                    $content .= $columnValue;
                }
846
847
                break;
        }
848

849
850
        // Always save column values, even if they are hidden.
        $this->variables->resultArray[$full_level . "."][$columnName] = ($content == '' && $flagControl) ? $columnValue : $content;
851
852

        return $content;
853
    }
854
855
856
857
858
859

    /**
     * Executes the Command in $cmd
     * RC: if RC==0 Returns Output, else 'RC - Output'
     *
     * @param    string $cmd : contains the Comand
Carsten  Rose's avatar
Carsten Rose committed
860
     *
861
862
863
864
865
866
     * @return   string  The content that is displayed on the website
     */
    private function myExec($cmd) {

        exec($cmd, $arr, $rc);

867
        $output = implode('<br>', $arr);
868
        if ($rc != 0) {
869
            $output = $rc . " - " . $output;
870
        }
871
872

        return ($output);
873
    }
874
875
876
877
878
879

    /**
     * The main method of the PlugIn
     *
     * @param    string $content : The PlugIn content
     * @param    array $conf : The PlugIn configuration
Carsten  Rose's avatar
Carsten Rose committed
880
     *
Carsten  Rose's avatar
Carsten Rose committed
881
     * @return    string The content that is displayed on the website
882
     */
Carsten  Rose's avatar
Carsten Rose committed
883
884
885
    //Checkt ob der Beginn von Array2 gleich ist wie Array1
    // gibt true/false zurück

886
887
888
889
890
    /**
     * Renders PageX: convert position content to token content. Respect default values depending on PageX
     *
     * @param    string $columnName
     * @param    string $columnValue
Carsten  Rose's avatar
Carsten Rose committed
891
     *
892
893
894
895
     * @return string rendered link
     *
     * $columnValue:
     * -------------
896
     * [<page id|alias>[&param=value&...]] | [text] | [tooltip] | [msgbox] | [class] | [target] | [render mode]
897
     *
898
     * param[0]: <page id|alias>[&param=value&...]
899
900
901
902
903
904
905
906
907
908
     * param[2]: text
     * param[3]: tooltip
     * param[4]: msgbox
     * param[5]: class
     * param[6]: target
     * param[7]: render mode
     * @throws SyntaxReportException
     */
    private function doFixColPosPage($columnName, $columnValue) {

Carsten  Rose's avatar
Carsten Rose committed
909
        $tokenList = "";
910

Carsten  Rose's avatar
Carsten Rose committed
911
        if (empty($columnName)) {
912
            return '';
Carsten  Rose's avatar
Carsten Rose committed
913
        }
914

915
        // Split definition
916
        $allParam = explode('|', $columnValue);
Carsten  Rose's avatar
Carsten Rose committed
917
        if (count($allParam) > 8) {
918
            throw new SyntaxReportException ("Too many parameter (max=8): $columnValue", ERROR_TOO_MANY_PARAMETER, null, __FILE__, __LINE__, $this->fr_error);
Carsten  Rose's avatar
Carsten Rose committed
919
        }
920

921
922
923
924
925
926
        // First Parameter: Split PageId|PageAlias and  URL Params
        $firstParam = explode('&', $allParam[0], 2);
        if (empty($firstParam[1])) {
            $firstParam[] = '';
        }

927
928
929
930
931
932
933
934
935
        switch ($columnName) {
            case COLUMN_PPAGED:
                // no pageid /pagealias given.
                $tokenList .= $this->composeLinkPart(TOKEN_URL_PARAM, $allParam[0]);
                break;
            default:
                $tokenList .= $this->composeLinkPart(TOKEN_PAGE, $firstParam[0]);            // -- PageID --
                $tokenList .= $this->composeLinkPart(TOKEN_URL_PARAM, $firstParam[1]);
        }
936

937
        if (isset($allParam[1]) && $allParam[1] !== '') {
Carsten  Rose's avatar
Carsten Rose committed
938
            $tokenList .= $this->composeLinkPart(TOKEN_TEXT, $allParam[1]);             // -- Text --
939
        }
940

941
        if (isset($allParam[2]) && $allParam[2] !== '') {
Carsten  Rose's avatar
Carsten Rose committed
942
            $tokenList .= $this->composeLinkPart(TOKEN_TOOL_TIP, $allParam[2]);         // -- tooltip --
943
        }
944

945
        if (isset($allParam[3]) && $allParam[3] !== '') {
946
            $text = isset($this->pageDefaults[DEFAULT_QUESTION][$columnName]) ? $this->pageDefaults[DEFAULT_QUESTION][$columnName] : '';
Carsten  Rose's avatar
Carsten Rose committed
947
            $tokenList .= $this->composeLinkPart(TOKEN_QUESTION, $allParam[3], $text);                // -- msgbox
948
        }
949

950
        if (isset($allParam[4]) && $allParam[4] !== '') {
Carsten  Rose's avatar
Carsten Rose committed
951
            $tokenList .= $this->composeLinkPart(TOKEN_CLASS, $allParam[4]);            // -- class --
952
        }
953

954
        if (isset($allParam[5]) && $allParam[5] !== '') {
Carsten  Rose's avatar
Carsten Rose committed
955
            $tokenList .= $this->composeLinkPart(TOKEN_TARGET, $allParam[5]);           // -- target --
956
        }
957

958
        if (isset($allParam[6]) && $allParam[6] !== '') {
Carsten  Rose's avatar
Carsten Rose committed
959
            $tokenList .= $this->composeLinkPart(TOKEN_RENDER, $allParam[6]);           // -- render mode --
960
        }
Carsten  Rose's avatar
Carsten Rose committed
961

962
        if (!isset($allParam[7])) {
963
            $allParam[7] = '1'; // if no SIP behaviour defined: sip is set
964
        }
965

Carsten  Rose's avatar
Carsten Rose committed
966
        $tokenList .= $this->composeLinkPart(TOKEN_SIP, $allParam[7]);           // -- SIP --
967

968
969
        if (isset($this->pageDefaults[DEFAULT_ICON][$columnName])) {
            $tokenList .= $this->pageDefaults[DEFAULT_ICON][$columnName] . "|";
970
        }
971

Carsten  Rose's avatar
Carsten Rose committed
972
        return ($tokenList);
973
    }
974

Carsten  Rose's avatar
Carsten Rose committed
975
976
    /**
     * Renders Download: convert position content to token content. Respect default values.
977
     * <exportFilename> | <text> | <1: urlparam|file> | <2: urlparam|file> | ... | <n: urlparam|file>
Carsten  Rose's avatar
Carsten Rose committed
978
     *
979
     * @param string $columnValue
Carsten  Rose's avatar
Carsten Rose committed
980
     *
Carsten  Rose's avatar
Carsten Rose committed
981
982
983
984
985
986
987
988
     * @return string rendered link
     * @throws SyntaxReportException
     */
    private function doFixColPosDownload($columnValue) {

        $tokenList = '';

        if ($columnValue == '') {
989
990
            throw new SyntaxReportException ("Missing parameter for " . DOWNLOAD_MODE_PDF . '/' . DOWNLOAD_MODE_FILE .
                '/' . DOWNLOAD_MODE_ZIP, ERROR_MISSING_REQUIRED_PARAMETER, null, __FILE__, __LINE__, $this->fr_error);
Carsten  Rose's avatar
Carsten Rose committed
991
992
993
994
995
        }

        // Split definition
        $allParam = explode('|', $columnValue);

996
997
998
        $value = array_shift($allParam);
        if ($value !== null) {
            $tokenList .= $this->composeLinkPart(TOKEN_DOWNLOAD, $value); // -- d:<exportFilename> --
Carsten  Rose's avatar
Carsten Rose committed
999
1000
        }