PageRenderTime 9ms CodeModel.GetById 56ms app.highlight 129ms RepoModel.GetById 6ms app.codeStats 1ms

/typo3/mod/user/ws/class.wslib_gui.php

https://bitbucket.org/linxpinx/mercurial
PHP | 1309 lines | 822 code | 149 blank | 338 comment | 129 complexity | e844cf02feb6e17625a557aed5682c4a MD5 | raw file

Large files files are truncated, but you can click here to view the full file

   1<?php
   2/***************************************************************
   3 *  Copyright notice
   4 *
   5 *  (c) 2005-2010 Kasper Skaarhoj (kasperYYYY@typo3.com)
   6 *  All rights reserved
   7 *
   8 *  This script is part of the TYPO3 project. The TYPO3 project is
   9 *  free software; you can redistribute it and/or modify
  10 *  it under the terms of the GNU General Public License as published by
  11 *  the Free Software Foundation; either version 2 of the License, or
  12 *  (at your option) any later version.
  13 *
  14 *  The GNU General Public License can be found at
  15 *  http://www.gnu.org/copyleft/gpl.html.
  16 *  A copy is found in the textfile GPL.txt and important notices to the license
  17 *  from the author is found in LICENSE.txt distributed with these scripts.
  18 *
  19 *
  20 *  This script is distributed in the hope that it will be useful,
  21 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
  22 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  23 *  GNU General Public License for more details.
  24 *
  25 *  This copyright notice MUST APPEAR in all copies of the script!
  26 ***************************************************************/
  27/**
  28 * Library with Workspace GUI related functionality. It is used by main workspace
  29 * module but also can be used from extensions. Originally 99.9%% of the code
  30 * was written by Kasper and only transfered here by Dmitry.
  31 *
  32 * $Id: class.wslib_gui.php 8157 2010-07-11 12:45:16Z psychomieze $
  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
  72require_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 */
  86class 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::getIconImage('pages',$pRec,$this->doc->backPath,' align="top" 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					list($titleRec) = $GLOBALS['TYPO3_DB']->exec_SELECTgetRows('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="logLayer" style="visibility: hidden; position: absolute;" 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-swap-version') .
 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') .

Large files files are truncated, but you can click here to view the full file