PageRenderTime 54ms CodeModel.GetById 13ms RepoModel.GetById 0ms app.codeStats 1ms

/typo3/sysext/version/cm1/index.php

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