TYPO3 CMS  TYPO3_7-6
EvaluateDisplayConditions.php
Go to the documentation of this file.
1 <?php
3 
4 /*
5  * This file is part of the TYPO3 CMS project.
6  *
7  * It is free software; you can redistribute it and/or modify it under
8  * the terms of the GNU General Public License, either version 2
9  * of the License, or any later version.
10  *
11  * For the full copyright and license information, please read the
12  * LICENSE.txt file that was distributed with this source code.
13  *
14  * The TYPO3 project - inspiring people to share!
15  */
16 
20 
27 {
34  public function addData(array $result)
35  {
36  $result = $this->removeFlexformFields($result);
37  $result = $this->removeFlexformSheets($result);
38  $result = $this->removeTcaColumns($result);
39 
40  return $result;
41  }
42 
49  protected function removeTcaColumns($result)
50  {
51  foreach ($result['processedTca']['columns'] as $columnName => $columnConfiguration) {
52  if (!isset($columnConfiguration['displayCond'])) {
53  continue;
54  }
55 
56  if (!$this->evaluateDisplayCondition($columnConfiguration['displayCond'], $result['databaseRow'])) {
57  unset($result['processedTca']['columns'][$columnName]);
58  }
59  }
60 
61  return $result;
62  }
63 
70  protected function removeFlexformSheets($result)
71  {
72  foreach ($result['processedTca']['columns'] as $columnName => $columnConfiguration) {
73  if (!isset($columnConfiguration['config']['type'])
74  || $columnConfiguration['config']['type'] !== 'flex'
75  || !isset($result['processedTca']['columns'][$columnName]['config']['ds']['sheets'])
76  || !is_array($result['processedTca']['columns'][$columnName]['config']['ds']['sheets'])
77  ) {
78  continue;
79  }
80 
81  $flexFormRowData = is_array($result['databaseRow'][$columnName]['data']) ? $result['databaseRow'][$columnName]['data'] : [];
82  $flexFormRowData = $this->flattenFlexformRowData($flexFormRowData);
83  $flexFormRowData['parentRec'] = $result['databaseRow'];
84 
85  $flexFormSheets = $result['processedTca']['columns'][$columnName]['config']['ds']['sheets'];
86  foreach ($flexFormSheets as $sheetName => $sheetConfiguration) {
87  if (!isset($sheetConfiguration['ROOT']['displayCond'])) {
88  continue;
89  }
90  if (!$this->evaluateDisplayCondition($sheetConfiguration['ROOT']['displayCond'], $flexFormRowData, true)) {
91  unset($result['processedTca']['columns'][$columnName]['config']['ds']['sheets'][$sheetName]);
92  }
93  }
94  }
95 
96  return $result;
97  }
98 
105  protected function removeFlexformFields($result)
106  {
107  foreach ($result['processedTca']['columns'] as $columnName => $columnConfiguration) {
108  if (!isset($columnConfiguration['config']['type'])
109  || $columnConfiguration['config']['type'] !== 'flex'
110  || !isset($result['processedTca']['columns'][$columnName]['config']['ds']['sheets'])
111  || !is_array($result['processedTca']['columns'][$columnName]['config']['ds']['sheets'])
112  ) {
113  continue;
114  }
115 
116  $flexFormRowData = is_array($result['databaseRow'][$columnName]['data']) ? $result['databaseRow'][$columnName]['data'] : [];
117  $flexFormRowData['parentRec'] = $result['databaseRow'];
118 
119  foreach ($result['processedTca']['columns'][$columnName]['config']['ds']['sheets'] as $sheetName => $sheetConfiguration) {
120  $flexFormSheetRowData = $flexFormRowData[$sheetName]['lDEF'];
121  $flexFormSheetRowData['parentRec'] = $result['databaseRow'];
122  $result['processedTca']['columns'][$columnName]['config']['ds']['sheets'][$sheetName] = $this->removeFlexformFieldsRecursive(
123  $result['processedTca']['columns'][$columnName]['config']['ds']['sheets'][$sheetName],
124  $flexFormSheetRowData
125  );
126  }
127  }
128 
129  return $result;
130  }
131 
139  protected function removeFlexformFieldsRecursive($structure, $flexFormRowData)
140  {
141  $newStructure = [];
142  foreach ($structure as $key => $value) {
143  if ($key === 'el' && is_array($value)) {
144  $newSubStructure = [];
145  foreach ($value as $subKey => $subValue) {
146  if (!isset($subValue['displayCond']) || $this->evaluateDisplayCondition($subValue['displayCond'], $flexFormRowData, true)) {
147  $newSubStructure[$subKey] = $subValue;
148  }
149  }
150  $value = $newSubStructure;
151  }
152  if (is_array($value)) {
153  $value = $this->removeFlexformFieldsRecursive($value, $flexFormRowData);
154  }
155  $newStructure[$key] = $value;
156  }
157 
158  return $newStructure;
159  }
160 
167  protected function flattenFlexformRowData($flexFormRowData)
168  {
169  $flatFlexFormRowData = [];
170  foreach ($flexFormRowData as $sheetName => $sheetConfiguration) {
171  foreach ($sheetConfiguration['lDEF'] as $fieldName => $fieldConfiguration) {
172  $flatFlexFormRowData[$sheetName . '.' . $fieldName] = $fieldConfiguration;
173  }
174  }
175 
176  return $flatFlexFormRowData;
177  }
178 
192  protected function evaluateDisplayCondition($displayCondition, array $record = [], $flexformContext = false, $recursionLevel = 0)
193  {
194  if ($recursionLevel > 99) {
195  // This should not happen, treat as misconfiguration
196  return true;
197  }
198  if (!is_array($displayCondition)) {
199  // DisplayCondition is not an array - just get its value
200  $result = $this->evaluateSingleDisplayCondition($displayCondition, $record, $flexformContext);
201  } else {
202  // Multiple conditions given as array ('AND|OR' => condition array)
203  $conditionEvaluations = [
204  'AND' => [],
205  'OR' => [],
206  ];
207  foreach ($displayCondition as $logicalOperator => $groupedDisplayConditions) {
208  $logicalOperator = strtoupper($logicalOperator);
209  if (($logicalOperator !== 'AND' && $logicalOperator !== 'OR') || !is_array($groupedDisplayConditions)) {
210  // Invalid line. Skip it.
211  continue;
212  } else {
213  foreach ($groupedDisplayConditions as $key => $singleDisplayCondition) {
214  $key = strtoupper($key);
215  if (($key === 'AND' || $key === 'OR') && is_array($singleDisplayCondition)) {
216  // Recursion statement: condition is 'AND' or 'OR' and is pointing to an array (should be conditions again)
217  $conditionEvaluations[$logicalOperator][] = $this->evaluateDisplayCondition(
218  [$key => $singleDisplayCondition],
219  $record,
220  $flexformContext,
221  $recursionLevel + 1
222  );
223  } else {
224  // Condition statement: collect evaluation of this single condition.
225  $conditionEvaluations[$logicalOperator][] = $this->evaluateSingleDisplayCondition(
226  $singleDisplayCondition,
227  $record,
228  $flexformContext
229  );
230  }
231  }
232  }
233  }
234  if (!empty($conditionEvaluations['OR']) && in_array(true, $conditionEvaluations['OR'], true)) {
235  // There are OR conditions and at least one of them is TRUE
236  $result = true;
237  } elseif (!empty($conditionEvaluations['AND']) && !in_array(false, $conditionEvaluations['AND'], true)) {
238  // There are AND conditions and none of them is FALSE
239  $result = true;
240  } elseif (!empty($conditionEvaluations['OR']) || !empty($conditionEvaluations['AND'])) {
241  // There are some conditions. But no OR was TRUE and at least one AND was FALSE
242  $result = false;
243  } else {
244  // There are no proper conditions - misconfiguration. Return TRUE.
245  $result = true;
246  }
247  }
248  return $result;
249  }
250 
264  protected function evaluateSingleDisplayCondition($displayCondition, array $record = [], $flexformContext = false)
265  {
266  $result = false;
267  list($matchType, $condition) = explode(':', $displayCondition, 2);
268  switch ($matchType) {
269  case 'EXT':
270  $result = $this->matchExtensionCondition($condition);
271  break;
272  case 'FIELD':
273  $result = $this->matchFieldCondition($condition, $record, $flexformContext);
274  break;
275  case 'HIDE_FOR_NON_ADMINS':
276  $result = $this->matchHideForNonAdminsCondition();
277  break;
278  case 'HIDE_L10N_SIBLINGS':
279  $result = $this->matchHideL10nSiblingsCondition();
280  break;
281  case 'REC':
282  $result = $this->matchRecordCondition($condition, $record);
283  break;
284  case 'VERSION':
285  $result = $this->matchVersionCondition($condition, $record);
286  break;
287  case 'USER':
288  $result = $this->matchUserCondition($condition, $record);
289  break;
290  }
291  return $result;
292  }
293 
304  protected function matchExtensionCondition($condition)
305  {
307  $result = false;
308  list($extensionKey, $operator, $operand) = explode(':', $condition, 3);
309  if ($operator === 'LOADED') {
310  if (strtoupper($operand) === 'TRUE') {
311  $result = ExtensionManagementUtility::isLoaded($extensionKey);
312  } elseif (strtoupper($operand) === 'FALSE') {
313  $result = !ExtensionManagementUtility::isLoaded($extensionKey);
314  }
315  }
316  return $result;
317  }
318 
331  protected function matchFieldCondition($condition, $record, $flexformContext = false)
332  {
333  list($fieldName, $operator, $operand) = explode(':', $condition, 3);
334  if ($flexformContext) {
335  if (strpos($fieldName, 'parentRec.') !== false) {
336  $fieldNameParts = explode('.', $fieldName, 2);
337  $fieldValue = $record['parentRec'][$fieldNameParts[1]];
338  } else {
339  $fieldValue = $record[$fieldName]['vDEF'];
340  }
341  } else {
342  $fieldValue = $record[$fieldName];
343  }
344  $result = false;
345  switch ($operator) {
346  case 'REQ':
347  if (is_array($fieldValue) && count($fieldValue) <= 1) {
348  $fieldValue = array_shift($fieldValue);
349  }
350  if (strtoupper($operand) === 'TRUE') {
351  $result = (bool)$fieldValue;
352  } else {
353  $result = !$fieldValue;
354  }
355  break;
356  case '>':
357  if (is_array($fieldValue) && count($fieldValue) <= 1) {
358  $fieldValue = array_shift($fieldValue);
359  }
360  $result = $fieldValue > $operand;
361  break;
362  case '<':
363  if (is_array($fieldValue) && count($fieldValue) <= 1) {
364  $fieldValue = array_shift($fieldValue);
365  }
366  $result = $fieldValue < $operand;
367  break;
368  case '>=':
369  if (is_array($fieldValue) && count($fieldValue) <= 1) {
370  $fieldValue = array_shift($fieldValue);
371  }
372  $result = $fieldValue >= $operand;
373  break;
374  case '<=':
375  if (is_array($fieldValue) && count($fieldValue) <= 1) {
376  $fieldValue = array_shift($fieldValue);
377  }
378  $result = $fieldValue <= $operand;
379  break;
380  case '-':
381  case '!-':
382  if (is_array($fieldValue) && count($fieldValue) <= 1) {
383  $fieldValue = array_shift($fieldValue);
384  }
385  list($minimum, $maximum) = explode('-', $operand);
386  $result = $fieldValue >= $minimum && $fieldValue <= $maximum;
387  if ($operator[0] === '!') {
388  $result = !$result;
389  }
390  break;
391  case '=':
392  case '!=':
393  if (is_array($fieldValue) && count($fieldValue) <= 1) {
394  $fieldValue = array_shift($fieldValue);
395  }
396  $result = $fieldValue == $operand;
397  if ($operator[0] === '!') {
398  $result = !$result;
399  }
400  break;
401  case 'IN':
402  case '!IN':
403  if (is_array($fieldValue)) {
404  $result = count(array_intersect($fieldValue, explode(',', $operand))) > 0;
405  } else {
406  $result = GeneralUtility::inList($operand, $fieldValue);
407  }
408  if ($operator[0] === '!') {
409  $result = !$result;
410  }
411  break;
412  case 'BIT':
413  case '!BIT':
414  $result = (bool)((int)$fieldValue & $operand);
415  if ($operator[0] === '!') {
416  $result = !$result;
417  }
418  break;
419  }
420  return $result;
421  }
422 
428  protected function matchHideForNonAdminsCondition()
429  {
430  return (bool)$this->getBackendUser()->isAdmin();
431  }
432 
440  protected function matchHideL10nSiblingsCondition()
441  {
442  GeneralUtility::deprecationLog('HIDE_L10N_SIBLINGS in Flexform display conditions has been deprecated with TYPO3 CMS 7 and will be removed with TYPO3 CMS 8.');
443  return true;
444  }
445 
457  protected function matchRecordCondition($condition, $record)
458  {
459  $result = false;
460  list($operator, $operand) = explode(':', $condition, 2);
461  if ($operator === 'NEW') {
462  if (strtoupper($operand) === 'TRUE') {
463  $result = !((int)$record['uid'] > 0);
464  } elseif (strtoupper($operand) === 'FALSE') {
465  $result = ((int)$record['uid'] > 0);
466  }
467  }
468  return $result;
469  }
470 
479  protected function matchVersionCondition($condition, $record)
480  {
481  $result = false;
482  list($operator, $operand) = explode(':', $condition, 2);
483  if ($operator === 'IS') {
484  $isNewRecord = !((int)$record['uid'] > 0);
485  // Detection of version can be done be detecting the workspace of the user
486  $isUserInWorkspace = $this->getBackendUser()->workspace > 0;
487  if ((int)$record['pid'] === -1 || (int)$record['_ORIG_pid'] === -1) {
488  $isRecordDetectedAsVersion = true;
489  } else {
490  $isRecordDetectedAsVersion = false;
491  }
492  // New records in a workspace are not handled as a version record
493  // if it's no new version, we detect versions like this:
494  // -- if user is in workspace: always TRUE
495  // -- if editor is in live ws: only TRUE if pid == -1
496  $isVersion = ($isUserInWorkspace || $isRecordDetectedAsVersion) && !$isNewRecord;
497  if (strtoupper($operand) === 'TRUE') {
498  $result = $isVersion;
499  } elseif (strtoupper($operand) === 'FALSE') {
500  $result = !$isVersion;
501  }
502  }
503  return $result;
504  }
505 
513  protected function matchUserCondition($condition, $record)
514  {
515  $conditionParameters = explode(':', $condition);
516  $userFunction = array_shift($conditionParameters);
517 
518  $parameter = [
519  'record' => $record,
520  'flexformValueKey' => 'vDEF',
521  'conditionParameters' => $conditionParameters
522  ];
523 
524  return (bool)GeneralUtility::callUserFunction($userFunction, $parameter, $this);
525  }
526 
532  protected function getBackendUser()
533  {
534  return $GLOBALS['BE_USER'];
535  }
536 }
static callUserFunction($funcName, &$params, &$ref, $checkPrefix='', $errorMode=0)
evaluateSingleDisplayCondition($displayCondition, array $record=[], $flexformContext=false)
if(TYPO3_MODE==='BE') $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_tsfebeuserauth.php']['frontendEditingController']['default']
evaluateDisplayCondition($displayCondition, array $record=[], $flexformContext=false, $recursionLevel=0)