0016374_4-4.patch

Administrator Admin, 2010-11-13 19:13

Download (76.8 KB)

View differences:

t3lib/class.t3lib_loaddbgroup.php (Arbeitskopie)
101 101
	var $MM_insert_fields = array();	// array of fields and value pairs used for insert in MM table
102 102
	var $MM_table_where = ''; // extra MM table where
103 103

  
104
	/**
105
	 * @var boolean
106
	 */
107
	protected $updateReferenceIndex = TRUE;
104 108

  
105 109
	/**
106 110
	 * Initialization of the class.
......
190 194
	}
191 195

  
192 196
	/**
197
	 * Sets whether the reference index shall be updated.
198
	 *
199
	 * @param boolean $updateReferenceIndex Whether the reference index shall be updated
200
	 * @return void
201
	 */
202
	public function setUpdateReferenceIndex($updateReferenceIndex) {
203
		$this->updateReferenceIndex = (bool)$updateReferenceIndex;
204
	}
205

  
206
	/**
193 207
	 * Explodes the item list and stores the parts in the internal arrays itemArray and tableArray from MM records.
194 208
	 *
195 209
	 * @param	string		Item list
......
547 561
			$whereClause .= ' AND '.$foreign_table_field.'='.$GLOBALS['TYPO3_DB']->fullQuoteStr($this->currentTable, $foreign_table);
548 562
		}
549 563

  
564
			// Select children in the same workspace:
565
		if (t3lib_BEfunc::isTableWorkspaceEnabled($this->currentTable) && t3lib_BEfunc::isTableWorkspaceEnabled($foreign_table)) {
566
			$currentRecord = t3lib_BEfunc::getRecord($this->currentTable, $uid, 't3ver_wsid', '', $useDeleteClause);
567
			$whereClause .= t3lib_BEfunc::getWorkspaceWhereClause($foreign_table, $currentRecord['t3ver_wsid']);
568
		}
569

  
550 570
			// get the correct sorting field
551 571
		if ($conf['foreign_sortby']) {											// specific manual sortby for data handled by this field
552 572
			if ($conf['symmetric_sortby'] && $conf['symmetric_field']) {
......
804 824
	 *
805 825
	 * @param	string		Table name
806 826
	 * @param	integer		Record UID
807
	 * @return	void
827
	 * @return	array Information concerning modifications delivered by t3lib_refindex::updateRefIndexTable()
808 828
	 */
809 829
	function updateRefIndex($table,$id)	{
810
		$refIndexObj = t3lib_div::makeInstance('t3lib_refindex');
811
		$result = $refIndexObj->updateRefIndexTable($table,$id);
830
		if ($this->updateReferenceIndex === TRUE) {
831
			/** @var $refIndexObj t3lib_refindex */
832
			$refIndexObj = t3lib_div::makeInstance('t3lib_refindex');
833
			return $refIndexObj->updateRefIndexTable($table,$id);
834
		}
812 835
	}
