/** * Render the control-icons for a record header (create new, sorting, delete, disable/enable). * Most of the parts are copy&paste from TYPO3\CMS\Recordlist\RecordList\DatabaseRecordList and * modified for the JavaScript calls here * * @param string $parentUid The uid of the parent (embedding) record (uid or NEW...) * @param string $foreign_table The table (foreign_table) we create control-icons for * @param array $data Current data * @param array $config (modified) TCA configuration of the field * @param bool $isVirtualRecord TRUE if the current record is virtual, FALSE otherwise * @return string The HTML code with the control-icons */ protected function renderForeignRecordHeaderControl($parentUid, $foreign_table, $data, $config = array(), $isVirtualRecord = false) { $rec = $data['databaseRow']; $languageService = $this->getLanguageService(); $backendUser = $this->getBackendUserAuthentication(); // Initialize: $cells = array(); $additionalCells = array(); $isNewItem = substr($rec['uid'], 0, 3) == 'NEW'; $isParentExisting = MathUtility::canBeInterpretedAsInteger($parentUid); $tcaTableCtrl =& $GLOBALS['TCA'][$foreign_table]['ctrl']; $tcaTableCols =& $GLOBALS['TCA'][$foreign_table]['columns']; $isPagesTable = $foreign_table === 'pages'; $isSysFileReferenceTable = $foreign_table === 'sys_file_reference'; $isOnSymmetricSide = RelationHandler::isOnSymmetricSide($parentUid, $config, $rec); $enableManualSorting = $tcaTableCtrl['sortby'] || $config['MM'] || !$isOnSymmetricSide && $config['foreign_sortby'] || $isOnSymmetricSide && $config['symmetric_sortby']; $nameObject = $this->inlineStackProcessor->getCurrentStructureDomObjectIdPrefix($this->data['inlineFirstPid']); $nameObjectFt = $nameObject . '-' . $foreign_table; $nameObjectFtId = $nameObjectFt . '-' . $rec['uid']; $calcPerms = $backendUser->calcPerms(BackendUtility::readPageAccess($rec['pid'], $backendUser->getPagePermsClause(1))); // If the listed table is 'pages' we have to request the permission settings for each page: $localCalcPerms = false; if ($isPagesTable) { $localCalcPerms = $backendUser->calcPerms(BackendUtility::getRecord('pages', $rec['uid'])); } // This expresses the edit permissions for this particular element: $permsEdit = $isPagesTable && $localCalcPerms & Permission::PAGE_EDIT || !$isPagesTable && $calcPerms & Permission::CONTENT_EDIT; // Controls: Defines which controls should be shown $enabledControls = $config['appearance']['enabledControls']; // Hook: Can disable/enable single controls for specific child records: foreach ($this->hookObjects as $hookObj) { /** @var InlineElementHookInterface $hookObj */ $hookObj->renderForeignRecordHeaderControl_preProcess($parentUid, $foreign_table, $rec, $config, $isVirtualRecord, $enabledControls); } if ($data['inlineIsDefaultLanguage']) { $cells['localize.isLocalizable'] = '<span title="' . $languageService->sL('LLL:EXT:lang/locallang_misc.xlf:localize.isLocalizable', true) . '">' . $this->iconFactory->getIcon('actions-edit-localize-status-low', Icon::SIZE_SMALL)->render() . '</span>'; } // "Info": (All records) if ($enabledControls['info'] && !$isNewItem) { if ($rec['table_local'] === 'sys_file') { $uid = (int) substr($rec['uid_local'], 9); $table = '_FILE'; } else { $uid = $rec['uid']; $table = $foreign_table; } $cells['info'] = ' <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) . '"> ' . $this->iconFactory->getIcon('actions-document-info', Icon::SIZE_SMALL)->render() . ' </a>'; } // If the table is NOT a read-only table, then show these links: if (!$tcaTableCtrl['readOnly'] && !$isVirtualRecord) { // "New record after" link (ONLY if the records in the table are sorted by a "sortby"-row or if default values can depend on previous record): if ($enabledControls['new'] && ($enableManualSorting || $tcaTableCtrl['useColumnsForDefaultValues'])) { if (!$isPagesTable && $calcPerms & Permission::CONTENT_EDIT || $isPagesTable && $calcPerms & Permission::PAGE_NEW) { $onClick = 'return inline.createNewRecord(' . GeneralUtility::quoteJSvalue($nameObjectFt) . ',' . GeneralUtility::quoteJSvalue($rec['uid']) . ')'; $style = ''; if ($config['inline']['inlineNewButtonStyle']) { $style = ' style="' . $config['inline']['inlineNewButtonStyle'] . '"'; } $cells['new'] = ' <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 . '> ' . $this->iconFactory->getIcon('actions-' . ($isPagesTable ? 'page' : 'document') . '-new', Icon::SIZE_SMALL)->render() . ' </a>'; } } // "Up/Down" links if ($enabledControls['sort'] && $permsEdit && $enableManualSorting) { // Up $onClick = 'return inline.changeSorting(' . GeneralUtility::quoteJSvalue($nameObjectFtId) . ', \'1\')'; $style = $config['inline']['first'] == $rec['uid'] ? 'style="visibility: hidden;"' : ''; $cells['sort.up'] = ' <a class="btn btn-default sortingUp" href="#" onclick="' . htmlspecialchars($onClick) . '" ' . $style . ' title="' . $languageService->sL('LLL:EXT:lang/locallang_mod_web_list.xlf:moveUp', true) . '"> ' . $this->iconFactory->getIcon('actions-move-up', Icon::SIZE_SMALL)->render() . ' </a>'; // Down $onClick = 'return inline.changeSorting(' . GeneralUtility::quoteJSvalue($nameObjectFtId) . ', \'-1\')'; $style = $config['inline']['last'] == $rec['uid'] ? 'style="visibility: hidden;"' : ''; $cells['sort.down'] = ' <a class="btn btn-default sortingDown" href="#" onclick="' . htmlspecialchars($onClick) . '" ' . $style . ' title="' . $languageService->sL('LLL:EXT:lang/locallang_mod_web_list.xlf:moveDown', true) . '"> ' . $this->iconFactory->getIcon('actions-move-down', Icon::SIZE_SMALL)->render() . ' </a>'; } // "Edit" link: if ($rec['table_local'] === 'sys_file' && !$isNewItem) { $sys_language_uid = 0; if (!empty($rec['sys_language_uid'])) { $sys_language_uid = $rec['sys_language_uid'][0]; } $recordInDatabase = $this->getDatabaseConnection()->exec_SELECTgetSingleRow('uid', 'sys_file_metadata', 'file = ' . (int) substr($rec['uid_local'], 9) . ' AND sys_language_uid = ' . $sys_language_uid); if ($backendUser->check('tables_modify', 'sys_file_metadata')) { $url = BackendUtility::getModuleUrl('record_edit', array('edit[sys_file_metadata][' . (int) $recordInDatabase['uid'] . ']' => 'edit')); $editOnClick = 'if (top.content.list_frame) {' . 'top.content.list_frame.location.href=' . GeneralUtility::quoteJSvalue($url . '&returnUrl=') . '+top.rawurlencode(top.content.list_frame.document.location.pathname+top.content.list_frame.document.location.search)' . ';' . '}'; $title = $languageService->sL('LLL:EXT:lang/locallang_core.xlf:cm.editMetadata'); $cells['editmetadata'] = ' <a class="btn btn-default" href="#" class="btn" onclick="' . htmlspecialchars($editOnClick) . '" title="' . htmlspecialchars($title) . '"> ' . $this->iconFactory->getIcon('actions-document-open', Icon::SIZE_SMALL)->render() . ' </a>'; } } // "Delete" link: if ($enabledControls['delete'] && ($isPagesTable && $localCalcPerms & Permission::PAGE_DELETE || !$isPagesTable && $calcPerms & Permission::CONTENT_EDIT || $isSysFileReferenceTable && $calcPerms & Permission::PAGE_EDIT)) { $title = $languageService->sL('LLL:EXT:lang/locallang_mod_web_list.xlf:delete', true); $icon = $this->iconFactory->getIcon('actions-edit-delete', Icon::SIZE_SMALL)->render(); $cells['delete'] = '<a href="#" class="btn btn-default t3js-editform-delete-inline-record" data-objectid="' . htmlspecialchars($nameObjectFtId) . '" title="' . $title . '">' . $icon . '</a>'; } // "Hide/Unhide" links: $hiddenField = $tcaTableCtrl['enablecolumns']['disabled']; if ($enabledControls['hide'] && $permsEdit && $hiddenField && $tcaTableCols[$hiddenField] && (!$tcaTableCols[$hiddenField]['exclude'] || $backendUser->check('non_exclude_fields', $foreign_table . ':' . $hiddenField))) { $onClick = 'return inline.enableDisableRecord(' . GeneralUtility::quoteJSvalue($nameObjectFtId) . ')'; $className = 't3js-' . $nameObjectFtId . '_disabled'; if ($rec[$hiddenField]) { $title = $languageService->sL('LLL:EXT:lang/locallang_mod_web_list.xlf:unHide' . ($isPagesTable ? 'Page' : ''), true); $cells['hide.unhide'] = ' <a class="btn btn-default hiddenHandle ' . $className . '" href="#" onclick="' . htmlspecialchars($onClick) . '"' . 'title="' . $title . '">' . $this->iconFactory->getIcon('actions-edit-unhide', Icon::SIZE_SMALL)->render() . ' </a>'; } else { $title = $languageService->sL('LLL:EXT:lang/locallang_mod_web_list.xlf:hide' . ($isPagesTable ? 'Page' : ''), true); $cells['hide.hide'] = ' <a class="btn btn-default hiddenHandle ' . $className . '" href="#" onclick="' . htmlspecialchars($onClick) . '"' . 'title="' . $title . '">' . $this->iconFactory->getIcon('actions-edit-hide', Icon::SIZE_SMALL)->render() . ' </a>'; } } // Drag&Drop Sorting: Sortable handler for script.aculo.us if ($enabledControls['dragdrop'] && $permsEdit && $enableManualSorting && $config['appearance']['useSortable']) { $additionalCells['dragdrop'] = ' <span class="btn btn-default sortableHandle" data-id="' . htmlspecialchars($rec['uid']) . '" title="' . $languageService->sL('LLL:EXT:lang/locallang_core.xlf:labels.move', true) . '"> ' . $this->iconFactory->getIcon('actions-move-move', Icon::SIZE_SMALL)->render() . ' </span>'; } } elseif ($isVirtualRecord && $isParentExisting) { if ($enabledControls['localize'] && $data['inlineIsDefaultLanguage']) { $onClick = 'inline.synchronizeLocalizeRecords(' . GeneralUtility::quoteJSvalue($nameObjectFt) . ', ' . GeneralUtility::quoteJSvalue($rec['uid']) . ');'; $cells['localize'] = ' <a class="btn btn-default" href="#" onclick="' . htmlspecialchars($onClick) . '" title="' . $languageService->sL('LLL:EXT:lang/locallang_misc.xlf:localize', true) . '"> ' . $this->iconFactory->getIcon('actions-document-localize', Icon::SIZE_SMALL)->render() . ' </a>'; } } // If the record is edit-locked by another user, we will show a little warning sign: if ($lockInfo = BackendUtility::isRecordLocked($foreign_table, $rec['uid'])) { $cells['locked'] = ' <a class="btn btn-default" href="#" onclick="alert(' . GeneralUtility::quoteJSvalue($lockInfo['msg']) . ');return false;"> ' . '<span title="' . htmlspecialchars($lockInfo['msg']) . '">' . $this->iconFactory->getIcon('status-warning-in-use', Icon::SIZE_SMALL)->render() . '</span>' . ' </a>'; } // Hook: Post-processing of single controls for specific child records: foreach ($this->hookObjects as $hookObj) { $hookObj->renderForeignRecordHeaderControl_postProcess($parentUid, $foreign_table, $rec, $config, $isVirtualRecord, $cells); } $out = ''; if (!empty($cells)) { $out .= ' <div class="btn-group btn-group-sm" role="group">' . implode('', $cells) . '</div>'; } if (!empty($additionalCells)) { $out .= ' <div class="btn-group btn-group-sm" role="group">' . implode('', $additionalCells) . '</div>'; } return $out; }
/** * Check, if a field should be skipped, that was defined to be handled as foreign_field or foreign_sortby of * the parent record of the "inline"-type - if so, we have to skip this field - the rendering is done via "inline" as hidden field * * @param string $table The table name * @param string $field The field name * @param array $row The record row from the database * @param array $config TCA configuration of the field * @return boolean Determines whether the field should be skipped. * @todo Define visibility */ public function skipField($table, $field, $row, $config) { $skipThisField = FALSE; if ($this->getStructureDepth()) { $searchArray = array('%OR' => array('config' => array(0 => array('%AND' => array('foreign_table' => $table, '%OR' => array('%AND' => array('appearance' => array('useCombination' => TRUE), 'foreign_selector' => $field), 'MM' => $config['MM']))), 1 => array('%AND' => array('foreign_table' => $config['foreign_table'], 'foreign_selector' => $config['foreign_field']))))); // Get the parent record from structure stack $level = $this->getStructureLevel(-1); // If we have symmetric fields, check on which side we are and hide fields, that are set automatically: if (RelationHandler::isOnSymmetricSide($level['uid'], $level['config'], $row)) { $searchArray['%OR']['config'][0]['%AND']['%OR']['symmetric_field'] = $field; $searchArray['%OR']['config'][0]['%AND']['%OR']['symmetric_sortby'] = $field; } else { $searchArray['%OR']['config'][0]['%AND']['%OR']['foreign_field'] = $field; $searchArray['%OR']['config'][0]['%AND']['%OR']['foreign_sortby'] = $field; } $skipThisField = $this->compareStructureConfiguration($searchArray, TRUE); } return $skipThisField; }
/** * Rendering of inline fields should be skipped under certain circumstances * * @return bool TRUE if field should be skipped based on inline configuration */ protected function inlineFieldShouldBeSkipped() { $table = $this->data['tableName']; $row = $this->data['databaseRow']; $fieldName = $this->data['fieldName']; $fieldConfig = $this->data['processedTca']['columns'][$fieldName]['config']; /** @var InlineStackProcessor $inlineStackProcessor */ $inlineStackProcessor = GeneralUtility::makeInstance(InlineStackProcessor::class); $inlineStackProcessor->initializeByGivenStructure($this->data['inlineStructure']); $structureDepth = $inlineStackProcessor->getStructureDepth(); $skipThisField = false; if ($structureDepth > 0) { $searchArray = array('%OR' => array('config' => array(0 => array('%AND' => array('foreign_table' => $table, '%OR' => array('%AND' => array('appearance' => array('useCombination' => true), 'foreign_selector' => $fieldName), 'MM' => $fieldConfig['MM']))), 1 => array('%AND' => array('foreign_table' => $fieldConfig['foreign_table'], 'foreign_selector' => $fieldConfig['foreign_field']))))); // Get the parent record from structure stack $level = $inlineStackProcessor->getStructureLevel(-1); // If we have symmetric fields, check on which side we are and hide fields, that are set automatically: if (RelationHandler::isOnSymmetricSide($level['uid'], $level['config'], $row)) { $searchArray['%OR']['config'][0]['%AND']['%OR']['symmetric_field'] = $fieldName; $searchArray['%OR']['config'][0]['%AND']['%OR']['symmetric_sortby'] = $fieldName; } else { $searchArray['%OR']['config'][0]['%AND']['%OR']['foreign_field'] = $fieldName; $searchArray['%OR']['config'][0]['%AND']['%OR']['foreign_sortby'] = $fieldName; } $skipThisField = $this->arrayCompareComplex($level, $searchArray); } return $skipThisField; }