TYPO3 CMS  TYPO3_6-2
GridDataService.php
Go to the documentation of this file.
1 <?php
3 
19 
26 
27  const SIGNAL_GenerateDataArray_BeforeCaching = 'generateDataArray.beforeCaching';
28  const SIGNAL_GenerateDataArray_PostProcesss = 'generateDataArray.postProcess';
29  const SIGNAL_GetDataArray_PostProcesss = 'getDataArray.postProcess';
30  const SIGNAL_SortDataArray_PostProcesss = 'sortDataArray.postProcess';
31 
32  const GridColumn_Collection = 'Workspaces_Collection';
33  const GridColumn_CollectionLevel = 'Workspaces_CollectionLevel';
34  const GridColumn_CollectionParent = 'Workspaces_CollectionParent';
35  const GridColumn_CollectionCurrent = 'Workspaces_CollectionCurrent';
36  const GridColumn_CollectionChildren = 'Workspaces_CollectionChildren';
37 
43  protected $currentWorkspace = NULL;
44 
50  protected $dataArray = array();
51 
57  protected $sort = '';
58 
64  protected $sortDir = '';
65 
69  protected $workspacesCache = NULL;
70 
74  protected $systemLanguages;
75 
79  protected $integrityService;
80 
90  public function generateGridListFromVersions($versions, $parameter, $currentWorkspace) {
91  // Read the given parameters from grid. If the parameter is not set use default values.
92  $filterTxt = isset($parameter->filterTxt) ? $parameter->filterTxt : '';
93  $start = isset($parameter->start) ? (int)$parameter->start : 0;
94  $limit = isset($parameter->limit) ? (int)$parameter->limit : 30;
95  $this->sort = isset($parameter->sort) ? $parameter->sort : 't3ver_oid';
96  $this->sortDir = isset($parameter->dir) ? $parameter->dir : 'ASC';
97  if (is_int($currentWorkspace)) {
98  $this->currentWorkspace = $currentWorkspace;
99  } else {
100  throw new \InvalidArgumentException('No such workspace defined');
101  }
102  $data = array();
103  $data['data'] = array();
104  $this->generateDataArray($versions, $filterTxt);
105  $data['total'] = count($this->dataArray);
106  $data['data'] = $this->getDataArray($start, $limit);
107  return $data;
108  }
109 
117  protected function generateDataArray(array $versions, $filterTxt) {
118  $workspaceAccess = $GLOBALS['BE_USER']->checkWorkspace($GLOBALS['BE_USER']->workspace);
119  $swapStage = $workspaceAccess['publish_access'] & 1 ? \TYPO3\CMS\Workspaces\Service\StagesService::STAGE_PUBLISH_ID : 0;
120  $swapAccess = $GLOBALS['BE_USER']->workspacePublishAccess($GLOBALS['BE_USER']->workspace) && $GLOBALS['BE_USER']->workspaceSwapAccess();
122  // check for dataArray in cache
123  if ($this->getDataArrayFromCache($versions, $filterTxt) === FALSE) {
125  $stagesObj = GeneralUtility::makeInstance('TYPO3\\CMS\\Workspaces\\Service\\StagesService');
126  $defaultGridColumns = array(
127  self::GridColumn_Collection => 0,
128  self::GridColumn_CollectionLevel => 0,
129  self::GridColumn_CollectionParent => '',
130  self::GridColumn_CollectionCurrent => '',
131  self::GridColumn_CollectionChildren => 0,
132  );
133  foreach ($versions as $table => $records) {
134  $hiddenField = $this->getTcaEnableColumnsFieldName($table, 'disabled');
135  $isRecordTypeAllowedToModify = $GLOBALS['BE_USER']->check('tables_modify', $table);
136 
137  foreach ($records as $record) {
138  $origRecord = BackendUtility::getRecord($table, $record['t3ver_oid']);
139  $versionRecord = BackendUtility::getRecord($table, $record['uid']);
140  $combinedRecord = \TYPO3\CMS\Workspaces\Domain\Model\CombinedRecord::createFromArrays($table, $origRecord, $versionRecord);
141  $this->getIntegrityService()->checkElement($combinedRecord);
142 
143  if ($hiddenField !== NULL) {
144  $recordState = $this->workspaceState($versionRecord['t3ver_state'], $origRecord[$hiddenField], $versionRecord[$hiddenField]);
145  } else {
146  $recordState = $this->workspaceState($versionRecord['t3ver_state']);
147  }
148 
149  $isDeletedPage = $table == 'pages' && $recordState == 'deleted';
150  $viewUrl = \TYPO3\CMS\Workspaces\Service\WorkspaceService::viewSingleRecord($table, $record['uid'], $origRecord, $versionRecord);
151  $versionArray = array();
152  $versionArray['table'] = $table;
153  $versionArray['id'] = $table . ':' . $record['uid'];
154  $versionArray['uid'] = $record['uid'];
155  $versionArray['workspace'] = $versionRecord['t3ver_id'];
156  $versionArray = array_merge($versionArray, $defaultGridColumns);
157  $versionArray['label_Workspace'] = htmlspecialchars(
158  BackendUtility::getRecordTitle($table, $versionRecord));
159  $versionArray['label_Live'] = htmlspecialchars(BackendUtility::getRecordTitle($table, $origRecord));
160  $versionArray['label_Stage'] = htmlspecialchars($stagesObj->getStageTitle($versionRecord['t3ver_stage']));
161  $tempStage = $stagesObj->getNextStage($versionRecord['t3ver_stage']);
162  $versionArray['label_nextStage'] = htmlspecialchars($stagesObj->getStageTitle($tempStage['uid']));
163  $tempStage = $stagesObj->getPrevStage($versionRecord['t3ver_stage']);
164  $versionArray['label_prevStage'] = htmlspecialchars($stagesObj->getStageTitle($tempStage['uid']));
165  $versionArray['path_Live'] = htmlspecialchars(
166  BackendUtility::getRecordPath($record['livepid'], '', 999));
167  $versionArray['path_Workspace'] = htmlspecialchars(
168  BackendUtility::getRecordPath($record['wspid'], '', 999));
169  $versionArray['workspace_Title'] = htmlspecialchars(\TYPO3\CMS\Workspaces\Service\WorkspaceService::getWorkspaceTitle($versionRecord['t3ver_wsid']));
170  $versionArray['workspace_Tstamp'] = $versionRecord['tstamp'];
171  $versionArray['workspace_Formated_Tstamp'] = BackendUtility::datetime($versionRecord['tstamp']);
172  $versionArray['t3ver_wsid'] = $versionRecord['t3ver_wsid'];
173  $versionArray['t3ver_oid'] = $record['t3ver_oid'];
174  $versionArray['livepid'] = $record['livepid'];
175  $versionArray['stage'] = $versionRecord['t3ver_stage'];
176  $versionArray['icon_Live'] = \TYPO3\CMS\Backend\Utility\IconUtility::mapRecordTypeToSpriteIconClass($table, $origRecord);
177  $versionArray['icon_Workspace'] = \TYPO3\CMS\Backend\Utility\IconUtility::mapRecordTypeToSpriteIconClass($table, $versionRecord);
178  $languageValue = $this->getLanguageValue($table, $versionRecord);
179  $versionArray['languageValue'] = $languageValue;
180  $versionArray['language'] = array(
181  'cls' => \TYPO3\CMS\Backend\Utility\IconUtility::getSpriteIconClasses($this->getSystemLanguageValue($languageValue, 'flagIcon')),
182  'title' => htmlspecialchars($this->getSystemLanguageValue($languageValue, 'title'))
183  );
184  $versionArray['allowedAction_nextStage'] = $isRecordTypeAllowedToModify && $stagesObj->isNextStageAllowedForUser($versionRecord['t3ver_stage']);
185  $versionArray['allowedAction_prevStage'] = $isRecordTypeAllowedToModify && $stagesObj->isPrevStageAllowedForUser($versionRecord['t3ver_stage']);
186  if ($swapAccess && $swapStage != 0 && $versionRecord['t3ver_stage'] == $swapStage) {
187  $versionArray['allowedAction_swap'] = $isRecordTypeAllowedToModify && $stagesObj->isNextStageAllowedForUser($swapStage);
188  } elseif ($swapAccess && $swapStage == 0) {
189  $versionArray['allowedAction_swap'] = $isRecordTypeAllowedToModify;
190  } else {
191  $versionArray['allowedAction_swap'] = FALSE;
192  }
193  $versionArray['allowedAction_delete'] = $isRecordTypeAllowedToModify;
194  // preview and editing of a deleted page won't work ;)
195  $versionArray['allowedAction_view'] = !$isDeletedPage && $viewUrl;
196  $versionArray['allowedAction_edit'] = $isRecordTypeAllowedToModify && !$isDeletedPage;
197  $versionArray['allowedAction_editVersionedPage'] = $isRecordTypeAllowedToModify && !$isDeletedPage;
198  $versionArray['state_Workspace'] = $recordState;
199 
200  $versionArray = array_merge(
201  $versionArray,
202  $this->getAdditionalColumnService()->getData($combinedRecord)
203  );
204 
205  if ($filterTxt == '' || $this->isFilterTextInVisibleColumns($filterTxt, $versionArray)) {
206  $versionIdentifier = $versionArray['id'];
207  $this->dataArray[$versionIdentifier] = $versionArray;
208  }
209  }
210  }
211  // Suggested slot method:
212  // methodName(\TYPO3\CMS\Workspaces\Service\GridDataService $gridData, array $dataArray, array $versions)
213  list($this->dataArray, $versions) = $this->emitSignal(self::SIGNAL_GenerateDataArray_BeforeCaching, $this->dataArray, $versions);
214  // Enrich elements after everything has been processed:
215  foreach ($this->dataArray as &$element) {
216  $identifier = $element['table'] . ':' . $element['t3ver_oid'];
217  $element['integrity'] = array(
218  'status' => $this->getIntegrityService()->getStatusRepresentation($identifier),
219  'messages' => htmlspecialchars($this->getIntegrityService()->getIssueMessages($identifier, TRUE))
220  );
221  }
222  $this->setDataArrayIntoCache($versions, $filterTxt);
223  }
224  // Suggested slot method:
225  // methodName(\TYPO3\CMS\Workspaces\Service\GridDataService $gridData, array $dataArray, array $versions)
226  list($this->dataArray, $versions) = $this->emitSignal(self::SIGNAL_GenerateDataArray_PostProcesss, $this->dataArray, $versions);
227  $this->sortDataArray();
229  }
230 
237  protected function resolveDataArrayDependencies() {
238  $collectionService = $this->getDependencyCollectionService();
239  $dependencyResolver = $collectionService->getDependencyResolver();
240 
241  foreach ($this->dataArray as $dataElement) {
242  $dependencyResolver->addElement($dataElement['table'], $dataElement['uid']);
243  }
244 
245  $this->dataArray = $collectionService->process($this->dataArray);
246  }
247 
255  protected function getDataArray($start, $limit) {
256  $dataArrayPart = array();
257  $dataArrayCount = count($this->dataArray);
258  $end = ($start + $limit < count($this->dataArray) ? $start + $limit : $dataArrayCount);
259 
260  // Ensure that there are numerical indexes
261  $this->dataArray = array_values(($this->dataArray));
262  for ($i = $start; $i < $end; $i++) {
263  $dataArrayPart[] = $this->dataArray[$i];
264  }
265 
266  // Ensure that collections are not cut for the pagination
267  if (!empty($this->dataArray[$i][self::GridColumn_Collection])) {
268  $collectionIdentifier = $this->dataArray[$i][self::GridColumn_Collection];
269  for ($i = $i + 1; $i < $dataArrayCount && $collectionIdentifier === $this->dataArray[$i][self::GridColumn_Collection]; $i++) {
270  $dataArrayPart[] = $this->dataArray[$i];
271  }
272  }
273 
274  // Suggested slot method:
275  // methodName(\TYPO3\CMS\Workspaces\Service\GridDataService $gridData, array $dataArray, $start, $limit, array $dataArrayPart)
276  list($this->dataArray, $start, $limit, $dataArrayPart) = $this->emitSignal(self::SIGNAL_GetDataArray_PostProcesss, $this->dataArray, $start, $limit, $dataArrayPart);
277  return $dataArrayPart;
278  }
279 
286  $this->workspacesCache = GeneralUtility::makeInstance('TYPO3\\CMS\\Core\\Cache\\CacheManager')->getCache('workspaces_cache');
287  }
288 
295  protected function setDataArrayIntoCache(array $versions, $filterTxt) {
296  $hash = $this->calculateHash($versions, $filterTxt);
297  $this->workspacesCache->set($hash, $this->dataArray, array($this->currentWorkspace, 'user_' . $GLOBALS['BE_USER']->user['uid']));
298  }
299 
307  protected function getDataArrayFromCache(array $versions, $filterTxt) {
308  $cacheEntry = FALSE;
309  $hash = $this->calculateHash($versions, $filterTxt);
310  $content = $this->workspacesCache->get($hash);
311  if ($content !== FALSE) {
312  $this->dataArray = $content;
313  $cacheEntry = TRUE;
314  }
315  return $cacheEntry;
316  }
317 
325  protected function calculateHash(array $versions, $filterTxt) {
326  $hashArray = array(
327  $GLOBALS['BE_USER']->workspace,
328  $GLOBALS['BE_USER']->user['uid'],
329  $versions,
330  $filterTxt,
331  $this->sort,
332  $this->sortDir,
333  $this->currentWorkspace
334  );
335  $hash = md5(serialize($hashArray));
336  return $hash;
337  }
338 
345  protected function sortDataArray() {
346  if (is_array($this->dataArray)) {
347  switch ($this->sort) {
348  case 'uid':
349  case 'change':
350  case 'workspace_Tstamp':
351  case 't3ver_oid':
352  case 'liveid':
353  case 'livepid':
354  case 'languageValue':
355  uasort($this->dataArray, array($this, 'intSort'));
356  break;
357  case 'label_Workspace':
358  case 'label_Live':
359  case 'label_Stage':
360  case 'workspace_Title':
361  case 'path_Live':
362  // case 'path_Workspace': This is the first sorting attribute
363  uasort($this->dataArray, array($this, 'stringSort'));
364  break;
365  default:
366  // Do nothing
367  }
368  } else {
369  GeneralUtility::sysLog('Try to sort "' . $this->sort . '" in "TYPO3\\CMS\\Workspaces\\Service\\GridDataService::sortDataArray" but $this->dataArray is empty! This might be the Bug #26422 which could not reproduced yet.', 3);
370  }
371  // Suggested slot method:
372  // methodName(\TYPO3\CMS\Workspaces\Service\GridDataService $gridData, array $dataArray, $sortColumn, $sortDirection)
373  list($this->dataArray, $this->sort, $this->sortDir) = $this->emitSignal(self::SIGNAL_SortDataArray_PostProcesss, $this->dataArray, $this->sort, $this->sortDir);
374  }
375 
383  protected function intSort(array $a, array $b) {
384  if (!$this->isSortable($a, $b)) {
385  return 0;
386  }
387  // First sort by using the page-path in current workspace
388  $path_cmp = strcasecmp($a['path_Workspace'], $b['path_Workspace']);
389  if ($path_cmp < 0) {
390  return $path_cmp;
391  } elseif ($path_cmp == 0) {
392  if ($a[$this->sort] == $b[$this->sort]) {
393  return 0;
394  }
395  if ($this->sortDir == 'ASC') {
396  return $a[$this->sort] < $b[$this->sort] ? -1 : 1;
397  } elseif ($this->sortDir == 'DESC') {
398  return $a[$this->sort] > $b[$this->sort] ? -1 : 1;
399  }
400  } elseif ($path_cmp > 0) {
401  return $path_cmp;
402  }
403  return 0;
404  }
405 
413  protected function stringSort($a, $b) {
414  if (!$this->isSortable($a, $b)) {
415  return 0;
416  }
417  $path_cmp = strcasecmp($a['path_Workspace'], $b['path_Workspace']);
418  if ($path_cmp < 0) {
419  return $path_cmp;
420  } elseif ($path_cmp == 0) {
421  if ($a[$this->sort] == $b[$this->sort]) {
422  return 0;
423  }
424  if ($this->sortDir == 'ASC') {
425  return strcasecmp($a[$this->sort], $b[$this->sort]);
426  } elseif ($this->sortDir == 'DESC') {
427  return strcasecmp($a[$this->sort], $b[$this->sort]) * -1;
428  }
429  } elseif ($path_cmp > 0) {
430  return $path_cmp;
431  }
432  return 0;
433  }
434 
444  protected function isSortable(array $a, array $b) {
445  return (
446  $a[self::GridColumn_CollectionLevel] === 0 && $b[self::GridColumn_CollectionLevel] === 0
447  || $a[self::GridColumn_CollectionParent] === $b[self::GridColumn_CollectionParent]
448  );
449  }
450 
459  protected function isFilterTextInVisibleColumns($filterText, array $versionArray) {
460  if (is_array($GLOBALS['BE_USER']->uc['moduleData']['Workspaces'][$GLOBALS['BE_USER']->workspace]['columns'])) {
461  foreach ($GLOBALS['BE_USER']->uc['moduleData']['Workspaces'][$GLOBALS['BE_USER']->workspace]['columns'] as $column => $value) {
462  if (isset($value['hidden']) && isset($column) && isset($versionArray[$column])) {
463  if ($value['hidden'] == 0) {
464  switch ($column) {
465  case 'workspace_Tstamp':
466  if (stripos($versionArray['workspace_Formated_Tstamp'], $filterText) !== FALSE) {
467  return TRUE;
468  }
469  break;
470  case 'change':
471  if (stripos(strval($versionArray[$column]), str_replace('%', '', $filterText)) !== FALSE) {
472  return TRUE;
473  }
474  break;
475  default:
476  if (stripos(strval($versionArray[$column]), $filterText) !== FALSE) {
477  return TRUE;
478  }
479  }
480  }
481  }
482  }
483  }
484  return FALSE;
485  }
486 
495  protected function workspaceState($stateId, $hiddenOnline = FALSE, $hiddenOffline = FALSE) {
496  $hiddenState = NULL;
497  if ($hiddenOnline == 0 && $hiddenOffline == 1) {
498  $hiddenState = 'hidden';
499  } elseif ($hiddenOnline == 1 && $hiddenOffline == 0) {
500  $hiddenState = 'unhidden';
501  }
502  switch ($stateId) {
503  case -1:
504  $state = 'new';
505  break;
506  case 2:
507  $state = 'deleted';
508  break;
509  case 4:
510  $state = 'moved';
511  break;
512  default:
513  $state = ($hiddenState ?: 'modified');
514  }
515  return $state;
516  }
517 
525  protected function getTcaEnableColumnsFieldName($table, $type) {
526  $fieldName = NULL;
527 
528  if (!(empty($GLOBALS['TCA'][$table]['ctrl']['enablecolumns'][$type]))) {
529  $fieldName = $GLOBALS['TCA'][$table]['ctrl']['enablecolumns'][$type];
530  }
531 
532  return $fieldName;
533  }
534 
543  protected function getLanguageValue($table, array $record) {
544  $languageValue = 0;
546  $languageField = $GLOBALS['TCA'][$table]['ctrl']['languageField'];
547  if (!empty($record[$languageField])) {
548  $languageValue = $record[$languageField];
549  }
550  }
551  return $languageValue;
552  }
553 
562  protected function getSystemLanguageValue($id, $key) {
563  $value = NULL;
564  $systemLanguages = $this->getSystemLanguages();
565  if (!empty($systemLanguages[$id][$key])) {
566  $value = $systemLanguages[$id][$key];
567  }
568  return $value;
569  }
570 
576  public function getSystemLanguages() {
577  if (!isset($this->systemLanguages)) {
579  $translateTools = GeneralUtility::makeInstance('TYPO3\\CMS\\Backend\\Configuration\\TranslationConfigurationProvider');
580  $this->systemLanguages = $translateTools->getSystemLanguages();
581  }
582  return $this->systemLanguages;
583  }
584 
590  protected function getIntegrityService() {
591  if (!isset($this->integrityService)) {
592  $this->integrityService = GeneralUtility::makeInstance('TYPO3\\CMS\\Workspaces\\Service\\IntegrityService');
593  }
595  }
596 
603  protected function emitSignal($signalName) {
604  // Arguments are always ($this, [method argument], [method argument], ...)
605  $signalArguments = array_merge(array($this), array_slice(func_get_args(), 1));
606  $slotReturn = $this->getSignalSlotDispatcher()->dispatch('TYPO3\\CMS\\Workspaces\\Service\\GridDataService', $signalName, $signalArguments);
607  return array_slice($slotReturn, 1);
608  }
609 
613  protected function getDependencyCollectionService() {
614  return GeneralUtility::makeInstance('TYPO3\\CMS\\Workspaces\\Service\\Dependency\\CollectionService');
615  }
616 
620  protected function getAdditionalColumnService() {
621  return $this->getObjectManager()->get('TYPO3\\CMS\\Workspaces\\Service\\AdditionalColumnService');
622  }
623 
627  protected function getSignalSlotDispatcher() {
628  return $this->getObjectManager()->get('TYPO3\\CMS\\Extbase\\SignalSlot\\Dispatcher');
629  }
630 
634  protected function getObjectManager() {
635  return \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance('TYPO3\\CMS\\Extbase\\Object\\ObjectManager');
636  }
637 
638 }
static viewSingleRecord($table, $uid, array $liveRecord=NULL, array $versionRecord=NULL)
static createFromArrays($table, array $liveRow, array $versionRow)
static mapRecordTypeToSpriteIconClass($table, array $row)
generateGridListFromVersions($versions, $parameter, $currentWorkspace)
static getRecordTitle($table, $row, $prep=FALSE, $forceResult=TRUE)
calculateHash(array $versions, $filterTxt)
isFilterTextInVisibleColumns($filterText, array $versionArray)
static getRecordPath($uid, $clause, $titleLimit, $fullTitleLimit=0)
if(!defined('TYPO3_MODE')) $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_userauth.php']['logoff_pre_processing'][]
workspaceState($stateId, $hiddenOnline=FALSE, $hiddenOffline=FALSE)
setDataArrayIntoCache(array $versions, $filterTxt)
getDataArrayFromCache(array $versions, $filterTxt)