813 836

  
814 837
	/**
t3lib/class.t3lib_tcemain.php (Arbeitskopie)
339 339
	var $copyMappingArray = Array();			// Used by the copy action to track the ids of new pages so subpages are correctly inserted! THIS is internally cleared for each executed copy operation! DO NOT USE THIS FROM OUTSIDE! Read from copyMappingArray_merged instead which is accumulating this information.
340 340
	var $remapStack = array();					// array used for remapping uids and values at the end of process_datamap
341 341
	var $remapStackRecords = array();			// array used for remapping uids and values at the end of process_datamap (e.g. $remapStackRecords[<table>][<uid>] = <index in $remapStack>)
342
	protected $remapStackChildIds = array();	// array used for checking whether new children need to be remapped
343
	protected $remapStackActions = array();		// array used for executing addition actions after remapping happened (sett processRemapStack())
344
	protected $remapStackRefIndex = array();	// array used for executing post-processing on the reference index
342 345
	var $updateRefIndexStack = array();			// array used for additional calls to $this->updateRefIndex
343 346
	var $callFromImpExp = false;				// tells, that this TCEmain was called from tx_impext - this variable is set by tx_impexp
344 347
	var $newIndexMap = array();					// Array for new flexform index mapping
......
913 916
											}
914 917
											$phShadowId = $this->insertDB($table,$id,$fieldArray,TRUE,0,TRUE);	// When inserted, $this->substNEWwithIDs[$id] will be changed to the uid of THIS version and so the interface will pick it up just nice!
915 918
											if ($phShadowId)	{
916
												$this->placeholderShadowing($table,$phShadowId);
919
													// Processes fields of the placeholder record:
920
												$this->triggerRemapAction(
921
													$table,
922
													$id,
923
													array($this, 'placeholderShadowing'),
924
													array($table, $phShadowId)
925
												);
917 926
													// Hold auto-versionized ids of placeholders:
918 927
												$this->autoVersionIdMap[$table][$this->substNEWwithIDs[$id]] = $phShadowId;
919 928
											}
......
1535 1544
				// check, if there is a NEW... id in the value, that should be substituded later
1536 1545
			if (strpos($value, 'NEW') !== false) {
1537 1546
				$this->remapStackRecords[$table][$id] = array('remapStackIndex' => count($this->remapStack));
1547
				$this->addNewValuesToRemapStackChildIds($valueArray);
1538 1548
				$this->remapStack[] = array(
1539 1549
					'func' => 'checkValue_group_select_processDBdata',
1540 1550
					'args' => array($valueArray, $tcaFieldConf, $id, $status, 'select', $table, $field),
......
1945 1955
			// We need to decide whether we use the stack or can save the relation directly.
1946 1956
		if(strpos($value, 'NEW') !== false || !t3lib_div::testInt($id)) {
1947 1957
			$this->remapStackRecords[$table][$id] = array('remapStackIndex' => count($this->remapStack));
1958
			$this->addNewValuesToRemapStackChildIds($valueArray);
1948 1959
			$this->remapStack[] = array(
1949 1960
				'func' => 'checkValue_inline_processDBdata',
1950 1961
				'args' => array($valueArray, $tcaFieldConf, $id, $status, $table, $field),
......
2565 2576

  
2566 2577
		$this->accumulateForNotifEmail = array();	// Reset notification array
2567 2578

  
2579
			// Resolve dependencies of version/workspaces actions:
2580
		$this->cmdmap = $this->getCommandMap($this->cmdmap)->process()->get();
2581

  
2568 2582
			// Traverse command map:
2569 2583
		foreach (array_keys($this->cmdmap) as $table) {
2570 2584

  
......
2625 2639
										}
2626 2640
									break;
2627 2641
									case 'swap':
2628
										$swapMode = $this->BE_USER->getTSConfigVal('options.workspaces.swapMode');
2629
										$elementList = array();
2630
										if ($swapMode == 'any' || ($swapMode == 'page' && $table == 'pages')) {
2631
											// check if we are allowed to do synchronios publish. We must have a single element in the cmdmap to be allowed
2632
											if (count($this->cmdmap) == 1 && count($this->cmdmap[$table]) == 1) {
2633
												$elementList = $this->findPageElementsForVersionSwap($table, $id, $value['swapWith']);
2634
											}
2635
										}
2636
										if (count($elementList) == 0) {
2637
											$elementList[$table][] = array($id, $value['swapWith']);
2638
										}
2639
										foreach ($elementList as $tbl => $idList) {
2640
											foreach ($idList as $idKey => $idSet) {
2641
												$this->version_swap($tbl,$idSet[0],$idSet[1],$value['swapIntoWS']);
2642
											}
2643
										}
2642
										$this->version_swap($table, $id, $value['swapWith'], $value['swapIntoWS']);
2644 2643
									break;
2645 2644
									case 'clearWSID':
2646 2645
										$this->version_clearWSID($table,$id);
......
2649 2648
										$this->version_clearWSID($table,$id,TRUE);
2650 2649
									break;
2651 2650
									case 'setStage':
2652
										$elementList = array();
2653
										$idList = $elementList[$table] = t3lib_div::trimExplode(',',$id,1);
2654
										$setStageMode = $this->BE_USER->getTSConfigVal('options.workspaces.changeStageMode');
2655
										if ($setStageMode == 'any' || $setStageMode == 'page') {
2656
											if (count($idList) == 1) {
2657
												$rec = t3lib_BEfunc::getRecord($table, $idList[0], 't3ver_wsid');
2658
												$workspaceId = $rec['t3ver_wsid'];
2659
											}
2660
											else {
2661
												$workspaceId = $this->BE_USER->workspace;
2662
											}
2663
											if ($table !== 'pages') {
2664
												if ($setStageMode == 'any') {
2665
													// (1) Find page to change stage and (2) find other elements from the same ws to change stage
2666
													$pageIdList = array();
2667
													$this->findPageIdsForVersionStateChange($table, $idList, $workspaceId, $pageIdList, $elementList);
2668
													$this->findPageElementsForVersionStageChange($pageIdList, $workspaceId, $elementList);
2669
												}
2670
											}
2671
											else {
2672
												// Find all elements from the same ws to change stage
2673
												$this->findRealPageIds($idList);
2674
												$this->findPageElementsForVersionStageChange($idList, $workspaceId, $elementList);
2675
											}
2651
										$elementIds = t3lib_div::trimExplode(',', $id, TRUE);
2652
										foreach ($elementIds as $elementId) {
2653
											$this->version_setStage($table, $elementId, $value['stageId'],
2654
											(isset($value['comment']) && $value['comment'] ? $value['comment'] : $this->generalComment),
2655
											TRUE);
2676 2656
										}
2677

  
2678
										foreach ($elementList as $tbl => $elementIdList) {
2679
											foreach($elementIdList as $elementId)	{
2680
												$this->version_setStage($tbl,$elementId,$value['stageId'],$value['comment']?$value['comment']:$this->generalComment, TRUE);
2681
											}
2682
										}
2683 2657
									break;
2684 2658
								}
2685 2659
							break;
......
2705 2679
		}
2706 2680

  
2707 2681
			// Finally, before exit, check if there are ID references to remap. This might be the case if versioning or copying has taken place!
2682
		$this->processRemapStack();
2708 2683
		$this->remapListedDBRecords();
2709 2684

  
2710 2685

  
......
2852 2827
						if ($theNewSQLID) {
2853 2828
							$this->copyRecord_fixRTEmagicImages($table, t3lib_BEfunc::wsMapId($table, $theNewSQLID));
2854 2829
							$this->copyMappingArray[$table][$origUid] = $theNewSQLID;
2830
								// Keep automatically versionized record information:
2831
							if (isset($copyTCE->autoVersionIdMap[$table][$theNewSQLID])) {
2832
								$this->autoVersionIdMap[$table][$theNewSQLID] = $copyTCE->autoVersionIdMap[$table][$theNewSQLID];
2833
							}
2855 2834
						}
2856 2835

  
2857 2836
							// Copy back the cached TSconfig
......
3188 3167
					} else {
3189 3168
						if (!t3lib_div::testInt($realDestPid)) {
3190 3169
							$newId = $this->copyRecord($v['table'], $v['id'], -$v['id']);
3191
						} elseif ($realDestPid == -1) {
3192
							$newId = $this->versionizeRecord($v['table'], $v['id'], 'Auto-created for WS #'.$this->BE_USER->workspace);
3170
						} elseif ($realDestPid == -1 && t3lib_BEfunc::isTableWorkspaceEnabled($v['table'])) {
3171
							$workspaceVersion = t3lib_BEfunc::getWorkspaceVersionOfRecord(
3172
								$this->BE_USER->workspace, $v['table'], $v['id'], 'uid'
3173
							);
3174
								// If workspace version does not exist, create a new one:
3175
							if ($workspaceVersion === FALSE) {
3176
								$newId = $this->versionizeRecord($v['table'], $v['id'], 'Auto-created for WS #' . $this->BE_USER->workspace);
3177
								// If workspace version already exists, use it:
3178
							} else {
3179
								$newId = $workspaceVersion['uid'];
3180
							}
3193 3181
						} else {
3194 3182
							$newId = $this->copyRecord_raw($v['table'], $v['id'], $realDestPid);
3195 3183
						}
......
3933 3921

  
3934 3922
												// Execute the copy:
3935 3923
											$newId = $this->copyRecord($table, $uid, -$uid, 1, $overrideValues, implode(',', $excludeFields), $language);
3924
											$autoVersionNewId = $this->getAutoVersionId($table, $newId);
3925
											if (is_null($autoVersionNewId) === FALSE) {
3926
												$this->triggerRemapAction(
3927
													$table,
3928
													$newId,
3929
													array($this, 'placeholderShadowing'),
3930
													array($table, $autoVersionNewId),
3931
													TRUE
3932
												);
3933
											}
3936 3934
										} else {
3937 3935

  
3938 3936
												// Create new record:
......
4027 4025
						if (t3lib_div::testInt($type) && isset($elementsOriginal[$type])) {
4028 4026
							$item = $elementsOriginal[$type];
4029 4027
							$item['id'] = $this->localize($item['table'], $item['id'], $language);
4028
							$item['id'] = $this->overlayAutoVersionId($item['table'], $item['id']);
4030 4029
							$dbAnalysisCurrent->itemArray[] = $item;
4031 4030
						} elseif (t3lib_div::inList('localize,synchronize', $type)) {
4032 4031
							foreach ($elementsOriginal as $originalId => $item) {
4033 4032
								$item['id'] = $this->localize($item['table'], $item['id'], $language);
4033
								$item['id'] = $this->overlayAutoVersionId($item['table'], $item['id']);
4034 4034
								$dbAnalysisCurrent->itemArray[] = $item;
4035 4035
							}
4036 4036
						}
......
4695 4695
					$verTablesArray[] = $tN;
4696 4696
				}
4697 4697
			}
4698
				// Remove the possible inline child tables from the tables to be versioniozed automatically:
4699
			$verTablesArray = array_diff($verTablesArray, $this->getPossibleInlineChildTablesOfParentTable('pages'));
4698 4700

  
4699 4701
				// Begin to copy pages if we're allowed to:
4700 4702
			if ($this->admin || in_array('pages',$allowedTablesArray))	{
......
4874 4876
													$this->deleteEl($table, $movePlhID, TRUE, TRUE); 	// For delete + completely delete!
4875 4877
												} else {	// Otherwise update the movePlaceholder:
4876 4878
													$GLOBALS['TYPO3_DB']->exec_UPDATEquery($table,'uid='.intval($movePlhID),$movePlh);
4877
													$this->updateRefIndex($table,$movePlhID);
4879
													$this->addRemapStackRefIndex($table, $movePlhID);
4878 4880
												}
4879 4881
											}
4880 4882

  
......
4886 4888
											$this->newlog2(($swapIntoWS ? 'Swapping' : 'Publishing').' successful for table "'.$table.'" uid '.$id.'=>'.$swapWith, $table, $id, $swapVersion['pid']);
4887 4889

  
4888 4890
												// Update reference index of the live record:
4889
											$this->updateRefIndex($table,$id);
4891
											$this->addRemapStackRefIndex($table, $id);
4890 4892
												// Set log entry for live record:
4891 4893
											$propArr = $this->getRecordPropertiesFromRow($table, $swapVersion);
4892 4894
											if ( $propArr['_ORIG_pid'] == -1) {
......
4898 4900
											$this->setHistory($table, $id, $theLogId);
4899 4901

  
4900 4902
												// Update reference index of the offline record:
4901
											$this->updateRefIndex($table,$swapWith);
4903
											$this->addRemapStackRefIndex($table, $swapWith);
4902 4904
												// Set log entry for offline record:
4903 4905
											$propArr = $this->getRecordPropertiesFromRow($table, $curVersion);
4904 4906
											if ( $propArr['_ORIG_pid'] == -1) {
......
5046 5048

  
5047 5049
			// Process pointer fields on normalized database:
5048 5050
		if ($inlineType == 'field') {
5049
				// Read relations that point to the current record (e.g. live record):
5051
			// Read relations that point to the current record (e.g. live record):
5052
			/** @var $dbAnalysisCur t3lib_loadDBGroup */
5050 5053
			$dbAnalysisCur = t3lib_div::makeInstance('t3lib_loadDBGroup');
