Exemplo n.º 1
0
 /**
  * Processing the data-array
  * Call this function to process the data-array set by start()
  *
  * @return void|FALSE
  */
 public function process_datamap()
 {
     $this->controlActiveElements();
     // Keep versionized(!) relations here locally:
     $registerDBList = array();
     $this->registerElementsToBeDeleted();
     $this->datamap = $this->unsetElementsToBeDeleted($this->datamap);
     // Editing frozen:
     if ($this->BE_USER->workspace !== 0 && $this->BE_USER->workspaceRec['freeze']) {
         if ($this->enableLogging) {
             $this->newlog('All editing in this workspace has been frozen!', 1);
         }
         return false;
     }
     // First prepare user defined objects (if any) for hooks which extend this function:
     $hookObjectsArr = array();
     if (is_array($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_tcemain.php']['processDatamapClass'])) {
         foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_tcemain.php']['processDatamapClass'] as $classRef) {
             $hookObject = GeneralUtility::getUserObj($classRef);
             if (method_exists($hookObject, 'processDatamap_beforeStart')) {
                 $hookObject->processDatamap_beforeStart($this);
             }
             $hookObjectsArr[] = $hookObject;
         }
     }
     // Organize tables so that the pages-table is always processed first. This is required if you want to make sure that content pointing to a new page will be created.
     $orderOfTables = array();
     // Set pages first.
     if (isset($this->datamap['pages'])) {
         $orderOfTables[] = 'pages';
     }
     $orderOfTables = array_unique(array_merge($orderOfTables, array_keys($this->datamap)));
     // Process the tables...
     foreach ($orderOfTables as $table) {
         // Check if
         //	   - table is set in $GLOBALS['TCA'],
         //	   - table is NOT readOnly
         //	   - the table is set with content in the data-array (if not, there's nothing to process...)
         //	   - permissions for tableaccess OK
         $modifyAccessList = $this->checkModifyAccessList($table);
         if ($this->enableLogging && !$modifyAccessList) {
             $this->log($table, 0, 2, 0, 1, 'Attempt to modify table \'%s\' without permission', 1, array($table));
         }
         if (!isset($GLOBALS['TCA'][$table]) || $this->tableReadOnly($table) || !is_array($this->datamap[$table]) || !$modifyAccessList) {
             continue;
         }
         if ($this->reverseOrder) {
             $this->datamap[$table] = array_reverse($this->datamap[$table], 1);
         }
         // For each record from the table, do:
         // $id is the record uid, may be a string if new records...
         // $incomingFieldArray is the array of fields
         foreach ($this->datamap[$table] as $id => $incomingFieldArray) {
             if (!is_array($incomingFieldArray)) {
                 continue;
             }
             $theRealPid = null;
             // Handle native date/time fields
             $dateTimeFormats = $this->databaseConnection->getDateTimeFormats($table);
             foreach ($GLOBALS['TCA'][$table]['columns'] as $column => $config) {
                 if (isset($incomingFieldArray[$column])) {
                     if (isset($config['config']['dbType']) && ($config['config']['dbType'] === 'date' || $config['config']['dbType'] === 'datetime')) {
                         $emptyValue = $dateTimeFormats[$config['config']['dbType']]['empty'];
                         $format = $dateTimeFormats[$config['config']['dbType']]['format'];
                         $incomingFieldArray[$column] = $incomingFieldArray[$column] ? gmdate($format, $incomingFieldArray[$column]) : $emptyValue;
                     }
                 }
             }
             // Hook: processDatamap_preProcessFieldArray
             foreach ($hookObjectsArr as $hookObj) {
                 if (method_exists($hookObj, 'processDatamap_preProcessFieldArray')) {
                     $hookObj->processDatamap_preProcessFieldArray($incomingFieldArray, $table, $id, $this);
                 }
             }
             // ******************************
             // Checking access to the record
             // ******************************
             $createNewVersion = false;
             $recordAccess = false;
             $old_pid_value = '';
             $this->autoVersioningUpdate = false;
             // Is it a new record? (Then Id is a string)
             if (!MathUtility::canBeInterpretedAsInteger($id)) {
                 // Get a fieldArray with default values
                 $fieldArray = $this->newFieldArray($table);
                 // A pid must be set for new records.
                 if (isset($incomingFieldArray['pid'])) {
                     // $value = the pid
                     $pid_value = $incomingFieldArray['pid'];
                     // Checking and finding numerical pid, it may be a string-reference to another value
                     $OK = 1;
                     // If a NEW... id
                     if (strstr($pid_value, 'NEW')) {
                         if ($pid_value[0] === '-') {
                             $negFlag = -1;
                             $pid_value = substr($pid_value, 1);
                         } else {
                             $negFlag = 1;
                         }
                         // Trying to find the correct numerical value as it should be mapped by earlier processing of another new record.
                         if (isset($this->substNEWwithIDs[$pid_value])) {
                             if ($negFlag === 1) {
                                 $old_pid_value = $this->substNEWwithIDs[$pid_value];
                             }
                             $pid_value = (int) ($negFlag * $this->substNEWwithIDs[$pid_value]);
                         } else {
                             $OK = 0;
                         }
                     }
                     $pid_value = (int) $pid_value;
                     // The $pid_value is now the numerical pid at this point
                     if ($OK) {
                         $sortRow = $GLOBALS['TCA'][$table]['ctrl']['sortby'];
                         // Points to a page on which to insert the element, possibly in the top of the page
                         if ($pid_value >= 0) {
                             // If this table is sorted we better find the top sorting number
                             if ($sortRow) {
                                 $fieldArray[$sortRow] = $this->getSortNumber($table, 0, $pid_value);
                             }
                             // The numerical pid is inserted in the data array
                             $fieldArray['pid'] = $pid_value;
                         } else {
                             // points to another record before ifself
                             // If this table is sorted we better find the top sorting number
                             if ($sortRow) {
                                 // Because $pid_value is < 0, getSortNumber returns an array
                                 $tempArray = $this->getSortNumber($table, 0, $pid_value);
                                 $fieldArray['pid'] = $tempArray['pid'];
                                 $fieldArray[$sortRow] = $tempArray['sortNumber'];
                             } else {
                                 // Here we fetch the PID of the record that we point to...
                                 $tempdata = $this->recordInfo($table, abs($pid_value), 'pid');
                                 $fieldArray['pid'] = $tempdata['pid'];
                             }
                         }
                     }
                 }
                 $theRealPid = $fieldArray['pid'];
                 // Now, check if we may insert records on this pid.
                 if ($theRealPid >= 0) {
                     // Checks if records can be inserted on this $pid.
                     $recordAccess = $this->checkRecordInsertAccess($table, $theRealPid);
                     if ($recordAccess) {
                         $this->addDefaultPermittedLanguageIfNotSet($table, $incomingFieldArray);
                         $recordAccess = $this->BE_USER->recordEditAccessInternals($table, $incomingFieldArray, true);
                         if (!$recordAccess) {
                             if ($this->enableLogging) {
                                 $this->newlog('recordEditAccessInternals() check failed. [' . $this->BE_USER->errorMsg . ']', 1);
                             }
                         } elseif (!$this->bypassWorkspaceRestrictions) {
                             // Workspace related processing:
                             // If LIVE records cannot be created in the current PID due to workspace restrictions, prepare creation of placeholder-record
                             if ($res = $this->BE_USER->workspaceAllowLiveRecordsInPID($theRealPid, $table)) {
                                 if ($res < 0) {
                                     $recordAccess = false;
                                     if ($this->enableLogging) {
                                         $this->newlog('Stage for versioning root point and users access level did not allow for editing', 1);
                                     }
                                 }
                             } else {
                                 // So, if no live records were allowed, we have to create a new version of this record:
                                 if ($GLOBALS['TCA'][$table]['ctrl']['versioningWS']) {
                                     $createNewVersion = true;
                                 } else {
                                     $recordAccess = false;
                                     if ($this->enableLogging) {
                                         $this->newlog('Record could not be created in this workspace in this branch', 1);
                                     }
                                 }
                             }
                         }
                     }
                 } else {
                     debug('Internal ERROR: pid should not be less than zero!');
                 }
                 // Yes new record, change $record_status to 'insert'
                 $status = 'new';
             } else {
                 // Nope... $id is a number
                 $fieldArray = array();
                 $recordAccess = $this->checkRecordUpdateAccess($table, $id, $incomingFieldArray, $hookObjectsArr);
                 if (!$recordAccess) {
                     if ($this->enableLogging) {
                         $propArr = $this->getRecordProperties($table, $id);
                         $this->log($table, $id, 2, 0, 1, 'Attempt to modify record \'%s\' (%s) without permission. Or non-existing page.', 2, array($propArr['header'], $table . ':' . $id), $propArr['event_pid']);
                     }
                     continue;
                 }
                 // Next check of the record permissions (internals)
                 $recordAccess = $this->BE_USER->recordEditAccessInternals($table, $id);
                 if (!$recordAccess) {
                     if ($this->enableLogging) {
                         $this->newlog('recordEditAccessInternals() check failed. [' . $this->BE_USER->errorMsg . ']', 1);
                     }
                 } else {
                     // Here we fetch the PID of the record that we point to...
                     $tempdata = $this->recordInfo($table, $id, 'pid' . ($GLOBALS['TCA'][$table]['ctrl']['versioningWS'] ? ',t3ver_wsid,t3ver_stage' : ''));
                     $theRealPid = $tempdata['pid'];
                     // Use the new id of the versionized record we're trying to write to:
                     // (This record is a child record of a parent and has already been versionized.)
                     if ($this->autoVersionIdMap[$table][$id]) {
                         // For the reason that creating a new version of this record, automatically
                         // created related child records (e.g. "IRRE"), update the accordant field:
                         $this->getVersionizedIncomingFieldArray($table, $id, $incomingFieldArray, $registerDBList);
                         // Use the new id of the copied/versionized record:
                         $id = $this->autoVersionIdMap[$table][$id];
                         $recordAccess = true;
                         $this->autoVersioningUpdate = true;
                     } elseif (!$this->bypassWorkspaceRestrictions && ($errorCode = $this->BE_USER->workspaceCannotEditRecord($table, $tempdata))) {
                         $recordAccess = false;
                         // Versioning is required and it must be offline version!
                         // Check if there already is a workspace version
                         $WSversion = BackendUtility::getWorkspaceVersionOfRecord($this->BE_USER->workspace, $table, $id, 'uid,t3ver_oid');
                         if ($WSversion) {
                             $id = $WSversion['uid'];
                             $recordAccess = true;
                         } elseif ($this->BE_USER->workspaceAllowAutoCreation($table, $id, $theRealPid)) {
                             // new version of a record created in a workspace - so always refresh pagetree to indicate there is a change in the workspace
                             $this->pagetreeNeedsRefresh = true;
                             /** @var $tce DataHandler */
                             $tce = GeneralUtility::makeInstance(__CLASS__);
                             $tce->stripslashes_values = false;
                             // Setting up command for creating a new version of the record:
                             $cmd = array();
                             $cmd[$table][$id]['version'] = array('action' => 'new', 'treeLevels' => -1, 'label' => 'Auto-created for WS #' . $this->BE_USER->workspace);
                             $tce->start(array(), $cmd);
                             $tce->process_cmdmap();
                             $this->errorLog = array_merge($this->errorLog, $tce->errorLog);
                             // If copying was successful, share the new uids (also of related children):
                             if ($tce->copyMappingArray[$table][$id]) {
                                 foreach ($tce->copyMappingArray as $origTable => $origIdArray) {
                                     foreach ($origIdArray as $origId => $newId) {
                                         $this->uploadedFileArray[$origTable][$newId] = $this->uploadedFileArray[$origTable][$origId];
                                         $this->autoVersionIdMap[$origTable][$origId] = $newId;
                                     }
                                 }
                                 ArrayUtility::mergeRecursiveWithOverrule($this->RTEmagic_copyIndex, $tce->RTEmagic_copyIndex);
                                 // See where RTEmagic_copyIndex is used inside fillInFieldArray() for more information...
                                 // Update registerDBList, that holds the copied relations to child records:
                                 $registerDBList = array_merge($registerDBList, $tce->registerDBList);
                                 // For the reason that creating a new version of this record, automatically
                                 // created related child records (e.g. "IRRE"), update the accordant field:
                                 $this->getVersionizedIncomingFieldArray($table, $id, $incomingFieldArray, $registerDBList);
                                 // Use the new id of the copied/versionized record:
                                 $id = $this->autoVersionIdMap[$table][$id];
                                 $recordAccess = true;
                                 $this->autoVersioningUpdate = true;
                             } elseif ($this->enableLogging) {
                                 $this->newlog('Could not be edited in offline workspace in the branch where found (failure state: \'' . $errorCode . '\'). Auto-creation of version failed!', 1);
                             }
                         } elseif ($this->enableLogging) {
                             $this->newlog('Could not be edited in offline workspace in the branch where found (failure state: \'' . $errorCode . '\'). Auto-creation of version not allowed in workspace!', 1);
                         }
                     }
                 }
                 // The default is 'update'
                 $status = 'update';
             }
             // If access was granted above, proceed to create or update record:
             if (!$recordAccess) {
                 continue;
             }
             // Here the "pid" is set IF NOT the old pid was a string pointing to a place in the subst-id array.
             list($tscPID) = BackendUtility::getTSCpid($table, $id, $old_pid_value ? $old_pid_value : $fieldArray['pid']);
             if ($status === 'new' && $table === 'pages') {
                 $TSConfig = $this->getTCEMAIN_TSconfig($tscPID);
                 if (isset($TSConfig['permissions.']) && is_array($TSConfig['permissions.'])) {
                     $fieldArray = $this->setTSconfigPermissions($fieldArray, $TSConfig['permissions.']);
                 }
             }
             // Processing of all fields in incomingFieldArray and setting them in $fieldArray
             $fieldArray = $this->fillInFieldArray($table, $id, $fieldArray, $incomingFieldArray, $theRealPid, $status, $tscPID);
             $newVersion_placeholderFieldArray = array();
             if ($createNewVersion) {
                 // create a placeholder array with already processed field content
                 $newVersion_placeholderFieldArray = $fieldArray;
             }
             // NOTICE! All manipulation beyond this point bypasses both "excludeFields" AND possible "MM" relations / file uploads to field!
             // Forcing some values unto field array:
             // NOTICE: This overriding is potentially dangerous; permissions per field is not checked!!!
             $fieldArray = $this->overrideFieldArray($table, $fieldArray);
             if ($createNewVersion) {
                 $newVersion_placeholderFieldArray = $this->overrideFieldArray($table, $newVersion_placeholderFieldArray);
             }
             // Setting system fields
             if ($status == 'new') {
                 if ($GLOBALS['TCA'][$table]['ctrl']['crdate']) {
                     $fieldArray[$GLOBALS['TCA'][$table]['ctrl']['crdate']] = $GLOBALS['EXEC_TIME'];
                     if ($createNewVersion) {
                         $newVersion_placeholderFieldArray[$GLOBALS['TCA'][$table]['ctrl']['crdate']] = $GLOBALS['EXEC_TIME'];
                     }
                 }
                 if ($GLOBALS['TCA'][$table]['ctrl']['cruser_id']) {
                     $fieldArray[$GLOBALS['TCA'][$table]['ctrl']['cruser_id']] = $this->userid;
                     if ($createNewVersion) {
                         $newVersion_placeholderFieldArray[$GLOBALS['TCA'][$table]['ctrl']['cruser_id']] = $this->userid;
                     }
                 }
             } elseif ($this->checkSimilar) {
                 // Removing fields which are equal to the current value:
                 $fieldArray = $this->compareFieldArrayWithCurrentAndUnset($table, $id, $fieldArray);
             }
             if ($GLOBALS['TCA'][$table]['ctrl']['tstamp'] && !empty($fieldArray)) {
                 $fieldArray[$GLOBALS['TCA'][$table]['ctrl']['tstamp']] = $GLOBALS['EXEC_TIME'];
                 if ($createNewVersion) {
                     $newVersion_placeholderFieldArray[$GLOBALS['TCA'][$table]['ctrl']['tstamp']] = $GLOBALS['EXEC_TIME'];
                 }
             }
             // Set stage to "Editing" to make sure we restart the workflow
             if ($GLOBALS['TCA'][$table]['ctrl']['versioningWS']) {
                 $fieldArray['t3ver_stage'] = 0;
             }
             // Hook: processDatamap_postProcessFieldArray
             foreach ($hookObjectsArr as $hookObj) {
                 if (method_exists($hookObj, 'processDatamap_postProcessFieldArray')) {
                     $hookObj->processDatamap_postProcessFieldArray($status, $table, $id, $fieldArray, $this);
                 }
             }
             // Performing insert/update. If fieldArray has been unset by some userfunction (see hook above), don't do anything
             // Kasper: Unsetting the fieldArray is dangerous; MM relations might be saved already and files could have been uploaded that are now "lost"
             if (is_array($fieldArray)) {
                 if ($status == 'new') {
                     if ($table === 'pages') {
                         // for new pages always a refresh is needed
                         $this->pagetreeNeedsRefresh = true;
                     }
                     // This creates a new version of the record with online placeholder and offline version
                     if ($createNewVersion) {
                         // new record created in a workspace - so always refresh pagetree to indicate there is a change in the workspace
                         $this->pagetreeNeedsRefresh = true;
                         $newVersion_placeholderFieldArray['t3ver_label'] = 'INITIAL PLACEHOLDER';
                         // Setting placeholder state value for temporary record
                         $newVersion_placeholderFieldArray['t3ver_state'] = (string) new VersionState(VersionState::NEW_PLACEHOLDER);
                         // Setting workspace - only so display of place holders can filter out those from other workspaces.
                         $newVersion_placeholderFieldArray['t3ver_wsid'] = $this->BE_USER->workspace;
                         $newVersion_placeholderFieldArray[$GLOBALS['TCA'][$table]['ctrl']['label']] = $this->getPlaceholderTitleForTableLabel($table);
                         // Saving placeholder as 'original'
                         $this->insertDB($table, $id, $newVersion_placeholderFieldArray, false);
                         // For the actual new offline version, set versioning values to point to placeholder:
                         $fieldArray['pid'] = -1;
                         $fieldArray['t3ver_oid'] = $this->substNEWwithIDs[$id];
                         $fieldArray['t3ver_id'] = 1;
                         // Setting placeholder state value for version (so it can know it is currently a new version...)
                         $fieldArray['t3ver_state'] = (string) new VersionState(VersionState::NEW_PLACEHOLDER_VERSION);
                         $fieldArray['t3ver_label'] = 'First draft version';
                         $fieldArray['t3ver_wsid'] = $this->BE_USER->workspace;
                         // When inserted, $this->substNEWwithIDs[$id] will be changed to the uid of THIS version and so the interface will pick it up just nice!
                         $phShadowId = $this->insertDB($table, $id, $fieldArray, true, 0, true);
                         if ($phShadowId) {
                             // Processes fields of the placeholder record:
                             $this->triggerRemapAction($table, $id, array($this, 'placeholderShadowing'), array($table, $phShadowId));
                             // Hold auto-versionized ids of placeholders:
                             $this->autoVersionIdMap[$table][$this->substNEWwithIDs[$id]] = $phShadowId;
                         }
                     } else {
                         $this->insertDB($table, $id, $fieldArray, false, $incomingFieldArray['uid']);
                     }
                 } else {
                     if ($table === 'pages') {
                         // only a certain number of fields needs to be checked for updates
                         // if $this->checkSimilar is TRUE, fields with unchanged values are already removed here
                         $fieldsToCheck = array_intersect($this->pagetreeRefreshFieldsFromPages, array_keys($fieldArray));
                         if (!empty($fieldsToCheck)) {
                             $this->pagetreeNeedsRefresh = true;
                         }
                     }
                     $this->updateDB($table, $id, $fieldArray);
                     $this->placeholderShadowing($table, $id);
                 }
             }
             // Hook: processDatamap_afterDatabaseOperations
             // Note: When using the hook after INSERT operations, you will only get the temporary NEW... id passed to your hook as $id,
             // but you can easily translate it to the real uid of the inserted record using the $this->substNEWwithIDs array.
             $this->hook_processDatamap_afterDatabaseOperations($hookObjectsArr, $status, $table, $id, $fieldArray);
         }
     }
     // Process the stack of relations to remap/correct
     $this->processRemapStack();
     $this->dbAnalysisStoreExec();
     $this->removeRegisteredFiles();
     // Hook: processDatamap_afterAllOperations
     // Note: When this hook gets called, all operations on the submitted data have been finished.
     foreach ($hookObjectsArr as $hookObj) {
         if (method_exists($hookObj, 'processDatamap_afterAllOperations')) {
             $hookObj->processDatamap_afterAllOperations($this);
         }
     }
     if ($this->isOuterMostInstance()) {
         $this->processClearCacheQueue();
         $this->resetElementsToBeDeleted();
     }
 }