Bug #78258 » DatabaseRecordList.php

Luis García, 2018-03-26 15:48

 
1
<?php
2
namespace Vendor\Package\Xclass;
3

    
4
use TYPO3\CMS\Backend\RecordList\RecordListGetTableHookInterface;
5
use TYPO3\CMS\Backend\Utility\BackendUtility;
6
use TYPO3\CMS\Core\Imaging\Icon;
7
use TYPO3\CMS\Core\Messaging\FlashMessage;
8
use TYPO3\CMS\Core\Messaging\FlashMessageService;
9
use TYPO3\CMS\Core\Utility\GeneralUtility;
10

    
11
/**
12
 * Xclass for TYPO3\CMS\Recordlist\RecordList\DatabaseRecordList
13
 *
14
 * Makes CSV exports without generating HTML code and accumulating rows to save memory.
15
 */
16
class DatabaseRecordList extends \TYPO3\CMS\Recordlist\RecordList\DatabaseRecordList
17
{
18
    /**
19
     * The output stream to write the CSV lines to.
20
     *
21
     * @var
22
     */
23
    protected $outputStream;
24

    
25
    /**
26
     * Table name (only for CSV export)
27
     *
28
     * @var string
29
     */
30
    protected $exportedTable;
31

    
32
    /**
33
     * Table field (column) where header value is found
34
     *
35
     * @var string
36
     */
37
    protected $titleCol;
38

    
39
    /**
40
     * Creates the listing of records from a single table
41
     *
42
     * @param string $table Table name
43
     * @param int $id Page id
44
     * @param string $rowList List of fields to show in the listing. Pseudo fields will be added including the record header.
45
     * @throws \UnexpectedValueException
46
     * @throws \TYPO3\CMS\Core\Exception
47
     * @return string HTML table with the listing for the record.
48
     */
49
    public function getTable($table, $id, $rowList = '')
50
    {
51
        $rowListArray = GeneralUtility::trimExplode(',', $rowList, true);
52
        // if no columns have been specified, show description (if configured)
53
        if (!empty($GLOBALS['TCA'][$table]['ctrl']['descriptionColumn']) && empty($rowListArray)) {
54
            array_push($rowListArray, $GLOBALS['TCA'][$table]['ctrl']['descriptionColumn']);
55
        }
56
        $backendUser = $this->getBackendUserAuthentication();
57
        $lang = $this->getLanguageService();
58
        $db = $this->getDatabaseConnection();
59
        // Init
60
        $addWhere = '';
61
        $titleCol = $GLOBALS['TCA'][$table]['ctrl']['label'];
62
        $thumbsCol = $GLOBALS['TCA'][$table]['ctrl']['thumbnail'];
63
        $l10nEnabled = $GLOBALS['TCA'][$table]['ctrl']['languageField']
64
            && $GLOBALS['TCA'][$table]['ctrl']['transOrigPointerField']
65
            && !$GLOBALS['TCA'][$table]['ctrl']['transOrigPointerTable'];
66
        $tableCollapsed = (bool)$this->tablesCollapsed[$table];
67
        // prepare space icon
68
        $this->spaceIcon = '<span class="btn btn-default disabled">' . $this->iconFactory->getIcon('empty-empty', Icon::SIZE_SMALL)->render() . '</span>';
69
        // Cleaning rowlist for duplicates and place the $titleCol as the first column always!
70
        $this->fieldArray = [];
71
        // title Column
72
        // Add title column
73
        $this->fieldArray[] = $titleCol;
74
        // Control-Panel
75
        if (!GeneralUtility::inList($rowList, '_CONTROL_')) {
76
            $this->fieldArray[] = '_CONTROL_';
77
        }
78
        // Clipboard
79
        if ($this->showClipboard) {
80
            $this->fieldArray[] = '_CLIPBOARD_';
81
        }
82
        // Ref
83
        if (!$this->dontShowClipControlPanels) {
84
            $this->fieldArray[] = '_REF_';
85
        }
86
        // Path
87
        if ($this->searchLevels) {
88
            $this->fieldArray[] = '_PATH_';
89
        }
90
        // Localization
91
        if ($this->localizationView && $l10nEnabled) {
92
            $this->fieldArray[] = '_LOCALIZATION_';
93
            $this->fieldArray[] = '_LOCALIZATION_b';
94
            // Only restrict to the default language if no search request is in place
95
            if ($this->searchString === '') {
96
                $addWhere .= ' AND (
97
                    ' . $GLOBALS['TCA'][$table]['ctrl']['languageField'] . '<=0
98
                    OR
99
                    ' . $GLOBALS['TCA'][$table]['ctrl']['transOrigPointerField'] . ' = 0
100
                )';
101
            }
102
        }
103
        // Cleaning up:
104
        $this->fieldArray = array_unique(array_merge($this->fieldArray, $rowListArray));
105
        if ($this->noControlPanels) {
106
            $tempArray = array_flip($this->fieldArray);
107
            unset($tempArray['_CONTROL_']);
108
            unset($tempArray['_CLIPBOARD_']);
109
            $this->fieldArray = array_keys($tempArray);
110
        }
111
        // Creating the list of fields to include in the SQL query:
112
        $selectFields = $this->fieldArray;
113
        $selectFields[] = 'uid';
114
        $selectFields[] = 'pid';
115
        // adding column for thumbnails
116
        if ($thumbsCol) {
117
            $selectFields[] = $thumbsCol;
118
        }
119
        if ($table == 'pages') {
120
            $selectFields[] = 'module';
121
            $selectFields[] = 'extendToSubpages';
122
            $selectFields[] = 'nav_hide';
123
            $selectFields[] = 'doktype';
124
            $selectFields[] = 'shortcut';
125
            $selectFields[] = 'shortcut_mode';
126
            $selectFields[] = 'mount_pid';
127
        }
128
        if (is_array($GLOBALS['TCA'][$table]['ctrl']['enablecolumns'])) {
129
            $selectFields = array_merge($selectFields, $GLOBALS['TCA'][$table]['ctrl']['enablecolumns']);
130
        }
131
        foreach (['type', 'typeicon_column', 'editlock'] as $field) {
132
            if ($GLOBALS['TCA'][$table]['ctrl'][$field]) {
133
                $selectFields[] = $GLOBALS['TCA'][$table]['ctrl'][$field];
134
            }
135
        }
136
        if ($GLOBALS['TCA'][$table]['ctrl']['versioningWS']) {
137
            $selectFields[] = 't3ver_id';
138
            $selectFields[] = 't3ver_state';
139
            $selectFields[] = 't3ver_wsid';
140
        }
141
        if ($l10nEnabled) {
142
            $selectFields[] = $GLOBALS['TCA'][$table]['ctrl']['languageField'];
143
            $selectFields[] = $GLOBALS['TCA'][$table]['ctrl']['transOrigPointerField'];
144
        }
145
        if ($GLOBALS['TCA'][$table]['ctrl']['label_alt']) {
146
            $selectFields = array_merge(
147
                $selectFields,
148
                GeneralUtility::trimExplode(',', $GLOBALS['TCA'][$table]['ctrl']['label_alt'], true)
149
            );
150
        }
151
        // Unique list!
152
        $selectFields = array_unique($selectFields);
153
        $fieldListFields = $this->makeFieldList($table, 1);
154
        if (empty($fieldListFields) && $GLOBALS['TYPO3_CONF_VARS']['BE']['debug']) {
155
            $message = sprintf($lang->sL('LLL:EXT:lang/locallang_mod_web_list.xlf:missingTcaColumnsMessage', true), $table, $table);
156
            $messageTitle = $lang->sL('LLL:EXT:lang/locallang_mod_web_list.xlf:missingTcaColumnsMessageTitle', true);
157
            /** @var FlashMessage $flashMessage */
158
            $flashMessage = GeneralUtility::makeInstance(
159
                FlashMessage::class,
160
                $message,
161
                $messageTitle,
162
                FlashMessage::WARNING,
163
                true
164
            );
165
            /** @var $flashMessageService FlashMessageService */
166
            $flashMessageService = GeneralUtility::makeInstance(FlashMessageService::class);
167
            /** @var $defaultFlashMessageQueue \TYPO3\CMS\Core\Messaging\FlashMessageQueue */
168
            $defaultFlashMessageQueue = $flashMessageService->getMessageQueueByIdentifier();
169
            $defaultFlashMessageQueue->enqueue($flashMessage);
170
        }
171
        // Making sure that the fields in the field-list ARE in the field-list from TCA!
172
        $selectFields = array_intersect($selectFields, $fieldListFields);
173
        // Implode it into a list of fields for the SQL-statement.
174
        $selFieldList = implode(',', $selectFields);
175
        $this->selFieldList = $selFieldList;
176
        if (is_array($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['typo3/class.db_list_extra.inc']['getTable'])) {
177
            foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['typo3/class.db_list_extra.inc']['getTable'] as $classData) {
178
                $hookObject = GeneralUtility::getUserObj($classData);
179
                if (!$hookObject instanceof RecordListGetTableHookInterface) {
180
                    throw new \UnexpectedValueException($classData . ' must implement interface ' . RecordListGetTableHookInterface::class, 1195114460);
181
                }
182
                $hookObject->getDBlistQuery($table, $id, $addWhere, $selFieldList, $this);
183
            }
184
        }
