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