TYPO3 API  SVNRelease
class.tx_version_tcemain.php
Go to the documentation of this file.
00001 <?php
00002 /***************************************************************
00003 *  Copyright notice
00004 *
00005 *  (c) 1999-2011 Kasper Skårhøj (kasperYYYY@typo3.com)
00006 *  (c) 2010-2011 Benjamin Mack (benni@typo3.org)
00007 *
00008 *  All rights reserved
00009 *
00010 *  This script is part of the TYPO3 project. The TYPO3 project is
00011 *  free software; you can redistribute it and/or modify
00012 *  it under the terms of the GNU General Public License as published by
00013 *  the Free Software Foundation; either version 2 of the License, or
00014 *  (at your option) any later version.
00015 *
00016 *  The GNU General Public License can be found at
00017 *  http://www.gnu.org/copyleft/gpl.html.
00018 *  A copy is found in the textfile GPL.txt and important notices to the license
00019 *  from the author is found in LICENSE.txt distributed with these scripts.
00020 *
00021 *
00022 *  This script is distributed in the hope that it will be useful,
00023 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00024 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00025 *  GNU General Public License for more details.
00026 *
00027 *  This copyright notice MUST APPEAR in all copies of the script!
00028 ***************************************************************/
00029 /**
00030  *
00031  * Contains some parts for staging, versioning and workspaces
00032  * to interact with the TYPO3 Core Engine
00033  *
00034  */
00035 class tx_version_tcemain {
00036 
00037     /**
00038      * For accumulating information about workspace stages raised
00039      * on elements so a single mail is sent as notification.
00040      * previously called "accumulateForNotifEmail" in tcemain
00041      *
00042      * @var array
00043      */
00044     protected $notificationEmailInfo = array();
00045 
00046     /**
00047      * General comment, eg. for staging in workspaces
00048      *
00049      * @var string
00050      */
00051     protected $generalComment = '';
00052 
00053     /**
00054      * Contains remapped IDs.
00055      *
00056      * @var array
00057      */
00058     protected $remappedIds = array();
00059 
00060     /****************************
00061      *****  Cmdmap  Hooks  ******
00062      ****************************/
00063 
00064     /**
00065      * hook that is called before any cmd of the commandmap is executed
00066      *
00067      * @param t3lib_TCEmain $tcemainObj reference to the main tcemain object
00068      * @return void
00069      */
00070     public function processCmdmap_beforeStart(t3lib_TCEmain $tcemainObj) {
00071             // Reset notification array
00072         $this->notificationEmailInfo = array();
00073             // Resolve dependencies of version/workspaces actions:
00074         $tcemainObj->cmdmap = $this->getCommandMap($tcemainObj, $tcemainObj->cmdmap)->process()->get();
00075     }
00076 
00077 
00078     /**
00079      * hook that is called when no prepared command was found
00080      *
00081      * @param string $command the command to be executed
00082      * @param string $table the table of the record
00083      * @param integer $id the ID of the record
00084      * @param mixed $value the value containing the data
00085      * @param boolean $commandIsProcessed can be set so that other hooks or
00086      *              TCEmain knows that the default cmd doesn't have to be called
00087      * @param t3lib_TCEmain $tcemainObj reference to the main tcemain object
00088      * @return  void
00089      */
00090     public function processCmdmap($command, $table, $id, $value, &$commandIsProcessed, t3lib_TCEmain $tcemainObj) {
00091 
00092             // custom command "version"
00093         if ($command == 'version') {
00094             $commandWasProcessed = TRUE;
00095             $action = (string) $value['action'];
00096             switch ($action) {
00097 
00098                 case 'new':
00099                         // check if page / branch versioning is needed,
00100                         // or if "element" version can be used
00101                     $versionizeTree = -1;
00102                     if (isset($value['treeLevels'])) {
00103                         $versionizeTree = t3lib_div::intInRange($value['treeLevels'], -1, 100);
00104                     }
00105                     if ($table == 'pages' && $versionizeTree >= 0) {
00106                         $this->versionizePages($id, $value['label'], $versionizeTree, $tcemainObj);
00107                     } else {
00108                         $tcemainObj->versionizeRecord($table, $id, $value['label']);
00109                     }
00110                 break;
00111 
00112                 case 'swap':
00113                     $this->version_swap($table, $id, $value['swapWith'], $value['swapIntoWS'], $tcemainObj);
00114                 break;
00115 
00116                 case 'clearWSID':
00117                     $this->version_clearWSID($table, $id, FALSE, $tcemainObj);
00118                 break;
00119 
00120                 case 'flush':
00121                     $this->version_clearWSID($table, $id, TRUE, $tcemainObj);
00122                 break;
00123 
00124                 case 'setStage':
00125                     $elementIds = t3lib_div::trimExplode(',', $id, TRUE);
00126                     foreach ($elementIds as $elementId) {
00127                         $this->version_setStage($table, $elementId, $value['stageId'],
00128                             (isset($value['comment']) && $value['comment'] ? $value['comment'] : $this->generalComment),
00129                             TRUE,
00130                             $tcemainObj,
00131                             $value['notificationAlternativeRecipients']
00132                         );
00133                     }
00134                 break;
00135             }
00136         }
00137     }
00138 
00139     /**
00140      * hook that is called AFTER all commands of the commandmap was
00141      * executed
00142      *
00143      * @param t3lib_TCEmain $tcemainObj reference to the main tcemain object
00144      * @return  void
00145      */
00146     public function processCmdmap_afterFinish(t3lib_TCEmain $tcemainObj) {
00147             // Empty accumulation array:
00148         foreach ($this->notificationEmailInfo as $notifItem) {
00149             $this->notifyStageChange($notifItem['shared'][0], $notifItem['shared'][1], implode(', ', $notifItem['elements']), 0, $notifItem['shared'][2], $tcemainObj, $notifItem['alternativeRecipients']);
00150         }
00151 
00152             // Reset notification array
00153         $this->notificationEmailInfo = array();
00154             // Reset remapped IDs
00155         $this->remappedIds = array();
00156     }
00157 
00158 
00159     /**
00160      * hook that is called AFTER all commands of the commandmap was
00161      * executed
00162      *
00163      * @param string $table the table of the record
00164      * @param integer $id the ID of the record
00165      * @param array $record The accordant database record
00166      * @param boolean $recordWasDeleted can be set so that other hooks or
00167      *              TCEmain knows that the default delete action doesn't have to be called
00168      * @param t3lib_TCEmain $tcemainObj reference to the main tcemain object
00169      * @return  void
00170      */
00171     public function processCmdmap_deleteAction($table, $id, array $record, &$recordWasDeleted, t3lib_TCEmain $tcemainObj) {
00172             // only process the hook if it wasn't processed
00173             // by someone else before
00174         if (!$recordWasDeleted) {
00175             $recordWasDeleted = TRUE;
00176             $id = $record['uid'];
00177 
00178                 // For Live version, try if there is a workspace version because if so, rather "delete" that instead
00179                 // Look, if record is an offline version, then delete directly:
00180             if ($record['pid'] != -1) {
00181                 if ($wsVersion = t3lib_BEfunc::getWorkspaceVersionOfRecord($tcemainObj->BE_USER->workspace, $table, $id)) {
00182                     $record = $wsVersion;
00183                     $id = $record['uid'];
00184                 }
00185             }
00186 
00187                     // Look, if record is an offline version, then delete directly:
00188             if ($record['pid'] == -1) {
00189                 if ($GLOBALS['TCA'][$table]['ctrl']['versioningWS']) {
00190                         // In Live workspace, delete any. In other workspaces there must be match.
00191                     if ($tcemainObj->BE_USER->workspace == 0 || (int) $record['t3ver_wsid'] == $tcemainObj->BE_USER->workspace) {
00192                         $liveRec = t3lib_BEfunc::getLiveVersionOfRecord($table, $id, 'uid,t3ver_state');
00193 
00194                             // Delete those in WS 0 + if their live records state was not "Placeholder".
00195                         if ($record['t3ver_wsid']==0 || (int) $liveRec['t3ver_state'] <= 0) {
00196                             $tcemainObj->deleteEl($table, $id);
00197                         } else {
00198                                 // If live record was placeholder (new/deleted), rather clear
00199                                 // it from workspace (because it clears both version and placeholder).
00200                             $this->version_clearWSID($table, $id, FALSE, $tcemainObj);
00201                         }
00202                     } else $tcemainObj->newlog('Tried to delete record from another workspace',1);
00203                 } else $tcemainObj->newlog('Versioning not enabled for record with PID = -1!',2);
00204             } elseif ($res = $tcemainObj->BE_USER->workspaceAllowLiveRecordsInPID($record['pid'], $table)) {
00205                     // Look, if record is "online" or in a versionized branch, then delete directly.
00206                 if ($res>0) {
00207                     $tcemainObj->deleteEl($table, $id);
00208                 } else {
00209                     $tcemainObj->newlog('Stage of root point did not allow for deletion',1);
00210                 }
00211             } elseif ((int)$record['t3ver_state']===3) {
00212                     // Placeholders for moving operations are deletable directly.
00213 
00214                     // Get record which its a placeholder for and reset the t3ver_state of that:
00215                 if ($wsRec = t3lib_BEfunc::getWorkspaceVersionOfRecord($record['t3ver_wsid'], $table, $record['t3ver_move_id'], 'uid')) {
00216                         // Clear the state flag of the workspace version of the record
00217                         // Setting placeholder state value for version (so it can know it is currently a new version...)
00218                     $updateFields = array(
00219                         't3ver_state' => 0
00220                     );
00221                     $GLOBALS['TYPO3_DB']->exec_UPDATEquery($table, 'uid=' . intval($wsRec['uid']), $updateFields);
00222                 }
00223                 $tcemainObj->deleteEl($table, $id);
00224             } else {
00225                 // Otherwise, try to delete by versioning:
00226                 $tcemainObj->versionizeRecord($table, $id, 'DELETED!', TRUE);
00227                 $tcemainObj->deleteL10nOverlayRecords($table, $id);
00228             }
00229         }
00230     }
00231 
00232 
00233     /**
00234      * hook for t3lib_TCEmain::moveRecord that cares about moving records that
00235      * are *not* in the live workspace
00236      *
00237      * @param string $table the table of the record
00238      * @param integer $id the ID of the record
00239      * @param integer $destPid Position to move to: $destPid: >=0 then it points to
00240      *              a page-id on which to insert the record (as the first element).
00241      *              <0 then it points to a uid from its own table after which to insert it
00242      * @param array $propArr Record properties, like header and pid (includes workspace overlay)
00243      * @param array $moveRec Record properties, like header and pid (without workspace overlay)
00244      * @param integer $resolvedPid The final page ID of the record
00245      *              (workspaces and negative values are resolved)
00246      * @param boolean $recordWasMoved can be set so that other hooks or
00247      *              TCEmain knows that the default move action doesn't have to be called
00248      * @param   $table  the table
00249      */
00250     public function moveRecord($table, $uid, $destPid, array $propArr, array $moveRec, $resolvedPid, &$recordWasMoved, t3lib_TCEmain $tcemainObj) {
00251         global $TCA;
00252 
00253             // Only do something in Draft workspace
00254         if ($tcemainObj->BE_USER->workspace !== 0) {
00255             $recordWasMoved = TRUE;
00256 
00257                 // Get workspace version of the source record, if any:
00258             $WSversion = t3lib_BEfunc::getWorkspaceVersionOfRecord($tcemainObj->BE_USER->workspace, $table, $uid, 'uid,t3ver_oid');
00259 
00260                 // If no version exists and versioningWS is in version 2, a new placeholder is made automatically:
00261             if (!$WSversion['uid'] && (int)$TCA[$table]['ctrl']['versioningWS']>=2 && (int)$moveRec['t3ver_state']!=3)  {
00262                 $tcemainObj->versionizeRecord($table, $uid, 'Placeholder version for moving record');
00263                 $WSversion = t3lib_BEfunc::getWorkspaceVersionOfRecord($tcemainObj->BE_USER->workspace, $table, $uid, 'uid,t3ver_oid'); // Will not create new versions in live workspace though...
00264             }
00265 
00266                 // Check workspace permissions:
00267             $workspaceAccessBlocked = array();
00268                 // Element was in "New/Deleted/Moved" so it can be moved...
00269             $recIsNewVersion = (int)$moveRec['t3ver_state']>0;
00270 
00271             $destRes = $tcemainObj->BE_USER->workspaceAllowLiveRecordsInPID($resolvedPid, $table);
00272             $canMoveRecord = $recIsNewVersion || (int)$TCA[$table]['ctrl']['versioningWS'] >= 2;
00273 
00274                 // Workspace source check:
00275             if (!$recIsNewVersion) {
00276                 $errorCode = $tcemainObj->BE_USER->workspaceCannotEditRecord($table, $WSversion['uid'] ? $WSversion['uid'] : $uid);
00277                 if ($errorCode) {
00278                     $workspaceAccessBlocked['src1'] = 'Record could not be edited in workspace: ' . $errorCode . ' ';
00279                 } elseif (!$canMoveRecord && $tcemainObj->BE_USER->workspaceAllowLiveRecordsInPID($moveRec['pid'], $table) <= 0) {
00280                     $workspaceAccessBlocked['src2'] = 'Could not remove record from table "' . $table . '" from its page "'.$moveRec['pid'].'" ';
00281                 }
00282             }
00283 
00284                 // Workspace destination check:
00285 
00286                 // All records can be inserted if $destRes is greater than zero.
00287                 // Only new versions can be inserted if $destRes is false.
00288                 // NO RECORDS can be inserted if $destRes is negative which indicates a stage
00289                 //  not allowed for use. If "versioningWS" is version 2, moving can take place of versions.
00290             if (!($destRes > 0 || ($canMoveRecord && !$destRes))) {
00291                 $workspaceAccessBlocked['dest1'] = 'Could not insert record from table "' . $table . '" in destination PID "' . $resolvedPid . '" ';
00292             } elseif ($destRes == 1 && $WSversion['uid']) {
00293                 $workspaceAccessBlocked['dest2'] = 'Could not insert other versions in destination PID ';
00294             }
00295 
00296             if (!count($workspaceAccessBlocked)) {
00297                     // If the move operation is done on a versioned record, which is
00298                     // NOT new/deleted placeholder and versioningWS is in version 2, then...
00299                 if ($WSversion['uid'] && !$recIsNewVersion && (int)$TCA[$table]['ctrl']['versioningWS'] >= 2) {
00300                     $this->moveRecord_wsPlaceholders($table, $uid, $destPid, $WSversion['uid'], $tcemainObj);
00301                 } else {
00302                     // moving not needed, just behave like in live workspace
00303                     $recordWasMoved = FALSE;
00304                 }
00305             } else {
00306                 $tcemainObj->newlog("Move attempt failed due to workspace restrictions: " . implode(' // ', $workspaceAccessBlocked), 1);
00307             }
00308         }
00309     }
00310 
00311 
00312 
00313     /****************************
00314      *****  Notifications  ******
00315      ****************************/
00316 
00317     /**
00318      * Send an email notification to users in workspace
00319      *
00320      * @param array $stat Workspace access array (from t3lib_userauthgroup::checkWorkspace())
00321      * @param integer $stageId New Stage number: 0 = editing, 1= just ready for review, 10 = ready for publication, -1 = rejected!
00322      * @param string $table Table name of element (or list of element names if $id is zero)
00323      * @param integer $id Record uid of element (if zero, then $table is used as reference to element(s) alone)
00324      * @param string $comment User comment sent along with action
00325      * @param t3lib_TCEmain $tcemainObj TCEmain object
00326      * @param string $notificationAlternativeRecipients Comma separated list of recipients to notificate instead of be_users selected by sys_workspace, list is generated by workspace extension module
00327      * @return void
00328      */
00329     protected function notifyStageChange(array $stat, $stageId, $table, $id, $comment, t3lib_TCEmain $tcemainObj, $notificationAlternativeRecipients = FALSE) {
00330         $workspaceRec = t3lib_BEfunc::getRecord('sys_workspace', $stat['uid']);
00331             // So, if $id is not set, then $table is taken to be the complete element name!
00332         $elementName = $id ? $table . ':' . $id : $table;
00333 
00334         if (is_array($workspaceRec)) {
00335 
00336                  // Get the new stage title from workspaces library, if workspaces extension is installed
00337             if (t3lib_extMgm::isLoaded('workspaces')) {
00338                 $stageService = t3lib_div::makeInstance('Tx_Workspaces_Service_Stages');
00339                 $newStage = $stageService->getStageTitle((int)$stageId);
00340             } else {
00341                     // TODO: CONSTANTS SHOULD BE USED - tx_service_workspace_workspaces
00342                     // TODO: use localized labels
00343                     // Compile label:
00344                 switch ((int)$stageId) {
00345                     case 1:
00346                         $newStage = 'Ready for review';
00347                     break;
00348                     case 10:
00349                         $newStage = 'Ready for publishing';
00350                     break;
00351                     case -1:
00352                         $newStage = 'Element was rejected!';
00353                     break;
00354                     case 0:
00355                         $newStage = 'Rejected element was noticed and edited';
00356                     break;
00357                     default:
00358                         $newStage = 'Unknown state change!?';
00359                     break;
00360                 }
00361             }
00362 
00363             if ($notificationAlternativeRecipients == false) {
00364                     // Compile list of recipients:
00365                 $emails = array();
00366                 switch((int)$stat['stagechg_notification']) {
00367                     case 1:
00368                         switch((int)$stageId)   {
00369                             case 1:
00370                                 $emails = $this->getEmailsForStageChangeNotification($workspaceRec['reviewers']);
00371                             break;
00372                             case 10:
00373                                 $emails = $this->getEmailsForStageChangeNotification($workspaceRec['adminusers'], TRUE);
00374                             break;
00375                             case -1:
00376 #                               $emails = $this->getEmailsForStageChangeNotification($workspaceRec['reviewers']);
00377 #                               $emails = array_merge($emails,$this->getEmailsForStageChangeNotification($workspaceRec['members']));
00378 
00379                                     // List of elements to reject:
00380                                 $allElements = explode(',', $elementName);
00381                                     // Traverse them, and find the history of each
00382                                 foreach ($allElements as $elRef) {
00383                                     list($eTable, $eUid) = explode(':', $elRef);
00384 
00385                                     $rows = $GLOBALS['TYPO3_DB']->exec_SELECTgetRows(
00386                                             'log_data,tstamp,userid',
00387                                             'sys_log',
00388                                             'action=6 and details_nr=30
00389                                             AND tablename=' . $GLOBALS['TYPO3_DB']->fullQuoteStr($eTable, 'sys_log') . '
00390                                             AND recuid=' . intval($eUid),
00391                                             '',
00392                                             'uid DESC'
00393                                     );
00394                                         // Find all implicated since the last stage-raise from editing to review:
00395                                     foreach ($rows as $dat) {
00396                                         $data = unserialize($dat['log_data']);
00397 
00398                                         $emails = t3lib_div::array_merge($emails, $this->getEmailsForStageChangeNotification($dat['userid'], TRUE));
00399 
00400                                         if ($data['stage'] == 1) {
00401                                             break;
00402                                         }
00403                                     }
00404                                 }
00405                             break;
00406 
00407                             case 0:
00408                                 $emails = $this->getEmailsForStageChangeNotification($workspaceRec['members']);
00409                             break;
00410 
00411                             default:
00412                                 $emails = $this->getEmailsForStageChangeNotification($workspaceRec['adminusers'], TRUE);
00413                             break;
00414                         }
00415                     break;
00416                     case 10:
00417                         $emails = $this->getEmailsForStageChangeNotification($workspaceRec['adminusers'], TRUE);
00418                         $emails = t3lib_div::array_merge($emails, $this->getEmailsForStageChangeNotification($workspaceRec['reviewers']));
00419                         $emails = t3lib_div::array_merge($emails, $this->getEmailsForStageChangeNotification($workspaceRec['members']));
00420                     break;
00421                 }
00422             } else {
00423                 $emails = array();
00424                 foreach ($notificationAlternativeRecipients as $emailAddress) {
00425                     $emails[] = array('email' => $emailAddress);
00426                 }
00427             }
00428 
00429                 // prepare and then send the emails
00430             if (count($emails)) {
00431 
00432                     // Path to record is found:
00433                 list($elementTable, $elementUid) = explode(':', $elementName);
00434                 $elementUid = intval($elementUid);
00435                 $elementRecord = t3lib_BEfunc::getRecord($elementTable, $elementUid);
00436                 $recordTitle = t3lib_BEfunc::getRecordTitle($elementTable, $elementRecord);
00437 
00438                 if ($elementTable == 'pages') {
00439                     $pageUid = $elementUid;
00440                 } else {
00441                     t3lib_BEfunc::fixVersioningPid($elementTable, $elementRecord);
00442                     $pageUid = $elementUid = $elementRecord['pid'];
00443                 }
00444 
00445                     // fetch the TSconfig settings for the email
00446 
00447                     // old way, options are TCEMAIN.notificationEmail_body/subject
00448                 $TCEmainTSConfig = $tcemainObj->getTCEMAIN_TSconfig($pageUid);
00449 
00450                     // these options are deprecated since TYPO3 4.5, but are still
00451                     // used in order to provide backwards compatibility
00452                 $emailMessage = trim($TCEmainTSConfig['notificationEmail_body']);
00453                 $emailSubject = trim($TCEmainTSConfig['notificationEmail_subject']);
00454 
00455                     // new way, options are
00456                     // pageTSconfig: tx_version.workspaces.stageNotificationEmail.subject
00457                     // userTSconfig: page.tx_version.workspaces.stageNotificationEmail.subject
00458                 $pageTsConfig = t3lib_BEfunc::getPagesTSconfig($pageUid);
00459                 $emailConfig = $pageTsConfig['tx_version.']['workspaces.']['stageNotificationEmail.'];
00460 
00461                 $markers = array(
00462                     '###RECORD_TITLE###' => $recordTitle,
00463                     '###RECORD_PATH###' => t3lib_BEfunc::getRecordPath($elementUid, '', 20),
00464                     '###SITE_NAME###' => $GLOBALS['TYPO3_CONF_VARS']['SYS']['sitename'],
00465                     '###SITE_URL###' => t3lib_div::getIndpEnv('TYPO3_SITE_URL') . TYPO3_mainDir,
00466                     '###WORKSPACE_TITLE###' => $workspaceRec['title'],
00467                     '###WORKSPACE_UID###' => $workspaceRec['uid'],
00468                     '###ELEMENT_NAME###' => $elementName,
00469                     '###NEXT_STAGE###' => $newStage,
00470                     '###COMMENT###' => $comment,
00471                     '###USER_REALNAME###' => $tcemainObj->BE_USER->user['realName'],
00472                     '###USER_USERNAME###' => $tcemainObj->BE_USER->user['username']
00473                 );
00474 
00475 
00476                     // sending the emails the old way with sprintf(),
00477                     // because it was set explicitly in TSconfig
00478                 if ($emailMessage && $emailSubject) {
00479                     t3lib_div::deprecationLog('This TYPO3 installation uses Workspaces staging notification by setting the TSconfig options "TCEMAIN.notificationEmail_subject" / "TCEMAIN.notificationEmail_body". Please use the more flexible marker-based options tx_version.workspaces.stageNotificationEmail.message / tx_version.workspaces.stageNotificationEmail.subject');
00480 
00481                     $emailSubject = sprintf($emailSubject, $elementName);
00482                     $emailMessage = sprintf($emailMessage,
00483                         $markers['###SITE_NAME###'],
00484                         $markers['###SITE_URL###'],
00485                         $markers['###WORKSPACE_TITLE###'],
00486                         $markers['###WORKSPACE_UID###'],
00487                         $markers['###ELEMENT_NAME###'],
00488                         $markers['###NEXT_STAGE###'],
00489                         $markers['###COMMENT###'],
00490                         $markers['###USER_REALNAME###'],
00491                         $markers['###USER_USERNAME###'],
00492                         $markers['###RECORD_PATH###'],
00493                         $markers['###RECORD_TITLE###']
00494                     );
00495 
00496                         // filter out double email addresses
00497                     $emailRecipients = array();
00498                     foreach ($emails as $recip) {
00499                         $emailRecipients[$recip['email']] = $recip['email'];
00500                     }
00501                     $emailRecipients = implode(',', $emailRecipients);
00502 
00503                         // Send one email to everybody
00504                     t3lib_div::plainMailEncoded(
00505                         $emailRecipients,
00506                         $emailSubject,
00507                         $emailMessage
00508                     );
00509                 } else {
00510                         // send an email to each individual user, to ensure the
00511                         // multilanguage version of the email
00512 
00513                     $emailHeaders = $emailConfig['additionalHeaders'];
00514                     $emailRecipients = array();
00515 
00516                         // an array of language objects that are needed
00517                         // for emails with different languages
00518                     $languageObjects = array(
00519                         $GLOBALS['LANG']->lang => $GLOBALS['LANG']
00520                     );
00521 
00522                         // loop through each recipient and send the email
00523                     foreach ($emails as $recipientData) {
00524                             // don't send an email twice
00525                         if (isset($emailRecipients[$recipientData['email']])) {
00526                             continue;
00527                         }
00528                         $emailSubject = $emailConfig['subject'];
00529                         $emailMessage = $emailConfig['message'];
00530                         $emailRecipients[$recipientData['email']] = $recipientData['email'];
00531 
00532                             // check if the email needs to be localized
00533                             // in the users' language
00534                         if (t3lib_div::isFirstPartOfStr($emailSubject, 'LLL:') || t3lib_div::isFirstPartOfStr($emailMessage, 'LLL:')) {
00535                             $recipientLanguage = ($recipientData['lang'] ? $recipientData['lang'] : 'default');
00536                             if (!isset($languageObjects[$recipientLanguage])) {
00537                                     // a LANG object in this language hasn't been
00538                                     // instantiated yet, so this is done here
00539                                 /** @var $languageObject language */
00540                                 $languageObject = t3lib_div::makeInstance('language');
00541                                 $languageObject->init($recipientLanguage);
00542                                 $languageObjects[$recipientLanguage] = $languageObject;
00543                             } else {
00544                                 $languageObject = $languageObjects[$recipientLanguage];
00545                             }
00546 
00547                             if (t3lib_div::isFirstPartOfStr($emailSubject, 'LLL:')) {
00548                                 $emailSubject = $languageObject->sL($emailSubject);
00549                             }
00550 
00551                             if (t3lib_div::isFirstPartOfStr($emailMessage, 'LLL:')) {
00552                                 $emailMessage = $languageObject->sL($emailMessage);
00553                             }
00554                         }
00555 
00556                         $emailSubject = t3lib_parseHtml::substituteMarkerArray($emailSubject, $markers, '', TRUE, TRUE);
00557                         $emailMessage = t3lib_parseHtml::substituteMarkerArray($emailMessage, $markers, '', TRUE, TRUE);
00558                             // Send an email to the recipient
00559                         t3lib_div::plainMailEncoded(
00560                             $recipientData['email'],
00561                             $emailSubject,
00562                             $emailMessage,
00563                             $emailHeaders
00564                         );
00565                     }
00566                     $emailRecipients = implode(',', $emailRecipients);
00567                 }
00568                 $tcemainObj->newlog2('Notification email for stage change was sent to "' . $emailRecipients . '"', $table, $id);
00569             }
00570         }
00571     }
00572 
00573     /**
00574      * Return be_users that should be notified on stage change from input list.
00575      * previously called notifyStageChange_getEmails() in tcemain
00576      *
00577      * @param   string      $listOfUsers List of backend users, on the form "be_users_10,be_users_2" or "10,2" in case noTablePrefix is set.
00578      * @param   boolean     $noTablePrefix If set, the input list are integers and not strings.
00579      * @return  array       Array of emails
00580      */
00581     protected function getEmailsForStageChangeNotification($listOfUsers, $noTablePrefix = FALSE) {
00582         $users = t3lib_div::trimExplode(',', $listOfUsers, 1);
00583         $emails = array();
00584         foreach ($users as $userIdent) {
00585             if ($noTablePrefix) {
00586                 $id = intval($userIdent);
00587             } else {
00588                 list($table, $id) = t3lib_div::revExplode('_', $userIdent, 2);
00589             }
00590             if ($table === 'be_users' || $noTablePrefix) {
00591                 if ($userRecord = t3lib_BEfunc::getRecord('be_users', $id, 'uid,email,lang,realName')) {
00592                     if (strlen(trim($userRecord['email']))) {
00593                         $emails[$id] = $userRecord;
00594                     }
00595                 }
00596             }
00597         }
00598         return $emails;
00599     }
00600 
00601 
00602     /****************************
00603      *****  Stage Changes  ******
00604      ****************************/
00605 
00606     /**
00607      * Setting stage of record
00608      *
00609      * @param string $table Table name
00610      * @param integer $integer Record UID
00611      * @param integer $stageId Stage ID to set
00612      * @param string $comment Comment that goes into log
00613      * @param boolean $notificationEmailInfo Accumulate state changes in memory for compiled notification email?
00614      * @param t3lib_TCEmain $tcemainObj TCEmain object
00615      * @param string $notificationAlternativeRecipients comma separated list of recipients to notificate instead of normal be_users
00616      * @return void
00617      */
00618     protected function version_setStage($table, $id, $stageId, $comment = '', $notificationEmailInfo = FALSE, t3lib_TCEmain $tcemainObj, $notificationAlternativeRecipients = FALSE) {
00619         if ($errorCode = $tcemainObj->BE_USER->workspaceCannotEditOfflineVersion($table, $id)) {
00620             $tcemainObj->newlog('Attempt to set stage for record failed: ' . $errorCode, 1);
00621         } elseif ($tcemainObj->checkRecordUpdateAccess($table, $id)) {
00622             $record = t3lib_BEfunc::getRecord($table, $id);
00623             $stat = $tcemainObj->BE_USER->checkWorkspace($record['t3ver_wsid']);
00624                 // check if the usere is allowed to the current stage, so it's also allowed to send to next stage
00625             if ($GLOBALS['BE_USER']->workspaceCheckStageForCurrent($record['t3ver_stage'])) {
00626 
00627                     // Set stage of record:
00628                 $updateData = array(
00629                     't3ver_stage' => $stageId
00630                 );
00631                 $GLOBALS['TYPO3_DB']->exec_UPDATEquery($table, 'uid=' . intval($id), $updateData);
00632                 $tcemainObj->newlog2('Stage for record was changed to ' . $stageId . '. Comment was: "' . substr($comment, 0, 100) . '"', $table, $id);
00633 
00634                     // TEMPORARY, except 6-30 as action/detail number which is observed elsewhere!
00635                 $tcemainObj->log($table, $id, 6, 0, 0, 'Stage raised...', 30, array('comment' => $comment, 'stage' => $stageId));
00636 
00637                 if ((int)$stat['stagechg_notification'] > 0) {
00638                     if ($notificationEmailInfo) {
00639                         $this->notificationEmailInfo[$stat['uid'] . ':' . $stageId . ':' . $comment]['shared'] = array($stat, $stageId, $comment);
00640                         $this->notificationEmailInfo[$stat['uid'] . ':' . $stageId . ':' . $comment]['elements'][] = $table . ':' . $id;
00641                         $this->notificationEmailInfo[$stat['uid'] . ':' . $stageId . ':' . $comment]['alternativeRecipients'] = $notificationAlternativeRecipients;
00642                     } else {
00643                         $this->notifyStageChange($stat, $stageId, $table, $id, $comment, $tcemainObj, $notificationAlternativeRecipients);
00644                     }
00645                 }
00646             } else $tcemainObj->newlog('The member user tried to set a stage value "' . $stageId . '" that was not allowed', 1);
00647         } else $tcemainObj->newlog('Attempt to set stage for record failed because you do not have edit access', 1);
00648     }
00649 
00650 
00651 
00652     /*****************************
00653      *****  CMD versioning  ******
00654      *****************************/
00655 
00656     /**
00657      * Creates a new version of a page including content and possible subpages.
00658      *
00659      * @param integer $uid Page uid to create new version of.
00660      * @param string $label Version label
00661      * @param integer $versionizeTree Indicating "treeLevel" - "page" (0) or "branch" (>=1) ["element" type must call versionizeRecord() directly]
00662      * @param t3lib_TCEmain $tcemainObj TCEmain object
00663      * @return void
00664      * @see copyPages()
00665      */
00666     protected function versionizePages($uid, $label, $versionizeTree, t3lib_TCEmain $tcemainObj) {
00667         global $TCA;
00668 
00669         $uid = intval($uid);
00670             // returns the branch
00671         $brExist = $tcemainObj->doesBranchExist('', $uid, $tcemainObj->pMap['show'], 1);
00672 
00673             // Checks if we had permissions
00674         if ($brExist != -1) {
00675 
00676                 // Make list of tables that should come along with a new version of the page:
00677             $verTablesArray = array();
00678             $allTables = array_keys($TCA);
00679             foreach ($allTables as $tableName) {
00680                 if ($tableName != 'pages' && ($versionizeTree > 0 || $TCA[$tableName]['ctrl']['versioning_followPages'])) {
00681                     $verTablesArray[] = $tableName;
00682                 }
00683             }
00684 
00685                 // Remove the possible inline child tables from the tables to be versioniozed automatically:
00686             $verTablesArray = array_diff($verTablesArray, $this->getPossibleInlineChildTablesOfParentTable('pages'));
00687 
00688                 // Begin to copy pages if we're allowed to:
00689             if ($tcemainObj->BE_USER->workspaceVersioningTypeAccess($versionizeTree)) {
00690 
00691                     // Versionize this page:
00692                 $theNewRootID = $tcemainObj->versionizeRecord('pages', $uid, $label, FALSE, $versionizeTree);
00693                 if ($theNewRootID) {
00694                     $this->rawCopyPageContent($uid, $theNewRootID, $verTablesArray, $tcemainObj);
00695 
00696                         // If we're going to copy recursively...:
00697                     if ($versionizeTree > 0) {
00698 
00699                             // Get ALL subpages to copy (read permissions respected - they should NOT be...):
00700                         $CPtable = $tcemainObj->int_pageTreeInfo(array(), $uid, intval($versionizeTree), $theNewRootID);
00701 
00702                             // Now copying the subpages
00703                         foreach ($CPtable as $thePageUid => $thePagePid)    {
00704                             $newPid = $tcemainObj->copyMappingArray['pages'][$thePagePid];
00705                             if (isset($newPid)) {
00706                                 $theNewRootID = $tcemainObj->copyRecord_raw('pages', $thePageUid, $newPid);
00707                                 $this->rawCopyPageContent($thePageUid, $theNewRootID, $verTablesArray, $tcemainObj);
00708                             } else {
00709                                 $tcemainObj->newlog('Something went wrong during copying branch (for versioning)', 1);
00710                                 break;
00711                             }
00712                         }
00713                     }   // else the page was not copied. Too bad...
00714                 } else $tcemainObj->newlog('The root version could not be created!',1);
00715             } else $tcemainObj->newlog('Versioning type "'.$versionizeTree.'" was not allowed in workspace',1);
00716         } else $tcemainObj->newlog('Could not read all subpages to versionize.',1);
00717     }
00718 
00719 
00720     /**
00721      * Swapping versions of a record
00722      * Version from archive (future/past, called "swap version") will get the uid of the "t3ver_oid", the official element with uid = "t3ver_oid" will get the new versions old uid. PIDs are swapped also
00723      *
00724      * @param string $table Table name
00725      * @param integer $id UID of the online record to swap
00726      * @param integer $swapWith UID of the archived version to swap with!
00727      * @param boolean $swapIntoWS If set, swaps online into workspace instead of publishing out of workspace.
00728      * @param t3lib_TCEmain $tcemainObj TCEmain object
00729      * @return void
00730      */
00731     protected function version_swap($table, $id, $swapWith, $swapIntoWS=0, t3lib_TCEmain $tcemainObj) {
00732         global $TCA;
00733 
00734             // First, check if we may actually edit the online record
00735         if ($tcemainObj->checkRecordUpdateAccess($table, $id)) {
00736 
00737                 // Select the two versions:
00738             $curVersion = t3lib_BEfunc::getRecord($table, $id, '*');
00739             $swapVersion = t3lib_BEfunc::getRecord($table, $swapWith, '*');
00740             $movePlh = array();
00741             $movePlhID = 0;
00742 
00743             if (is_array($curVersion) && is_array($swapVersion)) {
00744                 if ($tcemainObj->BE_USER->workspacePublishAccess($swapVersion['t3ver_wsid'])) {
00745                     $wsAccess = $tcemainObj->BE_USER->checkWorkspace($swapVersion['t3ver_wsid']);
00746                     if ($swapVersion['t3ver_wsid'] <= 0 || !($wsAccess['publish_access'] & 1) || (int)$swapVersion['t3ver_stage'] === -10) {
00747                         if ($tcemainObj->doesRecordExist($table,$swapWith,'show') && $tcemainObj->checkRecordUpdateAccess($table,$swapWith)) {
00748                             if (!$swapIntoWS || $tcemainObj->BE_USER->workspaceSwapAccess()) {
00749 
00750                                     // Check if the swapWith record really IS a version of the original!
00751                                 if ((int)$swapVersion['pid'] == -1 && (int)$curVersion['pid'] >= 0 && !strcmp($swapVersion['t3ver_oid'], $id)) {
00752 
00753                                         // Lock file name:
00754                                     $lockFileName = PATH_site.'typo3temp/swap_locking/' . $table . ':' . $id . '.ser';
00755 
00756                                     if (!@is_file($lockFileName))   {
00757 
00758                                             // Write lock-file:
00759                                         t3lib_div::writeFileToTypo3tempDir($lockFileName, serialize(array(
00760                                             'tstamp' => $GLOBALS['EXEC_TIME'],
00761                                             'user'   => $tcemainObj->BE_USER->user['username'],
00762                                             'curVersion'  => $curVersion,
00763                                             'swapVersion' => $swapVersion
00764                                         )));
00765 
00766                                             // Find fields to keep
00767                                         $keepFields = $tcemainObj->getUniqueFields($table);
00768                                         if ($TCA[$table]['ctrl']['sortby']) {
00769                                             $keepFields[] = $TCA[$table]['ctrl']['sortby'];
00770                                         }
00771                                             // l10n-fields must be kept otherwise the localization
00772                                             // will be lost during the publishing
00773                                         if (!isset($TCA[$table]['ctrl']['transOrigPointerTable']) && $TCA[$table]['ctrl']['transOrigPointerField']) {
00774                                             $keepFields[] = $TCA[$table]['ctrl']['transOrigPointerField'];
00775                                         }
00776 
00777                                             // Swap "keepfields"
00778                                         foreach ($keepFields as $fN) {
00779                                             $tmp = $swapVersion[$fN];
00780                                             $swapVersion[$fN] = $curVersion[$fN];
00781                                             $curVersion[$fN] = $tmp;
00782                                         }
00783 
00784                                             // Preserve states:
00785                                         $t3ver_state = array();
00786                                         $t3ver_state['swapVersion'] = $swapVersion['t3ver_state'];
00787                                         $t3ver_state['curVersion'] = $curVersion['t3ver_state'];
00788 
00789                                             // Modify offline version to become online:
00790                                         $tmp_wsid = $swapVersion['t3ver_wsid'];
00791                                             // Set pid for ONLINE
00792                                         $swapVersion['pid'] = intval($curVersion['pid']);
00793                                             // We clear this because t3ver_oid only make sense for offline versions
00794                                             // and we want to prevent unintentional misuse of this
00795                                             // value for online records.
00796                                         $swapVersion['t3ver_oid'] = 0;
00797                                             // In case of swapping and the offline record has a state
00798                                             // (like 2 or 4 for deleting or move-pointer) we set the
00799                                             // current workspace ID so the record is not deselected
00800                                             // in the interface by t3lib_BEfunc::versioningPlaceholderClause()
00801                                         $swapVersion['t3ver_wsid'] = 0;
00802                                         if ($swapIntoWS) {
00803                                             if ($t3ver_state['swapVersion'] > 0) {
00804                                                 $swapVersion['t3ver_wsid'] = $tcemainObj->BE_USER->workspace;
00805                                             } else {
00806                                                 $swapVersion['t3ver_wsid'] = intval($curVersion['t3ver_wsid']);
00807                                             }
00808                                         }
00809                                         $swapVersion['t3ver_tstamp'] = $GLOBALS['EXEC_TIME'];
00810                                         $swapVersion['t3ver_stage'] = 0;
00811                                         if (!$swapIntoWS) {
00812                                             $swapVersion['t3ver_state'] = 0;
00813                                         }
00814 
00815                                             // Moving element.
00816                                         if ((int)$TCA[$table]['ctrl']['versioningWS']>=2)   {       //  && $t3ver_state['swapVersion']==4   // Maybe we don't need this?
00817                                             if ($plhRec = t3lib_BEfunc::getMovePlaceholder($table, $id, 't3ver_state,pid,uid' . ($TCA[$table]['ctrl']['sortby'] ? ',' . $TCA[$table]['ctrl']['sortby'] : ''))) {
00818                                                 $movePlhID = $plhRec['uid'];
00819                                                 $movePlh['pid'] = $swapVersion['pid'];
00820                                                 $swapVersion['pid'] = intval($plhRec['pid']);
00821 
00822                                                 $curVersion['t3ver_state'] = intval($swapVersion['t3ver_state']);
00823                                                 $swapVersion['t3ver_state'] = 0;
00824 
00825                                                 if ($TCA[$table]['ctrl']['sortby']) {
00826                                                         // sortby is a "keepFields" which is why this will work...
00827                                                     $movePlh[$TCA[$table]['ctrl']['sortby']] = $swapVersion[$TCA[$table]['ctrl']['sortby']];
00828                                                     $swapVersion[$TCA[$table]['ctrl']['sortby']] = $plhRec[$TCA[$table]['ctrl']['sortby']];
00829                                                 }
00830                                             }
00831                                         }
00832 
00833                                             // Take care of relations in each field (e.g. IRRE):
00834                                         if (is_array($GLOBALS['TCA'][$table]['columns'])) {
00835                                             foreach ($GLOBALS['TCA'][$table]['columns'] as $field => $fieldConf) {
00836                                                 $this->version_swap_procBasedOnFieldType(
00837                                                     $table, $field, $fieldConf['config'], $curVersion, $swapVersion, $tcemainObj
00838                                                 );
00839                                             }
00840                                         }
00841                                         unset($swapVersion['uid']);
00842 
00843                                             // Modify online version to become offline:
00844                                         unset($curVersion['uid']);
00845                                             // Set pid for OFFLINE
00846                                         $curVersion['pid'] = -1;
00847                                         $curVersion['t3ver_oid'] = intval($id);
00848                                         $curVersion['t3ver_wsid'] = ($swapIntoWS ? intval($tmp_wsid) : 0);
00849                                         $curVersion['t3ver_tstamp'] = $GLOBALS['EXEC_TIME'];
00850                                         $curVersion['t3ver_count'] = $curVersion['t3ver_count']+1;  // Increment lifecycle counter
00851                                         $curVersion['t3ver_stage'] = 0;
00852                                         if (!$swapIntoWS) {
00853                                             $curVersion['t3ver_state'] = 0;
00854                                         }
00855 
00856                                             // Keeping the swapmode state
00857                                         if ($table === 'pages') {
00858                                             $curVersion['t3ver_swapmode'] = $swapVersion['t3ver_swapmode'];
00859                                         }
00860 
00861                                             // Registering and swapping MM relations in current and swap records:
00862                                         $tcemainObj->version_remapMMForVersionSwap($table, $id, $swapWith);
00863 
00864                                             // Generating proper history data to prepare logging
00865                                         $tcemainObj->compareFieldArrayWithCurrentAndUnset($table, $id, $swapVersion);
00866                                         $tcemainObj->compareFieldArrayWithCurrentAndUnset($table, $swapWith, $curVersion);
00867 
00868                                             // Execute swapping:
00869                                         $sqlErrors = array();
00870                                         $GLOBALS['TYPO3_DB']->exec_UPDATEquery($table, 'uid=' . intval($id), $swapVersion);
00871                                         if ($GLOBALS['TYPO3_DB']->sql_error()) {
00872                                             $sqlErrors[] = $GLOBALS['TYPO3_DB']->sql_error();
00873                                         } else {
00874                                             $GLOBALS['TYPO3_DB']->exec_UPDATEquery($table, 'uid=' . intval($swapWith), $curVersion);
00875                                             if ($GLOBALS['TYPO3_DB']->sql_error()) {
00876                                                 $sqlErrors[] = $GLOBALS['TYPO3_DB']->sql_error();
00877                                             } else {
00878                                                 unlink($lockFileName);
00879                                             }
00880                                         }
00881 
00882                                         if (!count($sqlErrors)) {
00883                                                 // Register swapped ids for later remapping:
00884                                             $this->remappedIds[$table][$id] =$swapWith;
00885                                             $this->remappedIds[$table][$swapWith] = $id;
00886 
00887                                                 // If a moving operation took place...:
00888                                             if ($movePlhID) {
00889                                                     // Remove, if normal publishing:
00890                                                 if (!$swapIntoWS) {
00891                                                         // For delete + completely delete!
00892                                                     $tcemainObj->deleteEl($table, $movePlhID, TRUE, TRUE);
00893                                                 } else {
00894                                                     // Otherwise update the movePlaceholder:
00895                                                     $GLOBALS['TYPO3_DB']->exec_UPDATEquery($table, 'uid=' . intval($movePlhID), $movePlh);
00896                                                     $tcemainObj->addRemapStackRefIndex($table, $movePlhID);
00897                                                 }
00898                                             }
00899 
00900                                                 // Checking for delete:
00901                                                 // Delete only if new/deleted placeholders are there.
00902                                             if (!$swapIntoWS && ((int)$t3ver_state['swapVersion'] === 1 || (int)$t3ver_state['swapVersion'] === 2)) {
00903                                                     // Force delete
00904                                                 $tcemainObj->deleteEl($table, $id, TRUE);
00905                                             }
00906 
00907                                             $tcemainObj->newlog2(($swapIntoWS ? 'Swapping' : 'Publishing') . ' successful for table "' . $table . '" uid ' . $id . '=>' . $swapWith, $table, $id, $swapVersion['pid']);
00908 
00909                                                 // Update reference index of the live record:
00910                                             $tcemainObj->addRemapStackRefIndex($table, $id);
00911 
00912                                                 // Set log entry for live record:
00913                                             $propArr = $tcemainObj->getRecordPropertiesFromRow($table, $swapVersion);
00914                                             if ($propArr['_ORIG_pid'] == -1) {
00915                                                 $label = $GLOBALS['LANG']->sL ('LLL:EXT:lang/locallang_tcemain.xml:version_swap.offline_record_updated');
00916                                             } else {
00917                                                 $label = $GLOBALS['LANG']->sL ('LLL:EXT:lang/locallang_tcemain.xml:version_swap.online_record_updated');
00918                                             }
00919                                             $theLogId = $tcemainObj->log($table, $id, 2, $propArr['pid'], 0, $label , 10, array($propArr['header'], $table . ':' . $id), $propArr['event_pid']);
00920                                             $tcemainObj->setHistory($table, $id, $theLogId);
00921 
00922                                                 // Update reference index of the offline record:
00923                                             $tcemainObj->addRemapStackRefIndex($table, $swapWith);
00924                                                 // Set log entry for offline record:
00925                                             $propArr = $tcemainObj->getRecordPropertiesFromRow($table, $curVersion);
00926                                             if ($propArr['_ORIG_pid'] == -1) {
00927                                                 $label = $GLOBALS['LANG']->sL ('LLL:EXT:lang/locallang_tcemain.xml:version_swap.offline_record_updated');
00928                                             } else {
00929                                                 $label = $GLOBALS['LANG']->sL ('LLL:EXT:lang/locallang_tcemain.xml:version_swap.online_record_updated');
00930                                             }
00931                                             $theLogId = $tcemainObj->log($table, $swapWith, 2, $propArr['pid'], 0, $label, 10, array($propArr['header'], $table . ':' . $swapWith), $propArr['event_pid']);
00932                                             $tcemainObj->setHistory($table, $swapWith, $theLogId);
00933 
00934                                                 // SWAPPING pids for subrecords:
00935                                             if ($table=='pages' && $swapVersion['t3ver_swapmode'] >= 0) {
00936 
00937                                                     // Collect table names that should be copied along with the tables:
00938                                                 foreach ($TCA as $tN => $tCfg)  {
00939                                                         // For "Branch" publishing swap ALL,
00940                                                         // otherwise for "page" publishing, swap only "versioning_followPages" tables
00941                                                     if ($swapVersion['t3ver_swapmode'] > 0 || $TCA[$tN]['ctrl']['versioning_followPages']) {
00942                                                         $temporaryPid = -($id+1000000);
00943 
00944                                                         $GLOBALS['TYPO3_DB']->exec_UPDATEquery($tN, 'pid=' . intval($id), array('pid' => $temporaryPid));
00945                                                         if ($GLOBALS['TYPO3_DB']->sql_error()) {
00946                                                             $sqlErrors[] = $GLOBALS['TYPO3_DB']->sql_error();
00947                                                         }
00948 
00949                                                         $GLOBALS['TYPO3_DB']->exec_UPDATEquery($tN, 'pid=' . intval($swapWith), array('pid' => $id));
00950                                                         if ($GLOBALS['TYPO3_DB']->sql_error()) {
00951                                                             $sqlErrors[] = $GLOBALS['TYPO3_DB']->sql_error();
00952                                                         }
00953 
00954                                                         $GLOBALS['TYPO3_DB']->exec_UPDATEquery($tN, 'pid=' . intval($temporaryPid), array('pid' => $swapWith));
00955                                                         if ($GLOBALS['TYPO3_DB']->sql_error()) {
00956                                                             $sqlErrors[] = $GLOBALS['TYPO3_DB']->sql_error();
00957                                                         }
00958 
00959                                                         if (count($sqlErrors)) {
00960                                                             $tcemainObj->newlog('During Swapping: SQL errors happened: ' . implode('; ', $sqlErrors), 2);
00961                                                         }
00962                                                     }
00963                                                 }
00964                                             }
00965                                                 // Clear cache:
00966                                             $tcemainObj->clear_cache($table, $id);
00967 
00968                                                 // Checking for "new-placeholder" and if found, delete it (BUT FIRST after swapping!):
00969                                             if (!$swapIntoWS && $t3ver_state['curVersion']>0) {
00970                                                     // For delete + completely delete!
00971                                                 $tcemainObj->deleteEl($table, $swapWith, TRUE, TRUE);
00972                                             }
00973                                         } else $tcemainObj->newlog('During Swapping: SQL errors happened: ' . implode('; ', $sqlErrors), 2);
00974                                     } else $tcemainObj->newlog('A swapping lock file was present. Either another swap process is already running or a previous swap process failed. Ask your administrator to handle the situation.', 2);
00975                                 } else $tcemainObj->newlog('In swap version, either pid was not -1 or the t3ver_oid didn\'t match the id of the online version as it must!', 2);
00976                             } else $tcemainObj->newlog('Workspace #' . $swapVersion['t3ver_wsid'] . ' does not support swapping.', 1);
00977                         } else $tcemainObj->newlog('You cannot publish a record you do not have edit and show permissions for', 1);
00978                     } else $tcemainObj->newlog('Records in workspace #' . $swapVersion['t3ver_wsid'] . ' can only be published when in "Publish" stage.', 1);
00979                 } else $tcemainObj->newlog('User could not publish records from workspace #' . $swapVersion['t3ver_wsid'], 1);
00980             } else $tcemainObj->newlog('Error: Either online or swap version could not be selected!', 2);
00981         } else $tcemainObj->newlog('Error: You cannot swap versions for a record you do not have access to edit!', 1);
00982     }
00983 
00984 
00985     /**
00986      * Update relations on version/workspace swapping.
00987      *
00988      * @param string $table: Record Table
00989      * @param string $field: Record field
00990      * @param array $conf: TCA configuration of current field
00991      * @param array $curVersion: Reference to the current (original) record
00992      * @param array $swapVersion: Reference to the record (workspace/versionized) to publish in or swap with
00993      * @param t3lib_TCEmain $tcemainObj TCEmain object
00994      * @return  void
00995      */
00996     protected function version_swap_procBasedOnFieldType($table, $field, array $conf, array &$curVersion, array &$swapVersion, t3lib_TCEmain $tcemainObj) {
00997         $inlineType = $tcemainObj->getInlineFieldType($conf);
00998 
00999             // Process pointer fields on normalized database:
01000         if ($inlineType == 'field') {
01001             // Read relations that point to the current record (e.g. live record):
01002             /** @var $dbAnalysisCur t3lib_loadDBGroup */
01003             $dbAnalysisCur = t3lib_div::makeInstance('t3lib_loadDBGroup');
01004             $dbAnalysisCur->setUpdateReferenceIndex(FALSE);
01005             $dbAnalysisCur->start('', $conf['foreign_table'], '', $curVersion['uid'], $table, $conf);
01006             // Read relations that point to the record to be swapped with e.g. draft record):
01007             /** @var $dbAnalysisSwap t3lib_loadDBGroup */
01008             $dbAnalysisSwap = t3lib_div::makeInstance('t3lib_loadDBGroup');
01009             $dbAnalysisSwap->setUpdateReferenceIndex(FALSE);
01010             $dbAnalysisSwap->start('', $conf['foreign_table'], '', $swapVersion['uid'], $table, $conf);
01011                 // Update relations for both (workspace/versioning) sites:
01012 
01013             if (count($dbAnalysisCur->itemArray)) {
01014                 $dbAnalysisCur->writeForeignField($conf, $curVersion['uid'], $swapVersion['uid']);
01015                 $tcemainObj->addRemapAction(
01016                     $table, $curVersion['uid'],
01017                     array($this, 'writeRemappedForeignField'),
01018                     array($dbAnalysisCur, $conf, $swapVersion['uid'])
01019                 );
01020             }
01021 
01022             if (count($dbAnalysisSwap->itemArray)) {
01023                 $dbAnalysisSwap->writeForeignField($conf, $swapVersion['uid'], $curVersion['uid']);
01024                 $tcemainObj->addRemapAction(
01025                     $table, $curVersion['uid'],
01026                     array($this, 'writeRemappedForeignField'),
01027                     array($dbAnalysisSwap, $conf, $curVersion['uid'])
01028                 );
01029             }
01030 
01031             $items = array_merge($dbAnalysisCur->itemArray, $dbAnalysisSwap->itemArray);
01032             foreach ($items as $item) {
01033                 $tcemainObj->addRemapStackRefIndex($item['table'], $item['id']);
01034             }
01035 
01036             // Swap field values (CSV):
01037             // BUT: These values will be swapped back in the next steps, when the *CHILD RECORD ITSELF* is swapped!
01038         } elseif ($inlineType == 'list') {
01039             $tempValue = $curVersion[$field];
01040             $curVersion[$field] = $swapVersion[$field];
01041             $swapVersion[$field] = $tempValue;
01042         }
01043     }
01044 
01045     /**
01046      * Writes remapped foreign field (IRRE).
01047      *
01048      * @param t3lib_loadDBGroup $dbAnalysis Instance that holds the sorting order of child records
01049      * @param array $configuration The TCA field configuration
01050      * @param integer $parentId The uid of the parent record
01051      * @return void
01052      */
01053     public function writeRemappedForeignField(t3lib_loadDBGroup $dbAnalysis, array $configuration, $parentId) {
01054         foreach ($dbAnalysis->itemArray as &$item) {
01055             if (isset($this->remappedIds[$item['table']][$item['id']])) {
01056                 $item['id'] = $this->remappedIds[$item['table']][$item['id']];
01057             }
01058         }
01059 
01060         $dbAnalysis->writeForeignField($configuration, $parentId);
01061     }
01062 
01063 
01064 
01065     /**
01066      * Release version from this workspace (and into "Live" workspace but as an offline version).
01067      *
01068      * @param string $table Table name
01069      * @param integer $id Record UID
01070      * @param boolean $flush If set, will completely delete element
01071      * @param t3lib_TCEmain $tcemainObj TCEmain object
01072      * @return  void
01073      */
01074     protected function version_clearWSID($table, $id, $flush = FALSE, t3lib_TCEmain $tcemainObj) {
01075         global $TCA;
01076 
01077         if ($errorCode = $tcemainObj->BE_USER->workspaceCannotEditOfflineVersion($table, $id)) {
01078             $tcemainObj->newlog('Attempt to reset workspace for record failed: ' . $errorCode, 1);
01079         } elseif ($tcemainObj->checkRecordUpdateAccess($table, $id)) {
01080             if ($liveRec = t3lib_BEfunc::getLiveVersionOfRecord($table, $id, 'uid,t3ver_state')) {
01081                     // Clear workspace ID:
01082                 $updateData = array(
01083                     't3ver_wsid' => 0
01084                 );
01085                 $GLOBALS['TYPO3_DB']->exec_UPDATEquery($table, 'uid=' . intval($id), $updateData);
01086 
01087                     // Clear workspace ID for live version AND DELETE IT as well because it is a new record!
01088                 if ((int) $liveRec['t3ver_state'] == 1 || (int) $liveRec['t3ver_state'] == 2) {
01089                     $GLOBALS['TYPO3_DB']->exec_UPDATEquery($table,'uid=' . intval($liveRec['uid']), $updateData);
01090                         // THIS assumes that the record was placeholder ONLY for ONE record (namely $id)
01091                     $tcemainObj->deleteEl($table, $liveRec['uid'], TRUE);
01092                 }
01093 
01094                     // If "deleted" flag is set for the version that got released
01095                     // it doesn't make sense to keep that "placeholder" anymore and we delete it completly.
01096                 $wsRec = t3lib_BEfunc::getRecord($table, $id);
01097                 if ($flush || ((int) $wsRec['t3ver_state'] == 1 || (int) $wsRec['t3ver_state'] == 2)) {
01098                     $tcemainObj->deleteEl($table, $id, TRUE, TRUE);
01099                 }
01100 
01101                     // Remove the move-placeholder if found for live record.
01102                 if ((int)$TCA[$table]['ctrl']['versioningWS'] >= 2) {
01103                     if ($plhRec = t3lib_BEfunc::getMovePlaceholder($table, $liveRec['uid'], 'uid')) {
01104                         $tcemainObj->deleteEl($table, $plhRec['uid'], TRUE, TRUE);
01105                     }
01106                 }
01107             }
01108         } else $tcemainObj->newlog('Attempt to reset workspace for record failed because you do not have edit access',1);
01109     }
01110 
01111 
01112     /*******************************
01113      *****  helper functions  ******
01114      *******************************/
01115 
01116 
01117     /**
01118      * Copies all records from tables in $copyTablesArray from page with $old_pid to page with $new_pid
01119      * Uses raw-copy for the operation (meant for versioning!)
01120      *
01121      * @param integer $oldPageId Current page id.
01122      * @param integer $newPageId New page id
01123      * @param array $copyTablesArray Array of tables from which to copy
01124      * @param t3lib_TCEmain $tcemainObj TCEmain object
01125      * @return void
01126      * @see versionizePages()
01127      */
01128     protected function rawCopyPageContent($oldPageId, $newPageId, array $copyTablesArray, t3lib_TCEmain $tcemainObj) {
01129         global $TCA;
01130 
01131         if ($newPageId) {
01132             foreach ($copyTablesArray as $table) {
01133                         // all records under the page is copied.
01134                 if ($table && is_array($TCA[$table]) && $table != 'pages') {
01135                     $mres = $GLOBALS['TYPO3_DB']->exec_SELECTquery(
01136                         'uid',
01137                         $table,
01138                         'pid=' . intval($oldPageId) . $tcemainObj->deleteClause($table)
01139                     );
01140                     while ($row = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($mres)) {
01141                             // Check, if this record has already been copied by a parent record as relation:
01142                         if (!$tcemainObj->copyMappingArray[$table][$row['uid']]) {
01143                                 // Copying each of the underlying records (method RAW)
01144                             $tcemainObj->copyRecord_raw($table, $row['uid'], $newPageId);
01145                         }
01146                     }
01147                     $GLOBALS['TYPO3_DB']->sql_free_result($mres);
01148                 }
01149             }
01150         }
01151     }
01152 
01153 
01154     /**
01155      * Finds all elements for swapping versions in workspace
01156      *
01157      * @param  string $table Table name of the original element to swap
01158      * @param integer $id UID of the original element to swap (online)
01159      * @param integer $offlineId As above but offline
01160      * @return array Element data. Key is table name, values are array with first element as online UID, second - offline UID
01161      */
01162     protected function findPageElementsForVersionSwap($table, $id, $offlineId) {
01163         global  $TCA;
01164 
01165         $rec = t3lib_BEfunc::getRecord($table, $offlineId, 't3ver_wsid');
01166         $workspaceId = $rec['t3ver_wsid'];
01167 
01168         $elementData = array();
01169         if ($workspaceId != 0) {
01170             // Get page UID for LIVE and workspace
01171             if ($table != 'pages') {
01172                 $rec = t3lib_BEfunc::getRecord($table, $id, 'pid');
01173                 $pageId = $rec['pid'];
01174                 $rec = t3lib_BEfunc::getRecord('pages', $pageId);
01175                 t3lib_BEfunc::workspaceOL('pages', $rec, $workspaceId);
01176                 $offlinePageId = $rec['_ORIG_uid'];
01177             } else {
01178                 $pageId = $id;
01179                 $offlinePageId = $offlineId;
01180             }
01181 
01182             // Traversing all tables supporting versioning:
01183             foreach ($TCA as $table => $cfg) {
01184                 if ($TCA[$table]['ctrl']['versioningWS'] && $table != 'pages') {
01185                     $res = $GLOBALS['TYPO3_DB']->exec_SELECTquery('A.uid AS offlineUid, B.uid AS uid',
01186                             $table . ' A,' . $table . ' B',
01187                             'A.pid=-1 AND B.pid=' . $pageId . ' AND A.t3ver_wsid=' . $workspaceId .
01188                             ' AND B.uid=A.t3ver_oid' .
01189                             t3lib_BEfunc::deleteClause($table, 'A') . t3lib_BEfunc::deleteClause($table, 'B'));
01190                     while (FALSE != ($row = $GLOBALS['TYPO3_DB']->sql_fetch_row($res))) {
01191                         $elementData[$table][] = array($row[1], $row[0]);
01192                     }
01193                     $GLOBALS['TYPO3_DB']->sql_free_result($res);
01194                 }
01195             }
01196             if ($offlinePageId && $offlinePageId != $pageId) {
01197                 $elementData['pages'][] = array($pageId, $offlinePageId);
01198             }
01199         }
01200         return $elementData;
01201     }
01202 
01203     /**
01204      * Searches for all elements from all tables on the given pages in the same workspace.
01205      *
01206      * @param array $pageIdList List of PIDs to search
01207      * @param integer $workspaceId Workspace ID
01208      * @param array $elementList List of found elements. Key is table name, value is array of element UIDs
01209      * @return void
01210      */
01211     protected function findPageElementsForVersionStageChange(array $pageIdList, $workspaceId, array &$elementList) {
01212         global $TCA;
01213 
01214         if ($workspaceId != 0) {
01215                 // Traversing all tables supporting versioning:
01216             foreach ($TCA as $table => $cfg)    {
01217                 if ($TCA[$table]['ctrl']['versioningWS'] && $table != 'pages')  {
01218                     $res = $GLOBALS['TYPO3_DB']->exec_SELECTquery('DISTINCT A.uid',
01219                         $table . ' A,' . $table . ' B',
01220                         'A.pid=-1' .        // Offline version
01221                         ' AND A.t3ver_wsid=' . $workspaceId .
01222                         ' AND B.pid IN (' . implode(',', $pageIdList) . ') AND A.t3ver_oid=B.uid' .
01223                         t3lib_BEfunc::deleteClause($table,'A').
01224                         t3lib_BEfunc::deleteClause($table,'B')
01225                     );
01226                     while (FALSE !== ($row = $GLOBALS['TYPO3_DB']->sql_fetch_row($res))) {
01227                         $elementList[$table][] = $row[0];
01228                     }
01229                     $GLOBALS['TYPO3_DB']->sql_free_result($res);
01230                     if (is_array($elementList[$table])) {
01231                         // Yes, it is possible to get non-unique array even with DISTINCT above!
01232                         // It happens because several UIDs are passed in the array already.
01233                         $elementList[$table] = array_unique($elementList[$table]);
01234                     }
01235                 }
01236             }
01237         }
01238     }
01239 
01240 
01241     /**
01242      * Finds page UIDs for the element from table <code>$table</code> with UIDs from <code>$idList</code>
01243      *
01244      * @param string $table Table to search
01245      * @param array $idList List of records' UIDs
01246      * @param integer $workspaceId Workspace ID. We need this parameter because user can be in LIVE but he still can publisg DRAFT from ws module!
01247      * @param array $pageIdList List of found page UIDs
01248      * @param array $elementList List of found element UIDs. Key is table name, value is list of UIDs
01249      * @return void
01250      */
01251     protected function findPageIdsForVersionStateChange($table, array $idList, $workspaceId, array &$pageIdList, array &$elementList) {
01252         if ($workspaceId != 0) {
01253             $res = $GLOBALS['TYPO3_DB']->exec_SELECTquery('DISTINCT B.pid',
01254                 $table . ' A,' . $table . ' B',
01255                 'A.pid=-1' .        // Offline version
01256                 ' AND A.t3ver_wsid=' . $workspaceId .
01257                 ' AND A.uid IN (' . implode(',', $idList) . ') AND A.t3ver_oid=B.uid' .
01258                 t3lib_BEfunc::deleteClause($table,'A').
01259                 t3lib_BEfunc::deleteClause($table,'B')
01260             );
01261             while (FALSE !== ($row = $GLOBALS['TYPO3_DB']->sql_fetch_row($res))) {
01262                 $pageIdList[] = $row[0];
01263                     // Find ws version
01264                     // Note: cannot use t3lib_BEfunc::getRecordWSOL()
01265                     // here because it does not accept workspace id!
01266                 $rec = t3lib_BEfunc::getRecord('pages', $row[0]);
01267                 t3lib_BEfunc::workspaceOL('pages', $rec, $workspaceId);
01268                 if ($rec['_ORIG_uid']) {
01269                     $elementList['pages'][$row[0]] = $rec['_ORIG_uid'];
01270                 }
01271             }
01272             $GLOBALS['TYPO3_DB']->sql_free_result($res);
01273                 // The line below is necessary even with DISTINCT
01274                 // because several elements can be passed by caller
01275             $pageIdList = array_unique($pageIdList);
01276         }
01277     }
01278 
01279 
01280     /**
01281      * Finds real page IDs for state change.
01282      *
01283      * @param   array   $idList List of page UIDs, possibly versioned
01284      * @return  void
01285      */
01286     protected function findRealPageIds(array &$idList) {
01287         foreach ($idList as $key => $id) {
01288             $rec = t3lib_BEfunc::getRecord('pages', $id, 't3ver_oid');
01289             if ($rec['t3ver_oid'] > 0) {
01290                 $idList[$key] = $rec['t3ver_oid'];
01291             }
01292         }
01293     }
01294 
01295 
01296     /**
01297      * Creates a move placeholder for workspaces.
01298      * USE ONLY INTERNALLY
01299      * Moving placeholder: Can be done because the system sees it as a placeholder for NEW elements like t3ver_state=1
01300      * Moving original: Will either create the placeholder if it doesn't exist or move existing placeholder in workspace.
01301      *
01302      * @param string $table Table name to move
01303      * @param integer $uid Record uid to move (online record)
01304      * @param integer $destPid Position to move to: $destPid: >=0 then it points to a page-id on which to insert the record (as the first element). <0 then it points to a uid from its own table after which to insert it (works if
01305      * @param integer $wsUid UID of offline version of online record
01306      * @param t3lib_TCEmain $tcemainObj TCEmain object
01307      * @return void
01308      * @see moveRecord()
01309      */
01310     protected function moveRecord_wsPlaceholders($table, $uid, $destPid, $wsUid, t3lib_TCEmain $tcemainObj) {
01311         global $TCA;
01312 
01313         if ($plh = t3lib_BEfunc::getMovePlaceholder($table, $uid, 'uid')) {
01314                 // If already a placeholder exists, move it:
01315             $tcemainObj->moveRecord_raw($table, $plh['uid'], $destPid);
01316         } else {
01317                 // First, we create a placeholder record in the Live workspace that
01318                 // represents the position to where the record is eventually moved to.
01319             $newVersion_placeholderFieldArray = array();
01320             if ($TCA[$table]['ctrl']['crdate']) {
01321                 $newVersion_placeholderFieldArray[$TCA[$table]['ctrl']['crdate']] = $GLOBALS['EXEC_TIME'];
01322             }
01323             if ($TCA[$table]['ctrl']['cruser_id']) {
01324                 $newVersion_placeholderFieldArray[$TCA[$table]['ctrl']['cruser_id']] = $tcemainObj->userid;
01325             }
01326             if ($TCA[$table]['ctrl']['tstamp']) {
01327                 $newVersion_placeholderFieldArray[$TCA[$table]['ctrl']['tstamp']] = $GLOBALS['EXEC_TIME'];
01328             }
01329 
01330             if ($table == 'pages') {
01331                     // Copy page access settings from original page to placeholder
01332                 $perms_clause = $tcemainObj->BE_USER->getPagePermsClause(1);
01333                 $access = t3lib_BEfunc::readPageAccess($uid, $perms_clause);
01334 
01335                 $newVersion_placeholderFieldArray['perms_userid']    = $access['perms_userid'];
01336                 $newVersion_placeholderFieldArray['perms_groupid']   = $access['perms_groupid'];
01337                 $newVersion_placeholderFieldArray['perms_user']      = $access['perms_user'];
01338                 $newVersion_placeholderFieldArray['perms_group']     = $access['perms_group'];
01339                 $newVersion_placeholderFieldArray['perms_everybody'] = $access['perms_everybody'];
01340             }
01341 
01342             $newVersion_placeholderFieldArray['t3ver_label'] = 'MOVE-TO PLACEHOLDER for #' . $uid;
01343             $newVersion_placeholderFieldArray['t3ver_move_id'] = $uid;
01344                 // Setting placeholder state value for temporary record
01345             $newVersion_placeholderFieldArray['t3ver_state'] = 3;
01346 
01347                 // Setting workspace - only so display of place holders can filter out those from other workspaces.
01348             $newVersion_placeholderFieldArray['t3ver_wsid'] = $tcemainObj->BE_USER->workspace;
01349             $newVersion_placeholderFieldArray[$TCA[$table]['ctrl']['label']] = '[MOVE-TO PLACEHOLDER for #' . $uid . ', WS#' . $tcemainObj->BE_USER->workspace . ']';
01350 
01351                 // moving localized records requires to keep localization-settings for the placeholder too
01352             if (array_key_exists('languageField', $GLOBALS['TCA'][$table]['ctrl']) && array_key_exists('transOrigPointerField', $GLOBALS['TCA'][$table]['ctrl'])) {
01353                 $l10nParentRec = t3lib_BEfunc::getRecord($table, $uid);
01354                 $newVersion_placeholderFieldArray[$GLOBALS['TCA'][$table]['ctrl']['languageField']] = $l10nParentRec[$GLOBALS['TCA'][$table]['ctrl']['languageField']];
01355                 $newVersion_placeholderFieldArray[$GLOBALS['TCA'][$table]['ctrl']['transOrigPointerField']] = $l10nParentRec[$GLOBALS['TCA'][$table]['ctrl']['transOrigPointerField']];
01356                 unset($l10nParentRec);
01357             }
01358 
01359                 // Initially, create at root level.
01360             $newVersion_placeholderFieldArray['pid'] = 0;
01361             $id = 'NEW_MOVE_PLH';
01362                 // Saving placeholder as 'original'
01363             $tcemainObj->insertDB($table, $id, $newVersion_placeholderFieldArray, FALSE);
01364 
01365                 // Move the new placeholder from temporary root-level to location:
01366             $tcemainObj->moveRecord_raw($table, $tcemainObj->substNEWwithIDs[$id], $destPid);
01367 
01368                 // Move the workspace-version of the original to be the version of the move-to-placeholder:
01369                 // Setting placeholder state value for version (so it can know it is currently a new version...)
01370             $updateFields = array(
01371                 't3ver_state' => 4
01372             );
01373             $GLOBALS['TYPO3_DB']->exec_UPDATEquery($table, 'uid=' . intval($wsUid), $updateFields);
01374         }
01375 
01376             // Check for the localizations of that element and move them as well
01377         $tcemainObj->moveL10nOverlayRecords($table, $uid, $destPid);
01378     }
01379 
01380     /**
01381      * Gets all possible child tables that are used on each parent table as field.
01382      *
01383      * @param string $parentTable Name of the parent table
01384      * @param array $possibleInlineChildren Collected possible inline children
01385      *              (will be filled automatically during recursive calls)
01386      * @return array
01387      */
01388     protected function getPossibleInlineChildTablesOfParentTable($parentTable, array $possibleInlineChildren = array()) {
01389         t3lib_div::loadTCA($parentTable);
01390 
01391         foreach ($GLOBALS['TCA'][$parentTable]['columns'] as $parentField => $parentFieldDefinition) {
01392             if (isset($parentFieldDefinition['config']['type'])) {
01393                 $parentFieldConfiguration = $parentFieldDefinition['config'];
01394                 if ($parentFieldConfiguration['type'] == 'inline' && isset($parentFieldConfiguration['foreign_table'])) {
01395                     if (!in_array($parentFieldConfiguration['foreign_table'], $possibleInlineChildren)) {
01396                         $possibleInlineChildren = $this->getPossibleInlineChildTablesOfParentTable(
01397                             $parentFieldConfiguration['foreign_table'],
01398                             array_merge($possibleInlineChildren, $parentFieldConfiguration['foreign_table'])
01399                         );
01400                     }
01401                 }
01402             }
01403         }
01404 
01405         return $possibleInlineChildren;
01406     }
01407 
01408     /**
01409      * Gets an instance of the command map helper.
01410      *
01411      * @param t3lib_TCEmain $tceMain TCEmain object
01412      * @param array $commandMap The command map as submitted to t3lib_TCEmain
01413      * @return tx_version_tcemain_CommandMap
01414      */
01415     public function getCommandMap(t3lib_TCEmain $tceMain, array $commandMap) {
01416         return t3lib_div::makeInstance('tx_version_tcemain_CommandMap', $this, $tceMain, $commandMap);
01417     }
01418 }
01419 
01420 if (defined('TYPO3_MODE') && isset($GLOBALS['TYPO3_CONF_VARS'][TYPO3_MODE]['XCLASS']['ext/version/class.tx_version_tcemain.php'])) {
01421     include_once($GLOBALS['TYPO3_CONF_VARS'][TYPO3_MODE]['XCLASS']['ext/version/class.tx_version_tcemain.php']);
01422 }
01423 ?>