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