7607db86dfeae3c6cef891f57b79f20ab66d4a2e
[Packages/TYPO3.CMS.git] / typo3 / mod / user / ws / class.wslib_gui.php
1 <?php
2 /***************************************************************
3 * Copyright notice
4 *
5 * (c) 2005-2010 Kasper Skaarhoj (kasperYYYY@typo3.com)
6 * All rights reserved
7 *
8 * This script is part of the TYPO3 project. The TYPO3 project is
9 * free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
13 *
14 * The GNU General Public License can be found at
15 * http://www.gnu.org/copyleft/gpl.html.
16 * A copy is found in the textfile GPL.txt and important notices to the license
17 * from the author is found in LICENSE.txt distributed with these scripts.
18 *
19 *
20 * This script is distributed in the hope that it will be useful,
21 * but WITHOUT ANY WARRANTY; without even the implied warranty of
22 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23 * GNU General Public License for more details.
24 *
25 * This copyright notice MUST APPEAR in all copies of the script!
26 ***************************************************************/
27 /**
28 * Library with Workspace GUI related functionality. It is used by main workspace
29 * module but also can be used from extensions. Originally 99.9%% of the code
30 * was written by Kasper and only transfered here by Dmitry.
31 *
32 * $Id$
33 *
34 * @author Kasper Skaarhoj <kasperYYYY@typo3.com>
35 * @author Dmitry Dulepov <dmitry@typo3.org>
36 */
37 /**
38 * [CLASS/FUNCTION INDEX of SCRIPT]
39 *
40 *
41 *
42 * 84: class wslib_gui
43 *
44 * SECTION: Public functions
45 * 128: function getWorkspaceOverview(&$doc, $wsid = null, $filter = 0, $pageId = -1)
46 * 192: function hlSubelements(origId, verId, over, diffLayer)
47 *
48 * SECTION: Private functions (do not use outside of this class!)
49 * 224: function initVars()
50 * 253: function displayWorkspaceOverview_setInPageArray(&$pArray, $rlArr, $table, $row)
51 * 284: function markupNewOriginals()
52 * 309: function displayWorkspaceOverview_list($pArray, $tableRows=array(), $c=0, $warnAboutVersions=FALSE)
53 * 504: function displayWorkspaceOverview_pageTreeIconTitle($pageUid, $title, $indentCount)
54 * 518: function formatVerId($verId)
55 * 529: function formatWorkspace($wsid)
56 * 559: function createDiffView($table, $diff_1_record, $diff_2_record)
57 * 676: function versionsInOtherWS($table, $uid)
58 * 705: function showStageChangeLog($table,$id,$stageCommands)
59 * 757: function displayWorkspaceOverview_commandLinks($table,&$rec_on,&$rec_off,$vType)
60 * 830: function formatCount($count)
61 * 860: function subElements($uid,$treeLevel,$origId=0)
62 * 963: function subElements_getNonPageRecords($tN, $uid, &$recList)
63 * 993: function subElements_renderItem(&$tCell,$tN,$uid,$rec,$origId,$iconMode,$HTMLdata)
64 * 1066: function displayWorkspaceOverview_commandLinksSub($table,$rec,$origId)
65 * 1113: function displayWorkspaceOverview_stageCmd($table,&$rec_off)
66 *
67 * TOTAL FUNCTIONS: 19
68 * (This index is automatically created/updated by the extension "extdeveval")
69 *
70 */
71
72 require_once(PATH_typo3 . 'mod/user/ws/class.wslib.php');
73 $LANG->includeLLFile('EXT:lang/locallang_mod_user_ws.xml');
74 $LANG->includeLLFile('EXT:lang/locallang_misc.xml');
75
76 /**
77 * Library with Workspace GUI related functionality. It is used by main workspace
78 * module but also can be used from extensions. Originally 99.9%% of the code
79 * was written by Kasper and only transfered here by Dmitry.
80 *
81 * @author Kasper Skaarhoj <kasperYYYY@typo3.com>
82 * @author Dmitry Dulepov <dmitry@typo3.org>
83 * @package TYPO3
84 * @subpackage core
85 */
86 class wslib_gui {
87
88 // Static:
89 var $pageTreeIndent = 8;
90 var $pageTreeIndent_titleLgd = 30;
91
92 // Options
93 var $diff = false;
94 var $expandSubElements = false;
95 var $alwaysDisplayHeader = false;
96
97 // Internal
98 var $showWorkspaceCol = 0;
99 var $doc;
100 var $formatWorkspace_cache = array();
101 var $targets = array(); // Accumulation of online targets.
102 var $be_user_Array = array();
103 var $pageModule = '';
104 var $formatCount_cache = array();
105 var $stageIndex = array();
106 var $addHlSubelementsScript = false;
107
108 /*********************************
109 *
110 * Public functions
111 *
112 *********************************/
113
114 /**
115 * Creates HTML to display workspace overview. Can be used to display overview for all possible records or only for single page.
116 *
117 * The following code is <strong>required</strong> in BE module when this function is used:
118 * <code>
119 * $this->doc->getContextMenuCode();
120 * </code>
121 * or click-menu will not be generated properly!
122 *
123 * @param object $doc Document (to use for formatting)
124 * @param int $wsid Workspace ID, If <code>null</code>, the value is obtained from current BE user
125 * @param int $filter If 0, than no filtering, if 10 than select for publishing, otherwise stage value
126 * @param int $pageId If greater than zero, than it is UID of page in LIVE workspaces to select records for
127 * @return string Generated HTML
128 */
129 function getWorkspaceOverview(&$doc, $wsid = null, $filter = 0, $pageId = -1) {
130 global $LANG;
131
132 // Setup
133 $this->workspaceId = (!is_null($wsid) ? $wsid : $GLOBALS['BE_USER']->workspace);
134 $this->showWorkspaceCol = $GLOBALS['BE_USER']->workspace == 0 && $this->workspaceId <= -98;
135 $this->doc = $doc;
136 $this->initVars();
137
138 // Initialize workspace object and request all pending versions:
139 $wslibObj = t3lib_div::makeInstance('wslib');
140
141 // Selecting ALL versions belonging to the workspace:
142 $versions = $wslibObj->selectVersionsInWorkspace($this->workspaceId, $filter, -99, $pageId);
143
144 // Traverse versions and build page-display array:
145 $pArray = array();
146 $wmArray = array(); // is page in web mount?
147 $rlArray = array(); // root line of page
148 $pagePermsClause = $GLOBALS['BE_USER']->getPagePermsClause(1);
149 foreach($versions as $table => $records) {
150 if (is_array($records)) {
151 foreach($records as $rec) {
152 $pageIdField = $table==='pages' ? 't3ver_oid' : 'realpid';
153 $recPageId = $rec[$pageIdField];
154 if (!isset($wmArray[$recPageId])) {
155 $wmArray[$recPageId] = $GLOBALS['BE_USER']->isInWebMount($recPageId,$pagePermsClause);
156 }
157 if ($wmArray[$recPageId]) {
158 if (!isset($rlArray[$recPageId])) {
159 $rlArray[$recPageId] = t3lib_BEfunc::BEgetRootLine($recPageId, 'AND 1=1');
160 }
161 $this->displayWorkspaceOverview_setInPageArray(
162 $pArray,
163 $rlArray[$recPageId],
164 $table,
165 $rec
166 );
167 }
168 }
169 }
170 }
171
172 // Page-browser:
173 $resultsPerPage = 50;
174 $pointer = t3lib_div::_GP('browsePointer');
175 $browseStat = $this->cropWorkspaceOverview_list($pArray,$pointer,$resultsPerPage);
176 $browse = '';
177 $browse .= 'Showing ' . $browseStat['begin'] . ' to ' . ($browseStat['end'] ? $browseStat['end'] . ' out of ' . $browseStat['allItems'] : $browseStat['allItems']) . ' versions:<br />';
178 if (!($browseStat['begin']==1 && !$browseStat['end'])) {
179 for($a=0;$a<ceil($browseStat['allItems']/$resultsPerPage);$a++) {
180 $browse.=($a==(int)$pointer?'<strong>':'').'<a href="'.htmlspecialchars('index.php?browsePointer='.rawurlencode($a)).'">['.($a+1).']</a>'.($a==(int)$pointer?'</strong>':'').' ';
181 }
182 $browse.= '<br />';
183 }
184 $browse.= '<br />';
185
186 $workspaceOverviewList = $this->displayWorkspaceOverview_list($pArray);
187 if ($workspaceOverviewList || $this->alwaysDisplayHeader) {
188 // Make header of overview:
189 $tableRows = array();
190 $tableHeader = '
191 <tr class="bgColor5 tableheader">
192 <td nowrap="nowrap" width="100">' . $LANG->getLL('label_pagetree') . '</td>
193 <td nowrap="nowrap" colspan="2">' . $LANG->getLL('label_live_version') . '</td>
194 <td nowrap="nowrap" colspan="2">' . $LANG->getLL('label_draft_versions') . '</td>
195 <td nowrap="nowrap">' . $LANG->getLL('label_stage') . '</td>
196 <td nowrap="nowrap">' . $LANG->getLL('label_publish') . '</td>
197 <td><select name="_with_selected_do" onchange="if (confirm(\'' . $LANG->getLL('submit_apply_action_on_selected_elements') . '\')) {document.forms[0].submit();}">
198 <option value="_">' . $LANG->getLL('label_doaction_default') . '</option>';
199
200 if ($this->publishAccess && !($GLOBALS['BE_USER']->workspaceRec['publish_access'] & 1)) {
201 $tableHeader .= '<option value="publish">' . $LANG->getLL('label_doaction_publish') . '</option>';
202 if ($GLOBALS['BE_USER']->workspaceSwapAccess()) {
203 $tableHeader .= '<option value="swap">' . $LANG->getLL('label_doaction_swap') . '</option>';
204 }
205 }
206 if ($GLOBALS['BE_USER']->workspace !== 0) {
207 $tableHeader .= '<option value="release">' . $LANG->getLL('label_doaction_release') . '</option>';
208 }
209 $tableHeader .= $GLOBALS['BE_USER']->workspaceCheckStageForCurrent('-1') ? '<option value="stage_-1">' . $LANG->getLL('label_doaction_stage_reject') . '</option>' : '';
210 $tableHeader .= $GLOBALS['BE_USER']->workspaceCheckStageForCurrent('0') ? '<option value="stage_0">' . $LANG->getLL('label_doaction_stage_editing') . '</option>' : '';
211 $tableHeader .= $GLOBALS['BE_USER']->workspaceCheckStageForCurrent('1') ? '<option value="stage_1">' . $LANG->getLL('label_doaction_stage_review') . '</option>' : '';
212 $tableHeader .= $GLOBALS['BE_USER']->workspaceCheckStageForCurrent('10') ? '<option value="stage_10">' . $LANG->getLL('label_doaction_stage_publish') . '</option>' : '';
213
214 $tableHeader .= '<option value="flush">' . $LANG->getLL('label_doaction_flush') . '</option>
215 </select></td>
216 <td>' . $LANG->getLL('label_lifecycle') . '</td>
217 '.($this->showWorkspaceCol ? '<td>' . $LANG->getLL('label_workspace') . '</td>' : '').'
218 </tr>';
219 $tableRows[] = $tableHeader;
220
221 // Add lines from overview:
222 $tableRows = array_merge($tableRows, $workspaceOverviewList);
223
224 $table = '<table border="0" cellpadding="0" cellspacing="1" class="lrPadding workspace-overview">'.implode('',$tableRows).'</table>';
225
226 // script
227 if ($this->addHlSubelementsScript && !strstr($this->doc->JScode, 'function hlSubelements(')) {
228 $table = $this->doc->wrapScriptTags('
229 function hlSubelements(origId, verId, over, diffLayer) {
230 if (over) {
231 document.getElementById(\'orig_\'+origId).attributes.getNamedItem("class").nodeValue = \'typo3-ver-hl\';
232 document.getElementById(\'ver_\'+verId).attributes.getNamedItem("class").nodeValue = \'typo3-ver-hl\';
233 if (diffLayer) {
234 document.getElementById(\'diff_\'+verId).style.visibility = \'visible\';
235 }
236 } else {
237 document.getElementById(\'orig_\'+origId).attributes.getNamedItem("class").nodeValue = \'typo3-ver\';
238 document.getElementById(\'ver_\'+verId).attributes.getNamedItem("class").nodeValue = \'typo3-ver\';
239 if (diffLayer) {
240 document.getElementById(\'diff_\'+verId).style.visibility = \'hidden\';
241 }
242 }
243 }
244 ') . $table;
245 }
246
247 return $browse . $table . $this->markupNewOriginals();
248 }
249 return '';
250 }
251
252 /*********************************
253 *
254 * Private functions (do not use outside of this class!)
255 *
256 *********************************/
257
258 /**
259 * Initializes several class variables
260 *
261 * @return void
262 */
263 function initVars() {
264 // Init users
265 $be_group_Array = t3lib_BEfunc::getListGroupNames('title,uid');
266 $groupArray = array_keys($be_group_Array);
267 // Need 'admin' field for t3lib_iconWorks::getIconImage()
268 $this->be_user_Array = t3lib_BEfunc::getUserNames('username,usergroup,usergroup_cached_list,uid,admin,workspace_perms');
269 if (!$GLOBALS['BE_USER']->isAdmin()) {
270 $this->be_user_Array = t3lib_BEfunc::blindUserNames($this->be_user_Array,$groupArray,1);
271 }
272
273 // If another page module was specified, replace the default Page module with the new one
274 $newPageModule = trim($GLOBALS['BE_USER']->getTSConfigVal('options.overridePageModule'));
275 $this->pageModule = t3lib_BEfunc::isModuleSetInTBE_MODULES($newPageModule) ? $newPageModule : 'web_layout';
276
277 // Setting publish access permission for workspace:
278 $this->publishAccess = $GLOBALS['BE_USER']->workspacePublishAccess($GLOBALS['BE_USER']->workspace); // FIXME Should be $this->workspaceId here?
279 }
280
281 /**
282 * Building up of the $pArray variable which is a hierarchical storage of table-rows arranged according to the level in the rootline the element was found
283 * (Internal)
284 * Made for recursive calling
285 *
286 * @param array Array that is built up with the page tree structure
287 * @param array Root line for element (table / row); The element is stored in pArray according to this root line.
288 * @param string Table name
289 * @param array Table row
290 * @return void $pArray is passed by reference and modified internally
291 */
292 function displayWorkspaceOverview_setInPageArray(&$pArray, $rlArr, $table, $row) {
293 // Initialize:
294 ksort($rlArr);
295 reset($rlArr);
296 if (!$rlArr[0]['uid']) {
297 array_shift($rlArr);
298 }
299
300 // Get and remove first element in root line:
301 $cEl = current($rlArr);
302 $pUid = $cEl['t3ver_oid'] ? $cEl['t3ver_oid'] : $cEl['uid']; // Done to pile up "false versions" in the right branch...
303
304 $pArray[$pUid] = $cEl['title'];
305 array_shift($rlArr);
306
307 // If there are elements left in the root line, call this function recursively (to build $pArray in depth)
308 if (count($rlArr)) {
309 if (!isset($pArray[$pUid.'.'])) {
310 $pArray[$pUid.'.'] = array();
311 }
312 $this->displayWorkspaceOverview_setInPageArray($pArray[$pUid.'.'], $rlArr, $table, $row);
313 } else { // If this was the last element, set the value:
314 $pArray[$pUid.'_'][$table][$row['t3ver_oid']][] = $row;
315 }
316 }
317
318 /**
319 * JavaScript code to mark up new records that are online (in sub element lists)
320 *
321 * @return string HTML javascript section
322 */
323 function markupNewOriginals() {
324
325 if (count($this->targets)) {
326 $scriptCode = '';
327 foreach($this->targets as $key => $rec) {
328 $scriptCode.='
329 document.getElementById(\''.$key.'\').attributes.getNamedItem("class").nodeValue = \'typo3-ver-new\';
330 ';
331 }
332
333 return $this->doc->wrapScriptTags($scriptCode);
334 }
335 }
336
337
338 /**
339 * Rendering the content for the publish / review overview:
340 * (Made for internal recursive calling)
341 *
342 * @param array Hierarchical storage of the elements to display (see displayWorkspaceOverview() / displayWorkspaceOverview_setInPageArray())
343 * @param array Existing array of table rows to add to
344 * @param array Depth counter
345 * @param boolean If set, a warning is shown if versions are found (internal flag)
346 * @return array Table rows, see displayWorkspaceOverview()
347 */
348 function displayWorkspaceOverview_list($pArray, $tableRows=array(), $c=0, $warnAboutVersions=FALSE) {
349 global $TCA, $LANG;
350
351 // Initialize:
352 $fullColSpan = $this->showWorkspaceCol ? 10 : 9;
353
354 // Traverse $pArray
355 if (is_array($pArray)) {
356 foreach($pArray as $k => $v) {
357 if (t3lib_div::testInt($k)) {
358
359 // If there are elements on this level, output a divider row which just creates a little visual space.
360 if (is_array($pArray[$k.'_'])) {
361 $tableRows[] = '
362 <tr>
363 <td colspan="'.$fullColSpan.'"><img src="clear.gif" width="1" height="3" alt="" /></td>
364 </tr>';
365 }
366
367 // Printing a level from the page tree with no additional content:
368 // If there are NO elements on this level OR if there are NO pages on a level with elements, then show page tree icon and title (otherwise it is shown with the display of the elements)
369 if (!is_array($pArray[$k.'_']) || !is_array($pArray[$k.'_']['pages'])) {
370 $tableRows[] = '
371 <tr class="bgColor4-20">
372 <td nowrap="nowrap" colspan="'.$fullColSpan.'">'.
373 $this->displayWorkspaceOverview_pageTreeIconTitle($k,$pArray[$k],$c).
374 '</td>
375 </tr>';
376 }
377
378 // If there ARE elements on this level, print them:
379 $warnAboutVersions_next = $warnAboutVersions;
380 $warnAboutVersions_nonPages = FALSE;
381 $warnAboutVersions_page = FALSE;
382 if (is_array($pArray[$k.'_'])) {
383 foreach($pArray[$k.'_'] as $table => $oidArray) {
384 foreach($oidArray as $oid => $recs) {
385
386 // Get CURRENT online record and icon based on "t3ver_oid":
387 $rec_on = t3lib_BEfunc::getRecord($table,$oid);
388 $icon = t3lib_iconWorks::getIconImage($table, $rec_on, $this->doc->backPath,' align="top" title="'.t3lib_BEfunc::getRecordIconAltText($rec_on,$table).'"');
389 if ($GLOBALS['BE_USER']->workspace===0) { // Only edit online records if in ONLINE workspace:
390 $icon = $this->doc->wrapClickMenuOnIcon($icon, $table, $rec_on['uid'], 2, '', '+edit,view,info,delete');
391 }
392
393 // MAIN CELL / Online version display:
394 // Create the main cells which will span over the number of versions there is. If the table is "pages" then it will show the page tree icon and title (which was not shown by the code above)
395 $verLinkUrl = t3lib_extMgm::isLoaded('version') && $TCA[$table]['ctrl']['versioningWS'];
396 $origElement = $icon.
397 ($verLinkUrl ? '<a href="'.htmlspecialchars($this->doc->backPath.t3lib_extMgm::extRelPath('version').'cm1/index.php?table='.$table.'&uid='.$rec_on['uid']).'">' : '').
398 t3lib_BEfunc::getRecordTitle($table,$rec_on,TRUE).
399 ($verLinkUrl ? '</a>' : '');
400 $mainCell_rowSpan = count($recs)>1 ? ' rowspan="'.count($recs).'"' : '';
401 $mainCell = $table==='pages' ? '
402 <td class="bgColor4-20" nowrap="nowrap"'.$mainCell_rowSpan.'>'.
403 $this->displayWorkspaceOverview_pageTreeIconTitle($k,$pArray[$k],$c).
404 '</td>' : '
405 <td class="bgColor"'.$mainCell_rowSpan.'></td>';
406 $mainCell.= '
407 <td align="center"'.$mainCell_rowSpan.'>'.$this->formatVerId($rec_on['t3ver_id']).'</td>
408 <td nowrap="nowrap"'.$mainCell_rowSpan.'>'.
409 $origElement.
410 '###SUB_ELEMENTS###'. // For substitution with sub-elements, if any.
411 '</td>';
412
413 // Offline versions display:
414 // Traverse the versions of the element
415 foreach($recs as $rec) {
416
417 // Get the offline version record:
418 $rec_off = t3lib_BEfunc::getRecord($table,$rec['uid']);
419
420 // Prepare swap-mode values:
421 if ($table==='pages' && $rec_off['t3ver_swapmode']!=-1) {
422 if ($rec_off['t3ver_swapmode']>0) {
423 $vType = 'branch'; // Do not translate!
424 } else {
425 $vType = 'page'; // Do not translate!
426 }
427 } else {
428 $vType = 'element'; // Do not translate!
429 }
430
431 // Get icon:
432 $icon = t3lib_iconWorks::getIconImage($table, $rec_off, $this->doc->backPath, ' align="top" title="'.t3lib_BEfunc::getRecordIconAltText($rec_off,$table).'"');
433 $tempUid = ($table != 'pages' || $vType==='branch' || $GLOBALS['BE_USER']->workspace == 0 ? $rec_off['uid'] : $rec_on['uid']);
434 $icon = $this->doc->wrapClickMenuOnIcon($icon, $table, $tempUid, 2, '', '+edit,' . ($table == 'pages' ? 'view,info,' : '') . 'delete');
435
436 // Prepare diff-code:
437 if ($this->diff) {
438 $diffCode = '';
439 list($diffHTML,$diffPct) = $this->createDiffView($table, $rec_off, $rec_on);
440 if ($rec_on['t3ver_state']==1) { // New record:
441 $diffCode.= $this->doc->icons(1) . $LANG->getLL('label_newrecord') . '<br />';
442 $diffCode.= $diffHTML;
443 } elseif ($rec_off['t3ver_state']==2) {
444 $diffCode.= $this->doc->icons(2) . $LANG->getLL('label_deletedrecord') . '<br/>';
445 } elseif ($rec_on['t3ver_state']==3) {
446 $diffCode.= $this->doc->icons(1) . $LANG->getLL('label_moveto_placeholder') . '<br/>';
447 } elseif ($rec_off['t3ver_state']==4) {
448 $diffCode.= $this->doc->icons(1) . $LANG->getLL('label_moveto_pointer') . '<br/>';
449 } else {
450 $diffCode.= ($diffPct<0 ? 'N/A' : ($diffPct ? $diffPct.'% change:' : ''));
451 $diffCode.= ($diffPct<0 ? $LANG->getLL('label_notapplicable') : ($diffPct ? sprintf($LANG->getLL('label_percentchange'), $diffPct) : ''));
452 $diffCode.= $diffHTML;
453 }
454
455 } else $diffCode = '';
456
457 switch($vType) {
458 case 'element':
459 $swapLabel = ' ['.$LANG->getLL('label_element').']';
460 $swapClass = 'ver-element'; // Do not translate!
461 $warnAboutVersions_nonPages = $warnAboutVersions_page; // Setting this if sub elements are found with a page+content (must be rendered prior to this of course!)
462 break;
463 case 'page':
464 $swapLabel = ' ['.$LANG->getLL('label_page').']';
465 $swapClass = 'ver-page'; // Do not translate!
466 $warnAboutVersions_page = !$this->showWorkspaceCol; // This value is true only if multiple workspaces are shown and we need the opposite here.
467 break;
468 case 'branch':
469 $swapLabel = ' ['.$LANG->getLL('label_branch').']';
470 $swapClass = 'ver-branch'; // Do not translate!
471 $warnAboutVersions_next = !$this->showWorkspaceCol; // This value is true only if multiple workspaces are shown and we need the opposite here.
472 break;
473 }
474
475 // Modify main cell based on first version shown:
476 $subElements = array();
477 if ($table==='pages' && $rec_off['t3ver_swapmode']!=-1 && $mainCell) { // For "Page" and "Branch" swap modes where $mainCell is still carrying content (only first version)
478 $subElements['on'] = $this->subElements($rec_on['uid'], $rec_off['t3ver_swapmode']);
479 $subElements['off'] = $this->subElements($rec_off['uid'],$rec_off['t3ver_swapmode'],$rec_on['uid']);
480 }
481 $mainCell = str_replace('###SUB_ELEMENTS###', $subElements['on'], $mainCell);
482
483 // Create version element:
484 $versionsInOtherWS = $this->versionsInOtherWS($table, $rec_on['uid']);
485 $versionsInOtherWSWarning = $versionsInOtherWS && $GLOBALS['BE_USER']->workspace !== 0 ? '<br />' . $this->doc->icons(2) . $LANG->getLL('label_otherversions') . ' ' . $versionsInOtherWS : '';
486 $multipleWarning = (!$mainCell && $GLOBALS['BE_USER']->workspace !==0 ? '<br />' . $this->doc->icons(3) . '<strong>' . $LANG->getLL('label_multipleversions') . '</strong>' : '');
487 $verWarning = $warnAboutVersions || ($warnAboutVersions_nonPages && $GLOBALS['TCA'][$table]['ctrl']['versioning_followPages']) ? '<br />' . $this->doc->icons(3) . '<strong>' . $LANG->getLL('label_nestedversions') . '</strong>' : '';
488 $verElement = $icon.
489 '<a href="'.htmlspecialchars($this->doc->backPath.t3lib_extMgm::extRelPath('version').'cm1/index.php?id='.($table==='pages'?$rec_on['uid']:$rec_on['pid']).'&details='.rawurlencode($table.':'.$rec_off['uid']).'&returnUrl='.rawurlencode(t3lib_div::getIndpEnv('REQUEST_URI'))).'">'.
490 t3lib_BEfunc::getRecordTitle($table,$rec_off,TRUE).
491 '</a>'.
492 $versionsInOtherWSWarning.
493 $multipleWarning.
494 $verWarning;
495 if ($diffCode) {
496 $verElement = '
497 <table border="0" cellpadding="0" cellspacing="0" class="ver-verElement">
498 <tr>
499 <td nowrap="nowrap" width="180">'.$verElement.'&nbsp;&nbsp;</td>
500 <td class="c-diffCell">'.$diffCode.'</td>
501 </tr>
502 </table>';
503 }
504
505 // Create version cell:
506 $verCell = '
507 <td align="center">'.$this->formatVerId($rec_off['t3ver_id']).'</td>
508 <td nowrap="nowrap">'.
509 $verElement.
510 $subElements['off'].
511 '</td>';
512
513 // Compile table row:
514 $tableRows[] = '
515 <tr class="bgColor4">
516 '.$mainCell.$verCell.'
517 <td nowrap="nowrap">'.$this->showStageChangeLog($table,$rec_off['uid'],$this->displayWorkspaceOverview_stageCmd($table,$rec_off)).'</td>
518 <td nowrap="nowrap" class="'.$swapClass.'">'.
519 $this->displayWorkspaceOverview_commandLinks($table,$rec_on,$rec_off,$vType).
520 htmlspecialchars($swapLabel).
521 '</td>
522 <td nowrap="nowrap" align="center"><input type="checkbox" name="items['.$table.':'.$rec_off['uid'].']" id="items['.$table.':'.$rec_off['uid'].']" value="1"/></td>
523 <td nowrap="nowrap">'.htmlspecialchars($this->formatCount($rec_off['t3ver_count'])).'</td>'. // Lifecycle
524 ($this->showWorkspaceCol ? '
525 <td nowrap="nowrap">'.htmlspecialchars($this->formatWorkspace($rec_off['t3ver_wsid'])).'</td>' : '').'
526 </tr>';
527
528 // Reset the main cell:
529 $mainCell = '';
530 }
531 }
532 }
533 }
534 // Call recursively for sub-rows:
535 $tableRows = $this->displayWorkspaceOverview_list($pArray[$k.'.'], $tableRows, $c+1, $warnAboutVersions_next);
536 }
537 }
538 }
539 return $tableRows;
540 }
541
542 /**
543 * Filtering out items in pArray according to pointer and result-per-page setting
544 *
545 * @param array Hierarchical storage of the elements to display (see displayWorkspaceOverview() / displayWorkspaceOverview_setInPageArray())
546 * @return array Returns statistics about the pointer state.
547 */
548 function cropWorkspaceOverview_list(&$pArray,$pointer=0,$resPerPage=50,$stat=array()) {
549
550 // Traverse $pArray
551 if (is_array($pArray)) {
552 foreach($pArray as $k => $v) {
553 if (t3lib_div::testInt($k)) {
554
555 if (is_array($pArray[$k.'_'])) {
556 foreach($pArray[$k.'_'] as $table => $oidArray) {
557 foreach($oidArray as $oid => $recs) {
558
559 // Check, if the item count has reached the point where we want to set the in-point.
560 $beginWasSet = FALSE;
561 if (!isset($stat['begin']) && (int)$stat['allItems'] >= $pointer*$resPerPage) {
562 $stat['begin']=(int)$stat['allItems']+1;
563 $beginWasSet = TRUE;
564 }
565
566 // If in-point is not set, unset the previous items.
567 if (!isset($stat['begin'])) {
568 unset($pArray[$k.'_'][$table][$oid]);
569 }
570
571 // Increase counter:
572 $stat['allItems']+=count($recs);
573
574 // Check if end-point is reached:
575 if (!$beginWasSet && !isset($stat['end']) && $stat['allItems'] > ($pointer+1)*$resPerPage) {
576 $stat['end']=$stat['allItems']-1;
577 }
578
579 // If end-point is reached, unset following items.
580 if (isset($stat['end'])) {
581 unset($pArray[$k.'_'][$table][$oid]);
582 }
583 }
584
585 // Clean-up if no more items:
586 if (!count($pArray[$k.'_'][$table])) {
587 unset($pArray[$k.'_'][$table]);
588 }
589 }
590
591 // Clean-up if no more items:
592 if (!count($pArray[$k.'_'])) {
593 unset($pArray[$k.'_']);
594 }
595 }
596
597 // Call recursively for sub-rows:
598 if (is_array($pArray[$k.'.'])) {
599 $stat = $this->cropWorkspaceOverview_list($pArray[$k.'.'],$pointer,$resPerPage,$stat);
600 }
601 }
602 }
603 }
604 return $stat;
605 }
606
607 /**
608 * Create indentation, icon and title for the page tree identification for the list.
609 *
610 * @param integer Page UID (record will be looked up again)
611 * @param string Page title
612 * @param integer Depth counter from displayWorkspaceOverview_list() used to indent the icon and title
613 * @return string HTML content
614 */
615 function displayWorkspaceOverview_pageTreeIconTitle($pageUid, $title, $indentCount) {
616 $pRec = t3lib_BEfunc::getRecord('pages',$pageUid);
617 return '<img src="clear.gif" width="1" height="1" hspace="'.($indentCount * $this->pageTreeIndent).'" align="top" alt="" />'. // Indenting page tree
618 t3lib_iconWorks::getIconImage('pages',$pRec,$this->doc->backPath,' align="top" title="'.t3lib_BEfunc::getRecordIconAltText($pRec,'pages').'"').
619 htmlspecialchars(t3lib_div::fixed_lgd_cs($title,$this->pageTreeIndent_titleLgd)).
620 '&nbsp;&nbsp;';
621 }
622
623 /**
624 * Formatting the version number for HTML output
625 *
626 * @param integer Version number
627 * @return string Version number for output
628 */
629 function formatVerId($verId) {
630 return '1.'.$verId;
631 }
632
633
634 /**
635 * Formatting workspace ID into a visual label
636 *
637 * @param integer Workspace ID
638 * @return string Workspace title
639 */
640 function formatWorkspace($wsid) {
641
642 // Render, if not cached:
643 if (!isset($this->formatWorkspace_cache[$wsid])) {
644 switch($wsid) {
645 case -1:
646 $this->formatWorkspace_cache[$wsid] = '[Draft]';
647 break;
648 case 0:
649 $this->formatWorkspace_cache[$wsid] = ''; // Does not output anything for ONLINE because it might confuse people to think that the elemnet IS online which is not the case - only that it exists as an offline version in the online workspace...
650 break;
651 default:
652 list($titleRec) = $GLOBALS['TYPO3_DB']->exec_SELECTgetRows('title','sys_workspace','uid='.intval($wsid).t3lib_BEfunc::deleteClause('sys_workspace'));
653 $this->formatWorkspace_cache[$wsid] = '['.$wsid.'] '.$titleRec['title'];
654 break;
655 }
656 }
657
658 return $this->formatWorkspace_cache[$wsid];
659 }
660
661
662 /**
663 * Create visual difference view of two records. Using t3lib_diff library
664 *
665 * @param string Table name
666 * @param array New version record (green)
667 * @param array Old version record (red)
668 * @return array Array with two keys (0/1) with HTML content / percentage integer (if -1, then it means N/A) indicating amount of change
669 */
670 function createDiffView($table, $diff_1_record, $diff_2_record) {
671 global $TCA, $LANG;
672
673 // Initialize:
674 $pctChange = 'N/A';
675
676 // Check that records are arrays:
677 if (is_array($diff_1_record) && is_array($diff_2_record)) {
678
679 // Load full table description and initialize diff-object:
680 t3lib_div::loadTCA($table);
681 $t3lib_diff_Obj = t3lib_div::makeInstance('t3lib_diff');
682
683 // Add header row:
684 $tRows = array();
685 $tRows[] = '
686 <tr class="bgColor5 tableheader">
687 <td>' . $LANG->getLL('diffview_label_field_name') . '</td>
688 <td width="98%" nowrap="nowrap">' . $LANG->getLL('diffview_label_colored_diff_view') . '</td>
689 </tr>
690 ';
691
692 // Initialize variables to pick up string lengths in:
693 $allStrLen = 0;
694 $diffStrLen = 0;
695
696 // Traversing the first record and process all fields which are editable:
697 foreach($diff_1_record as $fN => $fV) {
698 if ($TCA[$table]['columns'][$fN] && $TCA[$table]['columns'][$fN]['config']['type']!='passthrough' && !t3lib_div::inList('t3ver_label',$fN)) {
699
700 // Check if it is files:
701 $isFiles = FALSE;
702 if (strcmp(trim($diff_1_record[$fN]),trim($diff_2_record[$fN])) &&
703 $TCA[$table]['columns'][$fN]['config']['type']=='group' &&
704 $TCA[$table]['columns'][$fN]['config']['internal_type']=='file') {
705
706 // Initialize:
707 $uploadFolder = $TCA[$table]['columns'][$fN]['config']['uploadfolder'];
708 $files1 = array_flip(t3lib_div::trimExplode(',', $diff_1_record[$fN],1));
709 $files2 = array_flip(t3lib_div::trimExplode(',', $diff_2_record[$fN],1));
710
711 // Traverse filenames and read their md5 sum:
712 foreach($files1 as $filename => $tmp) {
713 $files1[$filename] = @is_file(PATH_site.$uploadFolder.'/'.$filename) ? md5(t3lib_div::getUrl(PATH_site.$uploadFolder.'/'.$filename)) : $filename;
714 }
715 foreach($files2 as $filename => $tmp) {
716 $files2[$filename] = @is_file(PATH_site.$uploadFolder.'/'.$filename) ? md5(t3lib_div::getUrl(PATH_site.$uploadFolder.'/'.$filename)) : $filename;
717 }
718
719 // Implode MD5 sums and set flag:
720 $diff_1_record[$fN] = implode(' ',$files1);
721 $diff_2_record[$fN] = implode(' ',$files2);
722 $isFiles = TRUE;
723 }
724
725 // If there is a change of value:
726 if (strcmp(trim($diff_1_record[$fN]),trim($diff_2_record[$fN]))) {
727
728
729 // Get the best visual presentation of the value and present that:
730 $val1 = t3lib_BEfunc::getProcessedValue($table,$fN,$diff_2_record[$fN],0,1);
731 $val2 = t3lib_BEfunc::getProcessedValue($table,$fN,$diff_1_record[$fN],0,1);
732
733 // Make diff result and record string lenghts:
734 $diffres = $t3lib_diff_Obj->makeDiffDisplay($val1,$val2,$isFiles?'div':'span');
735 $diffStrLen+= $t3lib_diff_Obj->differenceLgd;
736 $allStrLen+= strlen($val1.$val2);
737
738 // If the compared values were files, substituted MD5 hashes:
739 if ($isFiles) {
740 $allFiles = array_merge($files1,$files2);
741 foreach($allFiles as $filename => $token) {
742 if (strlen($token)==32 && strstr($diffres,$token)) {
743 $filename =
744 t3lib_BEfunc::thumbCode(array($fN=>$filename),$table,$fN,$this->doc->backPath).
745 $filename;
746 $diffres = str_replace($token,$filename,$diffres);
747 }
748 }
749 }
750
751 // Add table row with result:
752 $tRows[] = '
753 <tr class="bgColor4">
754 <td>'.htmlspecialchars($GLOBALS['LANG']->sL(t3lib_BEfunc::getItemLabel($table,$fN))).'</td>
755 <td width="98%">'.$diffres.'</td>
756 </tr>
757 ';
758 } else {
759 // Add string lengths even if value matched - in this was the change percentage is not high if only a single field is changed:
760 $allStrLen+=strlen($diff_1_record[$fN].$diff_2_record[$fN]);
761 }
762 }
763 }
764
765 // Calculate final change percentage:
766 $pctChange = $allStrLen ? ceil($diffStrLen*100/$allStrLen) : -1;
767
768 // Create visual representation of result:
769 if (count($tRows)>1) {
770 $content.= '<table border="0" cellpadding="1" cellspacing="1" class="diffTable">'.implode('',$tRows).'</table>';
771 } else {
772 $content.= '<span class="nobr">'.$this->doc->icons(1).$LANG->getLL('diffview_complete_match').'</span>';
773 }
774 } else $content.= $this->doc->icons(3).$LANG->getLL('diffview_cannot_find_records');
775
776 // Return value:
777 return array($content,$pctChange);
778 }
779
780 /**
781 * Looking for versions of a record in other workspaces than the current
782 *
783 * @param string Table name
784 * @param integer Record uid
785 * @return string List of other workspace IDs
786 */
787 function versionsInOtherWS($table, $uid) {
788 // Check for duplicates:
789 // Select all versions of record NOT in this workspace:
790 $rows = $GLOBALS['TYPO3_DB']->exec_SELECTgetRows(
791 't3ver_wsid',
792 $table,
793 'pid=-1
794 AND t3ver_oid='.intval($uid).'
795 AND t3ver_wsid!='.intval($GLOBALS['BE_USER']->workspace).// TODO should be $this->workspaceId here???
796 ' AND (t3ver_wsid=-1 OR t3ver_wsid>0)'.
797 t3lib_BEfunc::deleteClause($table),
798 '',
799 't3ver_wsid',
800 '',
801 't3ver_wsid'
802 );
803 if (count($rows)) {
804 return implode(',',array_keys($rows));
805 }
806 }
807
808 /**
809 * Looks up stage changes for version and displays a formatted view on mouseover.
810 *
811 * @param string Table name
812 * @param integer Record ID
813 * @param string HTML string to wrap the mouseover around (should be stage change links)
814 * @return string HTML code.
815 */
816 function showStageChangeLog($table,$id,$stageCommands) {
817 global $LANG;
818
819 $rows = $GLOBALS['TYPO3_DB']->exec_SELECTgetRows(
820 'log_data,tstamp,userid',
821 'sys_log',
822 'action=6 and details_nr=30
823 AND tablename='.$GLOBALS['TYPO3_DB']->fullQuoteStr($table,'sys_log').'
824 AND recuid='.intval($id)
825 );
826
827 $entry = array();
828 foreach($rows as $dat) {
829 $data = unserialize($dat['log_data']);
830 $username = $this->be_user_Array[$dat['userid']] ? $this->be_user_Array[$dat['userid']]['username'] : '['.$dat['userid'].']';
831
832 switch($data['stage']) {
833 case 1:
834 $text = $LANG->getLL('stage_sent_to_review');
835 break;
836 case 10:
837 $text = $LANG->getLL('stage_approved_for_publish');
838 break;
839 case -1:
840 $text = $LANG->getLL('stage_rejected');
841 break;
842 case 0:
843 $text = $LANG->getLL('stage_reset_to_editing');
844 break;
845 default:
846 $text = $LANG->getLL('stage_undefined');
847 break;
848 }
849 $text = t3lib_BEfunc::datetime($dat['tstamp']).': ' . sprintf($text, htmlspecialchars($username));
850 $text.= ($data['comment'] ? '<br />' . $LANG->getLL('stage_label_user_comment') . ' <em>' . htmlspecialchars($data['comment']) . '</em>' : '');
851
852 $entry[] = $text;
853 }
854
855 return count($entry) ? '<span onmouseover="document.getElementById(\'log_'.$table.$id.'\').style.visibility = \'visible\';" onmouseout="document.getElementById(\'log_'.$table.$id.'\').style.visibility = \'hidden\';">'.$stageCommands.' ('.count($entry).')</span>'.
856 '<div class="logLayer" style="visibility: hidden; position: absolute;" id="log_'.$table.$id.'">'.implode('<hr/>',$entry).'</div>' : $stageCommands;
857 }
858
859 /**
860 * Links to publishing etc of a version
861 *
862 * @param string Table name
863 * @param array Online record
864 * @param array Offline record (version)
865 * @param string Swap type, "branch", "page" or "element"
866 * @return string HTML content, mainly link tags and images.
867 */
868 function displayWorkspaceOverview_commandLinks($table,&$rec_on,&$rec_off,$vType) {
869 global $LANG;
870
871 if ($this->publishAccess && (!($GLOBALS['BE_USER']->workspaceRec['publish_access']&1) || (int)$rec_off['t3ver_stage']===10)) {
872 $actionLinks =
873 '<a href="'.htmlspecialchars($this->doc->issueCommand(
874 '&cmd['.$table.']['.$rec_on['uid'].'][version][action]=swap'.
875 '&cmd['.$table.']['.$rec_on['uid'].'][version][swapWith]='.$rec_off['uid']
876 )).' " title="' . $LANG->getLL('img_title_publish') . '">'.
877 t3lib_iconWorks::getSpriteIcon('actions-version-swap-version') .
878 '</a>';
879 if ($GLOBALS['BE_USER']->workspaceSwapAccess()) {
880 $actionLinks.=
881 '<a href="'.htmlspecialchars($this->doc->issueCommand(
882 '&cmd['.$table.']['.$rec_on['uid'].'][version][action]=swap'.
883 '&cmd['.$table.']['.$rec_on['uid'].'][version][swapWith]='.$rec_off['uid'].
884 '&cmd['.$table.']['.$rec_on['uid'].'][version][swapIntoWS]=1'
885 )).'" title="' . $LANG->getLL('img_title_swap') . '">'.
886 t3lib_iconWorks::getSpriteIcon('actions-version-swap-workspace') .
887 '</a>';
888 }
889 }
890
891 if (!$GLOBALS['BE_USER']->workspaceCannotEditOfflineVersion($table,$rec_off)) {
892 if ($GLOBALS['BE_USER']->workspace!==0) {
893 // Release
894 $confirm = $LANG->JScharCode($LANG->getLL('remove_from_ws_confirmation'));
895 $actionLinks.=
896 '<a href="'.htmlspecialchars($this->doc->issueCommand('&cmd['.$table.']['.$rec_off['uid'].'][version][action]=clearWSID')).'" onclick="return confirm(' . $confirm . ');" title="' . $LANG->getLL('img_title_remove_from_ws') . '">'.
897 t3lib_iconWorks::getSpriteIcon('actions-version-document-remove') .
898 '</a>';
899 }
900
901 // Edit
902 if ($table==='pages' && $vType!=='element') {
903 $tempUid = ($vType==='branch' || $GLOBALS['BE_USER']->workspace===0 ? $rec_off['uid'] : $rec_on['uid']);
904 $actionLinks.=
905 '<a href="#" onclick="top.loadEditId('.$tempUid.');top.goToModule(\''.$this->pageModule.'\'); return false;" title="' . $LANG->getLL('img_title_edit_page') . '">'.
906 t3lib_iconWorks::getSpriteIcon('actions-page-open') .
907 '</a>';
908 } else {
909 $params = '&edit['.$table.']['.$rec_off['uid'].']=edit';
910 $actionLinks.=
911 '<a href="#" onclick="'.htmlspecialchars(t3lib_BEfunc::editOnClick($params,$this->doc->backPath)).'" title="' . $LANG->getLL('img_title_edit_element') . '">'.
912 t3lib_iconWorks::getSpriteIcon('actions-document-open') .
913 '</a>';
914 }
915 }
916
917 // History/Log
918 $actionLinks.=
919 '<a href="'.htmlspecialchars($this->doc->backPath.'show_rechis.php?element='.rawurlencode($table.':'.$rec_off['uid']).'&returnUrl='.rawurlencode(t3lib_div::getIndpEnv('REQUEST_URI'))).'" title="' . $LANG->getLL('img_title_show_log') . '">' .
920 t3lib_iconWorks::getSpriteIcon('actions-document-history-open') .
921 '</a>';
922
923 // View
924 if ($table==='pages') {
925 $tempUid = ($vType==='branch' || $GLOBALS['BE_USER']->workspace===0 ? $rec_off['uid'] : $rec_on['uid']);
926 $actionLinks.=
927 '<a href="#" onclick="'.htmlspecialchars(t3lib_BEfunc::viewOnClick($tempUid,$this->doc->backPath,t3lib_BEfunc::BEgetRootLine($tempUid))).'">'.
928 t3lib_iconWorks::getSpriteIcon('actions-document-view') .
929 '</a>';
930 }
931
932 return $actionLinks;
933 }
934
935 /**
936 * Format publishing count for version (lifecycle state)
937 *
938 * @param integer t3ver_count value (number of times it has been online)
939 * @return string String translation of count.
940 */
941 function formatCount($count) {
942 global $LANG;
943
944 // Render, if not cached:
945 if (!isset($this->formatCount_cache[$count])) {
946 switch($count) {
947 case 0:
948 $this->formatCount_cache[$count] = $LANG->getLL('workspace_list_publishing_count_draft');
949 break;
950 case 1:
951 $this->formatCount_cache[$count] = $LANG->getLL('workspace_list_publishing_count_archive');
952 break;
953 default:
954 $this->formatCount_cache[$count] = sprintf($LANG->getLL('workspace_list_publishing_count'), $count);
955 break;
956 }
957 }
958
959 return $this->formatCount_cache[$count];
960 }
961
962
963 /**
964 * Creates display of sub elements of a page when the swap mode is either "Page" or "Branch" (0 / ALL)
965 *
966 * @param integer Page uid (for either online or offline version, but it MUST have swapmode/treeLevel set to >0 (not -1 indicating element versioning)
967 * @param integer The treeLevel value, >0 indicates "branch" while 0 means page+content. (-1 would have meant element versioning, but that should never happen for a call to this function!)
968 * @param integer For offline versions; This is t3ver_oid, the original ID of the online page.
969 * @return string HTML content.
970 */
971 function subElements($uid,$treeLevel,$origId=0) {
972 global $TCA, $LANG;
973
974 if ($GLOBALS['BE_USER']->workspace===0 || !$this->expandSubElements) { // In online workspace we have a reduced view because otherwise it will bloat the listing:
975 return '<br />
976 <img'.t3lib_iconWorks::skinImg($this->doc->backPath,'gfx/ol/joinbottom.gif','width="18" height="16"').' align="top" alt="" title="" />'.
977 ($origId ?
978 '<a href="'.htmlspecialchars($this->doc->backPath.t3lib_extMgm::extRelPath('version').'cm1/index.php?id='.$uid.'&details='.rawurlencode('pages:'.$uid).'&returnUrl='.rawurlencode(t3lib_div::getIndpEnv('REQUEST_URI'))).'">'.
979 '<span class="typo3-dimmed"><em>['.$LANG->getLL('label_subelementsdetails').']</em><span></a>' :
980 '<span class="typo3-dimmed"><em>['.$LANG->getLL('label_subelements').']</em><span>');
981 } else { // For an offline workspace, show sub elements:
982
983 $tCell = array();
984
985 // Find records that follow pages when swapping versions:
986 $recList = array();
987 foreach($TCA as $tN => $tCfg) {
988 if ($tN!='pages' && ($treeLevel>0 || $TCA[$tN]['ctrl']['versioning_followPages'])) {
989 $this->subElements_getNonPageRecords($tN, $uid, $recList);
990 }
991 }
992
993 // Render records collected above:
994 $elCount = count($recList)-1;
995 foreach($recList as $c => $comb) {
996 list($tN,$rec) = $comb;
997
998 $this->subElements_renderItem(
999 $tCell,
1000 $tN,
1001 $uid,
1002 $rec,
1003 $origId,
1004 $c==$elCount && $treeLevel==0 ? 1 : 0, // If true, will show bottom-join icon.
1005 ''
1006 );
1007 }
1008
1009 // For branch, dive into the subtree:
1010 if ($treeLevel>0) {
1011
1012 // Drawing tree:
1013 $tree = t3lib_div::makeInstance('t3lib_pageTree');
1014 $tree->init('AND '.$GLOBALS['BE_USER']->getPagePermsClause(1));
1015 $tree->makeHTML = 2; // 2=Also rendering depth-data into the result array
1016 $tree->getTree($uid, 99, '');
1017
1018 // Traverse page tree:
1019 foreach($tree->tree as $data) {
1020
1021 // Render page in table cell:
1022 $this->subElements_renderItem(
1023 $tCell,
1024 'pages',
1025 $uid,
1026 t3lib_BEfunc::getRecord('pages',$data['row']['uid']), // Needs all fields, at least more than what is given in $data['row']...
1027 $origId,
1028 2, // 2=the join icon and icon for the record is not rendered for pages (where all is in $data['HTML']
1029 $data['HTML']
1030 );
1031
1032 // Find all records from page and collect in $recList:
1033 $recList = array();
1034 foreach($TCA as $tN => $tCfg) {
1035 if ($tN!=='pages') {
1036 $this->subElements_getNonPageRecords($tN, $data['row']['uid'], $recList);
1037 }
1038 }
1039
1040 // Render records collected above:
1041 $elCount = count($recList)-1;
1042 foreach($recList as $c => $comb) {
1043 list($tN,$rec) = $comb;
1044
1045 $this->subElements_renderItem(
1046 $tCell,
1047 $tN,
1048 $uid,
1049 $rec,
1050 $origId,
1051 $c==$elCount?1:0, // If true, will show bottom-join icon.
1052 $data['HTML_depthData']
1053 );
1054 }
1055 }
1056 }
1057
1058 return '
1059 <!-- Sub-element tree for versions -->
1060 <table border="0" cellpadding="0" cellspacing="1" class="ver-subtree">
1061 '.implode('',$tCell).'
1062 </table>';
1063 }
1064 }
1065
1066 /**
1067 * Select records from a table and add them to recList
1068 *
1069 * @param string Table name (from TCA)
1070 * @param integer PID to select records from
1071 * @param array Array where records are accumulated, passed by reference
1072 * @return void
1073 */
1074 function subElements_getNonPageRecords($tN, $uid, &$recList) {
1075 global $TCA;
1076
1077 $records = $GLOBALS['TYPO3_DB']->exec_SELECTgetRows(
1078 '*',
1079 $tN,
1080 'pid='.intval($uid).
1081 ($TCA[$tN]['ctrl']['versioningWS'] ? ' AND t3ver_state=0' : '').
1082 t3lib_BEfunc::deleteClause($tN),
1083 '',
1084 $TCA[$tN]['ctrl']['sortby'] ? $TCA[$tN]['ctrl']['sortby'] : $GLOBALS['TYPO3_DB']->stripOrderBy($TCA[$tN]['ctrl']['default_sortby'])
1085 );
1086
1087 foreach($records as $rec) {
1088 $recList[] = array($tN,$rec);
1089 }
1090 }
1091
1092 /**
1093 * Render a single item in a subelement list into a table row:
1094 *
1095 * @param array Table rows, passed by reference
1096 * @param string Table name
1097 * @param integer Page uid for which the subelements are selected/shown
1098 * @param array Row of element in list
1099 * @param integer The uid of the online version of $uid. If zero it means we are drawing a row for the online version itself while a value means we are drawing display for an offline version.
1100 * @param integer Mode of icon display: 0=not the last, 1= is the last in list (make joinbottom icon then), 2=do not shown icons are all (for pages from the page tree already rendered)
1101 * @param string Prefix HTML data (icons for tree rendering)
1102 * @return void (Content accumulated in $tCell!)
1103 */
1104 function subElements_renderItem(&$tCell,$tN,$uid,$rec,$origId,$iconMode,$HTMLdata) {
1105 global $TCA;
1106
1107 // Initialize:
1108 $origUidFields = $TCA[$tN]['ctrl']['origUid'];
1109 $diffCode = '';
1110
1111 if ($origUidFields) { // If there is a field for this table with original uids we will use that to connect records:
1112 if (!$origId) { // In case we are displaying the online originals:
1113 $this->targets['orig_'.$uid.'_'.$tN.'_'.$rec['uid']] = $rec; // Build up target array (important that
1114 $tdParams = ' id="orig_'.$uid.'_'.$tN.'_'.$rec['uid'].'" class="typo3-ver"'; // Setting ID of the table row
1115 } else { // Version branch:
1116 if ($this->targets['orig_'.$origId.'_'.$tN.'_'.$rec[$origUidFields]]) { // If there IS a corresponding original record...:
1117
1118 // Prepare Table row parameters:
1119 $this->addHlSubelementsScript = true;
1120 $tdParams = ' onmouseover="hlSubelements(\''.$origId.'_'.$tN.'_'.$rec[$origUidFields].'\', \''.$uid.'_'.$tN.'_'.$rec[$origUidFields].'\', 1, '.($this->diff==2?1:0).');"'.
1121 ' onmouseout="hlSubelements(\''.$origId.'_'.$tN.'_'.$rec[$origUidFields].'\', \''.$uid.'_'.$tN.'_'.$rec[$origUidFields].'\', 0, '.($this->diff==2?1:0).');"'.
1122 ' id="ver_'.$uid.'_'.$tN.'_'.$rec[$origUidFields].'" class="typo3-ver"';
1123
1124 // Create diff view:
1125 if ($this->diff) {
1126 list($diffHTML,$diffPct) = $this->createDiffView($tN, $rec, $this->targets['orig_'.$origId.'_'.$tN.'_'.$rec[$origUidFields]]);
1127
1128 if ($this->diff==2) {
1129 $diffCode =
1130 ($diffPct ? '<span class="nobr">'.$diffPct.'% change</span>' : '-').
1131 '<div style="visibility: hidden; position: absolute;" id="diff_'.$uid.'_'.$tN.'_'.$rec[$origUidFields].'" class="diffLayer">'.
1132 $diffHTML.
1133 '</div>';
1134 } else {
1135 $diffCode =
1136 ($diffPct<0 ? 'N/A' : ($diffPct ? $diffPct.'% change:' : '')).
1137 $diffHTML;
1138 }
1139 }
1140
1141 // Unsetting the target fields allows us to mark all originals without a version in the subtree (see ->markupNewOriginals())
1142 unset($this->targets['orig_'.$origId.'_'.$tN.'_'.$rec[$origUidFields]]);
1143 } else { // No original record, so must be new:
1144 $tdParams = ' class="typo3-ver-new"';
1145 }
1146 }
1147 } else { // If no original uid column is supported for this table we are forced NOT to display any diff or highlighting.
1148 $tdParams = ' class="typo3-ver-noComp"';
1149 }
1150
1151 // Compile the cell:
1152 $tCell[] = '
1153 <tr'.$tdParams.'>
1154 <td class="iconTitle">'.
1155 $HTMLdata.
1156 ($iconMode < 2 ?
1157 '<img'.t3lib_iconWorks::skinImg($this->doc->backPath,'gfx/ol/join'.($iconMode ? 'bottom' : '').'.gif','width="18" height="16"').' alt="" />'.
1158 t3lib_iconWorks::getIconImage($tN, $rec, $this->doc->backPath,'') : '').
1159 t3lib_BEfunc::getRecordTitle($tN, $rec, TRUE).
1160 '</td>
1161 <td class="cmdCell">'.
1162 $this->displayWorkspaceOverview_commandLinksSub($tN,$rec,$origId).
1163 '</td>'.($origId ? '<td class="diffCell">'.
1164 $diffCode.
1165 '</td>':'').'
1166 </tr>';
1167 }
1168
1169 /**
1170 * Links to publishing etc of a version
1171 *
1172 * @param string Table name
1173 * @param array Record array
1174 * @param integer The uid of the online version of $uid. If zero it means we are drawing a row for the online version itself while a value means we are drawing display for an offline version.
1175 * @return string HTML content, mainly link tags and images.
1176 */
1177 function displayWorkspaceOverview_commandLinksSub($table,$rec,$origId) {
1178 global $LANG;
1179
1180 $uid = $rec['uid'];
1181 if ($origId || $GLOBALS['BE_USER']->workspace===0) {
1182 if (!$GLOBALS['BE_USER']->workspaceCannotEditRecord($table,$rec)) {
1183 // Edit
1184 if ($table==='pages') {
1185 $actionLinks.=
1186 '<a href="#" onclick="top.loadEditId('.$uid.');top.goToModule(\''.$this->pageModule.'\'); return false;" title="' . $LANG->getLL('img_title_edit_page') . '">'.
1187 t3lib_iconWorks::getSpriteIcon('actions-page-open') .
1188 '</a>';
1189 } else {
1190 $params = '&edit['.$table.']['.$uid.']=edit';
1191 $actionLinks.=
1192 '<a href="#" onclick="'.htmlspecialchars(t3lib_BEfunc::editOnClick($params,$this->doc->backPath)).'" title="' . $LANG->getLL('img_title_edit_element') . '">'.
1193 t3lib_iconWorks::getSpriteIcon('actions-document-open') .
1194 '</a>';
1195 }
1196 }
1197
1198 // History/Log
1199 $actionLinks.=
1200 '<a href="'.htmlspecialchars($this->doc->backPath.'show_rechis.php?element='.rawurlencode($table.':'.$uid).'&returnUrl='.rawurlencode(t3lib_div::getIndpEnv('REQUEST_URI'))).'" title="' . $LANG->getLL('img_title_show_log') . '">'.
1201 t3lib_iconWorks::getSpriteIcon('actions-document-history-open') .
1202 '</a>';
1203 }
1204
1205 // View
1206 if ($table==='pages') {
1207 $actionLinks.=
1208 '<a href="#" onclick="'.htmlspecialchars(t3lib_BEfunc::viewOnClick($uid,$this->doc->backPath,t3lib_BEfunc::BEgetRootLine($uid))).'">'.
1209 t3lib_iconWorks::getSpriteIcon('actions-document-view') .
1210 '</a>';
1211 }
1212
1213 return $actionLinks;
1214 }
1215
1216
1217 /**
1218 * Links to stage change of a version
1219 *
1220 * @param string Table name
1221 * @param array Offline record (version)
1222 * @return string HTML content, mainly link tags and images.
1223 */
1224 function displayWorkspaceOverview_stageCmd($table,&$rec_off) {
1225 global $LANG;
1226
1227 switch((int)$rec_off['t3ver_stage']) {
1228 case 0:
1229 $sId = 1;
1230 $sLabel = $LANG->getLL('stage_editing');
1231 $color = '#666666'; // TODO Use CSS?
1232 $label = $LANG->getLL('label_commentforreviewer');
1233 $titleAttrib = $LANG->getLL('label_sendtoreview');
1234 break;
1235 case 1:
1236 $sId = 10;
1237 $sLabel = $LANG->getLL('label_review');
1238 $color = '#6666cc'; // TODO Use CSS?
1239 $label = $LANG->getLL('label_commentforpublisher');
1240 $titleAttrib = $LANG->getLL('label_approveforpublishing');
1241 break;
1242 case 10:
1243 $sLabel = $LANG->getLL('label_publish');
1244 $color = '#66cc66'; // TODO Use CSS?
1245 break;
1246 case -1:
1247 $sLabel = $this->doc->icons(2).$LANG->getLL('label_rejected');
1248 $sId = 0;
1249 $color = '#ff0000'; // TODO Use CSS?
1250 $label = $LANG->getLL('stage_label_user_comment');
1251 $titleAttrib = $LANG->getLL('label_resetstage');
1252 break;
1253 default:
1254 $sLabel = $LANG->getLL('label_undefined');
1255 $sId = 0;
1256 $color = '';
1257 break;
1258 }
1259 #debug($sId);
1260
1261 $raiseOk = !$GLOBALS['BE_USER']->workspaceCannotEditOfflineVersion($table,$rec_off);
1262
1263 if ($raiseOk && $rec_off['t3ver_stage'] != -1 && $GLOBALS['BE_USER']->workspaceCheckStageForCurrent($sId)) {
1264 $onClick = 'var commentTxt=window.prompt("'.$LANG->getLL('explain_reject').'","");
1265 if (commentTxt!=null) {window.location.href="'.$this->doc->issueCommand(
1266 '&cmd['.$table.']['.$rec_off['uid'].'][version][action]=setStage'.
1267 '&cmd['.$table.']['.$rec_off['uid'].'][version][stageId]=-1'
1268 ).'&cmd['.$table.']['.$rec_off['uid'].'][version][comment]="+escape(commentTxt);}'.
1269 ' return false;';
1270 // Reject:
1271 $actionLinks.=
1272 '<a href="#" onclick="'.htmlspecialchars($onClick).'"title="'.$LANG->getLL('label_reject').'"> '.
1273 t3lib_iconWorks::getSpriteIcon('actions-move-down') .
1274 '</a>';
1275 } else {
1276 // Reject:
1277 $actionLinks.=
1278 '<img src="'.$this->doc->backPath.'gfx/clear.gif" width="14" height="14" alt="" align="top" title="" />';
1279 }
1280
1281 // TODO Use CSS?
1282 $actionLinks.= '<span style="background-color: '.$color.'; color: white;">'.$sLabel.'</span>';
1283
1284 // Raise
1285 if ($raiseOk && $GLOBALS['BE_USER']->workspaceCheckStageForCurrent($sId)) {
1286 $onClick = 'var commentTxt=window.prompt("'.$label.'","");
1287 if (commentTxt!=null) {window.location.href="'.$this->doc->issueCommand(
1288 '&cmd['.$table.']['.$rec_off['uid'].'][version][action]=setStage'.
1289 '&cmd['.$table.']['.$rec_off['uid'].'][version][stageId]='.$sId
1290 ).'&cmd['.$table.']['.$rec_off['uid'].'][version][comment]="+escape(commentTxt);}'.
1291 ' return false;';
1292 if ($rec_off['t3ver_stage']!=10) {
1293 $actionLinks.=
1294 '<a href="#" onclick="'.htmlspecialchars($onClick).'" title="'.htmlspecialchars($titleAttrib).'">'.
1295 t3lib_iconWorks::getSpriteIcon('actions-move-up') .
1296 '</a>';
1297
1298 $this->stageIndex[$sId][$table][] = $rec_off['uid'];
1299 }
1300 }
1301
1302 return $actionLinks;
1303 }
1304 }
1305
1306
1307 if (defined('TYPO3_MODE') && $TYPO3_CONF_VARS[TYPO3_MODE]['XCLASS']['typo3/mod/user/ws/class.wslib_gui.php']) {
1308 include_once($TYPO3_CONF_VARS[TYPO3_MODE]['XCLASS']['typo3/mod/user/ws/class.wslib_gui.php']);
1309 }
1310
1311 ?>