Fluidtypo3: Use custom field as title of FCE - typo3

I don't want to use the default header in my FCE's, but only custom flux fields. In the backend list views my FCE's are shown as "[no title]" because the default header is not filled. This leads to much confusion for editors.
How can I define one of my custom flux fields to be used as title for the FCE in TYPO3 Backend list views etc.?

You can't just use a field from the flexform, because all fields from the FCE are stored in the same field in the database (pi_flexform).
What you can do is to render the content element title with a user function. It is registered with a line like this in the TCA config:
$GLOBALS['TCA']['tt_content']['ctrl']['label_userFunc'] = 'Vendor\\Extkey\\Utility\\ContentElementLabelRenderer->getContentElementTitle';
The user function itself could look like this:
<?php
namespace Vendor\Extkey\Utility;
/**
* This class renders a human readable title for FCEs,
* so one is able to find a content element by its headline.
*/
class ContentElementLabelRenderer implements \TYPO3\CMS\Core\SingletonInterface {
/**
* #var \TYPO3\CMS\Extbase\Service\FlexFormService
* #inject
*/
protected $flexFormService = null;
/**
* Returns the content element title for a given content element
*/
public function getContentElementTitle(&$params) {
if (null === $this->flexFormService) {
$this->flexFormService = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance('TYPO3\\CMS\\Extbase\\Service\\FlexFormService');
}
if (ctype_digit($params['row']['uid']) && 'fluidcontent_content' === $params['row']['CType']) {
// If this is a FCE, parse the flexform and template name and generate the
// title in a template specific way.
$row = $params['row'];
$additionalRowData = $GLOBALS['TYPO3_DB']->exec_SELECTgetSingleRow('pi_flexform, tx_fed_fcefile', 'tt_content', 'uid = ' . $row['uid']);
$flexFormContent = $this->flexFormService->convertFlexFormContentToArray($additionalRowData['pi_flexform']);
$lastColonPosition = strrpos($additionalRowData['tx_fed_fcefile'], ':');
$contentElementType = (FALSE === $lastColonPosition) ? 'invalidtype' : substr($additionalRowData['tx_fed_fcefile'], $lastColonPosition + 1);
switch ($contentElementType) {
case 'Image.html':
$params['title'] = 'Image: "' . ($flexFormContent['title'] ?: $flexFormContent['subtitle']) . '"';
break;
default:
$params['title'] = 'Unknown content element type';
break;
}
}
else {
// If this is not a FCEm, print out "normal"
// title. Not the real thing, but comes pretty close, hopefully.
$params['title'] = $params['row']['header'] ?: ($params['row']['subheader'] ?: $params['row']['bodytext']);
}
}
}
This produces a maintainance problem though: Every time you add or change a content element, you have to update this file.

Related

Change language parameters in site configuration for some pages in a site

Is there a possibility in TYPO3 10 LTS to change the option fallbackType in the site configuration for a single page?
Background:
I use for the site the option fallbackType fallback. But for a few page I need fallbackType strict. Up to TYPO3 8 this could be achieved with a simple extension template and different config configurations. Is there a way in TYPO3 10?
As far as i know there is no possibility to configurate TYPO3 to behave in this way. The only way to override the language configuration for a single page is a custom middleware.
You can find a working example here: https://github.com/qbus-agentur/typo3-dynamic-language-mode.
I changed the main function:
public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
{
$lang = $this->getCurrentSiteLanguage($request);
$pageArguments = $this->getPageArguments($request);
if ($lang === null || $pageArguments === null) {
return $handler->handle($request);
}
// get current page ID
$currentRouting = $request->getAttribute('routing');
$currentPage = $currentRouting->getPageId();
$site = $request->getAttribute('site');
$default = $site->getLanguages()[0];
if ($lang->getLanguageId() !== $default->getLanguageId()) {
// Check if page is in $pagesWhereToChange and apply a dynamic language configuration in that case
if (in_array($currentPage, $this->pagesWhereToChange) > 0) {
\Closure::bind(function() use ($lang, $newId) {
$lang->fallbackType = 'free';
$lang->fallbackLanguageIds = [];
}, null, SiteLanguage::class)();
}
}
return $handler->handle($request);
}
Where $this->pagesWhereToChange is an array of pages:
/**
* pagesWhereToChange
*
* #var array
*/
protected $pagesWhereToChange = [30,41];
It's not possible to configure the array with typoscript because TSFE is not rendered yet.