185
        // Create the SQL query for selecting the elements in the listing:
186
        // do not do paging when outputting as CSV
187
        if ($this->csvOutput) {
188
            $this->iLimit = 0;
189
        }
190
        if ($this->firstElementNumber > 2 && $this->iLimit > 0) {
191
            // Get the two previous rows for sorting if displaying page > 1
192
            $this->firstElementNumber = $this->firstElementNumber - 2;
193
            $this->iLimit = $this->iLimit + 2;
194
            // (API function from TYPO3\CMS\Recordlist\RecordList\AbstractDatabaseRecordList)
195
            $queryParts = $this->makeQueryArray($table, $id, $addWhere);
196

    
197
            $this->firstElementNumber = $this->firstElementNumber + 2;
198
            $this->iLimit = $this->iLimit - 2;
199
        } else {
200
            // (API function from TYPO3\CMS\Recordlist\RecordList\AbstractDatabaseRecordList)
201
            $queryParts = $this->makeQueryArray($table, $id, $addWhere);
202
        }
203

    
204
        // Finding the total amount of records on the page
205
        // (API function from TYPO3\CMS\Recordlist\RecordList\AbstractDatabaseRecordList)
206
        $this->setTotalItems($queryParts);
207

    
208
        // Init:
209
        $dbCount = 0;
210
        $out = '';
211
        $tableHeader = '';
212
        $result = null;
213
        $listOnlyInSingleTableMode = $this->listOnlyInSingleTableMode && !$this->table;
214
        // If the count query returned any number of records, we perform the real query,
215
        // selecting records.
216
        if ($this->totalItems) {
217
            // Fetch records only if not in single table mode
218
            if ($listOnlyInSingleTableMode) {
219
                $dbCount = $this->totalItems;
220
            } else {
221
                // Set the showLimit to the number of records when outputting as CSV
222
                if ($this->csvOutput) {
223
                    $this->showLimit = $this->totalItems;
224
                    $this->iLimit = $this->totalItems;
225
                }
226
                $result = $db->exec_SELECT_queryArray($queryParts);
227
                $dbCount = $db->sql_num_rows($result);
228
            }
229
        }
230
        // If any records was selected, render the list:
231
        if ($dbCount) {
232
            $tableTitle = $lang->sL($GLOBALS['TCA'][$table]['ctrl']['title'], true);
233
            if ($tableTitle === '') {
234
                $tableTitle = $table;
235
            }
236
            // Header line is drawn
237
            $theData = [];
238
            if ($this->disableSingleTableView) {
239
                $theData[$titleCol] = '<span class="c-table">' . BackendUtility::wrapInHelp($table, '', $tableTitle)
240
                    . '</span> (<span class="t3js-table-total-items">' . $this->totalItems . '</span>)';
241
            } else {
242
                $icon = $this->table
243
                    ? '<span title="' . $lang->getLL('contractView', true) . '">' . $this->iconFactory->getIcon('actions-view-table-collapse', Icon::SIZE_SMALL)->render() . '</span>'
244
                    : '<span title="' . $lang->getLL('expandView', true) . '">' . $this->iconFactory->getIcon('actions-view-table-expand', Icon::SIZE_SMALL)->render() . '</span>';
245
                $theData[$titleCol] = $this->linkWrapTable($table, $tableTitle . ' (<span class="t3js-table-total-items">' . $this->totalItems . '</span>) ' . $icon);
246
            }
247
            if ($listOnlyInSingleTableMode) {
248
                $tableHeader .= BackendUtility::wrapInHelp($table, '', $theData[$titleCol]);
249
            } else {
250
                // Render collapse button if in multi table mode
251
                $collapseIcon = '';
252
                if (!$this->table) {
253
                    $href = htmlspecialchars(($this->listURL() . '&collapse[' . $table . ']=' . ($tableCollapsed ? '0' : '1')));
254
                    $title = $tableCollapsed
255
                        ? $lang->sL('LLL:EXT:lang/locallang_core.xlf:labels.expandTable', true)
256
                        : $lang->sL('LLL:EXT:lang/locallang_core.xlf:labels.collapseTable', true);
257
                    $icon = '<span class="collapseIcon">' . $this->iconFactory->getIcon(($tableCollapsed ? 'actions-view-list-expand' : 'actions-view-list-collapse'), Icon::SIZE_SMALL)->render() . '</span>';
258
                    $collapseIcon = '<a href="' . $href . '" title="' . $title . '" class="pull-right t3js-toggle-recordlist" data-table="' . htmlspecialchars($table) . '" data-toggle="collapse" data-target="#recordlist-' . htmlspecialchars($table) . '">' . $icon . '</a>';
259
                }
260
                $tableHeader .= $theData[$titleCol] . $collapseIcon;
261
            }
262
            // Render table rows only if in multi table view or if in single table view
263
            $rowOutput = '';
264
            if (!$listOnlyInSingleTableMode || $this->table) {
265
                // Fixing an order table for sortby tables
266
                $this->currentTable = [];
267
                $currentIdList = [];
268
                $doSort = $GLOBALS['TCA'][$table]['ctrl']['sortby'] && !$this->sortField;
269
                $prevUid = 0;
270
                $prevPrevUid = 0;
271
                // Get first two rows and initialize prevPrevUid and prevUid if on page > 1
272
                if ($this->firstElementNumber > 2 && $this->iLimit > 0) {
273
                    $row = $db->sql_fetch_assoc($result);
274
                    $prevPrevUid = -((int)$row['uid']);
275
                    $row = $db->sql_fetch_assoc($result);
276
                    $prevUid = $row['uid'];
277
                }
278

    
279
                // XCLASS: Handle CSV output without generating HTML code or accumulating rows.
280
                if ($this->csvOutput) {
281
                    $this->outputCsvFile($table, $result);
282
                }
283

    
284
                $accRows = [];
285
                // Accumulate rows here
286
                while ($row = $db->sql_fetch_assoc($result)) {
287
                    if (!$this->isRowListingConditionFulfilled($table, $row)) {
288
                        continue;
289
                    }
290
                    // In offline workspace, look for alternative record:
291
                    BackendUtility::workspaceOL($table, $row, $backendUser->workspace, true);
292
                    if (is_array($row)) {
293
                        $accRows[] = $row;
294
                        $currentIdList[] = $row['uid'];
295
                        if ($doSort) {
296
                            if ($prevUid) {
297
                                $this->currentTable['prev'][$row['uid']] = $prevPrevUid;
298
                                $this->currentTable['next'][$prevUid] = '-' . $row['uid'];
299
                                $this->currentTable['prevUid'][$row['uid']] = $prevUid;
300
                            }
301
                            $prevPrevUid = isset($this->currentTable['prev'][$row['uid']]) ? -$prevUid : $row['pid'];
302
                            $prevUid = $row['uid'];
303
                        }
304
                    }
305
                }
306
                $db->sql_free_result($result);
307
                $this->totalRowCount = count($accRows);
308
                // CSV initiated
309
                if ($this->csvOutput) {
310
                    $this->initCSV();
311
                }
312
                // Render items:
313
                $this->CBnames = [];
314
                $this->duplicateStack = [];
315
                $this->eCounter = $this->firstElementNumber;
316
                $cc = 0;
317
                foreach ($accRows as $row) {
318
                    // Render item row if counter < limit
319
                    if ($cc < $this->iLimit) {
320
                        $cc++;
321
                        $this->translations = false;
322
                        $rowOutput .= $this->renderListRow($table, $row, $cc, $titleCol, $thumbsCol);
323
                        // If localization view is enabled and no search happened it means that the selected
324
                        // records are either default or All language and here we will not select translations
325
                        // which point to the main record:
326
                        if ($this->localizationView && $l10nEnabled && $this->searchString === '') {
327
                            // For each available translation, render the record:
328
                            if (is_array($this->translations)) {
329
                                foreach ($this->translations as $lRow) {
330
                                    // $lRow isn't always what we want - if record was moved we've to work with the
331
                                    // placeholder records otherwise the list is messed up a bit
332
                                    if ($row['_MOVE_PLH_uid'] && $row['_MOVE_PLH_pid']) {
333
                                        $where = 't3ver_move_id="' . (int)$lRow['uid'] . '" AND pid="' . $row['_MOVE_PLH_pid']
334
                                            . '" AND t3ver_wsid=' . $row['t3ver_wsid'] . BackendUtility::deleteClause($table);
335
                                        $tmpRow = BackendUtility::getRecordRaw($table, $where, $selFieldList);
336
                                        $lRow = is_array($tmpRow) ? $tmpRow : $lRow;
337
                                    }
338
                                    // In offline workspace, look for alternative record:
339
                                    BackendUtility::workspaceOL($table, $lRow, $backendUser->workspace, true);
340
                                    if (is_array($lRow) && $backendUser->checkLanguageAccess($lRow[$GLOBALS['TCA'][$table]['ctrl']['languageField']])) {
341
                                        $currentIdList[] = $lRow['uid'];
342
                                        $rowOutput .= $this->renderListRow($table, $lRow, $cc, $titleCol, $thumbsCol, 18);
343
                                    }
344
                                }
345
                            }
346
                        }
347
                    }
348
                    // Counter of total rows incremented:
349
                    $this->eCounter++;
350
                }
351
                // Record navigation is added to the beginning and end of the table if in single
352
                // table mode
353
                if ($this->table) {
354
                    $rowOutput = $this->renderListNavigation('top') . $rowOutput . $this->renderListNavigation('bottom');
355
                } else {
356
                    // Show that there are more records than shown
357
                    if ($this->totalItems > $this->itemsLimitPerTable) {
358
                        $countOnFirstPage = $this->totalItems > $this->itemsLimitSingleTable ? $this->itemsLimitSingleTable : $this->totalItems;
359
                        $hasMore = $this->totalItems > $this->itemsLimitSingleTable;
360
                        $colspan = $this->showIcon ? count($this->fieldArray) + 1 : count($this->fieldArray);
361
                        $rowOutput .= '<tr><td colspan="' . $colspan . '">
362
								<a href="' . htmlspecialchars(($this->listURL() . '&table=' . rawurlencode($table))) . '" class="btn btn-default">'
363
                            . '<span class="t3-icon fa fa-chevron-down"></span> <i>[1 - ' . $countOnFirstPage . ($hasMore ? '+' : '') . ']</i></a>
364
								</td></tr>';
365
                    }
366
                }
367
                // The header row for the table is now created:
368
                $out .= $this->renderListHeader($table, $currentIdList);
369
            }
370

    
371
            $collapseClass = $tableCollapsed && !$this->table ? 'collapse' : 'collapse in';
372
            $dataState = $tableCollapsed && !$this->table ? 'collapsed' : 'expanded';
373

    
374
            // The list of records is added after the header:
375
            $out .= $rowOutput;
376
            // ... and it is all wrapped in a table:
377
            $out = '
378

    
379

    
380

    
381
			<!--
382
				DB listing of elements:	"' . htmlspecialchars($table) . '"
383
			-->
384
				<div class="panel panel-space panel-default recordlist">
385
					<div class="panel-heading">
386
					' . $tableHeader . '
387
					</div>
388
					<div class="' . $collapseClass . '" data-state="' . $dataState . '" id="recordlist-' . htmlspecialchars($table) . '">
389
						<div class="table-fit">
390
							<table data-table="' . htmlspecialchars($table) . '" class="table table-striped table-hover' . ($listOnlyInSingleTableMode ? ' typo3-dblist-overview' : '') . '">
391
								' . $out . '
392
							</table>
393
						</div>
394
					</div>
395
				</div>
396
			';
397
            // Output csv if...
398
            // This ends the page with exit.
399
            if ($this->csvOutput) {
400
                $this->outputCSV($table);
401
            }
402
        }
