Exemplo n.º 1
0
 /**
  * @test
  */
 public function purgeItemArrayReturnsTrueIfItemsHaveBeenPurged()
 {
     $GLOBALS['TCA']['sys_category']['ctrl']['versioningWS'] = true;
     $this->subject->tableArray = array('sys_category' => array(1, 2, 3));
     $this->subject->expects($this->once())->method('purgeVersionedIds')->with('sys_category', array(1, 2, 3))->will($this->returnValue(array(2)));
     $this->assertTrue($this->subject->purgeItemArray(0));
 }
Exemplo n.º 2
0
 /**
  * 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;
 }
Exemplo n.º 3
0
 /**
  * Writes remapped foreign field (IRRE).
  *
  * @param \TYPO3\CMS\Core\Database\RelationHandler $dbAnalysis Instance that holds the sorting order of child records
  * @param array $configuration The TCA field configuration
  * @param int $parentId The uid of the parent record
  * @return void
  */
 public function writeRemappedForeignField(\TYPO3\CMS\Core\Database\RelationHandler $dbAnalysis, array $configuration, $parentId)
 {
     foreach ($dbAnalysis->itemArray as &$item) {
         if (isset($this->remappedIds[$item['table']][$item['id']])) {
             $item['id'] = $this->remappedIds[$item['table']][$item['id']];
         }
     }
     $dbAnalysis->writeForeignField($configuration, $parentId);
 }
    /**
     * 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;
    }
 /**
  * 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;
 }