2 namespace TYPO3\CMS\Backend\Form\Container;
18 use TYPO3\CMS\Backend\Form\Exception\AccessDeniedContentEditException;
78 parent::__construct($nodeFactory, $data);
93 $this->inlineData =
$data[
'inlineData'];
98 $record =
$data[
'databaseRow'];
99 $inlineConfig =
$data[
'inlineParentConfig'];
100 $foreignTable = $inlineConfig[
'foreign_table'];
108 $this->inlineData[
'map'][$formPrefix] = $domObjectId;
113 if (!empty($inlineConfig[
'foreign_selector'])) {
114 $foreign_selector = $inlineConfig[
'foreign_selector'];
115 $valueToNormalize = $record[$foreign_selector];
116 if (is_array($record[$foreign_selector])) {
118 $valueToNormalize = implode(
',', $record[$foreign_selector]);
120 $record[$foreign_selector] = $this->
normalizeUid($valueToNormalize);
124 $appendFormFieldNames =
'[' . $foreignTable .
'][' . $record[
'uid'] .
']';
125 $objectId = $domObjectId .
'-' . $foreignTable .
'-' . $record[
'uid'];
128 $combinationHtml =
'';
129 $isNewRecord =
$data[
'command'] ===
'new';
130 if (!
$data[
'isInlineDefaultLanguageRecordInLocalizedParentContext']) {
131 $collapseAll = isset($inlineConfig[
'appearance'][
'collapseAll']) && $inlineConfig[
'appearance'][
'collapseAll'];
132 $expandAll = isset($inlineConfig[
'appearance'][
'collapseAll']) && !$inlineConfig[
'appearance'][
'collapseAll'];
134 $isExpanded = $expandAll || !$collapseAll;
136 $expandCollapseStateArray =
$data[
'inlineExpandCollapseStateArray'];
137 $isExpandedByUcState = isset($expandCollapseStateArray[$foreignTable])
138 && is_array($expandCollapseStateArray[$foreignTable])
139 && in_array($record[
'uid'], $expandCollapseStateArray[$foreignTable]) !==
false;
140 $isExpanded = $inlineConfig[
'renderFieldsOnly'] || !$collapseAll && $isExpandedByUcState || $expandAll;
143 if ($isNewRecord || $isExpanded) {
145 $combinationHtml =
'';
146 if (isset(
$data[
'combinationChild'])) {
148 $combinationHtml = $combinationChild[
'html'];
149 $combinationChild[
'html'] =
'';
153 $html = $childArray[
'html'];
154 $childArray[
'html'] =
'';
158 $html =
'<!--notloaded-->';
162 $html .=
'<input type="hidden" name="data' . $appendFormFieldNames .
'[pid]" value="' . $record[
'pid'] .
'"/>';
164 $ucFieldName =
'uc[inlineView]'
165 .
'[' .
$data[
'inlineTopMostParentTableName'] .
']'
166 .
'[' .
$data[
'inlineTopMostParentUid'] .
']'
167 . $appendFormFieldNames;
168 $html .=
'<input type="hidden" name="' . $ucFieldName .
'" value="' . $isExpanded .
'" />';
171 $html .=
'<input type="hidden" name="cmd' . $appendFormFieldNames .
'[delete]" value="1" disabled="disabled" />';
173 && !empty(
$GLOBALS[
'TCA'][$foreignTable][
'ctrl'][
'enablecolumns'][
'disabled'])
175 $checked = !empty($record[
'hidden']) ?
' checked="checked"' :
'';
176 $html .=
'<input type="checkbox" name="data' . $appendFormFieldNames .
'[hidden]_0" value="1"' . $checked .
' />';
177 $html .=
'<input type="input" name="data' . $appendFormFieldNames .
'[hidden]" value="' . $record[
'hidden'] .
'" />';
181 $class = $isExpanded ?
'panel-visible' :
'panel-collapsed';
183 if ($inlineConfig[
'renderFieldsOnly']) {
185 $html = $html . $combinationHtml;
188 if (
$data[
'isInlineDefaultLanguageRecordInLocalizedParentContext']) {
189 $class .=
' t3-form-field-container-inline-placeHolder';
191 if (isset($record[
'hidden']) && (
int)$record[
'hidden']) {
192 $class .=
' t3-form-field-container-inline-hidden';
194 $class .= ($isNewRecord ?
' inlineIsNewRecord' :
'');
196 <div class="panel panel-default panel-condensed ' . trim($class) .
'" id="' . $objectId .
'_div">
197 <div class="panel-heading" data-toggle="formengine-inline" id="' . $objectId .
'_header" data-expandSingle="' . ($inlineConfig[
'appearance'][
'expandSingle'] ? 1 : 0) .
'">
198 <div class="form-irre-header">
199 <div class="form-irre-header-cell form-irre-header-icon">
200 <span class="caret"></span>
205 <div class="panel-collapse" id="' . $objectId .
'_fields" data-expandSingle="' . ($inlineConfig[
'appearance'][
'expandSingle'] ? 1 : 0) .
'" data-returnURL="' . htmlspecialchars(
GeneralUtility::getIndpEnv(
'REQUEST_URI')) .
'">' . $html . $combinationHtml .
'</div>
209 $resultArray[
'html'] = $html;
221 $domObjectId = $this->inlineStackProcessor->getCurrentStructureDomObjectIdPrefix($data[
'inlineFirstPid']);
222 $data[
'tabAndInlineStack'][] = [
224 $domObjectId .
'-' . $data[
'tableName'] .
'-' . $data[
'databaseRow'][
'uid'],
228 $data[
'renderType'] =
'fullRecordContainer';
229 return $this->nodeFactory->create($data)->render();
245 $childData = $data[
'combinationChild'];
246 $parentConfig = $data[
'inlineParentConfig'];
251 if (!isset($parentConfig[
'appearance'][
'suppressCombinationWarning']) || empty($parentConfig[
'appearance'][
'suppressCombinationWarning'])) {
252 $combinationWarningMessage =
'LLL:EXT:lang/locallang_core.xlf:warning.inline_use_combination';
253 if (!empty($parentConfig[
'appearance'][
'overwriteCombinationWarningMessage'])) {
254 $combinationWarningMessage = $parentConfig[
'appearance'][
'overwriteCombinationWarningMessage'];
262 $resultArray[
'html'] = $flashMessage->render();
269 if ($childData[
'command'] ===
'new') {
270 $comboFormFieldName =
'data[' . $childData[
'tableName'] .
'][' . $childData[
'databaseRow'][
'uid'] .
'][pid]';
271 $resultArray[
'html'] .=
'<input type="hidden" name="' . htmlspecialchars($comboFormFieldName) .
'" value="' . htmlspecialchars($childData[
'databaseRow'][
'pid']) .
'" />';
274 if ($childData[
'command'] ===
'new' || $parentConfig[
'foreign_unique'] === $parentConfig[
'foreign_selector']) {
275 $parentFormFieldName =
'data' . $appendFormFieldNames .
'[' . $parentConfig[
'foreign_selector'] .
']';
276 $resultArray[
'html'] .=
'<input type="hidden" name="' . htmlspecialchars($parentFormFieldName) .
'" value="' . htmlspecialchars($childData[
'databaseRow'][
'uid']) .
'" />';
293 $inlineConfig = $data[
'inlineParentConfig'];
294 $foreignTable = $inlineConfig[
'foreign_table'];
295 $rec = $data[
'databaseRow'];
297 $domObjectId = $this->inlineStackProcessor->getCurrentStructureDomObjectIdPrefix($data[
'inlineFirstPid']);
298 $objectId = $domObjectId .
'-' . $foreignTable .
'-' . $rec[
'uid'];
300 $recordTitle = $data[
'recordTitle'];
301 if (empty($recordTitle)) {
302 $recordTitle =
'<em>[' . $languageService->sL(
'LLL:EXT:lang/locallang_core.xlf:labels.no_title',
true) .
']</em>';
307 $iconImg =
'<span title="' . $altText .
'" id="' . htmlspecialchars($objectId) .
'_icon' .
'">' . $this->iconFactory->getIconForRecord($foreignTable, $rec,
Icon::SIZE_SMALL)->render() .
'</span>';
308 $label =
'<span id="' . $objectId .
'_label">' . $recordTitle .
'</span>';
309 $ctrl = $this->renderForeignRecordHeaderControl($data);
313 if (!empty($inlineConfig[
'appearance'][
'headerThumbnail'][
'field'])) {
314 $fieldValue = $rec[$inlineConfig[
'appearance'][
'headerThumbnail'][
'field']];
318 if (!empty($fileUid)) {
321 }
catch (\InvalidArgumentException $e) {
324 if ($fileObject && $fileObject->isMissing()) {
325 $flashMessage = \TYPO3\CMS\Core\Resource\Utility\BackendUtility::getFlashMessageForMissingFile($fileObject);
326 $thumbnail = $flashMessage->render();
328 $imageSetup = $inlineConfig[
'appearance'][
'headerThumbnail'];
329 unset($imageSetup[
'field']);
330 if (!empty($rec[
'crop'])) {
331 $imageSetup[
'crop'] = $rec[
'crop'];
333 $imageSetup = array_merge(array(
'width' =>
'45',
'height' =>
'45c'), $imageSetup);
336 if ($processedImage->getProperty(
'width')) {
337 $imageUrl = $processedImage->getPublicUrl(
true);
338 $thumbnail =
'<img src="' . $imageUrl .
'" ' .
339 'width="' . $processedImage->getProperty(
'width') .
'" ' .
340 'height="' . $processedImage->getProperty(
'height') .
'" ' .
341 'alt="' . htmlspecialchars($altText) .
'" ' .
342 'title="' . htmlspecialchars($altText) .
'">';
348 if (!empty($inlineConfig[
'appearance'][
'headerThumbnail'][
'field']) && $thumbnail) {
349 $mediaContainer =
'<div class="form-irre-header-cell form-irre-header-thumbnail" id="' . $objectId .
'_thumbnailcontainer">' . $thumbnail .
'</div>';
351 $mediaContainer =
'<div class="form-irre-header-cell form-irre-header-icon" id="' . $objectId .
'_iconcontainer">' . $iconImg .
'</div>';
353 $header = $mediaContainer .
'
354 <div class="form-irre-header-cell form-irre-header-body">' . $label .
'</div>
355 <div class="form-irre-header-cell form-irre-header-control t3js-formengine-irre-control">' . $ctrl .
'</div>';
368 protected function renderForeignRecordHeaderControl(array
$data)
370 $rec = $data[
'databaseRow'];
371 $inlineConfig = $data[
'inlineParentConfig'];
372 $foreignTable = $inlineConfig[
'foreign_table'];
377 $additionalCells = array();
378 $isNewItem = substr($rec[
'uid'], 0, 3) ==
'NEW';
380 $tcaTableCtrl = &
$GLOBALS[
'TCA'][$foreignTable][
'ctrl'];
381 $tcaTableCols = &
$GLOBALS[
'TCA'][$foreignTable][
'columns'];
382 $isPagesTable = $foreignTable ===
'pages';
383 $isSysFileReferenceTable = $foreignTable ===
'sys_file_reference';
384 $enableManualSorting = $tcaTableCtrl[
'sortby'] || $inlineConfig[
'MM'] || !$data[
'isOnSymmetricSide']
385 && $inlineConfig[
'foreign_sortby'] || $data[
'isOnSymmetricSide'] && $inlineConfig[
'symmetric_sortby'];
386 $nameObject = $this->inlineStackProcessor->getCurrentStructureDomObjectIdPrefix($data[
'inlineFirstPid']);
387 $nameObjectFt = $nameObject .
'-' . $foreignTable;
388 $nameObjectFtId = $nameObjectFt .
'-' . $rec[
'uid'];
391 $localCalcPerms =
false;
398 $enabledControls = $inlineConfig[
'appearance'][
'enabledControls'];
400 foreach ($this->hookObjects as $hookObj) {
402 $hookObj->renderForeignRecordHeaderControl_preProcess($data[
'inlineParentUid'], $foreignTable, $rec, $inlineConfig, $data[
'isInlineDefaultLanguageRecordInLocalizedParentContext'], $enabledControls);
404 if ($data[
'isInlineDefaultLanguageRecordInLocalizedParentContext']) {
405 $cells[
'localize.isLocalizable'] =
'<span title="' . $languageService->sL(
'LLL:EXT:lang/locallang_misc.xlf:localize.isLocalizable',
true) .
'">'
406 . $this->iconFactory->getIcon(
'actions-edit-localize-status-low',
Icon::SIZE_SMALL)->render()
410 if ($enabledControls[
'info'] && !$isNewItem) {
411 if ($rec[
'table_local'] ===
'sys_file') {
412 $uid = (int)substr($rec[
'uid_local'], 9);
416 $table = $foreignTable;
419 <a class="btn btn-default" href="#" onclick="' . htmlspecialchars((
'top.launchView(' .
GeneralUtility::quoteJSvalue($table) .
', ' .
GeneralUtility::quoteJSvalue($uid) .
'); return false;')) .
'" title="' . $languageService->sL(
'LLL:EXT:lang/locallang_mod_web_list.xlf:showInfo',
true) .
'">
420 ' . $this->iconFactory->getIcon(
'actions-document-info',
Icon::SIZE_SMALL)->render() .
'
424 if (!$tcaTableCtrl[
'readOnly'] && !$data[
'isInlineDefaultLanguageRecordInLocalizedParentContext']) {
426 if ($enabledControls[
'new'] && ($enableManualSorting || $tcaTableCtrl[
'useColumnsForDefaultValues'])) {
427 if (!$isPagesTable && $calcPerms & Permission::CONTENT_EDIT || $isPagesTable && $calcPerms &
Permission::PAGE_NEW) {
430 if ($inlineConfig[
'inline'][
'inlineNewButtonStyle']) {
431 $style =
' style="' . $inlineConfig[
'inline'][
'inlineNewButtonStyle'] .
'"';
434 <a class="btn btn-default inlineNewButton ' . $this->inlineData[
'config'][$nameObject][
'md5'] .
'" href="#" onclick="' . htmlspecialchars($onClick) .
'" title="' . $languageService->sL((
'LLL:EXT:lang/locallang_mod_web_list.xlf:new' . ($isPagesTable ?
'Page' :
'Record')),
true) .
'" ' . $style .
'>
435 ' . $this->iconFactory->getIcon(
'actions-' . ($isPagesTable ?
'page' :
'document') .
'-new',
Icon::SIZE_SMALL)->render() .
'
440 if ($enabledControls[
'sort'] && $permsEdit && $enableManualSorting) {
443 $style = $inlineConfig[
'inline'][
'first'] == $rec[
'uid'] ?
'style="visibility: hidden;"' :
'';
444 $cells[
'sort.up'] =
'
445 <a class="btn btn-default sortingUp" href="#" onclick="' . htmlspecialchars($onClick) .
'" ' . $style .
' title="' . $languageService->sL(
'LLL:EXT:lang/locallang_mod_web_list.xlf:moveUp',
true) .
'">
446 ' . $this->iconFactory->getIcon(
'actions-move-up',
Icon::SIZE_SMALL)->render() .
'
450 $style = $inlineConfig[
'inline'][
'last'] == $rec[
'uid'] ?
'style="visibility: hidden;"' :
'';
451 $cells[
'sort.down'] =
'
452 <a class="btn btn-default sortingDown" href="#" onclick="' . htmlspecialchars($onClick) .
'" ' . $style .
' title="' . $languageService->sL(
'LLL:EXT:lang/locallang_mod_web_list.xlf:moveDown',
true) .
'">
453 ' . $this->iconFactory->getIcon(
'actions-move-down',
Icon::SIZE_SMALL)->render() .
'
457 if (($rec[
'table_local'] ===
'sys_file') && !$isNewItem) {
458 $sys_language_uid = 0;
459 if (!empty($rec[
'sys_language_uid'])) {
460 $sys_language_uid = $rec[
'sys_language_uid'][0];
465 'file = ' . (
int)substr($rec[
'uid_local'], 9) .
' AND sys_language_uid = ' . $sys_language_uid
467 if ($backendUser->check(
'tables_modify',
'sys_file_metadata')) {
468 $url = BackendUtility::getModuleUrl(
'record_edit', array(
469 'edit[sys_file_metadata][' . (
int)$recordInDatabase[
'uid'] .
']' =>
'edit'
471 $editOnClick =
'if (top.content.list_frame) {' .
472 'top.content.list_frame.location.href=' .
474 '+top.rawurlencode(top.content.list_frame.document.location.pathname+top.content.list_frame.document.location.search)' .
477 $title = $languageService->sL(
'LLL:EXT:lang/locallang_core.xlf:cm.editMetadata');
478 $cells[
'editmetadata'] =
'
479 <a class="btn btn-default" href="#" class="btn" onclick="' . htmlspecialchars($editOnClick) .
'" title="' . htmlspecialchars($title) .
'">
480 ' . $this->iconFactory->getIcon(
'actions-document-open',
Icon::SIZE_SMALL)->render() .
'
486 || !$isPagesTable && $calcPerms & Permission::CONTENT_EDIT
489 $title = $languageService->sL(
'LLL:EXT:lang/locallang_mod_web_list.xlf:delete',
true);
490 $icon = $this->iconFactory->getIcon(
'actions-edit-delete',
Icon::SIZE_SMALL)->render();
491 $cells[
'delete'] =
'<a href="#" class="btn btn-default t3js-editform-delete-inline-record" data-objectid="' . htmlspecialchars($nameObjectFtId) .
'" title="' . $title .
'">' . $icon .
'</a>';
495 $hiddenField = $tcaTableCtrl[
'enablecolumns'][
'disabled'];
496 if ($enabledControls[
'hide'] && $permsEdit && $hiddenField && $tcaTableCols[$hiddenField] && (!$tcaTableCols[$hiddenField][
'exclude'] || $backendUser->check(
'non_exclude_fields', $foreignTable .
':' . $hiddenField))) {
498 $className =
't3js-' . $nameObjectFtId .
'_disabled';
499 if ($rec[$hiddenField]) {
500 $title = $languageService->sL((
'LLL:EXT:lang/locallang_mod_web_list.xlf:unHide' . ($isPagesTable ?
'Page' :
'')),
true);
501 $cells[
'hide.unhide'] =
'
502 <a class="btn btn-default hiddenHandle ' . $className .
'" href="#" onclick="'
503 . htmlspecialchars($onClick) .
'"' .
'title="' . $title .
'">' .
504 $this->iconFactory->getIcon(
'actions-edit-unhide',
Icon::SIZE_SMALL)->render() .
'
507 $title = $languageService->sL((
'LLL:EXT:lang/locallang_mod_web_list.xlf:hide' . ($isPagesTable ?
'Page' :
'')),
true);
508 $cells[
'hide.hide'] =
'
509 <a class="btn btn-default hiddenHandle ' . $className .
'" href="#" onclick="'
510 . htmlspecialchars($onClick) .
'"' .
'title="' . $title .
'">' .
511 $this->iconFactory->getIcon(
'actions-edit-hide',
Icon::SIZE_SMALL)->render() .
'
516 if ($enabledControls[
'dragdrop'] && $permsEdit && $enableManualSorting && $inlineConfig[
'appearance'][
'useSortable']) {
517 $additionalCells[
'dragdrop'] =
'
518 <span class="btn btn-default sortableHandle" data-id="' . htmlspecialchars($rec[
'uid']) .
'" title="' . $languageService->sL(
'LLL:EXT:lang/locallang_core.xlf:labels.move',
true) .
'">
519 ' . $this->iconFactory->getIcon(
'actions-move-move',
Icon::SIZE_SMALL)->render() .
'
522 }
elseif ($data[
'isInlineDefaultLanguageRecordInLocalizedParentContext'] && $isParentExisting) {
523 if ($enabledControls[
'localize'] && $data[
'isInlineDefaultLanguageRecordInLocalizedParentContext']) {
525 $cells[
'localize'] =
'
526 <a class="btn btn-default" href="#" onclick="' . htmlspecialchars($onClick) .
'" title="' . $languageService->sL(
'LLL:EXT:lang/locallang_misc.xlf:localize',
true) .
'">
527 ' . $this->iconFactory->getIcon(
'actions-document-localize',
Icon::SIZE_SMALL)->render() .
'
535 ' .
'<span title="' . htmlspecialchars($lockInfo[
'msg']) .
'">' . $this->iconFactory->getIcon(
'status-warning-in-use',
Icon::SIZE_SMALL)->render() .
'</span>' .
'
539 foreach ($this->hookObjects as $hookObj) {
540 $hookObj->renderForeignRecordHeaderControl_postProcess($data[
'inlineParentUid'], $foreignTable, $rec, $inlineConfig, $data[
'isInlineDefaultLanguageRecordInLocalizedParentContext'], $cells);
544 if (!empty($cells)) {
545 $out .=
' <div class="btn-group btn-group-sm" role="group">' . implode(
'', $cells) .
'</div>';
547 if (!empty($additionalCells)) {
548 $out .=
' <div class="btn-group btn-group-sm" role="group">' . implode(
'', $additionalCells) .
'</div>';
561 $parts = explode(
'|', $string);
575 $this->hookObjects = array();
576 if (isset(
$GLOBALS[
'TYPO3_CONF_VARS'][
'SC_OPTIONS'][
't3lib/class.t3lib_tceforms_inline.php'][
'tceformsInlineHook'])) {
577 $tceformsInlineHook = &
$GLOBALS[
'TYPO3_CONF_VARS'][
'SC_OPTIONS'][
't3lib/class.t3lib_tceforms_inline.php'][
'tceformsInlineHook'];
578 if (is_array($tceformsInlineHook)) {
579 foreach ($tceformsInlineHook as $classData) {
582 throw new \UnexpectedValueException(
'$processObject must implement interface ' . InlineElementHookInterface::class, 1202072000);
584 $this->hookObjects[] = $processObject;