403
        // Return content:
404
        return $out;
405
    }
406

    
407
    /**
408
     * Initializes internal csvLines array with the header of field names
409
     *
410
     * @param string $prefix Filename prefix (table name)
411
     * @return void
412
     */
413
    protected function initCsvOutput($prefix)
414
    {
415
        // Setting filename:
416
        $filename = $prefix . '_' . date('dmy-Hi') . '.csv';
417

    
418
        // Deactivate buffered output! (For huge files)
419
        // ob_end_clean();
420

    
421
        // Prepare output stream
422
        $this->outputStream = fopen('php://output', 'w');
423

    
424
        // Creating output header:
425
        header('Content-Type: application/octet-stream');
426
        header('Content-Disposition: attachment; filename="' . $filename . '"');
427

    
428
        // Headers to show CSV content in browser
429
        // header('Content-Type: text/plain');
430
        // header('Content-Disposition: inline; filename="' . $filename . '"');
431

    
432
        // Cache-Control header is needed here to solve an issue with browser IE and
433
        // versions lower than 9. See for more information: http://support.microsoft.com/kb/323308
434
        header("Cache-Control: ''");
435

    
436
        $this->addHeaderRowToCSV();
437
    }
438

    
439
    /**
440
     * Adds input row of values to the internal csvLines array as a CSV formatted line
441
     *
442
     * @param array $csvRow Array with values to be listed.
443
     * @return void
444
     */
