I want to display my own extension in TYPO3's CE Shortcut.
The button for my extension is displayed under the listview as button. And I can select an entry and it's saved. But I can't access the selected id in the frontend.
What I tried so far:
I added in /Configuration/TCA/Overrides/tt_content.php the Method addToInsertRecords. So the button to select an entry is shown up in the backend.
defined('TYPO3_MODE') or die();
call_user_func(function () {
\TYPO3\CMS\Core\Utility\ExtensionManagementUtility::addToInsertRecords('tx_eislist_domain_model_eis');
});
To show in Frontend the Typoscript is:
tt_content.shortcut.20.tables := addToList(tx_eislist_domain_model_eis)
tt_content.shortcut.20.conf.tx_eislist_domain_model_eis = USER
tt_content.shortcut.20.conf.tx_eislist_domain_model_eis {
userFunc = TYPO3\CMS\Extbase\Core\Bootstrap->run
extensionName = EisList
pluginName = Eis
vendorName = Emma
controller = Eisbar
action = show
switchableControllerActions {
Eisbar {
1 = show
}
}
settings =< plugin.tx_eislist_eis.settings
settings {
insertRecord = 1
useStdWrap = singleRecords
displayMode = single
singleRecords.field = uid
}
}
tt_content.shortcut.variables.shortcuts.tables := addToList(tx_eislist_domain_model_eis)
tt_content.shortcut.variables.shortcuts.conf.tx_eislist_domain_model_eis < tt_content.shortcut.20.conf.tx_eislist_domain_model_eis
In PHP with
$this->settings['singleRecords']
I can access the variable from TypoScript. But I get the string "field" => "uid". Instead of the selected value ID.
I think you need to inject configurationManager to get stdWrap functionality from / in setup typoscript file. Just use a configurationManager variable and an injectFunction in your controller which contains the show action:
/**
* #var \TYPO3\CMS\Extbase\Configuration\ConfigurationManagerInterface
*/
protected $configurationManager;
/**
* Injects the Configuration Manager and is initializing the framework settings
*
* #param \TYPO3\CMS\Extbase\Configuration\ConfigurationManagerInterface $configurationManager Instance of the
* Configuration Manager
*/
public function injectConfigurationManager(
\TYPO3\CMS\Extbase\Configuration\ConfigurationManagerInterface $configurationManager
) {
$this->configurationManager = $configurationManager;
$originalSettings = $this->configurationManager->getConfiguration(
\TYPO3\CMS\Extbase\Configuration\ConfigurationManagerInterface::CONFIGURATION_TYPE_SETTINGS
);
// 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 . '.']
);
}
}
}
$this->settings = $originalSettings;
}
Related
I want to set the the page title within my extension, so the current {page} object in the Fluid Templates will also show the set title.
$GLOBALS['TSFE']->altPageTitle = $pageTitle; will only set the <title> tag and has no impact to the {page.title}
My Primary Goal: To show the 'correct' title of a detail page within a breadcrumb.
Any ideas how to manipulate that?
I don't know anything about a {page} object in fluid, so I did it with a ViewHelper similar to the one from here
/**
* A view helper for setting the document title in the <title> tag.
*
* = Examples =
*
* <page.title mode="prepend" glue=" - ">{blog.name}</page.title>
*
* <page.title mode="replace">Something here</page.title>
*
* <h1><page.title mode="append" glue=" | " display="render">Title</page.title></h1>
*
* #license http://www.gnu.org/licenses/lgpl.html GNU Lesser General Public License, version 3 or later
*/
class PageTitleViewHelper extends \TYPO3\CMS\Fluid\Core\ViewHelper\AbstractViewHelper {
/**
* #var bool
*/
protected $escapeOutput = false;
/**
* #param string $mode Method for adding the new title to the existing one.
* #param string $glue Glue the new title to the old title with this string.
* #param string $display If render, this tag displays it's children. By default it doesn't display anything.
* #return string Rendered content or blank depending on display mode.
* #author Nathan Lenz <nathan.lenz#organicvalley.coop>
*/
public function render($mode = 'replace', $glue = ' - ', $display = 'none') {
$renderedContent = $this->renderChildren();
$existingTitle = $GLOBALS['TSFE']->page['title'];
if ($mode === 'prepend' && !empty($existingTitle)) {
$newTitle = $renderedContent.$glue.$existingTitle;
} else if ($mode === 'append' && !empty($existingTitle)) {
$newTitle = $existingTitle.$glue.$renderedContent;
} else {
$newTitle = $renderedContent;
}
$GLOBALS['TSFE']->page['title'] = $newTitle;
$GLOBALS['TSFE']->indexedDocTitle = $newTitle;
if ($display === 'render') {
return $renderedContent;
} else {
return '';
}
}
}
For cached pages and plugins this should work:
$GLOBALS['TSFE']->page['title'] = $pageTitle;
I ran into a problem. When I upload a file (image) as visual swatch attribute in Magento 2.2.2, nothing happens I see an empty field (I attached a screenshot)
Access log show a POST request with response code 200, and I checked the directory pub/media/attribute/swatch/swatch_image/30x20/0/1 the file is there.
You have to need change of core file other wise override to it
Path : vendor/magento/module-swatches/Block/Adminhtml/Attribute/Edit/OptionsVisual.php
public function getJsonConfig()
* Parse swatch labels for template
*
* #codeCoverageIgnore
* #param null $swatchStoreValue //Remove
* #return string //Remove
* #param null|array $swatchStoreValue //Add
* #return null|array //Add
*/
protected function reformatSwatchLabels($swatchStoreValue = null)
{
if ($swatchStoreValue === null) {
return;
}
$newSwatch = ''; // Remove
$newSwatch = []; //Add
foreach ($swatchStoreValue as $key => $value) {
if ($value[0] == '#') {
$newSwatch[$key] = 'background: '.$value;
When using the new linkhandler in TYPO3 like the link below:
https://usetypo3.com/linkhandler.html
i only have one parameter for the detail page:
config.recordLinks.tx_news {
typolink {
parameter = {$myConstants.newsDetailPid}
}
}
How can ich change the linkhandler (hook etc.) in order to get the detail page from the news Category (sys category)?
Use the following code:
config.recordLinks.tx_news {
typolink {
parameter.stdWrap.cObject = CONTENT
parameter.stdWrap.cObject {
table = sys_category
select {
pidInList = 100
# pid of category records
max = 1
selectFields = sys_category.single_pid AS detailPid
join = sys_category_record_mm ON sys_category_record_mm.uid_local = sys_category.uid
where = sys_category_record_mm.uid_foreign = { field: uid }
where.insertData = 1
andWhere.stdWrap.intVal = 1
andWhere.stdWrap.stripHtml = 1
}
renderObj = TEXT
renderObj.field = detailPid
renderObj.wrap = |
}
additionalParams.data = field:uid
additionalParams.wrap = &tx_news_pi1[news]=|
useCacheHash = 1
}
}
https://www.clickstorm.de/blog/linkhandler-typo3/
You find the documentation of the linkhandler integration here:
https://docs.typo3.org/typo3cms/extensions/core/8.7/Changelog/8.6/Feature-79626-IntegrateRecordLinkHandler.html
There you can see that you can specify an own class for the handling. No hooks are provided as far as I can see.
This is possible with the next upcoming version of ext:news, see this change for details.
By using the following TypoScript
config.recordLinks.tx_news {
typolink {
# Detail page
parameter.cObject = USER
parameter.cObject {
userFunc = GeorgRinger\News\Service\LinkHandlerTargetPageService->process
news.data = field:uid
# Page used if no detail page is set in the category
fallback = 123
}
additionalParams.data = field:uid
additionalParams.wrap = &tx_news_pi1[controller]=News&tx_news_pi1[action]=detail&tx_news_pi1[news]=|
}
}
and the according userfunc
<?php
declare(strict_types=1);
namespace GeorgRinger\News\Service;
use TYPO3\CMS\Core\Database\ConnectionPool;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer;
/**
* This file is part of the "news" Extension for TYPO3 CMS.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*/
class LinkHandlerTargetPageService
{
/** #var ContentObjectRenderer */
public $cObj;
public function process(string $content = '', array $configuration = []): int
{
$fallbackPageId = (int)($configuration['fallback'] ?? 0);
$newsId = (int)$this->cObj->stdWrapValue('news', $configuration, null);
if ($newsId === 0) {
return $fallbackPageId;
}
$singlePid = $this->getSinglePidFromCategory($newsId);
return $singlePid ?: $fallbackPageId;
}
/**
* Obtains a pid for the single view from the category.
*
* #param int $newsId
* #return int
*/
protected function getSinglePidFromCategory(int $newsId): int
{
$queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
->getQueryBuilderForTable('sys_category');
$categoryRecord = $queryBuilder
->select('title', 'single_pid')
->from('sys_category')
->leftJoin(
'sys_category',
'sys_category_record_mm',
'sys_category_record_mm',
$queryBuilder->expr()->eq('sys_category_record_mm.uid_local', $queryBuilder->quoteIdentifier('sys_category.uid'))
)
->where(
$queryBuilder->expr()->eq('sys_category_record_mm.tablenames', $queryBuilder->createNamedParameter('tx_news_domain_model_news', \PDO::PARAM_STR)),
$queryBuilder->expr()->gt('sys_category.single_pid', $queryBuilder->createNamedParameter(0, \PDO::PARAM_INT)),
$queryBuilder->expr()->eq('sys_category_record_mm.uid_foreign', $queryBuilder->createNamedParameter($newsId, \PDO::PARAM_INT))
)
->orderBy('sys_category_record_mm.sorting')
->setMaxResults(1)
->execute()->fetch();
return (int)$categoryRecord['single_pid'];
}
}
Of course you can copy the PHP Class to your site package and adopt the namespaces in the TS as well to have it working in your installation
In a custom extbase extension, I need to call a show action, passing it another value than uid (this is a continuation of Use other than primary key as RealURL id_field).
As the value "other than uid" is not resolved, it results in the exception http://wiki.typo3.org/Exception/CMS/1297759968
EDIT: here's the current working (but ugly) Controller code:
/**
* ItemController
*/
class ItemController extends \TYPO3\CMS\Extbase\Mvc\Controller\ActionController {
/**
* itemRepository
*
* #var \STUBR\Weiterbildung\Domain\Repository\ItemRepository
* #inject
*/
protected $itemRepository = NULL;
/**
* action list
*
* #return void
*/
public function listAction(\STUBR\Weiterbildung\Domain\Model\Item $item=null) {
if (!empty(\TYPO3\CMS\Core\Utility\GeneralUtility::_GET('tx_weiterbildung_pi1'))){
$this->forward('show');
}
$items = $this->itemRepository->findAll();
$this->view->assign('items', $items);
}
/**
* action show
*
* #param \STUBR\Weiterbildung\Domain\Model\Item $item
* #return void
*/
public function showAction(\STUBR\Weiterbildung\Domain\Model\Item $item=null) {
$tx_weiterbildung_pi1_get = \TYPO3\CMS\Core\Utility\GeneralUtility::_GET('tx_weiterbildung_pi1');
if (!empty($tx_weiterbildung_pi1_get)){
$dfid = intval($tx_weiterbildung_pi1_get['durchfuehrungId']);
}
$items = $this->itemRepository->findByDurchfuehrungId($dfid);
// helpful:
//\TYPO3\CMS\Extbase\Utility\DebuggerUtility::var_dump($item);
$this->view->assign('item', $items[0]);
}
}
PS here are the 5 lines that could be used in the Repository if the magical method wouldn't work:
//public function findByDurchfuehrungId($dfid) {
// $query = $this->createQuery();
// $query->matching($query->equals('durchfuehrungId', $dfid));
// return $query->execute();
//}
Yes, when you're using actions with model binding in param it will always look for object by field defined as ID - in our case it's always uid, but remember, that you do not need to bind model automatically, as you can retrive it yourself.
Most probably you remember, that some time ago I advised to use <f:link.page additionalParams="{originalUid:myObj.originalUid}"Click here</f:link.page> instead of <f:link.action...>
In that case your show action would look like this:
public function showAction() {
$item = $this->itemRepository->findByOriginalUid(intval(GeneralUtility::_GET('originalUid')));
$this->view->assign('item', $item);
}
Where findByOriginalUid should work magically without declaration, but even if it doesn't it's just matter of 5 lines in the repo ;)
Other sample
According to the code you pasted I'd use it rather like this, in this case listAction gets a role of dispatcher for whole plugin:
public function listAction() {
// access get param in array
$tx_weiterbildung_pi1_get = \TYPO3\CMS\Core\Utility\GeneralUtility::_GET('tx_weiterbildung_pi1');
$dfId = intval($tx_weiterbildung_pi1_get['durchfuehrungId']);
if ($dfId > 0) { // tx_weiterbildung_pi1 exists and is positive, that means you want to go to showAction
$item = $this->itemRepository->findByDurchfuehrungId($dfId);
if (!is_null($item)) { // Forward to showAction with found $item
$this->forward('show', null, null, array('item' => $item));
}else { // Or redirect to the view URL after excluding single item ID from GET params
$this->redirectToUri($this->uriBuilder->setArgumentsToBeExcludedFromQueryString(array('tx_weiterbildung_pi1'))->build());
}
}
// No `tx_weiterbildung_pi1` param, so it should be displayed as a listAction
$items = $this->itemRepository->findAll();
$this->view->assign('items', $items);
}
/**
* #param \STUBR\Weiterbildung\Domain\Model\Item $item
*/
public function showAction(\STUBR\Weiterbildung\Domain\Model\Item $item = null) {
$this->view->assign('item', $item);
}
Your finder should also getFirst() object if possible:
public function findByDurchfuehrungId($DfId) {
$query = $this->createQuery();
$query->matching($query->equals('durchfuehrungId', $DfId));
return $query->execute()->getFirst();
}
I wonder why no one ever asked this question.
Every zend Action function in controller class has 3 paramters, namely 'module', 'controller', and 'action'.
What happens, when I get a parameter named 'action' from a form or url, for example "?action=edit" ??
I tested it: action holds its value from router, not 'edit'.
public function someAction() {
$params = $this->getRequest()->getParams();
...
How could I pass the parameter named "action", if I had to ??
Thanks in advance.
The default route is Zend_Controller_Router_Route_Module which uses default keys for module, controller, & action:
protected $_moduleKey = 'module';
protected $_controllerKey = 'controller';
protected $_actionKey = 'action';
// ...
/**
* Set request keys based on values in request object
*
* #return void
*/
protected function _setRequestKeys()
{
if (null !== $this->_request) {
$this->_moduleKey = $this->_request->getModuleKey();
$this->_controllerKey = $this->_request->getControllerKey();
$this->_actionKey = $this->_request->getActionKey();
}
if (null !== $this->_dispatcher) {
$this->_defaults += array(
$this->_controllerKey => $this->_dispatcher->getDefaultControllerName(),
$this->_actionKey => $this->_dispatcher->getDefaultAction(),
$this->_moduleKey => $this->_dispatcher->getDefaultModule()
);
}
$this->_keysSet = true;
}
/**
* Matches a user submitted path. Assigns and returns an array of variables
* on a successful match.
*
* If a request object is registered, it uses its setModuleName(),
* setControllerName(), and setActionName() accessors to set those values.
* Always returns the values as an array.
*
* #param string $path Path used to match against this routing map
* #return array An array of assigned values or a false on a mismatch
*/
public function match($path, $partial = false)
{
$this->_setRequestKeys();
$values = array();
$params = array();
if (!$partial) {
$path = trim($path, self::URI_DELIMITER);
} else {
$matchedPath = $path;
}
if ($path != '') {
$path = explode(self::URI_DELIMITER, $path);
if ($this->_dispatcher && $this->_dispatcher->isValidModule($path[0])) {
$values[$this->_moduleKey] = array_shift($path);
$this->_moduleValid = true;
}
if (count($path) && !empty($path[0])) {
$values[$this->_controllerKey] = array_shift($path);
}
if (count($path) && !empty($path[0])) {
$values[$this->_actionKey] = array_shift($path);
}
if ($numSegs = count($path)) {
for ($i = 0; $i < $numSegs; $i = $i + 2) {
$key = urldecode($path[$i]);
$val = isset($path[$i + 1]) ? urldecode($path[$i + 1]) : null;
$params[$key] = (isset($params[$key]) ? (array_merge((array) $params[$key], array($val))): $val);
}
}
}
if ($partial) {
$this->setMatchedPath($matchedPath);
}
$this->_values = $values + $params;
return $this->_values + $this->_defaults;
}
You can see that the default module route has default keys for mvc params, however, it will use the keys set by the request object if it exists and we can modify these keys.
e.g. in your bootstrap:
class Bootstrap extends Zend_Application_Bootstrap_Bootstrap
{
protected function _initRequestKeys()
{
$this->bootstrap('frontcontroller');
$frontController = $this->getResource('frontcontroller');
/* #var $frontController Zend_Controller_Front */
$request = new Zend_Controller_Request_Http();
// change action key
$request->setActionKey("new_action_key");
// change module
$request->setModuleKey("new_module_key");
// change controller
$request->setControllerKey("new_controller_key");
// don't forget to set the configured request
// object to the front controller
$frontController->setRequest($request);
}
}
Now you can use module, controller, & action as $_GET params.
After a little testing it seems that how you pass the key "action" matters.
If you try and pass a parameter named "action" with $this->_request->getParams() you will get the controller action value key pair.
If you pass the "action" key from a form with $form->getValues() you will retrieve the value from the form element named "action".
As with so many things, your use case determines how you need to handle the situation.
Good Luck.