5054
			$dbAnalysisCur->setUpdateReferenceIndex(FALSE);
5051 5055
			$dbAnalysisCur->start('', $conf['foreign_table'], '', $curVersion['uid'], $table, $conf);
5052
				// Read relations that point to the record to be swapped with e.g. draft record):
5056
			// Read relations that point to the record to be swapped with e.g. draft record):
5057
			/** @var $dbAnalysisSwap t3lib_loadDBGroup */
5053 5058
			$dbAnalysisSwap = t3lib_div::makeInstance('t3lib_loadDBGroup');
5059
			$dbAnalysisSwap->setUpdateReferenceIndex(FALSE);
5054 5060
			$dbAnalysisSwap->start('', $conf['foreign_table'], '', $swapVersion['uid'], $table, $conf);
5055 5061
				// Update relations for both (workspace/versioning) sites:
5056 5062
			$dbAnalysisCur->writeForeignField($conf,$curVersion['uid'],$swapVersion['uid']);
5057 5063
			$dbAnalysisSwap->writeForeignField($conf,$swapVersion['uid'],$curVersion['uid']);
5058 5064

  
5065
			$items = array_merge($dbAnalysisCur->itemArray, $dbAnalysisSwap->itemArray);
5066
			foreach ($items as $item) {
5067
				$this->addRemapStackRefIndex($item['table'], $item['id']);
5068
			}
5069

  
5059 5070
			// Swap field values (CSV):
5060 5071
			// BUT: These values will be swapped back in the next steps, when the *CHILD RECORD ITSELF* is swapped!
