How to read value from plugin flexform in TypoScript? - typo3

I have a plugin for a custom content element. The plugin contains a flexform. I'd like to use a value from the flexform in my TypoScript setup. How can I do this?
More specifically, the flexform is defined as:
<T3DataStructure>
<meta>
<langDisable>1</langDisable>
</meta>
<sheets>
<sDEF>
<ROOT>
<TCEforms>
<sheetTitle>LLL:EXT:cb_foundation/Resources/Private/Language/locallang_db.xlf:magellan.sheetTitle</sheetTitle>
</TCEforms>
<type>array</type>
<el>
<settings.magellan.cols>
<TCEforms>
<label>LLL:EXT:cb_foundation/Resources/Private/Language/locallang_db.xlf:magellan.cols</label>
<config>
<type>input</type>
<size>20</size>
<eval>trim</eval>
</config>
</TCEforms>
</settings.magellan.cols>
</el>
</ROOT>
</sDEF>
</sheets>
</T3DataStructure>
My custom element is added to tt_content using the following TypoScript:
lib.cb_foundation.magellan = HMENU
lib.cb_foundation.magellan {
1 = TMENU
1 {
sectionIndex = 1
sectionIndex.type = header
sectionIndex.useColPos = 0
wrap = <div data-magellan-expedition="fixed"><dl class="sub-nav"> | </dl></div>
NO {
allWrap = <dd data-magellan-arrival="c{field:sectionIndex_uid}">|</dd>
allWrap.insertData = 1
}
}
special = list
special.value.data = page:uid
}
tt_content.cbfoundation_magellan =< lib.cb_foundation.magellan
What I want to do is to set tt_content.cbfoundation_magellan.1.sectionIndex.useColPos using the value found in the flexform. How can I do that?

This is not possible. Typoscript does not parse values from a flexform, unfortunately.
You need an extbase controller or pi1 to get the values from the flexform and assign them to a separate template which renders your custom content element.
Extbase Example:
class ContentElementsController extends \TYPO3\CMS\Extbase\Mvc\Controller\ActionController {
public function YourContentElementAction() {
// Get the data object (contains the tt_content fields)
$data = $this->configurationManager->getContentObject()->data;
// Append flexform values
$this->configurationManager->getContentObject()->readFlexformIntoConf($data['pi_flexform'], $data);
// Assign to template
$this->view->assign('data', $data);
}
}

I dont know, if the extension gridelements or TYPO3 6.2.26 bring the functionality, but the following works for me:
dataWrap = {field:flexform_YOURFIELD}

The following StackOverflow answer describes, how plugin flexform settings of EXT News can be accessed from TS Setup using a small PHP USER Class:
https://stackoverflow.com/a/40006158/430742
It should be easy to adapt for other plugins and their flexform settings.

As stated here there is a solution now in TYPO3 >= 8.4.
The feature is documented in the TYPO3 Core Changelog.

Related

Issues with DCE on TYPO3 upgrade

