Project

General

Profile

Bug #17354 » DifferentLanguageFallbackThenDefaultLanguageForPages.patch

Alex Kellner, 2018-10-30 14:48

View differences:

typo3/sysext/extbase/Classes/Persistence/Generic/Storage/Typo3DbBackend.php
) {
if (in_array($row[$GLOBALS['TCA'][$tableName]['ctrl']['languageField']], [-1, 0])) {
$overlayMode = $querySettings->getLanguageMode() === 'strict' ? 'hideNonTranslated' : '';
$row = $pageRepository->getRecordOverlay($tableName, $row, $querySettings->getLanguageUid(), $overlayMode);
$row = $pageRepository->getRecordOverlayWithFallback(
$tableName,
$row,
$querySettings->getLanguageUid(),
$overlayMode
);
}
}
if ($row !== null && is_array($row)) {
typo3/sysext/frontend/Classes/Aspect/FileMetadataOverlayAspect.php
{
$overlaidMetaData = $data->getArrayCopy();
$this->getTsfe()->sys_page->versionOL('sys_file_metadata', $overlaidMetaData);
$overlaidMetaData = $this->getTsfe()->sys_page->getRecordOverlay(
$overlaidMetaData = $this->getTsfe()->sys_page->getRecordOverlayWithFallback(
'sys_file_metadata',
$overlaidMetaData,
$this->getTsfe()->sys_language_content,
$this->getTsfe()->sys_language_contentOL
$overlaidMetaData
);
if ($overlaidMetaData !== null) {
$data->exchangeArray($overlaidMetaData);
typo3/sysext/frontend/Classes/Category/Collection/CategoryCollection.php
if ($this->getItemTableName() === 'pages') {
$record = $tsfe->sys_page->getPageOverlay($record);
} else {
$record = $tsfe->sys_page->getRecordOverlay(
$record = $this->getFrontendObject()->sys_page->getRecordOverlayWithFallback(
$this->getItemTableName(),
$record,
$tsfe->sys_language_content,
$tsfe->sys_language_contentOL
$record
);
}
}
typo3/sysext/frontend/Classes/ContentObject/FilesContentObject.php
if ($referencesForeignTable === 'pages') {
$element = $pageRepository->getPageOverlay($element);
} else {
$element = $pageRepository->getRecordOverlay(
$referencesForeignTable,
$element,
$GLOBALS['TSFE']->sys_language_content,
$GLOBALS['TSFE']->sys_language_contentOL
);
$element = $pageRepository->getRecordOverlayWithFallback($referencesForeignTable, $element);
}
}
typo3/sysext/frontend/Classes/ContentObject/Menu/AbstractMenuContentObject.php
$tok = true;
// There is an alternative language active AND the current page requires a translation:
if ($tsfe->sys_language_uid && GeneralUtility::hideIfNotTranslated($data['l18n_cfg'])) {
if (!$data['_PAGES_OVERLAY']) {
if (!$data['_PAGES_OVERLAY'] || (int)$data['sys_language_uid'] !== (int)$GLOBALS['TSFE']->sys_language_uid) {
$tok = false;
}
}
......
while ($row = $statement->fetch()) {
$this->sys_page->versionOL('tt_content', $row);
if ($tsfe->sys_language_contentOL && $basePageRow['_PAGES_OVERLAY_LANGUAGE']) {
$row = $this->sys_page->getRecordOverlay('tt_content', $row, $basePageRow['_PAGES_OVERLAY_LANGUAGE'], $tsfe->sys_language_contentOL);
$row = $this->sys_page->getRecordOverlayWithFallback('tt_content', $row, $basePageRow['_PAGES_OVERLAY_LANGUAGE']);
}
if ($this->mconf['sectionIndex.']['type'] !== 'all') {
$doIncludeInSectionIndex = $row['sectionIndex'] >= 1;
typo3/sysext/frontend/Classes/ContentObject/RecordsContentObject.php
// Reset items and data
$this->itemArray = [];
$this->data = [];
$tsfe = $this->getTypoScriptFrontendController();
$theValue = '';
$originalRec = $GLOBALS['TSFE']->currentRecord;
$originalRec = $tsfe->currentRecord;
// If the currentRecord is set, we register, that this record has invoked this function.
// It's should not be allowed to do this again then!!
if ($originalRec) {
++$GLOBALS['TSFE']->recordRegister[$originalRec];
++$tsfe->recordRegister[$originalRec];
}
$tables = isset($conf['tables.']) ? $this->cObj->stdWrap($conf['tables'], $conf['tables.']) : $conf['tables'];
......
// Perform overlays if necessary (records coming from category collections are already overlaid)
if ($source) {
// Versioning preview
$GLOBALS['TSFE']->sys_page->versionOL($val['table'], $row);
$tsfe->sys_page->versionOL($val['table'], $row);
// Language overlay
if (is_array($row) && $GLOBALS['TSFE']->sys_language_contentOL) {
if (is_array($row) && $tsfe->sys_language_contentOL) {
if ($val['table'] === 'pages') {
$row = $GLOBALS['TSFE']->sys_page->getPageOverlay($row);
$row = $tsfe->sys_page->getPageOverlay($row);
} else {
$row = $GLOBALS['TSFE']->sys_page->getRecordOverlay($val['table'], $row, $GLOBALS['TSFE']->sys_language_content, $GLOBALS['TSFE']->sys_language_contentOL);
$fallbackChain = NULL;
if (isset($conf['languageFallbackChain'])) {
$fallbackChain = GeneralUtility::intExplode(',', $conf['languageFallbackChain']);
}
$pageLanguageBinding = !isset($conf['respectPageLanguageBinding']) || (int)$conf['respectPageLanguageBinding'] !== '0';
$row = $tsfe->sys_page->getRecordOverlayWithFallback(
$val['table'], $row, NULL, NULL, $fallbackChain, $pageLanguageBinding
);
}
}
}
......
if (!$dontCheckPid) {
$row = $this->cObj->checkPid($row['pid']) ? $row : '';
}
if ($row && !$GLOBALS['TSFE']->recordRegister[$val['table'] . ':' . $val['id']]) {
if ($row && !$tsfe->recordRegister[($val['table'] . ':' . $val['id'])]) {
$renderObjName = $conf['conf.'][$val['table']] ?: '<' . $val['table'];
$renderObjKey = $conf['conf.'][$val['table']] ? 'conf.' . $val['table'] : '';
$renderObjConf = $conf['conf.'][$val['table'] . '.'];
$this->cObj->currentRecordNumber++;
$cObj->parentRecordNumber = $this->cObj->currentRecordNumber;
$GLOBALS['TSFE']->currentRecord = $val['table'] . ':' . $val['id'];
$tsfe->currentRecord = $val['table'] . ':' . $val['id'];
$this->cObj->lastChanged($row['tstamp']);
$cObj->start($row, $val['table']);
$tmpValue = $cObj->cObjGetSingle($renderObjName, $renderObjConf, $renderObjKey);
......
$theValue = $this->cObj->stdWrap($theValue, $conf['stdWrap.']);
}
// Restore
$GLOBALS['TSFE']->currentRecord = $originalRec;
$tsfe->currentRecord = $originalRec;
if ($originalRec) {
--$GLOBALS['TSFE']->recordRegister[$originalRec];
}
......
{
return GeneralUtility::makeInstance(TimeTracker::class);
}
/**
* @return \TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController
*/
protected function getTypoScriptFrontendController() {
return $GLOBALS['TSFE'];
}
}
typo3/sysext/frontend/Classes/Controller/TypoScriptFrontendController.php
*/
public $sys_language_content = 0;
/**
* Contains the prioritized language fallback chain
*
* @var array
*/
public $languageFallbackChain = [];
/**
* Contains the prioritized language fallback chain for the current page
*
* @var array
*/
public $languageFallbackChainWithPageLanguageBinding = [];
/**
* Site content overlay flag; If set - and sys_language_content is > 0 - ,
* records selected will try to look for a translation pointing to their uid. (If
......
// Get values from TypoScript:
$this->sys_language_uid = ($this->sys_language_content = (int)$this->config['config']['sys_language_uid']);
list($this->sys_language_mode, $sys_language_content) = GeneralUtility::trimExplode(';', $this->config['config']['sys_language_mode']);
$languageFallbackChain = GeneralUtility::intExplode(',', $this->config['config']['languageFallbackChain']);
$this->sys_language_contentOL = $this->config['config']['sys_language_overlay'];
// If sys_language_uid is set to another language than default:
if ($this->sys_language_uid > 0) {
......
$this->pageNotFoundAndExit('Page is not available in the requested language (strict).');
break;
case 'content_fallback':
// Setting content uid (but leaving the sys_language_uid) when a content_fallback
// value was found.
$fallBackOrder = GeneralUtility::trimExplode(',', $sys_language_content);
foreach ($fallBackOrder as $orderValue) {
if ($orderValue === '0' || $orderValue === '') {
$this->sys_language_content = 0;
// default is to fallback to default language
$this->sys_language_content = 0;
foreach ($languageFallbackChain as $languageId) {
// break the language chain if the current language is selected
if ($languageId === 0) {
break;
}
if (MathUtility::canBeInterpretedAsInteger($orderValue) && !empty($this->sys_page->getPageOverlay($this->id, (int)$orderValue))) {
$this->sys_language_content = (int)$orderValue;
break;
// ignore current and invalid language ids
if ($this->sys_language_uid === $languageId || $languageId < 0) {
continue;
}
if ($orderValue === 'pageNotFound') {
// The existing fallbacks have not been found, but instead of continuing
// page rendering with default language, a "page not found" message should be shown
// instead.
$this->pageNotFoundAndExit('Page is not available in the requested language (fallbacks did not apply).');
$pageOverlay = $this->sys_page->getPageOverlay($this->id, $languageId);
if (!empty($pageOverlay)) {
// Setting content uid (but leaving the sys_language_uid)
$this->sys_language_content = $languageId;
// overlay page with existing page overlay
$this->page = array_merge($this->page, $pageOverlay);
break;
}
}
break;
......
}
}
} else {
// Setting sys_language if an overlay record was found (which it is only if a language is used)
$this->page = $this->sys_page->getPageOverlay($this->page, $this->sys_language_uid);
// overlay page with existing page overlay
$this->page = array_merge($this->page, $olRec);
}
// calculate the language fallback chains
if ($this->sys_language_mode === 'content_fallback') {
foreach ($languageFallbackChain as $languageId) {
// ignore current and invalid language ids
if ($this->sys_language_uid === $languageId || $languageId < 0) {
continue;
}
$this->languageFallbackChain[] = $languageId;
if ($languageId === 0 || count($this->sys_page->getPageOverlay($this->id, $languageId))) {
$this->languageFallbackChainWithPageLanguageBinding[] = $languageId;
}
}
}
}
// Setting sys_language_uid inside sys-page:
$this->sys_page->sys_language_uid = $this->sys_language_uid;
// If default translation is not available:
if ((!$this->sys_language_uid || !$this->sys_language_content) && GeneralUtility::hideIfDefaultLanguage($this->page['l18n_cfg'])) {
// If default translation is not to be selected
if ($this->page['l18n_cfg']&1 && !($this->sys_language_uid && $this->sys_language_content)) {
$message = 'Page is not available in default language.';
GeneralUtility::sysLog($message, 'cms', GeneralUtility::SYSLOG_SEVERITY_ERROR);
$this->pageNotFoundAndExit($message);
typo3/sysext/frontend/Classes/Page/PageRepository.php
use TYPO3\CMS\Core\Utility\HttpUtility;
use TYPO3\CMS\Core\Utility\RootlineUtility;
use TYPO3\CMS\Core\Versioning\VersionState;
use TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController;
/**
* Page functions, a lot of sql/pages-related functions
......
return [];
}
// Initialize:
$noSpecialLanguageRequested = false;
if ($lUid < 0) {
$noSpecialLanguageRequested = true;
$lUid = $this->sys_language_uid;
}
$row = null;
......
$page_ids[] = $origPage;
}
}
$languages = [$lUid];
if ($noSpecialLanguageRequested) {
$languages = array_unique(array_merge(
[$lUid],
$this->getTypoScriptFrontendController()->languageFallbackChain,
[$this->getTypoScriptFrontendController()->sys_language_content]
));
}
// NOTE regarding the query restrictions
// Currently the showHiddenRecords of TSFE set will allow
// pages_language_overlay records to be selected as they are
......
'pid',
$queryBuilder->createNamedParameter($page_ids, Connection::PARAM_INT_ARRAY)
),
$queryBuilder->expr()->eq(
$queryBuilder->expr()->in(
'sys_language_uid',
$queryBuilder->createNamedParameter($lUid, \PDO::PARAM_INT)
$queryBuilder->createNamedParameter($languages, Connection::PARAM_INT_ARRAY)
)
)
->execute();
......
}
/**
* Creates language-overlay for records in general (where translation is found
* in records from the same table)
* Creates a language overlay for records stored inside tables which contain the
* translation information themselves. In addition to getRecordOverlay this method
* also checks for specified content language fallbacks and includes them.
*
* @param string $table Table name
* @param array $row Record to overlay. Must contain uid, pid and $table]['ctrl']['languageField']
* @param array $row Record to overlay. Must containt uid, pid and $table]['ctrl']['languageField']
* @param int $sys_language_content Pointer to the sys_language uid for content on the site.
* @param string $OLmode Overlay mode. If "hideNonTranslated" then records without translation will not be returned un-translated but unset (and return value is FALSE)
* @param array|NULL $fallbackList ordered fallback list of language ids
* @param bool $pageLanguageBinding use page language binding for the language fallback chain (only used if fallbackList is NULL)
* @return mixed Returns the input record, possibly overlaid with a translation. But if $OLmode is "hideNonTranslated" then it will return NULL if no translation is found.
* @throws \UnexpectedValueException
* @return mixed Returns the input record, possibly overlaid with a translation. But if $OLmode is "hideNonTranslated" then it will return FALSE if no translation is found.
*/
public function getRecordOverlay($table, $row, $sys_language_content, $OLmode = '')
public function getRecordOverlayWithFallback(
$table,
$row,
$sys_language_content = null,
$OLmode = null,
$fallbackList = null,
$pageLanguageBinding = true
) {
$tsfe = $this->getTypoScriptFrontendController();
$sys_language_content = $sys_language_content !== null ? $sys_language_content : $tsfe->sys_language_content;
$OLmode = $OLmode !== null ? $OLmode: $tsfe->sys_language_contentOL;
if ($fallbackList === null) {
$fallbackList = $tsfe->languageFallbackChainWithPageLanguageBinding;
if (!$pageLanguageBinding) {
$fallbackList = $tsfe->languageFallbackChain;
}
}
$record = $this->getRecordOverlayWithoutFallback($table, $row, $sys_language_content, $OLmode);
if (!is_array($record) || !isset($record['_LOCALIZED_UID'])) {
if ($sys_language_content && $OLmode !== 'hideNonTranslated' && !empty($fallbackList)) {
foreach ($fallbackList as $fallbackId) {
$fallbackId = (int)$fallbackId;
if ($fallbackId === 0) {
break;
}
$record = $this->getRecordOverlayWithoutFallback($table, $row, $fallbackId, $OLmode);
if (isset($record['_LOCALIZED_UID']) && is_array($record)) {
break;
}
}
}
}
return $record;
}
/**
* Creates a language overlay for records stored inside tables which contain the
* translation information themselves.
*
*
* @param string $table Table name the record comes from
* @param array $row Record to overlay. Must contain the columns uid, pid and $TCA[$table]['ctrl']['languageField']
* @param int $sys_language_content Pointer to the sys_language uid to use for content.
* @param string $OLmode Overlay mode. If "hideNonTranslated" then records without translation will not be returned translated but unset (and return value is FALSE)
* @throws \InvalidArgumentException
* @throws \UnexpectedValueException
* @return mixed Returns the input record, possibly overlaid with a translation. But if $OLmode is "hideNonTranslated" then it will return NULL if no translation is found.
*/
public function getRecordOverlayWithoutFallback($table, $row, $sys_language_content, $OLmode = '')
{
if (is_array($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_page.php']['getRecordOverlay'])) {
foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_page.php']['getRecordOverlay'] as $classRef) {
......
return $row;
}
/**
* Creates a language overlay for records stored inside tables which contain the
* translation information themselves.
*
*
* @param string $table Table name the record comes from
* @param array $row Record to overlay. Must contain the columns uid, pid and $TCA[$table]['ctrl']['languageField']
* @param int $sys_language_content Pointer to the sys_language uid to use for content.
* @param string $OLmode Overlay mode. If "hideNonTranslated" then records without translation will not be returned translated but unset (and return value is FALSE)
* @throws \InvalidArgumentException
* @throws \UnexpectedValueException
* @return mixed Returns the input record, possibly overlaid with a translation. But if $OLmode is "hideNonTranslated" then it will return NULL if no translation is found.
*/
public function getRecordOverlayWithoutFallback2($table, $row, $sys_language_content, $OLmode) {
if (is_array($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_page.php']['getRecordOverlay'])) {
foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_page.php']['getRecordOverlay'] as $classRef) {
$hookObject = GeneralUtility::getUserObj($classRef);
if (!$hookObject instanceof PageRepositoryGetRecordOverlayHookInterface) {
throw new \UnexpectedValueException('$hookObject must implement interface ' . PageRepositoryGetRecordOverlayHookInterface::class, 1269881658);
}
$hookObject->getRecordOverlay_preProcess($table, $row, $sys_language_content, $OLmode, $this);
}
}
if ($row['uid'] > 0 && ($row['pid'] > 0 || in_array($table, $this->tableNamesAllowedOnRootLevel, true))) {
if ($GLOBALS['TCA'][$table] && $GLOBALS['TCA'][$table]['ctrl']['languageField'] && $GLOBALS['TCA'][$table]['ctrl']['transOrigPointerField']) {
if (!$GLOBALS['TCA'][$table]['ctrl']['transOrigPointerTable']) {
// Will not be able to work with other tables (Just didn't implement it yet;
// Requires a scan over all tables [ctrl] part for first FIND the table that
// carries localization information for this table (which could even be more
// than a single table) and then use that. Could be implemented, but obviously
// takes a little more....) Will try to overlay a record only if the
// sys_language_content value is larger than zero.
if ($sys_language_content > 0) {
// Must be default language or [All], otherwise no overlaying:
if ($row[$GLOBALS['TCA'][$table]['ctrl']['languageField']] <= 0) {
// Select overlay record:
$res = $this->getDatabaseConnection()->exec_SELECTquery('*', $table, 'pid=' . (int)$row['pid'] . ' AND ' . $GLOBALS['TCA'][$table]['ctrl']['languageField'] . '=' . (int)$sys_language_content . ' AND ' . $GLOBALS['TCA'][$table]['ctrl']['transOrigPointerField'] . '=' . (int)$row['uid'] . $this->enableFields($table), '', '', '1');
$olrow = $this->getDatabaseConnection()->sql_fetch_assoc($res);
$this->getDatabaseConnection()->sql_free_result($res);
$this->versionOL($table, $olrow);
// Merge record content by traversing all fields:
if (is_array($olrow)) {
if (isset($olrow['_ORIG_uid'])) {
$row['_ORIG_uid'] = $olrow['_ORIG_uid'];
}
if (isset($olrow['_ORIG_pid'])) {
$row['_ORIG_pid'] = $olrow['_ORIG_pid'];
}
foreach ($row as $fN => $fV) {
if ($fN !== 'uid' && $fN !== 'pid' && isset($olrow[$fN])) {
if ($this->shouldFieldBeOverlaid($table, $fN, $olrow[$fN])) {
$row[$fN] = $olrow[$fN];
}
} elseif ($fN === 'uid') {
$row['_LOCALIZED_UID'] = $olrow['uid'];
}
}
} elseif ($OLmode === 'hideNonTranslated' && (int)$row[$GLOBALS['TCA'][$table]['ctrl']['languageField']] === 0) {
// Unset, if non-translated records should be hidden. ONLY done if the source
// record really is default language and not [All] in which case it is allowed.
$row = null;
}
} elseif ($sys_language_content != $row[$GLOBALS['TCA'][$table]['ctrl']['languageField']]) {
$row = null;
}
} else {
// When default language is displayed, we never want to return a record carrying
// another language!
if ($row[$GLOBALS['TCA'][$table]['ctrl']['languageField']] > 0) {
$row = null;
}
}
}
}
}
if (is_array($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_page.php']['getRecordOverlay'])) {
foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_page.php']['getRecordOverlay'] as $classRef) {
$hookObject = GeneralUtility::getUserObj($classRef);
if (!$hookObject instanceof PageRepositoryGetRecordOverlayHookInterface) {
throw new \UnexpectedValueException('$hookObject must implement interface ' . PageRepositoryGetRecordOverlayHookInterface::class, 1269881659);
}
$hookObject->getRecordOverlay_postProcess($table, $row, $sys_language_content, $OLmode, $this);
}
}
return $row;
}
/**
* Creates language-overlay for records in general (where translation is found
* in records from the same table)
*
* @param string $table Table name
* @param array $row Record to overlay. Must contain uid, pid and $table]['ctrl']['languageField']
* @param int $sys_language_content Pointer to the sys_language uid for content on the site.
* @param string $OLmode Overlay mode. If "hideNonTranslated" then records without translation will not be returned un-translated but unset (and return value is FALSE)
* @throws \UnexpectedValueException
* @return mixed Returns the input record, possibly overlaid with a translation. But if $OLmode is "hideNonTranslated" then it will return FALSE if no translation is found.
* @deprecated
*/
public function getRecordOverlay($table, $row, $sys_language_content, $OLmode = '') {
GeneralUtility::logDeprecatedFunction();
return $this->getRecordOverlayWithoutFallback($table, $row, $sys_language_content, $OLmode);
}
/************************************************
*
* Page related: Menu, Domain record, Root line
typo3/sysext/frontend/Tests/Unit/ContentObject/Menu/AbstractMenuContentObjectTest.php
$GLOBALS['TSFE']->sys_language_contentOL = 1;
$this->subject->sys_page->expects($this->once())->method('getPage')->will($this->returnValue(['_PAGES_OVERLAY_LANGUAGE' => 1]));
$this->subject->parent_cObj->expects($this->once())->method('exec_getQuery')->willReturn($statementProphet->reveal());
$this->subject->sys_page->expects($this->once())->method('getRecordOverlay')->will($this->returnValue(['uid' => 0, 'header' => 'OVERLAID']));
$this->subject->sys_page->expects($this->once())->method('getRecordOverlayWithFallback')->will($this->returnValue(['uid' => 0, 'header' => 'OVERLAID']));
$result = $this->subject->_call('sectionIndex', 'field');
$this->assertEquals($result[0]['title'], 'OVERLAID');
}
(12-12/12)