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