5061 5072
		} elseif ($inlineType == 'list') {
......
5212 5223

  
5213 5224

  
5214 5225

  
5215

  
5216 5226
	/*********************************************
5217 5227
	 *
5218 5228
	 * Cmd: Helper functions
......
5385 5395
				$this->remapListedDBRecords_procDBRefs($conf, $value, $theUidToUpdate, $table);
5386 5396

  
5387 5397
			} elseif ($inlineType !== false) {
5398
				/** @var $dbAnalysis t3lib_loadDBGroup */
5388 5399
				$dbAnalysis = t3lib_div::makeInstance('t3lib_loadDBGroup');
5389 5400
				$dbAnalysis->start($value, $conf['foreign_table'], '', 0, $table, $conf);
5390 5401

  
5402
					// Update child records if using pointer fields ('foreign_field'):
5403
				if ($inlineType == 'field') {
5404
					$dbAnalysis->writeForeignField($conf, $uid, $theUidToUpdate);
5405
				}
5406

  
5391 5407
					// If the current field is set on a page record, update the pid of related child records:
5392 5408
				if ($table == 'pages') {
5393 5409
					$thePidToUpdate = $theUidToUpdate;
......
5397 5413
					$thePidToUpdate = $this->copyMappingArray_merged['pages'][$thePidToUpdate];
5398 5414
				}
5399 5415

  
5400
					// Update child records if using pointer fields ('foreign_field'):
5401
				if ($inlineType == 'field') {
5402
					$dbAnalysis->writeForeignField($conf, $uid, $theUidToUpdate);
5403
				}
5404

  
5405
					// Update child records if change to pid is required:
5416
					// Update child records if change to pid is required (only if the current record is not on a workspace):
5406 5417
				if ($thePidToUpdate) {
5407 5418
					$updateValues = array('pid' => $thePidToUpdate);
5408 5419
					foreach ($dbAnalysis->itemArray as $v) {
5409
						if ($v['id'] && $v['table']) {
5420
						if ($v['id'] && $v['table'] && is_null(t3lib_BEfunc::getLiveVersionIdOfRecord($v['table'], $v['id']))) {
5410 5421
							$GLOBALS['TYPO3_DB']->exec_UPDATEquery($v['table'], 'uid='.intval($v['id']), $updateValues);
5411 5422
						}
5412 5423
					}
......
5422 5433
	 * @return	void
5423 5434
	 */
5424 5435
	function processRemapStack() {
5436
			// Processes the remap stack:
5425 5437
		if(is_array($this->remapStack)) {
5426 5438
			foreach($this->remapStack as $remapAction) {
5427 5439
					// If no position index for the arguments was set, skip this remap action:
......
5491 5503
				}
5492 5504
			}
5493 5505
		}
5506
			// Processes the remap stack actions:
5507
		if ($this->remapStackActions) {
5508
			foreach ($this->remapStackActions as $action) {
5509
				if (isset($action['callback']) && isset($action['arguments'])) {
5510
					call_user_func_array(
5511
						$action['callback'],
5512
						$action['arguments']
5513
					);
5514
				}
5515
			}
5516
		}
5517
			// Processes the reference index updates of the remap stack:
5518
		foreach ($this->remapStackRefIndex as $table => $idArray) {
5519
			foreach ($idArray as $id) {
5520
				$this->updateRefIndex($table, $id);
5521
				unset($this->remapStackRefIndex[$table][$id]);
5522
			}
5523
		}
5494 5524
			// Reset:
5495 5525
		$this->remapStack = array();
5496 5526
		$this->remapStackRecords = array();
5527
		$this->remapStackActions = array();
5528
		$this->remapStackRefIndex = array();
5497 5529
	}
5498 5530

  
5499 5531
	/**
5532
	 * Triggers a remap action for a specific record.
5533
	 *
5534
	 * Some records are post-processed by the processRemapStack() method (e.g. IRRE children).
5535
	 * This method determines wether an action/modification is executed directly to a record
5536
	 * or is postponed to happen after remapping data.
5537
	 *
5538
	 * @param string $table Name of the table
5539
	 * @param string $id Id of the record (can also be a "NEW..." string)
5540
 	 * @param array $callback The method to be called
5541
	 * @param array $arguments The arguments to be submitted to the callback method
5542
	 * @param boolean $forceRemapStackActions Whether to force to use the stack
5543
	 * @return void
5544
	 *
5545
	 * @see processRemapStack
5546
	 */
5547
	protected function triggerRemapAction($table, $id, array $callback, array $arguments, $forceRemapStackActions = FALSE) {
5548
			// Check whether the affected record is marked to be remapped:
5549
		if (!$forceRemapStackActions && !isset($this->remapStackRecords[$table][$id]) && !isset($this->remapStackChildIds[$id])) {
5550
			call_user_func_array($callback, $arguments);
5551
		} else {
5552
			$this->remapStackActions[] = array(
5553
				'affects' => array(
5554
					'table' => $table,
5555
					'id' => $id,
5556
				),
5557
				'callback' => $callback,
5558
				'arguments' => $arguments,
5559
			);
5560
		}
5561
	}
5562

  
5563
	/**
5564
	 * Adds a table-id-pair to the reference index remapping stack.
5565
	 *
5566
	 * @param string $table
5567
	 * @param integer $id
5568
	 * @return void
5569
	 */
5570
	protected function addRemapStackRefIndex($table, $id) {
5571
		$this->remapStackRefIndex[$table][$id] = $id;
5572
	}
5573

  
5574
	/**
5500 5575
	 * If a parent record was versionized on a workspace in $this->process_datamap,
5501 5576
	 * it might be possible, that child records (e.g. on using IRRE) were affected.
5502 5577
	 * This function finds these relations and updates their uids in the $incomingFieldArray.
......
7819 7893
		}
7820 7894
		return $result;
7821 7895
	}
7896

  
7897
	/**
7898
	 * Gets all possible child tables that are used on each parent table as field.
7899
	 *
7900
	 * @param string $parentTable
7901
	 * @param array $possibleInlineChildren
7902
	 * @return array
7903
	 */
7904
	protected function getPossibleInlineChildTablesOfParentTable($parentTable, array $possibleInlineChildren = array()) {
7905
		t3lib_div::loadTCA($parentTable);
7906

  
7907
		foreach ($GLOBALS['TCA'][$parentTable]['columns'] as $parentField => $parentFieldDefinition) {
7908
			if (isset($parentFieldDefinition['config']['type'])) {
7909
				$parentFieldConfiguration = $parentFieldDefinition['config'];
7910
				if ($parentFieldConfiguration['type'] == 'inline' && isset($parentFieldConfiguration['foreign_table'])) {
7911
					if (!in_array($parentFieldConfiguration['foreign_table'], $possibleInlineChildren)) {
7912
						$possibleInlineChildren = $this->getPossibleInlineChildTablesOfParentTable(
7913
							$parentFieldConfiguration['foreign_table'],
7914
							array_merge($possibleInlineChildren, $parentFieldConfiguration['foreign_table'])
7915
						);
7916
					}
7917
				}
7918
			}
7919
		}
7920

  
7921
		return $possibleInlineChildren;
7922
	}
7923

  
7924
	/**
7925
	 * Gets the automatically versionized id of a record.
7926
	 *
7927
	 * @param string $table Name of the table
7928
	 * @param integer $id Uid of the record
7929
	 * @return integer
7930
	 */
7931
	protected function getAutoVersionId($table, $id) {
7932
		$result = NULL;
7933

  
7934
		if (isset($this->autoVersionIdMap[$table][$id])) {
7935
			$result = $this->autoVersionIdMap[$table][$id];
7936
		}
7937

  
7938
		return $result;
7939
	}
7940

  
7941
	/**
7942
	 * Overlays the automatically versionized id of a record.
7943
	 *
7944
	 * @param string $table Name of the table
7945
	 * @param integer $id Uid of the record
7946
	 * @return integer
7947
	 */
7948
	protected function overlayAutoVersionId($table, $id) {
7949
		$autoVersionId = $this->getAutoVersionId($table, $id);
7950

  
7951
		if (is_null($autoVersionId) === FALSE) {
7952
			$id = $autoVersionId;
7953
		}
7954

  
7955
		return $id;
7956
	}
7957

  
7958
	/**
7959
	 * Adds new values to the remapStackChildIds array.
7960
	 *
7961
	 * @param array $idValues uid values
7962
	 * @return void
7963
	 */
7964
	protected function addNewValuesToRemapStackChildIds(array $idValues) {
7965
		foreach ($idValues as $idValue) {
7966
			if (strpos($idValue, 'NEW') === 0) {
7967
				$this->remapStackChildIds[$idValue] = TRUE;
7968
			}
7969
		}
7970
	}
7971

  
7972
	/**
7973
	 * @return t3lib_TCEmain_CommandMap
7974
	 */
7975
	protected function getCommandMap(array $commandMap) {
7976
		return t3lib_div::makeInstance('t3lib_TCEmain_CommandMap', $this, $commandMap);
7977
	}
7822 7978
}
7823 7979

  
7824 7980

  
t3lib/class.t3lib_tcemain_commandmap.php (Revision 40148)
1
<?php
2
/***************************************************************
3
 * Copyright notice
4
 *
5
 * (c) 2010 Oliver Hader <oliver@typo3.org>
6
 * All rights reserved
7
 *
8
 * This script is part of the TYPO3 project. The TYPO3 project is
9
 * free software; you can redistribute it and/or modify
10
 * it under the terms of the GNU General Public License as published by
11
 * the Free Software Foundation; either version 2 of the License, or
12
 * (at your option) any later version.
13
 *
14
 * The GNU General Public License can be found at
15
 * http://www.gnu.org/copyleft/gpl.html.
16
 * A copy is found in the textfile GPL.txt and important notices to the license
17
 * from the author is found in LICENSE.txt distributed with these scripts.
18
 *
19
 *
20
 * This script is distributed in the hope that it will be useful,
21
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
22
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
23
 * GNU General Public License for more details.
24
 *
25
 * This copyright notice MUST APPEAR in all copies of the script!
26
 ***************************************************************/
27

  
28
/**
29
 * Handles the t3lib_TCEmain command map and is only used in combination with t3lib_TCEmain.
30
 */
31
class t3lib_TCEmain_CommandMap {
32
	const SCOPE_WorkspacesSwap = 'SCOPE_WorkspacesSwap';
33
	const SCOPE_WorkspacesSetStage = 'SCOPE_WorkspacesSetStage';
34

  
35
	const KEY_ScopeErrorMessage = 'KEY_ScopeErrorMessage';
36
	const KEY_ScopeErrorCode = 'KEY_ScopeErrorCode';
37
	const KEY_GetElementPropertiesCallback = 'KEY_GetElementPropertiesCallback';
38
	const KEY_GetCommonPropertiesCallback = 'KEY_GetCommonPropertiesCallback';
39
	const KEY_ElementConstructCallback = 'KEY_EventConstructCallback';
40
	const KEY_ElementCreateChildReferenceCallback = 'KEY_ElementCreateChildReferenceCallback';
41
	const KEY_ElementCreateParentReferenceCallback = 'KEY_ElementCreateParentReferenceCallback';
42
	const KEY_PurgeWithErrorMessageGetIdCallback = 'KEY_PurgeWithErrorMessageGetIdCallback';
43
	const KEY_UpdateGetIdCallback = 'KEY_UpdateGetIdCallback';
44
	const KEY_TransformDependentElementsToUseLiveId = 'KEY_TransformDependentElementsToUseLiveId';
45

  
46
	/**
47
	 * @var t3lib_TCEmain
48
	 */
49
	protected $parent;
50

  
51
	/**
52
	 * @var array
53
	 */
54
	protected $commandMap = array();
55

  
56
	/**
57
	 * @var string
58
	 */
59
	protected $workspacesSwapMode;
60

  
61
	/**
62
	 * @var string
63
	 */
64
	protected $workspacesChangeStageMode;
65

  
66
	/**
67
	 * @var boolean
68
	 */
69
	protected $workspacesConsiderReferences;
70

  
71
	/**
72
	 * @var array
73
	 */
74
	protected $scopes;
75

  
76
	/**
77
	 * Creates this object.
78
	 *
79
	 * @param t3lib_TCEmain $parent
80
	 * @param array $commandMap
81
	 */
82
	public function __construct(t3lib_TCEmain $parent, array $commandMap) {
83
		$this->setParent($parent);
84
		$this->set($commandMap);
85

  
86
		$this->setWorkspacesSwapMode($parent->BE_USER->getTSConfigVal('options.workspaces.swapMode'));
87
		$this->setWorkspacesChangeStageMode($parent->BE_USER->getTSConfigVal('options.workspaces.changeStageMode'));
88
		$this->setWorkspacesConsiderReferences($parent->BE_USER->getTSConfigVal('options.workspaces.considerReferences'));
89

  
90
		$this->constructScopes();
91
	}
92

  
93
	/**
94
	 * Gets the command map.
95
	 *
96
	 * @return array
97
	 */
98
	public function get() {
99
		return $this->commandMap;
100
	}
101

  
102
	/**
103
	 * Sets the command map.
104
	 *
105
	 * @param array $commandMap
106
	 * @return t3lib_TCEmain_CommandMap
107
	 */
108
	public function set(array $commandMap) {
109
		$this->commandMap = $commandMap;
110
		return $this;
111
	}
112

  
113
	/**
114
	 * Gets the parent object.
115
	 *
116
	 * @return t3lib_TCEmain
117
	 */
118
	public function getParent() {
119
		return $this->parent;
120
	}
121

  
122
	/**
123
	 * Sets the parent object.
124
	 *
125
	 * @param t3lib_TCEmain $parent
126
	 * @return t3lib_TCEmain_CommandMap
127
	 */
128
	public function setParent(t3lib_TCEmain $parent) {
129
		$this->parent = $parent;
130
		return $this;
131
	}
132

  
133
	/**
134
	 * Sets the workspaces swap mode
135
	 * (see options.workspaces.swapMode).
136
	 *
137
	 * @param string $workspacesSwapMode
138
	 * @return t3lib_TCEmain_CommandMap
139
	 */
140
	public function setWorkspacesSwapMode($workspacesSwapMode) {
141
		$this->workspacesSwapMode = (string)$workspacesSwapMode;
142
		return $this;
143
	}
144

  
145
	/**
146
	 * Sets the workspaces change stage mode
147
	 * see options.workspaces.changeStageMode)
148
	 *
149
	 * @param string $workspacesChangeStageMode
150
	 * @return t3lib_TCEmain_CommandMap
151
	 */
152
	public function setWorkspacesChangeStageMode($workspacesChangeStageMode) {
153
		$this->workspacesChangeStageMode = (string)$workspacesChangeStageMode;
154
		return $this;
155
	}
156

  
157
	/**
158
	 * Sets the workspace behaviour to automatically consider references
159
	 * (see options.workspaces.considerReferences)
160
	 *
161
	 * @param boolean $workspacesConsiderReferences
162
	 * @return t3lib_TCEmain_CommandMap
163
	 */
164
	public function setWorkspacesConsiderReferences($workspacesConsiderReferences) {
165
		$this->workspacesConsiderReferences = (bool)$workspacesConsiderReferences;
166
		return $this;
167
	}
168

  
169
	/**
170
	 * Processes the command map.
171
	 *
172
	 * @return t3lib_TCEmain_CommandMap
173
	 */
174
	public function process() {
175
		$this->resolveWorkspacesSwapDependencies();
176
		$this->resolveWorkspacesSetStageDependencies();
177
		return $this;
178
	}
179

  
180
	/**
181
	 * Resolves workspaces related dependencies for swapping/publishing of the command map.
182
	 * Workspaces records that have children or (relative) parents which are versionized
183
	 * but not published with this request, are removed from the command map. Otherwise
184
	 * this would produce hanging record sets and lost references.
185
	 *
186
	 * @return void
187
	 */
188
	protected function resolveWorkspacesSwapDependencies() {
189
		$scope = self::SCOPE_WorkspacesSwap;
190
		$dependency = $this->getDependencyUtility($scope);
191

  
192
		foreach ($this->commandMap as $table => $liveIdCollection) {
193
			foreach ($liveIdCollection as $liveId => $commandCollection) {
194
				foreach ($commandCollection as $command => $properties) {
195
					if ($command === 'version' && isset($properties['action']) && $properties['action'] === 'swap') {
196
						if (isset($properties['swapWith']) && t3lib_div::testInt($properties['swapWith'])) {
197
							$this->addWorkspacesSwapElements($dependency, $table, $liveId, $properties);
198
						}
199
					}
200
				}
201
			}
202
		}
203

  
204
		$this->applyWorkspacesDependencies($dependency, $scope);
205
	}
206

  
207
	/**
208
	 * Adds workspaces elements for swapping/publishing and takes care of the swapMode.
209
	 *
210
	 * @param t3lib_utility_Dependency $dependency
211
	 * @param string $table
212
	 * @param iteger $liveId
213
	 * @param array $properties
214
	 * @return void
215
	 */
216
	protected function addWorkspacesSwapElements(t3lib_utility_Dependency $dependency, $table, $liveId, array $properties) {
217
		$elementList = array();
218

  
219
		// Fetch accordant elements if the swapMode is 'any' or 'pages':
220
		if ($this->workspacesSwapMode === 'any' || $this->workspacesSwapMode === 'pages' && $table === 'pages') {
221
			$elementList = $this->getParent()->findPageElementsForVersionSwap($table, $liveId, $properties['swapWith']);
222
		}
223

  
224
		foreach ($elementList as $elementTable => $elementIdArray) {
225
			foreach ($elementIdArray as $elementIds) {
226
				$dependency->addElement(
227
					$elementTable, $elementIds[1],
228
					array('liveId' => $elementIds[0], 'properties' => array_merge($properties, array('swapWith' => $elementIds[1])))
229
				);
230
			}
231
		}
232

  
233
		if (count($elementList) === 0) {
234
			$dependency->addElement(
235
				$table, $properties['swapWith'], array('liveId' => $liveId, 'properties' => $properties)
236
			);
237
		}
238
	}
239

  
240
	/**
241
	 * Resolves workspaces related dependencies for staging of the command map.
242
	 * Workspaces records that have children or (relative) parents which are versionized
243
	 * but not staged with this request, are removed from the command map.
244
	 *
245
	 * @return void
246
	 */
247
	protected function resolveWorkspacesSetStageDependencies() {
248
		$scope = self::SCOPE_WorkspacesSetStage;
249
		$dependency = $this->getDependencyUtility($scope);
250

  
251
		foreach ($this->commandMap as $table => $liveIdCollection) {
252
			foreach ($liveIdCollection as $liveIdList => $commandCollection) {
253
				foreach ($commandCollection as $command => $properties) {
254
					if ($command === 'version' && isset($properties['action']) && $properties['action'] === 'setStage') {
255
						if (isset($properties['stageId']) && t3lib_div::testInt($properties['stageId'])) {
256
							$this->addWorkspacesSetStageElements($dependency, $table, $liveIdList, $properties);
257
							$this->explodeSetStage($table, $liveIdList, $properties);
258
						}
259
					}
260
				}
261
			}
262
		}
263

  
264
		$this->applyWorkspacesDependencies($dependency, $scope);
265
	}
266

  
267
	/**
268
	 * Adds workspaces elements for staging and takes care of the changeStageMode.
269
	 *
270
	 * @param t3lib_utility_Dependency $dependency
271
	 * @param string $table
272
	 * @param string $liveIdList
273
	 * @param array $properties
274
	 * @return void
275
	 */
276
	protected function addWorkspacesSetStageElements(t3lib_utility_Dependency $dependency, $table, $liveIdList, array $properties) {
277
		$liveIds = t3lib_div::trimExplode(',', $liveIdList, TRUE);
278
		$elementList = array($table => $liveIds);
279

  
280
		if (t3lib_div::inList('any,pages', $this->workspacesChangeStageMode)) {
281
			if (count($liveIds) === 1) {
282
				$workspaceRecord = t3lib_BEfunc::getRecord($table, $liveIds[0], 't3ver_wsid');
283
				$workspaceId = $workspaceRecord['t3ver_wsid'];
284
			} else {
285
				$workspaceId = $this->getParent()->BE_USER->workspace;
286
			}
287

  
288
			if ($table === 'pages') {
289
				// Find all elements from the same ws to change stage
290
				$this->getParent()->findRealPageIds($liveIds);
291
				$this->getParent()->findPageElementsForVersionStageChange($liveIds, $workspaceId, $elementList);
292
			} elseif ($this->workspacesChangeStageMode === 'any') {
293
				// Find page to change stage:
294
				$pageIdList = array();
295
				$this->getParent()->findPageIdsForVersionStateChange($table, $liveIds, $workspaceId, $pageIdList, $elementList);
296
				// Find other elements from the same ws to change stage:
297
				$this->getParent()->findPageElementsForVersionStageChange($pageIdList, $workspaceId, $elementList);
298
			}
299
		}
300

  
301
		foreach ($elementList as $elementTable => $elementIds) {
302
			foreach($elementIds as $elementId) {
303
				$dependency->addElement(
304
					$elementTable, $elementId,
305
					array('properties' => $properties)
306
				);
307
			}
308
		}
309
	}
310

  
311
	/**
312
	 * Explodes id-lists in the command map for staging actions.
313
	 *
314
	 * @throws RuntimeException
315
	 * @param string $table
316
	 * @param string $liveIdList
317
	 * @param array $properties
318
	 * @return void
319
	 */
320
	protected function explodeSetStage($table, $liveIdList, array $properties) {
321
		$extractedCommandMap = array();
322
		$liveIds = t3lib_div::trimExplode(',', $liveIdList, TRUE);
323

  
324
		if (count($liveIds) > 1) {
325
			foreach ($liveIds as $liveId) {
326
				if (isset($this->commandMap[$table][$liveId]['version'])) {
327
					throw new RuntimeException('Command map for [' . $table . '][' . $liveId . '][version] was already set.', 1289391048);
328
				}
329

  
330
				$extractedCommandMap[$table][$liveId]['version'] = $properties;
331
			}
332

  
333
			$this->remove($table, $liveIdList, 'version');
334
			$this->mergeToBottom($extractedCommandMap);
335
		}
336
	}
337

  
338
	/**
339
	 * Applies the workspaces dependencies and removes incomplete structures or automatically
340
	 * completes them, depending on the options.workspaces.considerReferences setting
341
	 *
342
	 * @param t3lib_utility_Dependency $dependency
343
	 * @param string $scope
344
	 * @return void
345
	 */
346
	protected function applyWorkspacesDependencies(t3lib_utility_Dependency $dependency, $scope) {
347
		$transformDependentElementsToUseLiveId = $this->getScopeData($scope, self::KEY_TransformDependentElementsToUseLiveId);
348

  
349
		$elementsToBeVersionized = $dependency->getElements();
350
		if ($transformDependentElementsToUseLiveId) {
351
			$elementsToBeVersionized = $this->transformDependentElementsToUseLiveId($elementsToBeVersionized);
352
		}
353

  
354
		$outerMostParents = $dependency->getOuterMostParents();
355
		/** @var $outerMostParent t3lib_utility_Dependency_Element */
356
		foreach ($outerMostParents as $outerMostParent) {
357
			$dependentElements = $dependency->getNestedElements($outerMostParent);
358
			if ($transformDependentElementsToUseLiveId) {
359
				$dependentElements = $this->transformDependentElementsToUseLiveId($dependentElements);
360
			}
361

  
362
			$intersectingElements = array_intersect_key($dependentElements, $elementsToBeVersionized);
363

  
364
			if (count($intersectingElements) > 0) {
365
				// If at least one element intersects but not all, throw away all elements of the depdendent structure:
366
				if (count($intersectingElements) !== count($dependentElements) && $this->workspacesConsiderReferences === FALSE) {
367
					$this->purgeWithErrorMessage($intersectingElements, $scope);
368
				// If everything is fine or references shall be considered automatically:
369
				} else {
370
					$this->update(current($intersectingElements), $dependentElements, $scope);
371
				}
372
			}
373
		}
374
	}
375

  
376
	/**
377
	 * Purges incomplete structures from the command map and triggers an error message.
378
	 *
379
	 * @param array $elements
380
	 * @param string $scope
381
	 * @return void
382
	 */
383
	protected function purgeWithErrorMessage(array $elements, $scope) {
384
		/** @var $dependentElement t3lib_utility_Dependency_Element */
385
		foreach ($elements as $element) {
386
			$table = $element->getTable();
387
			$id = $this->processCallback(
388
				$this->getScopeData($scope, self::KEY_PurgeWithErrorMessageGetIdCallback),
389
				array($element)
390
			);
391

  
392
			$this->remove($table, $id, 'version');
393
			$this->getParent()->log(
394
				$table, $id,
395
				5, 0, 1,
396
				$this->getScopeData($scope, self::KEY_ScopeErrorMessage),
397
				$this->getScopeData($scope, self::KEY_ScopeErrorCode),
398
				array(
399
					t3lib_BEfunc::getRecordTitle($table, t3lib_BEfunc::getRecord($table, $id)),
400
					$table, $id
401
				)
402
			);
403
		}
404
	}
405

  
406
	/**
407
	 * Updates the command map accordant to valid structures and takes care of the correct order.
408
	 *
409
	 * @param t3lib_utility_Dependency_Element $intersectingElement
410
	 * @param array $elements
411
	 * @param string $scope
412
	 * @return void
413
	 */
414
	protected function update(t3lib_utility_Dependency_Element $intersectingElement, array $elements, $scope) {
415
		$orderedCommandMap = array();
416

  
417
		$commonProperties = $this->processCallback(
418
			$this->getScopeData($scope, self::KEY_GetCommonPropertiesCallback),
419
			array($intersectingElement)
420
		);
421

  
422
		/** @var $dependentElement t3lib_utility_Dependency_Element */
423
		foreach ($elements as $element) {
424
			$table = $element->getTable();
425
			$id = $this->processCallback(
426
				$this->getScopeData($scope, self::KEY_UpdateGetIdCallback),
427
				array($element)
428
			);
429

  
430
			$this->remove($table, $id, 'version');
431
			$orderedCommandMap[$table][$id]['version'] = array_merge(
432
				$commonProperties,
433
				$this->processCallback(
434
					$this->getScopeData($scope, self::KEY_GetElementPropertiesCallback),
435
					array($element)
436
				)
437
			);
438
		}
439

  
440
		// Ensure that ordered command map is on top of the command map:
441
		$this->mergeToTop($orderedCommandMap);
442
	}
443

  
444
	/**
445
	 * Merges command map elements to the top of the current command map..
446
	 *
447
	 * @param array $commandMap
448
	 * @return void
449
	 */
450
	protected function mergeToTop(array $commandMap) {
451
		$this->commandMap = t3lib_div::array_merge_recursive_overrule($commandMap, $this->commandMap);
452
	}
453

  
454
	/**
455
	 * Merges command map elements to the bottom of the current command map.
456
	 *
457
	 * @param array $commandMap
458
	 * @return void
459
	 */
460
	protected function mergeToBottom(array $commandMap) {
461
		$this->commandMap = t3lib_div::array_merge_recursive_overrule($this->commandMap, $commandMap);
462
	}
463

  
464
	/**
465
	 * Removes an element from the command map.
466
	 *
467
	 * @param string $table
468
	 * @param string $id
469
	 * @param string $command (optional)
470
	 * @return void
471
	 */
472
	protected function remove($table, $id, $command = NULL) {
473
		if (is_string($command)) {
474
			unset($this->commandMap[$table][$id][$command]);
475
		} else {
476
			unset($this->commandMap[$table][$id]);
477
		}
478
	}
479

  
480
	/**
481
	 * Callback to get the liveId of an dependent element.
482
	 *
483
	 * @param t3lib_utility_Dependency_Element $element
484
	 * @return integer
485
	 */
486
	protected function getElementLiveIdCallback(t3lib_utility_Dependency_Element $element) {
487
		return $element->getDataValue('liveId');
488
	}
489

  
490
	/**
491
	 * Callback to get the real id of an dependent element.
492
	 *
493
	 * @param t3lib_utility_Dependency_Element $element
494
	 * @return integer
495
	 */
496
	protected function getElementIdCallback(t3lib_utility_Dependency_Element $element) {
497
		return $element->getId();
498
	}
499

  
500
	/**
501
	 * Callback to get the specific properties of a dependent element for swapping/publishing.
502
	 *
503
	 * @param t3lib_utility_Dependency_Element $element
504
	 * @return array
505
	 */
506
	protected function getElementSwapPropertiesCallback(t3lib_utility_Dependency_Element $element) {
507
		return array(
508
			'swapWith' => $element->getId(),
509
		);
510
	}
511

  
512
	/**
513
	 * Callback to get common properties of dependent elements for swapping/publishing.
514
	 *
515
	 * @param t3lib_utility_Dependency_Element $element
516
	 * @return array
517
	 */
518
	protected function getCommonSwapPropertiesCallback(t3lib_utility_Dependency_Element $element) {
519
		$commonSwapProperties = array();
520

  
521
		$elementProperties = $element->getDataValue('properties');
522
		if (isset($elementProperties['action'])) {
523
			$commonSwapProperties['action'] = $elementProperties['action'];
524
		}
525
		if (isset($elementProperties['swapIntoWS'])) {
526
			$commonSwapProperties['swapIntoWS'] = $elementProperties['swapIntoWS'];
527
		}
528

  
529
		return $commonSwapProperties;
530
	}
531

  
532
	/**
533
	 * Callback to get the specific properties of a dependent element for staging.
534
	 *
535
	 * @param t3lib_utility_Dependency_Element $element
536
	 * @return array
537
	 */
538
	protected function getElementSetStagePropertiesCallback(t3lib_utility_Dependency_Element $element) {
539
		return $this->getCommonSetStagePropertiesCallback($element);
540
	}
541

  
542
	/**
543
	 * Callback to get common properties of dependent elements for staging.
544
	 *
545
	 * @param t3lib_utility_Dependency_Element $element
546
	 * @return array
547
	 */
548
	protected function getCommonSetStagePropertiesCallback(t3lib_utility_Dependency_Element $element) {
549
		$commonSetStageProperties = array();
550

  
551
		$elementProperties = $element->getDataValue('properties');
552
		if (isset($elementProperties['stageId'])) {
553
			$commonSetStageProperties['stageId'] = $elementProperties['stageId'];
554
		}
555
		if (isset($elementProperties['comment'])) {
556
			$commonSetStageProperties['comment'] = $elementProperties['comment'];
557
		}
558

  
559
		return $commonSetStageProperties;
560
	}
561

  
562

  
563
	/**
564
	 * Gets an instance of the depency resolver utility.
565
	 *
566
	 * @return t3lib_utility_Dependency
567
	 */
568
	protected function getDependencyUtility($scope) {
569

  
570
		/** @var $dependency t3lib_utility_Dependency */
571
		$dependency = t3lib_div::makeInstance('t3lib_utility_Dependency');
572
		$dependency->setOuterMostParentsRequireReferences(TRUE);
573

  
574
		if ($this->getScopeData($scope, self::KEY_ElementConstructCallback)) {
575
			$dependency->setEventCallback(
576
				t3lib_utility_Dependency_Element::EVENT_Construct,
577
				$this->getDependencyCallback($this->getScopeData($scope, self::KEY_ElementConstructCallback))
578
			);
579
		}
580
		if ($this->getScopeData($scope, self::KEY_ElementCreateChildReferenceCallback)) {
581
			$dependency->setEventCallback(
582
				t3lib_utility_Dependency_Element::EVENT_CreateChildReference,
583
				$this->getDependencyCallback($this->getScopeData($scope, self::KEY_ElementCreateChildReferenceCallback))
584
			);
585
		}
586
		if ($this->getScopeData($scope, self::KEY_ElementCreateParentReferenceCallback)) {
587
			$dependency->setEventCallback(
588
				t3lib_utility_Dependency_Element::EVENT_CreateParentReference,
589
				$this->getDependencyCallback($this->getScopeData($scope, self::KEY_ElementCreateParentReferenceCallback))
590
			);
591
		}
592

  
593
		return $dependency;
594
	}
595

  
596
	/**
597
	 * Callback to determine whether a new child reference shall be considered in the dependency resolver utility.
598
	 *
599
	 * @param array $callerArguments
600
	 * @param array $targetArgument
601
	 * @param t3lib_utility_Dependency_Element $caller
602
	 * @param string $eventName
603
	 * @return string Skip response (if required)
604
	 */
605
	public function createNewDependentElementChildReferenceCallback(array $callerArguments, array $targetArgument, t3lib_utility_Dependency_Element $caller, $eventName) {
606
		/** @var $reference t3lib_utility_Dependency_Reference */
607
		$reference = $callerArguments['reference'];
608

  
609
		$fieldCOnfiguration = t3lib_BEfunc::getTcaFieldConfiguration($caller->getTable(), $reference->getField());
610

  
611
		if (!$fieldCOnfiguration || !t3lib_div::inList('field,list', $this->getParent()->getInlineFieldType($fieldCOnfiguration))) {
612
			return t3lib_utility_Dependency_Element::RESPONSE_Skip;
613
		}
614
	}
615

  
616
	/**
617
	 * Callback to determine whether a new parent reference shall be considered in the dependency resolver utility.
618
	 *
619
	 * @param array $callerArguments
620
	 * @param array $targetArgument
621
	 * @param t3lib_utility_Dependency_Element $caller
622
	 * @param string $eventName
623
	 * @return string Skip response (if required)
624
	 */
625
	public function createNewDependentElementParentReferenceCallback(array $callerArguments, array $targetArgument, t3lib_utility_Dependency_Element $caller, $eventName) {
626
		/** @var $reference t3lib_utility_Dependency_Reference */
627
		$reference = $callerArguments['reference'];
628

  
629
		$fieldCOnfiguration = t3lib_BEfunc::getTcaFieldConfiguration($reference->getElement()->getTable(), $reference->getField());
630

  
631
		if (!$fieldCOnfiguration || !t3lib_div::inList('field,list', $this->getParent()->getInlineFieldType($fieldCOnfiguration))) {
632
			return t3lib_utility_Dependency_Element::RESPONSE_Skip;
633
		}
634
	}
635

  
636
	/**
637
	 * Callback to add additional data to new elements created in the dependency resolver utility.
638
	 *
639
	 * @param t3lib_utility_Dependency_Element $caller
640
	 * @param array $callerArguments
641
	 * @param array $targetArgument
642
	 * @return void
643
	 */
644
	public function createNewDependentElementCallback(array $callerArguments, array $targetArgument, t3lib_utility_Dependency_Element $caller) {
645
		if ($caller->hasDataValue('liveId') === FALSE) {
646
			$liveId = t3lib_BEfunc::getLiveVersionIdOfRecord($caller->getTable(), $caller->getId());
647
			if (is_null($liveId) === FALSE) {
648
				$caller->setDataValue('liveId', $liveId);
649
			}
650
		}
651
	}
652

  
653
	/**
654
	 * Transforms dependent elements to use the liveId as array key.
655
	 *
656
	 * @param array $elements Depedent elements, each of type t3lib_utility_Dependency_Element
657
	 * @return array
658
	 */
659
	protected function transformDependentElementsToUseLiveId(array $elements) {
660
		$transformedElements = array();
661

  
662
		/** @var $element t3lib_utility_Dependency_Element */
663
		foreach ($elements as $element) {
664
			$elementName = t3lib_utility_Dependency_Element::getIdentifier(
665
				$element->getTable(), $element->getDataValue('liveId')
666
			);
667
			$transformedElements[$elementName] = $element;
668
		}
669

  
670
		return $transformedElements;
671
	}
672

  
673
	/**
674
	 * Constructs the scope settings.
675
	 * Currently the scopes for swapping/publishing and staging are available.
676
	 *
677
	 * @return void
678
	 */
679
	protected function constructScopes() {
680
		$this->scopes = array(
681
			self::SCOPE_WorkspacesSwap => array(
682
				self::KEY_ScopeErrorMessage => 'Record "%s" (%s:%s) cannot be swapped or published independently, because it is related to other new or modified records.',
683
				self::KEY_ScopeErrorCode => 1288283630,
684
				self::KEY_GetElementPropertiesCallback => 'getElementSwapPropertiesCallback',
685
				self::KEY_GetCommonPropertiesCallback => 'getCommonSwapPropertiesCallback',
686
				self::KEY_ElementConstructCallback => 'createNewDependentElementCallback',
687
				self::KEY_ElementCreateChildReferenceCallback => 'createNewDependentElementChildReferenceCallback',
688
				self::KEY_ElementCreateParentReferenceCallback => 'createNewDependentElementParentReferenceCallback',
689
				self::KEY_PurgeWithErrorMessageGetIdCallback => 'getElementLiveIdCallback',
690
				self::KEY_UpdateGetIdCallback => 'getElementLiveIdCallback',
691
				self::KEY_TransformDependentElementsToUseLiveId => TRUE,
692
			),
693
			self::SCOPE_WorkspacesSetStage => array(
694
				self::KEY_ScopeErrorMessage => 'Record "%s" (%s:%s) cannot be sent to another stage independently, because it is related to other new or modified records.',
695
				self::KEY_ScopeErrorCode => 1289342524,
696
				self::KEY_GetElementPropertiesCallback => 'getElementSetStagePropertiesCallback',
697
				self::KEY_GetCommonPropertiesCallback => 'getCommonSetStagePropertiesCallback',
698
				self::KEY_ElementConstructCallback => NULL,
699
				self::KEY_ElementCreateChildReferenceCallback => 'createNewDependentElementChildReferenceCallback',
700
				self::KEY_ElementCreateParentReferenceCallback => 'createNewDependentElementParentReferenceCallback',
701
				self::KEY_PurgeWithErrorMessageGetIdCallback => 'getElementIdCallback',
702
				self::KEY_UpdateGetIdCallback => 'getElementIdCallback',
703
				self::KEY_TransformDependentElementsToUseLiveId => FALSE,
704
			),
705
		);
706
	}
707

  
708
	/**
709
	 * Gets data for a particular scope.
710
	 *
711
	 * @throws RuntimeException
712
	 * @param string $scope
713
	 * @param string $key
714
	 * @return string
715
	 */
716
	protected function getScopeData($scope, $key) {
717
		if (!isset($this->scopes[$scope])) {
718
			throw new RuntimeException('Scope "' . $scope . '" is not defined.', 1289342187);
719
		}
720

  
721
		return $this->scopes[$scope][$key];
722
	}
723

  
724
	/**
725
	 * Gets a new callback to be used in the dependency resolver utility.
726
	 *
727
	 * @param string $callbackMethod
728
	 * @param array $targetArguments
729
	 * @return t3lib_utility_Dependency_Callback
730
	 */
731
	protected function getDependencyCallback($method, array $targetArguments = array()) {
732
		return t3lib_div::makeInstance('t3lib_utility_Dependency_Callback', $this, $method, $targetArguments);
733
	}
734

  
735
	/**
736
	 * Processes a local callback inside this object.
737
	 *
738
	 * @param string $method
739
	 * @param array $callbackArguments
740
	 * @return mixed
741
	 */
742
	protected function processCallback($method, array $callbackArguments) {
743
		return call_user_func_array(array($this, $method), $callbackArguments);
744
	}
745
}
t3lib/utility/class.t3lib_utility_dependency.php (Revision 40148)
1
<?php
2
/***************************************************************
3
 * Copyright notice
4
 *
5
 * (c) 2010 Oliver Hader <oliver@typo3.org>
6
 * All rights reserved
7
 *
8
 * This script is part of the TYPO3 project. The TYPO3 project is
9
 * free software; you can redistribute it and/or modify
10
 * it under the terms of the GNU General Public License as published by
11
 * the Free Software Foundation; either version 2 of the License, or
12
 * (at your option) any later version.
13
 *
14
 * The GNU General Public License can be found at
15
 * http://www.gnu.org/copyleft/gpl.html.
16
 * A copy is found in the textfile GPL.txt and important notices to the license
17
 * from the author is found in LICENSE.txt distributed with these scripts.
18
 *
19
 *
20
 * This script is distributed in the hope that it will be useful,
21
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
22
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
23
 * GNU General Public License for more details.
24
 *
25
 * This copyright notice MUST APPEAR in all copies of the script!
26
 ***************************************************************/
27

  
28
/**
29
 * Object to handle and determine dependent references of elements.
30
 */
31
class t3lib_utility_Dependency {
32
	/**
33
	 * @var t3lib_utility_Dependency_Factory
34
	 */
35
	protected $factory;
36

  
37
	/**
38
	 * @var array
39
	 */
40
	protected $elements = array();
41

  
42
	/**
43
	 * @var array
44
	 */
45
	protected $eventCallbacks = array();
46

  
47
	/**
48
	 * @var boolean
49
	 */
50
	protected $outerMostParentsRequireReferences = FALSE;
51

  
52
	/**
53
	 * @var array
54
	 */
55
	protected $outerMostParents;
56

  
57
	/**
58
	 * Sets a callback for a particular event.
59
	 *
60
	 * @param string $eventName
61
	 * @param t3lib_utility_Dependency_Callback $callback
62
	 * @return t3lib_utility_Dependency
63
	 */
64
	public function setEventCallback($eventName, t3lib_utility_Dependency_Callback $callback) {
65
		$this->eventCallbacks[$eventName] = $callback;
66
		return $this;
67
	}
68

  
69
	/**
70
	 * Executes a registered callback (if any) for a particular event.
71
	 *
72
	 * @param string $eventName
73
	 * @param object $caller
74
	 * @param array $callerArguments
75
	 * @return mixed
76
	 */
77
	public function executeEventCallback($eventName, $caller, array $callerArguments = array()) {
78
		if (isset($this->eventCallbacks[$eventName])) {
79
			/** @var $callback t3lib_utility_Dependency_Callback */
80
			$callback = $this->eventCallbacks[$eventName];
81
			return $callback->execute($callerArguments, $caller, $eventName);
82
		}
83
	}
84

  
85
	/**
86
	 * Sets the condition that outermost parents required at least one child or parent reference.
87
	 *
88
	 * @param boolean $outerMostParentsRequireReferences
89
	 * @return t3lib_utility_Dependency
90
	 */
91
	public function setOuterMostParentsRequireReferences($outerMostParentsRequireReferences) {
92
		$this->outerMostParentsRequireReferences = (bool)$outerMostParentsRequireReferences;
93
		return $this;
94
	}
95

  
96
	/**
97
	 * Adds an element to be checked for dependent references.
98
	 *
99
	 * @param string $table
100
	 * @param integer $id
101
	 * @param array $data
102
	 * @return t3lib_utility_Dependency_Element
103
	 */
104
	public function addElement($table, $id, array $data = array()) {
105
		$element = $this->getFactory()->getElement($table, $id, $data, $this);
106
		$elementName = $element->__toString();
107
		$this->elements[$elementName] = $element;
108
		return $element;
109
	}
110

  
111
	/**
112
	 * Gets the outermost parents that define complete dependent structure each.
113
	 *
114
	 * @return array
115
	 */
116
	public function getOuterMostParents() {
117
		if (!isset($this->outerMostParents)) {
118
			$this->outerMostParents = array();
119

  
120
			/** @var $element t3lib_utility_Dependency_Element */
121
			foreach ($this->elements as $element) {
122
				$this->processOuterMostParent($element);
123
			}
124
		}
125

  
126
		return $this->outerMostParents;
127
	}
128

  
129
	/**
130
	 * Processes and registers the outermost parents accordant to the registered elements.
131
	 *
132
	 * @param t3lib_utility_Dependency_Element $element
133
	 * @return void
134
	 */
135
	protected function processOuterMostParent(t3lib_utility_Dependency_Element $element) {
136
		if ($this->outerMostParentsRequireReferences === FALSE || $element->hasReferences()) {
137
			$outerMostParent = $element->getOuterMostParent();
138

  
139
			if ($outerMostParent !== FALSE) {
140
				$outerMostParentName = $outerMostParent->__toString();
141
				if (!isset($this->outerMostParents[$outerMostParentName])) {
142
					$this->outerMostParents[$outerMostParentName] = $outerMostParent;
143
				}
144
			}
145
		}
146
	}
147

  
148
	/**
149
	 * Gets all nested elements (including the parent) of a particular outermost parent element.
150
	 *
151
	 * @throws RuntimeException
152
	 * @param t3lib_utility_Dependency_Element $outerMostParent
153
	 * @return array
154
	 */
155
	public function getNestedElements(t3lib_utility_Dependency_Element $outerMostParent) {
156
		$outerMostParentName = $outerMostParent->__toString();
157

  
158
		if (!isset($this->outerMostParents[$outerMostParentName])) {
159
			throw new RuntimeException(
160
				'Element "' . $outerMostParentName . '" was detected as outermost parent.',
161
				1289318609
162
			);
163
		}
164

  
165
		$nestedStructure = array_merge(
166
			array($outerMostParentName => $outerMostParent),
167
			$outerMostParent->getNestedChildren()
168
		);
169

  
170
		return $nestedStructure;
171
	}
172

  
173
	/**
174
	 * Gets the registered elements.
175
	 *
176
	 * @return array
177
	 */
178
	public function getElements() {
179
		return $this->elements;
180
	}
181

  
182
	/**
183
	 * Gets an instance of the factory to keep track of element or reference entities.
184
	 *
185
	 * @return t3lib_utility_Dependency_Factory
186
	 */
187
	public function getFactory() {
188
		if (!isset($this->factory)) {
189
			$this->factory = t3lib_div::makeInstance('t3lib_utility_Dependency_Factory');
190
		}
191
		return $this->factory;
192
	}
193
}
t3lib/utility/dependency/class.t3lib_utility_dependency_factory.php (Revision 40148)
1
<?php
2
/***************************************************************
3
 * Copyright notice
4
 *
5
 * (c) 2010 Oliver Hader <oliver@typo3.org>
6
 * All rights reserved
7
 *
8
 * This script is part of the TYPO3 project. The TYPO3 project is
9
 * free software; you can redistribute it and/or modify
10
 * it under the terms of the GNU General Public License as published by
11
 * the Free Software Foundation; either version 2 of the License, or
12
 * (at your option) any later version.
13
 *
14
 * The GNU General Public License can be found at
... This diff was truncated because it exceeds the maximum size that can be displayed.