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