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