* Direct styles changes to CSS classes
[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 * @author Dmitry Dulepov <typo3@accio.lv>
34 */
35 /**
36 * [CLASS/FUNCTION INDEX of SCRIPT]
37 *
38 *
39 *
40 * 120: class SC_mod_user_ws_index extends t3lib_SCbase
41 *
42 * SECTION: Standard module initialization
43 * 157: function menuConfig()
44 * 204: function init()
45 * 282: function main()
46 * 323: function printContent()
47 *
48 * SECTION: Module content: Publish
49 * 353: function moduleContent_publish()
50 * 454: function displayVersionDetails($details)
51 * 463: function displayWorkspaceOverview()
52 * 538: function displayWorkspaceOverview_list($pArray, $tableRows=array(), $c=0, $warnAboutVersions=FALSE)
53 * 726: function displayWorkspaceOverview_pageTreeIconTitle($pageUid, $title, $indentCount)
54 * 741: function displayWorkspaceOverview_stageCmd($table,&$rec_off)
55 * 831: function displayWorkspaceOverview_commandLinks($table,&$rec_on,&$rec_off,$vType)
56 * 904: function displayWorkspaceOverview_commandLinksSub($table,$rec,$origId)
57 * 952: function displayWorkspaceOverview_setInPageArray(&$pArray,$rlArr,$table,$row)
58 * 983: function subElements($uid,$treeLevel,$origId=0)
59 * 1086: function subElements_getNonPageRecords($tN, $uid, &$recList)
60 * 1116: function subElements_renderItem(&$tCell,$tN,$uid,$rec,$origId,$iconMode,$HTMLdata)
61 * 1185: function markupNewOriginals()
62 * 1207: function createDiffView($table, $diff_1_record, $diff_2_record)
63 *
64 * SECTION: Module content: Workspace list
65 * 1339: function moduleContent_workspaceList()
66 * 1354: function workspaceList_displayUserWorkspaceList()
67 * 1431: function workspaceList_getUserWorkspaceList()
68 * 1473: function workspaceList_formatWorkspaceData(&$wksp)
69 * 1515: function workspaceList_getWebMountPoints(&$wksp)
70 * 1564: function workspaceList_getFileMountPoints(&$wksp)
71 * 1617: function workspaceList_displayUserWorkspaceListHeader()
72 * 1637: function workspaceList_getUserList(&$wksp)
73 * 1664: function workspaceList_getUserListForSysWorkspace(&$wksp)
74 * 1691: function workspaceList_getUserListWithAccess(&$list, $access)
75 * 1764: function workspaceList_displayIcons($currentWorkspace, &$wksp)
76 * 1812: function workspaceList_hasEditAccess(&$wksp)
77 * 1824: function workspaceList_createFakeWorkspaceRecord($uid)
78 *
79 * SECTION: Helper functions
80 * 1889: function formatVerId($verId)
81 * 1899: function formatWorkspace($wsid)
82 * 1926: function formatCount($count)
83 * 1954: function versionsInOtherWS($table,$uid)
84 * 1984: function showStageChangeLog($table,$id,$stageCommands)
85 *
86 * SECTION: Processing
87 * 2045: function publishAction()
88 *
89 * TOTAL FUNCTIONS: 37
90 * (This index is automatically created/updated by the extension "extdeveval")
91 *
92 */
93
94
95 // Initialize module:
96 unset($MCONF);
97 require ('conf.php');
98 require ($BACK_PATH.'init.php');
99 require ($BACK_PATH.'template.php');
100 $BE_USER->modAccess($MCONF,1);
101
102 // Include libraries of various kinds used inside:
103 $LANG->includeLLFile('EXT:lang/locallang_mod_user_ws.xml');
104 require_once(PATH_t3lib.'class.t3lib_scbase.php');
105 require_once(PATH_typo3.'mod/user/ws/class.wslib.php');
106 require_once(PATH_t3lib.'class.t3lib_diff.php');
107 require_once(PATH_t3lib.'class.t3lib_pagetree.php');
108 require_once(PATH_t3lib.'class.t3lib_tcemain.php');
109
110
111
112
113 /**
114 * Module: Workspace manager
115 *
116 * @author Kasper Skaarhoj <kasperYYYY@typo3.com>
117 * @package TYPO3
118 * @subpackage core
119 */
120 class SC_mod_user_ws_index extends t3lib_SCbase {
121
122 // Static:
123 var $pageTreeIndent = 8;
124 var $pageTreeIndent_titleLgd = 30;
125
126 // Default variables for backend modules
127 var $MCONF = array(); // Module configuration
128 var $MOD_MENU = array(); // Module menu items
129 var $MOD_SETTINGS = array(); // Module session settings
130 var $doc; // Document Template Object
131 var $content; // Accumulated content
132
133
134 // Internal:
135 var $showWorkspaceCol = 0;
136 var $formatWorkspace_cache = array();
137 var $formatCount_cache = array();
138 var $targets = array(); // Accumulation of online targets.
139 var $pageModule = '';
140 var $publishAccess = FALSE;
141 var $be_user_Array = array();
142 var $be_user_Array_full = array(); // not blinded, used by workspace listing
143 var $stageIndex = array();
144
145
146 /*********************************
147 *
148 * Standard module initialization
149 *
150 *********************************/
151
152 /**
153 * Initialize menu configuration
154 *
155 * @return void
156 */
157 function menuConfig() {
158 global $LANG;
159
160 // Menu items:
161 $this->MOD_MENU = array(
162 'function' => array(
163 'publish' => $LANG->getLL('menuitem_review'),
164 'workspaces' => $LANG->getLL('menuitem_workspaces'),
165 ),
166 'filter' => array(
167 1 => $LANG->getLL('filter_drafts'),
168 2 => $LANG->getLL('filter_archive'),
169 0 => $LANG->getLL('filter_all'),
170 ),
171 'display' => array(
172 0 => '[Live workspace]',
173 -98 => 'Draft Workspaces',
174 -99 => 'All',
175 -1 => '[Default Draft]'
176 ),
177 'diff' => array(
178 0 => $LANG->getLL('diff_no_diff'),
179 1 => $LANG->getLL('diff_show_inline'),
180 2 => $LANG->getLL('diff_show_popup'),
181 ),
182 'expandSubElements' => '',
183 );
184
185 // Add workspaces:
186 if ($GLOBALS['BE_USER']->workspace===0) { // Spend time on this only in online workspace because it might take time:
187 $workspaces = $GLOBALS['TYPO3_DB']->exec_SELECTgetRows('uid,title,adminusers,members,reviewers','sys_workspace','pid=0'.t3lib_BEfunc::deleteClause('sys_workspace'),'','title');
188 foreach($workspaces as $rec) {
189 if ($GLOBALS['BE_USER']->checkWorkspace($rec)) {
190 $this->MOD_MENU['display'][$rec['uid']] = '['.$rec['uid'].'] '.$rec['title'];
191 }
192 }
193 }
194
195 // CLEANSE SETTINGS
196 $this->MOD_SETTINGS = t3lib_BEfunc::getModuleData($this->MOD_MENU, t3lib_div::_GP('SET'), $this->MCONF['name'], 'ses');
197 }
198
199 /**
200 * Standard init function of a module.
201 *
202 * @return void
203 */
204 function init() {
205 global $BACK_PATH, $BE_USER;
206
207 // Setting module configuration:
208 $this->MCONF = $GLOBALS['MCONF'];
209
210 // Initialize Document Template object:
211 $this->doc = t3lib_div::makeInstance('noDoc');
212 $this->doc->backPath = $BACK_PATH;
213 $this->doc->docType = 'xhtml_trans';
214
215 // JavaScript
216 $plusIcon = t3lib_iconWorks::skinImg($BACK_PATH, 'gfx/ol/plusbullet.gif', 'width="18" height="16"', 1);
217 $minusIcon = t3lib_iconWorks::skinImg($BACK_PATH, 'gfx/ol/minusbullet.gif', 'width="18" height="16"', 1);
218 $this->doc->JScode = $this->doc->wrapScriptTags('
219 script_ended = 0;
220 function jumpToUrl(URL) { //
221 document.location = URL;
222 }
223
224 function hlSubelements(origId, verId, over, diffLayer) { //
225 if (over) {
226 document.getElementById(\'orig_\'+origId).attributes.getNamedItem("class").nodeValue = \'typo3-ver-hl\';
227 document.getElementById(\'ver_\'+verId).attributes.getNamedItem("class").nodeValue = \'typo3-ver-hl\';
228 if (diffLayer) {
229 document.getElementById(\'diff_\'+verId).style.visibility = \'visible\';
230 }
231 } else {
232 document.getElementById(\'orig_\'+origId).attributes.getNamedItem("class").nodeValue = \'typo3-ver\';
233 document.getElementById(\'ver_\'+verId).attributes.getNamedItem("class").nodeValue = \'typo3-ver\';
234 if (diffLayer) {
235 document.getElementById(\'diff_\'+verId).style.visibility = \'hidden\';
236 }
237 }
238 }
239
240 function expandCollapse(rowNumber) { //
241 elementId = \'wl_\' + rowNumber;
242 element = document.getElementById(elementId);
243 image = document.getElementById(elementId + \'i\');
244 if (element.style) {
245 if (element.style.display == \'none\') {
246 element.style.display = \'\';
247 image.src = \'' . $minusIcon . '\';
248 } else {
249 element.style.display = \'none\';
250 image.src = \'' . $plusIcon . '\';
251 }
252 }
253 }
254 ');
255 $this->doc->form = '<form action="index.php" method="post" name="pageform">';
256
257 // Setting up the context sensitive menu:
258 $CMparts = $this->doc->getContextMenuCode();
259 $this->doc->JScode.= $CMparts[0];
260 $this->doc->bodyTagAdditions = $CMparts[1];
261 $this->doc->postCode.= $CMparts[2];
262
263 // Add JS for dynamic tabs:
264 $this->doc->JScode.= $this->doc->getDynTabMenuJScode();
265
266 // If another page module was specified, replace the default Page module with the new one
267 $newPageModule = trim($BE_USER->getTSConfigVal('options.overridePageModule'));
268 $this->pageModule = t3lib_BEfunc::isModuleSetInTBE_MODULES($newPageModule) ? $newPageModule : 'web_layout';
269
270 // Setting publish access permission for workspace:
271 $this->publishAccess = $BE_USER->workspacePublishAccess($BE_USER->workspace);
272
273 // Parent initialization:
274 parent::init();
275 }
276
277 /**
278 * Main function for Workspace Manager module.
279 *
280 * @return void
281 */
282 function main() {
283 global $LANG, $BE_USER, $BACK_PATH;
284
285 // See if we need to switch workspace
286 $changeWorkspace = t3lib_div::_GET('changeWorkspace');
287 if ($changeWorkspace != '') {
288 $BE_USER->setWorkspace($changeWorkspace);
289 $this->content = $this->doc->startPage($LANG->getLL('title'));
290 $this->content .= $this->doc->wrapScriptTags('top.document.location="' . $BACK_PATH . 'alt_main.php";');
291 }
292 else {
293 // Perform workspace publishing action if buttons are pressed:
294 $errors = $this->publishAction();
295
296 // Starting page:
297 $this->content.=$this->doc->startPage($LANG->getLL('title'));
298 $this->content.=$this->doc->header($LANG->getLL('title'));
299 $this->content.=$this->doc->spacer(5);
300
301 // Build top menu:
302 $menuItems = array();
303 $menuItems[] = array(
304 'label' => $LANG->getLL('menuitem_review'),
305 'content' => (count($errors) ? '<h3>' . $LANG->getLL('label_errors') . '</h3><br/>'.implode('<br/>',$errors).'<hr/>' : '').$this->moduleContent_publish()
306 );
307 $menuItems[] = array(
308 'label' => $LANG->getLL('menuitem_workspaces'),
309 'content' => $this->moduleContent_workspaceList()
310 );
311
312 // Add hidden fields and create tabs:
313 $content = $this->doc->getDynTabMenu($menuItems,'user_ws');
314 $this->content.=$this->doc->section('',$content,0,1);
315 }
316 }
317
318 /**
319 * Print module content. Called as last thing in the global scope.
320 *
321 * @return void
322 */
323 function printContent() {
324 global $SOBE;
325
326 $this->content.= $this->doc->endPage();
327 echo $this->content;
328 }
329
330
331
332
333
334
335
336
337
338
339
340
341
342 /*********************************
343 *
344 * Module content: Publish
345 *
346 *********************************/
347
348 /**
349 * Rendering the content for the publish and review panel in the workspace manager
350 *
351 * @return string HTML content
352 */
353 function moduleContent_publish() {
354 global $LANG;
355
356 // Initialize:
357 $content = '';
358 $details = t3lib_div::_GP('details');
359
360 // Create additional menus:
361 $menu = '';
362 if ($GLOBALS['BE_USER']->workspace===0) {
363 $menu.= t3lib_BEfunc::getFuncMenu(0,'SET[filter]',$this->MOD_SETTINGS['filter'],$this->MOD_MENU['filter']);
364 $menu.= t3lib_BEfunc::getFuncMenu(0,'SET[display]',$this->MOD_SETTINGS['display'],$this->MOD_MENU['display']);
365 }
366 $menu.= t3lib_BEfunc::getFuncMenu(0,'SET[diff]',$this->MOD_SETTINGS['diff'],$this->MOD_MENU['diff']);
367 if ($GLOBALS['BE_USER']->workspace!==0) {
368 $menu.= t3lib_BEfunc::getFuncCheck(0,'SET[expandSubElements]',$this->MOD_SETTINGS['expandSubElements']).' Show sub-elements - ';
369 }
370
371 // Create header:
372 $title = '';
373 $description = '';
374 switch($GLOBALS['BE_USER']->workspace) {
375 case 0:
376 $title = t3lib_iconWorks::getIconImage('sys_workspace', array(), $this->doc->backPath, ' align="top"').'[LIVE workspace]';
377 $description = $LANG->getLL('workspace_description_live');
378 break;
379 case -1:
380 $title = t3lib_iconWorks::getIconImage('sys_workspace', array(), $this->doc->backPath, ' align="top"').'[Draft workspace]';
381 $description = $LANG->getLL('workspace_description_draft');
382 break;
383 case -99:
384 $title = $this->doc->icons(3).'[NONE]';
385 $description = $LANG->getLL('workspace_description_no_access');
386 break;
387 default:
388 $title = t3lib_iconWorks::getIconImage('sys_workspace', $GLOBALS['BE_USER']->workspaceRec, $this->doc->backPath, ' align="top"').
389 '['.$GLOBALS['BE_USER']->workspace.'] '.t3lib_BEfunc::getRecordTitle('sys_workspace',$GLOBALS['BE_USER']->workspaceRec,TRUE);
390 $description = ($GLOBALS['BE_USER']->workspaceRec['description'] ? htmlspecialchars($GLOBALS['BE_USER']->workspaceRec['description']) : '<em>[None]</em>');
391 break;
392 }
393
394 // Buttons for publish / swap:
395 $actionLinks = '';
396 if ($GLOBALS['BE_USER']->workspace!==0) {
397 if ($this->publishAccess) {
398 $confirmation = $LANG->JScharCode($LANG->getLL(($GLOBALS['BE_USER']->workspaceRec['publish_access'] & 1) ? 'submit_publish_workspace_confirmation_1' : 'submit_publish_workspace_confirmation_2'));
399 $actionLinks.= '<input type="submit" name="_publish" value="' . $LANG->getLL('submit_publish_workspace') . '" onclick="return confirm(' . $confirmation . ');"/>';
400 if ($GLOBALS['BE_USER']->workspaceSwapAccess()) {
401 $confirmation = $LANG->JScharCode($LANG->getLL(($GLOBALS['BE_USER']->workspaceRec['publish_access'] & 1) ? 'submit_swap_workspace_confirmation_1' : 'submit_swap_workspace_confirmation_2'));
402 $actionLinks.= '<input type="submit" name="_swap" value="' . $LANG->getLL('submit_swap_workspace') . '" onclick="return confirm(' . $confirmation . ');" />';
403 }
404 } else {
405 $actionLinks.= $this->doc->icons(1) . $LANG->getLL('no_publish_permission');
406 }
407 }
408
409 $wsAccess = $GLOBALS['BE_USER']->checkWorkspace($GLOBALS['BE_USER']->workspaceRec);
410
411 // Add header to content variable:
412 $content = '
413 <table border="0" cellpadding="1" cellspacing="1" class="lrPadding" style="border: 1px solid black;">
414 <tr>
415 <td class="bgColor2" nowrap="nowrap"><b>' . $LANG->getLL('label_workspace') . '</b>&nbsp;</td>
416 <td class="bgColor4" nowrap="nowrap">'.$title.'</td>
417 </tr>
418 <tr>
419 <td class="bgColor2" nowrap="nowrap"><b>' . $LANG->getLL('label_description') . '</b>&nbsp;</td>
420 <td class="bgColor4">'.$description.'</td>
421 </tr>'.($GLOBALS['BE_USER']->workspace!=-99 && !$details ? '
422 <tr>
423 <td class="bgColor2" nowrap="nowrap"><b>' . $LANG->getLL('label_options') . '</b>&nbsp;</td>
424 <td class="bgColor4">'.$menu.$actionLinks.'</td>
425 </tr>
426 <tr>
427 <td class="bgColor2" nowrap="nowrap"><b>' . $LANG->getLL('label_status') . '</b>&nbsp;</td>
428 <td class="bgColor4">Access level: ' . $GLOBALS['LANG']->getLL('workspace_list_access_' . $wsAccess['_ACCESS']) . '</td>
429 </tr>' : '').'
430 </table>
431 <br/>
432 ';
433
434 // Add publishing and review overview:
435 if ($GLOBALS['BE_USER']->workspace!=-99) {
436 if ($details) {
437 $content.= $this->displayVersionDetails($details);
438 } else {
439 $content.= $this->displayWorkspaceOverview();
440 }
441 $content.='<br/>';
442 }
443
444 // Return content:
445 return $content;
446 }
447
448 /**
449 * Display details for a single version from workspace
450 *
451 * @param string Version identification, made of table and uid
452 * @return string HTML string
453 */
454 function displayVersionDetails($details) {
455 return 'TODO: Show details for version "'.$details.'"<hr/><a href="index.php">BACK</a>';
456 }
457
458 /**
459 * Rendering the overview of versions in the current workspace
460 *
461 * @return string HTML (table)
462 */
463 function displayWorkspaceOverview() {
464 global $LANG;
465
466 // Initialize variables:
467 $this->showWorkspaceCol = $GLOBALS['BE_USER']->workspace===0 && $this->MOD_SETTINGS['display']<=-98;
468
469 // Get usernames and groupnames
470 $be_group_Array = t3lib_BEfunc::getListGroupNames('title,uid');
471 $groupArray = array_keys($be_group_Array);
472 // Need 'admin' field for t3lib_iconWorks::getIconImage()
473 $this->be_user_Array_full = $this->be_user_Array = t3lib_BEfunc::getUserNames('username,usergroup,usergroup_cached_list,uid,admin,workspace_perms');
474 if (!$GLOBALS['BE_USER']->isAdmin()) $this->be_user_Array = t3lib_BEfunc::blindUserNames($this->be_user_Array,$groupArray,1);
475
476 // Initialize Workspace ID and filter-value:
477 if ($GLOBALS['BE_USER']->workspace===0) {
478 $wsid = $this->MOD_SETTINGS['display']; // Set wsid to the value from the menu (displaying content of other workspaces)
479 $filter = $this->MOD_SETTINGS['filter'];
480 } else {
481 $wsid = $GLOBALS['BE_USER']->workspace;
482 $filter = 0;
483 }
484
485 // Initialize workspace object and request all pending versions:
486 $wslibObj = t3lib_div::makeInstance('wslib');
487
488 // Selecting ALL versions belonging to the workspace:
489 $versions = $wslibObj->selectVersionsInWorkspace($wsid, $filter);
490
491 // Traverse versions and build page-display array:
492 $pArray = array();
493 foreach($versions as $table => $records) {
494 if (is_array($records)) {
495 foreach($records as $rec) {
496 $pageIdField = $table==='pages' ? 't3ver_oid' : 'realpid';
497 $this->displayWorkspaceOverview_setInPageArray(
498 $pArray,
499 t3lib_BEfunc::BEgetRootLine($rec[$pageIdField], 'AND 1=1'),
500 $table,
501 $rec
502 );
503 }
504 }
505 }
506
507 // Make header of overview:
508 $tableRows = array();
509 $tableRows[] = '
510 <tr class="bgColor5 tableheader">
511 <td nowrap="nowrap" width="100">' . $LANG->getLL('label_pagetree') . '</td>
512 <td nowrap="nowrap" colspan="2">' . $LANG->getLL('label_live_version') . '</td>
513 <td nowrap="nowrap" colspan="2">' . $LANG->getLL('label_draft_versions') . '</td>
514 <td nowrap="nowrap">' . $LANG->getLL('label_stage') . '</td>
515 <td nowrap="nowrap">' . $LANG->getLL('label_publish') . '</td>
516 <td>' . $LANG->getLL('label_lifecycle') . '</td>
517 '.($this->showWorkspaceCol ? '<td>' . $LANG->getLL('label_workspace') . '</td>' : '').'
518 </tr>';
519
520 // Add lines from overview:
521 $tableRows = array_merge($tableRows, $this->displayWorkspaceOverview_list($pArray));
522
523 $table = '<table border="0" cellpadding="0" cellspacing="1" class="lrPadding workspace-overview">'.implode('',$tableRows).'</table>';
524
525 return $table.$this->markupNewOriginals();
526 }
527
528 /**
529 * Rendering the content for the publish / review overview:
530 * (Made for internal recursive calling)
531 *
532 * @param array Hierarchical storage of the elements to display (see displayWorkspaceOverview() / displayWorkspaceOverview_setInPageArray())
533 * @param array Existing array of table rows to add to
534 * @param array Depth counter
535 * @param boolean If set, a warning is shown if versions are found (internal flag)
536 * @return array Table rows, see displayWorkspaceOverview()
537 */
538 function displayWorkspaceOverview_list($pArray, $tableRows=array(), $c=0, $warnAboutVersions=FALSE) {
539 global $TCA;
540
541 // Initialize:
542 $fullColSpan = ($this->showWorkspaceCol?9:8);
543
544 // Traverse $pArray
545 if (is_array($pArray)) {
546 foreach($pArray as $k => $v) {
547 if (t3lib_div::testInt($k)) {
548
549 // If there are elements on this level, output a divider row which just creates a little visual space.
550 if (is_array($pArray[$k.'_'])) {
551 $tableRows[] = '
552 <tr>
553 <td colspan="'.$fullColSpan.'"><img src="clear.gif" width="1" height="3" alt="" /></td>
554 </tr>';
555 }
556
557 // Printing a level from the page tree with no additional content:
558 // 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)
559 if (!is_array($pArray[$k.'_']) || !is_array($pArray[$k.'_']['pages'])) {
560 $tableRows[] = '
561 <tr class="bgColor4-20">
562 <td nowrap="nowrap" colspan="'.$fullColSpan.'">'.
563 $this->displayWorkspaceOverview_pageTreeIconTitle($k,$pArray[$k],$c).
564 '</td>
565 </tr>';
566 }
567
568 // If there ARE elements on this level, print them:
569 $warnAboutVersions_next = $warnAboutVersions;
570 $warnAboutVersions_nonPages = FALSE;
571 $warnAboutVersions_page = FALSE;
572 if (is_array($pArray[$k.'_'])) {
573 foreach($pArray[$k.'_'] as $table => $oidArray) {
574 foreach($oidArray as $oid => $recs) {
575
576 // Get CURRENT online record and icon based on "t3ver_oid":
577 $rec_on = t3lib_BEfunc::getRecord($table,$oid);
578 $icon = t3lib_iconWorks::getIconImage($table, $rec_on, $this->doc->backPath,' align="top" title="'.t3lib_BEfunc::getRecordIconAltText($rec_on,$table).'"');
579 if ($GLOBALS['BE_USER']->workspace===0) { // Only edit online records if in ONLINE workspace:
580 $icon = $this->doc->wrapClickMenuOnIcon($icon, $table, $rec_on['uid'], 2, '', '+edit,view,info,delete');
581 }
582
583 // MAIN CELL / Online version display:
584 // 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)
585 $verLinkUrl = t3lib_extMgm::isLoaded('version') && $TCA[$table]['ctrl']['versioningWS'];
586 $origElement = $icon.
587 ($verLinkUrl ? '<a href="'.htmlspecialchars($this->doc->backPath.t3lib_extMgm::extRelPath('version').'cm1/index.php?table='.$table.'&uid='.$rec_on['uid']).'">' : '').
588 t3lib_BEfunc::getRecordTitle($table,$rec_on,TRUE).
589 ($verLinkUrl ? '</a>' : '');
590 $mainCell_rowSpan = count($recs)>1 ? ' rowspan="'.count($recs).'"' : '';
591 $mainCell = $table==='pages' ? '
592 <td class="bgColor4-20" nowrap="nowrap"'.$mainCell_rowSpan.'>'.
593 $this->displayWorkspaceOverview_pageTreeIconTitle($k,$pArray[$k],$c).
594 '</td>' : '
595 <td class="bgColor"'.$mainCell_rowSpan.'></td>';
596 $mainCell.= '
597 <td align="center"'.$mainCell_rowSpan.'>'.$this->formatVerId($rec_on['t3ver_id']).'</td>
598 <td nowrap="nowrap"'.$mainCell_rowSpan.'>'.
599 $origElement.
600 '###SUB_ELEMENTS###'. // For substitution with sub-elements, if any.
601 '</td>';
602
603 // Offline versions display:
604 // Traverse the versions of the element
605 foreach($recs as $rec) {
606
607 // Get the offline version record and icon:
608 $rec_off = t3lib_BEfunc::getRecord($table,$rec['uid']);
609 $icon = t3lib_iconWorks::getIconImage($table, $rec_off, $this->doc->backPath, ' align="top" title="'.t3lib_BEfunc::getRecordIconAltText($rec_off,$table).'"');
610 $icon = $this->doc->wrapClickMenuOnIcon($icon, $table, $rec_off['uid'], 2, '', '+edit,view,info,delete');
611
612 // Prepare diff-code:
613 if ($this->MOD_SETTINGS['diff']) {
614 if ($rec_on['t3ver_state']!=1) { // Not new record:
615 list($diffHTML,$diffPct) = $this->createDiffView($table, $rec_off, $rec_on);
616 $diffCode = ($diffPct<0 ? 'N/A' : ($diffPct ? $diffPct.'% change:' : '')).
617 $diffHTML;
618 } else {
619 $diffCode = $this->doc->icons(1).'New element'; // TODO Localize?
620 }
621 } else $diffCode = '';
622
623 // Prepare swap-mode values:
624 if ($table==='pages' && $rec_off['t3ver_swapmode']!=-1) {
625 if ($rec_off['t3ver_swapmode']>0) {
626 $vType = 'branch'; // Do not translate!
627 } else {
628 $vType = 'page'; // Do not translate!
629 }
630 } else {
631 $vType = 'element'; // Do not translate!
632 }
633
634 switch($vType) {
635 case 'element':
636 $swapLabel = ' [Element]'; // TODO Localize?
637 $swapClass = 'ver-element'; // Do not translate!
638 $warnAboutVersions_nonPages = $warnAboutVersions_page; // Setting this if sub elements are found with a page+content (must be rendered prior to this of course!)
639 break;
640 case 'page':
641 $swapLabel = ' [Page]'; // TODO Localize?
642 $swapClass = 'ver-page'; // Do not translate!
643 $warnAboutVersions_page = !$this->showWorkspaceCol; // This value is true only if multiple workspaces are shown and we need the opposite here.
644 break;
645 case 'branch':
646 $swapLabel = ' [Branch]'; // TODO Localize?
647 $swapClass = 'ver-branch'; // Do not translate!
648 $warnAboutVersions_next = !$this->showWorkspaceCol; // This value is true only if multiple workspaces are shown and we need the opposite here.
649 break;
650 }
651
652 // Modify main cell based on first version shown:
653 $subElements = array();
654 if ($table==='pages' && $rec_off['t3ver_swapmode']!=-1 && $mainCell) { // For "Page" and "Branch" swap modes where $mainCell is still carrying content (only first version)
655 $subElements['on'] = $this->subElements($rec_on['uid'], $rec_off['t3ver_swapmode']);
656 $subElements['off'] = $this->subElements($rec_off['uid'],$rec_off['t3ver_swapmode'],$rec_on['uid']);
657 }
658 $mainCell = str_replace('###SUB_ELEMENTS###', $subElements['on'], $mainCell);
659
660 // Create version element:
661 $versionsInOtherWS = $this->versionsInOtherWS($table, $rec_on['uid']);
662 $versionsInOtherWSWarning = $versionsInOtherWS && $GLOBALS['BE_USER']->workspace!==0 ? '<br/>'.$this->doc->icons(2).'Other version(s) in workspace '.$versionsInOtherWS : '';
663 $multipleWarning = (!$mainCell && $GLOBALS['BE_USER']->workspace!==0? '<br/>'.$this->doc->icons(3).'<b>Multiple versions in same workspace!</b>' : '');
664 $verWarning = $warnAboutVersions || ($warnAboutVersions_nonPages && $GLOBALS['TCA'][$table]['ctrl']['versioning_followPages'])? '<br/>'.$this->doc->icons(3).'<b>Version inside version!</b>' : '';
665 $verElement = $icon.
666 '<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'))).'">'.
667 t3lib_BEfunc::getRecordTitle($table,$rec_off,TRUE).
668 '</a>'.
669 $versionsInOtherWSWarning.
670 $multipleWarning.
671 $verWarning;
672 if ($diffCode) {
673 $verElement = '
674 <table border="0" cellpadding="0" cellspacing="0" class="ver-verElement">
675 <tr>
676 <td nowrap="nowrap" width="180">'.$verElement.'&nbsp;&nbsp;</td>
677 <td class="c-diffCell">'.$diffCode.'</td>
678 </tr>
679 </table>';
680 }
681
682 // Create version cell:
683 $verCell = '
684 <td align="center">'.$this->formatVerId($rec_off['t3ver_id']).'</td>
685 <td nowrap="nowrap">'.
686 $verElement.
687 $subElements['off'].
688 '</td>';
689
690 // Compile table row:
691 $tableRows[] = '
692 <tr class="bgColor4">
693 '.$mainCell.$verCell.'
694 <td nowrap="nowrap">'.$this->showStageChangeLog($table,$rec_off['uid'],$this->displayWorkspaceOverview_stageCmd($table,$rec_off)).'</td>
695 <td nowrap="nowrap" class="'.$swapClass.'">'.
696 $this->displayWorkspaceOverview_commandLinks($table,$rec_on,$rec_off,$vType).
697 htmlspecialchars($swapLabel).
698 '</td>
699 <td nowrap="nowrap">'.htmlspecialchars($this->formatCount($rec_off['t3ver_count'])).'</td>'. // Lifecycle
700 ($this->showWorkspaceCol ? '
701 <td nowrap="nowrap">'.htmlspecialchars($this->formatWorkspace($rec_off['t3ver_wsid'])).'</td>' : '').'
702 </tr>';
703
704 // Reset the main cell:
705 $mainCell = '';
706 }
707 }
708 }
709 }
710 // Call recursively for sub-rows:
711 $tableRows = $this->displayWorkspaceOverview_list($pArray[$k.'.'], $tableRows, $c+1, $warnAboutVersions_next);
712 }
713 }
714 }
715 return $tableRows;
716 }
717
718 /**
719 * Create indentation, icon and title for the page tree identification for the list.
720 *
721 * @param integer Page UID (record will be looked up again)
722 * @param string Page title
723 * @param integer Depth counter from displayWorkspaceOverview_list() used to indent the icon and title
724 * @return string HTML content
725 */
726 function displayWorkspaceOverview_pageTreeIconTitle($pageUid, $title, $indentCount) {
727 $pRec = t3lib_BEfunc::getRecord('pages',$pageUid);
728 return '<img src="clear.gif" width="1" height="1" hspace="'.($indentCount * $this->pageTreeIndent).'" align="top" alt="" />'. // Indenting page tree
729 t3lib_iconWorks::getIconImage('pages',$pRec,$this->doc->backPath,' align="top" title="'.t3lib_BEfunc::getRecordIconAltText($pRec,'pages').'"').
730 htmlspecialchars(t3lib_div::fixed_lgd_cs($title,$this->pageTreeIndent_titleLgd)).
731 '&nbsp;&nbsp;';
732 }
733
734 /**
735 * Links to stage change of a version
736 *
737 * @param string Table name
738 * @param array Offline record (version)
739 * @return string HTML content, mainly link tags and images.
740 */
741 function displayWorkspaceOverview_stageCmd($table,&$rec_off) {
742 #debug($rec_off['t3ver_stage']);
743 switch((int)$rec_off['t3ver_stage']) {
744 case 0:
745 $sId = 1;
746 $sLabel = 'Editing'; // TODO Localize
747 $color = '#666666'; // TODO Use CSS?
748 $label = 'Comment for Reviewer:'; // TODO Localize
749 $titleAttrib = 'Send to Review'; // TODO Localize
750 break;
751 case 1:
752 $sId = 10;
753 $sLabel = 'Review'; // TODO Localize
754 $color = '#6666cc'; // TODO Use CSS?
755 $label = 'Comment for Publisher:'; // TODO Localize
756 $titleAttrib = 'Approve for Publishing'; // TODO Localize
757 break;
758 case 10:
759 $sLabel = 'Publish'; // TODO Localize
760 $color = '#66cc66'; // TODO Use CSS?
761 break;
762 case -1:
763 $sLabel = $this->doc->icons(2).'Rejected'; // TODO Localize
764 $sId = 0;
765 $color = '#ff0000'; // TODO Use CSS?
766 $label = 'Comment:'; // TODO Localize
767 $titleAttrib = 'Reset stage'; // TODO Localize
768 break;
769 default:
770 $sLabel = 'Undefined'; // TODO Localize
771 $sId = 0;
772 $color = '';
773 break;
774 }
775 #debug($sId);
776
777 $raiseOk = !$GLOBALS['BE_USER']->workspaceCannotEditOfflineVersion($table,$rec_off);
778
779 if ($raiseOk && $rec_off['t3ver_stage']!=-1) {
780 // TODO Localize
781 $onClick = 'var commentTxt=window.prompt("Please explain why you reject:","");
782 if (commentTxt!=null) {document.location="'.$this->doc->issueCommand(
783 '&cmd['.$table.']['.$rec_off['uid'].'][version][action]=setStage'.
784 '&cmd['.$table.']['.$rec_off['uid'].'][version][stageId]=-1'
785 ).'&cmd['.$table.']['.$rec_off['uid'].'][version][comment]="+escape(commentTxt);}'.
786 ' return false;';
787 // Reject:
788 $actionLinks.=
789 '<a href="#" onclick="'.htmlspecialchars($onClick).'">'.
790 '<img'.t3lib_iconWorks::skinImg($this->doc->backPath,'gfx/down.gif','width="14" height="14"').' alt="" align="top" title="Reject" />'.
791 '</a>';
792 } else {
793 // Reject:
794 $actionLinks.=
795 '<img src="'.$this->doc->backPath.'gfx/clear.gif" width="14" height="14" alt="" align="top" title="" />';
796 }
797
798 // TODO Use CSS?
799 $actionLinks.= '<span style="background-color: '.$color.'; color: white;">'.$sLabel.'</span>';
800
801 // Raise
802 if ($raiseOk) {
803 $onClick = 'var commentTxt=window.prompt("'.$label.'","");
804 if (commentTxt!=null) {document.location="'.$this->doc->issueCommand(
805 '&cmd['.$table.']['.$rec_off['uid'].'][version][action]=setStage'.
806 '&cmd['.$table.']['.$rec_off['uid'].'][version][stageId]='.$sId
807 ).'&cmd['.$table.']['.$rec_off['uid'].'][version][comment]="+escape(commentTxt);}'.
808 ' return false;';
809 if ($rec_off['t3ver_stage']!=10) {
810 $actionLinks.=
811 '<a href="#" onclick="'.htmlspecialchars($onClick).'">'.
812 '<img'.t3lib_iconWorks::skinImg($this->doc->backPath,'gfx/up.gif','width="14" height="14"').' alt="" align="top" title="'.htmlspecialchars($titleAttrib).'" />'.
813 '</a>';
814
815 $this->stageIndex[$sId][$table][] = $rec_off['uid'];
816 }
817 }
818
819 return $actionLinks;
820 }
821
822 /**
823 * Links to publishing etc of a version
824 *
825 * @param string Table name
826 * @param array Online record
827 * @param array Offline record (version)
828 * @param string Swap type, "branch", "page" or "element"
829 * @return string HTML content, mainly link tags and images.
830 */
831 function displayWorkspaceOverview_commandLinks($table,&$rec_on,&$rec_off,$vType) {
832 global $LANG;
833
834 if ($this->publishAccess && (!($GLOBALS['BE_USER']->workspaceRec['publish_access']&1) || (int)$rec_off['t3ver_stage']===10)) {
835 $actionLinks =
836 '<a href="'.htmlspecialchars($this->doc->issueCommand(
837 '&cmd['.$table.']['.$rec_on['uid'].'][version][action]=swap'.
838 '&cmd['.$table.']['.$rec_on['uid'].'][version][swapWith]='.$rec_off['uid']
839 )).'">'.
840 '<img'.t3lib_iconWorks::skinImg($this->doc->backPath,'gfx/insert1.gif','width="14" height="14"').' alt="" align="top" title="' . $LANG->getLL('img_title_publish') . '" />'.
841 '</a>';
842 if ($GLOBALS['BE_USER']->workspaceSwapAccess() && (int)$rec_on['t3ver_state']!==1 && (int)$rec_off['t3ver_state']!==2) {
843 $actionLinks.=
844 '<a href="'.htmlspecialchars($this->doc->issueCommand(
845 '&cmd['.$table.']['.$rec_on['uid'].'][version][action]=swap'.
846 '&cmd['.$table.']['.$rec_on['uid'].'][version][swapWith]='.$rec_off['uid'].
847 '&cmd['.$table.']['.$rec_on['uid'].'][version][swapIntoWS]=1'
848 )).'">'.
849 '<img'.t3lib_iconWorks::skinImg($this->doc->backPath,'gfx/swap.png','width="14" height="14"').' alt="" align="top" title="' . $LANG->getLL('img_title_swap') . '" />'.
850 '</a>';
851 }
852 }
853
854 if (!$GLOBALS['BE_USER']->workspaceCannotEditOfflineVersion($table,$rec_off)) {
855 // Release
856 $confirm = $LANG->JScharCode($LANG->getLL('remove_from_ws_confirmation'));
857 $actionLinks.=
858 '<a href="'.htmlspecialchars($this->doc->issueCommand('&cmd['.$table.']['.$rec_off['uid'].'][version][action]=clearWSID')).'" onclick="return confirm(' . $confirm . ');">'.
859 '<img'.t3lib_iconWorks::skinImg($this->doc->backPath,'gfx/group_clear.gif','width="14" height="14"').' alt="" align="top" title="' . $LANG->getLL('img_title_remove_from_ws') . '" />'.
860 '</a>';
861
862 // Edit
863 if ($table==='pages' && $vType!=='element') {
864 $tempUid = ($vType==='branch' || $GLOBALS['BE_USER']->workspace===0 ? $rec_off['uid'] : $rec_on['uid']);
865 $actionLinks.=
866 '<a href="#" onclick="top.loadEditId('.$tempUid.');top.goToModule(\''.$this->pageModule.'\'); return false;">'.
867 '<img'.t3lib_iconWorks::skinImg($this->doc->backPath,t3lib_extMgm::extRelPath('cms').'layout/layout.gif','width="14" height="12"').' title="' . $LANG->getLL('img_title_edit_page') . '" alt="" />'.
868 '</a>';
869 } else {
870 $params = '&edit['.$table.']['.$rec_off['uid'].']=edit';
871 $actionLinks.=
872 '<a href="#" onclick="'.htmlspecialchars(t3lib_BEfunc::editOnClick($params,$this->doc->backPath)).'">'.
873 '<img'.t3lib_iconWorks::skinImg($this->doc->backPath,'gfx/edit2.gif','width="12" height="12"').' title="' . $LANG->getLL('img_title_edit_element') . '" alt="" />'.
874 '</a>';
875 }
876 }
877
878 // History/Log
879 $actionLinks.=
880 '<a href="'.htmlspecialchars($this->doc->backPath.'show_rechis.php?element='.rawurlencode($table.':'.$rec_off['uid']).'&returnUrl='.rawurlencode(t3lib_div::getIndpEnv('REQUEST_URI'))).'">'.
881 '<img'.t3lib_iconWorks::skinImg($this->doc->backPath,'gfx/history2.gif','width="13" height="12"').' title="' . $LANG->getLL('img_title_show_log') . '" alt="" />'.
882 '</a>';
883
884 // View
885 if ($table==='pages') {
886 $tempUid = ($vType==='branch' || $GLOBALS['BE_USER']->workspace===0 ? $rec_off['uid'] : $rec_on['uid']);
887 $actionLinks.=
888 '<a href="#" onclick="'.htmlspecialchars(t3lib_BEfunc::viewOnClick($tempUid,$this->doc->backPath,t3lib_BEfunc::BEgetRootLine($tempUid))).'">'.
889 '<img'.t3lib_iconWorks::skinImg($this->doc->backPath,'gfx/zoom.gif','width="12" height="12"').' title="" alt="" />'.
890 '</a>';
891 }
892
893 return $actionLinks;
894 }
895
896 /**
897 * Links to publishing etc of a version
898 *
899 * @param string Table name
900 * @param array Record
901 * @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.
902 * @return string HTML content, mainly link tags and images.
903 */
904 function displayWorkspaceOverview_commandLinksSub($table,$rec,$origId) {
905 $uid = $rec['uid'];
906 if ($origId || $GLOBALS['BE_USER']->workspace===0) {
907 if (!$GLOBALS['BE_USER']->workspaceCannotEditRecord($table,$rec)) {
908 // Edit
909 if ($table==='pages') {
910 $actionLinks.=
911 '<a href="#" onclick="top.loadEditId('.$uid.');top.goToModule(\''.$this->pageModule.'\'); return false;">'.
912 '<img'.t3lib_iconWorks::skinImg($this->doc->backPath,t3lib_extMgm::extRelPath('cms').'layout/layout.gif','width="14" height="12"').' title="' . $LANG->getLL('img_title_edit_page') . '" alt="" />'.
913 '</a>';
914 } else {
915 $params = '&edit['.$table.']['.$uid.']=edit';
916 $actionLinks.=
917 '<a href="#" onclick="'.htmlspecialchars(t3lib_BEfunc::editOnClick($params,$this->doc->backPath)).'">'.
918 '<img'.t3lib_iconWorks::skinImg($this->doc->backPath,'gfx/edit2.gif','width="12" height="12"').' title="' . $LANG->getLL('img_title_edit_element') . '" alt="" />'.
919 '</a>';
920 }
921 }
922
923 // History/Log
924 $actionLinks.=
925 '<a href="'.htmlspecialchars($this->doc->backPath.'show_rechis.php?element='.rawurlencode($table.':'.$uid).'&returnUrl='.rawurlencode(t3lib_div::getIndpEnv('REQUEST_URI'))).'">'.
926 '<img'.t3lib_iconWorks::skinImg($this->doc->backPath,'gfx/history2.gif','width="13" height="12"').' title="' . $LANG->getLL('img_title_show_log') . '" alt="" />'.
927 '</a>';
928 }
929
930 // View
931 if ($table==='pages') {
932 $actionLinks.=
933 '<a href="#" onclick="'.htmlspecialchars(t3lib_BEfunc::viewOnClick($uid,$this->doc->backPath,t3lib_BEfunc::BEgetRootLine($uid))).'">'.
934 '<img'.t3lib_iconWorks::skinImg($this->doc->backPath,'gfx/zoom.gif','width="12" height="12"').' title="" alt="" />'.
935 '</a>';
936 }
937
938 return $actionLinks;
939 }
940
941 /**
942 * 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
943 * (Internal)
944 * Made for recursive calling
945 *
946 * @param array Array that is built up with the page tree structure
947 * @param array Root line for element (table / row); The element is stored in pArray according to this root line.
948 * @param string Table name
949 * @param array Table row
950 * @return void $pArray is passed by reference and modified internally
951 */
952 function displayWorkspaceOverview_setInPageArray(&$pArray,$rlArr,$table,$row) {
953
954 // Initialize:
955 ksort($rlArr);
956 reset($rlArr);
957 if (!$rlArr[0]['uid']) array_shift($rlArr);
958
959 // Get and remove first element in root line:
960 $cEl = current($rlArr);
961 $pUid = $cEl['t3ver_oid'] ? $cEl['t3ver_oid'] : $cEl['uid']; // Done to pile up "false versions" in the right branch...
962
963 $pArray[$pUid] = $cEl['title'];
964 array_shift($rlArr);
965
966 // If there are elements left in the root line, call this function recursively (to build $pArray in depth)
967 if (count($rlArr)) {
968 if (!isset($pArray[$pUid.'.'])) $pArray[$pUid.'.'] = array();
969 $this->displayWorkspaceOverview_setInPageArray($pArray[$pUid.'.'],$rlArr,$table,$row);
970 } else { // If this was the last element, set the value:
971 $pArray[$pUid.'_'][$table][$row['t3ver_oid']][] = $row;
972 }
973 }
974
975 /**
976 * Creates display of sub elements of a page when the swap mode is either "Page" or "Branch" (0 / ALL)
977 *
978 * @param integer Page uid (for either online or offline version, but it MUST have swapmode/treeLevel set to >0 (not -1 indicating element versioning)
979 * @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!)
980 * @param integer For offline versions; This is t3ver_oid, the original ID of the online page.
981 * @return string HTML content.
982 */
983 function subElements($uid,$treeLevel,$origId=0) {
984 global $TCA;
985
986 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:
987 return '<br/>
988 <img'.t3lib_iconWorks::skinImg($this->doc->backPath,'gfx/ol/joinbottom.gif','width="18" height="16"').' align="top" alt="" title="" />'.
989 ($origId ?
990 '<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'))).'">'.
991 '<span class="typo3-dimmed"><em>[Sub elements, click for details]</em><span></a>' :
992 '<span class="typo3-dimmed"><em>[Sub elements]</em><span>');
993 } else { // For an offline workspace, show sub elements:
994
995 $tCell = array();
996
997 // Find records that follow pages when swapping versions:
998 $recList = array();
999 foreach($TCA as $tN => $tCfg) {
1000 if ($tN!='pages' && ($treeLevel>0 || $TCA[$tN]['ctrl']['versioning_followPages'])) {
1001 $this->subElements_getNonPageRecords($tN, $uid, $recList);
1002 }
1003 }
1004
1005 // Render records collected above:
1006 $elCount = count($recList)-1;
1007 foreach($recList as $c => $comb) {
1008 list($tN,$rec) = $comb;
1009
1010 $this->subElements_renderItem(
1011 $tCell,
1012 $tN,
1013 $uid,
1014 $rec,
1015 $origId,
1016 $c==$elCount && $treeLevel==0 ? 1 : 0, // If true, will show bottom-join icon.
1017 ''
1018 );
1019 }
1020
1021 // For branch, dive into the subtree:
1022 if ($treeLevel>0) {
1023
1024 // Drawing tree:
1025 $tree = t3lib_div::makeInstance('t3lib_pageTree');
1026 $tree->init('AND '.$GLOBALS['BE_USER']->getPagePermsClause(1));
1027 $tree->makeHTML = 2; // 2=Also rendering depth-data into the result array
1028 $tree->getTree($uid, 99, '');
1029
1030 // Traverse page tree:
1031 foreach($tree->tree as $data) {
1032
1033 // Render page in table cell:
1034 $this->subElements_renderItem(
1035 $tCell,
1036 'pages',
1037 $uid,
1038 t3lib_BEfunc::getRecord('pages',$data['row']['uid']), // Needs all fields, at least more than what is given in $data['row']...
1039 $origId,
1040 2, // 2=the join icon and icon for the record is not rendered for pages (where all is in $data['HTML']
1041 $data['HTML']
1042 );
1043
1044 // Find all records from page and collect in $recList:
1045 $recList = array();
1046 foreach($TCA as $tN => $tCfg) {
1047 if ($tN!=='pages') {
1048 $this->subElements_getNonPageRecords($tN, $data['row']['uid'], $recList);
1049 }
1050 }
1051
1052 // Render records collected above:
1053 $elCount = count($recList)-1;
1054 foreach($recList as $c => $comb) {
1055 list($tN,$rec) = $comb;
1056
1057 $this->subElements_renderItem(
1058 $tCell,
1059 $tN,
1060 $uid,
1061 $rec,
1062 $origId,
1063 $c==$elCount?1:0, // If true, will show bottom-join icon.
1064 $data['HTML_depthData']
1065 );
1066 }
1067 }
1068 }
1069
1070 return '
1071 <!-- Sub-element tree for versions -->
1072 <table border="0" cellpadding="0" cellspacing="1" class="ver-subtree">
1073 '.implode('',$tCell).'
1074 </table>';
1075 }
1076 }
1077
1078 /**
1079 * Select records from a table and add them to recList
1080 *
1081 * @param string Table name (from TCA)
1082 * @param integer PID to select records from
1083 * @param array Array where records are accumulated, passed by reference
1084 * @return void
1085 */
1086 function subElements_getNonPageRecords($tN, $uid, &$recList) {
1087 global $TCA;
1088
1089 $records = $GLOBALS['TYPO3_DB']->exec_SELECTgetRows(
1090 '*',
1091 $tN,
1092 'pid='.intval($uid).
1093 ($TCA[$tN]['ctrl']['versioningWS'] ? ' AND t3ver_state=0' : '').
1094 t3lib_BEfunc::deleteClause($tN),
1095 '',
1096 $TCA[$tN]['ctrl']['sortby'] ? $TCA[$tN]['ctrl']['sortby'] : $GLOBALS['TYPO3_DB']->stripOrderBy($TCA[$tN]['ctrl']['default_sortby'])
1097 );
1098
1099 foreach($records as $rec) {
1100 $recList[] = array($tN,$rec);
1101 }
1102 }
1103
1104 /**
1105 * Render a single item in a subelement list into a table row:
1106 *
1107 * @param array Table rows, passed by reference
1108 * @param string Table name
1109 * @param integer Page uid for which the subelements are selected/shown
1110 * @param array Row of element in list
1111 * @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.
1112 * @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)
1113 * @param string Prefix HTML data (icons for tree rendering)
1114 * @return void (Content accumulated in $tCell!)
1115 */
1116 function subElements_renderItem(&$tCell,$tN,$uid,$rec,$origId,$iconMode,$HTMLdata) {
1117 global $TCA;
1118
1119 // Initialize:
1120 $origUidFields = $TCA[$tN]['ctrl']['origUid'];
1121 $diffCode = '';
1122
1123 if ($origUidFields) { // If there is a field for this table with original uids we will use that to connect records:
1124 if (!$origId) { // In case we are displaying the online originals:
1125 $this->targets['orig_'.$uid.'_'.$tN.'_'.$rec['uid']] = $rec; // Build up target array (important that
1126 $tdParams = ' id="orig_'.$uid.'_'.$tN.'_'.$rec['uid'].'" class="typo3-ver"'; // Setting ID of the table row
1127 } else { // Version branch:
1128 if ($this->targets['orig_'.$origId.'_'.$tN.'_'.$rec[$origUidFields]]) { // If there IS a corresponding original record...:
1129
1130 // Prepare Table row parameters:
1131 $tdParams = ' onmouseover="hlSubelements(\''.$origId.'_'.$tN.'_'.$rec[$origUidFields].'\', \''.$uid.'_'.$tN.'_'.$rec[$origUidFields].'\', 1, '.($this->MOD_SETTINGS['diff']==2?1:0).');"'.
1132 ' onmouseout="hlSubelements(\''.$origId.'_'.$tN.'_'.$rec[$origUidFields].'\', \''.$uid.'_'.$tN.'_'.$rec[$origUidFields].'\', 0, '.($this->MOD_SETTINGS['diff']==2?1:0).');"'.
1133 ' id="ver_'.$uid.'_'.$tN.'_'.$rec[$origUidFields].'" class="typo3-ver"';
1134
1135 // Create diff view:
1136 if ($this->MOD_SETTINGS['diff']) {
1137 list($diffHTML,$diffPct) = $this->createDiffView($tN, $rec, $this->targets['orig_'.$origId.'_'.$tN.'_'.$rec[$origUidFields]]);
1138
1139 if ($this->MOD_SETTINGS['diff']==2) {
1140 $diffCode =
1141 ($diffPct ? '<span class="nobr">'.$diffPct.'% change</span>' : '-').
1142 '<div style="visibility: hidden; position: absolute;" id="diff_'.$uid.'_'.$tN.'_'.$rec[$origUidFields].'" class="diffLayer">'.
1143 $diffHTML.
1144 '</div>';
1145 } else {
1146 $diffCode =
1147 ($diffPct<0 ? 'N/A' : ($diffPct ? $diffPct.'% change:' : '')).
1148 $diffHTML;
1149 }
1150 }
1151
1152 // Unsetting the target fields allows us to mark all originals without a version in the subtree (see ->markupNewOriginals())
1153 unset($this->targets['orig_'.$origId.'_'.$tN.'_'.$rec[$origUidFields]]);
1154 } else { // No original record, so must be new:
1155 $tdParams = ' class="typo3-ver-new"';
1156 }
1157 }
1158 } else { // If no original uid column is supported for this table we are forced NOT to display any diff or highlighting.
1159 $tdParams = ' class="typo3-ver-noComp"';
1160 }
1161
1162 // Compile the cell:
1163 $tCell[] = '
1164 <tr'.$tdParams.'>
1165 <td class="iconTitle">'.
1166 $HTMLdata.
1167 ($iconMode < 2 ?
1168 '<img'.t3lib_iconWorks::skinImg($this->doc->backPath,'gfx/ol/join'.($iconMode ? 'bottom' : '').'.gif','width="18" height="16"').' alt="" />'.
1169 t3lib_iconWorks::getIconImage($tN, $rec, $this->doc->backPath,'') : '').
1170 t3lib_BEfunc::getRecordTitle($tN, $rec, TRUE).
1171 '</td>
1172 <td class="cmdCell">'.
1173 $this->displayWorkspaceOverview_commandLinksSub($tN,$rec,$origId).
1174 '</td>'.($origId ? '<td class="diffCell">'.
1175 $diffCode.
1176 '</td>':'').'
1177 </tr>';
1178 }
1179
1180 /**
1181 * JavaScript code to mark up new records that are online (in sub element lists)
1182 *
1183 * @return string HTML javascript section
1184 */
1185 function markupNewOriginals() {
1186
1187 if (count($this->targets)) {
1188 $scriptCode = '';
1189 foreach($this->targets as $key => $rec) {
1190 $scriptCode.='
1191 document.getElementById(\''.$key.'\').attributes.getNamedItem("class").nodeValue = \'typo3-ver-new\';
1192 ';
1193 }
1194
1195 return $this->doc->wrapScriptTags($scriptCode);
1196 }
1197 }
1198
1199 /**
1200 * Create visual difference view of two records. Using t3lib_diff library
1201 *
1202 * @param string Table name
1203 * @param array New version record (green)
1204 * @param array Old version record (red)
1205 * @return array Array with two keys (0/1) with HTML content / percentage integer (if -1, then it means N/A) indicating amount of change
1206 */
1207 function createDiffView($table, $diff_1_record, $diff_2_record) {
1208 global $TCA, $LANG;
1209
1210 // Initialize:
1211 $pctChange = 'N/A';
1212
1213 // Check that records are arrays:
1214 if (is_array($diff_1_record) && is_array($diff_2_record)) {
1215
1216 // Load full table description and initialize diff-object:
1217 t3lib_div::loadTCA($table);
1218 $t3lib_diff_Obj = t3lib_div::makeInstance('t3lib_diff');
1219
1220 // Add header row:
1221 $tRows = array();
1222 $tRows[] = '
1223 <tr class="bgColor5 tableheader">
1224 <td>' . $LANG->getLL('diffview_label_field_name') . '</td>
1225 <td width="98%" nowrap="nowrap">' . $LANG->getLL('diffview_label_colored_diff_view') . '</td>
1226 </tr>
1227 ';
1228
1229 // Initialize variables to pick up string lengths in:
1230 $allStrLen = 0;
1231 $diffStrLen = 0;
1232
1233 // Traversing the first record and process all fields which are editable:
1234 foreach($diff_1_record as $fN => $fV) {
1235 if ($TCA[$table]['columns'][$fN] && $TCA[$table]['columns'][$fN]['config']['type']!='passthrough' && !t3lib_div::inList('t3ver_label',$fN)) {
1236
1237 // Check if it is files:
1238 $isFiles = FALSE;
1239 if (strcmp(trim($diff_1_record[$fN]),trim($diff_2_record[$fN])) &&
1240 $TCA[$table]['columns'][$fN]['config']['type']=='group' &&
1241 $TCA[$table]['columns'][$fN]['config']['internal_type']=='file') {
1242
1243 // Initialize:
1244 $uploadFolder = $TCA[$table]['columns'][$fN]['config']['uploadfolder'];
1245 $files1 = array_flip(t3lib_div::trimExplode(',', $diff_1_record[$fN],1));
1246 $files2 = array_flip(t3lib_div::trimExplode(',', $diff_2_record[$fN],1));
1247
1248 // Traverse filenames and read their md5 sum:
1249 foreach($files1 as $filename => $tmp) {
1250 $files1[$filename] = @is_file(PATH_site.$uploadFolder.'/'.$filename) ? md5(t3lib_div::getUrl(PATH_site.$uploadFolder.'/'.$filename)) : $filename;
1251 }
1252 foreach($files2 as $filename => $tmp) {
1253 $files2[$filename] = @is_file(PATH_site.$uploadFolder.'/'.$filename) ? md5(t3lib_div::getUrl(PATH_site.$uploadFolder.'/'.$filename)) : $filename;
1254 }
1255
1256 // Implode MD5 sums and set flag:
1257 $diff_1_record[$fN] = implode(' ',$files1);
1258 $diff_2_record[$fN] = implode(' ',$files2);
1259 $isFiles = TRUE;
1260 }
1261
1262 // If there is a change of value:
1263 if (strcmp(trim($diff_1_record[$fN]),trim($diff_2_record[$fN]))) {
1264
1265
1266 // Get the best visual presentation of the value and present that:
1267 $val1 = t3lib_BEfunc::getProcessedValue($table,$fN,$diff_2_record[$fN],0,1);
1268 $val2 = t3lib_BEfunc::getProcessedValue($table,$fN,$diff_1_record[$fN],0,1);
1269
1270 // Make diff result and record string lenghts:
1271 $diffres = $t3lib_diff_Obj->makeDiffDisplay($val1,$val2,$isFiles?'div':'span');
1272 $diffStrLen+= $t3lib_diff_Obj->differenceLgd;
1273 $allStrLen+= strlen($val1.$val2);
1274
1275 // If the compared values were files, substituted MD5 hashes:
1276 if ($isFiles) {
1277 $allFiles = array_merge($files1,$files2);
1278 foreach($allFiles as $filename => $token) {
1279 if (strlen($token)==32 && strstr($diffres,$token)) {
1280 $filename =
1281 t3lib_BEfunc::thumbCode(array($fN=>$filename),$table,$fN,$this->doc->backPath).
1282 $filename;
1283 $diffres = str_replace($token,$filename,$diffres);
1284 }
1285 }
1286 }
1287
1288 // Add table row with result:
1289 $tRows[] = '
1290 <tr class="bgColor4">
1291 <td>'.htmlspecialchars($GLOBALS['LANG']->sL(t3lib_BEfunc::getItemLabel($table,$fN))).'</td>
1292 <td width="98%">'.$diffres.'</td>
1293 </tr>
1294 ';
1295 } else {
1296 // Add string lengths even if value matched - in this was the change percentage is not high if only a single field is changed:
1297 $allStrLen+=strlen($diff_1_record[$fN].$diff_2_record[$fN]);
1298 }
1299 }
1300 }
1301
1302 // Calculate final change percentage:
1303 $pctChange = $allStrLen ? ceil($diffStrLen*100/$allStrLen) : -1;
1304
1305 // Create visual representation of result:
1306 if (count($tRows)>1) {
1307 $content.= '<table border="0" cellpadding="1" cellspacing="1" class="diffTable">'.implode('',$tRows).'</table>';
1308 } else {
1309 $content.= '<span class="nobr">'.$this->doc->icons(1).$LANG->getLL('diffview_complete_match').'</span>';
1310 }
1311 } else $content.= $this->doc->icons(3).$LANG->getLL('diffview_cannot_find_records');
1312
1313 // Return value:
1314 return array($content,$pctChange);
1315 }
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328 /********************************
1329 *
1330 * Module content: Workspace list
1331 *
1332 ********************************/
1333
1334 /**
1335 * Rendering of the workspace list
1336 *
1337 * @return string HTML
1338 */
1339 function moduleContent_workspaceList() {
1340 // Original Kasper's TODO: Workspace listing
1341 //
1342 // - 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.
1343 // - SWITCHING: Switching between available workspaces is done by a button shown for each in the list
1344 // - 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)
1345 // - 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!).
1346 return $this->workspaceList_displayUserWorkspaceList();
1347 }
1348
1349 /**
1350 * Generates HTML to display a list of workspaces.
1351 *
1352 * @return string Generated HTML code
1353 */
1354 function workspaceList_displayUserWorkspaceList() {
1355 global $BACK_PATH, $LANG;
1356
1357 // table header
1358 $content = $this->workspaceList_displayUserWorkspaceListHeader();
1359
1360 // get & walk workspace list generating content
1361 $wkspList = $this->workspaceList_getUserWorkspaceList();
1362 $rowNum = 1;
1363 foreach ($wkspList as $wksp) {
1364 $currentWksp = ($GLOBALS['BE_USER']->workspace == $wksp['uid']);
1365
1366 // Each workspace data occupies two rows:
1367 // (1) Folding + Icons + Title + Description
1368 // (2) Information about workspace (initially hidden)
1369
1370 $cssClass = ($currentWksp ? 'bgColor3' : 'bgColor4');
1371 // Start first row
1372 $content .= '<tr class="' . $cssClass . '">';
1373
1374 // row #1, column #1: expand icon
1375 $content .= '<td>' .
1376 '<a href="javascript:expandCollapse(' . $rowNum . ')">' .
1377 '<img ' . t3lib_iconWorks::skinImg($BACK_PATH, 'gfx/ol/plusbullet.gif', 'width="18" height="16"') . ' id="wl_' . $rowNum . 'i" border="0" hspace="1" alt="' . $LANG->getLL('img_title_show_more') . '" />' .
1378 '</a></td>';
1379
1380 // row #1, column #2: icon panel
1381 $content .= '<td nowrap="nowrap">'; // Mozilla Firefox will attempt wrap due to `width="1"` on topmost column
1382 $content .= $this->workspaceList_displayIcons($currentWksp, $wksp);
1383 $content .= '</td>';
1384
1385 // row #1, column #3: current workspace indicator
1386 $content .= '<td nowrap="nowrap" style="text-align: center">'; // Mozilla Firefox will attempt wrap due to `width="1"` on topmost column
1387 $content .= (!$currentWksp ? '&nbsp;' : '<img ' . t3lib_iconWorks::skinImg($BACK_PATH, 'gfx/icon_ok.gif', 'width="18" height="16"') . ' id="wl_' . $rowNum . 'i" border="0" hspace="1" alt="' . $LANG->getLL('img_title_current_workspace') . '" />');
1388 $content .= '</td>';
1389
1390 // row #1, column #4 and 5: title and description
1391 $content .= '<td nowrap="nowrap">' . $wksp['title'] . '</td>' .
1392 '<td>' . $wksp['description'] . '</td>';
1393 $content .= '</tr>';
1394
1395 // row #2, column #1 and #2
1396 $content .= '<tr id="wl_' . $rowNum . '" class="bgColor" style="display: none">';
1397 $content .= '<td colspan="2" style="border-right: none;">&nbsp;</td>';
1398
1399 // row #2, column #3, #4 and #4
1400 $content .= '<td colspan="3" style="border-left: none;">' .
1401 $this->workspaceList_formatWorkspaceData($wksp) .
1402 '</td>';
1403
1404 $content .= '</tr>';
1405 $rowNum++;
1406 }
1407 $content .= '</table>';
1408
1409 $newWkspUrl = 'workspaceforms.php?action=new';
1410
1411 // workspace creation link
1412 if ($GLOBALS['BE_USER']->isAdmin() || 0 != ($GLOBALS['BE_USER']->groupData['workspace_perms'] & 4)) {
1413 $content .= '<br /><a href="' . $newWkspUrl . '">' .
1414 '<img ' .
1415 t3lib_iconWorks::skinImg($BACK_PATH, 'gfx/new_el.gif', 'width="11" height="12"') .
1416 ' alt="' . $LANG->getLL('img_title_create_new_workspace'). '" id="ver-wl-new-workspace-icon" />' .
1417 $LANG->getLL('link_text_create_new_workspace') . '</a>';
1418 }
1419 return $content;
1420 }
1421
1422
1423
1424
1425
1426 /**
1427 * Retrieves a list of workspaces where user has access.
1428 *
1429 * @return array A list of workspaces available to the current BE user
1430 */
1431 function workspaceList_getUserWorkspaceList() {
1432 // Get BE users if necessary
1433 if (!is_array($this->be_user_Array)) {
1434 $this->be_user_Array = t3lib_BEfunc::getUserNames();
1435 }
1436 // Get list of all workspaces. Note: system workspaces will be always displayed before custom ones!
1437 $workspaces = $GLOBALS['TYPO3_DB']->exec_SELECTgetRows('*','sys_workspace','pid=0'.t3lib_BEfunc::deleteClause('sys_workspace'),'','title');
1438 $availableWorkspaces = array();
1439
1440 // Live
1441 $wksp = $this->workspaceList_createFakeWorkspaceRecord(0);
1442 $wksp = $GLOBALS['BE_USER']->checkWorkspace($wksp);
1443 if (false !== $wksp) {
1444 $availableWorkspaces[] = $wksp;
1445 }
1446
1447 // Draft
1448 $wksp = $this->workspaceList_createFakeWorkspaceRecord(-1);
1449 $wksp = $GLOBALS['BE_USER']->checkWorkspace($wksp);
1450 if (false !== $wksp) {
1451 $availableWorkspaces[] = $wksp;
1452 }
1453
1454 // Custom
1455 foreach($workspaces as $rec) {
1456 // see if user can access this workspace in any way
1457 if (false !== ($result = $GLOBALS['BE_USER']->checkWorkspace($rec))) {
1458 $availableWorkspaces[] = $result; // `$result` contains `$rec` plus access type through '_ACCESS' key
1459 }
1460 }
1461 return $availableWorkspaces;
1462 }
1463
1464 /**
1465 * Create inner information panel for workspace list. This panel is
1466 * initially hidden and becomes visible when user click on the expand
1467 * icon on the very left of workspace list against the workspace he
1468 * wants to explore.
1469 *
1470 * @param array Workspace information
1471 * @return string Formatted workspace information
1472 */
1473 function workspaceList_formatWorkspaceData(&$wksp) {
1474 global $LANG;
1475
1476 $content = '<table cellspacing="1" cellpadding="1" width="100%" class="ver-wl-details-table">' .
1477 '<tr><td class="ver-wl-details-label"><b>' . $LANG->getLL('workspace_list_label_file_mountpoints') . '</b></td>' .
1478 '<td class="ver-wl-details">' . $this->workspaceList_getFileMountPoints($wksp) . '</td></tr>' .
1479 '<tr><td class="ver-wl-details-label"><b>' . $LANG->getLL('workspace_list_label_db_mountpoints') . '</b></td>' .
1480 '<td class="ver-wl-details">' . $this->workspaceList_getWebMountPoints($wksp) . '</td></tr>';
1481 if ($wksp['uid'] > 0) {
1482 // Displaying information below makes sence only for custom workspaces
1483 $content .=
1484 '<tr><td class="ver-wl-details-label"><b>' . $LANG->getLL('workspace_list_label_frozen') . '</b></td>' .
1485 '<td class="ver-wl-details">' . $LANG->getLL($wksp['freeze'] ? 'workspace_list_label_frozen_yes' : 'workspace_list_label_frozen_no') . '</td></tr>' .
1486 '<tr><td class="ver-wl-details-label"><b>' . $LANG->getLL('workspace_list_label_publish_date') . '</b></td>' .
1487 '<td class="ver-wl-details">' . ($wksp['publish_time'] == 0 ? '&nbsp;&ndash;' : t3lib_BEfunc::datetime($wksp['publish_time'])) . '</td></tr>' .
1488 '<tr><td class="ver-wl-details-label"><b>' . $LANG->getLL('workspace_list_label_publish_date') . '</b></td>' .
1489 '<td class="ver-wl-details">' . ($wksp['unpublish_time'] == 0 ? '&nbsp;&ndash;' : t3lib_BEfunc::datetime($wksp['unpublish_time'])) . '</td></tr>' .
1490 '<tr><td class="ver-wl-details-label"><b>' . $LANG->getLL('workspace_list_label_your_access') . '</b></td>' .
1491 '<td class="ver-wl-details">' . $LANG->getLL('workspace_list_access_' . $wksp['_ACCESS']) . '</td></tr>' .
1492 '<tr><td class="ver-wl-details-label"><b>' . $LANG->getLL('workspace_list_label_workspace_users') . '</b></td>' .
1493 '<td class="ver-wl-details">' . $this->workspaceList_getUserList($wksp) . '</td></tr>';
1494 }
1495 else if ($GLOBALS['BE_USER']->isAdmin()) {
1496 // show users for draft/live workspace only to admin users
1497 $content .= '<tr><td class="ver-wl-details-label"><b>' . $LANG->getLL('workspace_list_label_workspace_users') . '</b></td>' .
1498 '<td class="ver-wl-details">' . $this->workspaceList_getUserList($wksp) . '</td></tr>';
1499 }
1500 $content .= '</table>';
1501
1502 return $content;
1503 }
1504
1505
1506
1507
1508
1509 /**
1510 * Retrieves and formats database mount points lists.
1511 *
1512 * @param array &$wksp Workspace record
1513 * @return string Generated HTML
1514 */
1515 function workspaceList_getWebMountPoints(&$wksp) {
1516 if ($wksp['uid'] <= 0) {
1517 // system workspaces
1518 return $GLOBALS['LANG']->getLL($wksp['uid'] == 0 ? 'workspace_list_db_mount_point_live' : 'workspace_list_db_mount_point_draft');
1519 }
1520
1521 // here only if obtaining mount points for custom workspaces
1522
1523 // Warning: all fields needed for t3lib_iconWorks::getIconImage()!
1524 $MPs = $GLOBALS['TYPO3_DB']->exec_SELECTgetRows('*', 'pages', 'deleted=0 AND uid IN (' . $GLOBALS['TYPO3_DB']->cleanIntList($wksp['db_mountpoints']) . ')', '', 'title');
1525 $content_array = array();
1526 if (count($MPs) > 0) {
1527 $isAdmin = $GLOBALS['BE_USER']->isAdmin();
1528 if (!$isAdmin) {
1529 // We need to fetch user's mount point list (including MPS mounted from groups).
1530 // This list must not be affects by current user's workspace. It means we cannot use
1531 // $BE_USER->isInWebMount() to check mount points.
1532 $userMPs = explode(',', $GLOBALS['BE_USER']->dataLists['webmount_list']); // includes group data if necessary!
1533 }
1534 foreach ($MPs as $mp) {
1535 if (!$isAdmin && !in_array($mp['uid'], $userMPs)) {
1536 // Show warning icon
1537 $title = $GLOBALS['LANG']->getLL('workspace_list_mount_point_inaccessible');
1538 $str = '<img ' . t3lib_iconWorks::skinImg($GLOBALS['BACK_PATH'], 'gfx/icon_warning.gif', 'width="18" height="16"') .
1539 ' alt="' . $title . '" title="' . $title . '" align="absmiddle" />';
1540 $classAttr = 'class="ver-wl-mp-inacessible" ';
1541 }
1542 else {
1543 // normal icon
1544 $str = t3lib_iconWorks::getIconImage('pages', $mp, $GLOBALS['BACK_PATH'], ' align="absmiddle"');
1545 $classAttr = '';
1546 }
1547 // Will show UID on hover. Just convinient to user.
1548 $content_array[] = $str . '<span ' . $classAttr . 'title="UID: ' . $mp['uid'] . '">' . $mp['title'] . '</span>';
1549 }
1550 }
1551 if (count($content_array) > 0) {
1552 return implode('<br />', $content_array);
1553 }
1554 // no mount points
1555 return $GLOBALS['LANG']->getLL('workspace_list_db_mount_point_custom');
1556 }
1557
1558 /**
1559 * Retrieves and formats file mount points lists.
1560 *
1561 * @param array &$wksp Workspace record
1562 * @return string Generated HTML
1563 */
1564 function workspaceList_getFileMountPoints(&$wksp) {
1565 if ($wksp['uid'] == -1) {
1566 // draft workspace - none!
1567 return $GLOBALS['LANG']->getLL('workspace_list_file_mount_point_draft');
1568 }
1569 else if ($wksp['uid'] == 0) {
1570 // live workspace
1571 return $GLOBALS['LANG']->getLL('workspace_list_file_mount_point_live');
1572 }
1573
1574 // Here if displaying information for custom workspace
1575
1576 // Warning: all fields needed for t3lib_iconWorks::getIconImage()!
1577 $MPs = $GLOBALS['TYPO3_DB']->exec_SELECTgetRows('*', 'sys_filemounts', 'deleted=0 AND hidden=0 AND uid IN (' . $GLOBALS['TYPO3_DB']->cleanIntList($wksp['file_mountpoints']) . ')', '', 'title');
1578 if (count($MPs) != 0) {
1579 // Has mount points
1580 $isAdmin = $GLOBALS['BE_USER']->isAdmin();
1581 if (!$isAdmin) {
1582 // We need to fetch user's mount point list (including MPS mounted from groups).
1583 // This list must not be affects by current user's workspace. It means we cannot use
1584 // $BE_USER->isInWebMount() to check mount points.
1585 $userMPs = explode(',', $GLOBALS['BE_USER']->dataLists['filemount_list']); // includes group data if necessary!
1586 }
1587 foreach ($MPs as $mp) {
1588 if (!$isAdmin && !in_array($mp['uid'], $userMPs)) {
1589 // Show warning icon
1590 $title = $GLOBALS['LANG']->getLL('workspace_list_mount_point_inaccessible');
1591 $str = '<img ' . t3lib_iconWorks::skinImg($GLOBALS['BACK_PATH'], 'gfx/icon_warning.gif', 'width="18" height="16"') .
1592 ' alt="' . $title . '" title="' . $title . '" align="absmiddle" />';
1593 $classAttr = 'class="ver-wl-mp-inacessible" ';
1594 }
1595 else {
1596 // normal icon
1597 $str = t3lib_iconWorks::getIconImage('sys_filemounts', $mp, $GLOBALS['BACK_PATH'], ' align="absmiddle"');
1598 $classAttr = '';
1599 }
1600 // Will show UID on hover. Just convinient to user.
1601 $content_array[] = $str . '<span ' . $classAttr . 'title="UID: ' . $mp['uid'] . '">' . $mp['title'] . '</span>';
1602 }
1603 }
1604 if (count($content_array) > 0) {
1605 return implode('<br />', $content_array);
1606 }
1607 // No file mount points
1608 return $GLOBALS['LANG']->getLL('workspace_list_file_mount_point_custom');
1609 }
1610
1611 /**
1612 * Creates a header for the workspace list table. This function only makes
1613 * <code>workspaceList_displayUserWorkspaceList()</code> smaller.
1614 *
1615 * @return string Generated content
1616 */
1617 function workspaceList_displayUserWorkspaceListHeader() {
1618 global $LANG;
1619 // TODO CSH lables?
1620 return '<table border="0" cellpadding="0" cellspacing="1" class="lrPadding workspace-overview ver-wl-table">
1621 <tr class="bgColor5 tableheader">
1622 <td width="1">&nbsp;</td>
1623 <td width="1">&nbsp;</td>
1624 <td nowrap="nowrap">' . $LANG->getLL('workspace_list_label_current_workspace') . '</td>
1625 <td nowrap="nowrap">' . $LANG->getLL('workspace_list_label_workspace_title') . '</td>
1626 <td nowrap="nowrap">' . $LANG->getLL('workspace_list_label_workspace_description') . '</td>
1627 </tr>';
1628 }
1629
1630
1631 /**
1632 * Generates a list of <code>&lt;option&gt;</code> tags with user names.
1633 *
1634 * @param array Workspace record
1635 * @return string Generated content
1636 */
1637 function workspaceList_getUserList(&$wksp) {
1638 global $LANG;
1639
1640 if ($wksp['uid'] > 0) {
1641 // custom workspaces
1642 $content = $this->workspaceList_getUserListWithAccess($wksp['adminusers'], $LANG->getLL('workspace_list_label_owners')); // owners
1643 $content .= $this->workspaceList_getUserListWithAccess($wksp['members'], $LANG->getLL('workspace_list_label_members')); // members
1644 $content .= $this->workspaceList_getUserListWithAccess($wksp['reviewers'], $LANG->getLL('workspace_list_label_reviewers')); // reviewers
1645 if ($content != '') {
1646 $content = '<table cellpadding="0" cellspacing="1" width="100%" class="lrPadding workspace-overview">' . $content . '</table>';
1647 } else {
1648 $content = $LANG->getLL($wksp['uid'] > 0 ? 'workspace_list_access_admins_only' : 'workspace_list_access_anyone');
1649 }
1650 }
1651 else {
1652 // live and draft workspace
1653 $content = $this->workspaceList_getUserListForSysWorkspace($wksp);
1654 }
1655 return $content;
1656 }
1657
1658 /**
1659 * Generates a list of user names that has access to the system workspace.
1660 *
1661 * @param array &$wksp Workspace record
1662 * @return string Generated content
1663 */
1664 function workspaceList_getUserListForSysWorkspace(&$wksp) {
1665 $option = ($wksp['uid'] == 0 ? 1 : 2);
1666 $content_array = array();
1667 foreach ($this->be_user_Array_full as $uid => $user) {
1668 if ($user['admin'] != 0 || 0 != ($user['workspace_perms'] & $option)) {
1669 if ($uid == $GLOBALS['BE_USER']->user['uid']) {
1670 // highlight current user
1671 $tag0 = '<span class="ver-wl-current-user">';
1672 $tag1 = '</span>';
1673 }
1674 else {
1675 $tag0 = $tag1 = '';
1676 }
1677 $content_array[] = t3lib_iconWorks::getIconImage('be_users', $uid, $GLOBALS['BACK_PATH'], ' align="middle" alt="UID: ' . $uid . '"') .
1678 $tag0 . $user['username'] . $tag1;
1679 }
1680 }
1681 return implode('<br />', $content_array);
1682 }
1683
1684 /**
1685 * Generates a list of user names that has access to the workspace.
1686 *
1687 * @param array A list of user IDs separated by comma
1688 * @param string Access string
1689 * @return string Generated content
1690 */
1691 function workspaceList_getUserListWithAccess(&$list, $access) {
1692 $content_array = array();
1693 if ($list != '') {
1694 $userIDs = explode(',', $list);
1695
1696 // get user names and sort
1697 $regExp = '/^(be_[^_]+)_(\d+)$/';
1698 $groups = false;
1699 foreach ($userIDs as $userUID) {
1700 $id = $userUID;
1701
1702 if (preg_match($regExp, $userUID)) {
1703 $table = preg_replace($regExp, '\1', $userUID);
1704 $id = intval(preg_replace($regExp, '\2', $userUID));
1705 if ($table == 'be_users') {
1706 // user
1707 $icon = $GLOBALS['TCA']['be_users']['typeicons'][$this->be_user_Array[$id]['admin']];
1708 if ($id == $GLOBALS['BE_USER']->user['uid']) {
1709 // highlight current user
1710 $tag0 = '<span class="ver-wl-current-user">';
1711 $tag1 = '</span>';
1712 }
1713 else {
1714 $tag0 = $tag1 = '';
1715 }
1716 $content_array[] = t3lib_iconWorks::getIconImage($table, $this->be_user_Array[$id], $GLOBALS['BACK_PATH'], ' align="middle" alt="UID: ' . $id . '"') .
1717 $tag0 . $this->be_user_Array_full[$id]['username'] . $tag1;
1718 }
1719 else {
1720 // group
1721 if (false === $groups) {
1722 $groups = t3lib_BEfunc::getGroupNames();
1723 }
1724 $content_array[] = t3lib_iconWorks::getIconImage($table, $groups[$id], $GLOBALS['BACK_PATH'], ' align="middle" alt="UID: ' . $id . '"') .
1725 $groups[$id]['title'];
1726 }
1727 }
1728 else {
1729 // user id
1730 if ($userUID == $GLOBALS['BE_USER']->user['uid']) {
1731 // highlight current user
1732 $tag0 = '<span class="ver-wl-current-user">';
1733 $tag1 = '</span>';
1734 }
1735 else {
1736 $tag0 = $tag1 = '';
1737 }
1738 $content_array[] = t3lib_iconWorks::getIconImage('be_users', $this->be_user_Array[$id], $GLOBALS['BACK_PATH'], ' align="middle" alt="UID: ' . $id . '"') .
1739 $tag0 . $this->be_user_Array_full[$userUID]['username'] . $tag1;
1740 }
1741 }
1742 sort($content_array);
1743 }
1744 else {
1745 $content_array[] = '&nbsp;&ndash;';
1746 }
1747
1748 $content = '<tr><td class="ver-wl-details-label ver-wl-details-user-list-label">';
1749 // TODO CSH lable explaining access here?
1750 $content .= '<b>' . $access . '</b></td>';
1751 $content .= '<td class="ver-wl-details">' . implode('<br />', $content_array) . '</td></tr>';
1752 return $content;
1753 }
1754
1755
1756
1757 /**
1758 * Creates a list of icons for workspace.
1759 *
1760 * @param boolean <code>true</code> if current workspace
1761 * @param array Workspace record
1762 * @return string Generated content
1763 */
1764 function workspaceList_displayIcons($currentWorkspace, &$wksp) {
1765 global $BACK_PATH, $LANG;
1766
1767 $content = '';
1768 // `edit workspace` button
1769 if ($this->workspaceList_hasEditAccess($wksp)) {
1770 // User can modify workspace parameters, display corresponding link and icon
1771 $editUrl = 'workspaceforms.php?action=edit&amp;wkspId=' . $wksp['uid'];
1772
1773 $content .= '<a href="' . $editUrl . '" />' .
1774 '<img ' . t3lib_iconWorks::skinImg($BACK_PATH, 'gfx/edit2.gif', 'width="11" height="12"') . ' border="0" alt="' . $LANG->getLL('workspace_list_icon_title_edit_workspace') . '" align="middle" hspace="1" />' .
1775 '</a>';
1776 } else {
1777 // User can NOT modify workspace parameters, display space
1778 // Get only withdth and height from skinning API
1779 $content .= '<img src="clear.gif" ' .
1780 t3lib_iconWorks::skinImg($BACK_PATH, 'gfx/edit2.gif', 'width="11" height="12"', 2) .
1781 ' border="0" alt="" hspace="1" align="middle" />';
1782 }
1783 // `switch workspace` button
1784 if (!$currentWorkspace) {
1785 // Workspace switching button
1786 $content .= '<a href="' .
1787 t3lib_div::getIndpEnv('SCRIPT_NAME') .
1788 '?changeWorkspace=' . $wksp['uid'] . '"/>' .
1789 '<img ' . t3lib_iconWorks::skinImg($BACK_PATH, 'gfx/switch.gif', 'width="11" height="10"') . ' border="0" alt="' . $LANG->getLL('workspace_list_icon_title_switch_workspace') . '" align="middle" hspace="1" />' .
1790 '</a>';
1791 } else {
1792 // Current workspace: empty space instead of workspace switching button
1793 //
1794 // Here get only width and height from skinning API
1795 $content .= '<img src="clear.gif" ' .
1796 t3lib_iconWorks::skinImg($BACK_PATH, 'gfx/switch.png', 'width="18" height="16"', 2) .
1797 ' border="0" alt="" hspace="1" align="middle" alt="" />';
1798 }
1799 return $content;
1800 }
1801
1802 /**
1803 * Checks if user has edit access to workspace. Access is granted if
1804 * workspace is custom and user is admin or the the owner of the workspace.
1805 * This function assumes that <code>$wksp</code> were passed through
1806 * <code>$GLOBALS['BE_USER']->checkWorkspace()</code> function to obtain
1807 * <code>_ACCESS</code> attribute of the workspace.
1808 *
1809 * @param array Workspace record
1810 * @return boolean <code>true</code> if user can modify workspace parameters
1811 */
1812 function workspaceList_hasEditAccess(&$wksp) {
1813 $access = &$wksp['_ACCESS'];
1814 return ($wksp['uid'] > 0 && ($access == 'admin' || $access == 'owner'));
1815 }
1816
1817 /**
1818 * Creates a fake workspace record for system workspaces. Record contains
1819 * all fields found in <code>sys_workspaces</code>.
1820 *
1821 * @param integer System workspace ID. Currently <code>0</code> and <code>-1</code> are accepted.
1822 * @return array Generated record (see <code>sys_workspaces</code> for structure)
1823 */
1824 function workspaceList_createFakeWorkspaceRecord($uid) {
1825 global $BE_USER;
1826
1827 $record = array(
1828 'uid' => $uid,
1829 'pid' => 0, // always 0!
1830 'tstamp' => 0, // does not really matter
1831 'deleted' => 0,
1832 // TODO Localize all strings below
1833 'title' => ($uid == 0 ? '[Live workspace]' : '[Draft workspace]'), // TODO Localize this!
1834 // TODO Localize all strings below
1835 'description' => ($uid == 0 ? 'Live workspace' : 'Draft workspace'), // TODO Localize this!
1836 'adminusers' => '',
1837 'members' => '',
1838 'reviewers' => '',
1839 'db_mountpoints' => '', // TODO get mount points from user profile
1840 'file_mountpoints' => '', // TODO get mount points from user profile for live workspace only (uid == 0)
1841 'publish_time' => 0,
1842 'unpublish_time' => 0,
1843 'freeze' => 0,
1844 'live_edit' => ($uid == 0),
1845 'vtypes' => 0,
1846 'disable_autocreate' => 0,
1847 'swap_modes' => 0,
1848 'publish_access' => 0,
1849 'stagechg_notification' => 0
1850 );
1851 return $record;
1852 }
1853
1854
1855
1856
1857
1858
1859
1860
1861
1862
1863
1864
1865
1866
1867
1868
1869
1870
1871
1872
1873
1874
1875
1876
1877 /**************************************
1878 *
1879 * Helper functions
1880 *
1881 *************************************/
1882
1883 /**
1884 * Formatting the version number for HTML output
1885 *
1886 * @param integer Version number
1887 * @return string Version number for output
1888 */
1889 function formatVerId($verId) {
1890 return '1.'.$verId;
1891 }
1892
1893 /**
1894 * Formatting workspace ID into a visual label
1895 *
1896 * @param integer Workspace ID
1897 * @return string Workspace title
1898 */
1899 function formatWorkspace($wsid) {
1900
1901 // Render, if not cached:
1902 if (!isset($this->formatWorkspace_cache[$wsid])) {
1903 switch($wsid) {
1904 case -1:
1905 $this->formatWorkspace_cache[$wsid] = '[Draft]';
1906 break;
1907 case 0:
1908 $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...
1909 break;
1910 default:
1911 list($titleRec) = $GLOBALS['TYPO3_DB']->exec_SELECTgetRows('title','sys_workspace','uid='.intval($wsid).t3lib_BEfunc::deleteClause('sys_workspace'));
1912 $this->formatWorkspace_cache[$wsid] = '['.$wsid.'] '.$titleRec['title'];
1913 break;
1914 }
1915 }
1916
1917 return $this->formatWorkspace_cache[$wsid];
1918 }
1919
1920 /**
1921 * Format publishing count for version (lifecycle state)
1922 *
1923 * @param integer t3ver_count value (number of times it has been online)
1924 * @return string String translation of count.
1925 */
1926 function formatCount($count) {
1927 global $LANG;
1928
1929 // Render, if not cached:
1930 if (!isset($this->formatCount_cache[$count])) {
1931 switch($count) {
1932 case 0:
1933 $this->formatCount_cache[$count] = $LANG->getLL('workspace_list_publishing_count_draft');
1934 break;
1935 case 1:
1936 $this->formatCount_cache[$count] = $LANG->getLL('workspace_list_publishing_count_archive');
1937 break;
1938 default:
1939 $this->formatCount_cache[$count] = sprintf($LANG->getLL('workspace_list_publishing_count'), $count);
1940 break;
1941 }
1942 }
1943
1944 return $this->formatCount_cache[$count];
1945 }
1946
1947 /**
1948 * Looking for versions of a record in other workspaces than the current
1949 *
1950 * @param string Table name
1951 * @param integer Record uid
1952 * @return string List of other workspace IDs
1953 */
1954 function versionsInOtherWS($table,$uid) {
1955
1956 // Check for duplicates:
1957 // Select all versions of record NOT in this workspace:
1958 $rows = $GLOBALS['TYPO3_DB']->exec_SELECTgetRows(
1959 't3ver_wsid',
1960 $table,
1961 'pid=-1
1962 AND t3ver_oid='.intval($uid).'
1963 AND t3ver_wsid!='.intval($GLOBALS['BE_USER']->workspace).'
1964 AND (t3ver_wsid=-1 OR t3ver_wsid>0)'.
1965 t3lib_BEfunc::deleteClause($table),
1966 '',
1967 't3ver_wsid',
1968 '',
1969 't3ver_wsid'
1970 );
1971 if (count($rows)) {
1972 return implode(',',array_keys($rows));
1973 }
1974 }
1975
1976 /**
1977 * Looks up stage changes for version and displays a formatted view on mouseover.
1978 *
1979 * @param string Table name
1980 * @param integer Record ID
1981 * @param string HTML string to wrap the mouseover around (should be stage change links)
1982 * @return string HTML code.
1983 */
1984 function showStageChangeLog($table,$id,$stageCommands) {
1985 global $LANG;
1986
1987 $rows = $GLOBALS['TYPO3_DB']->exec_SELECTgetRows(
1988 'log_data,tstamp,userid',
1989 'sys_log',
1990 'action=6 and details_nr=30
1991 AND tablename='.$GLOBALS['TYPO3_DB']->fullQuoteStr($table,'sys_log').'
1992 AND recuid='.intval($id)
1993 );
1994
1995 $entry = array();
1996 foreach($rows as $dat) {
1997 $data = unserialize($dat['log_data']);
1998 $username = $this->be_user_Array[$dat['userid']] ? $this->be_user_Array[$dat['userid']]['username'] : '['.$dat['userid'].']';
1999
2000 switch($data['stage']) {
2001 case 1:
2002 $text = $LANG->getLL('stage_sent_to_review');
2003 break;
2004 case 10:
2005 $text = $LANG->getLL('stage_approved_for_publish');
2006 break;
2007 case -1:
2008 $text = $LANG->getLL('stage_rejected');
2009 break;
2010 case 0:
2011 $text = $LANG->getLL('stage_reset_to_editing');
2012 break;
2013 default:
2014 $text = $LANG->getLL('stage_undefined');
2015 break;
2016 }
2017 $text = t3lib_BEfunc::datetime($dat['tstamp']).': ' . sprintf($text, $username);
2018 $text.= ($data['comment']?'<br/>' . $LANG->getLL('stage_label_user_comment'). ' <em>'.$data['comment'].'</em>':'');
2019
2020 $entry[] = $text;
2021 }
2022
2023 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>'.
2024 '<div class="logLayer" style="visibility: hidden; position: absolute;" id="log_'.$table.$id.'">'.implode('<hr/>',$entry).'</div>' : $stageCommands;
2025 }
2026
2027
2028
2029
2030
2031
2032
2033
2034 /**********************************
2035 *
2036 * Processing
2037 *
2038 **********************************/
2039
2040 /**
2041 * Will publish workspace if buttons are pressed
2042 *
2043 * @return void
2044 */
2045 function publishAction() {
2046
2047 // If "Publish" or "Swap" buttons are pressed:
2048 if (t3lib_div::_POST('_publish') || t3lib_div::_POST('_swap')) {
2049
2050 // Initialize workspace object and request all pending versions:
2051 $wslibObj = t3lib_div::makeInstance('wslib');
2052 $cmd = $wslibObj->getCmdArrayForPublishWS($GLOBALS['BE_USER']->workspace, t3lib_div::_POST('_swap'));
2053
2054 // Execute the commands:
2055 $tce = t3lib_div::makeInstance('t3lib_TCEmain');
2056 $tce->stripslashes_values = 0;
2057 $tce->start(array(), $cmd);
2058 $tce->process_cmdmap();
2059
2060 return $tce->errorLog;
2061 }
2062 }
2063 }
2064
2065 // Include extension?
2066 if (defined('TYPO3_MODE') && $TYPO3_CONF_VARS[TYPO3_MODE]['XCLASS']['typo3/mod/user/ws/index.php']) {
2067 include_once($TYPO3_CONF_VARS[TYPO3_MODE]['XCLASS']['typo3/mod/user/ws/index.php']);
2068 }
2069
2070
2071
2072
2073
2074
2075
2076
2077
2078 // Make instance:
2079 $SOBE = t3lib_div::makeInstance('SC_mod_user_ws_index');
2080 $SOBE->init();
2081 $SOBE->main();
2082 $SOBE->printContent();
2083 ?>