Todays special
[Packages/TYPO3.CMS.git] / typo3 / sysext / version / cm1 / index.php
1 <?php
2 /***************************************************************
3 * Copyright notice
4 *
5 * (c) 2004-2005 Kasper Skaarhoj (kasperYYYY@typo3.com)
6 * All rights reserved
7 *
8 * This script is part of the TYPO3 project. The TYPO3 project is
9 * free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
13 *
14 * The GNU General Public License can be found at
15 * http://www.gnu.org/copyleft/gpl.html.
16 *
17 * This script is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU General Public License for more details.
21 *
22 * This copyright notice MUST APPEAR in all copies of the script!
23 ***************************************************************/
24 /**
25 * Versioning module
26 *
27 * @author Kasper Skaarhoj <kasperYYYY@typo3.com>
28 */
29 /**
30 * [CLASS/FUNCTION INDEX of SCRIPT]
31 *
32 *
33 *
34 * 73: class tx_version_cm1 extends t3lib_SCbase
35 * 80: function main()
36 * 105: function jumpToUrl(URL)
37 * 139: function printContent()
38 * 150: function moduleContent()
39 * 310: function pageSubContent($pid,$c=0)
40 * 364: function lookForOwnVersions($table,$uid)
41 * 381: function adminLinks($table,$row)
42 *
43 * TOTAL FUNCTIONS: 7
44 * (This index is automatically created/updated by the extension "extdeveval")
45 *
46 */
47
48
49 // DEFAULT initialization of a module [BEGIN]
50 unset($MCONF);
51 require ('conf.php');
52 require ($BACK_PATH.'init.php');
53 require ($BACK_PATH.'template.php');
54 $LANG->includeLLFile('EXT:version/locallang.xml');
55 require_once (PATH_t3lib.'class.t3lib_scbase.php');
56 // DEFAULT initialization of a module [END]
57
58 require_once(PATH_t3lib.'class.t3lib_diff.php');
59 require_once(PATH_typo3.'mod/user/ws/class.wslib.php');
60 require_once(PATH_t3lib.'class.t3lib_pagetree.php');
61
62
63
64 /**
65 * Versioning module, including workspace management
66 *
67 * @author Kasper Skaarhoj <kasperYYYY@typo3.com>
68 * @package TYPO3
69 * @subpackage core
70 */
71 class tx_version_cm1 extends t3lib_SCbase {
72
73 // Default variables for backend modules
74 var $MCONF = array(); // Module configuration
75 var $MOD_MENU = array(); // Module menu items
76 var $MOD_SETTINGS = array(); // Module session settings
77 var $doc; // Document Template Object
78 var $content; // Accumulated content
79
80
81 // Internal:
82 var $showWorkspaceCol = 0;
83 var $formatWorkspace_cache = array();
84 var $formatCount_cache = array();
85 var $targets = array(); // Accumulation of online targets.
86 var $pageModule = ''; // Name of page module
87 var $publishAccess = FALSE;
88 var $be_user_Array = array();
89
90
91
92
93
94
95 /*********************************
96 *
97 * Standard module initialization
98 *
99 *********************************/
100
101 /**
102 * Initialize menu configuration
103 *
104 * @return void
105 */
106 function menuConfig() {
107
108 // Menu items:
109 $this->MOD_MENU = array(
110 'filter' => array(
111 1 => 'Drafts',
112 2 => 'Archive',
113 0 => 'All',
114 ),
115 'display' => array(
116 0 => '[ONLINE]',
117 -98 => 'Workspaces',
118 -99 => 'All',
119 -1 => '[Offline]'
120 ),
121 'diff' => ''
122 );
123
124 // Add workspaces:
125 if ($GLOBALS['BE_USER']->workspace===0) { // Spend time on this only in online workspace because it might take time:
126 $workspaces = $GLOBALS['TYPO3_DB']->exec_SELECTgetRows('uid,title,adminusers,members,reviewers','sys_workspace','pid=0'.t3lib_BEfunc::deleteClause('sys_workspace'),'','title');
127 foreach($workspaces as $rec) {
128 if ($GLOBALS['BE_USER']->checkWorkspace($rec)) {
129 $this->MOD_MENU['display'][$rec['uid']] = '['.$rec['uid'].'] '.$rec['title'];
130 }
131 }
132 }
133
134 // CLEANSE SETTINGS
135 $this->MOD_SETTINGS = t3lib_BEfunc::getModuleData($this->MOD_MENU, t3lib_div::_GP('SET'), $this->MCONF['name'], 'ses');
136 }
137
138 /**
139 * Main function of the module. Write the content to $this->content
140 *
141 * @return void
142 */
143 function main() {
144 global $BE_USER,$LANG,$BACK_PATH,$TCA_DESCR,$TCA,$CLIENT,$TYPO3_CONF_VARS;
145
146 // Setting module configuration:
147 $this->MCONF = $GLOBALS['MCONF'];
148
149 // Draw the header.
150 $this->doc = t3lib_div::makeInstance('mediumDoc');
151 $this->doc->backPath = $BACK_PATH;
152 $this->doc->form='<form action="" method="post">';
153
154 // Setting up the context sensitive menu:
155 $CMparts = $this->doc->getContextMenuCode();
156 $this->doc->JScode.= $CMparts[0];
157 $this->doc->bodyTagAdditions = $CMparts[1];
158 $this->doc->postCode.= $CMparts[2];
159
160 // Getting input data:
161 $this->id = intval(t3lib_div::_GP('id')); // Page id. If set, indicates activation from Web>Versioning module
162 if (!$this->id) {
163 $this->uid = intval(t3lib_div::_GP('uid')); // Record uid. Goes with table name to indicate specific record
164 $this->table = t3lib_div::_GP('table'); // Record table. Goes with uid to indicate specific record
165 } else {
166 $this->uid = $this->id;
167 $this->table = 'pages';
168 }
169 $this->details = t3lib_div::_GP('details'); // Page id. If set, indicates activation from Web>Versioning module
170
171 // Force this setting:
172 $this->MOD_SETTINGS['expandSubElements'] = TRUE;
173 $this->MOD_SETTINGS['diff'] = $this->details || $this->MOD_SETTINGS['diff']?1:0;
174
175 // Reading the record:
176 $record = t3lib_BEfunc::getRecord($this->table,$this->uid);
177 if ($record['pid']==-1) {
178 $record = t3lib_BEfunc::getRecord($this->table,$record['t3ver_oid']);
179 }
180
181 // Checking access etc.
182 if (is_array($record) && $TCA[$this->table]['ctrl']['versioningWS']) {
183 $this->uid = $record['uid']; // Might have changed if new live record was found!
184
185 // Access check!
186 // The page will show only if there is a valid page and if this page may be viewed by the user
187 $this->pageinfo = t3lib_BEfunc::readPageAccess($record['pid'],$this->perms_clause);
188 $access = is_array($this->pageinfo) ? 1 : 0;
189
190 if (($record['pid'] && $access) || ($BE_USER->user['admin'] && !$record['pid'])) {
191
192 // JavaScript
193 $this->doc->JScode = '
194 <script language="javascript" type="text/javascript">
195 script_ended = 0;
196 function jumpToUrl(URL) {
197 document.location = URL;
198 }
199
200 function hlSubelements(origId, verId, over, diffLayer) { //
201 if (over) {
202 document.getElementById(\'orig_\'+origId).attributes.getNamedItem("class").nodeValue = \'typo3-ver-hl\';
203 document.getElementById(\'ver_\'+verId).attributes.getNamedItem("class").nodeValue = \'typo3-ver-hl\';
204 if (diffLayer) {
205 document.getElementById(\'diff_\'+verId).style.visibility = \'visible\';
206 }
207 } else {
208 document.getElementById(\'orig_\'+origId).attributes.getNamedItem("class").nodeValue = \'typo3-ver\';
209 document.getElementById(\'ver_\'+verId).attributes.getNamedItem("class").nodeValue = \'typo3-ver\';
210 if (diffLayer) {
211 document.getElementById(\'diff_\'+verId).style.visibility = \'hidden\';
212 }
213 }
214 }
215 </script>
216 ';
217
218 // If another page module was specified, replace the default Page module with the new one
219 $newPageModule = trim($BE_USER->getTSConfigVal('options.overridePageModule'));
220 $this->pageModule = t3lib_BEfunc::isModuleSetInTBE_MODULES($newPageModule) ? $newPageModule : 'web_layout';
221
222 // Setting publish access permission for workspace:
223 $this->publishAccess = $BE_USER->workspacePublishAccess($BE_USER->workspace);
224
225
226 $headerSection = $this->doc->getHeader('pages',$this->pageinfo,$this->pageinfo['_thePath']).'<br/>'.$LANG->sL('LLL:EXT:lang/locallang_core.php:labels.path').': '.t3lib_div::fixed_lgd_pre($this->pageinfo['_thePath'],50);
227
228 $this->content.=$this->doc->startPage($LANG->getLL('title'));
229 $this->content.=$this->doc->header($LANG->getLL('title'));
230 $this->content.=$this->doc->spacer(5);
231 $this->content.=$this->doc->section('',$headerSection);
232 $this->content.=$this->doc->divider(5);
233
234 // Render content:
235 if ($this->id) {
236 $this->workspaceMgm();
237 } else {
238 $this->versioningMgm();
239 }
240
241 // ShortCut
242 if ($BE_USER->mayMakeShortcut()) {
243 $this->content.=$this->doc->spacer(20).$this->doc->section('',$this->doc->makeShortcutIcon('id',implode(',',array_keys($this->MOD_MENU)),$this->MCONF['name']));
244 }
245 }
246
247 $this->content.=$this->doc->spacer(10);
248 }
249 }
250
251 /**
252 * Outputs accumulated module content to browser.
253 *
254 * @return void
255 */
256 function printContent() {
257
258 $this->content.=$this->doc->endPage();
259 echo $this->content;
260 }
261
262
263
264
265
266
267
268
269
270
271 /******************************
272 *
273 * Versioning management
274 *
275 ******************************/
276
277 /**
278 * Management of versions for record
279 *
280 * @return void
281 */
282 function versioningMgm() {
283 global $TCA;
284
285 // Diffing:
286 $diff_1 = t3lib_div::_POST('diff_1');
287 $diff_2 = t3lib_div::_POST('diff_2');
288 if (t3lib_div::_POST('do_diff')) {
289 $content='';
290 $content.='<h3>DIFFING:</h3>';
291 if ($diff_1 && $diff_2) {
292 $diff_1_record = t3lib_BEfunc::getRecord($this->table, $diff_1);
293 $diff_2_record = t3lib_BEfunc::getRecord($this->table, $diff_2);
294
295 if (is_array($diff_1_record) && is_array($diff_2_record)) {
296 t3lib_div::loadTCA($this->table);
297 $t3lib_diff_Obj = t3lib_div::makeInstance('t3lib_diff');
298
299 $tRows=array();
300 $tRows[] = '
301 <tr class="bgColor5 tableheader">
302 <td>Fieldname:</td>
303 <td width="98%">Colored diff-view:</td>
304 </tr>
305 ';
306 foreach($diff_1_record as $fN => $fV) {
307 if ($TCA[$this->table]['columns'][$fN] && $TCA[$this->table]['columns'][$fN]['config']['type']!='passthrough' && !t3lib_div::inList('t3ver_label',$fN)) {
308 if (strcmp($diff_1_record[$fN],$diff_2_record[$fN])) {
309
310 $diffres = $t3lib_diff_Obj->makeDiffDisplay(
311 t3lib_BEfunc::getProcessedValue($this->table,$fN,$diff_2_record[$fN],0,1),
312 t3lib_BEfunc::getProcessedValue($this->table,$fN,$diff_1_record[$fN],0,1)
313 );
314
315 $tRows[] = '
316 <tr class="bgColor4">
317 <td>'.$fN.'</td>
318 <td width="98%">'.$diffres.'</td>
319 </tr>
320 ';
321 }
322 }
323 }
324
325 if (count($tRows)>1) {
326 $content.='<table border="0" cellpadding="1" cellspacing="1" width="100%">'.implode('',$tRows).'</table><br/><br/>';
327 } else {
328 $content.='Records matches completely on all editable fields!';
329 }
330 } else $content.='ERROR: Records could strangely not be found!';
331 } else {
332 $content.='ERROR: You didn\'t select two sources for diffing!';
333 }
334 }
335
336 // Element:
337 $record = t3lib_BEfunc::getRecord($this->table,$this->uid);
338 $recordIcon = t3lib_iconWorks::getIconImage($this->table,$record,$this->doc->backPath,'class="absmiddle"');
339 $recTitle = t3lib_BEfunc::getRecordTitle($this->table,$record,1);
340
341 // Display versions:
342 $content.='
343 '.$recordIcon.$recTitle.'
344 <form action="'.t3lib_div::getIndpEnv('REQUEST_URI').'" method="post">
345 <table border="0" cellspacing="1" cellpadding="1">';
346 $content.='
347 <tr class="bgColor5 tableheader">
348 <td>&nbsp;</td>
349 <td>&nbsp;</td>
350 <td>Title</td>
351 <td>UID</td>
352 <td title="t3ver_oid - Reference to live version UID">oid</td>
353 <td title="t3ver_id - Version number, incremental integer">id</td>
354 <td title="t3ver_wsid - Workspace ID. There can be only one version of an element per ID (except ID zero).">wsid</td>
355 <td title="t3ver_state - Special states of a version: 1=Placeholder for "New". 2=Marked deleted.">state</td>
356 <td title="t3ver_stage - Publishing stage: Editing (0), review (1), publish (10), rejected (-1).">stage</td>
357 <td title="t3ver_count - Life cycle counter. Incremented each time element is unpublished.">count</td>
358 <td>pid</td>
359 <td>t3ver_label</td>
360 <td colspan="2"><input type="submit" name="do_diff" value="Diff" /></td>
361 </tr>';
362
363 $versions = t3lib_BEfunc::selectVersionsOfRecord($this->table, $this->uid, '*', $GLOBALS['BE_USER']->workspace);
364 foreach($versions as $row) {
365 $adminLinks = $this->adminLinks($this->table,$row);
366
367 $content.='
368 <tr class="'.($row['uid']!=$this->uid ? 'bgColor4' : 'bgColor2 tableheader').'">
369 <td>'.($row['uid']!=$this->uid ? '<a href="'.$this->doc->issueCommand('&cmd['.$this->table.']['.$this->uid.'][version][swapWith]='.$row['uid'].'&cmd['.$this->table.']['.$this->uid.'][version][action]=swap').'">'.
370 '<img'.t3lib_iconWorks::skinImg($this->doc->backPath,'gfx/insert1.gif','width="14" height="14"').' alt="" title="SWAP with current" />'.
371 '</a>' /* (
372 $this->table == 'pages' ?
373 '<a href="'.$this->doc->issueCommand('&cmd['.$this->table.']['.$this->uid.'][version][action]=swap&cmd['.$this->table.']['.$this->uid.'][version][swapWith]='.$row['uid'].'&cmd['.$this->table.']['.$this->uid.'][version][swapContent]=1').'">'.
374 '<img'.t3lib_iconWorks::skinImg($this->doc->backPath,'gfx/insert2.gif','width="14" height="14"').' alt="" title="Publish page AND content!" />'.
375 '</a>'.
376 '<a href="'.$this->doc->issueCommand('&cmd['.$this->table.']['.$this->uid.'][version][action]=swap&cmd['.$this->table.']['.$this->uid.'][version][swapWith]='.$row['uid'].'&cmd['.$this->table.']['.$this->uid.'][version][swapContent]=ALL').'">'.
377 '<img'.t3lib_iconWorks::skinImg($this->doc->backPath,'gfx/insert4.gif','width="14" height="14"').' alt="" title="Publish page AND content! - AND ALL SUBPAGES!" />'.
378 '</a>' : '') */ : '<img'.t3lib_iconWorks::skinImg($this->doc->backPath,'gfx/blinkarrow_left.gif','width="5" height="9"').' alt="" title="CURRENT ONLINE VERSION!"/>').'</td>
379 <td>'.$adminLinks.'</td>
380 <td nowrap="nowrap">'.t3lib_BEfunc::getRecordTitle($this->table,$row,1).'</td>
381 <td>'.$row['uid'].'</td>
382 <td>'.$row['t3ver_oid'].'</td>
383 <td>'.$row['t3ver_id'].'</td>
384 <td>'.$row['t3ver_wsid'].'</td>
385 <td>'.$row['t3ver_state'].'</td>
386 <td>'.$row['t3ver_stage'].'</td>
387 <td>'.$row['t3ver_count'].'</td>
388 <td>'.$row['pid'].'</td>
389 <td nowrap="nowrap"><a href="#" onclick="'.htmlspecialchars(t3lib_BEfunc::editOnClick('&edit['.$this->table.']['.$row['uid'].']=edit&columnsOnly=t3ver_label',$this->doc->backPath)).'"><img'.t3lib_iconWorks::skinImg($this->doc->backPath,'gfx/edit2.gif','width="11" height="12"').' alt="" title="Edit"/></a>'.htmlspecialchars($row['t3ver_label']).'</td>
390 <td bgcolor="green"><input type="radio" name="diff_1" value="'.$row['uid'].'"'.($diff_1==$row['uid'] ? ' checked="checked"':'').'/></td>
391 <td bgcolor="red"><input type="radio" name="diff_2" value="'.$row['uid'].'"'.($diff_2==$row['uid'] ? ' checked="checked"':'').'/></td>
392 </tr>';
393
394 // Show sub-content if the table is pages AND it is not the online branch (because that will mostly render the WHOLE tree below - not smart;)
395 if ($this->table == 'pages' && $row['uid']!=$this->uid) {
396 $sub = $this->pageSubContent($row['uid']);
397
398 if ($sub) {
399 $content.='
400 <tr>
401 <td></td>
402 <td></td>
403 <td colspan="10">'.$sub.'</td>
404 <td colspan="2"></td>
405 </tr>';
406 }
407 }
408 }
409 $content.='</table></form>';
410
411 $this->content.=$this->doc->section('',$content,0,1);
412
413
414 // Create new:
415 $content='
416
417 <form action="'.$this->doc->backPath.'tce_db.php" method="post">
418 Label: <input type="text" name="cmd['.$this->table.']['.$this->uid.'][version][label]" /><br/>
419 '.($this->table == 'pages' ? '<select name="cmd['.$this->table.']['.$this->uid.'][version][treeLevels]">
420 <option value="0">Page: Page + content</option>
421 <option value="100">Branch: All subpages</option>
422 <option value="-1">Element: Just record</option>
423 </select>' : '').'
424 <br/><input type="hidden" name="cmd['.$this->table.']['.$this->uid.'][version][action]" value="new" />
425 <input type="hidden" name="prErr" value="1" />
426 <input type="hidden" name="redirect" value="'.t3lib_div::getIndpEnv('REQUEST_URI').'" />
427 <input type="submit" name="_" value="Create new version" />
428
429 </form>
430
431 ';
432
433 $this->content.=$this->doc->spacer(15);
434 $this->content.=$this->doc->section('Create new version',$content,0,1);
435
436 }
437
438 /**
439 * Recursively look for children for page version with $pid
440 *
441 * @param integer UID of page record for which to look up sub-elements following that version
442 * @param integer Counter, do not set (limits to 100 levels)
443 * @return string Table with content if any
444 */
445 function pageSubContent($pid,$c=0) {
446 global $TCA;
447
448 $tableNames = t3lib_div::removeArrayEntryByValue(array_keys($TCA),'pages');
449 $tableNames[] = 'pages';
450
451 foreach($tableNames as $tN) {
452 // Basically list ALL tables - not only those being copied might be found!
453 #if ($TCA[$tN]['ctrl']['versioning_followPages'] || $tN=='pages') {
454 $mres = $GLOBALS['TYPO3_DB']->exec_SELECTquery('*', $tN, 'pid='.intval($pid).t3lib_BEfunc::deleteClause($tN), '', ($TCA[$tN]['ctrl']['sortby'] ? $TCA[$tN]['ctrl']['sortby'] : ''));
455
456 if ($GLOBALS['TYPO3_DB']->sql_num_rows($mres)) {
457 $content.='
458 <tr>
459 <td colspan="4" class="'.($TCA[$tN]['ctrl']['versioning_followPages'] ? 'bgColor6' : ($tN=='pages' ? 'bgColor5' : 'bgColor-10')).'"'.(!$TCA[$tN]['ctrl']['versioning_followPages'] && $tN!='pages' ? ' style="color: #666666; font-style:italic;"':'').'>'.$tN.'</td>
460 </tr>';
461 while ($subrow = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($mres)) {
462 $ownVer = $this->lookForOwnVersions($tN,$subrow['uid']);
463 $content.='
464 <tr>
465 <td>'.$this->adminLinks($tN,$subrow).'</td>
466 <td>'.$subrow['uid'].'</td>
467 '.($ownVer>1 ? '<td style="font-weight: bold; background-color: yellow;"><a href="index.php?table='.rawurlencode($tN).'&uid='.$subrow['uid'].'">'.($ownVer-1).'</a></td>' : '<td></td>').'
468 <td width="98%">'.t3lib_BEfunc::getRecordTitle($tN,$subrow,1).'</td>
469 </tr>';
470
471 if ($tN == 'pages' && $c<100) {
472 $sub = $this->pageSubContent($subrow['uid'],$c+1);
473
474 if ($sub) {
475 $content.='
476 <tr>
477 <td></td>
478 <td></td>
479 <td></td>
480 <td width="98%">'.$sub.'</td>
481 </tr>';
482 }
483 }
484 }
485 }
486 #}
487 }
488
489 return $content ? '<table border="1" cellpadding="1" cellspacing="0" width="100%">'.$content.'</table>' : '';
490 }
491
492 /**
493 * Look for number of versions of a record
494 *
495 * @param string Table name
496 * @param integer Record uid
497 * @return integer Number of versions for record, false if none.
498 */
499 function lookForOwnVersions($table,$uid) {
500 global $TCA;
501
502 $versions = t3lib_BEfunc::selectVersionsOfRecord($table, $uid, 'uid');
503 if (is_array($versions)) {
504 return count($versions);
505 }
506 return FALSE;
507 }
508
509 /**
510 * Administrative links for a table / record
511 *
512 * @param string Table name
513 * @param array Record for which administrative links are generated.
514 * @return string HTML link tags.
515 */
516 function adminLinks($table,$row) {
517 global $BE_USER;
518
519 // Edit link:
520 $adminLink = '<a href="#" onclick="'.htmlspecialchars(t3lib_BEfunc::editOnClick('&edit['.$table.']['.$row['uid'].']=edit',$this->doc->backPath)).'">'.
521 '<img'.t3lib_iconWorks::skinImg($this->doc->backPath,'gfx/edit2.gif','width="11" height="12"').' alt="" title="Edit"/>'.
522 '</a>';
523
524 // Delete link:
525 $adminLink.= '<a href="'.htmlspecialchars($this->doc->issueCommand('&cmd['.$table.']['.$row['uid'].'][delete]=1')).'">'.
526 '<img'.t3lib_iconWorks::skinImg($this->doc->backPath,'gfx/garbage.gif','width="11" height="12"').' alt="" title="Delete"/>'.
527 '</a>';
528
529
530
531 if ($table == 'pages') {
532
533 // If another page module was specified, replace the default Page module with the new one
534 $newPageModule = trim($BE_USER->getTSConfigVal('options.overridePageModule'));
535 $pageModule = t3lib_BEfunc::isModuleSetInTBE_MODULES($newPageModule) ? $newPageModule : 'web_layout';
536
537 // Perform some acccess checks:
538 $a_wl = $BE_USER->check('modules','web_list');
539 $a_wp = t3lib_extMgm::isLoaded('cms') && $BE_USER->check('modules',$pageModule);
540
541 $adminLink.='<a href="#" onclick="top.loadEditId('.$row['uid'].');top.goToModule(\''.$pageModule.'\'); return false;">'.
542 '<img'.t3lib_iconWorks::skinImg($this->doc->backPath,t3lib_extMgm::extRelPath('cms').'layout/layout.gif','width="14" height="12"').' title="" alt="" />'.
543 '</a>';
544 $adminLink.='<a href="#" onclick="top.loadEditId('.$row['uid'].');top.goToModule(\'web_list\'); return false;">'.
545 '<img'.t3lib_iconWorks::skinImg($this->doc->backPath,'mod/web/list/list.gif','width="14" height="12"').' title="" alt="" />'.
546 '</a>';
547
548 // "View page" icon is added:
549 $adminLink.='<a href="#" onclick="'.htmlspecialchars(t3lib_BEfunc::viewOnClick($row['uid'],$this->doc->backPath,t3lib_BEfunc::BEgetRootLine($row['uid']))).'">'.
550 '<img'.t3lib_iconWorks::skinImg($this->doc->backPath,'gfx/zoom.gif','width="12" height="12"').' title="" alt="" />'.
551 '</a>';
552 } else {
553 if ($row['pid']==-1) {
554 $getVars = '&ADMCMD_vPrev['.rawurlencode($table.':'.$row['t3ver_oid']).']='.$row['uid'];
555
556 // "View page" icon is added:
557 $adminLink.='<a href="#" onclick="'.htmlspecialchars(t3lib_BEfunc::viewOnClick($row['_REAL_PID'],$this->doc->backPath,t3lib_BEfunc::BEgetRootLine($row['_REAL_PID']),'','',$getVars)).'">'.
558 '<img'.t3lib_iconWorks::skinImg($this->doc->backPath,'gfx/zoom.gif','width="12" height="12"').' title="" alt="" />'.
559 '</a>';
560 }
561 }
562
563 return $adminLink;
564 }
565
566
567
568
569
570
571
572
573
574
575
576 /******************************
577 *
578 * Workspace management
579 *
580 ******************************/
581
582 /**
583 * Management of workspace for page ID
584 * Called when $this->id is set.
585 *
586 * @return void
587 */
588 function workspaceMgm() {
589
590 $menu = '';
591 if ($GLOBALS['BE_USER']->workspace===0) {
592 $menu.= t3lib_BEfunc::getFuncMenu($this->id,'SET[filter]',$this->MOD_SETTINGS['filter'],$this->MOD_MENU['filter']);
593 $menu.= t3lib_BEfunc::getFuncMenu($this->id,'SET[display]',$this->MOD_SETTINGS['display'],$this->MOD_MENU['display']);
594 }
595 if (!$this->details && $GLOBALS['BE_USER']->workspace) {
596 $menu.= t3lib_BEfunc::getFuncCheck($this->id,'SET[diff]',$this->MOD_SETTINGS['diff']).' Show difference view';
597 }
598
599 if ($menu) {
600 $this->content.=$this->doc->section('',$menu,0,1);
601 }
602
603 $this->content.=$this->doc->spacer(15);
604 $this->content.=$this->doc->section($this->details ? 'Details for version' : 'Workspace management', $this->displayWorkspaceOverview(),0,1);
605 }
606
607 /**
608 * Rendering the overview of versions in the current workspace
609 *
610 * @return string HTML (table)
611 * @see typo3/mod/user/ws/index.php for sister function!
612 */
613 function displayWorkspaceOverview() {
614
615 // Initialize variables:
616 $this->showWorkspaceCol = $GLOBALS['BE_USER']->workspace===0 && $this->MOD_SETTINGS['display']<=-98;
617
618 // Get usernames and groupnames
619 $be_group_Array = t3lib_BEfunc::getListGroupNames('title,uid');
620 $groupArray = array_keys($be_group_Array);
621 $this->be_user_Array = t3lib_BEfunc::getUserNames();
622 if (!$GLOBALS['BE_USER']->isAdmin()) $this->be_user_Array = t3lib_BEfunc::blindUserNames($this->be_user_Array,$groupArray,1);
623
624 // Initialize Workspace ID and filter-value:
625 if ($GLOBALS['BE_USER']->workspace===0) {
626 $wsid = $this->details ? -99 : $this->MOD_SETTINGS['display']; // Set wsid to the value from the menu (displaying content of other workspaces)
627 $filter = $this->details ? 0 : $this->MOD_SETTINGS['filter'];
628 } else {
629 $wsid = $GLOBALS['BE_USER']->workspace;
630 $filter = 0;
631 }
632
633 // Initialize workspace object and request all pending versions:
634 $wslibObj = t3lib_div::makeInstance('wslib');
635
636 // Selecting ALL versions belonging to the workspace:
637 $versions = $wslibObj->selectVersionsInWorkspace($wsid, $filter, -99, $this->uid); // $this->uid is the page id of LIVE record.
638
639 // Traverse versions and build page-display array:
640 $pArray = array();
641 foreach($versions as $table => $records) {
642 foreach($records as $rec) {
643 $pageIdField = $table==='pages' ? 't3ver_oid' : 'realpid';
644 $this->displayWorkspaceOverview_setInPageArray(
645 $pArray,
646 $table,
647 $rec
648 );
649 }
650 }
651
652 // Make header of overview:
653 $tableRows = array();
654 if (count($pArray)) {
655 $tableRows[] = '
656 <tr class="bgColor5 tableheader">
657 <td nowrap="nowrap" colspan="2">Online Version:</td>
658 <td nowrap="nowrap" colspan="2">Offline Versions:</td>
659 <td nowrap="nowrap" colspan="4">Controls:</td>
660 </tr>';
661
662 // Add lines from overview:
663 $tableRows = array_merge($tableRows, $this->displayWorkspaceOverview_list($pArray));
664
665 $table = '<table border="0" cellpadding="0" cellspacing="1" class="lrPadding workspace-overview">'.implode('',$tableRows).'</table>';
666 } else $table = '';
667
668 $linkBack = t3lib_div::_GP('returnUrl') ? '<a href="'.htmlspecialchars(t3lib_div::_GP('returnUrl')).'" class="typo3-goBack"><img'.t3lib_iconWorks::skinImg($this->doc->backPath,'gfx/goback.gif','width="14" height="14"').' alt="" />Click here to go back</a><br/><br/>' : '';
669
670 $versionSelector = $GLOBALS['BE_USER']->workspace ? $this->doc->getVersionSelector($this->id) : '';
671
672 return $versionSelector.$linkBack.$table.$this->markupNewOriginals();
673 }
674
675 /**
676 * Rendering the content for the publish / review overview:
677 * (Made for internal recursive calling)
678 *
679 * @param array Storage of the elements to display (see displayWorkspaceOverview() / displayWorkspaceOverview_setInPageArray())
680 * @return array Table rows, see displayWorkspaceOverview()
681 */
682 function displayWorkspaceOverview_list($pArray) {
683 global $TCA;
684
685 // If there ARE elements on this level, print them:
686 $warnAboutVersions_nonPages = FALSE;
687 $warnAboutVersions_page = FALSE;
688 if (is_array($pArray)) {
689 foreach($pArray as $table => $oidArray) {
690 foreach($oidArray as $oid => $recs) {
691
692 // Get CURRENT online record and icon based on "t3ver_oid":
693 $rec_on = t3lib_BEfunc::getRecord($table,$oid);
694 $icon = t3lib_iconWorks::getIconImage($table, $rec_on, $this->doc->backPath,' align="top" title="'.t3lib_BEfunc::getRecordIconAltText($rec_on,$table).'"');
695 if ($GLOBALS['BE_USER']->workspace===0) { // Only edit online records if in ONLINE workspace:
696 $icon = $this->doc->wrapClickMenuOnIcon($icon, $table, $rec_on['uid'], 1, '', '+edit,view,info,delete');
697 }
698
699 // Online version display:
700 // Create the main cells which will span over the number of versions there is.
701 $verLinkUrl = $TCA[$table]['ctrl']['versioningWS'];
702 $origElement = $icon.
703 ($verLinkUrl ? '<a href="'.htmlspecialchars('index.php?table='.$table.'&uid='.$rec_on['uid']).'">' : '').
704 t3lib_BEfunc::getRecordTitle($table,$rec_on,TRUE).
705 ($verLinkUrl ? '</a>' : '');
706 $mainCell_rowSpan = count($recs)>1 ? ' rowspan="'.count($recs).'"' : '';
707 $mainCell = '
708 <td align="center"'.$mainCell_rowSpan.'>'.$this->formatVerId($rec_on['t3ver_id']).'</td>
709 <td nowrap="nowrap"'.$mainCell_rowSpan.'>'.
710 $origElement.
711 '###SUB_ELEMENTS###'. // For substitution with sub-elements, if any.
712 '</td>';
713
714 // Offline versions display:
715 // Traverse the versions of the element
716 foreach($recs as $rec) {
717
718 // Get the offline version record and icon:
719 $rec_off = t3lib_BEfunc::getRecord($table,$rec['uid']);
720 $icon = t3lib_iconWorks::getIconImage($table, $rec_off, $this->doc->backPath, ' align="top" title="'.t3lib_BEfunc::getRecordIconAltText($rec_off,$table).'"');
721 $icon = $this->doc->wrapClickMenuOnIcon($icon, $table, $rec_off['uid'], 1, '', '+edit,view,info,delete');
722
723 // Prepare diff-code:
724 if ($this->MOD_SETTINGS['diff']) {
725 if ($rec_on['t3ver_state']!=1) { // Not new record:
726 list($diffHTML,$diffPct) = $this->createDiffView($table, $rec_off, $rec_on);
727 $diffCode = ($diffPct<0 ? 'N/A' : ($diffPct ? $diffPct.'% change:' : '')).
728 $diffHTML;
729 } else {
730 $diffCode = $this->doc->icons(1).'New element';
731 }
732 } else $diffCode = '';
733
734 // Prepare swap-mode values:
735 if ($table==='pages' && $rec_off['t3ver_swapmode']!=-1) {
736 if ($rec_off['t3ver_swapmode']>0) {
737 $vType = 'branch';
738 } else {
739 $vType = 'page';
740 }
741 } else {
742 $vType = 'element';
743 }
744
745 switch($vType) {
746 case 'element':
747 $swapLabel = ' [Element]';
748 $swapClass = 'ver-element';
749 $warnAboutVersions_nonPages = $warnAboutVersions_page; // Setting this if sub elements are found with a page+content (must be rendered prior to this of course!)
750 break;
751 case 'page':
752 $swapLabel = ' [Page]';
753 $swapClass = 'ver-page';
754 $warnAboutVersions_page = !$this->showWorkspaceCol; // This value is true only if multiple workspaces are shown and we need the opposite here.
755 break;
756 case 'branch':
757 $swapLabel = ' [Branch]';
758 $swapClass = 'ver-branch';
759 break;
760 }
761
762 // Modify main cell based on first version shown:
763 $subElements = array();
764 if ($table==='pages' && $rec_off['t3ver_swapmode']!=-1 && $mainCell) { // For "Page" and "Branch" swap modes where $mainCell is still carrying content (only first version)
765 $subElements['on'] = $this->subElements($rec_on['uid'], $rec_off['t3ver_swapmode']);
766 $subElements['off'] = $this->subElements($rec_off['uid'],$rec_off['t3ver_swapmode'],$rec_on['uid']);
767 }
768 $mainCell = str_replace('###SUB_ELEMENTS###', $subElements['on'], $mainCell);
769
770 // Create version element:
771 $versionsInOtherWS = $this->versionsInOtherWS($table, $rec_on['uid']);
772 $versionsInOtherWSWarning = $versionsInOtherWS && $GLOBALS['BE_USER']->workspace!==0 ? '<br/>'.$this->doc->icons(2).'Other version(s) in workspace '.$versionsInOtherWS : '';
773 $multipleWarning = (!$mainCell && $GLOBALS['BE_USER']->workspace!==0? '<br/>'.$this->doc->icons(3).'<b>Multiple versions in same workspace!</b>' : '');
774 $verWarning = $warnAboutVersions || ($warnAboutVersions_nonPages && $GLOBALS['TCA'][$table]['ctrl']['versioning_followPages'])? '<br/>'.$this->doc->icons(3).'<b>Version inside version!</b>' : '';
775 $verElement = $icon.
776 (!$this->details ? '<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'))).'">' : '').
777 t3lib_BEfunc::getRecordTitle($table,$rec_off,TRUE).
778 (!$this->details ? '</a>' : '').
779 $versionsInOtherWSWarning.
780 $multipleWarning.
781 $verWarning;
782
783 $ctrlTable = '
784 <td nowrap="nowrap">'.$this->showStageChangeLog($table,$rec_off['uid'],$this->displayWorkspaceOverview_stageCmd($table,$rec_off)).'</td>
785 <td nowrap="nowrap" class="'.$swapClass.'">'.
786 $this->displayWorkspaceOverview_commandLinks($table,$rec_on,$rec_off,$vType).
787 htmlspecialchars($swapLabel).
788 '&nbsp;&nbsp;</td>
789 <td nowrap="nowrap"><b>Lifecycle:</b> '.htmlspecialchars($this->formatCount($rec_off['t3ver_count'])).'</td>'. // Lifecycle
790 ($this->showWorkspaceCol ? '
791 <td nowrap="nowrap">&nbsp;&nbsp;<b>Workspace:</b> '.htmlspecialchars($this->formatWorkspace($rec_off['t3ver_wsid'])).'</td>' : '');
792
793 if ($diffCode) {
794 $verElement = $verElement.'
795 <br/><b>Difference to live element:</b>
796 <table border="0" cellpadding="0" cellspacing="0" class="ver-verElement">
797 <tr>
798 <td class="c-diffCell">'.$diffCode.'</td>
799 </tr>
800 </table>';
801 }
802
803
804 // Create version cell:
805 $verCell = '
806 <td align="center">'.$this->formatVerId($rec_off['t3ver_id']).'</td>
807 <td nowrap="nowrap">'.
808 $verElement.
809 $subElements['off'].
810 '</td>
811 '.$ctrlTable;
812
813 // Compile table row:
814 $tableRows[] = '
815 <tr class="bgColor4">
816 '.$mainCell.$verCell.'
817 </tr>';
818
819 // Reset the main cell:
820 $mainCell = '';
821
822 }
823 }
824 }
825 }
826
827 return $tableRows;
828 }
829
830 /**
831 * Building up of the $pArray
832 * (Internal)
833 *
834 * @param array Array that is built up with the page tree structure
835 * @param string Table name
836 * @param array Table row
837 * @return void $pArray is passed by reference and modified internally
838 */
839 function displayWorkspaceOverview_setInPageArray(&$pArray,$table,$row) {
840 if (!$this->details || $this->details==$table.':'.$row['uid']) {
841 $pArray[$table][$row['t3ver_oid']][] = $row;
842 }
843 }
844
845
846
847
848
849
850
851
852
853
854 /**************************************
855 *
856 * Helper functions (REDUNDANT FROM user/ws/index.php - someone could refactor this...)
857 *
858 *************************************/
859
860 /**
861 * Formatting the version number for HTML output
862 *
863 * @param integer Version number
864 * @return string Version number for output
865 */
866 function formatVerId($verId) {
867 return '1.'.$verId;
868 }
869
870 /**
871 * Formatting workspace ID into a visual label
872 *
873 * @param integer Workspace ID
874 * @return string Workspace title
875 */
876 function formatWorkspace($wsid) {
877
878 // Render, if not cached:
879 if (!isset($this->formatWorkspace_cache[$wsid])) {
880 switch($wsid) {
881 case -1:
882 $this->formatWorkspace_cache[$wsid] = '[Offline]';
883 break;
884 case 0:
885 $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...
886 break;
887 default:
888 list($titleRec) = $GLOBALS['TYPO3_DB']->exec_SELECTgetRows('title','sys_workspace','uid='.intval($wsid).t3lib_BEfunc::deleteClause('sys_workspace'));
889 $this->formatWorkspace_cache[$wsid] = '['.$wsid.'] '.$titleRec['title'];
890 break;
891 }
892 }
893
894 return $this->formatWorkspace_cache[$wsid];
895 }
896
897 /**
898 * Format publishing count for version (lifecycle state)
899 *
900 * @param integer t3ver_count value (number of times it has been online)
901 * @return string String translation of count.
902 */
903 function formatCount($count) {
904
905 // Render, if not cached:
906 if (!isset($this->formatCount_cache[$count])) {
907 switch($count) {
908 case 0:
909 $this->formatCount_cache[$count] = 'Draft';
910 break;
911 case 1:
912 $this->formatCount_cache[$count] = 'Archive';
913 break;
914 default:
915 $this->formatCount_cache[$count] = 'Published '.$count.' times';
916 break;
917 }
918 }
919
920 return $this->formatCount_cache[$count];
921 }
922
923 /**
924 * Looking for versions of a record in other workspaces than the current
925 *
926 * @param string Table name
927 * @param integer Record uid
928 * @return string List of other workspace IDs
929 */
930 function versionsInOtherWS($table,$uid) {
931
932 // Check for duplicates:
933 // Select all versions of record NOT in this workspace:
934 $rows = $GLOBALS['TYPO3_DB']->exec_SELECTgetRows(
935 't3ver_wsid',
936 $table,
937 'pid=-1
938 AND t3ver_oid='.intval($uid).'
939 AND t3ver_wsid!='.intval($GLOBALS['BE_USER']->workspace).'
940 AND (t3ver_wsid=-1 OR t3ver_wsid>0)'.
941 t3lib_BEfunc::deleteClause($table),
942 '',
943 't3ver_wsid',
944 '',
945 't3ver_wsid'
946 );
947 if (count($rows)) {
948 return implode(',',array_keys($rows));
949 }
950 }
951
952 /**
953 * Looks up stage changes for version and displays a formatted view on mouseover.
954 *
955 * @param string Table name
956 * @param integer Record ID
957 * @param string HTML string to wrap the mouseover around (should be stage change links)
958 * @return string HTML code.
959 */
960 function showStageChangeLog($table,$id,$stageCommands) {
961 $rows = $GLOBALS['TYPO3_DB']->exec_SELECTgetRows(
962 'log_data,tstamp,userid',
963 'sys_log',
964 'action=6
965 AND tablename='.$GLOBALS['TYPO3_DB']->fullQuoteStr($table,'sys_log').'
966 AND recuid='.intval($id)
967 );
968
969 $entry = array();
970 foreach($rows as $dat) {
971 $data = unserialize($dat['log_data']);
972 $username = $this->be_user_Array[$dat['userid']] ? $this->be_user_Array[$dat['userid']]['username'] : '['.$dat['userid'].']';
973
974 switch($data['stage']) {
975 case 1:
976 $text = 'sent element to "Review"';
977 break;
978 case 10:
979 $text = 'approved for "Publish"';
980 break;
981 case -1:
982 $text = 'rejected element!';
983 break;
984 case 0:
985 $text = 'reset to "Editing"';
986 break;
987 default:
988 $text = '[undefined]';
989 break;
990 }
991 $text = t3lib_BEfunc::dateTime($dat['tstamp']).': "'.$username.'" '.$text;
992 $text.= ($data['comment']?'<br/>User Comment: <em>'.$data['comment'].'</em>':'');
993
994 $entry[] = $text;
995 }
996
997 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>'.
998 '<div class="logLayer" style="visibility: hidden; position: absolute;" id="log_'.$table.$id.'">'.implode('<hr/>',$entry).'</div>' : $stageCommands;
999 }
1000
1001 /**
1002 * Creates display of sub elements of a page when the swap mode is either "Page" or "Branch" (0 / ALL)
1003 *
1004 * @param integer Page uid (for either online or offline version, but it MUST have swapmode/treeLevel set to >0 (not -1 indicating element versioning)
1005 * @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!)
1006 * @param integer For offline versions; This is t3ver_oid, the original ID of the online page.
1007 * @return string HTML content.
1008 */
1009 function subElements($uid,$treeLevel,$origId=0) {
1010 global $TCA;
1011
1012 if (!$this->details && ($GLOBALS['BE_USER']->workspace===0 || !$this->MOD_SETTINGS['expandSubElements'])) { // In online workspace we have a reduced view because otherwise it will bloat the listing:
1013 return '<br/>
1014 <img'.t3lib_iconWorks::skinImg($this->doc->backPath,'gfx/ol/joinbottom.gif','width="18" height="16"').' align="top" alt="" title="" />'.
1015 ($origId ?
1016 '<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'))).'">'.
1017 '<span class="typo3-dimmed"><em>[Sub elements, click for details]</em><span></a>' :
1018 '<span class="typo3-dimmed"><em>[Sub elements]</em><span>');
1019 } else { // For an offline workspace, show sub elements:
1020
1021 $tCell = array();
1022
1023 // Find records that follow pages when swapping versions:
1024 $recList = array();
1025 foreach($TCA as $tN => $tCfg) {
1026 if ($tN!='pages' && ($treeLevel>0 || $TCA[$tN]['ctrl']['versioning_followPages'])) {
1027 $this->subElements_getNonPageRecords($tN, $uid, $recList);
1028 }
1029 }
1030
1031 // Render records collected above:
1032 $elCount = count($recList)-1;
1033 foreach($recList as $c => $comb) {
1034 list($tN,$rec) = $comb;
1035
1036 $this->subElements_renderItem(
1037 $tCell,
1038 $tN,
1039 $uid,
1040 $rec,
1041 $origId,
1042 $c==$elCount && $treeLevel==0 ? 1 : 0, // If true, will show bottom-join icon.
1043 ''
1044 );
1045 }
1046
1047 // For branch, dive into the subtree:
1048 if ($treeLevel>0) {
1049
1050 // Drawing tree:
1051 $tree = t3lib_div::makeInstance('t3lib_pageTree');
1052 $tree->init('AND '.$GLOBALS['BE_USER']->getPagePermsClause(1));
1053 $tree->makeHTML = 2; // 2=Also rendering depth-data into the result array
1054 $tree->getTree($uid, 99, '');
1055
1056 // Traverse page tree:
1057 foreach($tree->tree as $data) {
1058
1059 // Render page in table cell:
1060 $this->subElements_renderItem(
1061 $tCell,
1062 'pages',
1063 $uid,
1064 t3lib_BEfunc::getRecord('pages',$data['row']['uid']), // Needs all fields, at least more than what is given in $data['row']...
1065 $origId,
1066 2, // 2=the join icon and icon for the record is not rendered for pages (where all is in $data['HTML']
1067 $data['HTML']
1068 );
1069
1070 // Find all records from page and collect in $recList:
1071 $recList = array();
1072 foreach($TCA as $tN => $tCfg) {
1073 if ($tN!=='pages') {
1074 $this->subElements_getNonPageRecords($tN, $data['row']['uid'], $recList);
1075 }
1076 }
1077
1078 // Render records collected above:
1079 $elCount = count($recList)-1;
1080 foreach($recList as $c => $comb) {
1081 list($tN,$rec) = $comb;
1082
1083 $this->subElements_renderItem(
1084 $tCell,
1085 $tN,
1086 $uid,
1087 $rec,
1088 $origId,
1089 $c==$elCount?1:0, // If true, will show bottom-join icon.
1090 $data['HTML_depthData']
1091 );
1092 }
1093 }
1094 }
1095
1096 return '
1097 <!-- Sub-element tree for versions -->
1098 <table border="0" cellpadding="0" cellspacing="1" class="ver-subtree">
1099 '.implode('',$tCell).'
1100 </table>';
1101 }
1102 }
1103
1104 /**
1105 * Select records from a table and add them to recList
1106 *
1107 * @param string Table name (from TCA)
1108 * @param integer PID to select records from
1109 * @param array Array where records are accumulated, passed by reference
1110 * @return void
1111 */
1112 function subElements_getNonPageRecords($tN, $uid, &$recList) {
1113 global $TCA;
1114
1115 $records = $GLOBALS['TYPO3_DB']->exec_SELECTgetRows(
1116 '*',
1117 $tN,
1118 'pid='.intval($uid).
1119 ($TCA[$tN]['ctrl']['versioningWS'] ? ' AND t3ver_state=0' : '').
1120 t3lib_BEfunc::deleteClause($tN),
1121 '',
1122 $TCA[$tN]['ctrl']['sortby'] ? $TCA[$tN]['ctrl']['sortby'] : $GLOBALS['TYPO3_DB']->stripOrderBy($TCA[$tN]['ctrl']['default_sortby'])
1123 );
1124
1125 foreach($records as $rec) {
1126 $recList[] = array($tN,$rec);
1127 }
1128 }
1129
1130 /**
1131 * Render a single item in a subelement list into a table row:
1132 *
1133 * @param array Table rows, passed by reference
1134 * @param string Table name
1135 * @param integer Page uid for which the subelements are selected/shown
1136 * @param array Row of element in list
1137 * @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.
1138 * @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)
1139 * @param string Prefix HTML data (icons for tree rendering)
1140 * @return void (Content accumulated in $tCell!)
1141 */
1142 function subElements_renderItem(&$tCell,$tN,$uid,$rec,$origId,$iconMode,$HTMLdata) {
1143 global $TCA;
1144
1145 // Initialize:
1146 $origUidFields = $TCA[$tN]['ctrl']['origUid'];
1147 $diffCode = '';
1148
1149 if ($origUidFields) { // If there is a field for this table with original uids we will use that to connect records:
1150 if (!$origId) { // In case we are displaying the online originals:
1151 $this->targets['orig_'.$uid.'_'.$tN.'_'.$rec['uid']] = $rec; // Build up target array (important that
1152 $tdParams = ' id="orig_'.$uid.'_'.$tN.'_'.$rec['uid'].'" class="typo3-ver"'; // Setting ID of the table row
1153 } else { // Version branch:
1154 if ($this->targets['orig_'.$origId.'_'.$tN.'_'.$rec[$origUidFields]]) { // If there IS a corresponding original record...:
1155
1156 // Prepare Table row parameters:
1157 $tdParams = ' onmouseover="hlSubelements(\''.$origId.'_'.$tN.'_'.$rec[$origUidFields].'\', \''.$uid.'_'.$tN.'_'.$rec[$origUidFields].'\', 1, '.($this->MOD_SETTINGS['diff']==2?1:0).');"'.
1158 ' onmouseout="hlSubelements(\''.$origId.'_'.$tN.'_'.$rec[$origUidFields].'\', \''.$uid.'_'.$tN.'_'.$rec[$origUidFields].'\', 0, '.($this->MOD_SETTINGS['diff']==2?1:0).');"'.
1159 ' id="ver_'.$uid.'_'.$tN.'_'.$rec[$origUidFields].'" class="typo3-ver"';
1160
1161 // Create diff view:
1162 if ($this->MOD_SETTINGS['diff']) {
1163 list($diffHTML,$diffPct) = $this->createDiffView($tN, $rec, $this->targets['orig_'.$origId.'_'.$tN.'_'.$rec[$origUidFields]]);
1164
1165 if ($this->MOD_SETTINGS['diff']==2) {
1166 $diffCode =
1167 ($diffPct ? '<span class="nobr">'.$diffPct.'% change</span>' : '-').
1168 '<div style="visibility: hidden; position: absolute;" id="diff_'.$uid.'_'.$tN.'_'.$rec[$origUidFields].'" class="diffLayer">'.
1169 $diffHTML.
1170 '</div>';
1171 } else {
1172 $diffCode =
1173 ($diffPct<0 ? 'N/A' : ($diffPct ? $diffPct.'% change:' : '')).
1174 $diffHTML;
1175 }
1176 }
1177
1178 // Unsetting the target fields allows us to mark all originals without a version in the subtree (see ->markupNewOriginals())
1179 unset($this->targets['orig_'.$origId.'_'.$tN.'_'.$rec[$origUidFields]]);
1180 } else { // No original record, so must be new:
1181 $tdParams = ' class="typo3-ver-new"';
1182 }
1183 }
1184 } else { // If no original uid column is supported for this table we are forced NOT to display any diff or highlighting.
1185 $tdParams = ' class="typo3-ver-noComp"';
1186 }
1187
1188 // Compile the cell:
1189 $tCell[] = '
1190 <tr'.$tdParams.'>
1191 <td class="iconTitle">'.
1192 $HTMLdata.
1193 ($iconMode < 2 ?
1194 '<img'.t3lib_iconWorks::skinImg($this->doc->backPath,'gfx/ol/join'.($iconMode ? 'bottom' : '').'.gif','width="18" height="16"').' alt="" />'.
1195 t3lib_iconWorks::getIconImage($tN, $rec, $this->doc->backPath,'') : '').
1196 t3lib_BEfunc::getRecordTitle($tN, $rec, TRUE).
1197 '</td>
1198 <td class="cmdCell">'.
1199 $this->displayWorkspaceOverview_commandLinksSub($tN,$rec,$origId).
1200 '</td>'.($origId ? '<td class="diffCell">'.
1201 $diffCode.
1202 '</td>':'').'
1203 </tr>';
1204 }
1205
1206 /**
1207 * JavaScript code to mark up new records that are online (in sub element lists)
1208 *
1209 * @return string HTML javascript section
1210 */
1211 function markupNewOriginals() {
1212
1213 if (count($this->targets)) {
1214 $scriptCode = '';
1215 foreach($this->targets as $key => $rec) {
1216 $scriptCode.='
1217 document.getElementById(\''.$key.'\').attributes.getNamedItem("class").nodeValue = \'typo3-ver-new\';
1218 ';
1219 }
1220
1221 return $this->doc->wrapScriptTags($scriptCode);
1222 }
1223 }
1224
1225 /**
1226 * Create visual difference view of two records. Using t3lib_diff library
1227 *
1228 * @param string Table name
1229 * @param array New version record (green)
1230 * @param array Old version record (red)
1231 * @return array Array with two keys (0/1) with HTML content / percentage integer (if -1, then it means N/A) indicating amount of change
1232 */
1233 function createDiffView($table, $diff_1_record, $diff_2_record) {
1234 global $TCA;
1235
1236 // Initialize:
1237 $pctChange = 'N/A';
1238
1239 // Check that records are arrays:
1240 if (is_array($diff_1_record) && is_array($diff_2_record)) {
1241
1242 // Load full table description and initialize diff-object:
1243 t3lib_div::loadTCA($table);
1244 $t3lib_diff_Obj = t3lib_div::makeInstance('t3lib_diff');
1245
1246 // Add header row:
1247 $tRows = array();
1248 $tRows[] = '
1249 <tr class="bgColor5 tableheader">
1250 <td>Fieldname:</td>
1251 <td width="98%" nowrap="nowrap">Colored diff-view:</td>
1252 </tr>
1253 ';
1254
1255 // Initialize variables to pick up string lengths in:
1256 $allStrLen = 0;
1257 $diffStrLen = 0;
1258
1259 // Traversing the first record and process all fields which are editable:
1260 foreach($diff_1_record as $fN => $fV) {
1261 if ($TCA[$table]['columns'][$fN] && $TCA[$table]['columns'][$fN]['config']['type']!='passthrough' && !t3lib_div::inList('t3ver_label',$fN)) {
1262
1263 // Check if it is files:
1264 $isFiles = FALSE;
1265 if (strcmp(trim($diff_1_record[$fN]),trim($diff_2_record[$fN])) &&
1266 $TCA[$table]['columns'][$fN]['config']['type']=='group' &&
1267 $TCA[$table]['columns'][$fN]['config']['internal_type']=='file') {
1268
1269 // Initialize:
1270 $uploadFolder = $TCA[$table]['columns'][$fN]['config']['uploadfolder'];
1271 $files1 = array_flip(t3lib_div::trimExplode(',', $diff_1_record[$fN],1));
1272 $files2 = array_flip(t3lib_div::trimExplode(',', $diff_2_record[$fN],1));
1273
1274 // Traverse filenames and read their md5 sum:
1275 foreach($files1 as $filename => $tmp) {
1276 $files1[$filename] = @is_file(PATH_site.$uploadFolder.'/'.$filename) ? md5(t3lib_div::getUrl(PATH_site.$uploadFolder.'/'.$filename)) : $filename;
1277 }
1278 foreach($files2 as $filename => $tmp) {
1279 $files2[$filename] = @is_file(PATH_site.$uploadFolder.'/'.$filename) ? md5(t3lib_div::getUrl(PATH_site.$uploadFolder.'/'.$filename)) : $filename;
1280 }
1281
1282 // Implode MD5 sums and set flag:
1283 $diff_1_record[$fN] = implode(' ',$files1);
1284 $diff_2_record[$fN] = implode(' ',$files2);
1285 $isFiles = TRUE;
1286 }
1287
1288 // If there is a change of value:
1289 if (strcmp(trim($diff_1_record[$fN]),trim($diff_2_record[$fN]))) {
1290
1291
1292 // Get the best visual presentation of the value and present that:
1293 $val1 = t3lib_BEfunc::getProcessedValue($table,$fN,$diff_2_record[$fN],0,1);
1294 $val2 = t3lib_BEfunc::getProcessedValue($table,$fN,$diff_1_record[$fN],0,1);
1295
1296 // Make diff result and record string lenghts:
1297 $diffres = $t3lib_diff_Obj->makeDiffDisplay($val1,$val2,$isFiles?'div':'span');
1298 $diffStrLen+= $t3lib_diff_Obj->differenceLgd;
1299 $allStrLen+= strlen($val1.$val2);
1300
1301 // If the compared values were files, substituted MD5 hashes:
1302 if ($isFiles) {
1303 $allFiles = array_merge($files1,$files2);
1304 foreach($allFiles as $filename => $token) {
1305 if (strlen($token)==32 && strstr($diffres,$token)) {
1306 $filename =
1307 t3lib_BEfunc::thumbCode(array($fN=>$filename),$table,$fN,$this->doc->backPath).
1308 $filename;
1309 $diffres = str_replace($token,$filename,$diffres);
1310 }
1311 }
1312 }
1313
1314 // Add table row with result:
1315 $tRows[] = '
1316 <tr class="bgColor4">
1317 <td>'.htmlspecialchars($GLOBALS['LANG']->sL(t3lib_BEfunc::getItemLabel($table,$fN))).'</td>
1318 <td width="98%">'.$diffres.'</td>
1319 </tr>
1320 ';
1321 } else {
1322 // Add string lengths even if value matched - in this was the change percentage is not high if only a single field is changed:
1323 $allStrLen+=strlen($diff_1_record[$fN].$diff_2_record[$fN]);
1324 }
1325 }
1326 }
1327
1328 // Calculate final change percentage:
1329 $pctChange = $allStrLen ? ceil($diffStrLen*100/$allStrLen) : -1;
1330
1331 // Create visual representation of result:
1332 if (count($tRows)>1) {
1333 $content.= '<table border="0" cellpadding="1" cellspacing="1" class="diffTable">'.implode('',$tRows).'</table>';
1334 } else {
1335 $content.= '<span class="nobr">'.$this->doc->icons(1).'Complete match on editable fields.</span>';
1336 }
1337 } else $content.= $this->doc->icons(3).'ERROR: Records could strangely not be found!';
1338
1339 // Return value:
1340 return array($content,$pctChange);
1341 }
1342
1343 /**
1344 * Links to stage change of a version
1345 *
1346 * @param string Table name
1347 * @param array Offline record (version)
1348 * @return string HTML content, mainly link tags and images.
1349 */
1350 function displayWorkspaceOverview_stageCmd($table,&$rec_off) {
1351 #debug($rec_off['t3ver_stage']);
1352 switch((int)$rec_off['t3ver_stage']) {
1353 case 0:
1354 $sId = 1;
1355 $sLabel = 'Editing';
1356 $color = '#666666';
1357 $label = 'Comment for Reviewer:';
1358 $titleAttrib = 'Send to Review';
1359 break;
1360 case 1:
1361 $sId = 10;
1362 $sLabel = 'Review';
1363 $color = '#6666cc';
1364 $label = 'Comment for Publisher:';
1365 $titleAttrib = 'Approve for Publishing';
1366 break;
1367 case 10:
1368 $sLabel = 'Publish';
1369 $color = '#66cc66';
1370 break;
1371 case -1:
1372 $sLabel = $this->doc->icons(2).'Rejected';
1373 $sId = 0;
1374 $color = '#ff0000';
1375 $label = 'Comment:';
1376 $titleAttrib = 'Reset stage';
1377 break;
1378 default:
1379 $sLabel = 'Undefined';
1380 $sId = 0;
1381 $color = '';
1382 break;
1383 }
1384 #debug($sId);
1385
1386 $raiseOk = !$GLOBALS['BE_USER']->workspaceCannotEditOfflineVersion($table,$rec_off);
1387
1388 if ($raiseOk && $rec_off['t3ver_stage']!=-1) {
1389 $onClick = 'var commentTxt=window.prompt("Please explain why you reject:","");
1390 if (commentTxt!=null) {document.location="'.$this->doc->issueCommand(
1391 '&cmd['.$table.']['.$rec_off['uid'].'][version][action]=setStage'.
1392 '&cmd['.$table.']['.$rec_off['uid'].'][version][stageId]=-1'
1393 ).'&cmd['.$table.']['.$rec_off['uid'].'][version][comment]="+escape(commentTxt);}'.
1394 ' return false;';
1395 // Reject:
1396 $actionLinks.=
1397 '<a href="#" onclick="'.htmlspecialchars($onClick).'">'.
1398 '<img'.t3lib_iconWorks::skinImg($this->doc->backPath,'gfx/down.gif','width="14" height="14"').' alt="" align="top" title="Reject" />'.
1399 '</a>';
1400 } else {
1401 // Reject:
1402 $actionLinks.=
1403 '<img src="'.$this->doc->backPath.'gfx/clear.gif" width="14" height="14" alt="" align="top" title="" />';
1404 }
1405
1406 $actionLinks.= '<span style="background-color: '.$color.'; color: white;">'.$sLabel.'</span>';
1407
1408 // Raise
1409 if ($raiseOk) {
1410 $onClick = 'var commentTxt=window.prompt("'.$label.'","");
1411 if (commentTxt!=null) {document.location="'.$this->doc->issueCommand(
1412 '&cmd['.$table.']['.$rec_off['uid'].'][version][action]=setStage'.
1413 '&cmd['.$table.']['.$rec_off['uid'].'][version][stageId]='.$sId
1414 ).'&cmd['.$table.']['.$rec_off['uid'].'][version][comment]="+escape(commentTxt);}'.
1415 ' return false;';
1416 if ($rec_off['t3ver_stage']!=10) {
1417 $actionLinks.=
1418 '<a href="#" onclick="'.htmlspecialchars($onClick).'">'.
1419 '<img'.t3lib_iconWorks::skinImg($this->doc->backPath,'gfx/up.gif','width="14" height="14"').' alt="" align="top" title="'.htmlspecialchars($titleAttrib).'" />'.
1420 '</a>';
1421 }
1422 }
1423 return $actionLinks;
1424 }
1425
1426 /**
1427 * Links to publishing etc of a version
1428 *
1429 * @param string Table name
1430 * @param array Online record
1431 * @param array Offline record (version)
1432 * @param string Swap type, "branch", "page" or "element"
1433 * @return string HTML content, mainly link tags and images.
1434 */
1435 function displayWorkspaceOverview_commandLinks($table,&$rec_on,&$rec_off,$vType) {
1436 if ($this->publishAccess && (!($GLOBALS['BE_USER']->workspaceRec['publish_access']&1) || (int)$rec_off['t3ver_stage']===10)) {
1437 $actionLinks =
1438 '<a href="'.htmlspecialchars($this->doc->issueCommand(
1439 '&cmd['.$table.']['.$rec_on['uid'].'][version][action]=swap'.
1440 '&cmd['.$table.']['.$rec_on['uid'].'][version][swapWith]='.$rec_off['uid']
1441 )).'">'.
1442 '<img'.t3lib_iconWorks::skinImg($this->doc->backPath,'gfx/insert1.gif','width="14" height="14"').' alt="" align="top" title="Publish" />'.
1443 '</a>';
1444 if ($GLOBALS['BE_USER']->workspaceSwapAccess() && (int)$rec_on['t3ver_state']!==1 && (int)$rec_off['t3ver_state']!==2) {
1445 $actionLinks.=
1446 '<a href="'.htmlspecialchars($this->doc->issueCommand(
1447 '&cmd['.$table.']['.$rec_on['uid'].'][version][action]=swap'.
1448 '&cmd['.$table.']['.$rec_on['uid'].'][version][swapWith]='.$rec_off['uid'].
1449 '&cmd['.$table.']['.$rec_on['uid'].'][version][swapIntoWS]=1'
1450 )).'">'.
1451 '<img'.t3lib_iconWorks::skinImg($this->doc->backPath,'gfx/swap.png','width="14" height="14"').' alt="" align="top" title="Swap" />'.
1452 '</a>';
1453 }
1454 }
1455
1456 if (!$GLOBALS['BE_USER']->workspaceCannotEditOfflineVersion($table,$rec_off)) {
1457 // Release
1458 $actionLinks.=
1459 '<a href="'.htmlspecialchars($this->doc->issueCommand('&cmd['.$table.']['.$rec_off['uid'].'][version][action]=clearWSID')).'" onclick="return confirm(\'Remove from workspace?\');">'.
1460 '<img'.t3lib_iconWorks::skinImg($this->doc->backPath,'gfx/group_clear.gif','width="14" height="14"').' alt="" align="top" title="Remove from workspace" />'.
1461 '</a>';
1462
1463 // Edit
1464 if ($table==='pages' && $vType!=='element') {
1465 $tempUid = ($vType==='branch' || $GLOBALS['BE_USER']->workspace===0 ? $rec_off['uid'] : $rec_on['uid']);
1466 $actionLinks.=
1467 '<a href="#" onclick="top.loadEditId('.$tempUid.');top.goToModule(\''.$this->pageModule.'\'); return false;">'.
1468 '<img'.t3lib_iconWorks::skinImg($this->doc->backPath,t3lib_extMgm::extRelPath('cms').'layout/layout.gif','width="14" height="12"').' title="Edit page" alt="" />'.
1469 '</a>';
1470 } else {
1471 $params = '&edit['.$table.']['.$rec_off['uid'].']=edit';
1472 $actionLinks.=
1473 '<a href="#" onclick="'.htmlspecialchars(t3lib_BEfunc::editOnClick($params,$this->doc->backPath)).'">'.
1474 '<img'.t3lib_iconWorks::skinImg($this->doc->backPath,'gfx/edit2.gif','width="12" height="12"').' title="Edit element" alt="" />'.
1475 '</a>';
1476 }
1477 }
1478
1479 // History/Log
1480 $actionLinks.=
1481 '<a href="'.htmlspecialchars($this->doc->backPath.'show_rechis.php?element='.rawurlencode($table.':'.$rec_off['uid']).'&returnUrl='.rawurlencode(t3lib_div::getIndpEnv('REQUEST_URI'))).'">'.
1482 '<img'.t3lib_iconWorks::skinImg($this->doc->backPath,'gfx/history2.gif','width="13" height="12"').' title="Show Log" alt="" />'.
1483 '</a>';
1484
1485 // View
1486 if ($table==='pages') {
1487 $tempUid = ($vType==='branch' || $GLOBALS['BE_USER']->workspace===0 ? $rec_off['uid'] : $rec_on['uid']);
1488 $actionLinks.=
1489 '<a href="#" onclick="'.htmlspecialchars(t3lib_BEfunc::viewOnClick($tempUid,$this->doc->backPath,t3lib_BEfunc::BEgetRootLine($tempUid))).'">'.
1490 '<img'.t3lib_iconWorks::skinImg($this->doc->backPath,'gfx/zoom.gif','width="12" height="12"').' title="" alt="" />'.
1491 '</a>';
1492 }
1493
1494 return $actionLinks;
1495 }
1496
1497 /**
1498 * Links to publishing etc of a version
1499 *
1500 * @param string Table name
1501 * @param array Record
1502 * @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.
1503 * @return string HTML content, mainly link tags and images.
1504 */
1505 function displayWorkspaceOverview_commandLinksSub($table,$rec,$origId) {
1506 $uid = $rec['uid'];
1507 if ($origId || $GLOBALS['BE_USER']->workspace===0) {
1508 if (!$GLOBALS['BE_USER']->workspaceCannotEditRecord($table,$rec)) {
1509 // Edit
1510 if ($table==='pages') {
1511 $actionLinks.=
1512 '<a href="#" onclick="top.loadEditId('.$uid.');top.goToModule(\''.$this->pageModule.'\'); return false;">'.
1513 '<img'.t3lib_iconWorks::skinImg($this->doc->backPath,t3lib_extMgm::extRelPath('cms').'layout/layout.gif','width="14" height="12"').' title="Edit page" alt="" />'.
1514 '</a>';
1515 } else {
1516 $params = '&edit['.$table.']['.$uid.']=edit';
1517 $actionLinks.=
1518 '<a href="#" onclick="'.htmlspecialchars(t3lib_BEfunc::editOnClick($params,$this->doc->backPath)).'">'.
1519 '<img'.t3lib_iconWorks::skinImg($this->doc->backPath,'gfx/edit2.gif','width="12" height="12"').' title="Edit element" alt="" />'.
1520 '</a>';
1521 }
1522 }
1523
1524 // History/Log
1525 $actionLinks.=
1526 '<a href="'.htmlspecialchars($this->doc->backPath.'show_rechis.php?element='.rawurlencode($table.':'.$uid).'&returnUrl='.rawurlencode(t3lib_div::getIndpEnv('REQUEST_URI'))).'">'.
1527 '<img'.t3lib_iconWorks::skinImg($this->doc->backPath,'gfx/history2.gif','width="13" height="12"').' title="Show Log" alt="" />'.
1528 '</a>';
1529 }
1530
1531 // View
1532 if ($table==='pages') {
1533 $actionLinks.=
1534 '<a href="#" onclick="'.htmlspecialchars(t3lib_BEfunc::viewOnClick($uid,$this->doc->backPath,t3lib_BEfunc::BEgetRootLine($uid))).'">'.
1535 '<img'.t3lib_iconWorks::skinImg($this->doc->backPath,'gfx/zoom.gif','width="12" height="12"').' title="" alt="" />'.
1536 '</a>';
1537 }
1538
1539 return $actionLinks;
1540 }
1541 }
1542
1543
1544
1545 if (defined('TYPO3_MODE') && $TYPO3_CONF_VARS[TYPO3_MODE]['XCLASS']['ext/version/cm1/index.php']) {
1546 include_once($TYPO3_CONF_VARS[TYPO3_MODE]['XCLASS']['ext/version/cm1/index.php']);
1547 }
1548
1549
1550
1551
1552 // Make instance:
1553 $SOBE = t3lib_div::makeInstance('tx_version_cm1');
1554 $SOBE->init();
1555
1556
1557 $SOBE->main();
1558 $SOBE->printContent();
1559
1560 ?>