I have Upgraded typo3 from 9.5.5 to 10.4.19 and DCE from 2.03.x to 2.6.2
After the DCE upgrade, the Image upload field configuration seems wrongly changed (https://nimb.ws/slTyH8). The images are not in those fields and in Frontend the images are present which is the cache.
I have tried changing the old configuration of DCE to the new one. The field is okay now, But the website is refreshed and old images are gone.
I have more than 200 pages. So impossible to reupload. I have uploaded my backup and now I am at the previous state.
Checked the install tool, Unfortunately, there are no Upgrade wizards present for DCE. How can I make it correct without losing images?
Help me!
It is the change from old image handling to FAL-based referencing images. In former versions of DCE, the filename has been saved in tt_content.pi_flexform and there was a predefined/configured path, where this file should be searched. For some time, in TYPO3 files are handled via FAL and so as a relation to an entry of table sys_file...
A few weeks ago, a migrated some DCE elements for a customer. Instead of programming an upgrade-wizard, some lines of PHP were used.
Step 1: Export relevant data
Export the 'uid' of your content elements and filename within your DCE.
For the DCE with UID=2 whre the files field is called "image" this can be done via:
SELECT ExtractValue(pi_flexform, '//field[#index="settings.image"]/value') as filename, `uid`
FROM `tt_content`
WHERE CType='dce_dceuid2' AND ExtractValue(pi_flexform, '//field[#index="settings.image"]/value')!='';
Export the result as CSV.
Step 2: Create releation in sys_file_reference and update tt_content
Now we have to manage the changes to the database:
Create FAL-relations between the DCEs and the sys_file-entries (in form of sys_file_reference-records)
Update the pi_flexform of existing DCE content elements
Update the structure of old-styled DCEs
For these steps, I generated SQL-Queries with a PHP function:
function process(string $csv, string $CType, string $fieldName = 'image'): void
{
foreach (explode(chr(10), $csv) as $line) {
[$fileName, $uid] = explode(';', $line);
if ($fileName && $fileName !== '""') {
$uid = trim($uid);
echo htmlentities("INSERT INTO sys_file_reference (uid_local, uid_foreign, tablenames, fieldname, table_local) VALUES((SELECT uid FROM sys_file WHERE identifier=\"/_migrated_/pics/" . $fileName . "\"), $uid, 'tt_content', '" . $fieldName . "', 'sys_file');",
ENT_QUOTES | ENT_HTML5) . chr(10);
}
}
echo htmlentities('UPDATE tt_content SET pi_flexform=UpdateXML(`pi_flexform`, \'//field[#index="settings.' . $fieldName . '"]/value\', \'<value index=\"vDEF\">1</value>\') WHERE uid=437 AND CType=\'' . $CType . '\' AND ExtractValue(pi_flexform, \'//field[#index="settings.' . $fieldName . '"]/value\')!=\'\';',
ENT_QUOTES | ENT_HTML5) . chr(10);
$configuration = '<config>
<type>inline</type>
<foreign_table>sys_file_reference</foreign_table>
<foreign_field>uid_foreign</foreign_field>
<foreign_sortby>sorting_foreign</foreign_sortby>
<foreign_table_field>tablenames</foreign_table_field>
<foreign_match_fields>
<fieldname>{$variable}</fieldname>
</foreign_match_fields>
<foreign_label>uid_local</foreign_label>
<foreign_selector>uid_local</foreign_selector>
<overrideChildTca>
<columns>
<uid_local>
<config>
<appearance>
<elementBrowserType>file</elementBrowserType>
<elementBrowserAllowed>gif,jpg,jpeg,png</elementBrowserAllowed>
</appearance>
</config>
</uid_local>
</columns>
<types type="array">
<numIndex index="2">
<showitem>--palette--;LLL:EXT:core/Resources/Private/Language/locallang_tca.xlf:sys_file_reference.imageoverlayPalette;imageoverlayPalette,--palette--;;filePalette</showitem>
</numIndex>
</types>
</overrideChildTca>
<minitems>0</minitems>
<maxitems>1</maxitems>
<appearance>
<useSortable>1</useSortable>
<headerThumbnail>
<field>uid_local</field>
<width>45c</width>
<height>45</height>
</headerThumbnail>
<enabledControls>
<info>1</info>
<dragdrop>1</dragdrop>
<hide>1</hide>
<new>0</new>
<sort>0</sort>
<delete>1</delete>
</enabledControls>
<createNewRelationLinkTitle>LLL:EXT:frontend/Resources/Private/Language/locallang_ttc.xlf:images.addFileReference</createNewRelationLinkTitle>
</appearance>
<behaviour>
<allowLanguageSynchronization>1</allowLanguageSynchronization>
</behaviour>
<dce_load_schema>1</dce_load_schema>
<dce_get_fal_objects>1</dce_get_fal_objects>
</config>
';
echo 'UPDATE tx_dce_domain_model_dcefield SET configuration=\'' . htmlentities($configuration, ENT_QUOTES | ENT_HTML5) . '\' WHERE variable=\'' . $fieldName . '\' AND parent_dce=' . str_replace('dce_dceuid', '', $CType) . ';' . chr(10);
}
Now, we need to combine the CSV with the code:
$csv = 'image_1.jpg;82
typo3_box.jpg;904';
process($csv, 'dce_dceuid2', 'image');
This will not change anything, but only print out the necessary SQL-queries, which can be reviewed, copied and fired.
Thank you everyone for these detailed explanations.
But We found another method to solve. When analyzing the issue, The Old configuration for the image is having field name as<fieldname>{$bannerimage}</fieldname> And in the database the sys_file_reference table's field name contains the same variable as '{$bannerimage}
What we have done to solve the issue is:
Updated the image configuration to the newest conf and then replaced the <fieldname>{$variable}</fieldname> to <fieldname>bannerimage</fieldname>
The same is done in the DB too. fieldname changed from {$bannerimage} to bannerimage This fixed all problems without losing the image.

Any way to set PrimaryHeader from codebehind in ShieldUi BarChart

I am using the asp.net version of shieldui for charts and have a barchart and would like to set the primaryHeader or secondaryHeader with data from the database. I searched the documentation page of shieldui website and could not find anything.
What I tried is something like this:
<shield:ShieldChart ID="ShieldChart1" Width="100%" Height="200px" runat="server"
OnTakeDataSource="ShieldChart1_TakeDataSource"
CssClass="chart">
<PrimaryHeader Text=" ">
<Style Bold="true" FontSize="30px" />
</PrimaryHeader>
<SecondaryHeader Text="<asp:Label id="CardInvoiced" runat="server"/> ">
</SecondaryHeader>
<ExportOptions AllowExportToImage="false" AllowPrint="false" />
<Axes>
<shield:ChartAxisX
CategoricalValuesField="Area">
</shield:ChartAxisX>
<shield:ChartAxisY>
</shield:ChartAxisY>
</Axes>
<DataSeries>
<shield:ChartBarSeries DataFieldY="Sales" CollectionAlias="signups">
</shield:ChartBarSeries>
<shield:ChartBarSeries DataFieldY="Starts" CollectionAlias="starts">
</shield:ChartBarSeries>
<shield:ChartBarSeries DataFieldY="bis" CollectionAlias="bis">
</shield:ChartBarSeries>
<shield:ChartBarSeries DataFieldY="comps" CollectionAlias="comps">
</shield:ChartBarSeries>
</DataSeries>
</shield:ShieldChart>
ShieldUI error is:
Type 'Shield.Web.UI.ShieldChart' does not have a public property named 'Label'.
Any suggestion on how to accomplish this?
I solved this and here is how you set both PrimaryHeader and SecondaryHeader in ShieldUi graphs:
ShieldChartFSS7R.PrimaryHeader.Text = "Test Primary Header";
ShieldChartFSS7R.SecondaryHeader.Text = "Test Secondary Header";
Which shows the headers like this:

Magento2- How to copy custom data from quote to order and order item

I am building a custom module in Magento 2 that has a custom discount. I am trying to copy the discount from quote, quote item to order and order item.
In Magento 1, I declare the config.xml like this:
<fieldsets>
<sales_convert_quote_address>
<custom_discount_amount><to_order>*</to_order></custome_discount_amount>
<base_custom_discount_amount><to_order>*</to_order></base_custome_discount_amount>
</sales_convert_quote_address>
<sales_convert_quote_item>
<custome_discount_amount><to_order_item>*</to_order_item></custome_discount_amount>
<base_custom_discount_amount><to_order_item>*</to_order_item></base_custom_discount_amount>
</sales_convert_quote_item>
</fieldsets>
and my custom discount amount was copied to tables: sales_flat_order and sales_flat_order_item as expected.
In Magento 2, I created a file named fieldset.xml with this code:
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="../../../../../lib/internal/Magento/Framework/Object/etc/fieldset.xsd">
<scope id="global">
<fieldset id="sales_convert_quote_item">
<field name="custom_discount_amount">
<aspect name="to_order_item" />
</field>
<field name="base_custom_discount_amount">
<aspect name="to_order_item" />
</field>
</fieldset>
<fieldset id="sales_convert_quote_address">
<field name="custom_discount_amount">
<aspect name="to_order" />
</field>
<field name="base_custom_discount_amount">
<aspect name="to_order" />
</field>
</fieldset>
</scope>
</config>
but there is no success.
What else do I need to do in Magento 2 to make it work? Can you guys please help me?
In Magento 2 without using fieldset you can also copy custom data from quote item to order item by using plugin.
create di.xml in your module etc folder.
app/code/Vender/Yourmodule/etc/di.xml
<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../lib/internal/Magento/Framework/ObjectManager/etc/config.xsd">
<type name="Magento\Quote\Model\Quote\Item\ToOrderItem">
<plugin name="cedapi_quote_to_order_item" type="Vender\Yourmodule\Model\Plugin\Quote\QuoteToOrderItem"/>
</type>
</config>
Create a class to your module and define a function. app/code/Vender/Yourmodule/Model/Plugin/Quote
Create QuoteToOrderItem.php file
<?php
namespace Vender\Yourmodule\Model\Plugin\Quote;
use Closure;
class QuoteToOrderItem
{
/**
* #param \Magento\Quote\Model\Quote\Item\ToOrderItem $subject
* #param callable $proceed
* #param \Magento\Quote\Model\Quote\Item\AbstractItem $item
* #param array $additional
* #return \Magento\Sales\Model\Order\Item
* #SuppressWarnings(PHPMD.UnusedFormalParameter)
*/
public function aroundConvert(
\Magento\Quote\Model\Quote\Item\ToOrderItem $subject,
Closure $proceed,
\Magento\Quote\Model\Quote\Item\AbstractItem $item,
$additional = []
) {
/** #var $orderItem \Magento\Sales\Model\Order\Item */
$orderItem = $proceed($item, $additional);//result of function 'convert' in class 'Magento\Quote\Model\Quote\Item\ToOrderItem'
$orderItem->setCustomDesign($item->getCustomDesign());//set your required
return $orderItem;// return an object '$orderItem' which will replace result of function 'convert' in class 'Magento\Quote\Model\Quote\Item\ToOrderItem'
}
}
After spend some time and research issue, i stucked here:
Magento\Quote\Model\QuoteManagement.php
line 446
public function mergeDataObjects(
$interfaceName,
$firstDataObject,
$secondDataObject
) {
if (!$firstDataObject instanceof $interfaceName || !$secondDataObject instanceof $interfaceName) {
throw new \LogicException('Wrong prototype object given. It can only be of "' . $interfaceName . '" type.');
}
$secondObjectArray = $this->objectProcessor->buildOutputDataArray($secondDataObject, $interfaceName);
$this->_setDataValues($firstDataObject, $secondObjectArray, $interfaceName);
return $this;
}
Which ignores converted attributes because logic of merging based on presence getters and setters of target model\interface. So, if you converting attributes which haven't setters and getters in target model they will be ignored here:
Magento\Framework\Reflection\DataObjectProcessor.php line 75
public function buildOutputDataArray($dataObject, $dataObjectType)
{
$methods = $this->methodsMapProcessor->getMethodsMap($dataObjectType);
$outputData = [];
/** #var MethodReflection $method */
foreach (array_keys($methods) as $methodName) {
if (!$this->methodsMapProcessor->isMethodValidForDataField($dataObjectType, $methodName)) {
continue;
}
$value = $dataObject->{$methodName}();
$isMethodReturnValueRequired = $this->methodsMapProcessor->isMethodReturnValueRequired(
$dataObjectType,
$methodName
);
Maybe you might to use observer or plugin to avoid this problem. (issue encountered in 2.0.6 Magento version)
For anybody looking at this in the future, the fact that the fieldset XML doesn't work has been acknowledged by Magento as a bug. There is a core patch available in the ticket (not reproduced here since it may need to be tweaked with new Magento versions).
https://github.com/magento/magento2/issues/5823

Action based navigation in Zend Framework

i have navigation menu which is displayed to users based on certain action of the controller. here is what i am doing.
//in each controller-action where i want "action navigation menu"
public function indexAction()
{
$this->_helper->navigation()->renderActionNavigation();
}
public function newAction()
{
$this->_helper->navigation()->renderActionNavigation();
}
the navigation menu is displayed accordingly. here is my navigation.xml file.
<?xml version="1.0" encoding="UTF-8"?>
<configdata>
<item-index>
<new-item>
<label>New Item</label>
<route>admin-item-new</route>
</new-item>
<delete-item>
<label>Delete Item</label>
<uri>#</uri>
</delete-item>
</item-index>
<item-new>
<publish-unpublish-item>
<label>Save & Close</label>
<uri>#</uri>
</publish-unpublish-item>
<delete-item>
<label>Save & New</label>
<uri>#</uri>
</delete-item>
</item-new>
</configdata>
the parent element of each navigation menu represents a naming convention in the above navigation.xml file for example
`<item-index>` represents item{controller}index{action}
`<item-new>` represents item{controller}new{action}
//and so on
here is the action helper. Navigation.php i am using
class Zend_Controller_Action_Helper_Navigation extends Zend_Controller_Action_Helper_Abstract
{
private $_view = null;
public function direct()
{
$this->_view = Zend_Layout::getMvcInstance()->getView();
$this->_view->placeholder('action-navigation');
return $this;
}
public function renderActionNavigation()
{
$config = new Zend_Config_Xml(
APPLICATION_PATH.'/configs/navigation.xml', strtolower(
$this->getRequest()->getControllerName().'-'.
$this->getRequest()->getActionName()
)
);
$container = new Zend_Navigation($config);
$this->_view->partial('partials/_action-navigation.phtml', array('container' => $container));
}
}
and finally _action-navigation.phtml
<?php $this->placeholder('action-navigation')->captureStart(); ?>
<div class="statsRow">
<div class="wrapper" >
<?php foreach($this->container as $page): ?>
<?php endforeach; ?>
</div>
</div>
<?php $this->placeholder('action-navigation')->captureEnd(); ?>
My directory structure is as follows
/application
/layouts
admin.phtml
default.phtml
/modules
/admin
/controllers
/helpers
/Navigation.php
IndexController.php
/views
/helpers
/scripts
/partials
_action-navigation.pthml
sidebar.phtml
/index
/item
the weird behavior i am experiencing is. in my Bootstrap.php file there is an empty _initView() method. my application works properly if this method exist. note that this method is empty. but when i remove it it gives me following error.
Application error
Exception information:
Message: script 'partials/_action-navigation.phtml' not found in path (./views/scripts/)
i am not able to understand this behavior by Zend Framework. how is action-navigation code related to _initView method in Bootstrap? what is happening and any fix for this or any suggestion for improvement of my code?
Update:
The problem lies with this line of code
$this->_view->partial('partials/_action-navigation.phtml', array('container' => $container));
You forgot to add the name of the admin module as second argument:
$this->_view->partial('partials/_action-navigation.phtml', 'admin', array('container' => $container));
I Suggest you use this clean and lightweight way, removing your dependence to inform actual module and mantaining your view script clean without captureStart and End (it's used ob_start... when u do captureStart-end)
$navigation = $this->view->navigation()->menu()->renderPartial($container, 'partials/_action-navigation.phtml');
// U can use placeholders `append` and `prepend` methods too, if u need more control in your placeholder content
$this->view->placeholder('action-navigation')->set($navigation);

Magento: Email template crashes new template form

I have a module that I'm trying to use with an email template I created. I created the .phtml template directly (not through the new template form on the backend) into the locale > en_US > template > email folder. The template seems to work as the variables passed to it work and the email gets sent fine. My only problem is that now when I go into the management > Transactional Emails > New Template, the page crashes. The dropdown is empty and everything after it does't get rendered.
I think it might have something to do with the way I'm loading the template in the modules config.xml. When I remove the reference to the template the problem goes away. Put the reference back in and the form crashes..
Config.xml
<?xml version="1.0"?>
<config>
<modules>
<Optimise_Requestcallback>
<version>0.1.9</version>
</Optimise_Requestcallback>
</modules>
<frontend>
<routers>
<requestcallback>
<use>standard</use>
<args>
<module>Optimise_Requestcallback</module>
<frontName>request-callback</frontName>
</args>
</requestcallback>
</routers>
<layout>
<updates>
<requestcallback>
<file>optimise.xml</file>
</requestcallback>
</updates>
</layout>
</frontend>
<global>
<template>
<email>
<requestcallback_template translate="label" module="requestcallback">
<label>Optimise RequestCallback</label>
<file>requestcallback_template.html</file>
<type>html</type>
</requestcallback_template>
</email>
</template>
</global>
</config>
Here is how I send the email:
public function sendemailAction() {
$emailTemplate = Mage::getModel('core/email_template')
->loadDefault('requestcallback_template');
$emailTemplateVariables = array();
//Fetch submited params
$params = $this->getRequest()->getParams();
$subjectOfMail = "Request a Callback from the Puji Website<br /><br />Product = " . $params['product'] . "<br />Name = " . $params['name'] . "<br />Email = " . $params['email'] . "<br />Telephone = " . $params['telephone'] . "<br />Message = " . $params['comment'];
$emailTemplateVariables['body'] = $subjectOfMail;
$emailTemplate->setSenderName($params['name']);
$emailTemplate->setSenderEmail($params['email']);
try {
$emailTemplate->send('billy#optimiseweb.co.uk', 'Sales', $emailTemplateVariables);
Mage::getSingleton('core/session')->addSuccess('Thank you! We will contact you very soon.');
} catch (Exception $ex) {
$translate->setTranslateInline(true);
Mage::getSingleton('customer/session')->addError(Mage::helper('contacts')->__('Unable to submit your request. Please, try again later'));
$this->_redirect('*/*/');
return;
}
//Redirect back to index action of (this) activecodeline-simplecontact controller
$this->_redirect('request-callback/');
}
And the template itself probably couldn't be any simpler!
<!--#subject Request a Callback from the Puji Website #-->
{{var body}}
Can anyone see an issue here that would cause the New template form to crash?
There can be various reasons, but first check config.xml file for each custom module, one-by-one.
There must be one module, where you will find code like:
module="[some-module-name-here]"
Try removing this code one-by-one and reload the transactional e-mail template form again.
I am sure, it will solve the problem.