2 /***************************************************************
5 * (c) 2010-2011 Workspaces Team (http://forge.typo3.org/projects/show/typo3v4-workspaces)
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.
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.
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.
25 * This copyright notice MUST APPEAR in all copies of the script!
26 ***************************************************************/
29 * @author Workspaces Team (http://forge.typo3.org/projects/show/typo3v4-workspaces)
33 class tx_Workspaces_Service_GridData
{
34 protected $currentWorkspace = NULL;
35 protected $dataArray = array();
37 protected $sortDir = '';
38 protected $workspacesCache = NULL;
41 * Generates grid list array from given versions.
43 * @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"
44 * @param object $parameter
46 * @throws InvalidArgumentException
48 public function generateGridListFromVersions($versions, $parameter, $currentWorkspace) {
50 // Read the given parameters from grid. If the parameter is not set use default values.
51 $filterTxt = isset($parameter->filterTxt
) ?
$parameter->filterTxt
: '';
52 $start = isset($parameter->start
) ?
intval($parameter->start
) : 0;
53 $limit = isset($parameter->limit
) ?
intval($parameter->limit
) : 10;
54 $this->sort
= isset($parameter->sort
) ?
$parameter->sort
: 't3ver_oid';
55 $this->sortDir
= isset($parameter->dir
) ?
$parameter->dir
: 'ASC';
57 if (is_int($currentWorkspace)) {
58 $this->currentWorkspace
= $currentWorkspace;
60 throw new InvalidArgumentException('No such workspace defined');
64 $data['data'] = array();
66 $this->generateDataArray($versions, $filterTxt);
68 $data['total'] = count($this->dataArray
);
69 $data['data'] = $this->getDataArray($start, $limit);
75 * Generates grid list array from given versions.
77 * @param array $versions
78 * @param string $filterTxt
81 protected function generateDataArray(array $versions, $filterTxt) {
82 /** @var $stagesObj Tx_Workspaces_Service_Stages */
83 $stagesObj = t3lib_div
::makeInstance('Tx_Workspaces_Service_Stages');
85 /** @var $workspacesObj Tx_Workspaces_Service_Workspaces */
86 $workspacesObj = t3lib_div
::makeInstance('Tx_Workspaces_Service_Workspaces');
87 $availableWorkspaces = $workspacesObj->getAvailableWorkspaces();
89 $workspaceAccess = $GLOBALS['BE_USER']->checkWorkspace($GLOBALS['BE_USER']->workspace
);
90 $swapStage = ($workspaceAccess['publish_access'] & 1) ? Tx_Workspaces_Service_Stages
::STAGE_PUBLISH_ID
: 0;
91 $swapAccess = $GLOBALS['BE_USER']->workspacePublishAccess($GLOBALS['BE_USER']->workspace
) &&
92 $GLOBALS['BE_USER']->workspaceSwapAccess();
94 $this->initializeWorkspacesCachingFramework();
96 // check for dataArray in cache
97 if ($this->getDataArrayFromCache($versions, $filterTxt) == FALSE) {
98 $stagesObj = t3lib_div
::makeInstance('Tx_Workspaces_Service_Stages');
100 foreach ($versions as $table => $records) {
101 $versionArray = array('table' => $table);
103 foreach ($records as $record) {
105 $origRecord = t3lib_BEFunc
::getRecord($table, $record['t3ver_oid']);
106 $versionRecord = t3lib_BEFunc
::getRecord($table, $record['uid']);
108 if (isset($GLOBALS['TCA'][$table]['columns']['hidden'])) {
109 $recordState = $this->workspaceState($versionRecord['t3ver_state'], $origRecord['hidden'], $versionRecord['hidden']);
111 $recordState = $this->workspaceState($versionRecord['t3ver_state']);
113 $isDeletedPage = ($table == 'pages' && $recordState == 'deleted');
114 $viewUrl = tx_Workspaces_Service_Workspaces
::viewSingleRecord($table, $record['t3ver_oid'], $origRecord);
116 $pctChange = $this->calculateChangePercentage($table, $origRecord, $versionRecord);
117 $versionArray['uid'] = $record['uid'];
118 $versionArray['workspace'] = $versionRecord['t3ver_id'];
119 $versionArray['label_Workspace'] = htmlspecialchars(t3lib_befunc
::getRecordTitle($table, $versionRecord));
120 $versionArray['label_Live'] = htmlspecialchars(t3lib_befunc
::getRecordTitle($table, $origRecord));
121 $versionArray['label_Stage'] = htmlspecialchars($stagesObj->getStageTitle($versionRecord['t3ver_stage']));
122 $versionArray['change'] = $pctChange;
123 $versionArray['path_Live'] = htmlspecialchars(t3lib_BEfunc
::getRecordPath($record['livepid'], '', 999));
124 $versionArray['path_Workspace'] = htmlspecialchars(t3lib_BEfunc
::getRecordPath($record['wspid'], '', 999));
125 $versionArray['workspace_Title'] = htmlspecialchars(tx_Workspaces_Service_Workspaces
::getWorkspaceTitle($versionRecord['t3ver_wsid']));
127 $versionArray['workspace_Tstamp'] = $versionRecord['tstamp'];
128 $versionArray['workspace_Formated_Tstamp'] = t3lib_BEfunc
::datetime($versionRecord['tstamp']);
129 $versionArray['t3ver_oid'] = $record['t3ver_oid'];
130 $versionArray['livepid'] = $record['livepid'];
131 $versionArray['stage'] = $versionRecord['t3ver_stage'];
132 $versionArray['icon_Live'] = t3lib_iconWorks
::mapRecordTypeToSpriteIconClass($table, $origRecord);
133 $versionArray['icon_Workspace'] = t3lib_iconWorks
::mapRecordTypeToSpriteIconClass($table, $versionRecord);
135 $versionArray['allowedAction_nextStage'] = $stagesObj->isNextStageAllowedForUser($versionRecord['t3ver_stage']);
136 $versionArray['allowedAction_prevStage'] = $stagesObj->isPrevStageAllowedForUser($versionRecord['t3ver_stage']);
138 if ($swapAccess && $swapStage != 0 && $versionRecord['t3ver_stage'] == $swapStage) {
139 $versionArray['allowedAction_swap'] = $stagesObj->isNextStageAllowedForUser($swapStage);
140 } elseif ($swapAccess && $swapStage == 0) {
141 $versionArray['allowedAction_swap'] = TRUE;
143 $versionArray['allowedAction_swap'] = FALSE;
145 $versionArray['allowedAction_delete'] = TRUE;
146 // preview and editing of a deleted page won't work ;)
147 $versionArray['allowedAction_view'] = !$isDeletedPage && $viewUrl;
148 $versionArray['allowedAction_edit'] = !$isDeletedPage;
149 $versionArray['allowedAction_editVersionedPage'] = !$isDeletedPage;
151 $versionArray['state_Workspace'] = $recordState;
153 if ($filterTxt == '' ||
$this->isFilterTextInVisibleColumns($filterTxt, $versionArray)) {
154 $this->dataArray
[] = $versionArray;
158 $this->sortDataArray();
160 $this->setDataArrayIntoCache($versions, $filterTxt);
162 $this->sortDataArray();
166 * Gets the data array by considering the page to be shown in the grid view.
168 * @param integer $start
169 * @param integer $limit
172 protected function getDataArray($start, $limit) {
173 $dataArrayPart = array();
174 $end = $start +
$limit < count($this->dataArray
) ?
$start +
$limit : count($this->dataArray
);
176 for ($i = $start; $i < $end; $i++
) {
177 $dataArrayPart[] = $this->dataArray
[$i];
180 return $dataArrayPart;
185 * Initialize the workspace cache
189 protected function initializeWorkspacesCachingFramework() {
190 if (TYPO3_UseCachingFramework
=== TRUE) {
192 $GLOBALS['typo3CacheFactory']->create(
194 $GLOBALS['TYPO3_CONF_VARS']['SYS']['caching']['cacheConfigurations']['sys_workspace_cache']['frontend'],
195 $GLOBALS['TYPO3_CONF_VARS']['SYS']['caching']['cacheConfigurations']['sys_workspace_cache']['backend'],
196 $GLOBALS['TYPO3_CONF_VARS']['SYS']['caching']['cacheConfigurations']['sys_workspace_cache']['options']);
197 } catch (t3lib_cache_exception_DuplicateIdentifier
$e) {
198 // do nothing, a workspace cache already exists
201 $this->workspacesCache
= $GLOBALS['typo3CacheManager']->getCache('workspaces_cache');
207 * Put the generated dataArray into the workspace cache.
209 * @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"
210 * @param string $filterTxt The given filter text from the grid.
212 protected function setDataArrayIntoCache (array $versions, $filterTxt) {
213 if (TYPO3_UseCachingFramework
=== TRUE) {
214 $hash = $this->calculateHash($versions, $filterTxt);
215 $content = serialize($this->dataArray
);
217 $this->workspacesCache
->set($hash, $content, array($this->currentWorkspace
));
223 * Checks if a cache entry is given for given versions and filter text and tries to load the data array from cache.
225 * @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"
226 * @param string $filterTxt The given filter text from the grid.
228 protected function getDataArrayFromCache (array $versions, $filterTxt) {
231 if (TYPO3_UseCachingFramework
=== TRUE) {
232 $hash = $this->calculateHash($versions, $filterTxt);
234 $content = $this->workspacesCache
->get($hash);
236 if ($content != FALSE) {
237 $this->dataArray
= unserialize($content);
246 * Calculate 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.
248 * @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"
249 * @param string $filterTxt The given filter text from the grid.
251 protected function calculateHash (array $versions, $filterTxt) {
253 $GLOBALS['BE_USER']->workspace
,
254 $GLOBALS['BE_USER']->user
['uid'],
259 $this->currentWorkspace
);
260 $hash = md5(serialize($hashArray));
267 * Performs sorting on the data array accordant to the
268 * selected column in the grid view to be used for sorting.
272 protected function sortDataArray() {
273 switch ($this->sort
) {
276 case 'workspace_Tstamp';
280 usort($this->dataArray
, array($this, 'intSort'));
282 case 'label_Workspace';
285 case 'workspace_Title';
287 // case 'path_Workspace': This is the first sorting attribute
288 usort($this->dataArray
, array($this, 'stringSort'));
294 * Implements individual sorting for columns based on integer comparison.
300 protected function intSort(array $a, array $b) {
301 // Als erstes nach dem Pfad sortieren
302 $path_cmp = strcasecmp($a['path_Workspace'], $b['path_Workspace']);
306 } elseif ($path_cmp == 0) {
307 if ($a[$this->sort
] == $b[$this->sort
]) {
310 if ($this->sortDir
== 'ASC') {
311 return ($a[$this->sort
] < $b[$this->sort
]) ?
-1 : 1;
312 } elseif ($this->sortDir
== 'DESC') {
313 return ($a[$this->sort
] > $b[$this->sort
]) ?
-1 : 1;
315 } elseif ($path_cmp > 0) {
318 return 0; //ToDo: Throw Exception
322 * Implements individual sorting for columns based on string comparison.
328 protected function stringSort($a, $b) {
329 $path_cmp = strcasecmp($a['path_Workspace'], $b['path_Workspace']);
333 } elseif ($path_cmp == 0) {
334 if ($a[$this->sort
] == $b[$this->sort
]) {
337 if ($this->sortDir
== 'ASC') {
338 return (strcasecmp($a[$this->sort
], $b[$this->sort
]));
339 } elseif ($this->sortDir
== 'DESC') {
340 return (strcasecmp($a[$this->sort
], $b[$this->sort
]) * (-1));
342 } elseif ($path_cmp > 0) {
345 return 0; //ToDo: Throw Exception
349 * Determines whether the text used to filter the results is part of
350 * a column that is visible in the grid view.
352 * @param string $filterText
353 * @param array $versionArray
356 protected function isFilterTextInVisibleColumns($filterText, array $versionArray) {
357 if (is_array($GLOBALS['BE_USER']->uc
['moduleData']['Workspaces']['columns'])) {
358 foreach ($GLOBALS['BE_USER']->uc
['moduleData']['Workspaces']['columns'] as $column => $value) {
359 if (isset($value['hidden']) && isset($column) && isset($versionArray[$column])) {
360 if ($value['hidden'] == 0) {
362 case 'workspace_Tstamp':
363 if (stripos($versionArray['workspace_Formated_Tstamp'], $filterText) !== FALSE) {
368 if (stripos(strval($versionArray[$column]), str_replace('%', '', $filterText)) !== FALSE) {
373 if (stripos(strval($versionArray[$column]), $filterText) !== FALSE) {
385 * Calculates the percentage of changes between two records.
387 * @param string $table
388 * @param array $diffRecordOne
389 * @param array $diffRecordTwo
392 public function calculateChangePercentage($table, array $diffRecordOne, array $diffRecordTwo) {
395 $changePercentage = 0;
396 $changePercentageArray = array();
398 // Check that records are arrays:
399 if (is_array($diffRecordOne) && is_array($diffRecordTwo)) {
401 // Load full table description
402 t3lib_div
::loadTCA($table);
404 $similarityPercentage = 0;
406 // Traversing the first record and process all fields which are editable:
407 foreach ($diffRecordOne as $fieldName => $fieldValue) {
408 if ($GLOBALS['TCA'][$table]['columns'][$fieldName] && $GLOBALS['TCA'][$table]['columns'][$fieldName]['config']['type'] != 'passthrough' && !t3lib_div
::inList('t3ver_label', $fieldName)) {
410 if (strcmp(trim($diffRecordOne[$fieldName]), trim($diffRecordTwo[$fieldName]))
411 && $GLOBALS['TCA'][$table]['columns'][$fieldName]['config']['type'] == 'group'
412 && $GLOBALS['TCA'][$table]['columns'][$fieldName]['config']['internal_type'] == 'file'
416 $uploadFolder = $GLOBALS['TCA'][$table]['columns'][$fieldName]['config']['uploadfolder'];
417 $files1 = array_flip(t3lib_div
::trimExplode(',', $diffRecordOne[$fieldName], 1));
418 $files2 = array_flip(t3lib_div
::trimExplode(',', $diffRecordTwo[$fieldName], 1));
420 // Traverse filenames and read their md5 sum:
421 foreach ($files1 as $filename => $tmp) {
422 $files1[$filename] = @is_file
(PATH_site
. $uploadFolder . '/' . $filename) ?
md5(t3lib_div
::getUrl(PATH_site
. $uploadFolder . '/' . $filename)) : $filename;
424 foreach ($files2 as $filename => $tmp) {
425 $files2[$filename] = @is_file
(PATH_site
. $uploadFolder . '/' . $filename) ?
md5(t3lib_div
::getUrl(PATH_site
. $uploadFolder . '/' . $filename)) : $filename;
428 // Implode MD5 sums and set flag:
429 $diffRecordOne[$fieldName] = implode(' ', $files1);
430 $diffRecordTwo[$fieldName] = implode(' ', $files2);
433 // If there is a change of value:
434 if (strcmp(trim($diffRecordOne[$fieldName]), trim($diffRecordTwo[$fieldName]))) {
435 // Get the best visual presentation of the value to calculate differences:
436 $val1 = t3lib_BEfunc
::getProcessedValue($table, $fieldName, $diffRecordOne[$fieldName], 0, 1);
437 $val2 = t3lib_BEfunc
::getProcessedValue($table, $fieldName, $diffRecordTwo[$fieldName], 0, 1);
439 similar_text($val1, $val2, $similarityPercentage);
440 $changePercentageArray[] = $similarityPercentage > 0 ?
abs($similarityPercentage - 100) : 0;
445 // Calculate final change percentage:
446 if (is_array($changePercentageArray)) {
448 foreach ($changePercentageArray as $singlePctChange) {
449 $sumPctChange +
= $singlePctChange;
451 count($changePercentageArray) > 0 ?
$changePercentage = round($sumPctChange / count($changePercentageArray)) : $changePercentage = 0;
455 return $changePercentage;
459 * Gets the state of a given state value.
461 * @param integer stateId of offline record
462 * @param boolean hidden flag of online record
463 * @param boolean hidden flag of offline record
466 protected function workspaceState($stateId, $hiddenOnline = FALSE, $hiddenOffline = FALSE) {
482 if ($hiddenOnline == 0 && $hiddenOffline == 1) {
484 } elseif ($hiddenOnline == 1 && $hiddenOffline == 0) {
493 if (defined('TYPO3_MODE') && isset($GLOBALS['TYPO3_CONF_VARS'][TYPO3_MODE
]['XCLASS']['ext/workspaces/Classes/Service/GridData.php'])) {
494 include_once($GLOBALS['TYPO3_CONF_VARS'][TYPO3_MODE
]['XCLASS']['ext/workspaces/Classes/Service/GridData.php']);