713321acce8abe4b8180801731e366f7ab737e22
[Packages/TYPO3.CMS.git] / typo3 / sysext / workspaces / Classes / Service / GridData.php
1 <?php
2 /***************************************************************
3 * Copyright notice
4 *
5 * (c) 2010-2011 Workspaces Team (http://forge.typo3.org/projects/show/typo3v4-workspaces)
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 * @author Workspaces Team (http://forge.typo3.org/projects/show/typo3v4-workspaces)
30 * @package Workspaces
31 * @subpackage Service
32 */
33 class Tx_Workspaces_Service_GridData {
34 const SIGNAL_GenerateDataArray_BeforeCaching = 'generateDataArray.beforeCaching';
35 const SIGNAL_GenerateDataArray_PostProcesss = 'generateDataArray.postProcess';
36 const SIGNAL_GetDataArray_PostProcesss = 'getDataArray.postProcess';
37 const SIGNAL_SortDataArray_PostProcesss = 'sortDataArray.postProcess';
38 const SIGNAL_CalcChangePercentage_PreProcess = 'calcChangePercentage.preProcess';
39 const SIGNAL_CalcChangePercentage_PostProcess = 'calcChangePercentage.postProcess';
40
41 /**
42 * Id of the current active workspace.
43 *
44 * @var integer
45 */
46 protected $currentWorkspace = NULL;
47
48 /**
49 * Version record information (filtered, sorted and limited)
50 *
51 * @var array
52 */
53 protected $dataArray = array();
54
55 /**
56 * Name of the field used for sorting.
57 *
58 * @var string
59 */
60 protected $sort = '';
61
62 /**
63 * Direction used for sorting (ASC, DESC).
64 *
65 * @var string
66 */
67 protected $sortDir = '';
68
69 /**
70 * @var t3lib_cache_frontend_Frontend
71 */
72 protected $workspacesCache = NULL;
73
74 /**
75 * Generates grid list array from given versions.
76 *
77 * @param array $versions All records uids etc. First key is table name, second key incremental integer. Records are associative arrays with uid, t3ver_oid and t3ver_swapmode fields. The pid of the online record is found as "livepid" the pid of the offline record is found in "wspid"
78 * @param object $parameter Parameters as submitted by JavaScript component
79 * @param integer $currentWorkspace The current workspace
80 * @return array Version record information (filtered, sorted and limited)
81 * @throws InvalidArgumentException
82 */
83 public function generateGridListFromVersions($versions, $parameter, $currentWorkspace) {
84 // Read the given parameters from grid. If the parameter is not set use default values.
85 $filterTxt = isset($parameter->filterTxt) ? $parameter->filterTxt : '';
86 $start = isset($parameter->start) ? intval($parameter->start) : 0;
87 $limit = isset($parameter->limit) ? intval($parameter->limit) : 30;
88 $this->sort = isset($parameter->sort) ? $parameter->sort : 't3ver_oid';
89 $this->sortDir = isset($parameter->dir) ? $parameter->dir : 'ASC';
90
91 if (is_int($currentWorkspace)) {
92 $this->currentWorkspace = $currentWorkspace;
93 } else {
94 throw new InvalidArgumentException('No such workspace defined');
95 }
96
97 $data = array();
98 $data['data'] = array();
99
100 $this->generateDataArray($versions, $filterTxt);
101
102 $data['total'] = count($this->dataArray);
103 $data['data'] = $this->getDataArray($start, $limit);
104
105 return $data;
106 }
107
108 /**
109 * Generates grid list array from given versions.
110 *
111 * @param array $versions All available version records
112 * @param string $filterTxt Text to be used to filter record result
113 * @return void
114 */
115 protected function generateDataArray(array $versions, $filterTxt) {
116 $workspaceAccess = $GLOBALS['BE_USER']->checkWorkspace($GLOBALS['BE_USER']->workspace);
117 $swapStage = ($workspaceAccess['publish_access'] & 1) ? Tx_Workspaces_Service_Stages::STAGE_PUBLISH_ID : 0;
118 $swapAccess = ($GLOBALS['BE_USER']->workspacePublishAccess($GLOBALS['BE_USER']->workspace)
119 && $GLOBALS['BE_USER']->workspaceSwapAccess());
120
121 $this->initializeWorkspacesCachingFramework();
122
123 // check for dataArray in cache
124 if ($this->getDataArrayFromCache($versions, $filterTxt) === FALSE) {
125 /** @var $stagesObj Tx_Workspaces_Service_Stages */
126 $stagesObj = t3lib_div::makeInstance('Tx_Workspaces_Service_Stages');
127
128 foreach ($versions as $table => $records) {
129 $versionArray = array('table' => $table);
130 $isRecordTypeAllowedToModify = $GLOBALS['BE_USER']->check('tables_modify', $table);
131
132 foreach ($records as $record) {
133
134 $origRecord = t3lib_BEFunc::getRecord($table, $record['t3ver_oid']);
135 $versionRecord = t3lib_BEFunc::getRecord($table, $record['uid']);
136
137 if (isset($GLOBALS['TCA'][$table]['columns']['hidden'])) {
138 $recordState = $this->workspaceState($versionRecord['t3ver_state'], $origRecord['hidden'], $versionRecord['hidden']);
139 } else {
140 $recordState = $this->workspaceState($versionRecord['t3ver_state']);
141 }
142
143 $isDeletedPage = ($table == 'pages' && $recordState == 'deleted');
144 $viewUrl = Tx_Workspaces_Service_Workspaces::viewSingleRecord($table, $record['t3ver_oid'], $origRecord);
145
146 $pctChange = $this->calculateChangePercentage($table, $origRecord, $versionRecord);
147 $versionArray['id'] = $table . ':' . $record['uid'];
148 $versionArray['uid'] = $record['uid'];
149 $versionArray['workspace'] = $versionRecord['t3ver_id'];
150 $versionArray['label_Workspace'] = htmlspecialchars(t3lib_befunc::getRecordTitle($table, $versionRecord));
151 $versionArray['label_Live'] = htmlspecialchars(t3lib_befunc::getRecordTitle($table, $origRecord));
152 $versionArray['label_Stage'] = htmlspecialchars($stagesObj->getStageTitle($versionRecord['t3ver_stage']));
153 $tempStage = $stagesObj->getNextStage($versionRecord['t3ver_stage']);
154 $versionArray['label_nextStage'] = htmlspecialchars($stagesObj->getStageTitle($tempStage['uid']));
155 $tempStage = $stagesObj->getPrevStage($versionRecord['t3ver_stage']);
156 $versionArray['label_prevStage'] = htmlspecialchars($stagesObj->getStageTitle($tempStage['uid']));
157 $versionArray['change'] = $pctChange;
158 $versionArray['path_Live'] = htmlspecialchars(t3lib_BEfunc::getRecordPath($record['livepid'], '', 999));
159 $versionArray['path_Workspace'] = htmlspecialchars(t3lib_BEfunc::getRecordPath($record['wspid'], '', 999));
160 $versionArray['workspace_Title'] = htmlspecialchars(Tx_Workspaces_Service_Workspaces::getWorkspaceTitle($versionRecord['t3ver_wsid']));
161
162 $versionArray['workspace_Tstamp'] = $versionRecord['tstamp'];
163 $versionArray['workspace_Formated_Tstamp'] = t3lib_BEfunc::datetime($versionRecord['tstamp']);
164 $versionArray['t3ver_oid'] = $record['t3ver_oid'];
165 $versionArray['livepid'] = $record['livepid'];
166 $versionArray['stage'] = $versionRecord['t3ver_stage'];
167 $versionArray['icon_Live'] = t3lib_iconWorks::mapRecordTypeToSpriteIconClass($table, $origRecord);
168 $versionArray['icon_Workspace'] = t3lib_iconWorks::mapRecordTypeToSpriteIconClass($table, $versionRecord);
169
170 $versionArray['allowedAction_nextStage'] = $isRecordTypeAllowedToModify && $stagesObj->isNextStageAllowedForUser($versionRecord['t3ver_stage']);
171 $versionArray['allowedAction_prevStage'] = $isRecordTypeAllowedToModify && $stagesObj->isPrevStageAllowedForUser($versionRecord['t3ver_stage']);
172
173 if ($swapAccess && $swapStage != 0 && $versionRecord['t3ver_stage'] == $swapStage) {
174 $versionArray['allowedAction_swap'] = $isRecordTypeAllowedToModify && $stagesObj->isNextStageAllowedForUser($swapStage);
175 } elseif ($swapAccess && $swapStage == 0) {
176 $versionArray['allowedAction_swap'] = $isRecordTypeAllowedToModify;
177 } else {
178 $versionArray['allowedAction_swap'] = FALSE;
179 }
180
181 $versionArray['allowedAction_delete'] = $isRecordTypeAllowedToModify;
182 // preview and editing of a deleted page won't work ;)
183 $versionArray['allowedAction_view'] = !$isDeletedPage && $viewUrl;
184 $versionArray['allowedAction_edit'] = $isRecordTypeAllowedToModify && !$isDeletedPage;
185 $versionArray['allowedAction_editVersionedPage'] = $isRecordTypeAllowedToModify && !$isDeletedPage;
186
187 $versionArray['state_Workspace'] = $recordState;
188
189 if ($filterTxt == '' || $this->isFilterTextInVisibleColumns($filterTxt, $versionArray)) {
190 $this->dataArray[] = $versionArray;
191 }
192 }
193 }
194
195 // Suggested slot method:
196 // methodName(Tx_Workspaces_Service_GridData $gridData, array &$dataArray, array $versions)
197 $this->emitSignal(
198 self::SIGNAL_GenerateDataArray_BeforeCaching,
199 &$this->dataArray, $versions
200 );
201
202 $this->setDataArrayIntoCache($versions, $filterTxt);
203 }
204
205 // Suggested slot method:
206 // methodName(Tx_Workspaces_Service_GridData $gridData, array &$dataArray, array $versions)
207 $this->emitSignal(
208 self::SIGNAL_GenerateDataArray_PostProcesss,
209 &$this->dataArray, $versions
210 );
211
212 $this->sortDataArray();
213 }
214
215 /**
216 * Gets the data array by considering the page to be shown in the grid view.
217 *
218 * @param integer $start
219 * @param integer $limit
220 * @return array
221 */
222 protected function getDataArray($start, $limit) {
223 $dataArrayPart = array();
224 $end = $start + $limit < count($this->dataArray) ? $start + $limit : count($this->dataArray);
225
226 for ($i = $start; $i < $end; $i++) {
227 $dataArrayPart[] = $this->dataArray[$i];
228 }
229
230 // Suggested slot method:
231 // methodName(Tx_Workspaces_Service_GridData $gridData, array &$dataArray, $start, $limit)
232 $this->emitSignal(
233 self::SIGNAL_GetDataArray_PostProcesss,
234 &$this->dataArray, $start, $limit
235 );
236
237 return $dataArrayPart;
238 }
239
240 /**
241 * Initializes the workspace cache
242 *
243 * @return void
244 */
245 protected function initializeWorkspacesCachingFramework() {
246 $this->workspacesCache = $GLOBALS['typo3CacheManager']->getCache('workspaces_cache');
247 }
248
249 /**
250 * Puts the generated dataArray into the workspace cache.
251 *
252 * @param array $versions All records uids etc. First key is table name, second key incremental integer. Records are associative arrays with uid, t3ver_oid and t3ver_swapmode fields. The pid of the online record is found as "livepid" the pid of the offline record is found in "wspid"
253 * @param string $filterTxt The given filter text from the grid.
254 */
255 protected function setDataArrayIntoCache(array $versions, $filterTxt) {
256 $hash = $this->calculateHash($versions, $filterTxt);
257 $this->workspacesCache->set($hash, $this->dataArray, array($this->currentWorkspace));
258 }
259
260 /**
261 * Checks if a cache entry is given for given versions and filter text and tries to load the data array from cache.
262 *
263 * @param array $versions All records uids etc. First key is table name, second key incremental integer. Records are associative arrays with uid, t3ver_oid and t3ver_swapmode fields. The pid of the online record is found as "livepid" the pid of the offline record is found in "wspid"
264 * @param string $filterTxt The given filter text from the grid.
265 * @return boolean TRUE if cache entry was successfully fetched from cache and content put to $this->dataArray
266 */
267 protected function getDataArrayFromCache(array $versions, $filterTxt) {
268 $cacheEntry = FALSE;
269
270 $hash = $this->calculateHash($versions, $filterTxt);
271
272 $content = $this->workspacesCache->get($hash);
273
274 if ($content !== FALSE) {
275 $this->dataArray = $content;
276 $cacheEntry = TRUE;
277 }
278
279 return $cacheEntry;
280 }
281
282 /**
283 * Calculates the hash value of the used workspace, the user id, the versions array, the filter text, the sorting attribute, the workspace selected in grid and the sorting direction.
284 *
285 * @param array $versions All records uids etc. First key is table name, second key incremental integer. Records are associative arrays with uid, t3ver_oid and t3ver_swapmode fields. The pid of the online record is found as "livepid" the pid of the offline record is found in "wspid"
286 * @param string $filterTxt The given filter text from the grid.
287 * @return string
288 */
289 protected function calculateHash(array $versions, $filterTxt) {
290 $hashArray = array(
291 $GLOBALS['BE_USER']->workspace,
292 $GLOBALS['BE_USER']->user['uid'],
293 $versions,
294 $filterTxt,
295 $this->sort,
296 $this->sortDir,
297 $this->currentWorkspace);
298 $hash = md5(serialize($hashArray));
299
300 return $hash;
301 }
302
303 /**
304 * Performs sorting on the data array accordant to the
305 * selected column in the grid view to be used for sorting.
306 *
307 * @return void
308 */
309 protected function sortDataArray() {
310 if (is_array($this->dataArray)) {
311 switch ($this->sort) {
312 case 'uid';
313 case 'change';
314 case 'workspace_Tstamp';
315 case 't3ver_oid';
316 case 'liveid';
317 case 'livepid':
318 usort($this->dataArray, array($this, 'intSort'));
319 break;
320 case 'label_Workspace';
321 case 'label_Live';
322 case 'label_Stage';
323 case 'workspace_Title';
324 case 'path_Live':
325 // case 'path_Workspace': This is the first sorting attribute
326 usort($this->dataArray, array($this, 'stringSort'));
327 break;
328 }
329 } else {
330 t3lib_div::sysLog('Try to sort "' . $this->sort . '" in "Tx_Workspaces_Service_GridData::sortDataArray" but $this->dataArray is empty! This might be the Bug #26422 which could not reproduced yet.', 3);
331 }
332
333 // Suggested slot method:
334 // methodName(Tx_Workspaces_Service_GridData $gridData, array &$dataArray, $sortColumn, $sortDirection)
335 $this->emitSignal(
336 self::SIGNAL_SortDataArray_PostProcesss,
337 &$this->dataArray, $this->sort, $this->sortDir
338 );
339 }
340
341 /**
342 * Implements individual sorting for columns based on integer comparison.
343 *
344 * @param array $a First value
345 * @param array $b Second value
346 * @return integer
347 */
348 protected function intSort(array $a, array $b) {
349 // First sort by using the page-path in current workspace
350 $path_cmp = strcasecmp($a['path_Workspace'], $b['path_Workspace']);
351
352 if ($path_cmp < 0) {
353 return $path_cmp;
354 } elseif ($path_cmp == 0) {
355 if ($a[$this->sort] == $b[$this->sort]) {
356 return 0;
357 }
358 if ($this->sortDir == 'ASC') {
359 return ($a[$this->sort] < $b[$this->sort]) ? -1 : 1;
360 } elseif ($this->sortDir == 'DESC') {
361 return ($a[$this->sort] > $b[$this->sort]) ? -1 : 1;
362 }
363 } elseif ($path_cmp > 0) {
364 return $path_cmp;
365 }
366 return 0; //ToDo: Throw Exception
367 }
368
369 /**
370 * Implements individual sorting for columns based on string comparison.
371 *
372 * @param string $a First value
373 * @param string $b Second value
374 * @return integer
375 */
376 protected function stringSort($a, $b) {
377 $path_cmp = strcasecmp($a['path_Workspace'], $b['path_Workspace']);
378
379 if ($path_cmp < 0) {
380 return $path_cmp;
381 } elseif ($path_cmp == 0) {
382 if ($a[$this->sort] == $b[$this->sort]) {
383 return 0;
384 }
385 if ($this->sortDir == 'ASC') {
386 return (strcasecmp($a[$this->sort], $b[$this->sort]));
387 } elseif ($this->sortDir == 'DESC') {
388 return (strcasecmp($a[$this->sort], $b[$this->sort]) * (-1));
389 }
390 } elseif ($path_cmp > 0) {
391 return $path_cmp;
392 }
393 return 0; //ToDo: Throw Exception
394 }
395
396 /**
397 * Determines whether the text used to filter the results is part of
398 * a column that is visible in the grid view.
399 *
400 * @param string $filterText
401 * @param array $versionArray
402 * @return boolean
403 */
404 protected function isFilterTextInVisibleColumns($filterText, array $versionArray) {
405 if (is_array($GLOBALS['BE_USER']->uc['moduleData']['Workspaces'][$GLOBALS['BE_USER']->workspace]['columns'])) {
406 foreach ($GLOBALS['BE_USER']->uc['moduleData']['Workspaces'][$GLOBALS['BE_USER']->workspace]['columns'] as $column => $value) {
407 if (isset($value['hidden']) && isset($column) && isset($versionArray[$column])) {
408 if ($value['hidden'] == 0) {
409 switch ($column) {
410 case 'workspace_Tstamp':
411 if (stripos($versionArray['workspace_Formated_Tstamp'], $filterText) !== FALSE) {
412 return TRUE;
413 }
414 break;
415 case 'change':
416 if (stripos(strval($versionArray[$column]), str_replace('%', '', $filterText)) !== FALSE) {
417 return TRUE;
418 }
419 break;
420 default:
421 if (stripos(strval($versionArray[$column]), $filterText) !== FALSE) {
422 return TRUE;
423 }
424 }
425 }
426 }
427 }
428 }
429 return FALSE;
430 }
431
432 /**
433 * Calculates the percentage of changes between two records.
434 *
435 * @param string $table
436 * @param array $diffRecordOne
437 * @param array $diffRecordTwo
438 * @return integer
439 */
440 public function calculateChangePercentage($table, array $diffRecordOne, array $diffRecordTwo) {
441
442 // Initialize:
443 $processed = FALSE;
444 $changePercentage = 0;
445 $changePercentageArray = array();
446
447 // Suggested slot method:
448 // methodName(Tx_Workspaces_Service_GridData $gridData, &$changePercentage, array $firstRecord, array $secondRecord, &$processed)
449 $this->emitSignal(
450 self::SIGNAL_CalcChangePercentage_PreProcess,
451 &$changePercentage, $diffRecordOne, $diffRecordTwo, &$processed
452 );
453
454 // Check that records are arrays:
455 if ($processed === FALSE && is_array($diffRecordOne) && is_array($diffRecordTwo)) {
456
457 // Load full table description
458 t3lib_div::loadTCA($table);
459
460 $similarityPercentage = 0;
461
462 // Traversing the first record and process all fields which are editable:
463 foreach ($diffRecordOne as $fieldName => $fieldValue) {
464 if ($GLOBALS['TCA'][$table]['columns'][$fieldName] && $GLOBALS['TCA'][$table]['columns'][$fieldName]['config']['type'] != 'passthrough' && !t3lib_div::inList('t3ver_label', $fieldName)) {
465
466 if (strcmp(trim($diffRecordOne[$fieldName]), trim($diffRecordTwo[$fieldName]))
467 && $GLOBALS['TCA'][$table]['columns'][$fieldName]['config']['type'] == 'group'
468 && $GLOBALS['TCA'][$table]['columns'][$fieldName]['config']['internal_type'] == 'file'
469 ) {
470
471 // Initialize:
472 $uploadFolder = $GLOBALS['TCA'][$table]['columns'][$fieldName]['config']['uploadfolder'];
473 $files1 = array_flip(t3lib_div::trimExplode(',', $diffRecordOne[$fieldName], 1));
474 $files2 = array_flip(t3lib_div::trimExplode(',', $diffRecordTwo[$fieldName], 1));
475
476 // Traverse filenames and read their md5 sum:
477 foreach ($files1 as $filename => $tmp) {
478 $files1[$filename] = @is_file(PATH_site . $uploadFolder . '/' . $filename) ? md5(t3lib_div::getUrl(PATH_site . $uploadFolder . '/' . $filename)) : $filename;
479 }
480 foreach ($files2 as $filename => $tmp) {
481 $files2[$filename] = @is_file(PATH_site . $uploadFolder . '/' . $filename) ? md5(t3lib_div::getUrl(PATH_site . $uploadFolder . '/' . $filename)) : $filename;
482 }
483
484 // Implode MD5 sums and set flag:
485 $diffRecordOne[$fieldName] = implode(' ', $files1);
486 $diffRecordTwo[$fieldName] = implode(' ', $files2);
487 }
488
489 // If there is a change of value:
490 if (strcmp(trim($diffRecordOne[$fieldName]), trim($diffRecordTwo[$fieldName]))) {
491 // Get the best visual presentation of the value to calculate differences:
492 $val1 = t3lib_BEfunc::getProcessedValue($table, $fieldName, $diffRecordOne[$fieldName], 0, 1);
493 $val2 = t3lib_BEfunc::getProcessedValue($table, $fieldName, $diffRecordTwo[$fieldName], 0, 1);
494
495 similar_text($val1, $val2, $similarityPercentage);
496 $changePercentageArray[] = $similarityPercentage > 0 ? abs($similarityPercentage - 100) : 0;
497 }
498 }
499 }
500
501 // Calculate final change percentage:
502 if (is_array($changePercentageArray)) {
503 $sumPctChange = 0;
504 foreach ($changePercentageArray as $singlePctChange) {
505 $sumPctChange += $singlePctChange;
506 }
507 count($changePercentageArray) > 0 ? $changePercentage = round($sumPctChange / count($changePercentageArray)) : $changePercentage = 0;
508 }
509
510 // Suggested slot method:
511 // methodName(Tx_Workspaces_Service_GridData $gridData, &$changePercentage, array $firstRecord, array $secondRecord, array $changePercentageArray)
512 $this->emitSignal(
513 self::SIGNAL_CalcChangePercentage_PostProcess,
514 &$changePercentage, $diffRecordOne, $diffRecordTwo, $changePercentageArray
515 );
516 }
517
518 return $changePercentage;
519 }
520
521 /**
522 * Gets the state of a given state value.
523 *
524 * @param integer $stateId stateId of offline record
525 * @param boolean $hiddenOnline hidden status of online record
526 * @param boolean $hiddenOffline hidden status of offline record
527 * @return string
528 */
529 protected function workspaceState($stateId, $hiddenOnline = FALSE, $hiddenOffline = FALSE) {
530 switch ($stateId) {
531 case -1:
532 $state = 'new';
533 break;
534 case 1:
535 case 2:
536 $state = 'deleted';
537 break;
538 case 4:
539 $state = 'moved';
540 break;
541 default:
542 $state = 'modified';
543 }
544
545 if ($hiddenOnline == 0 && $hiddenOffline == 1) {
546 $state = 'hidden';
547 } elseif ($hiddenOnline == 1 && $hiddenOffline == 0) {
548 $state = 'unhidden';
549 }
550
551 return $state;
552 }
553
554 /**
555 * Emits a signal to be handled by any registered slots.
556 *
557 * @param string $signalName Name of the signal
558 * @return void
559 */
560 protected function emitSignal($signalName) {
561 // Arguments are always ($this, [method argument], [method argument], ...)
562 $signalArguments = array_merge(
563 array($this),
564 array_slice(func_get_args(), 1)
565 );
566
567 $this->getSignalSlotDispatcher()->dispatch(
568 'Tx_Workspaces_Service_GridData',
569 $signalName,
570 $signalArguments
571 );
572 }
573
574 /**
575 * @return Tx_Extbase_SignalSlot_Dispatcher
576 */
577 protected function getSignalSlotDispatcher() {
578 return $this->getObjectManager()->get('Tx_Extbase_SignalSlot_Dispatcher');
579 }
580
581 /**
582 * @return Tx_Extbase_Object_ObjectManager
583 */
584 protected function getObjectManager() {
585 return t3lib_div::makeInstance('Tx_Extbase_Object_ObjectManager');
586 }
587 }
588
589
590 if (defined('TYPO3_MODE') && isset($GLOBALS['TYPO3_CONF_VARS'][TYPO3_MODE]['XCLASS']['ext/workspaces/Classes/Service/GridData.php'])) {
591 include_once($GLOBALS['TYPO3_CONF_VARS'][TYPO3_MODE]['XCLASS']['ext/workspaces/Classes/Service/GridData.php']);
592 }
593
594 ?>