445
    public function setCsvRow($csvRow)
446
    {
447
        if ($this->outputStream !== false) {
448
            fputcsv($this->outputStream, $csvRow);
449
        }
450
    }
451

    
452
    /**
453
     * Output records of a single table to a downloadable CSV file before the rows get fetched to be displayed
454
     * by the List module.
455
     *
456
     * @param string $table The table to be exported.
457
     * @param bool|\mysqli_result|object $result The DB handle to read the records.
458
     */
459
    protected function outputCsvFile($table, $result)
460
    {
461
        $backendUser = $this->getBackendUserAuthentication();
462
        $db = $this->getDatabaseConnection();
463

    
464
        $this->exportedTable = $table;
465
        $this->titleCol = $GLOBALS['TCA'][$table]['ctrl']['label'];
466
        $l10nEnabled = $GLOBALS['TCA'][$table]['ctrl']['languageField']
467
            && $GLOBALS['TCA'][$table]['ctrl']['transOrigPointerField']
468
            && !$GLOBALS['TCA'][$table]['ctrl']['transOrigPointerTable'];
469

    
470
        $this->initCsvOutput($table);
471

    
472
        $cc = 0;
473

    
474
        // Accumulate rows here
475
        while ($row = $db->sql_fetch_assoc($result)) {
476
            if (!$this->isRowListingConditionFulfilled($table, $row)) {
477
                continue;
478
            }
479

    
480
            // In offline workspace, look for alternative record:
481
            BackendUtility::workspaceOL($table, $row, $backendUser->workspace, true);
482
            if (is_array($row)) {
483
                // Render item row if counter < limit
484
                if ($cc < $this->iLimit) {
485
                    $cc++;
486
                    $this->translations = false;
487

    
488
                    $this->renderListRowForCsv($row);
489

    
490
                    // If localization view is enabled and no search happened it means that the selected
491
                    // records are either default or All language and here we will not select translations
492
                    // which point to the main record:
493
                    if ($this->localizationView && $l10nEnabled && $this->searchString === '') {
494
                        // For each available translation, render the record:
495
                        if (is_array($this->translations)) {
496
                            foreach ($this->translations as $lRow) {
497
                                // $lRow isn't always what we want - if record was moved we've to work with the
498
                                // placeholder records otherwise the list is messed up a bit
499
                                if ($row['_MOVE_PLH_uid'] && $row['_MOVE_PLH_pid']) {
500
                                    $where = 't3ver_move_id="' . (int)$lRow['uid'] . '" AND pid="' . $row['_MOVE_PLH_pid']
501
                                        . '" AND t3ver_wsid=' . $row['t3ver_wsid'] . BackendUtility::deleteClause($table);
502
                                    $tmpRow = BackendUtility::getRecordRaw($table, $where, $this->selFieldList);
503
                                    $lRow = is_array($tmpRow) ? $tmpRow : $lRow;
504
                                }
505

    
506
                                // In offline workspace, look for alternative record:
507
                                BackendUtility::workspaceOL($table, $lRow, $backendUser->workspace, true);
508
                                if (is_array($lRow) && $backendUser->checkLanguageAccess($lRow[$GLOBALS['TCA'][$table]['ctrl']['languageField']])) {
509
                                    $this->renderListRowForCsv($lRow);
510
                                }
511
                            }
512
                        }
513
                    }
514
                }
515
                // Counter of total rows incremented:
516
                $this->eCounter++;
517
            }
518
        }
519

    
520
        $db->sql_free_result($result);
521

    
522
        fclose($this->outputStream);
523

    
524
        exit();
525
    }
