[TASK] Re-work/simplify copyright header in PHP files - Part 2
[Packages/TYPO3.CMS.git] / typo3 / sysext / recordlist / Classes / RecordList / DatabaseRecordList.php
1 <?php
2 namespace TYPO3\CMS\Recordlist\RecordList;
3
4 /**
5 * This file is part of the TYPO3 CMS project.
6 *
7 * It is free software; you can redistribute it and/or modify it under
8 * the terms of the GNU General Public License, either version 2
9 * of the License, or any later version.
10 *
11 * For the full copyright and license information, please read the
12 * LICENSE.txt file that was distributed with this source code.
13 *
14 * The TYPO3 project - inspiring people to share!
15 */
16
17 use TYPO3\CMS\Core\Utility\ExtensionManagementUtility;
18 use TYPO3\CMS\Core\Utility\GeneralUtility;
19 use TYPO3\CMS\Backend\Utility\BackendUtility;
20 use TYPO3\CMS\Backend\Utility\IconUtility;
21
22 /**
23 * Class for rendering of Web>List module
24 *
25 * @author Kasper Skårhøj <kasperYYYY@typo3.com>
26 */
27 class DatabaseRecordList extends \TYPO3\CMS\Recordlist\RecordList\AbstractDatabaseRecordList {
28
29 // External:
30 // If TRUE, table rows in the list will alternate in background colors (and have background colors at all!)
31 /**
32 * @todo Define visibility
33 */
34 public $alternateBgColors = FALSE;
35
36 // Used to indicate which tables (values in the array) that can have a create-new-record link. If the array is empty, all tables are allowed.
37 /**
38 * @todo Define visibility
39 */
40 public $allowedNewTables = array();
41
42 // Used to indicate which tables (values in the array) that cannot have a create-new-record link. If the array is empty, all tables are allowed.
43 /**
44 * @todo Define visibility
45 */
46 public $deniedNewTables = array();
47
48 // If TRUE, the control panel will contain links to the create-new wizards for pages and tt_content elements (normally, the link goes to just creating a new element without the wizards!).
49 /**
50 * @todo Define visibility
51 */
52 public $newWizards = FALSE;
53
54 // If TRUE, will disable the rendering of clipboard + control panels.
55 /**
56 * @todo Define visibility
57 */
58 public $dontShowClipControlPanels = FALSE;
59
60 // If TRUE, will show the clipboard in the field list.
61 /**
62 * @todo Define visibility
63 */
64 public $showClipboard = FALSE;
65
66 // If TRUE, will DISABLE all control panels in lists. (Takes precedence)
67 /**
68 * @todo Define visibility
69 */
70 public $noControlPanels = FALSE;
71
72 // If TRUE, clickmenus will be rendered
73 /**
74 * @todo Define visibility
75 */
76 public $clickMenuEnabled = TRUE;
77
78 // Count of record rows in view
79 /**
80 * @todo Define visibility
81 */
82 public $totalRowCount;
83
84 // Space icon used for alignment
85 /**
86 * @todo Define visibility
87 */
88 public $spaceIcon;
89
90 // Internal:
91 // Set to the page record (see writeTop())
92 /**
93 * @todo Define visibility
94 */
95 public $pageRow = array();
96
97 // Used to accumulate CSV lines for CSV export.
98 protected $csvLines = array();
99
100 // If set, the listing is returned as CSV instead.
101 /**
102 * @todo Define visibility
103 */
104 public $csvOutput = FALSE;
105
106 /**
107 * Clipboard object
108 *
109 * @var \TYPO3\CMS\Backend\Clipboard\Clipboard
110 * @todo Define visibility
111 */
112 public $clipObj;
113
114 // Tracking names of elements (for clipboard use)
115 /**
116 * @todo Define visibility
117 */
118 public $CBnames = array();
119
120 // Used to track which elements has duplicates and how many
121 /**
122 * @todo Define visibility
123 */
124 public $duplicateStack = array();
125
126 /**
127 * [$tablename][$uid] = number of references to this record
128 *
129 * @var array
130 */
131 protected $referenceCount = array();
132
133 // Translations of the current record
134 /**
135 * @todo Define visibility
136 */
137 public $translations;
138
139 // select fields for the query which fetches the translations of the current record
140 /**
141 * @todo Define visibility
142 */
143 public $selFieldList;
144
145 public $disableSingleTableView = FALSE;
146
147 /**
148 * Create the panel of buttons for submitting the form or otherwise perform operations.
149 *
150 * @return array All available buttons as an assoc. array
151 */
152 public function getButtons() {
153 $buttons = array(
154 'csh' => '',
155 'view' => '',
156 'edit' => '',
157 'hide_unhide' => '',
158 'move' => '',
159 'new_record' => '',
160 'paste' => '',
161 'level_up' => '',
162 'cache' => '',
163 'reload' => '',
164 'shortcut' => '',
165 'back' => '',
166 'csv' => '',
167 'export' => ''
168 );
169 // Get users permissions for this page record:
170 $localCalcPerms = $GLOBALS['BE_USER']->calcPerms($this->pageRow);
171 // CSH
172 if (!strlen($this->id)) {
173 $buttons['csh'] = BackendUtility::cshItem('xMOD_csh_corebe', 'list_module_noId', $GLOBALS['BACK_PATH'], '', TRUE);
174 } elseif (!$this->id) {
175 $buttons['csh'] = BackendUtility::cshItem('xMOD_csh_corebe', 'list_module_root', $GLOBALS['BACK_PATH'], '', TRUE);
176 } else {
177 $buttons['csh'] = BackendUtility::cshItem('xMOD_csh_corebe', 'list_module', $GLOBALS['BACK_PATH'], '', TRUE);
178 }
179 if (isset($this->id)) {
180 // View Exclude doktypes 254,255 Configuration: mod.web_list.noViewWithDokTypes = 254,255
181 if (isset($GLOBALS['SOBE']->modTSconfig['properties']['noViewWithDokTypes'])) {
182 $noViewDokTypes = GeneralUtility::trimExplode(',', $GLOBALS['SOBE']->modTSconfig['properties']['noViewWithDokTypes'], TRUE);
183 } else {
184 //default exclusion: doktype 254 (folder), 255 (recycler)
185 $noViewDokTypes = array(\TYPO3\CMS\Frontend\Page\PageRepository::DOKTYPE_SYSFOLDER, \TYPO3\CMS\Frontend\Page\PageRepository::DOKTYPE_RECYCLER);
186 }
187 if (!in_array($this->pageRow['doktype'], $noViewDokTypes)) {
188 $buttons['view'] = '<a href="#" onclick="' . htmlspecialchars(BackendUtility::viewOnClick($this->id, $this->backPath, BackendUtility::BEgetRootLine($this->id))) . '" title="' . $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_core.xlf:labels.showPage', TRUE) . '">' . IconUtility::getSpriteIcon('actions-document-view') . '</a>';
189 }
190 // New record
191 if (!$GLOBALS['SOBE']->modTSconfig['properties']['noCreateRecordsLink']) {
192 $buttons['new_record'] = '<a href="#" onclick="' . htmlspecialchars(('return jumpExt(\'' . $this->backPath . 'db_new.php?id=' . $this->id . '\');')) . '" title="' . $GLOBALS['LANG']->getLL('newRecordGeneral', TRUE) . '">' . IconUtility::getSpriteIcon('actions-document-new') . '</a>';
193 }
194 // If edit permissions are set, see \TYPO3\CMS\Core\Authentication\BackendUserAuthentication
195 if ($localCalcPerms & 2 && !empty($this->id)) {
196 // Edit
197 $params = '&edit[pages][' . $this->pageRow['uid'] . ']=edit';
198 $buttons['edit'] = '<a href="#" onclick="' . htmlspecialchars(BackendUtility::editOnClick($params, $this->backPath, -1)) . '" title="' . $GLOBALS['LANG']->getLL('editPage', TRUE) . '">' . IconUtility::getSpriteIcon('actions-page-open') . '</a>';
199 }
200 // Paste
201 if ($localCalcPerms & 8 || $localCalcPerms & 16) {
202 $elFromTable = $this->clipObj->elFromTable('');
203 if (count($elFromTable)) {
204 $buttons['paste'] = '<a href="' . htmlspecialchars($this->clipObj->pasteUrl('', $this->id)) . '" onclick="' . htmlspecialchars(('return ' . $this->clipObj->confirmMsg('pages', $this->pageRow, 'into', $elFromTable))) . '" title="' . $GLOBALS['LANG']->getLL('clip_paste', TRUE) . '">' . IconUtility::getSpriteIcon('actions-document-paste-after') . '</a>';
205 }
206 }
207 // Cache
208 $buttons['cache'] = '<a href="' . htmlspecialchars(($this->listURL() . '&clear_cache=1')) . '" title="' . $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_core.xlf:labels.clear_cache', TRUE) . '">' . IconUtility::getSpriteIcon('actions-system-cache-clear') . '</a>';
209 if ($this->table && (!isset($GLOBALS['SOBE']->modTSconfig['properties']['noExportRecordsLinks'])
210 || (isset($GLOBALS['SOBE']->modTSconfig['properties']['noExportRecordsLinks']) && !$GLOBALS['SOBE']->modTSconfig['properties']['noExportRecordsLinks']))
211 ) {
212 // CSV
213 $buttons['csv'] = '<a href="' . htmlspecialchars(($this->listURL() . '&csv=1')) . '" title="' . $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_core.xlf:labels.csv', TRUE) . '">' . IconUtility::getSpriteIcon('mimetypes-text-csv') . '</a>';
214 // Export
215 if (ExtensionManagementUtility::isLoaded('impexp')) {
216 $url = BackendUtility::getModuleUrl('xMOD_tximpexp', array('tx_impexp[action]' => 'export'));
217 $buttons['export'] = '<a href="' . htmlspecialchars(($url . '&tx_impexp[list][]=' . rawurlencode(($this->table . ':' . $this->id)))) . '" title="' . $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_core.xlf:rm.export', TRUE) . '">' . IconUtility::getSpriteIcon('actions-document-export-t3d') . '</a>';
218 }
219 }
220 // Reload
221 $buttons['reload'] = '<a href="' . htmlspecialchars($this->listURL()) . '" title="' . $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_core.xlf:labels.reload', TRUE) . '">' . IconUtility::getSpriteIcon('actions-system-refresh') . '</a>';
222 // Shortcut
223 if ($GLOBALS['BE_USER']->mayMakeShortcut()) {
224 $buttons['shortcut'] = $GLOBALS['TBE_TEMPLATE']->makeShortcutIcon('id, imagemode, pointer, table, search_field, search_levels, showLimit, sortField, sortRev', implode(',', array_keys($this->MOD_MENU)), 'web_list');
225 }
226 // Back
227 if ($this->returnUrl) {
228 $buttons['back'] = '<a href="' . htmlspecialchars(GeneralUtility::linkThisUrl($this->returnUrl, array('id' => $this->id))) . '" class="typo3-goBack" title="' . $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_core.xlf:labels.goBack', TRUE) . '">' . IconUtility::getSpriteIcon('actions-view-go-back') . '</a>';
229 }
230 }
231 return $buttons;
232 }
233
234 /**
235 * Creates the listing of records from a single table
236 *
237 * @param string $table Table name
238 * @param integer $id Page id
239 * @param string $rowlist List of fields to show in the listing. Pseudo fields will be added including the record header.
240 * @return string HTML table with the listing for the record.
241 * @todo Define visibility
242 */
243 public function getTable($table, $id, $rowlist) {
244 // Init
245 $addWhere = '';
246 $titleCol = $GLOBALS['TCA'][$table]['ctrl']['label'];
247 $thumbsCol = $GLOBALS['TCA'][$table]['ctrl']['thumbnail'];
248 $l10nEnabled = $GLOBALS['TCA'][$table]['ctrl']['languageField'] && $GLOBALS['TCA'][$table]['ctrl']['transOrigPointerField'] && !$GLOBALS['TCA'][$table]['ctrl']['transOrigPointerTable'];
249 $tableCollapsed = (boolean)$this->tablesCollapsed[$table];
250 // prepare space icon
251 $this->spaceIcon = IconUtility::getSpriteIcon('empty-empty', array('style' => 'background-position: 0 10px;'));
252 // Cleaning rowlist for duplicates and place the $titleCol as the first column always!
253 $this->fieldArray = array();
254 // title Column
255 // Add title column
256 $this->fieldArray[] = $titleCol;
257 // Control-Panel
258 if (!GeneralUtility::inList($rowlist, '_CONTROL_')) {
259 $this->fieldArray[] = '_CONTROL_';
260 $this->fieldArray[] = '_AFTERCONTROL_';
261 }
262 // Clipboard
263 if ($this->showClipboard) {
264 $this->fieldArray[] = '_CLIPBOARD_';
265 }
266 // Ref
267 if (!$this->dontShowClipControlPanels) {
268 $this->fieldArray[] = '_REF_';
269 $this->fieldArray[] = '_AFTERREF_';
270 }
271 // Path
272 if ($this->searchLevels) {
273 $this->fieldArray[] = '_PATH_';
274 }
275 // Localization
276 if ($this->localizationView && $l10nEnabled) {
277 $this->fieldArray[] = '_LOCALIZATION_';
278 $this->fieldArray[] = '_LOCALIZATION_b';
279 $addWhere .= ' AND (
280 ' . $GLOBALS['TCA'][$table]['ctrl']['languageField'] . '<=0
281 OR
282 ' . $GLOBALS['TCA'][$table]['ctrl']['transOrigPointerField'] . ' = 0
283 )';
284 }
285 // Cleaning up:
286 $this->fieldArray = array_unique(array_merge($this->fieldArray, GeneralUtility::trimExplode(',', $rowlist, TRUE)));
287 if ($this->noControlPanels) {
288 $tempArray = array_flip($this->fieldArray);
289 unset($tempArray['_CONTROL_']);
290 unset($tempArray['_CLIPBOARD_']);
291 $this->fieldArray = array_keys($tempArray);
292 }
293 // Creating the list of fields to include in the SQL query:
294 $selectFields = $this->fieldArray;
295 $selectFields[] = 'uid';
296 $selectFields[] = 'pid';
297 // adding column for thumbnails
298 if ($thumbsCol) {
299 $selectFields[] = $thumbsCol;
300 }
301 if ($table == 'pages') {
302 $selectFields[] = 'module';
303 $selectFields[] = 'extendToSubpages';
304 $selectFields[] = 'nav_hide';
305 $selectFields[] = 'doktype';
306 }
307 if (is_array($GLOBALS['TCA'][$table]['ctrl']['enablecolumns'])) {
308 $selectFields = array_merge($selectFields, $GLOBALS['TCA'][$table]['ctrl']['enablecolumns']);
309 }
310 if ($GLOBALS['TCA'][$table]['ctrl']['type']) {
311 $selectFields[] = $GLOBALS['TCA'][$table]['ctrl']['type'];
312 }
313 if ($GLOBALS['TCA'][$table]['ctrl']['typeicon_column']) {
314 $selectFields[] = $GLOBALS['TCA'][$table]['ctrl']['typeicon_column'];
315 }
316 if ($GLOBALS['TCA'][$table]['ctrl']['versioningWS']) {
317 $selectFields[] = 't3ver_id';
318 $selectFields[] = 't3ver_state';
319 $selectFields[] = 't3ver_wsid';
320 }
321 if ($l10nEnabled) {
322 $selectFields[] = $GLOBALS['TCA'][$table]['ctrl']['languageField'];
323 $selectFields[] = $GLOBALS['TCA'][$table]['ctrl']['transOrigPointerField'];
324 }
325 if ($GLOBALS['TCA'][$table]['ctrl']['label_alt']) {
326 $selectFields = array_merge($selectFields, GeneralUtility::trimExplode(',', $GLOBALS['TCA'][$table]['ctrl']['label_alt'], TRUE));
327 }
328 // Unique list!
329 $selectFields = array_unique($selectFields);
330 $fieldListFields = $this->makeFieldList($table, 1);
331 if (empty($fieldListFields) && $GLOBALS['TYPO3_CONF_VARS']['BE']['debug']) {
332 $message = sprintf($GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_mod_web_list.xlf:missingTcaColumnsMessage', TRUE), $table, $table);
333 $messageTitle = $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_mod_web_list.xlf:missingTcaColumnsMessageTitle', TRUE);
334 $flashMessage = GeneralUtility::makeInstance('TYPO3\\CMS\\Core\\Messaging\\FlashMessage', $message, $messageTitle, \TYPO3\CMS\Core\Messaging\FlashMessage::WARNING, TRUE);
335 /** @var $flashMessageService \TYPO3\CMS\Core\Messaging\FlashMessageService */
336 $flashMessageService = GeneralUtility::makeInstance('TYPO3\\CMS\\Core\\Messaging\\FlashMessageService');
337 /** @var $defaultFlashMessageQueue \TYPO3\CMS\Core\Messaging\FlashMessageQueue */
338 $defaultFlashMessageQueue = $flashMessageService->getMessageQueueByIdentifier();
339 $defaultFlashMessageQueue->enqueue($flashMessage);
340 }
341 // Making sure that the fields in the field-list ARE in the field-list from TCA!
342 $selectFields = array_intersect($selectFields, $fieldListFields);
343 // Implode it into a list of fields for the SQL-statement.
344 $selFieldList = implode(',', $selectFields);
345 $this->selFieldList = $selFieldList;
346 /**
347 * @hook DB-List getTable
348 * @date 2007-11-16
349 * @request Malte Jansen <mail@maltejansen.de>
350 */
351 if (is_array($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['typo3/class.db_list_extra.inc']['getTable'])) {
352 foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['typo3/class.db_list_extra.inc']['getTable'] as $classData) {
353 $hookObject = GeneralUtility::getUserObj($classData);
354 if (!$hookObject instanceof \TYPO3\CMS\Backend\RecordList\RecordListGetTableHookInterface) {
355 throw new \UnexpectedValueException('$hookObject must implement interface TYPO3\\CMS\\Backend\\RecordList\\RecordListGetTableHookInterface', 1195114460);
356 }
357 $hookObject->getDBlistQuery($table, $id, $addWhere, $selFieldList, $this);
358 }
359 }
360 // Create the SQL query for selecting the elements in the listing:
361 // do not do paging when outputting as CSV
362 if ($this->csvOutput) {
363 $this->iLimit = 0;
364 }
365 if ($this->firstElementNumber > 2 && $this->iLimit > 0) {
366 // Get the two previous rows for sorting if displaying page > 1
367 $this->firstElementNumber = $this->firstElementNumber - 2;
368 $this->iLimit = $this->iLimit + 2;
369 // (API function from TYPO3\CMS\Recordlist\RecordList\AbstractDatabaseRecordList)
370 $queryParts = $this->makeQueryArray($table, $id, $addWhere, $selFieldList);
371 $this->firstElementNumber = $this->firstElementNumber + 2;
372 $this->iLimit = $this->iLimit - 2;
373 } else {
374 // (API function from TYPO3\CMS\Recordlist\RecordList\AbstractDatabaseRecordList)
375 $queryParts = $this->makeQueryArray($table, $id, $addWhere, $selFieldList);
376 }
377
378 // Finding the total amount of records on the page
379 // (API function from TYPO3\CMS\Recordlist\RecordList\AbstractDatabaseRecordList)
380 $this->setTotalItems($queryParts);
381
382 // Init:
383 $dbCount = 0;
384 $out = '';
385 $listOnlyInSingleTableMode = $this->listOnlyInSingleTableMode && !$this->table;
386 // If the count query returned any number of records, we perform the real query, selecting records.
387 if ($this->totalItems) {
388 // Fetch records only if not in single table mode or if in multi table mode and not collapsed
389 if ($listOnlyInSingleTableMode || !$this->table && $tableCollapsed) {
390 $dbCount = $this->totalItems;
391 } else {
392 // Set the showLimit to the number of records when outputting as CSV
393 if ($this->csvOutput) {
394 $this->showLimit = $this->totalItems;
395 $this->iLimit = $this->totalItems;
396 }
397 $result = $GLOBALS['TYPO3_DB']->exec_SELECT_queryArray($queryParts);
398 $dbCount = $GLOBALS['TYPO3_DB']->sql_num_rows($result);
399 }
400 }
401 // If any records was selected, render the list:
402 if ($dbCount) {
403 // Half line is drawn between tables:
404 if (!$listOnlyInSingleTableMode) {
405 $theData = array();
406 if (!$this->table && !$rowlist) {
407 $theData[$titleCol] = '<img src="clear.gif" width="' . ($GLOBALS['SOBE']->MOD_SETTINGS['bigControlPanel'] ? '230' : '350') . '" height="1" alt="" />';
408 if (in_array('_CONTROL_', $this->fieldArray)) {
409 $theData['_CONTROL_'] = '';
410 }
411 if (in_array('_CLIPBOARD_', $this->fieldArray)) {
412 $theData['_CLIPBOARD_'] = '';
413 }
414 }
415 $out .= $this->addelement(0, '', $theData, 'class="c-table-row-spacer"', $this->leftMargin);
416 }
417 $tableTitle = $GLOBALS['LANG']->sL($GLOBALS['TCA'][$table]['ctrl']['title'], TRUE);
418 if ($tableTitle === '') {
419 $tableTitle = $table;
420 }
421 // Header line is drawn
422 $theData = array();
423 if ($this->disableSingleTableView) {
424 $theData[$titleCol] = '<span class="c-table">' . BackendUtility::wrapInHelp($table, '', $tableTitle) . '</span> (' . $this->totalItems . ')';
425 } else {
426 $theData[$titleCol] = $this->linkWrapTable($table, '<span class="c-table">' . $tableTitle . '</span> (' . $this->totalItems . ') ' . ($this->table ? IconUtility::getSpriteIcon('actions-view-table-collapse', array('title' => $GLOBALS['LANG']->getLL('contractView', TRUE))) : IconUtility::getSpriteIcon('actions-view-table-expand', array('title' => $GLOBALS['LANG']->getLL('expandView', TRUE)))));
427 }
428 if ($listOnlyInSingleTableMode) {
429 $out .= '
430 <tr>
431 <td class="t3-row-header" style="width:95%;">' . BackendUtility::wrapInHelp($table, '', $theData[$titleCol]) . '</td>
432 </tr>';
433 } else {
434 // Render collapse button if in multi table mode
435 $collapseIcon = '';
436 if (!$this->table) {
437 $collapseIcon = '<a href="' . htmlspecialchars(($this->listURL() . '&collapse[' . $table . ']=' . ($tableCollapsed ? '0' : '1'))) . '" title="' . ($tableCollapsed ? $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_core.xlf:labels.expandTable', TRUE) : $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_core.xlf:labels.collapseTable', TRUE)) . '">' . ($tableCollapsed ? IconUtility::getSpriteIcon('actions-view-list-expand', array('class' => 'collapseIcon')) : IconUtility::getSpriteIcon('actions-view-list-collapse', array('class' => 'collapseIcon'))) . '</a>';
438 }
439 $out .= $this->addElement(1, $collapseIcon, $theData, ' class="t3-row-header"', '');
440 }
441 // Render table rows only if in multi table view and not collapsed or if in single table view
442 if (!$listOnlyInSingleTableMode && (!$tableCollapsed || $this->table)) {
443 // Fixing a order table for sortby tables
444 $this->currentTable = array();
445 $currentIdList = array();
446 $doSort = $GLOBALS['TCA'][$table]['ctrl']['sortby'] && !$this->sortField;
447 $prevUid = 0;
448 $prevPrevUid = 0;
449 // Get first two rows and initialize prevPrevUid and prevUid if on page > 1
450 if ($this->firstElementNumber > 2 && $this->iLimit > 0) {
451 $row = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($result);
452 $prevPrevUid = -((int)$row['uid']);
453 $row = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($result);
454 $prevUid = $row['uid'];
455 }
456 $accRows = array();
457 // Accumulate rows here
458 while ($row = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($result)) {
459 if (!$this->isRowListingConditionFulfilled($table, $row)) {
460 continue;
461 }
462 // In offline workspace, look for alternative record:
463 BackendUtility::workspaceOL($table, $row, $GLOBALS['BE_USER']->workspace, TRUE);
464 if (is_array($row)) {
465 $accRows[] = $row;
466 $currentIdList[] = $row['uid'];
467 if ($doSort) {
468 if ($prevUid) {
469 $this->currentTable['prev'][$row['uid']] = $prevPrevUid;
470 $this->currentTable['next'][$prevUid] = '-' . $row['uid'];
471 $this->currentTable['prevUid'][$row['uid']] = $prevUid;
472 }
473 $prevPrevUid = isset($this->currentTable['prev'][$row['uid']]) ? -$prevUid : $row['pid'];
474 $prevUid = $row['uid'];
475 }
476 }
477 }
478 $GLOBALS['TYPO3_DB']->sql_free_result($result);
479 $this->totalRowCount = count($accRows);
480 // CSV initiated
481 if ($this->csvOutput) {
482 $this->initCSV();
483 }
484 // Render items:
485 $this->CBnames = array();
486 $this->duplicateStack = array();
487 $this->eCounter = $this->firstElementNumber;
488 $iOut = '';
489 $cc = 0;
490 foreach ($accRows as $row) {
491 // Render item row if counter < limit
492 if ($cc < $this->iLimit) {
493 $cc++;
494 $this->translations = FALSE;
495 $iOut .= $this->renderListRow($table, $row, $cc, $titleCol, $thumbsCol);
496 // If localization view is enabled it means that the selected records are
497 // either default or All language and here we will not select translations
498 // which point to the main record:
499 if ($this->localizationView && $l10nEnabled) {
500 // For each available translation, render the record:
501 if (is_array($this->translations)) {
502 foreach ($this->translations as $lRow) {
503 // $lRow isn't always what we want - if record was moved we've to work with the
504 // placeholder records otherwise the list is messed up a bit
505 if ($row['_MOVE_PLH_uid'] && $row['_MOVE_PLH_pid']) {
506 $tmpRow = BackendUtility::getRecordRaw($table, 't3ver_move_id="' . (int)$lRow['uid'] . '" AND pid="' . $row['_MOVE_PLH_pid'] . '" AND t3ver_wsid=' . $row['t3ver_wsid'] . BackendUtility::deleteClause($table), $selFieldList);
507 $lRow = is_array($tmpRow) ? $tmpRow : $lRow;
508 }
509 // In offline workspace, look for alternative record:
510 BackendUtility::workspaceOL($table, $lRow, $GLOBALS['BE_USER']->workspace, TRUE);
511 if (is_array($lRow) && $GLOBALS['BE_USER']->checkLanguageAccess($lRow[$GLOBALS['TCA'][$table]['ctrl']['languageField']])) {
512 $currentIdList[] = $lRow['uid'];
513 $iOut .= $this->renderListRow($table, $lRow, $cc, $titleCol, $thumbsCol, 18);
514 }
515 }
516 }
517 }
518 }
519 // Counter of total rows incremented:
520 $this->eCounter++;
521 }
522 // Record navigation is added to the beginning and end of the table if in single table mode
523 if ($this->table) {
524 $iOut = $this->renderListNavigation('top') . $iOut . $this->renderListNavigation('bottom');
525 } else {
526 // Show that there are more records than shown
527 if ($this->totalItems > $this->itemsLimitPerTable) {
528 $countOnFirstPage = $this->totalItems > $this->itemsLimitSingleTable ? $this->itemsLimitSingleTable : $this->totalItems;
529 $hasMore = $this->totalItems > $this->itemsLimitSingleTable;
530 $colspan = $this->showIcon ? count($this->fieldArray) + 1 : count($this->fieldArray);
531 $iOut .= '<tr><td colspan="' . $colspan . '" style="padding:5px;">
532 <a href="' . htmlspecialchars(($this->listURL() . '&table=' . rawurlencode($table))) . '">' . '<img' . IconUtility::skinImg($this->backPath, 'gfx/pildown.gif', 'width="14" height="14"') . ' alt="" />' . ' <i>[1 - ' . $countOnFirstPage . ($hasMore ? '+' : '') . ']</i></a>
533 </td></tr>';
534 }
535 }
536 // The header row for the table is now created:
537 $out .= $this->renderListHeader($table, $currentIdList);
538 }
539 // The list of records is added after the header:
540 $out .= $iOut;
541 unset($iOut);
542 // ... and it is all wrapped in a table:
543 $out = '
544
545
546
547 <!--
548 DB listing of elements: "' . htmlspecialchars($table) . '"
549 -->
550 <table border="0" cellpadding="0" cellspacing="0" class="typo3-dblist' . ($listOnlyInSingleTableMode ? ' typo3-dblist-overview' : '') . '">
551 ' . $out . '
552 </table>';
553 // Output csv if...
554 // This ends the page with exit.
555 if ($this->csvOutput) {
556 $this->outputCSV($table);
557 }
558 }
559 // Return content:
560 return $out;
561 }
562
563 /**
564 * Check if all row listing conditions are fulfilled.
565 *
566 * This function serves as a dummy method to be overriden in extending classes.
567 *
568 * @param string $table Table name
569 * @param array $row Record
570 * @return boolean True, if all conditions are fulfilled.
571 */
572 protected function isRowListingConditionFulfilled($table, $row) {
573 return TRUE;
574 }
575
576 /**
577 * Rendering a single row for the list
578 *
579 * @param string $table Table name
580 * @param array $row Current record
581 * @param integer $cc Counter, counting for each time an element is rendered (used for alternating colors)
582 * @param string $titleCol Table field (column) where header value is found
583 * @param string $thumbsCol Table field (column) where (possible) thumbnails can be found
584 * @param integer $indent Indent from left.
585 * @return string Table row for the element
586 * @access private
587 * @see getTable()
588 * @todo Define visibility
589 */
590 public function renderListRow($table, $row, $cc, $titleCol, $thumbsCol, $indent = 0) {
591 $iOut = '';
592 // If in search mode, make sure the preview will show the correct page
593 if (strlen($this->searchString)) {
594 $id_orig = $this->id;
595 $this->id = $row['pid'];
596 }
597 if (is_array($row)) {
598 // Add special classes for first and last row
599 $rowSpecial = '';
600 if ($cc == 1 && $indent == 0) {
601 $rowSpecial .= ' firstcol';
602 }
603 if ($cc == $this->totalRowCount || $cc == $this->iLimit) {
604 $rowSpecial .= ' lastcol';
605 }
606 // Background color, if any:
607 if ($this->alternateBgColors) {
608 $row_bgColor = $cc % 2 ? ' class="db_list_normal' . $rowSpecial . '"' : ' class="db_list_alt' . $rowSpecial . '"';
609 } else {
610 $row_bgColor = ' class="db_list_normal' . $rowSpecial . '"';
611 }
612 // Overriding with versions background color if any:
613 $row_bgColor = $row['_CSSCLASS'] ? ' class="' . $row['_CSSCLASS'] . '"' : $row_bgColor;
614 // Incr. counter.
615 $this->counter++;
616 // The icon with link
617 $alttext = BackendUtility::getRecordIconAltText($row, $table);
618 $iconImg = IconUtility::getSpriteIconForRecord($table, $row, array('title' => htmlspecialchars($alttext), 'style' => $indent ? ' margin-left: ' . $indent . 'px;' : ''));
619 $theIcon = $this->clickMenuEnabled ? $GLOBALS['SOBE']->doc->wrapClickMenuOnIcon($iconImg, $table, $row['uid']) : $iconImg;
620 // Preparing and getting the data-array
621 $theData = array();
622 foreach ($this->fieldArray as $fCol) {
623 if ($fCol == $titleCol) {
624 $recTitle = BackendUtility::getRecordTitle($table, $row, FALSE, TRUE);
625 // If the record is edit-locked by another user, we will show a little warning sign:
626 if ($lockInfo = BackendUtility::isRecordLocked($table, $row['uid'])) {
627 $warning = '<a href="#" onclick="alert(' . GeneralUtility::quoteJSvalue($lockInfo['msg']) . '); return false;" title="' . htmlspecialchars($lockInfo['msg']) . '">' . IconUtility::getSpriteIcon('status-warning-in-use') . '</a>';
628 }
629 $theData[$fCol] = $warning . $this->linkWrapItems($table, $row['uid'], $recTitle, $row);
630 // Render thumbnails, if:
631 // - a thumbnail column exists
632 // - there is content in it
633 // - the thumbnail column is visible for the current type
634 $type = 0;
635 if (isset($GLOBALS['TCA'][$table]['ctrl']['type'])) {
636 $typeColumn = $GLOBALS['TCA'][$table]['ctrl']['type'];
637 $type = $row[$typeColumn];
638 }
639 // If current type doesn't exist, set it to 0 (or to 1 for historical reasons, if 0 doesn't exist)
640 if (!isset($GLOBALS['TCA'][$table]['types'][$type])) {
641 $type = isset($GLOBALS['TCA'][$table]['types'][0]) ? 0 : 1;
642 }
643 $visibleColumns = $GLOBALS['TCA'][$table]['types'][$type]['showitem'];
644
645 if ($this->thumbs &&
646 trim($row[$thumbsCol]) &&
647 preg_match('/(^|(.*(;|,)?))' . $thumbsCol . '(((;|,).*)|$)/', $visibleColumns) === 1
648 ) {
649 $theData[$fCol] .= '<br />' . $this->thumbCode($row, $table, $thumbsCol);
650 }
651 $localizationMarkerClass = '';
652 if (isset($GLOBALS['TCA'][$table]['ctrl']['languageField']) && $row[$GLOBALS['TCA'][$table]['ctrl']['languageField']] != 0 && $row[$GLOBALS['TCA'][$table]['ctrl']['transOrigPointerField']] != 0) {
653 // It's a translated record with a language parent
654 $localizationMarkerClass = ' localization';
655 }
656 } elseif ($fCol == 'pid') {
657 $theData[$fCol] = $row[$fCol];
658 } elseif ($fCol == '_PATH_') {
659 $theData[$fCol] = $this->recPath($row['pid']);
660 } elseif ($fCol == '_REF_') {
661 $theData[$fCol] = $this->createReferenceHtml($table, $row['uid']);
662 } elseif ($fCol == '_CONTROL_') {
663 $theData[$fCol] = $this->makeControl($table, $row);
664 } elseif ($fCol == '_AFTERCONTROL_' || $fCol == '_AFTERREF_') {
665 $theData[$fCol] = '&nbsp;';
666 } elseif ($fCol == '_CLIPBOARD_') {
667 $theData[$fCol] = $this->makeClip($table, $row);
668 } elseif ($fCol == '_LOCALIZATION_') {
669 list($lC1, $lC2) = $this->makeLocalizationPanel($table, $row);
670 $theData[$fCol] = $lC1;
671 $theData[$fCol . 'b'] = $lC2;
672 } elseif ($fCol == '_LOCALIZATION_b') {
673
674 } else {
675 $tmpProc = BackendUtility::getProcessedValueExtra($table, $fCol, $row[$fCol], 100, $row['uid']);
676 $theData[$fCol] = $this->linkUrlMail(htmlspecialchars($tmpProc), $row[$fCol]);
677 if ($this->csvOutput) {
678 $row[$fCol] = BackendUtility::getProcessedValueExtra($table, $fCol, $row[$fCol], 0, $row['uid']);
679 }
680 }
681 }
682 // Reset the ID if it was overwritten
683 if (strlen($this->searchString)) {
684 $this->id = $id_orig;
685 }
686 // Add row to CSV list:
687 if ($this->csvOutput) {
688 $this->addToCSV($row, $table);
689 }
690 // Add classes to table cells
691 $this->addElement_tdCssClass[$titleCol] = 'col-title' . $localizationMarkerClass;
692 if (!$this->dontShowClipControlPanels) {
693 $this->addElement_tdCssClass['_CONTROL_'] = 'col-control';
694 $this->addElement_tdCssClass['_AFTERCONTROL_'] = 'col-control-space';
695 $this->addElement_tdCssClass['_CLIPBOARD_'] = 'col-clipboard';
696 }
697 $this->addElement_tdCssClass['_PATH_'] = 'col-path';
698 $this->addElement_tdCssClass['_LOCALIZATION_'] = 'col-localizationa';
699 $this->addElement_tdCssClass['_LOCALIZATION_b'] = 'col-localizationb';
700 // Create element in table cells:
701 $iOut .= $this->addelement(1, $theIcon, $theData, $row_bgColor);
702 // Finally, return table row element:
703 return $iOut;
704 }
705 }
706
707 /**
708 * Gets the number of records referencing the record with the UID $uid in
709 * the table $tableName.
710 *
711 * @param string $tableName
712 * @param integer $uid
713 * @return integer The number of references to record $uid in table
714 */
715 protected function getReferenceCount($tableName, $uid) {
716 if (!isset($this->referenceCount[$tableName][$uid])) {
717 $numberOfReferences = $GLOBALS['TYPO3_DB']->exec_SELECTcountRows('*', 'sys_refindex', 'ref_table = ' . $GLOBALS['TYPO3_DB']->fullQuoteStr($tableName, 'sys_refindex') . ' AND ref_uid = ' . $uid . ' AND deleted = 0');
718 $this->referenceCount[$tableName][$uid] = $numberOfReferences;
719 }
720 return $this->referenceCount[$tableName][$uid];
721 }
722
723 /**
724 * Rendering the header row for a table
725 *
726 * @param string $table Table name
727 * @param array $currentIdList Array of the currently displayed uids of the table
728 * @return string Header table row
729 * @access private
730 * @see getTable()
731 * @todo Define visibility
732 */
733 public function renderListHeader($table, $currentIdList) {
734 // Init:
735 $theData = array();
736 // Traverse the fields:
737 foreach ($this->fieldArray as $fCol) {
738 // Calculate users permissions to edit records in the table:
739 $permsEdit = $this->calcPerms & ($table == 'pages' ? 2 : 16);
740 switch ((string) $fCol) {
741 case '_PATH_':
742 // Path
743 $theData[$fCol] = '<i>[' . $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_core.xlf:labels._PATH_', TRUE) . ']</i>';
744 break;
745 case '_REF_':
746 // References
747 $theData[$fCol] = '<i>[' . $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_mod_file_list.xlf:c__REF_', TRUE) . ']</i>';
748 break;
749 case '_LOCALIZATION_':
750 // Path
751 $theData[$fCol] = '<i>[' . $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_core.xlf:labels._LOCALIZATION_', TRUE) . ']</i>';
752 break;
753 case '_LOCALIZATION_b':
754 // Path
755 $theData[$fCol] = $GLOBALS['LANG']->getLL('Localize', TRUE);
756 break;
757 case '_CLIPBOARD_':
758 // Clipboard:
759 $cells = array();
760 // If there are elements on the clipboard for this table, then display the "paste into" icon:
761 $elFromTable = $this->clipObj->elFromTable($table);
762 if (count($elFromTable)) {
763 $cells['pasteAfter'] = '<a href="' . htmlspecialchars($this->clipObj->pasteUrl($table, $this->id)) . '" onclick="' . htmlspecialchars(('return ' . $this->clipObj->confirmMsg('pages', $this->pageRow, 'into', $elFromTable))) . '" title="' . $GLOBALS['LANG']->getLL('clip_paste', TRUE) . '">' . IconUtility::getSpriteIcon('actions-document-paste-after') . '</a>';
764 }
765 // If the numeric clipboard pads are enabled, display the control icons for that:
766 if ($this->clipObj->current != 'normal') {
767 // The "select" link:
768 $cells['copyMarked'] = $this->linkClipboardHeaderIcon(IconUtility::getSpriteIcon('actions-edit-copy', array('title' => $GLOBALS['LANG']->getLL('clip_selectMarked', TRUE))), $table, 'setCB');
769 // The "edit marked" link:
770 $editIdList = implode(',', $currentIdList);
771 $editIdList = '\'+editList(\'' . $table . '\',\'' . $editIdList . '\')+\'';
772 $params = '&edit[' . $table . '][' . $editIdList . ']=edit&disHelp=1';
773 $cells['edit'] = '<a href="#" onclick="' . htmlspecialchars(BackendUtility::editOnClick($params, $this->backPath, -1)) . '" title="' . $GLOBALS['LANG']->getLL('clip_editMarked', TRUE) . '">' . IconUtility::getSpriteIcon('actions-document-open') . '</a>';
774 // The "Delete marked" link:
775 $cells['delete'] = $this->linkClipboardHeaderIcon(IconUtility::getSpriteIcon('actions-edit-delete', array('title' => $GLOBALS['LANG']->getLL('clip_deleteMarked', TRUE))), $table, 'delete', sprintf($GLOBALS['LANG']->getLL('clip_deleteMarkedWarning'), $GLOBALS['LANG']->sL($GLOBALS['TCA'][$table]['ctrl']['title'])));
776 // The "Select all" link:
777 $cells['markAll'] = '<a class="cbcCheckAll" rel="" href="#" onclick="' . htmlspecialchars(('checkOffCB(\'' . implode(',', $this->CBnames) . '\', this); return false;')) . '" title="' . $GLOBALS['LANG']->getLL('clip_markRecords', TRUE) . '">' . IconUtility::getSpriteIcon('actions-document-select') . '</a>';
778 } else {
779 $cells['empty'] = '';
780 }
781 /**
782 * @hook renderListHeaderActions: Allows to change the clipboard icons of the Web>List table headers
783 * @date 2007-11-20
784 * @request Bernhard Kraft <krafbt@kraftb.at>
785 * @usage Above each listed table in Web>List a header row is shown. This hook allows to modify the icons responsible for the clipboard functions (shown above the clipboard checkboxes when a clipboard other than "Normal" is selected), or other "Action" functions which perform operations on the listed records.
786 */
787 if (is_array($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['typo3/class.db_list_extra.inc']['actions'])) {
788 foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['typo3/class.db_list_extra.inc']['actions'] as $classData) {
789 $hookObject = GeneralUtility::getUserObj($classData);
790 if (!$hookObject instanceof \TYPO3\CMS\Recordlist\RecordList\RecordListHookInterface) {
791 throw new \UnexpectedValueException('$hookObject must implement interface TYPO3\\CMS\\Recordlist\\RecordList\\RecordListHookInterface', 1195567850);
792 }
793 $cells = $hookObject->renderListHeaderActions($table, $currentIdList, $cells, $this);
794 }
795 }
796 $theData[$fCol] = implode('', $cells);
797 break;
798 case '_CONTROL_':
799 // Control panel:
800 if (!$GLOBALS['TCA'][$table]['ctrl']['readOnly']) {
801 // If new records can be created on this page, add links:
802 if ($this->calcPerms & ($table == 'pages' ? 8 : 16) && $this->showNewRecLink($table)) {
803 if ($table == 'tt_content' && $this->newWizards) {
804 // If mod.web_list.newContentWiz.overrideWithExtension is set, use that extension's create new content wizard instead:
805 $tmpTSc = BackendUtility::getModTSconfig($this->pageinfo['uid'], 'mod.web_list');
806 $tmpTSc = $tmpTSc['properties']['newContentWiz.']['overrideWithExtension'];
807 $newContentWizScriptPath = $this->backPath . ExtensionManagementUtility::isLoaded($tmpTSc) ? ExtensionManagementUtility::extRelPath($tmpTSc) . 'mod1/db_new_content_el.php' : 'sysext/cms/layout/db_new_content_el.php';
808 $icon = '<a href="#" onclick="' . htmlspecialchars(('return jumpExt(\'' . $newContentWizScriptPath . '?id=' . $this->id . '\');')) . '" title="' . $GLOBALS['LANG']->getLL('new', TRUE) . '">' . ($table == 'pages' ? IconUtility::getSpriteIcon('actions-page-new') : IconUtility::getSpriteIcon('actions-document-new')) . '</a>';
809 } elseif ($table == 'pages' && $this->newWizards) {
810 $icon = '<a href="' . htmlspecialchars(($this->backPath . 'db_new.php?id=' . $this->id . '&pagesOnly=1&returnUrl=' . rawurlencode(GeneralUtility::getIndpEnv('REQUEST_URI')))) . '" title="' . $GLOBALS['LANG']->getLL('new', TRUE) . '">' . ($table == 'pages' ? IconUtility::getSpriteIcon('actions-page-new') : IconUtility::getSpriteIcon('actions-document-new')) . '</a>';
811 } else {
812 $params = '&edit[' . $table . '][' . $this->id . ']=new';
813 if ($table == 'pages_language_overlay') {
814 $params .= '&overrideVals[pages_language_overlay][doktype]=' . (int)$this->pageRow['doktype'];
815 }
816 $icon = '<a href="#" onclick="' . htmlspecialchars(BackendUtility::editOnClick($params, $this->backPath, -1)) . '" title="' . $GLOBALS['LANG']->getLL('new', TRUE) . '">' . ($table == 'pages' ? IconUtility::getSpriteIcon('actions-page-new') : IconUtility::getSpriteIcon('actions-document-new')) . '</a>';
817 }
818 }
819 // If the table can be edited, add link for editing ALL SHOWN fields for all listed records:
820 if ($permsEdit && $this->table && is_array($currentIdList)) {
821 $editIdList = implode(',', $currentIdList);
822 if ($this->clipNumPane()) {
823 $editIdList = '\'+editList(\'' . $table . '\',\'' . $editIdList . '\')+\'';
824 }
825 $params = '&edit[' . $table . '][' . $editIdList . ']=edit&columnsOnly=' . implode(',', $this->fieldArray) . '&disHelp=1';
826 $icon .= '<a href="#" onclick="' . htmlspecialchars(BackendUtility::editOnClick($params, $this->backPath, -1)) . '" title="' . $GLOBALS['LANG']->getLL('editShownColumns', TRUE) . '">' . IconUtility::getSpriteIcon('actions-document-open') . '</a>';
827 }
828 // Add an empty entry, so column count fits again after moving this into $icon
829 $theData[$fCol] = '&nbsp;';
830 }
831 break;
832 case '_AFTERCONTROL_':
833
834 case '_AFTERREF_':
835 // space column
836 $theData[$fCol] = '&nbsp;';
837 break;
838 default:
839 // Regular fields header:
840 $theData[$fCol] = '';
841
842 // Check if $fCol is really a field and get the label and remove the colons at the end
843 $sortLabel = BackendUtility::getItemLabel($table, $fCol);
844 if ($sortLabel !== NULL) {
845 $sortLabel = $GLOBALS['LANG']->sL($sortLabel, TRUE);
846 $sortLabel = rtrim(trim($sortLabel), ':');
847 } else {
848 // No TCA field, only output the $fCol variable with square brackets []
849 $sortLabel = htmlspecialchars($fCol);
850 $sortLabel = '<i>[' . rtrim(trim($sortLabel), ':') . ']</i>';
851 }
852
853 if ($this->table && is_array($currentIdList)) {
854 // If the numeric clipboard pads are selected, show duplicate sorting link:
855 if ($this->clipNumPane()) {
856 $theData[$fCol] .= '<a href="' . htmlspecialchars(($this->listURL('', -1) . '&duplicateField=' . $fCol)) . '" title="' . $GLOBALS['LANG']->getLL('clip_duplicates', TRUE) . '">' . IconUtility::getSpriteIcon('actions-document-duplicates-select') . '</a>';
857 }
858 // If the table can be edited, add link for editing THIS field for all listed records:
859 if (!$GLOBALS['TCA'][$table]['ctrl']['readOnly'] && $permsEdit && $GLOBALS['TCA'][$table]['columns'][$fCol]) {
860 $editIdList = implode(',', $currentIdList);
861 if ($this->clipNumPane()) {
862 $editIdList = '\'+editList(\'' . $table . '\',\'' . $editIdList . '\')+\'';
863 }
864 $params = '&edit[' . $table . '][' . $editIdList . ']=edit&columnsOnly=' . $fCol . '&disHelp=1';
865 $iTitle = sprintf($GLOBALS['LANG']->getLL('editThisColumn'), $sortLabel);
866 $theData[$fCol] .= '<a href="#" onclick="' . htmlspecialchars(BackendUtility::editOnClick($params, $this->backPath, -1)) . '" title="' . htmlspecialchars($iTitle) . '">' . IconUtility::getSpriteIcon('actions-document-open') . '</a>';
867 }
868 }
869 $theData[$fCol] .= $this->addSortLink($sortLabel, $fCol, $table);
870 }
871 }
872 /**
873 * @hook renderListHeader: Allows to change the contents of columns/cells of the Web>List table headers
874 * @date 2007-11-20
875 * @request Bernhard Kraft <krafbt@kraftb.at>
876 * @usage Above each listed table in Web>List a header row is shown. Containing the labels of all shown fields and additional icons to create new records for this table or perform special clipboard tasks like mark and copy all listed records to clipboard, etc.
877 */
878 if (is_array($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['typo3/class.db_list_extra.inc']['actions'])) {
879 foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['typo3/class.db_list_extra.inc']['actions'] as $classData) {
880 $hookObject = GeneralUtility::getUserObj($classData);
881 if (!$hookObject instanceof \TYPO3\CMS\Recordlist\RecordList\RecordListHookInterface) {
882 throw new \UnexpectedValueException('$hookObject must implement interface TYPO3\\CMS\\Recordlist\\RecordList\\RecordListHookInterface', 1195567855);
883 }
884 $theData = $hookObject->renderListHeader($table, $currentIdList, $theData, $this);
885 }
886 }
887 // Create and return header table row:
888 return $this->addelement(1, $icon, $theData, ' class="c-headLine"', '');
889 }
890
891 /**
892 * Get pointer for first element on the page
893 *
894 * @param int $page Page number starting with 1
895 * @return int Pointer to first element on the page (starting with 0)
896 */
897 protected function getPointerForPage($page) {
898 return ($page - 1) * $this->iLimit;
899 }
900
901 /**
902 * Creates a page browser for tables with many records
903 *
904 * @param string $renderPart Distinguish between 'top' and 'bottom' part of the navigation (above or below the records)
905 * @return string Navigation HTML
906 */
907 protected function renderListNavigation($renderPart = 'top') {
908 $totalPages = ceil($this->totalItems / $this->iLimit);
909 $content = '';
910 $returnContent = '';
911 // Show page selector if not all records fit into one page
912 if ($totalPages > 1) {
913 $listURL = $this->listURL('', $this->table);
914 // 1 = first page
915 // 0 = first element
916 $currentPage = floor($this->firstElementNumber / $this->iLimit) + 1;
917 // Compile first, previous, next, last and refresh buttons
918 if ($currentPage > 1) {
919 $labelFirst = $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_common.xlf:first');
920 $labelPrevious = $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_common.xlf:previous');
921 $first = '<a href="' . $listURL . '&pointer=' . $this->getPointerForPage(1) . '">' . IconUtility::getSpriteIcon('actions-view-paging-first', array('title' => $labelFirst)) . '</a>';
922 $previous = '<a href="' . $listURL . '&pointer=' . $this->getPointerForPage($currentPage - 1) . '">' . IconUtility::getSpriteIcon('actions-view-paging-previous', array('title' => $labelPrevious)) . '</a>';
923 } else {
924 $first = IconUtility::getSpriteIcon('actions-view-paging-first-disabled');
925 $previous = IconUtility::getSpriteIcon('actions-view-paging-previous-disabled');
926 }
927 if ($currentPage < $totalPages) {
928 $labelNext = $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_common.xlf:next');
929 $labelLast = $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_common.xlf:last');
930 $next = '<a href="' . $listURL . '&pointer=' . $this->getPointerForPage($currentPage + 1) . '">' . IconUtility::getSpriteIcon('actions-view-paging-next', array('title' => $labelNext)) . '</a>';
931 $last = '<a href="' . $listURL . '&pointer=' . $this->getPointerForPage($totalPages) . '">' . IconUtility::getSpriteIcon('actions-view-paging-last', array('title' => $labelLast)) . '</a>';
932 } else {
933 $next = IconUtility::getSpriteIcon('actions-view-paging-next-disabled');
934 $last = IconUtility::getSpriteIcon('actions-view-paging-last-disabled');
935 }
936 $reload = '<a href="#" onclick="document.dblistForm.action=\'' . $listURL . '&pointer=\'+calculatePointer(document.getElementById(\'jumpPage-' . $renderPart . '\').value); document.dblistForm.submit(); return true;" title="' . $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_common.xlf:reload', TRUE) . '">' . IconUtility::getSpriteIcon('actions-system-refresh') . '</a>';
937 if ($renderPart === 'top') {
938 // Add js to traverse a page select input to a pointer value
939 $content = '
940 <script type="text/JavaScript">
941 /*<![CDATA[*/
942
943 function calculatePointer(page) {
944 if (page > ' . $totalPages . ') {
945 page = ' . $totalPages . ';
946 }
947
948 if (page < 1) {
949 page = 1;
950 }
951
952 pointer = (page - 1) * ' . $this->iLimit . ';
953
954 return pointer;
955 }
956
957 /*]]>*/
958 </script>
959 ';
960 }
961 $pageNumberInput = '<span>
962 <input type="text" value="' . $currentPage . '" size="3" id="jumpPage-' . $renderPart . '" name="jumpPage-' . $renderPart . '" onkeyup="if (event.keyCode == Event.KEY_RETURN) { document.dblistForm.action=\'' . $listURL . '&pointer=\'+calculatePointer(this.value); document.dblistForm.submit(); } return true;" />
963 </span>';
964 $pageIndicator = '<span class="pageIndicator">' . sprintf($GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_mod_web_list.xlf:pageIndicator'), $pageNumberInput, $totalPages) . '</span>';
965 if ($this->totalItems > $this->firstElementNumber + $this->iLimit) {
966 $lastElementNumber = $this->firstElementNumber + $this->iLimit;
967 } else {
968 $lastElementNumber = $this->totalItems;
969 }
970 $rangeIndicator = '<span class="pageIndicator">' . sprintf($GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_mod_web_list.xlf:rangeIndicator'), ($this->firstElementNumber + 1), $lastElementNumber) . '</span>';
971 $content .= '<div id="typo3-dblist-pagination">' . $first . $previous . '<span class="bar">&nbsp;</span>' . $rangeIndicator . '<span class="bar">&nbsp;</span>' . $pageIndicator . '<span class="bar">&nbsp;</span>' . $next . $last . '<span class="bar">&nbsp;</span>' . $reload . '</div>';
972 $data = array();
973 $titleColumn = $this->fieldArray[0];
974 $data[$titleColumn] = $content;
975 $returnContent = $this->addElement(1, '', $data);
976 }
977 // end of if pages > 1
978 return $returnContent;
979 }
980
981 /*********************************
982 *
983 * Rendering of various elements
984 *
985 *********************************/
986 /**
987 * Creates the control panel for a single record in the listing.
988 *
989 * @param string $table The table
990 * @param array $row The record for which to make the control panel.
991 * @return string HTML table with the control panel (unless disabled)
992 * @todo Define visibility
993 */
994 public function makeControl($table, $row) {
995 if ($this->dontShowClipControlPanels) {
996 return '';
997 }
998 $rowUid = $row['uid'];
999 if (ExtensionManagementUtility::isLoaded('version') && isset($row['_ORIG_uid'])) {
1000 $rowUid = $row['_ORIG_uid'];
1001 }
1002 $cells = array();
1003 // If the listed table is 'pages' we have to request the permission settings for each page:
1004 if ($table == 'pages') {
1005 $localCalcPerms = $GLOBALS['BE_USER']->calcPerms(BackendUtility::getRecord('pages', $row['uid']));
1006 }
1007 // This expresses the edit permissions for this particular element:
1008 $permsEdit = $table == 'pages' && $localCalcPerms & 2 || $table != 'pages' && $this->calcPerms & 16;
1009 // "Show" link (only pages and tt_content elements)
1010 if ($table == 'pages' || $table == 'tt_content') {
1011 $cells['view'] = '<a href="#" onclick="'
1012 . htmlspecialchars(
1013 BackendUtility::viewOnClick(
1014 ($table === 'tt_content' ? $this->id : $row['uid']),
1015 $this->backPath,
1016 '',
1017 ($table === 'tt_content' ? '#' . $row['uid'] : '')
1018 )
1019 ) . '" title="' . $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_core.xlf:labels.showPage', TRUE) . '">'
1020 . IconUtility::getSpriteIcon('actions-document-view') . '</a>';
1021 } elseif (!$this->table) {
1022 $cells['view'] = $this->spaceIcon;
1023 }
1024 // "Edit" link: ( Only if permissions to edit the page-record of the content of the parent page ($this->id)
1025 if ($permsEdit) {
1026 $params = '&edit[' . $table . '][' . $row['uid'] . ']=edit';
1027 $cells['edit'] = '<a href="#" onclick="' . htmlspecialchars(BackendUtility::editOnClick($params, $this->backPath, -1)) . '" title="' . $GLOBALS['LANG']->getLL('edit', TRUE) . '">' . ($GLOBALS['TCA'][$table]['ctrl']['readOnly'] ? IconUtility::getSpriteIcon('actions-document-open-read-only') : IconUtility::getSpriteIcon('actions-document-open')) . '</a>';
1028 } elseif (!$this->table) {
1029 $cells['edit'] = $this->spaceIcon;
1030 }
1031 // "Move" wizard link for pages/tt_content elements:
1032 if ($table == 'tt_content' && $permsEdit || $table == 'pages') {
1033 $cells['move'] = '<a href="#" onclick="' . htmlspecialchars(('return jumpExt(\'' . $this->backPath . 'move_el.php?table=' . $table . '&uid=' . $row['uid'] . '\');')) . '" title="' . $GLOBALS['LANG']->getLL(('move_' . ($table == 'tt_content' ? 'record' : 'page')), TRUE) . '">' . ($table == 'tt_content' ? IconUtility::getSpriteIcon('actions-document-move') : IconUtility::getSpriteIcon('actions-page-move')) . '</a>';
1034 } elseif (!$this->table) {
1035 $cells['move'] = $this->spaceIcon;
1036 }
1037 // If the extended control panel is enabled OR if we are seeing a single table:
1038 if ($GLOBALS['SOBE']->MOD_SETTINGS['bigControlPanel'] || $this->table) {
1039 // "Info": (All records)
1040 $cells['viewBig'] = '<a href="#" onclick="' . htmlspecialchars(('top.launchView(\'' . $table . '\', \'' . $row['uid'] . '\'); return false;')) . '" title="' . $GLOBALS['LANG']->getLL('showInfo', TRUE) . '">' . IconUtility::getSpriteIcon('actions-document-info') . '</a>';
1041 // If the table is NOT a read-only table, then show these links:
1042 if (!$GLOBALS['TCA'][$table]['ctrl']['readOnly']) {
1043 // "Revert" link (history/undo)
1044 $cells['history'] = '<a href="#" onclick="' . htmlspecialchars(('return jumpExt(' . GeneralUtility::quoteJSvalue($this->backPath . BackendUtility::getModuleUrl('record_history', array('element' => $table . ':' . $row['uid']))) . ',\'#latest\');')) . '" title="' . $GLOBALS['LANG']->getLL('history', TRUE) . '">' . IconUtility::getSpriteIcon('actions-document-history-open') . '</a>';
1045 // Versioning:
1046 if (ExtensionManagementUtility::isLoaded('version') && !ExtensionManagementUtility::isLoaded('workspaces')) {
1047 $vers = BackendUtility::selectVersionsOfRecord($table, $row['uid'], 'uid', $GLOBALS['BE_USER']->workspace, FALSE, $row);
1048 // If table can be versionized.
1049 if (is_array($vers)) {
1050 $versionIcon = 'no-version';
1051 if (count($vers) > 1) {
1052 $versionIcon = count($vers) - 1;
1053 }
1054 $cells['version'] = '<a href="' . htmlspecialchars($this->backPath . BackendUtility::getModuleUrl('web_txversionM1', array('table' => $table, 'uid' => $row['uid']))) . '" title="' . $GLOBALS['LANG']->getLL('displayVersions', TRUE) . '">' . IconUtility::getSpriteIcon(('status-version-' . $versionIcon)) . '</a>';
1055 } elseif (!$this->table) {
1056 $cells['version'] = $this->spaceIcon;
1057 }
1058 }
1059 // "Edit Perms" link:
1060 if ($table == 'pages' && $GLOBALS['BE_USER']->check('modules', 'web_perm') && ExtensionManagementUtility::isLoaded('perm')) {
1061 $cells['perms'] = '<a href="' . htmlspecialchars((BackendUtility::getModuleUrl('web_perm') . '&id=' . $row['uid'] . '&return_id=' . $row['uid'] . '&edit=1')) . '" title="' . $GLOBALS['LANG']->getLL('permissions', TRUE) . '">' . IconUtility::getSpriteIcon('status-status-locked') . '</a>';
1062 } elseif (!$this->table && $GLOBALS['BE_USER']->check('modules', 'web_perm')) {
1063 $cells['perms'] = $this->spaceIcon;
1064 }
1065 // "New record after" link (ONLY if the records in the table are sorted by a "sortby"-row or if default values can depend on previous record):
1066 if ($GLOBALS['TCA'][$table]['ctrl']['sortby'] || $GLOBALS['TCA'][$table]['ctrl']['useColumnsForDefaultValues']) {
1067 if ($table != 'pages' && $this->calcPerms & 16 || $table == 'pages' && $this->calcPerms & 8) {
1068 if ($this->showNewRecLink($table)) {
1069 $params = '&edit[' . $table . '][' . -($row['_MOVE_PLH'] ? $row['_MOVE_PLH_uid'] : $row['uid']) . ']=new';
1070 $cells['new'] = '<a href="#" onclick="' . htmlspecialchars(BackendUtility::editOnClick($params, $this->backPath, -1)) . '" title="' . $GLOBALS['LANG']->getLL(('new' . ($table == 'pages ' ? 'Page' : 'Record')), TRUE) . '">' . ($table == 'pages' ? IconUtility::getSpriteIcon('actions-page-new') : IconUtility::getSpriteIcon('actions-document-new')) . '</a>';
1071 }
1072 }
1073 } elseif (!$this->table) {
1074 $cells['new'] = $this->spaceIcon;
1075 }
1076 // "Up/Down" links
1077 if ($permsEdit && $GLOBALS['TCA'][$table]['ctrl']['sortby'] && !$this->sortField && !$this->searchLevels) {
1078 if (isset($this->currentTable['prev'][$row['uid']])) {
1079 // Up
1080 $params = '&cmd[' . $table . '][' . $row['uid'] . '][move]=' . $this->currentTable['prev'][$row['uid']];
1081 $cells['moveUp'] = '<a href="#" onclick="' . htmlspecialchars(('return jumpToUrl(\'' . $GLOBALS['SOBE']->doc->issueCommand($params, -1) . '\');')) . '" title="' . $GLOBALS['LANG']->getLL('moveUp', TRUE) . '">' . IconUtility::getSpriteIcon('actions-move-up') . '</a>';
1082 } else {
1083 $cells['moveUp'] = $this->spaceIcon;
1084 }
1085 if ($this->currentTable['next'][$row['uid']]) {
1086 // Down
1087 $params = '&cmd[' . $table . '][' . $row['uid'] . '][move]=' . $this->currentTable['next'][$row['uid']];
1088 $cells['moveDown'] = '<a href="#" onclick="' . htmlspecialchars(('return jumpToUrl(\'' . $GLOBALS['SOBE']->doc->issueCommand($params, -1) . '\');')) . '" title="' . $GLOBALS['LANG']->getLL('moveDown', TRUE) . '">' . IconUtility::getSpriteIcon('actions-move-down') . '</a>';
1089 } else {
1090 $cells['moveDown'] = $this->spaceIcon;
1091 }
1092 } elseif (!$this->table) {
1093 $cells['moveUp'] = $this->spaceIcon;
1094 $cells['moveDown'] = $this->spaceIcon;
1095 }
1096 // "Hide/Unhide" links:
1097 $hiddenField = $GLOBALS['TCA'][$table]['ctrl']['enablecolumns']['disabled'];
1098 if ($permsEdit && $hiddenField && $GLOBALS['TCA'][$table]['columns'][$hiddenField] && (!$GLOBALS['TCA'][$table]['columns'][$hiddenField]['exclude'] || $GLOBALS['BE_USER']->check('non_exclude_fields', $table . ':' . $hiddenField))) {
1099 if ($row[$hiddenField]) {
1100 $params = '&data[' . $table . '][' . $rowUid . '][' . $hiddenField . ']=0';
1101 $cells['hide'] = '<a href="#" onclick="' . htmlspecialchars('return jumpToUrl(\'' . $GLOBALS['SOBE']->doc->issueCommand($params, -1) . '\');') . '" title="' . $GLOBALS['LANG']->getLL(('unHide' . ($table == 'pages' ? 'Page' : '')), TRUE) . '">' . IconUtility::getSpriteIcon('actions-edit-unhide') . '</a>';
1102 } else {
1103 $params = '&data[' . $table . '][' . $rowUid . '][' . $hiddenField . ']=1';
1104 $cells['hide'] = '<a href="#" onclick="' . htmlspecialchars('return jumpToUrl(\'' . $GLOBALS['SOBE']->doc->issueCommand($params, -1) . '\');') . '" title="' . $GLOBALS['LANG']->getLL(('hide' . ($table == 'pages' ? 'Page' : '')), TRUE) . '">' . IconUtility::getSpriteIcon('actions-edit-hide') . '</a>';
1105 }
1106 } elseif (!$this->table) {
1107 $cells['hide'] = $this->spaceIcon;
1108 }
1109 // "Delete" link:
1110 if ($table == 'pages' && $localCalcPerms & 4 || $table != 'pages' && $this->calcPerms & 16) {
1111 // Check if the record version is in "deleted" state, because that will switch the action to "restore"
1112 if ($GLOBALS['BE_USER']->workspace > 0 && isset($row['t3ver_state']) && (int)$row['t3ver_state'] === 2) {
1113 $actionName = 'restore';
1114 $refCountMsg = '';
1115 } else {
1116 $actionName = 'delete';
1117 $refCountMsg = BackendUtility::referenceCount(
1118 $table,
1119 $row['uid'],
1120 ' ' . $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_core.xlf:labels.referencesToRecord'),
1121 $this->getReferenceCount($table, $row['uid'])) . BackendUtility::translationCount($table, $row['uid'],
1122 ' ' . $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_core.xlf:labels.translationsOfRecord')
1123 );
1124 }
1125
1126 $titleOrig = BackendUtility::getRecordTitle($table, $row, FALSE, TRUE);
1127 $title = GeneralUtility::slashJS(GeneralUtility::fixed_lgd_cs($titleOrig, $this->fixedL), 1);
1128 $warningText = GeneralUtility::quoteJSvalue(
1129 $GLOBALS['LANG']->getLL($actionName . 'Warning') . ' "' . $title . '" ' . $refCountMsg
1130 );
1131
1132 $params = '&cmd[' . $table . '][' . $row['uid'] . '][delete]=1';
1133 $onClick = htmlspecialchars(
1134 ('if (confirm(' . $warningText . ')) {jumpToUrl(\'' . $GLOBALS['SOBE']->doc->issueCommand($params, -1) . '\');} return false;')
1135 );
1136
1137 $icon = IconUtility::getSpriteIcon('actions-edit-' . $actionName);
1138 $linkTitle = $GLOBALS['LANG']->getLL($actionName, TRUE);
1139 $cells['delete'] = '<a href="#" onclick="' . $onClick . '" title="' . $linkTitle . '">' . $icon . '</a>';
1140 } elseif (!$this->table) {
1141 $cells['delete'] = $this->spaceIcon;
1142 }
1143 // "Levels" links: Moving pages into new levels...
1144 if ($permsEdit && $table == 'pages' && !$this->searchLevels) {
1145 // Up (Paste as the page right after the current parent page)
1146 if ($this->calcPerms & 8) {
1147 $params = '&cmd[' . $table . '][' . $row['uid'] . '][move]=' . -$this->id;
1148 $cells['moveLeft'] = '<a href="#" onclick="' . htmlspecialchars('return jumpToUrl(\'' . $GLOBALS['SOBE']->doc->issueCommand($params, -1) . '\');') . '" title="' . $GLOBALS['LANG']->getLL('prevLevel', TRUE) . '">' . IconUtility::getSpriteIcon('actions-move-left') . '</a>';
1149 }
1150 // Down (Paste as subpage to the page right above)
1151 if ($this->currentTable['prevUid'][$row['uid']]) {
1152 $localCalcPerms = $GLOBALS['BE_USER']->calcPerms(BackendUtility::getRecord('pages', $this->currentTable['prevUid'][$row['uid']]));
1153 if ($localCalcPerms & 8) {
1154 $params = '&cmd[' . $table . '][' . $row['uid'] . '][move]=' . $this->currentTable['prevUid'][$row['uid']];
1155 $cells['moveRight'] = '<a href="#" onclick="' . htmlspecialchars('return jumpToUrl(\'' . $GLOBALS['SOBE']->doc->issueCommand($params, -1) . '\');') . '" title="' . $GLOBALS['LANG']->getLL('nextLevel', TRUE) . '">' . IconUtility::getSpriteIcon('actions-move-right') . '</a>';
1156 } else {
1157 $cells['moveRight'] = $this->spaceIcon;
1158 }
1159 } else {
1160 $cells['moveRight'] = $this->spaceIcon;
1161 }
1162 } elseif (!$this->table) {
1163 $cells['moveLeft'] = $this->spaceIcon;
1164 $cells['moveRight'] = $this->spaceIcon;
1165 }
1166 }
1167 }
1168 /**
1169 * @hook recStatInfoHooks: Allows to insert HTML before record icons on various places
1170 * @date 2007-09-22
1171 * @request Kasper Skårhøj <kasper2007@typo3.com>
1172 */
1173 if (is_array($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['GLOBAL']['recStatInfoHooks'])) {
1174 $stat = '';
1175 $_params = array($table, $row['uid']);
1176 foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['GLOBAL']['recStatInfoHooks'] as $_funcRef) {
1177 $stat .= GeneralUtility::callUserFunction($_funcRef, $_params, $this);
1178 }
1179 $cells['stat'] = $stat;
1180 }
1181 /**
1182 * @hook makeControl: Allows to change control icons of records in list-module
1183 * @date 2007-11-20
1184 * @request Bernhard Kraft <krafbt@kraftb.at>
1185 * @usage This hook method gets passed the current $cells array as third parameter. This array contains values for the icons/actions generated for each record in Web>List. Each array entry is accessible by an index-key. The order of the icons is dependend on the order of those array entries.
1186 */
1187 if (is_array($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['typo3/class.db_list_extra.inc']['actions'])) {
1188 foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['typo3/class.db_list_extra.inc']['actions'] as $classData) {
1189 $hookObject = GeneralUtility::getUserObj($classData);
1190 if (!$hookObject instanceof \TYPO3\CMS\Recordlist\RecordList\RecordListHookInterface) {
1191 throw new \UnexpectedValueException('$hookObject must implement interface TYPO3\\CMS\\Recordlist\\RecordList\\RecordListHookInterface', 1195567840);
1192 }
1193 $cells = $hookObject->makeControl($table, $row, $cells, $this);
1194 }
1195 }
1196 // Compile items into a DIV-element:
1197 return '
1198 <!-- CONTROL PANEL: ' . $table . ':' . $row['uid'] . ' -->
1199 <div class="typo3-DBctrl">' . implode('', $cells) . '</div>';
1200 }
1201
1202 /**
1203 * Creates the clipboard panel for a single record in the listing.
1204 *
1205 * @param string $table The table
1206 * @param array $row The record for which to make the clipboard panel.
1207 * @return string HTML table with the clipboard panel (unless disabled)
1208 * @todo Define visibility
1209 */
1210 public function makeClip($table, $row) {
1211 // Return blank, if disabled:
1212 if ($this->dontShowClipControlPanels) {
1213 return '';
1214 }
1215 $cells = array();
1216 $cells['pasteAfter'] = ($cells['pasteInto'] = $this->spaceIcon);
1217 //enables to hide the copy, cut and paste icons for localized records - doesn't make much sense to perform these options for them
1218 $isL10nOverlay = $this->localizationView && $table != 'pages_language_overlay' && $row[$GLOBALS['TCA'][$table]['ctrl']['transOrigPointerField']] != 0;
1219 // Return blank, if disabled:
1220 // Whether a numeric clipboard pad is active or the normal pad we will see different content of the panel:
1221 // For the "Normal" pad:
1222 if ($this->clipObj->current == 'normal') {
1223 // Show copy/cut icons:
1224 $isSel = (string) $this->clipObj->isSelected($table, $row['uid']);
1225 $cells['copy'] = $isL10nOverlay ? $this->spaceIcon : '<a href="#" onclick="' . htmlspecialchars(('return jumpSelf(\'' . $this->clipObj->selUrlDB($table, $row['uid'], 1, ($isSel == 'copy'), array('returnUrl' => '')) . '\');')) . '" title="' . $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_core.xlf:cm.copy', TRUE) . '">' . (!$isSel == 'copy' ? IconUtility::getSpriteIcon('actions-edit-copy') : IconUtility::getSpriteIcon('actions-edit-copy-release')) . '</a>';
1226 $cells['cut'] = $isL10nOverlay ? $this->spaceIcon : '<a href="#" onclick="' . htmlspecialchars(('return jumpSelf(\'' . $this->clipObj->selUrlDB($table, $row['uid'], 0, ($isSel == 'cut'), array('returnUrl' => '')) . '\');')) . '" title="' . $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_core.xlf:cm.cut', TRUE) . '">' . (!$isSel == 'cut' ? IconUtility::getSpriteIcon('actions-edit-cut') : IconUtility::getSpriteIcon('actions-edit-cut-release')) . '</a>';
1227 } else {
1228 // For the numeric clipboard pads (showing checkboxes where one can select elements on/off)
1229 // Setting name of the element in ->CBnames array:
1230 $n = $table . '|' . $row['uid'];
1231 $this->CBnames[] = $n;
1232 // Check if the current element is selected and if so, prepare to set the checkbox as selected:
1233 $checked = $this->clipObj->isSelected($table, $row['uid']) ? ' checked="checked"' : '';
1234 // If the "duplicateField" value is set then select all elements which are duplicates...
1235 if ($this->duplicateField && isset($row[$this->duplicateField])) {
1236 $checked = '';
1237 if (in_array($row[$this->duplicateField], $this->duplicateStack)) {
1238 $checked = ' checked="checked"';
1239 }
1240 $this->duplicateStack[] = $row[$this->duplicateField];
1241 }
1242 // Adding the checkbox to the panel:
1243 $cells['select'] = $isL10nOverlay ? $this->spaceIcon : '<input type="hidden" name="CBH[' . $n . ']" value="0" /><input type="checkbox" name="CBC[' . $n . ']" value="1" class="smallCheckboxes"' . $checked . ' />';
1244 }
1245 // Now, looking for selected elements from the current table:
1246 $elFromTable = $this->clipObj->elFromTable($table);
1247 if (count($elFromTable) && $GLOBALS['TCA'][$table]['ctrl']['sortby']) {
1248 // IF elements are found and they can be individually ordered, then add a "paste after" icon:
1249 $cells['pasteAfter'] = $isL10nOverlay ? $this->spaceIcon : '<a href="' . htmlspecialchars($this->clipObj->pasteUrl($table, -$row['uid'])) . '" onclick="' . htmlspecialchars(('return ' . $this->clipObj->confirmMsg($table, $row, 'after', $elFromTable))) . '" title="' . $GLOBALS['LANG']->getLL('clip_pasteAfter', TRUE) . '">' . IconUtility::getSpriteIcon('actions-document-paste-after') . '</a>';
1250 }
1251 // Now, looking for elements in general:
1252 $elFromTable = $this->clipObj->elFromTable('');
1253 if ($table == 'pages' && count($elFromTable)) {
1254 $cells['pasteInto'] = '<a href="' . htmlspecialchars($this->clipObj->pasteUrl('', $row['uid'])) . '" onclick="' . htmlspecialchars(('return ' . $this->clipObj->confirmMsg($table, $row, 'into', $elFromTable))) . '" title="' . $GLOBALS['LANG']->getLL('clip_pasteInto', TRUE) . '">' . IconUtility::getSpriteIcon('actions-document-paste-into') . '</a>';
1255 }
1256 /**
1257 * @hook makeClip: Allows to change clip-icons of records in list-module
1258 * @date 2007-11-20
1259 * @request Bernhard Kraft <krafbt@kraftb.at>
1260 * @usage This hook method gets passed the current $cells array as third parameter. This array contains values for the clipboard icons generated for each record in Web>List. Each array entry is accessible by an index-key. The order of the icons is dependend on the order of those array entries.
1261 */
1262 if (is_array($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['typo3/class.db_list_extra.inc']['actions'])) {
1263 foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['typo3/class.db_list_extra.inc']['actions'] as $classData) {
1264 $hookObject = GeneralUtility::getUserObj($classData);
1265 if (!$hookObject instanceof \TYPO3\CMS\Recordlist\RecordList\RecordListHookInterface) {
1266 throw new \UnexpectedValueException('$hookObject must implement interface TYPO3\\CMS\\Recordlist\\RecordList\\RecordListHookInterface', 1195567845);
1267 }
1268 $cells = $hookObject->makeClip($table, $row, $cells, $this);
1269 }
1270 }
1271 // Compile items into a DIV-element:
1272 return ' <!-- CLIPBOARD PANEL: ' . $table . ':' . $row['uid'] . ' -->
1273 <div class="typo3-clipCtrl">' . implode('', $cells) . '</div>';
1274 }
1275
1276 /**
1277 * Creates the HTML for a reference count for the record with the UID $uid
1278 * in the table $tableName.
1279 *
1280 * @param string $tableName
1281 * @param integer $uid
1282 * @return string HTML of reference a link, will be empty if there are no
1283 */
1284 protected function createReferenceHtml($tableName, $uid) {
1285 $rows = $GLOBALS['TYPO3_DB']->exec_SELECTgetRows(
1286 'tablename, recuid, field',
1287 'sys_refindex',
1288 'ref_table = ' . $GLOBALS['TYPO3_DB']->fullQuoteStr($tableName, 'sys_refindex') .
1289 ' AND ref_uid = ' . $uid . ' AND deleted = 0'
1290 );
1291 return $this->generateReferenceToolTip($rows, '\'' . $tableName . '\', \'' . $uid . '\'');
1292 }
1293
1294 /**
1295 * Creates the localization panel
1296 *
1297 * @param string $table The table
1298 * @param array $row The record for which to make the localization panel.
1299 * @return array Array with key 0/1 with content for column 1 and 2
1300 * @todo Define visibility
1301 */
1302 public function makeLocalizationPanel($table, $row) {
1303 $out = array(
1304 0 => '',
1305 1 => ''
1306 );
1307 // Reset translations
1308 $this->translations = array();
1309 $translations = $this->translateTools->translationInfo($table, $row['uid'], 0, $row, $this->selFieldList);
1310 // Language title and icon:
1311 $out[0] = $this->languageFlag($row[$GLOBALS['TCA'][$table]['ctrl']['languageField']]);
1312 if (is_array($translations)) {
1313 $this->translations = $translations['translations'];
1314 // Traverse page translations and add icon for each language that does NOT yet exist:
1315 $lNew = '';
1316 foreach ($this->pageOverlays as $lUid_OnPage => $lsysRec) {
1317 if (!isset($translations['translations'][$lUid_OnPage]) && $GLOBALS['BE_USER']->checkLanguageAccess($lUid_OnPage)) {
1318 $url = substr($this->listURL(), strlen($this->backPath));
1319 $href = $GLOBALS['SOBE']->doc->issueCommand('&cmd[' . $table . '][' . $row['uid'] . '][localize]=' . $lUid_OnPage, $url . '&justLocalized=' . rawurlencode(($table . ':' . $row['uid'] . ':' . $lUid_OnPage)));
1320 $language = BackendUtility::getRecord('sys_language', $lUid_OnPage, 'title');
1321 if ($this->languageIconTitles[$lUid_OnPage]['flagIcon']) {
1322 $lC = IconUtility::getSpriteIcon($this->languageIconTitles[$lUid_OnPage]['flagIcon']);
1323 } else {
1324 $lC = $this->languageIconTitles[$lUid_OnPage]['title'];
1325 }
1326 $lC = '<a href="' . htmlspecialchars($href) . '" title="' . htmlspecialchars($language['title']) . '">' . $lC . '</a> ';
1327 $lNew .= $lC;
1328 }
1329 }
1330 if ($lNew) {
1331 $out[1] .= $lNew;
1332 }
1333 } elseif ($row['l18n_parent']) {
1334 $out[0] = '&nbsp;&nbsp;&nbsp;&nbsp;' . $out[0];
1335 }
1336 return $out;
1337 }
1338
1339 /**
1340 * Create the selector box for selecting fields to display from a table:
1341 *
1342 * @param string $table Table name
1343 * @param boolean $formFields If TRUE, form-fields will be wrapped around the table.
1344 * @return string HTML table with the selector box (name: displayFields['.$table.'][])
1345 * @todo Define visibility
1346 */
1347 public function fieldSelectBox($table, $formFields = 1) {
1348 // Init:
1349 $formElements = array('', '');
1350 if ($formFields) {
1351 $formElements = array('<form action="' . htmlspecialchars($this->listURL()) . '" method="post">', '</form>');
1352 }
1353 // Load already selected fields, if any:
1354 $setFields = is_array($this->setFields[$table]) ? $this->setFields[$table] : array();
1355 // Request fields from table:
1356 $fields = $this->makeFieldList($table, FALSE, TRUE);
1357 // Add pseudo "control" fields
1358 $fields[] = '_PATH_';
1359 $fields[] = '_REF_';
1360 $fields[] = '_LOCALIZATION_';
1361 $fields[] = '_CONTROL_';
1362 $fields[] = '_CLIPBOARD_';
1363 // Create an option for each field:
1364 $opt = array();
1365 $opt[] = '<option value=""></option>';
1366 foreach ($fields as $fN) {
1367 // Field label
1368 $fL = is_array($GLOBALS['TCA'][$table]['columns'][$fN]) ? rtrim($GLOBALS['LANG']->sL($GLOBALS['TCA'][$table]['columns'][$fN]['label']), ':') : '[' . $fN . ']';
1369 $opt[] = '
1370 <option value="' . $fN . '"' . (in_array($fN, $setFields) ? ' selected="selected"' : '') . '>' . htmlspecialchars($fL) . '</option>';
1371 }
1372 // Compile the options into a multiple selector box:
1373 $lMenu = '
1374 <select size="' . \TYPO3\CMS\Core\Utility\MathUtility::forceIntegerInRange((count($fields) + 1), 3, 20) . '" multiple="multiple" name="displayFields[' . $table . '][]">' . implode('', $opt) . '
1375 </select>
1376 ';
1377 // Table with the field selector::
1378 $content = $formElements[0] . '
1379
1380 <!--
1381 Field selector for extended table view:
1382 -->
1383 <table border="0" cellpadding="0" cellspacing="0" id="typo3-dblist-fieldSelect">
1384 <tr>
1385 <td>' . $lMenu . '</td>
1386 <td><input type="submit" name="search" value="' . $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_core.xlf:labels.setFields', TRUE) . '" /></td>
1387 </tr>
1388 </table>
1389 ' . $formElements[1];
1390 return '<div class="db_list-fieldSelect">' . $content . '</div>';
1391 }
1392
1393 /*********************************
1394 *
1395 * Helper functions
1396 *
1397 *********************************/
1398 /**
1399 * Creates a link around $string. The link contains an onclick action which submits the script with some clipboard action.
1400 * Currently, this is used for setting elements / delete elements.
1401 *
1402 * @param string $string The HTML content to link (image/text)
1403 * @param string $table Table name
1404 * @param string $cmd Clipboard command (eg. "setCB" or "delete")
1405 * @param string $warning Warning text, if any ("delete" uses this for confirmation)
1406 * @return string <a> tag wrapped link.
1407 * @todo Define visibility
1408 */
1409 public function linkClipboardHeaderIcon($string, $table, $cmd, $warning = '') {
1410 $onClickEvent = 'document.dblistForm.cmd.value=\'' . $cmd . '\';document.dblistForm.cmd_table.value=\'' . $table . '\';document.dblistForm.submit();';
1411 if ($warning) {
1412 $onClickEvent = 'if (confirm(' . GeneralUtility::quoteJSvalue($warning) . ')){' . $onClickEvent . '}';
1413 }
1414 return '<a href="#" onclick="' . htmlspecialchars(($onClickEvent . 'return false;')) . '">' . $string . '</a>';
1415 }
1416
1417 /**
1418 * Returns TRUE if a numeric clipboard pad is selected/active
1419 *
1420 * @return boolean
1421 * @todo Define visibility
1422 */
1423 public function clipNumPane() {
1424 return in_Array('_CLIPBOARD_', $this->fieldArray) && $this->clipObj->current != 'normal';
1425 }
1426
1427 /**
1428 * Creates a sort-by link on the input string ($code).
1429 * It will automatically detect if sorting should be ascending or descending depending on $this->sortRev.
1430 * Also some fields will not be possible to sort (including if single-table-view is disabled).
1431 *
1432 * @param string $code The string to link (text)
1433 * @param string $field The fieldname represented by the title ($code)
1434 * @param string $table Table name
1435 * @return string Linked $code variable
1436 * @todo Define visibility
1437 */
1438 public function addSortLink($code, $field, $table) {
1439 // Certain circumstances just return string right away (no links):
1440 if ($field == '_CONTROL_' || $field == '_LOCALIZATION_' || $field == '_CLIPBOARD_' || $field == '_REF_' || $this->disableSingleTableView) {
1441 return $code;
1442 }
1443 // If "_PATH_" (showing record path) is selected, force sorting by pid field (will at least group the records!)
1444 if ($field == '_PATH_') {
1445 $field = 'pid';
1446 }
1447 // Create the sort link:
1448 $sortUrl = $this->listURL('', -1, 'sortField,sortRev,table,firstElementNumber') . '&table=' . $table . '&sortField=' . $field . '&sortRev=' . ($this->sortRev || $this->sortField != $field ? 0 : 1);
1449 $sortArrow = $this->sortField === $field ? IconUtility::getSpriteIcon('status-status-sorting-' . ($this->sortRev ? 'desc' : 'asc')) : '';
1450 // Return linked field:
1451 return '<a href="' . htmlspecialchars($sortUrl) . '">' . $code . $sortArrow . '</a>';
1452 }
1453
1454 /**
1455 * Returns the path for a certain pid
1456 * The result is cached internally for the session, thus you can call this function as much as you like without performance problems.
1457 *
1458 * @param integer $pid The page id for which to get the path
1459 * @return string The path.
1460 * @todo Define visibility
1461 */
1462 public function recPath($pid) {
1463 if (!isset($this->recPath_cache[$pid])) {
1464 $this->recPath_cache[$pid] = BackendUtility::getRecordPath($pid, $this->perms_clause, 20);
1465 }
1466 return $this->recPath_cache[$pid];
1467 }
1468
1469 /**
1470 * Returns TRUE if a link for creating new records should be displayed for $table
1471 *
1472 * @param string $table Table name
1473 * @return boolean Returns TRUE if a link for creating new records should be displayed for $table
1474 * @see SC_db_new::showNewRecLink
1475 * @todo Define visibility
1476 */
1477 public function showNewRecLink($table) {
1478 // No deny/allow tables are set:
1479 if (!count($this->allowedNewTables) && !count($this->deniedNewTables)) {
1480 return TRUE;
1481 } elseif (!in_array($table, $this->deniedNewTables) && (!count($this->allowedNewTables) || in_array($table, $this->allowedNewTables))) {
1482 return TRUE;
1483 } else {
1484 return FALSE;
1485 }
1486 }
1487
1488 /**
1489 * Creates the "&returnUrl" parameter for links - this is used when the script links to other scripts and passes its own URL with the link so other scripts can return to the listing again.
1490 * Uses REQUEST_URI as value.
1491 *
1492 * @return string
1493 * @todo Define visibility
1494 */
1495 public function makeReturnUrl() {
1496 return '&returnUrl=' . rawurlencode(GeneralUtility::getIndpEnv('REQUEST_URI'));
1497 }
1498
1499 /************************************
1500 *
1501 * CSV related functions
1502 *
1503 ************************************/
1504 /**
1505 * Initializes internal csvLines array with the header of field names
1506 *
1507 * @return void
1508 */
1509 protected function initCSV() {
1510 $this->addHeaderRowToCSV();
1511 }
1512
1513 /**
1514 * Add header line with field names as CSV line
1515 *
1516 * @return void
1517 */
1518 protected function addHeaderRowToCSV() {
1519 // Add header row, control fields will be reduced inside addToCSV()
1520 $this->addToCSV(array_combine($this->fieldArray, $this->fieldArray));
1521 }
1522
1523 /**
1524 * Adds selected columns of one table row as CSV line.
1525 *
1526 * @param array $row Record array, from which the values of fields found in $this->fieldArray will be listed in the CSV output.
1527 * @return void
1528 */
1529 protected function addToCSV(array $row = array()) {
1530 $rowReducedByControlFields = self::removeControlFieldsFromFieldRow($row);
1531 $rowReducedToSelectedColumns = array_intersect_key($rowReducedByControlFields, array_flip($this->fieldArray));
1532 $this->setCsvRow($rowReducedToSelectedColumns);
1533 }
1534
1535 /**
1536 * Remove control fields from row for CSV export
1537 *
1538 * @param array $row fieldNames => fieldValues
1539 * @return array Input array reduces by control fields
1540 */
1541 static protected function removeControlFieldsFromFieldRow(array $row = array()) {
1542 // Possible control fields in a list row
1543 $controlFields = array(
1544 '_PATH_',
1545 '_REF_',
1546 '_CONTROL_',
1547 '_AFTERCONTROL_',
1548 '_AFTERREF_',
1549 '_CLIPBOARD_',
1550 '_LOCALIZATION_',
1551 '_LOCALIZATION_b'
1552 );
1553 return array_diff_key($row, array_flip($controlFields));
1554 }
1555
1556 /**
1557 * Adds input row of values to the internal csvLines array as a CSV formatted line
1558 *
1559 * @param array $csvRow Array with values to be listed.
1560 * @return void
1561 * @todo Define visibility
1562 */
1563 public function setCsvRow($csvRow) {
1564 $this->csvLines[] = GeneralUtility::csvValues($csvRow);
1565 }
1566
1567 /**
1568 * Compiles the internal csvLines array to a csv-string and outputs it to the browser.
1569 * This function exits!
1570 *
1571 * @param string $prefix Filename prefix:
1572 * @return void EXITS php execusion!
1573 * @todo Define visibility
1574 */
1575 public function outputCSV($prefix) {
1576 // Setting filename:
1577 $filename = $prefix . '_' . date('dmy-Hi') . '.csv';
1578 // Creating output header:
1579 header('Content-Type: application/octet-stream');
1580 header('Content-Disposition: attachment; filename=' . $filename);
1581 // Cache-Control header is needed here to solve an issue with browser IE and
1582 // versions lower then 9. See for more information: http://support.microsoft.com/kb/323308
1583 header("Cache-Control: ''");
1584 // Printing the content of the CSV lines:
1585 echo implode(chr(13) . chr(10), $this->csvLines);
1586 // Exits:
1587 die;
1588 }
1589
1590 }