‪TYPO3CMS  10.4
InlineControlContainer.php
Go to the documentation of this file.
1 <?php
2 
3 /*
4  * This file is part of the TYPO3 CMS project.
5  *
6  * It is free software; you can redistribute it and/or modify it under
7  * the terms of the GNU General Public License, either version 2
8  * of the License, or any later version.
9  *
10  * For the full copyright and license information, please read the
11  * LICENSE.txt file that was distributed with this source code.
12  *
13  * The TYPO3 project - inspiring people to share!
14  */
15 
17 
30 
43 {
49  protected ‪$inlineData = [];
50 
54  protected ‪$inlineStackProcessor;
55 
59  protected ‪$iconFactory;
60 
64  protected ‪$requireJsModules = [];
65 
71  protected ‪$defaultFieldInformation = [
72  'tcaDescription' => [
73  'renderType' => 'tcaDescription',
74  ],
75  ];
76 
80  protected ‪$defaultFieldWizard = [
81  'localizationStateSelector' => [
82  'renderType' => 'localizationStateSelector',
83  ],
84  ];
85 
93  {
94  parent::__construct(‪$nodeFactory, ‪$data);
95  $this->iconFactory = GeneralUtility::makeInstance(IconFactory::class);
96  }
97 
103  public function ‪render()
104  {
105  $languageService = $this->‪getLanguageService();
106 
107  $this->inlineData = $this->data['inlineData'];
108 
110  ‪$inlineStackProcessor = GeneralUtility::makeInstance(InlineStackProcessor::class);
111  $this->inlineStackProcessor = ‪$inlineStackProcessor;
112  ‪$inlineStackProcessor->‪initializeByGivenStructure($this->data['inlineStructure']);
113 
114  $table = $this->data['tableName'];
115  $row = $this->data['databaseRow'];
116  $field = $this->data['fieldName'];
117  $parameterArray = $this->data['parameterArray'];
118 
119  $resultArray = $this->‪initializeResultArray();
120 
121  $config = $parameterArray['fieldConf']['config'];
122  $foreign_table = $config['foreign_table'];
123  $isReadOnly = isset($config['readOnly']) && $config['readOnly'];
124  $language = 0;
125  $languageFieldName = ‪$GLOBALS['TCA'][$table]['ctrl']['languageField'];
127  $language = isset($row[$languageFieldName][0]) ? (int)$row[$languageFieldName][0] : (int)$row[$languageFieldName];
128  }
129 
130  // Add the current inline job to the structure stack
131  $newStructureItem = [
132  'table' => $table,
133  'uid' => $row['uid'],
134  'field' => $field,
135  'config' => $config,
136  ];
137  // Extract FlexForm parts (if any) from element name, e.g. array('vDEF', 'lDEF', 'FlexField', 'vDEF')
138  if (!empty($parameterArray['itemFormElName'])) {
139  $flexFormParts = $this->‪extractFlexFormParts($parameterArray['itemFormElName']);
140  if ($flexFormParts !== null) {
141  $newStructureItem['flexform'] = $flexFormParts;
142  }
143  }
145 
146  // Transport the flexform DS identifier fields to the FormInlineAjaxController
147  if (!empty($newStructureItem['flexform'])
148  && isset($this->data['processedTca']['columns'][$field]['config']['dataStructureIdentifier'])
149  ) {
150  $config['dataStructureIdentifier'] = $this->data['processedTca']['columns'][$field]['config']['dataStructureIdentifier'];
151  }
152 
153  // Hand over original returnUrl to FormInlineAjaxController. Needed if opening for instance a
154  // nested element in a new view to then go back to the original returnUrl and not the url of
155  // the inline ajax controller
156  $config['originalReturnUrl'] = $this->data['returnUrl'];
157 
158  // e.g. data[<table>][<uid>][<field>]
160  // e.g. data-<pid>-<table1>-<uid1>-<field1>-<table2>-<uid2>-<field2>
161  $nameObject = ‪$inlineStackProcessor->‪getCurrentStructureDomObjectIdPrefix($this->data['inlineFirstPid']);
162 
163  $config['inline']['first'] = false;
164  $firstChild = reset($this->data['parameterArray']['fieldConf']['children']);
165  if (isset($firstChild['databaseRow']['uid'])) {
166  $config['inline']['first'] = $firstChild['databaseRow']['uid'];
167  }
168  $config['inline']['last'] = false;
169  $lastChild = end($this->data['parameterArray']['fieldConf']['children']);
170  if (isset($lastChild['databaseRow']['uid'])) {
171  $config['inline']['last'] = $lastChild['databaseRow']['uid'];
172  }
173 
175 
176  $this->inlineData['config'][$nameObject] = [
177  'table' => $foreign_table,
178  ];
179  $configJson = (string)json_encode($config);
180  $this->inlineData['config'][$nameObject . '-' . $foreign_table] = [
181  'min' => $config['minitems'],
182  'max' => $config['maxitems'],
183  'sortable' => $config['appearance']['useSortable'],
184  'top' => [
185  'table' => $top['table'],
186  'uid' => $top['uid']
187  ],
188  'context' => [
189  'config' => $configJson,
190  'hmac' => GeneralUtility::hmac($configJson, 'InlineContext'),
191  ],
192  ];
193  $this->inlineData['nested'][$nameObject] = $this->data['tabAndInlineStack'];
194 
195  $uniqueMax = 0;
196  $uniqueIds = [];
197 
198  if ($config['foreign_unique']) {
199  // Add inlineData['unique'] with JS unique configuration
200  // @todo: Improve validation and throw an exception if type is neither select nor group here
201  $type = $config['selectorOrUniqueConfiguration']['config']['type'] === 'select' ? 'select' : 'groupdb';
202  foreach ($parameterArray['fieldConf']['children'] as $child) {
203  // Determine used unique ids, skip not localized records
204  if (!$child['isInlineDefaultLanguageRecordInLocalizedParentContext']) {
205  $value = $child['databaseRow'][$config['foreign_unique']];
206  // We're assuming there is only one connected value here for both select and group
207  if ($type === 'select') {
208  // A select field is an array of uids. See TcaSelectItems data provider for details.
209  // Pick first entry, ends up as eg. $value = 42.
210  $value = $value['0'];
211  } else {
212  // A group field is an array of arrays containing uid + table + title + row.
213  // See TcaGroup data provider for details.
214  // Pick the first one (always on 0), and use uid + table only. Exclude title + row
215  // since the entire inlineData['unique'] array ends up in JavaScript in the end
216  // and we don't need and want the title and the entire row data in the frontend.
217  // Ends up as $value = [ 'uid' => '42', 'table' => 'tx_my_table' ]
218  $value = [
219  'uid' => $value[0]['uid'],
220  'table' => $value[0]['table'],
221  ];
222  }
223  // Note structure of $value is different in select vs. group: It's a uid for select, but an
224  // array with uid + table for group.
225  $uniqueIds[$child['databaseRow']['uid']] = $value;
226  }
227  }
228  $possibleRecords = $config['selectorOrUniquePossibleRecords'];
229  $possibleRecordsUidToTitle = [];
230  foreach ($possibleRecords as $possibleRecord) {
231  $possibleRecordsUidToTitle[$possibleRecord[1]] = $possibleRecord[0];
232  }
233  $uniqueMax = $config['appearance']['useCombination'] || empty($possibleRecords) ? -1 : count($possibleRecords);
234  $this->inlineData['unique'][$nameObject . '-' . $foreign_table] = [
235  'max' => $uniqueMax,
236  'used' => $uniqueIds,
237  'type' => $type,
238  'table' => $foreign_table,
239  'elTable' => $config['selectorOrUniqueConfiguration']['foreignTable'],
240  'field' => $config['foreign_unique'],
241  'selector' => $config['selectorOrUniqueConfiguration']['isSelector'] ? $type : false,
242  'possible' => $possibleRecordsUidToTitle,
243  ];
244  }
245 
246  $resultArray['inlineData'] = ‪$this->inlineData;
247 
248  // @todo: It might be a good idea to have something like "isLocalizedRecord" or similar set by a data provider
249  $uidOfDefaultRecord = $row[‪$GLOBALS['TCA'][$table]['ctrl']['transOrigPointerField']];
250  $isLocalizedParent = $language > 0
251  && ($uidOfDefaultRecord[0] ?? $uidOfDefaultRecord) > 0
253  $numberOfFullLocalizedChildren = 0;
254  $numberOfNotYetLocalizedChildren = 0;
255  foreach ($this->data['parameterArray']['fieldConf']['children'] as $child) {
256  if (!$child['isInlineDefaultLanguageRecordInLocalizedParentContext']) {
257  $numberOfFullLocalizedChildren++;
258  }
259  if ($isLocalizedParent && $child['isInlineDefaultLanguageRecordInLocalizedParentContext']) {
260  $numberOfNotYetLocalizedChildren++;
261  }
262  }
263 
264  // Render the localization buttons if needed
265  $localizationButtons = '';
266  if ($numberOfNotYetLocalizedChildren) {
267  // Add the "Localize all records" button before all child records:
268  if (isset($config['appearance']['showAllLocalizationLink']) && $config['appearance']['showAllLocalizationLink']) {
269  $localizationButtons = ' ' . $this->‪getLevelInteractionButton('localize', $config);
270  }
271  // Add the "Synchronize with default language" button before all child records:
272  if (isset($config['appearance']['showSynchronizationLink']) && $config['appearance']['showSynchronizationLink']) {
273  $localizationButtons .= ' ' . $this->‪getLevelInteractionButton('synchronize', $config);
274  }
275  }
276 
277  // Define how to show the "Create new record" button - if there are more than maxitems, hide it
278  if ($isReadOnly || $numberOfFullLocalizedChildren >= $config['maxitems'] || ($uniqueMax > 0 && $numberOfFullLocalizedChildren >= $uniqueMax)) {
279  $config['inline']['inlineNewButtonStyle'] = 'display: none;';
280  $config['inline']['inlineNewRelationButtonStyle'] = 'display: none;';
281  $config['inline']['inlineOnlineMediaAddButtonStyle'] = 'display: none;';
282  }
283 
284  // Render the "new record" level button:
285  $newRecordButton = '';
286  if (!empty($config['appearance']['enabledControls']['new'])) {
287  $newRecordButton = $this->‪getLevelInteractionButton('newRecord', $config);
288  }
289 
290  // Wrap all inline fields of a record with a <div> (like a container)
291  $html = '<div class="form-group" id="' . htmlspecialchars($nameObject) . '" data-uid="' . htmlspecialchars((string)$row['uid']) . '" data-foreign-table="' . htmlspecialchars($foreign_table) . '" data-object-group="' . htmlspecialchars($nameObject) . '-' . htmlspecialchars((string)$foreign_table) . '" data-form-field="' . htmlspecialchars($nameForm) . '" data-appearance="' . htmlspecialchars((string)json_encode($config['appearance'])) . '">';
292 
293  $fieldInformationResult = $this->‪renderFieldInformation();
294  $html .= $fieldInformationResult['html'];
295  $resultArray = $this->‪mergeChildReturnIntoExistingResult($resultArray, $fieldInformationResult, false);
296 
297  // Add the level buttons before all child records:
298  if ($config['appearance']['levelLinksPosition'] === 'both' || $config['appearance']['levelLinksPosition'] === 'top') {
299  $html .= '<div class="form-group t3js-formengine-validation-marker">' . $newRecordButton . $localizationButtons . '</div>';
300  }
301 
302  // If it's required to select from possible child records (reusable children), add a selector box
303  if (!$isReadOnly && $config['foreign_selector'] && $config['appearance']['showPossibleRecordsSelector'] !== false) {
304  if ($config['selectorOrUniqueConfiguration']['config']['type'] === 'select') {
305  $selectorBox = $this->‪renderPossibleRecordsSelectorTypeSelect($config, $uniqueIds);
306  } else {
307  $selectorBox = $this->‪renderPossibleRecordsSelectorTypeGroupDB($config);
308  }
309  $html .= $selectorBox . $localizationButtons;
310  }
311 
312  $title = $languageService->sL(trim($parameterArray['fieldConf']['label']));
313  $html .= '<div class="panel-group panel-hover" data-title="' . htmlspecialchars($title) . '" id="' . $nameObject . '_records">';
314 
315  $sortableRecordUids = [];
316  foreach ($this->data['parameterArray']['fieldConf']['children'] as $options) {
317  $options['inlineParentUid'] = $row['uid'];
318  $options['inlineFirstPid'] = $this->data['inlineFirstPid'];
319  // @todo: this can be removed if this container no longer sets additional info to $config
320  $options['inlineParentConfig'] = $config;
321  $options['inlineData'] = ‪$this->inlineData;
322  $options['inlineStructure'] = ‪$inlineStackProcessor->‪getStructure();
323  $options['inlineExpandCollapseStateArray'] = $this->data['inlineExpandCollapseStateArray'];
324  $options['renderType'] = 'inlineRecordContainer';
325  $childResult = $this->nodeFactory->create($options)->render();
326  $html .= $childResult['html'];
327  $resultArray = $this->‪mergeChildReturnIntoExistingResult($resultArray, $childResult, false);
328  if (!$options['isInlineDefaultLanguageRecordInLocalizedParentContext']) {
329  // Don't add record to list of "valid" uids if it is only the default
330  // language record of a not yet localized child
331  $sortableRecordUids[] = $options['databaseRow']['uid'];
332  }
333  }
334 
335  $html .= '</div>';
336 
337  $fieldWizardResult = $this->‪renderFieldWizard();
338  $fieldWizardHtml = $fieldWizardResult['html'];
339  $resultArray = $this->‪mergeChildReturnIntoExistingResult($resultArray, $fieldWizardResult, false);
340  $html .= $fieldWizardHtml;
341 
342  // Add the level buttons after all child records:
343  if (!$isReadOnly && ($config['appearance']['levelLinksPosition'] === 'both' || $config['appearance']['levelLinksPosition'] === 'bottom')) {
344  $html .= $newRecordButton . $localizationButtons;
345  }
346  if (is_array($config['customControls'])) {
347  $html .= '<div id="' . $nameObject . '_customControls">';
348  foreach ($config['customControls'] as $customControlConfig) {
349  if (!isset($customControlConfig['userFunc'])) {
350  throw new \RuntimeException('Support for customControl without a userFunc key in TCA type inline is not supported.', 1548052629);
351  }
352  $parameters = [
353  'table' => $table,
354  'field' => $field,
355  'row' => $row,
356  'nameObject' => $nameObject,
357  'nameForm' => $nameForm,
358  'config' => $config,
359  'customControlConfig' => $customControlConfig,
360  ];
361  $html .= GeneralUtility::callUserFunction($customControlConfig['userFunc'], $parameters, $this);
362  }
363  $html .= '</div>';
364  }
365  $resultArray['requireJsModules'] = array_merge($resultArray['requireJsModules'], $this->requireJsModules);
366  $resultArray['requireJsModules'][] = ['TYPO3/CMS/Backend/FormEngine/Container/InlineControlContainer' => '
367  function(InlineControlContainer) {
368  new InlineControlContainer(' . GeneralUtility::quoteJSvalue($nameObject) . ');
369  }'
370  ];
371 
372  // Publish the uids of the child records in the given order to the browser
373  $html .= '<input type="hidden" name="' . $nameForm . '" value="' . implode(',', $sortableRecordUids) . '" '
374  . ' data-formengine-validation-rules="' . htmlspecialchars($this->‪getValidationDataAsJsonString(['type' => 'inline', 'minitems' => $config['minitems'], 'maxitems' => $config['maxitems']])) . '"'
375  . ' class="inlineRecord" />';
376  // Close the wrap for all inline fields (container)
377  $html .= '</div>';
378 
379  $resultArray['html'] = $html;
380  return $resultArray;
381  }
382 
391  protected function ‪getLevelInteractionButton(string $type, array $conf = []): string
392  {
393  $languageService = $this->‪getLanguageService();
394  $attributes = [];
395  switch ($type) {
396  case 'newRecord':
397  $title = htmlspecialchars($languageService->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:cm.createnew'));
398  $icon = 'actions-add';
399  $className = 'typo3-newRecordLink t3js-inline-controls';
400  $attributes['class'] = 'btn btn-default t3js-create-new-button';
401  if (!empty($conf['inline']['inlineNewButtonStyle'])) {
402  $attributes['style'] = $conf['inline']['inlineNewButtonStyle'];
403  }
404  if (!empty($conf['appearance']['newRecordLinkAddTitle'])) {
405  $title = htmlspecialchars(sprintf(
406  $languageService->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:cm.createnew.link'),
407  $languageService->sL(‪$GLOBALS['TCA'][$conf['foreign_table']]['ctrl']['title'])
408  ));
409  } elseif (isset($conf['appearance']['newRecordLinkTitle']) && $conf['appearance']['newRecordLinkTitle'] !== '') {
410  $title = htmlspecialchars($languageService->sL($conf['appearance']['newRecordLinkTitle']));
411  }
412  break;
413  case 'localize':
414  $title = htmlspecialchars($languageService->sL('LLL:EXT:core/Resources/Private/Language/locallang_misc.xlf:localizeAllRecords'));
415  $icon = 'actions-document-localize';
416  $className = 'typo3-localizationLink';
417  $attributes['class'] = 'btn btn-default t3js-synchronizelocalize-button';
418  $attributes['data-type'] = 'localize';
419  break;
420  case 'synchronize':
421  $title = htmlspecialchars($languageService->sL('LLL:EXT:core/Resources/Private/Language/locallang_misc.xlf:synchronizeWithOriginalLanguage'));
422  $icon = 'actions-document-synchronize';
423  $className = 'typo3-synchronizationLink';
424  $attributes['class'] = 'btn btn-default inlineNewButton t3js-synchronizelocalize-button';
425  $attributes['data-type'] = 'synchronize';
426  break;
427  default:
428  $title = '';
429  $icon = '';
430  $className = '';
431  }
432  // Create the button:
433  $icon = $icon ? $this->iconFactory->getIcon($icon, ‪Icon::SIZE_SMALL)->render() : '';
434  $button = $this->‪wrapWithButton($icon . ' ' . $title, $attributes);
435  return '<div' . ($className ? ' class="' . $className . '"' : '') . 'title="' . $title . '">' . $button . '</div>';
436  }
437 
445  protected function ‪wrapWithButton(string $text, array $attributes = []): string
446  {
447  return '<button type="button" ' . GeneralUtility::implodeAttributes($attributes, true, true) . '>' . $text . '</button>';
448  }
449 
457  protected function ‪renderPossibleRecordsSelectorTypeGroupDB(array $inlineConfiguration)
458  {
459  $backendUser = $this->‪getBackendUserAuthentication();
460  $languageService = $this->‪getLanguageService();
461 
462  $groupFieldConfiguration = $inlineConfiguration['selectorOrUniqueConfiguration']['config'];
463 
464  $foreign_table = $inlineConfiguration['foreign_table'];
465  $allowed = $groupFieldConfiguration['allowed'];
466  $currentStructureDomObjectIdPrefix = $this->inlineStackProcessor->getCurrentStructureDomObjectIdPrefix($this->data['inlineFirstPid']);
467  $objectPrefix = $currentStructureDomObjectIdPrefix . '-' . $foreign_table;
468  $mode = 'db';
469  $showUpload = false;
470  $showByUrl = false;
471  $elementBrowserEnabled = true;
472  if (!empty($inlineConfiguration['appearance']['createNewRelationLinkTitle'])) {
473  $createNewRelationText = htmlspecialchars($languageService->sL($inlineConfiguration['appearance']['createNewRelationLinkTitle']));
474  } else {
475  $createNewRelationText = htmlspecialchars($languageService->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:cm.createNewRelation'));
476  }
477  if (is_array($groupFieldConfiguration['appearance'])) {
478  if (isset($groupFieldConfiguration['appearance']['elementBrowserType'])) {
479  $mode = $groupFieldConfiguration['appearance']['elementBrowserType'];
480  }
481  if ($mode === 'file') {
482  $showUpload = true;
483  $showByUrl = true;
484  }
485  if (isset($inlineConfiguration['appearance']['fileUploadAllowed'])) {
486  $showUpload = (bool)$inlineConfiguration['appearance']['fileUploadAllowed'];
487  }
488  if (isset($inlineConfiguration['appearance']['fileByUrlAllowed'])) {
489  $showByUrl = (bool)$inlineConfiguration['appearance']['fileByUrlAllowed'];
490  }
491  if (isset($groupFieldConfiguration['appearance']['elementBrowserAllowed'])) {
492  $allowed = $groupFieldConfiguration['appearance']['elementBrowserAllowed'];
493  }
494  if (isset($inlineConfiguration['appearance']['elementBrowserEnabled'])) {
495  $elementBrowserEnabled = (bool)$inlineConfiguration['appearance']['elementBrowserEnabled'];
496  }
497  }
498  // Remove any white-spaces from the allowed extension lists
499  $allowed = implode(',', ‪GeneralUtility::trimExplode(',', $allowed, true));
500  $browserParams = '|||' . $allowed . '|' . $objectPrefix;
501  $buttonStyle = '';
502  if (isset($inlineConfiguration['inline']['inlineNewRelationButtonStyle'])) {
503  $buttonStyle = ' style="' . $inlineConfiguration['inline']['inlineNewRelationButtonStyle'] . '"';
504  }
505  $item = '';
506  if ($elementBrowserEnabled) {
507  $item .= '
508  <button type="button" class="btn btn-default t3js-element-browser" data-mode="' . htmlspecialchars($mode) . '" data-params="' . htmlspecialchars($browserParams) . '"
509  ' . $buttonStyle . ' title="' . $createNewRelationText . '">
510  ' . $this->iconFactory->getIcon('actions-insert-record', ‪Icon::SIZE_SMALL)->render() . '
511  ' . $createNewRelationText . '
512  </button>';
513  }
514 
515  $isDirectFileUploadEnabled = (bool)$backendUser->uc['edit_docModuleUpload'];
516  $allowedArray = ‪GeneralUtility::trimExplode(',', $allowed, true);
518  if (!empty($allowedArray)) {
519  $onlineMediaAllowed = array_intersect($allowedArray, $onlineMediaAllowed);
520  }
521  if (($showUpload || $showByUrl) && $isDirectFileUploadEnabled) {
522  $folder = $backendUser->getDefaultUploadFolder(
523  $this->data['tableName'] === 'pages' ? $this->data['vanillaUid'] : $this->data['parentPageRow']['uid'],
524  $this->data['tableName'],
525  $this->data['fieldName']
526  );
527  if (
528  $folder instanceof Folder
529  && $folder->getStorage()->checkUserActionPermission('add', 'File')
530  ) {
531  if ($showUpload) {
532  $maxFileSize = GeneralUtility::getMaxUploadFileSize() * 1024;
533  $item .= ' <button type="button" class="btn btn-default t3js-drag-uploader inlineNewFileUploadButton"
534  ' . $buttonStyle . '
535  data-dropzone-target="#' . htmlspecialchars(‪StringUtility::escapeCssSelector($currentStructureDomObjectIdPrefix)) . '"
536  data-insert-dropzone-before="1"
537  data-file-irre-object="' . htmlspecialchars($objectPrefix) . '"
538  data-file-allowed="' . htmlspecialchars($allowed) . '"
539  data-target-folder="' . htmlspecialchars($folder->getCombinedIdentifier()) . '"
540  data-max-file-size="' . htmlspecialchars((string)$maxFileSize) . '"
541  >';
542  $item .= $this->iconFactory->getIcon('actions-upload', ‪Icon::SIZE_SMALL)->render() . ' ';
543  $item .= htmlspecialchars($languageService->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:file_upload.select-and-submit'));
544  $item .= '</button>';
545 
546  $this->requireJsModules[] = ['TYPO3/CMS/Backend/DragUploader' => 'function(dragUploader){dragUploader.initialize()}'];
547  }
548  if (!empty($onlineMediaAllowed) && $showByUrl) {
549  $buttonStyle = '';
550  if (isset($inlineConfiguration['inline']['inlineOnlineMediaAddButtonStyle'])) {
551  $buttonStyle = ' style="' . $inlineConfiguration['inline']['inlineOnlineMediaAddButtonStyle'] . '"';
552  }
553  $this->requireJsModules[] = 'TYPO3/CMS/Backend/OnlineMedia';
554  $buttonText = htmlspecialchars($languageService->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:online_media.new_media.button'));
555  $placeholder = htmlspecialchars($languageService->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:online_media.new_media.placeholder'));
556  $buttonSubmit = htmlspecialchars($languageService->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:online_media.new_media.submit'));
557  $allowedMediaUrl = htmlspecialchars($languageService->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:cm.allowEmbedSources'));
558  $item .= '
559  <button type="button" class="btn btn-default t3js-online-media-add-btn"
560  ' . $buttonStyle . '
561  data-file-irre-object="' . htmlspecialchars($objectPrefix) . '"
562  data-online-media-allowed="' . htmlspecialchars(implode(',', $onlineMediaAllowed)) . '"
563  data-online-media-allowed-help-text="' . $allowedMediaUrl . '"
564  data-target-folder="' . htmlspecialchars($folder->getCombinedIdentifier()) . '"
565  title="' . $buttonText . '"
566  data-btn-submit="' . $buttonSubmit . '"
567  data-placeholder="' . $placeholder . '"
568  >
569  ' . $this->iconFactory->getIcon('actions-online-media-add', ‪Icon::SIZE_SMALL)->render() . '
570  ' . $buttonText . '</button>';
571  }
572  }
573  }
574 
575  $item = '<div class="form-control-wrap t3js-inline-controls">' . $item . '</div>';
576  $allowedList = '';
577  $allowedLabelKey = ($mode === 'file') ? 'allowedFileExtensions' : 'allowedRelations';
578  $allowedLabel = htmlspecialchars($languageService->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:cm.' . $allowedLabelKey));
579  foreach ($allowedArray as $allowedItem) {
580  $allowedList .= '<span class="label label-success">' . strtoupper($allowedItem) . '</span> ';
581  }
582  if (!empty($allowedList)) {
583  $item .= '<div class="help-block">' . $allowedLabel . '<br>' . $allowedList . '</div>';
584  }
585  $item = '<div class="form-group t3js-formengine-validation-marker">' . $item . '</div>';
586  return $item;
587  }
588 
597  protected function ‪renderPossibleRecordsSelectorTypeSelect(array $config, array $uniqueIds)
598  {
599  $possibleRecords = $config['selectorOrUniquePossibleRecords'];
600  $nameObject = $this->inlineStackProcessor->getCurrentStructureDomObjectIdPrefix($this->data['inlineFirstPid']);
601  // Create option tags:
602  $opt = [];
603  foreach ($possibleRecords as $p) {
604  if (!in_array($p[1], $uniqueIds)) {
605  $opt[] = '<option value="' . htmlspecialchars($p[1]) . '">' . htmlspecialchars($p[0]) . '</option>';
606  }
607  }
608  // Put together the selector box:
609  $size = (int)$config['size'];
610  $size = $config['autoSizeMax'] ? ‪MathUtility::forceIntegerInRange(count($possibleRecords) + 1, ‪MathUtility::forceIntegerInRange($size, 1), $config['autoSizeMax']) : $size;
611  $item = '
612  <select id="' . $nameObject . '-' . $config['foreign_table'] . '_selector" class="form-control t3js-create-new-selector"' . ($size ? ' size="' . $size . '"' : '') . '>
613  ' . implode('', $opt) . '
614  </select>';
615 
616  if ($size <= 1) {
617  // Add a "Create new relation" button for adding new relations
618  // This is necessary, if the size of the selector is "1" or if
619  // there is only one record item in the select-box, that is selected by default
620  // The selector-box creates a new relation on using an onChange event (see some line above)
621  if (!empty($config['appearance']['createNewRelationLinkTitle'])) {
622  $createNewRelationText = htmlspecialchars($this->‪getLanguageService()->sL($config['appearance']['createNewRelationLinkTitle']));
623  } else {
624  $createNewRelationText = htmlspecialchars($this->‪getLanguageService()->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:cm.createNewRelation'));
625  }
626  $item .= '
627  <span class="input-group-btn">
628  <button type="button" class="btn btn-default t3js-create-new-button" title="' . $createNewRelationText . '">
629  ' . $this->iconFactory->getIcon('actions-add', ‪Icon::SIZE_SMALL)->render() . $createNewRelationText . '
630  </button>
631  </span>';
632  } else {
633  $item .= '
634  <span class="input-group-btn btn"></span>';
635  }
636 
637  // Wrap the selector and add a spacer to the bottom
638  $item = '<div class="input-group form-group t3js-formengine-validation-marker">' . $item . '</div>';
639  return $item;
640  }
641 
650  protected function ‪extractFlexFormParts($formElementName)
651  {
652  $flexFormParts = null;
653  $matches = [];
654  if (preg_match('#^data(?:\[[^]]+\]){3}(\[data\](?:\[[^]]+\]){4,})$#', $formElementName, $matches)) {
655  $flexFormParts = ‪GeneralUtility::trimExplode(
656  '][',
657  trim($matches[1], '[]')
658  );
659  }
660  return $flexFormParts;
661  }
662 
666  protected function ‪getBackendUserAuthentication()
667  {
668  return ‪$GLOBALS['BE_USER'];
669  }
670 
674  protected function ‪getLanguageService()
675  {
676  return ‪$GLOBALS['LANG'];
677  }
678 }
‪TYPO3\CMS\Core\Imaging\Icon\SIZE_SMALL
‪const SIZE_SMALL
Definition: Icon.php:30
‪TYPO3\CMS\Backend\Form\Container\InlineControlContainer\getLevelInteractionButton
‪string getLevelInteractionButton(string $type, array $conf=[])
Definition: InlineControlContainer.php:385
‪TYPO3\CMS\Backend\Form\Container\InlineControlContainer\render
‪array render()
Definition: InlineControlContainer.php:97
‪TYPO3\CMS\Core\Resource\OnlineMedia\Helpers\OnlineMediaHelperRegistry
Definition: OnlineMediaHelperRegistry.php:27
‪TYPO3\CMS\Backend\Form\Container\InlineControlContainer\extractFlexFormParts
‪array null extractFlexFormParts($formElementName)
Definition: InlineControlContainer.php:644
‪TYPO3\CMS\Core\Utility\MathUtility\canBeInterpretedAsInteger
‪static bool canBeInterpretedAsInteger($var)
Definition: MathUtility.php:74
‪TYPO3\CMS\Backend\Form\AbstractNode\mergeChildReturnIntoExistingResult
‪array mergeChildReturnIntoExistingResult(array $existing, array $childReturn, bool $mergeHtml=true)
Definition: AbstractNode.php:116
‪TYPO3\CMS\Backend\Form\AbstractNode\initializeResultArray
‪array initializeResultArray()
Definition: AbstractNode.php:90
‪TYPO3\CMS\Backend\Form\Container\InlineControlContainer\$iconFactory
‪IconFactory $iconFactory
Definition: InlineControlContainer.php:56
‪TYPO3\CMS\Backend\Form\InlineStackProcessor\getStructure
‪array getStructure()
Definition: InlineStackProcessor.php:127
‪TYPO3\CMS\Core\Imaging\Icon
Definition: Icon.php:26
‪TYPO3\CMS\Backend\Form\Container\InlineControlContainer\wrapWithButton
‪string wrapWithButton(string $text, array $attributes=[])
Definition: InlineControlContainer.php:439
‪TYPO3\CMS\Backend\Form\Container\InlineControlContainer\__construct
‪__construct(NodeFactory $nodeFactory, array $data)
Definition: InlineControlContainer.php:86
‪TYPO3\CMS\Backend\Form\Container\InlineControlContainer
Definition: InlineControlContainer.php:43
‪TYPO3\CMS\Backend\Form\AbstractNode\$nodeFactory
‪NodeFactory $nodeFactory
Definition: AbstractNode.php:36
‪TYPO3\CMS\Core\Utility\MathUtility\forceIntegerInRange
‪static int forceIntegerInRange($theInt, $min, $max=2000000000, $defaultValue=0)
Definition: MathUtility.php:32
‪TYPO3\CMS\Backend\Form\Container
Definition: AbstractContainer.php:16
‪TYPO3\CMS\Backend\Form\Container\InlineControlContainer\renderPossibleRecordsSelectorTypeSelect
‪string renderPossibleRecordsSelectorTypeSelect(array $config, array $uniqueIds)
Definition: InlineControlContainer.php:591
‪TYPO3\CMS\Core\Imaging\IconFactory
Definition: IconFactory.php:33
‪TYPO3\CMS\Backend\Form\InlineStackProcessor\getCurrentStructureDomObjectIdPrefix
‪string getCurrentStructureDomObjectIdPrefix($inlineFirstPid)
Definition: InlineStackProcessor.php:164
‪TYPO3\CMS\Core\Resource\OnlineMedia\Helpers\OnlineMediaHelperRegistry\getSupportedFileExtensions
‪string[] getSupportedFileExtensions()
Definition: OnlineMediaHelperRegistry.php:94
‪TYPO3\CMS\Backend\Form\Container\InlineControlContainer\$defaultFieldInformation
‪array $defaultFieldInformation
Definition: InlineControlContainer.php:66
‪TYPO3\CMS\Backend\Form\InlineStackProcessor\initializeByGivenStructure
‪initializeByGivenStructure(array $structure=[])
Definition: InlineStackProcessor.php:42
‪TYPO3\CMS\Core\Utility\StringUtility\escapeCssSelector
‪static string escapeCssSelector(string $selector)
Definition: StringUtility.php:107
‪TYPO3\CMS\Core\Resource\Folder\getStorage
‪ResourceStorage getStorage()
Definition: Folder.php:149
‪TYPO3\CMS\Backend\Form\Container\InlineControlContainer\getBackendUserAuthentication
‪BackendUserAuthentication getBackendUserAuthentication()
Definition: InlineControlContainer.php:660
‪TYPO3\CMS\Backend\Form\AbstractNode\getValidationDataAsJsonString
‪string getValidationDataAsJsonString(array $config)
Definition: AbstractNode.php:151
‪TYPO3\CMS\Backend\Form\AbstractNode\$data
‪array $data
Definition: AbstractNode.php:42
‪TYPO3\CMS\Core\Resource\Folder
Definition: Folder.php:37
‪TYPO3\CMS\Backend\Form\Container\AbstractContainer
Definition: AbstractContainer.php:28
‪TYPO3\CMS\Backend\Form\Container\InlineControlContainer\$requireJsModules
‪string[] $requireJsModules
Definition: InlineControlContainer.php:60
‪TYPO3\CMS\Core\Authentication\BackendUserAuthentication
Definition: BackendUserAuthentication.php:62
‪TYPO3\CMS\Backend\Form\InlineStackProcessor\getStructureLevel
‪array getStructureLevel($level)
Definition: InlineStackProcessor.php:184
‪TYPO3\CMS\Backend\Utility\BackendUtility
Definition: BackendUtility.php:75
‪TYPO3\CMS\Backend\Form\Container\AbstractContainer\renderFieldInformation
‪array renderFieldInformation()
Definition: AbstractContainer.php:34
‪TYPO3\CMS\Core\Utility\GeneralUtility\trimExplode
‪static string[] trimExplode($delim, $string, $removeEmptyValues=false, $limit=0)
Definition: GeneralUtility.php:1059
‪TYPO3\CMS\Backend\Form\NodeFactory
Definition: NodeFactory.php:37
‪TYPO3\CMS\Core\Resource\OnlineMedia\Helpers\OnlineMediaHelperRegistry\getInstance
‪static OnlineMediaHelperRegistry getInstance()
Definition: OnlineMediaHelperRegistry.php:33
‪TYPO3\CMS\Backend\Form\InlineStackProcessor\pushStableStructureItem
‪pushStableStructureItem(array $structureItem=[])
Definition: InlineStackProcessor.php:137
‪TYPO3\CMS\Backend\Form\InlineStackProcessor\getCurrentStructureFormPrefix
‪string getCurrentStructureFormPrefix()
Definition: InlineStackProcessor.php:147
‪TYPO3\CMS\Backend\Form\Container\InlineControlContainer\renderPossibleRecordsSelectorTypeGroupDB
‪string renderPossibleRecordsSelectorTypeGroupDB(array $inlineConfiguration)
Definition: InlineControlContainer.php:451
‪TYPO3\CMS\Backend\Utility\BackendUtility\isTableLocalizable
‪static bool isTableLocalizable($table)
Definition: BackendUtility.php:578
‪$GLOBALS
‪$GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['adminpanel']['modules']
Definition: ext_localconf.php:5
‪TYPO3\CMS\Backend\Form\Container\InlineControlContainer\$inlineData
‪array $inlineData
Definition: InlineControlContainer.php:48
‪TYPO3\CMS\Core\Utility\MathUtility
Definition: MathUtility.php:22
‪TYPO3\CMS\Core\Localization\LanguageService
Definition: LanguageService.php:42
‪TYPO3\CMS\Backend\Form\Container\InlineControlContainer\$defaultFieldWizard
‪array $defaultFieldWizard
Definition: InlineControlContainer.php:74
‪TYPO3\CMS\Backend\Form\InlineStackProcessor
Definition: InlineStackProcessor.php:30
‪TYPO3\CMS\Backend\Form\Container\InlineControlContainer\getLanguageService
‪LanguageService getLanguageService()
Definition: InlineControlContainer.php:668
‪TYPO3\CMS\Core\Utility\GeneralUtility
Definition: GeneralUtility.php:46
‪TYPO3\CMS\Core\Utility\StringUtility
Definition: StringUtility.php:22
‪TYPO3\CMS\Backend\Form\Container\AbstractContainer\renderFieldWizard
‪array renderFieldWizard()
Definition: AbstractContainer.php:68
‪TYPO3\CMS\Core\Resource\ResourceStorage\checkUserActionPermission
‪bool checkUserActionPermission($action, $type)
Definition: ResourceStorage.php:666
‪TYPO3\CMS\Backend\Form\Container\InlineControlContainer\$inlineStackProcessor
‪InlineStackProcessor $inlineStackProcessor
Definition: InlineControlContainer.php:52