TYPO3: Render a plugin via Typoscript or ViewHelper and change settings

I would like to load a plugin dynamically according to some data. First I tried to do it with Typoscript, but after some research I figured out, that it is not possible to change the settings of the plugin (see old forum entry).
I need to change settings.simplepoll.uid according to the passed data:
This is the Typoscript I tried:
lib.loadSimplepoll = USER
lib.loadSimplepoll {
userFunc = TYPO3\CMS\Extbase\Core\Bootstrap->run
extensionName = Simplepoll
pluginName = Polllisting
vendorName = Pixelink
switchableControllerActions {
SimplePoll {
1 = list
}
}
settings < plugin.tx_simplepoll.settings
settings {
simplepoll {
uid.current = 1
}
}
}
The call in the template looks like that:
<f:cObject typoscriptObjectPath="lib.loadSimplepoll">{newsItem.simplepoll}</f:cObject>
After figuring out, that changing the settings is not possible, I tried a viewhelper:
<?php
namespace Vendor\Extension\ViewHelpers;
use TYPO3\CMS\Core\Utility\GeneralUtility;
class LoadSimplepollViewHelper extends \TYPO3\CMS\Fluid\Core\ViewHelper\AbstractViewHelper
{
/**
* #param int $uid Uid of poll
* #return string
*/
public function render($uid) {
$cObj = GeneralUtility::makeInstance('TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer');
$configurationManager = GeneralUtility::makeInstance('TYPO3\\CMS\\Extbase\\Configuration\\ConfigurationManager');
$simplepollTs = $configurationManager->getConfiguration(
\TYPO3\CMS\Extbase\Configuration\ConfigurationManagerInterface::CONFIGURATION_TYPE_SETTINGS,
'simplepoll',
'Polllisting'
);
$ttContentConfig = array(
'tables' => 'tt_content',
'source' => 1030,
'dontCheckPid' => 1
);
// returning this works perfectly!
// but I need to change the "settings.simplepoll.uid"
$data = $cObj->RECORDS($ttContentConfig);
$cObj->start($data, 'tx_simplepoll_domain_model_simplepoll');
$renderObjName = '<tt_content.list.20.simplepoll_polllisting';
$renderObjConf = $GLOBALS['TSFE']->tmpl->setup['tt_content.']['list.']['20.']['simplepoll_polllisting.'];
$renderObjConf['persistence']['storagePid'] = 394; // This does not work!
$renderObjConf['settings'] = $simplepollTs;
$renderObjConf['settings']['simplepoll']['uid'] = $uid;
return $cObj->cObjGetSingle($renderObjName, $renderObjConf);
}
}
The viehelper is called like this:
{vh:LoadSimplepoll(uid: '{newsItem.simplepoll}')}
Now I am able to change the uid of the poll with this line:
$renderObjConf['settings']['simplepoll']['uid'] = $uid;
My problem is now, that it loads the poll, but not the answers. I tracked this down to the fact, that the plugin somehow does not know the Record Storage Page anymore. The line $renderObjConf['persistence']['storagePid'] = 394; does not help.
How can I tell the plugin the Storage Pid?
Or is there another/better way to load a plugin with changing data?
Why shouldn't it be possible to modify settings.simplepoll.uid in typoscript?
because the extension simplepoll does not handle any stdWrap functionality to its typoscript settings.
Have a look into the code:
this special setting is used here:
$simplePoll = $this->simplePollRepository->findByUid($this->settings['simplepoll']['uid']);
no stdWrap, just plain usage.
compare it to ext:news:
before any settings is used it is processed. A dedicated join of typoscript settings with the settings in the plugin. And if necessary there is a stdWrap possible: here
$this->originalSettings = $originalSettings;
// Use stdWrap for given defined settings
if (isset($originalSettings['useStdWrap']) && !empty($originalSettings['useStdWrap'])) {
$typoScriptService = GeneralUtility::makeInstance(TypoScriptService::class);
$typoScriptArray = $typoScriptService->convertPlainArrayToTypoScriptArray($originalSettings);
$stdWrapProperties = GeneralUtility::trimExplode(',', $originalSettings['useStdWrap'], true);
foreach ($stdWrapProperties as $key) {
if (is_array($typoScriptArray[$key . '.'])) {
$originalSettings[$key] = $this->configurationManager->getContentObject()->stdWrap(
$typoScriptArray[$key],
$typoScriptArray[$key . '.']
);
}
}
}
As you can see:
extbase does not support you with typoscript stdWrap functionality.
You (and every extension author) need to do it by hand. But that was so even before extbase.
In this way: as you can not configure your value you only can trick TYPO3 (and the plugin):
if you have a small number of uids you can have one variant for each uid
lib.loadSimplepoll123 < lib.loadSimplepoll
lib.loadSimplepoll123.settings.simplepoll.uid = 123
lib.loadSimplepoll234 < lib.loadSimplepoll
lib.loadSimplepoll234.settings.simplepoll.uid = 234
lib.loadSimplepoll345 < lib.loadSimplepoll
lib.loadSimplepoll345.settings.simplepoll.uid = 345
lib.loadSimplepoll456 < lib.loadSimplepoll
lib.loadSimplepoll456.settings.simplepoll.uid = 456
and call it like
<f:cObject typoscriptObjectPath="lib.loadSimplepoll{newsItem.simplepoll}" />
or you build a pull request implementing the stdWrap functionality and send it to the extension author.
Why shouldn't it be possible to modify settings.simplepoll.uid in typoscript?
you just need the correct construction to modify it.
For a single value you can use current, but use it properly. It is a stdWrap function which needs to be evaluated.
If there is no stdWrap evaluation by default it might work with a cObject of type TEXT
settings.simplepoll.uid.cObject = TEXT
settings.simplepoll.uid.cObject.current = 1
or to indicate a stdWrap you need to use stdWrap literally:
settings.simplepoll.uid.stdWrap.current = 1
another variant of data transfer are named parameters. Just build an associative array as data parameter and access the values individual:
fluid:
<f:cObject typoscriptObjectPath="lib.arraytest" data="{a:'abc',b:'xyz'}" >
inside text
</f:cObject>
and the typoscript:
lib.arraytest = COA
lib.arraytest {
10 = TEXT
10.field = a
10.wrap = /|/
20 = TEXT
20.field = b
20.wrap = \|\
}
which results in an output of /abc/\xyz\. Be aware: the inner text of the f:cobject tag will be lost as the data parameter has priority about inner children.
In the meantime I got the Viewhelpter to work:
Viewhelper:
<?php
namespace Vendor\Extension\ViewHelpers;
use TYPO3\CMS\Core\Utility\GeneralUtility;
class LoadSimplepollViewHelper extends \TYPO3\CMS\Fluid\Core\ViewHelper\AbstractViewHelper
{
/**
* #return void
*/
public function initializeArguments()
{
parent::initializeArguments();
$this->registerArgument('simplepollUid', 'int', 'Uid of simplepoll', false);
}
/**
* #return string
*/
public function render()
{
$configurationManager = GeneralUtility::makeInstance('TYPO3\\CMS\\Extbase\\Configuration\\ConfigurationManager');
$simplepollTs = $configurationManager->getConfiguration(
\TYPO3\CMS\Extbase\Configuration\ConfigurationManagerInterface::CONFIGURATION_TYPE_SETTINGS,
'simplepoll',
'Polllisting');
$cObj = GeneralUtility::makeInstance('TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer');
$renderObjName = '<tt_content.list.20.simplepoll_polllisting';
$renderObjConf = $GLOBALS['TSFE']->tmpl->setup['tt_content.']['list.']['20.']['simplepoll_polllisting.'];
$renderObjConf['settings'] = $simplepollTs;
$renderObjConf['settings']['simplepoll']['uid'] = (int)$this->arguments['simplepollUid'];
return $cObj->cObjGetSingle($renderObjName, $renderObjConf);
}
}
Call the viewhelper in the template (don't forget to register the namespace):
{vh:LoadSimplepoll(simplepollUid: '{newsItem.ipgSimplepoll}')}
That's it.

LinkHandler hook on TYPO3 8

Is there any hook to handle links with the new LinkHandler for TYPO3 8.7?
On the old LinkHandler extension is possible to define a hook to process the links as we want.
I need to overwrite the parameter of typolink based on some rules. Is there a way to do this on my extension?
There are multiple points to hook into.
TypoLink post-processing
You can hook into the TypoLink post-processing to modify the typolink itself before it gets rendered.
For this, you first register your custom class in ext_tables/ext_localconf:
$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tslib/class.tslib_content.php']['typoLink_PostProc'][] = 'Vendor\\Name\\Service\\TypoLinkProcessingService->postProcessTypoLink';
Then, inside your TypoLinkProcessingService class (with your name of choice, of course), you'd handle it inside your own method. For visualisation purposes, in this example, I'm altering an URL if it is a link to a youtube video in order to turn off "related videos" at the end, and to modify the color used by the controls inside youtube's player.
public function postProcessTypoLink(&$parameters, ContentObjectRenderer &$parentObject)
{
if (isset($parameters['finalTagParts']['url'])) {
$urlParts = parse_url($parameters['finalTagParts']['url']);
if (stristr($urlParts['host'], 'youtube.com') !== false && stristr($urlParts['path'], 'watch') !== false) {
$parameters['finalTag'] = str_replace(
'"' . htmlspecialchars($parameters['finalTagParts']['url']) . '"',
'"' . htmlspecialchars($parameters['finalTagParts']['url'] . '&rel=0&color=ffffff') . '"',
$parameters['finalTag']
);
}
}
}
TypoLink UserFunc
Another option is to utilise userFunc and adapt links.
For this, you configure your linkhandler configuration (PageTS) to provide userFunc inside typolink. Add TypoScript as needed to later fetch the configured data.
config.recordLinks {
tx_myest {
typolink {
userFunc = Vendor\Name\UserFunc\TypolinkUserFunc->parseLinkHandlerTypolink
userFunc {
newsUid = TEXT
newsUid.data = field:uid
newsClass = TEXT
newsClass.data = parameters:class
defaultDetailPid = 53
}
}
}
}
Inside your parseLinkHandlerTypolink method, you can access configured properties and adapt as required:
class TypolinkUserFunc
{
/**
* #var \TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer
* #inject
*/
public $cObj;
/**
* Add a method description here
*
* #param array $content
* #param array $configuration
* #return string
*/
public function parseNewsLinkHandlerTypolink(array $content, array $configuration)
{
if (!$configuration['newsUid']) {
return;
}
$params = $this->cObj->cObjGetSingle($configuration['newsClass'], $configuration['newsClass.']);
$newsUid = (int)$this->cObj->cObjGetSingle($configuration['newsUid'], $configuration['newsUid.']);
// ... your code goes here ...
$url = $this->cObj->typolink('', $typolink);
return '<a href="' . $url . '" ' . $attributes . '>';
}
}
Alternatively, this hook that has been introduced in 8.6 may also help you: https://docs.typo3.org/typo3cms/extensions/core/Changelog/8.6/Feature-79121-ImplementHookInTypolinkForModificationOfPageParams.html

Set templavoila template with typoscript

Is it possible to set a page's templavoila template with typoscript?
I have solved it with:
includeLibs.lang = fileadmin/user_tvtest.php
page.1 = USER_INT
page.1.userFunc = user_tvtest->main
page.10 = USER_INT
page.10.userFunc = tx_templavoila_pi1->main_page
page.10.disableExplosivePreview = 1
and in fileadmin/user_tvtest.php:
class user_tvtest
{
function main($content, $conf)
{
if (is_mobile())
{
$GLOBALS['TSFE']->page['tx_templavoila_to'] = 7;
//$GLOBALS['TSFE']->page['tx_templavoila_ds'] = 7;
}
}
}
http://daschmi.de/templavoila-template-domainbezogen-umschalten-gleicher-seitenbaum/
Look how the page is configured by TemplaVoila:
page = PAGE
page.typeNum = 0
page.10 = USER
page.10.userFunc = tx_templavoila_pi1->main_page
page.shortcutIcon = {$faviconPath}
They call the main_page function of class tx_templavoila_pi1 via page.userFunc:
/**
* Main function for rendering of Page Templates of TemplaVoila
*
* #param string Standard content input. Ignore.
* #param array TypoScript array for the plugin.
* #return string HTML content for the Page Template elements.
*/
function main_page($content,$conf) {
$this->initVars($conf);
// Current page record which we MIGHT manipulate a little:
$pageRecord = $GLOBALS['TSFE']->page;
// Find DS and Template in root line IF there is no Data Structure set for the current page:
if (!$pageRecord['tx_templavoila_ds']) {
foreach($GLOBALS['TSFE']->tmpl->rootLine as $pRec) {
if ($pageRecord['uid'] != $pRec['uid']) {
if ($pRec['tx_templavoila_next_ds']) { // If there is a next-level DS:
$pageRecord['tx_templavoila_ds'] = $pRec['tx_templavoila_next_ds'];
$pageRecord['tx_templavoila_to'] = $pRec['tx_templavoila_next_to'];
} elseif ($pRec['tx_templavoila_ds']) { // Otherwise try the NORMAL DS:
$pageRecord['tx_templavoila_ds'] = $pRec['tx_templavoila_ds'];
$pageRecord['tx_templavoila_to'] = $pRec['tx_templavoila_to'];
}
} else break;
}
}
// "Show content from this page instead" support. Note: using current DS/TO!
if ($pageRecord['content_from_pid']) {
$ds = $pageRecord['tx_templavoila_ds'];
$to = $pageRecord['tx_templavoila_to'];
$pageRecord = $GLOBALS['TSFE']->sys_page->getPage($pageRecord['content_from_pid']);
$pageRecord['tx_templavoila_ds'] = $ds;
$pageRecord['tx_templavoila_to'] = $to;
}
return $this->renderElement($pageRecord, 'pages');
}
This function checks the current page or search trough the rootline (TSFE) for the configured page template. The script does not check any TypoScript settings at all so I suppose thats not supported by TemplaVoila right now.
It should not be too hard extending this class with a custom function that will check some TypoScript settings.

Set 'selected' value in select box dropdown list - based on first letter

I have a zend form that has a select box with 1000+ id->name options sorted alphabetically.
When rendered and looking at it in a browser, if you type Ch it goes all the way to that option;
Is there a way I can set the value to be selected via the first few letters after the form has been initialized? In other words $form->getElement('name')->setSelected('Ch') or similar;
I know that with setValue(34) I can set the name to be selected that has the ID 34.
Just wrote the code myself
class My_Form_Element_Select extends Zend_Form_Element_Select{
/**
* Sets the the first option to start with certain letters to be selected
* #param string $string The first few letters to search for
*/
public function setSelected($string){
$string = strtolower($string);
$options = $this->_getMultiOptions();
$length = strlen($string);
foreach($options as $value => $option){
if($string == strtolower(substr($option,0,$length))){
$this->setValue($value);
break;
}
}
return $this;
}