526

    
527
    /**
528
     * Render a single row for CSV
529
     *
530
     * @param mixed[] $row Current record
531
     * @return void
532
     */
533
    protected function renderListRowForCsv($row)
534
    {
535
        if (!is_array($row)) {
536
            return;
537
        }
538

    
539
        $id_orig = null;
540

    
541
        // If in search mode, make sure the preview will show the correct page
542
        if ((string)$this->searchString !== '') {
543
            $id_orig = $this->id;
544
            $this->id = $row['pid'];
545
        }
546

    
547
        // Incr. counter.
548
        $this->counter++;
549

    
550
        foreach ($this->fieldArray as $fCol) {
551
            if ($fCol != $this->titleCol
552
                && $fCol != 'pid'
553
                && $fCol != '_PATH_'
554
                && $fCol != '_REF_'
555
                && $fCol != '_CONTROL_'
556
                && $fCol != '_CLIPBOARD_'
557
                && $fCol != '_LOCALIZATION_'
558
                && $fCol != '_LOCALIZATION_b'
559
            ) {
560
                $row[$fCol] = BackendUtility::getProcessedValueExtra($this->exportedTable, $fCol, $row[$fCol], 0, $row['uid']);
561
            }
562
        }
563

    
564
        // Reset the ID if it was overwritten
565
        if ((string)$this->searchString !== '') {
566
            $this->id = $id_orig;
567
        }
568

    
569
        // Add row to CSV list:
570
        $this->addToCSV($row);
571
    }
572
}
    (1-1/1)