Instantly share code, notes, and snippets.
Last active
November 12, 2019 16:37
-
Star
2
(2)
You must be signed in to star a gist -
Fork
0
(0)
You must be signed in to fork a gist
-
Save xperseguers/5c099f9b24a5427a79c09fd480da1cf8 to your computer and use it in GitHub Desktop.
TypoScript-based language menu only showing available single news localizations
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
declare(strict_types=1); | |
/* | |
* This file is part of the TYPO3 CMS project. | |
* | |
* It is free software; you can redistribute it and/or modify it under | |
* the terms of the GNU General Public License, either version 2 | |
* of the License, or any later version. | |
* | |
* For the full copyright and license information, please read the | |
* LICENSE.txt file that was distributed with TYPO3 source code. | |
* | |
* The TYPO3 project - inspiring people to share! | |
*/ | |
namespace Causal\LbTemplate\ContentObject\Menu; | |
use TYPO3\CMS\Core\Database\ConnectionPool; | |
use TYPO3\CMS\Core\Utility\GeneralUtility; | |
use TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer; | |
use TYPO3\CMS\Frontend\ContentObject\Menu\AbstractMenuContentObject; | |
use TYPO3\CMS\Frontend\ContentObject\Menu\TextMenuContentObject; | |
/** | |
* Class SingleRecordLocalizationMenu | |
* @package Causal\LbTemplate\ContentObject\Menu | |
* | |
* Sample Usage (for a menu of localizations from a single EXT:news article): | |
* | |
* lib.menus.newsLanguages = HMENU | |
* lib.menus.newsLanguages { | |
* special = userfunction | |
* special { | |
* userFunc = Causal\LbTemplate\ContentObject\Menu\SingleRecordLocalizationMenu->prepareLanguageMenu | |
* | |
* # configure how to fetch the single element of this page | |
* table = tx_news_domain_model_news | |
* record = TEXT | |
* record.data = GP:tx_news_pi1|news | |
* | |
* # same meaning as when using "special = language" | |
* value = 0,3,1,2 | |
* | |
* # default language is German (sys_language_uid = 0), but it could be another uid if you have a site with | |
* # various default languages and one of the available translations is the possible default default itself | |
* # for that subsite. In that case, the trick for such case (generally-speaking, not particularly for this | |
* # menu) is to avoid using "0" in the list of values above and replace it with the corresponding "translation" | |
* defaultLanguage = 0 | |
* | |
* # syntax is the one (option split) typically used for a language menu | |
* overrideTitles = DE || EN || FR || IT | |
* } | |
* | |
* wrap = <ul class="list-inline"> | </ul> | |
* | |
* 1 = TMENU | |
* 1 { | |
* NO = 1 | |
* NO { | |
* allWrap = <li> | </li> | |
* ATagParams = class="btn btn-default" | |
* } | |
* | |
* ACT < .NO | |
* ACT.ATagParams = class="btn btn-default active" | |
* } | |
* } | |
* | |
*/ | |
class SingleRecordLocalizationMenu /* trick to invoke protected methods */ extends AbstractMenuContentObject | |
{ | |
/** | |
* @var ContentObjectRenderer | |
*/ | |
public $cObj; | |
/** | |
* Rendering the cObject, HMENU | |
* | |
* @param string $content | |
* @param array $conf Array of TypoScript properties | |
* @return array | |
*/ | |
public function prepareLanguageMenu(string $content, array $conf): array | |
{ | |
if (empty($conf['table'])) { | |
throw new \RuntimeException('No table specified', 1571397821); | |
} | |
$recordUid = $conf['record']; | |
if (!empty($conf['record.'])) { | |
$recordUid = $this->cObj->cObjGetSingle($conf['record'], $conf['record.']); | |
} | |
$recordUid = (int)$recordUid; | |
if (empty($recordUid)) { | |
// No menu available | |
return []; | |
} | |
// First of all, we want TYPO3 to prepare the links to the various localizations of current page | |
// "->prepareMenuItemsForLanguageMenu" is the protected method which we cannot invoke without the trick of the | |
// class inheritance (or Reflection which is less-efficient) | |
$menuObject = GeneralUtility::makeInstance(TextMenuContentObject::class); | |
$menuObject->parent_cObj = $this->cObj; | |
$fakeConf = [ | |
'addQueryString' => '1', | |
'addQueryString.' => [ | |
'exclude' => 'id,L,cHash', | |
], | |
'1' => 'TMENU', | |
]; | |
$menuObject->start($GLOBALS['TSFE']->tmpl, $GLOBALS['TSFE']->sys_page, '', $fakeConf, 1); | |
$pageMenu = $menuObject->prepareMenuItemsForLanguageMenu($conf['value']); | |
// Then we look for available translations of the record | |
$languageUids = $this->getAvailableRecordLocalizations($conf['table'], $recordUid); | |
$languageUids[] = (int)$conf['defaultLanguage']; | |
// We want an associative array of $language => $position in the menu | |
// and their corresponding language codes to be shown | |
$orderedLanguages = array_flip(GeneralUtility::intExplode(',', $conf['value'], true)); | |
$languageTitles = GeneralUtility::trimExplode('||', $conf['overrideTitles']); | |
// We need to override the page titles with the language code | |
foreach ($pageMenu as $key => $_) { | |
$pageMenu[$key]['title'] = $languageTitles[$key]; | |
} | |
// Figure out which languages should get removed | |
$removeLanguageUids = array_diff(array_keys($orderedLanguages), $languageUids); | |
// Remove unwanted links | |
foreach ($removeLanguageUids as $languageUid) { | |
$pos = $orderedLanguages[$languageUid]; | |
unset($pageMenu[$pos]); | |
} | |
// Ensure one of the pages is "active" | |
$hasActiveLanguage = false; | |
foreach ($pageMenu as $key => $page) { | |
$hasActiveLanguage |= $page['ITEM_STATE'] === 'ACT'; | |
} | |
if (!$hasActiveLanguage) { | |
$pos = $orderedLanguages[(int)$conf['defaultLanguage']]; | |
$pageMenu[$pos]['ITEM_STATE'] = 'ACT'; | |
} | |
// Reindex the trimmed-down menu | |
$pageMenu = array_values($pageMenu); | |
// No need for a menu if we have no other language | |
if (count($pageMenu) === 1) { | |
$pageMenu = []; | |
} | |
return $pageMenu; | |
} | |
/** | |
* @param string $table | |
* @param int $uid | |
* @return array | |
*/ | |
protected function getAvailableRecordLocalizations(string $table, int $uid): array | |
{ | |
$rows = GeneralUtility::makeInstance(ConnectionPool::class) | |
->getConnectionForTable($table) | |
->select( | |
['sys_language_uid'], | |
$table, | |
[ | |
'l10n_parent' => $uid, | |
] | |
) | |
->fetchAll(); | |
$languageUids = []; | |
foreach ($rows as $row) { | |
$languageUids[] = $row['sys_language_uid']; | |
} | |
return $languageUids; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment