TYPO3  7.6
SingleFieldContainer.php
Go to the documentation of this file.
1 <?php
2 namespace TYPO3\CMS\Backend\Form\Container;
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 
27 
38 {
44  public function render()
45  {
46  $backendUser = $this->getBackendUserAuthentication();
47  $languageService = $this->getLanguageService();
48  $resultArray = $this->initializeResultArray();
49 
50  $table = $this->data['tableName'];
51  $row = $this->data['databaseRow'];
52  $fieldName = $this->data['fieldName'];
53 
54  // @todo: it should be safe at this point, this array exists ...
55  if (!is_array($this->data['processedTca']['columns'][$fieldName])) {
56  return $resultArray;
57  }
58 
59  $parameterArray = array();
60  $parameterArray['fieldConf'] = $this->data['processedTca']['columns'][$fieldName];
61 
62  $isOverlay = false;
63 
64  // This field decides whether the current record is an overlay (as opposed to being a standalone record)
65  // Based on this decision we need to trigger field exclusion or special rendering (like readOnly)
66  if (isset($this->data['processedTca']['ctrl']['transOrigPointerField'])
67  && is_array($this->data['processedTca']['columns'][$this->data['processedTca']['ctrl']['transOrigPointerField']])
68  && is_array($row[$this->data['processedTca']['ctrl']['transOrigPointerField']])
69  && $row[$this->data['processedTca']['ctrl']['transOrigPointerField']][0] > 0
70  ) {
71  $isOverlay = true;
72  }
73 
74  // A couple of early returns in case the field should not be rendered
75  // Check if this field is configured and editable according to exclude fields and other configuration
76  if (// Return if BE-user has no access rights to this field, @todo: another user access rights check!
77  $parameterArray['fieldConf']['exclude'] && !$backendUser->check('non_exclude_fields', $table . ':' . $fieldName)
78  || $parameterArray['fieldConf']['config']['type'] === 'passthrough'
79  // @todo: Drop option "showIfRTE" ?
80  || !$backendUser->isRTE() && $parameterArray['fieldConf']['config']['showIfRTE']
81  // Return if field should not be rendered in translated records
82  || $isOverlay && !$parameterArray['fieldConf']['l10n_display'] && $parameterArray['fieldConf']['l10n_mode'] === 'exclude'
83  // @todo: localizationMode still needs handling!
84  || $isOverlay && $this->data['localizationMode'] && $this->data['localizationMode'] !== $parameterArray['fieldConf']['l10n_cat']
85  || $this->inlineFieldShouldBeSkipped()
86  ) {
87  return $resultArray;
88  }
89 
90  $parameterArray['fieldTSConfig'] = [];
91  if (isset($this->data['pageTsConfig']['TCEFORM.'][$table . '.'][$fieldName . '.'])
92  && is_array($this->data['pageTsConfig']['TCEFORM.'][$table . '.'][$fieldName . '.'])
93  ) {
94  $parameterArray['fieldTSConfig'] = $this->data['pageTsConfig']['TCEFORM.'][$table . '.'][$fieldName . '.'];
95  }
96  if ($parameterArray['fieldTSConfig']['disabled']) {
97  return $resultArray;
98  }
99 
100  // Override fieldConf by fieldTSconfig:
101  $parameterArray['fieldConf']['config'] = FormEngineUtility::overrideFieldConf($parameterArray['fieldConf']['config'], $parameterArray['fieldTSConfig']);
102  $parameterArray['itemFormElName'] = 'data[' . $table . '][' . $row['uid'] . '][' . $fieldName . ']';
103  $parameterArray['itemFormElID'] = 'data_' . $table . '_' . $row['uid'] . '_' . $fieldName;
104  $newElementBaseName = $this->data['elementBaseName'] . '[' . $table . '][' . $row['uid'] . '][' . $fieldName . ']';
105 
106  // The value to show in the form field.
107  $parameterArray['itemFormElValue'] = $row[$fieldName];
108  // Set field to read-only if configured for translated records to show default language content as readonly
109  if ($parameterArray['fieldConf']['l10n_display']
110  && GeneralUtility::inList($parameterArray['fieldConf']['l10n_display'], 'defaultAsReadonly')
111  && $isOverlay
112  ) {
113  $parameterArray['fieldConf']['config']['readOnly'] = true;
114  $parameterArray['itemFormElValue'] = $this->data['defaultLanguageRow'][$fieldName];
115  }
116 
117  if (strpos($this->data['processedTca']['ctrl']['type'], ':') === false) {
118  $typeField = $this->data['processedTca']['ctrl']['type'];
119  } else {
120  $typeField = substr($this->data['processedTca']['ctrl']['type'], 0, strpos($this->data['processedTca']['ctrl']['type'], ':'));
121  }
122  // Create a JavaScript code line which will ask the user to save/update the form due to changing the element.
123  // This is used for eg. "type" fields and others configured with "requestUpdate"
124  if (!empty($this->data['processedTca']['ctrl']['type'])
125  && $fieldName === $typeField
126  || !empty($this->data['processedTca']['ctrl']['requestUpdate'])
127  && GeneralUtility::inList(str_replace(' ', '', $this->data['processedTca']['ctrl']['requestUpdate']), $fieldName)
128  ) {
129  if ($backendUser->jsConfirmation(JsConfirmation::TYPE_CHANGE)) {
130  $alertMsgOnChange = 'top.TYPO3.Modal.confirm(TBE_EDITOR.labels.refreshRequired.title, TBE_EDITOR.labels.refreshRequired.content).on("button.clicked", function(e) { if (e.target.name == "ok" && TBE_EDITOR.checkSubmit(-1)) { TBE_EDITOR.submitForm() } top.TYPO3.Modal.dismiss(); });';
131  } else {
132  $alertMsgOnChange = 'if (TBE_EDITOR.checkSubmit(-1)){ TBE_EDITOR.submitForm() };';
133  }
134  } else {
135  $alertMsgOnChange = '';
136  }
137 
138  // JavaScript code for event handlers:
139  $parameterArray['fieldChangeFunc'] = array();
140  $parameterArray['fieldChangeFunc']['TBE_EDITOR_fieldChanged'] = 'TBE_EDITOR.fieldChanged(' . GeneralUtility::quoteJSvalue($table) . ',' . GeneralUtility::quoteJSvalue($row['uid']) . ',' . GeneralUtility::quoteJSvalue($fieldName) . ',' . GeneralUtility::quoteJSvalue($parameterArray['itemFormElName']) . ');';
141  $parameterArray['fieldChangeFunc']['alert'] = $alertMsgOnChange;
142 
143  // If this is the child of an inline type and it is the field creating the label
144  if ($this->isInlineChildAndLabelField($table, $fieldName)) {
146  $inlineStackProcessor = GeneralUtility::makeInstance(InlineStackProcessor::class);
147  $inlineStackProcessor->initializeByGivenStructure($this->data['inlineStructure']);
148  $inlineDomObjectId = $inlineStackProcessor->getCurrentStructureDomObjectIdPrefix($this->data['inlineFirstPid']);
149  $inlineObjectId = implode(
150  '-',
151  array(
152  $inlineDomObjectId,
153  $table,
154  $row['uid']
155  )
156  );
157  $parameterArray['fieldChangeFunc']['inline'] = 'inline.handleChangedField(' . GeneralUtility::quoteJSvalue($parameterArray['itemFormElName']) . ',' . GeneralUtility::quoteJSvalue($inlineObjectId) . ');';
158  }
159 
160  // Based on the type of the item, call a render function on a child element
161  $options = $this->data;
162  $options['parameterArray'] = $parameterArray;
163  $options['elementBaseName'] = $newElementBaseName;
164  if (!empty($parameterArray['fieldConf']['config']['renderType'])) {
165  $options['renderType'] = $parameterArray['fieldConf']['config']['renderType'];
166  } else {
167  // Fallback to type if no renderType is given
168  $options['renderType'] = $parameterArray['fieldConf']['config']['type'];
169  }
170  $resultArray = $this->nodeFactory->create($options)->render();
171 
172  // If output is empty stop further processing.
173  // This means there was internal processing only and we don't need to add additional information
174  if (empty($resultArray['html'])) {
175  return $resultArray;
176  }
177 
178  $html = $resultArray['html'];
179 
180  // @todo: the language handling, the null and the placeholder stuff should be embedded in the single
181  // @todo: element classes. Basically, this method should return here and have the element classes
182  // @todo: decide on language stuff and other wraps already.
183 
184  // Add language + diff
185  $renderLanguageDiff = true;
186  if ($parameterArray['fieldConf']['l10n_display'] && (GeneralUtility::inList($parameterArray['fieldConf']['l10n_display'], 'hideDiff')
187  || GeneralUtility::inList($parameterArray['fieldConf']['l10n_display'], 'defaultAsReadonly'))
188  ) {
189  $renderLanguageDiff = false;
190  }
191  if ($renderLanguageDiff) {
192  $html = $this->renderDefaultLanguageContent($table, $fieldName, $row, $html);
193  $html = $this->renderDefaultLanguageDiff($table, $fieldName, $row, $html);
194  }
195 
196  $fieldItemClasses = array(
197  't3js-formengine-field-item'
198  );
199 
200  // NULL value and placeholder handling
201  $nullControlNameAttribute = ' name="' . htmlspecialchars('control[active][' . $table . '][' . $row['uid'] . '][' . $fieldName . ']') . '"';
202  if (!empty($parameterArray['fieldConf']['config']['eval']) && GeneralUtility::inList($parameterArray['fieldConf']['config']['eval'], 'null')
203  && (empty($parameterArray['fieldConf']['config']['mode']) || $parameterArray['fieldConf']['config']['mode'] !== 'useOrOverridePlaceholder')
204  ) {
205  // This field has eval=null set, but has no useOverridePlaceholder defined.
206  // Goal is to have a field that can distinct between NULL and empty string in the database.
207  // A checkbox and an additional hidden field will be created, both with the same name
208  // and prefixed with "control[active]". If the checkbox is set (value 1), the value from the casual
209  // input field will be written to the database. If the checkbox is not set, the hidden field
210  // transfers value=0 to DataHandler, the value of the input field will then be reset to NULL by the
211  // DataHandler at an early point in processing, so NULL will be written to DB as field value.
212 
213  // If the value of the field *is* NULL at the moment, an additional class is set
214  // @todo: This does not work well at the moment, but is kept for now. see input_14 of ext:styleguide as example
215  $checked = ' checked="checked"';
216  if ($this->data['databaseRow'][$fieldName] === null) {
217  $fieldItemClasses[] = 'disabled';
218  $checked = '';
219  }
220 
221  $formElementName = 'data[' . $table . '][' . $row['uid'] . '][' . $fieldName . ']';
222  $onChange = htmlspecialchars(
223  'typo3form.fieldSetNull(' . GeneralUtility::quoteJSvalue($formElementName) . ', !this.checked)'
224  );
225 
226  $nullValueWrap = array();
227  $nullValueWrap[] = '<div class="' . implode(' ', $fieldItemClasses) . '">';
228  $nullValueWrap[] = '<div class="t3-form-field-disable"></div>';
229  $nullValueWrap[] = '<div class="checkbox">';
230  $nullValueWrap[] = '<label>';
231  $nullValueWrap[] = '<input type="hidden"' . $nullControlNameAttribute . ' value="0" />';
232  $nullValueWrap[] = '<input type="checkbox"' . $nullControlNameAttribute . ' value="1" onchange="' . $onChange . '"' . $checked . ' /> &nbsp;';
233  $nullValueWrap[] = '</label>';
234  $nullValueWrap[] = $html;
235  $nullValueWrap[] = '</div>';
236  $nullValueWrap[] = '</div>';
237 
238  $html = implode(LF, $nullValueWrap);
239  } elseif (isset($parameterArray['fieldConf']['config']['mode']) && $parameterArray['fieldConf']['config']['mode'] === 'useOrOverridePlaceholder') {
240  // This field has useOverridePlaceholder set.
241  // Here, a value from a deeper DB structure can be "fetched up" as value, and can also be overridden by a
242  // local value. This is used in FAL, where eg. the "title" field can have the default value from sys_file_metadata,
243  // the title field of sys_file_reference is then set to NULL. Or the "override" checkbox is set, and a string
244  // or an empty string is then written to the field of sys_file_reference.
245  // The situation is similar to the NULL handling above, but additionally a "default" value should be shown.
246  // To achieve this, again a hidden control[hidden] field is added together with a checkbox with the same name
247  // to transfer the information whether the default value should be used or not: Checkbox checked transfers 1 as
248  // value in control[active], meaning the overridden value should be used.
249  // Additionally to the casual input field, a second field is added containing the "placeholder" value. This
250  // field has no name attribute and is not transferred at all. Those two are then hidden / shown depending
251  // on the state of the above checkbox in via JS.
252 
253  $placeholder = empty($parameterArray['fieldConf']['config']['placeholder']) ? '' : $parameterArray['fieldConf']['config']['placeholder'];
254  $onChange = 'typo3form.fieldTogglePlaceholder(' . GeneralUtility::quoteJSvalue($parameterArray['itemFormElName']) . ', !this.checked)';
255  $checked = $parameterArray['itemFormElValue'] === null ? '' : ' checked="checked"';
256 
257  $resultArray['additionalJavaScriptPost'][] = 'typo3form.fieldTogglePlaceholder('
258  . GeneralUtility::quoteJSvalue($parameterArray['itemFormElName']) . ', ' . ($checked ? 'false' : 'true') . ');';
259 
260  // Renders an input or textarea field depending on type of "parent"
261  $options = array();
262  $options['databaseRow'] = array();
263  $options['table'] = '';
264  $options['parameterArray'] = $parameterArray;
265  $options['parameterArray']['itemFormElValue'] = GeneralUtility::fixed_lgd_cs($placeholder, 30);
266  $options['renderType'] = 'none';
267  $noneElementResult = $this->nodeFactory->create($options)->render();
268  $noneElementHtml = $noneElementResult['html'];
269 
270  $placeholderWrap = array();
271  $placeholderWrap[] = '<div class="' . implode(' ', $fieldItemClasses) . '">';
272  $placeholderWrap[] = '<div class="t3-form-field-disable"></div>';
273  $placeholderWrap[] = '<div class="checkbox">';
274  $placeholderWrap[] = '<label>';
275  $placeholderWrap[] = '<input type="hidden"' . $nullControlNameAttribute . ' value="0" />';
276  $placeholderWrap[] = '<input type="checkbox"' . $nullControlNameAttribute . ' value="1" id="tce-forms-textfield-use-override-' . $fieldName . '-' . $row['uid'] . '" onchange="' . htmlspecialchars($onChange) . '"' . $checked . ' />';
277  $placeholderWrap[] = sprintf($languageService->sL('LLL:EXT:lang/locallang_core.xlf:labels.placeholder.override'), BackendUtility::getRecordTitlePrep($placeholder, 20));
278  $placeholderWrap[] = '</label>';
279  $placeholderWrap[] = '</div>';
280  $placeholderWrap[] = '<div class="t3js-formengine-placeholder-placeholder">';
281  $placeholderWrap[] = $noneElementHtml;
282  $placeholderWrap[] = '</div>';
283  $placeholderWrap[] = '<div class="t3js-formengine-placeholder-formfield">';
284  $placeholderWrap[] = $html;
285  $placeholderWrap[] = '</div>';
286  $placeholderWrap[] = '</div>';
287 
288  $html = implode(LF, $placeholderWrap);
289  } elseif ($parameterArray['fieldConf']['config']['type'] !== 'user' || empty($parameterArray['fieldConf']['config']['noTableWrapping'])) {
290  // Add a casual wrap if the field is not of type user with no wrap requested.
291  $standardWrap = array();
292  $standardWrap[] = '<div class="' . implode(' ', $fieldItemClasses) . '">';
293  $standardWrap[] = '<div class="t3-form-field-disable"></div>';
294  $standardWrap[] = $html;
295  $standardWrap[] = '</div>';
296 
297  $html = implode(LF, $standardWrap);
298  }
299 
300  $resultArray['html'] = $html;
301  return $resultArray;
302  }
303 
314  protected function renderDefaultLanguageContent($table, $field, $row, $item)
315  {
316  if (is_array($this->data['defaultLanguageRow'])) {
317  $defaultLanguageValue = BackendUtility::getProcessedValue(
318  $table,
319  $field,
320  $this->data['defaultLanguageRow'][$field],
321  0,
322  true,
323  false,
324  $this->data['defaultLanguageRow']['uid'],
325  true,
326  $this->data['defaultLanguageRow']['pid']
327  );
328  $fieldConfig = $this->data['processedTca']['columns'][$field];
329  // Don't show content if it's for IRRE child records:
330  if ($fieldConfig['config']['type'] !== 'inline') {
332  $iconFactory = GeneralUtility::makeInstance(IconFactory::class);
333  if ($defaultLanguageValue !== '') {
334  $item .= '<div class="t3-form-original-language" title="' . $this->getLanguageService()->sL('LLL:EXT:lang/locallang_misc.xlf:localizeMergeIfNotBlank', true) . '">'
335  . $iconFactory->getIcon($this->data['systemLanguageRows'][0]['flagIconIdentifier'], Icon::SIZE_SMALL)->render()
336  . $this->getMergeBehaviourIcon($fieldConfig['l10n_mode'])
337  . $this->previewFieldValue($defaultLanguageValue, $fieldConfig, $field) . '</div>';
338  }
339  $additionalPreviewLanguages = $this->data['additionalLanguageRows'];
340  foreach ($additionalPreviewLanguages as $previewLanguage) {
341  $defaultLanguageValue = BackendUtility::getProcessedValue(
342  $table,
343  $field,
344  $previewLanguage[$field],
345  0,
346  true
347  );
348  if ($defaultLanguageValue !== '') {
349  $item .= '<div class="t3-form-original-language" title="' . $this->getLanguageService()->sL('LLL:EXT:lang/locallang_misc.xlf:localizeMergeIfNotBlank', true) . '">'
350  . $iconFactory->getIcon($this->data['systemLanguageRows'][$previewLanguage['sys_language_uid']]['flagIconIdentifier'], Icon::SIZE_SMALL)->render()
351  . $this->getMergeBehaviourIcon($fieldConfig['l10n_mode'])
352  . $this->previewFieldValue($defaultLanguageValue, $fieldConfig, $field) . '</div>';
353  }
354  }
355  }
356  }
357  return $item;
358  }
359 
369  protected function getMergeBehaviourIcon($l10nMode)
370  {
371  $icon = '';
372  if ($l10nMode === 'mergeIfNotBlank') {
374  $iconFactory = GeneralUtility::makeInstance(IconFactory::class);
375  $icon = $iconFactory->getIcon('actions-edit-merge-localization', Icon::SIZE_SMALL)->render();
376  }
377  return $icon;
378  }
379 
390  protected function renderDefaultLanguageDiff($table, $field, $row, $item)
391  {
392  if (is_array($this->data['defaultLanguageDataDiff'][$table . ':' . $row['uid']])) {
393  // Initialize:
394  $dLVal = array(
395  'old' => $this->data['defaultLanguageDataDiff'][$table . ':' . $row['uid']],
396  'new' => $this->data['defaultLanguageData'][$table . ':' . $row['uid']]
397  );
398  // There must be diff-data:
399  if (isset($dLVal['old'][$field])) {
400  if ((string)$dLVal['old'][$field] !== (string)$dLVal['new'][$field]) {
401  // Create diff-result:
402  $diffUtility = GeneralUtility::makeInstance(DiffUtility::class);
403  $diffres = $diffUtility->makeDiffDisplay(
404  BackendUtility::getProcessedValue($table, $field, $dLVal['old'][$field], 0, 1),
405  BackendUtility::getProcessedValue($table, $field, $dLVal['new'][$field], 0, 1)
406  );
407  $item .= '<div class="t3-form-original-language-diff">
408  <div class="t3-form-original-language-diffheader">' .
409  htmlspecialchars($this->getLanguageService()->sL('LLL:EXT:lang/locallang_core.xlf:labels.changeInOrig')) .
410  '</div>
411  <div class="t3-form-original-language-diffcontent">' . $diffres . '</div>
412  </div>';
413  }
414  }
415  }
416  return $item;
417  }
418 
428  protected function isInlineChildAndLabelField($table, $field)
429  {
431  $inlineStackProcessor = GeneralUtility::makeInstance(InlineStackProcessor::class);
432  $inlineStackProcessor->initializeByGivenStructure($this->data['inlineStructure']);
433  $level = $inlineStackProcessor->getStructureLevel(-1);
434  if ($level['config']['foreign_label']) {
435  $label = $level['config']['foreign_label'];
436  } else {
437  $label = $this->data['processedTca']['ctrl']['label'];
438  }
439  return $level['config']['foreign_table'] === $table && $label === $field;
440  }
441 
447  protected function inlineFieldShouldBeSkipped()
448  {
449  $table = $this->data['tableName'];
450  $row = $this->data['databaseRow'];
451  $fieldName = $this->data['fieldName'];
452  $fieldConfig = $this->data['processedTca']['columns'][$fieldName]['config'];
453 
455  $inlineStackProcessor = GeneralUtility::makeInstance(InlineStackProcessor::class);
456  $inlineStackProcessor->initializeByGivenStructure($this->data['inlineStructure']);
457  $structureDepth = $inlineStackProcessor->getStructureDepth();
458 
459  $skipThisField = false;
460  if ($structureDepth > 0) {
461  $searchArray = array(
462  '%OR' => array(
463  'config' => array(
464  0 => array(
465  '%AND' => array(
466  'foreign_table' => $table,
467  '%OR' => array(
468  '%AND' => array(
469  'appearance' => array('useCombination' => true),
470  'foreign_selector' => $fieldName
471  ),
472  'MM' => $fieldConfig['MM']
473  )
474  )
475  ),
476  1 => array(
477  '%AND' => array(
478  'foreign_table' => $fieldConfig['foreign_table'],
479  'foreign_selector' => $fieldConfig['foreign_field']
480  )
481  )
482  )
483  )
484  );
485  // Get the parent record from structure stack
486  $level = $inlineStackProcessor->getStructureLevel(-1);
487  // If we have symmetric fields, check on which side we are and hide fields, that are set automatically:
488  if ($this->data['isOnSymmetricSide']) {
489  $searchArray['%OR']['config'][0]['%AND']['%OR']['symmetric_field'] = $fieldName;
490  $searchArray['%OR']['config'][0]['%AND']['%OR']['symmetric_sortby'] = $fieldName;
491  } else {
492  $searchArray['%OR']['config'][0]['%AND']['%OR']['foreign_field'] = $fieldName;
493  $searchArray['%OR']['config'][0]['%AND']['%OR']['foreign_sortby'] = $fieldName;
494  }
495  $skipThisField = $this->arrayCompareComplex($level, $searchArray);
496  }
497  return $skipThisField;
498  }
499 
532  protected function arrayCompareComplex($subjectArray, $searchArray, $type = '')
533  {
534  $localMatches = 0;
535  $localEntries = 0;
536  if (is_array($searchArray) && !empty($searchArray)) {
537  // If no type was passed, try to determine
538  if (!$type) {
539  reset($searchArray);
540  $type = key($searchArray);
541  $searchArray = current($searchArray);
542  }
543  // We use '%AND' and '%OR' in uppercase
544  $type = strtoupper($type);
545  // Split regular elements from sub elements
546  foreach ($searchArray as $key => $value) {
547  $localEntries++;
548  // Process a sub-group of OR-conditions
549  if ($key === '%OR') {
550  $localMatches += $this->arrayCompareComplex($subjectArray, $value, '%OR') ? 1 : 0;
551  } elseif ($key === '%AND') {
552  $localMatches += $this->arrayCompareComplex($subjectArray, $value, '%AND') ? 1 : 0;
553  } elseif (is_array($value) && $this->isAssociativeArray($searchArray)) {
554  $localMatches += $this->arrayCompareComplex($subjectArray[$key], $value, $type) ? 1 : 0;
555  } elseif (is_array($value)) {
556  $localMatches += $this->arrayCompareComplex($subjectArray, $value, $type) ? 1 : 0;
557  } else {
558  if (isset($subjectArray[$key]) && isset($value)) {
559  // Boolean match:
560  if (is_bool($value)) {
561  $localMatches += !($subjectArray[$key] xor $value) ? 1 : 0;
562  } elseif (is_numeric($subjectArray[$key]) && is_numeric($value)) {
563  $localMatches += $subjectArray[$key] == $value ? 1 : 0;
564  } else {
565  $localMatches += $subjectArray[$key] === $value ? 1 : 0;
566  }
567  }
568  }
569  // If one or more matches are required ('OR'), return TRUE after the first successful match
570  if ($type === '%OR' && $localMatches > 0) {
571  return true;
572  }
573  // If all matches are required ('AND') and we have no result after the first run, return FALSE
574  if ($type === '%AND' && $localMatches == 0) {
575  return false;
576  }
577  }
578  }
579  // Return the result for '%AND' (if nothing was checked, TRUE is returned)
580  return $localEntries === $localMatches;
581  }
582 
589  protected function isAssociativeArray($object)
590  {
591  return is_array($object) && !empty($object) && array_keys($object) !== range(0, sizeof($object) - 1);
592  }
593 
597  protected function getBackendUserAuthentication()
598  {
599  return $GLOBALS['BE_USER'];
600  }
601 
605  protected function getLanguageService()
606  {
607  return $GLOBALS['LANG'];
608  }
609 }