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