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