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