Using FAL in Extbase correctly - typo3

Domain model
class Image extends AbstractContent {
/**
* #var \TYPO3\CMS\Extbase\Domain\Model\FileReference
*/
protected $file;
/**
* Gets the image file
*
* #return \TYPO3\CMS\Extbase\Domain\Model\FileReference
*/
public function getFile() {
return $this->file;
}
/**
* Sets the image file
*
* #param \TYPO3\CMS\Extbase\Domain\Model\FileReference $file
* #return void
*/
public function setFile($file) {
$this->file = $file;
}
}
Import service fragments
/**
* #var \TYPO3\CMS\Core\Resource\ResourceStorage
*/
protected $defaultStorage;
[...]
$this->defaultStorage = ResourceFactory::getInstance()->getDefaultStorage();
[...]
$file = $this->defaultStorage->addFile(
'/tmp/4711',
$this->defaultStorage->getRootLevelFolder(),
'foo.jpg',
'overrideExistingFile'
);
$falReference = ResourceFactory::getInstance()->createFileReferenceObject(
array(
'uid_local' => $file->getUid(),
'uid_foreign' => uniqid('NEW_'),
'uid' => uniqid('NEW_'),
)
);
$reference = GeneralUtility::makeInstance(FileReference::class);
$reference->setOriginalResource($falReference);
$content = GeneralUtility::makeInstance(Image::class);
$content->setFile($reference);
After saving $content the image is available through the record and the filemount but the Ref column in BE > FILE > File List) is - and not >= 1. So its look like the reference is some how broken. When I'm using the BE to add an image to the record it's all fine. I'm using TYPO3 CMS 7.3-dev.
What's wrong with my code?

I get the hint in the Slack channel of TYPO3.
You just need to set plugin.tx_myext.persistence.updateReferenceIndex = 1 respectively module.tx_myext.persistence.updateReferenceIndex = 1 and the index will be updated.
Alternatively you could use \TYPO3\CMS\Core\Database\ReferenceIndex::updateRefIndexTable().

When I had to use FAL in my extension I found this link:
http://t3-developer.com/extbase-fluid/extensions-erweitern/fal-in-eigenen-extensions/fal-in-typo3-extensions-verwenden/
Since it is in German, I will in the very shortest explain what is done there:
extend your data model in ext_tables.sql
add a column of some char type (e.g. varchar)
add your column to the column section in your TCA array in ext_tables.php
'mypictures' => array(
'exclude' => 1,
'label' => 'My Pictures',
'config' => \TYPO3\CMS\Core\Utility\ExtensionManagementUtility::getFileFieldTCAConfig('image', array(
'appearance' => array(
'createNewRelationLinkTitle' => 'LLL:EXT:cms/locallang_ttc.xlf:images.addFileReference'
),
'minitems' => 0,
'maxitems' => 99,
), $GLOBALS['TYPO3_CONF_VARS']['GFX']['imagefile_ext']),
),
Extend your modelfiles. Pay attention to annotations!
You can use your media in your fluid template

Related

Restrict page media allowedExtensions TYPO3 9.5.x

How can I restrict allowedExtention just for $GLOBALS['TCA']['pages']['columns']['media']? But not using $GLOBALS['TYPO3_CONF_VARS']['GFX']['imagefile_ext'] as it will add restriction for all FAL fields.
I found class
class ImageManipulationElement extends AbstractFormElement
{
/**
* Default element configuration
*
* #var array
*/
protected static $defaultConfig = [
'file_field' => 'uid_local',
'allowedExtensions' => null, // default: $GLOBALS['TYPO3_CONF_VARS']['GFX']['imagefile_ext']
Looks like should be something like:
$GLOBALS['TCA']['pages']['columns']['media']['config']['overrideChildTca']['allowedExtensions'] = 'jpg, jpeg';
Hard way will drope all usefull things for this field.
$GLOBALS['TCA']['pages']['columns']['media'] = [
'exclude' => true,
'label' => 'LLL:EXT:frontend/Resources/Private/Language/locallang_tca.xlf:pages.media',
'config' => \TYPO3\CMS\Core\Utility\ExtensionManagementUtility::getFileFieldTCAConfig(
'media',
[], 'jpg, jpeg'
)
];
So not is our way. I need just some override like
$GLOBALS['TCA']['pages']['columns']['media']['config']['overrideChildTca']['columns']...['allowedExtention'] = 'jpg, jpeg';
Who know knows how to do this?
After small searching I found this example:
$allowExtensions = 'jpg,jpeg';
$GLOBALS['TCA']['pages']['columns']['media']['config']['filter'][0]['parameters']['allowedFileExtensions'] = '$allowExtensions;
$GLOBALS['TCA']['pages']['columns']['media']['config']['overrideChildTca']['columns']['uid_local']['config']['appearance']['elementBrowserAllowed']= $allowExtensions;
added to typo3conf/ext/myext/Configuration/TCA/Overrides/pages.php

How to update images stored as strings in extbase?

I have recently created an extension that has a file upload feature. I decided to store it as a string. I have used it like this:
In the controller:
public function initializeAction() {
if ($this->arguments->hasArgument('blog')) {
$this->arguments->getArgument('blog')->getPropertyMappingConfiguration()->setTargetTypeForSubProperty('image', 'array');
}
}
In the model:
/**
* Returns the image
*
* #return string $image
*/
public function getImage()
{
return $this->image;
}
/**
* Sets the image
*
* #param \array $image
* #return void
*/
public function setImage(array $image)
{
die(debug::var_dump($image));
if (!empty($image['name']))
{
$imageName = $image['name'];
$imageTempName = $image['tmp_name'];
$basicFileUtility = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance('TYPO3\\CMS\\Core\\Utility\\File\\BasicFileUtility');
$imageNameNew = $basicFileUtility->getUniqueName($imageName, \TYPO3\CMS\Core\Utility\GeneralUtility::getFileAbsFileName('uploads/tx_myextension/'));
\TYPO3\CMS\Core\Utility\GeneralUtility::upload_copy_move($imageTempName, $imageNameNew);
$this->image = basename($imageNameNew);
}
}
The TCA:
'config' => [
'type' => 'group',
'internal_type' => 'file',
'uploadfolder' => 'uploads/tx_myextension',
'show_thumbs' => 1,
'size' => 1,
'allowed' => $GLOBALS['TYPO3_CONF_VARS']['GFX']['imagefile_ext'],
'disallowed' => ''
],
In my form:
<f:form action="update" name="blog" object="{blog}" >
<f:form.upload property="image" class="form-control" />
...
Now this works perfectly when a new object is created, however when I try to change this image (using updateAction), I get the this error message:
Exception while property mapping at property path "image":
No converter found which can be used to convert from "string" to "array".
I would like to avoid uploading via FAL or writing my own conversion. I'm hoping that I just missed something trivial.
Take an look to an update script from tt_address to see how to make an update.
To make it short: You must read all entries of your files and move them from upload dir to your file storage and then you must add an file_reference which connect the sys_file with your domain model:
GeneralUtility::upload_copy_move(
PATH_site . 'uploads/tx_myextension/' . $imageName,
PATH_site . 'fileadmin/tx_myextension/' . $imageName);
$fileObject = $this->storage->getFile('fileadmin/tx_myextension/' . $imageName);
if ($fileObject instanceof \TYPO3\CMS\Core\Resource\File) {
$this->fileRepository->add($fileObject);
$dataArray = [
'uid_local' => $fileObject->getUid(),
'tablenames' => 'tx_your_domain_model',
'fieldname' => 'image',
'uid_foreign' => 'your model uid',
'table_local' => 'sys_file',
'cruser_id' => 999,
'pid' => 'your_model_pid',
'sorting_foreign' => $imageCount,
'sys_language_uid' => 0
];
if ($this->getDatabaseConnection()->exec_INSERTquery('sys_file_reference', $dataArray)) {
$imageCount++;
}
}
It was a very stupid and simple mistake from my part. In the edit form I forgot to add this: enctype="multipart/form-data"
<f:form action="update" enctype="multipart/form-data" name="blog" object="{blog}" >

Scanning translatable strings in zend 3 forms with Poedit

Zend 3 translates form labels automatically.
If forms are created using array specification, how is it possible to scan translatable form element strings with Poedit?
How to add translator->translate() functionality to forms? I tried the following in module.php onBootstrap method but this does not work:
$sm = $e->getApplication()->getServiceManager();
$vhm = $sm->get('ViewHelperManager');
$translator = $sm->get('MvcTranslator');
$vhm->get('form')->setTranslator($translator);
I want to use it like $form->translator->translate(), in such a way it would be possible to scan code with Poedit to find translatable labeles, placeholders etc.
Here's a TranslatorFactory if you need
final class TranslatorFactory implements FactoryInterface
{
public function __invoke(ContainerInterface $container, $requestedName, array $options = null)
{
// get translator files' paths from config
$paths = $container->get('config')['settings']['localization-paths'] ?? [];
$translator = new Translator();
// add zend-i18n-resources files to translator
$translator->addTranslationFilePattern(
'phpArray',
Resources::getBasePath(),
Resources::getPatternForValidator()
);
// add your translation files to translator
foreach ($paths as $path) {
$translator->addTranslationFilePattern('phpArray', $path, '%s.php');
}
// todo: replace with user's preferred language
$translator->setLocale('tr');
return $translator;
}
}
And add your factory to service manager
'service_manager' => [
'factories' => [
\Zend\I18n\Translator\TranslatorInterface::class => \MyModule\Factory\TranslatorFactory::class,
],
],
Not sure if you're still looking for a solution, so I'll add mine.
I use the TranslatorAwareTrait in my AbstractForm class.
use Zend\I18n\Translator\TranslatorAwareTrait;
abstract class AbstractForm extends \Zend\Form\Form implements
{
use TranslatorAwareTrait;
// Form stuff
}
Then, in the *FormFactory do the following:
use Zend\I18n\Translator\Translator;
use Zend\ServiceManager\Factory\FactoryInterface;
class SomeFormFactory implements FactoryInterface
{
/**
* #param ContainerInterface $container
* #param string $requestedName
* #param array|null $options
* #return mixed|object|AbstractForm
* #throws \Psr\Container\ContainerExceptionInterface
* #throws \Psr\Container\NotFoundExceptionInterface
*/
public function __invoke(ContainerInterface $container, $requestedName, array $options = null)
{
// Obviously you'll have more/other requirements. Fulfil them here.
$form = new SomeForm();
$form->setTranslator(
$container->get('translator')
);
return $form;
}
}
Usage example:
use Zend\I18n\Translator\TranslatorAwareTrait;
abstract class AbstractForm extends \Zend\Form\Form implements
{
use TranslatorAwareTrait;
public function init()
{
if (!$this->has('submit')) {
$this->addSubmitButton();
}
}
public function addSubmitButton($value = 'Save', array $classes = null)
{
$this->add([
'name' => 'submit',
'type' => Submit::class,
'attributes' => [
'value' =>
// Translate $value before passing to this function
$value === 'Save' ? $this->getTranslator()->translate('Save') : $value,
'class' => (!is_null($classes) ? join (' ', $classes) : 'btn btn-primary'),
],
]);
}
}
On the other hand, you could...
Translate strings before passing them if you're translating with Poedit.
If your modules contain the following config (in each module!):
'translator' => [
'translation_file_patterns' => [
[
'type' => 'gettext',
'base_dir' => __DIR__ . '/../language',
'pattern' => '%s.mo',
],
],
],
You can see here that translation is done using gettext. This is a PHP module that searches for the following code strings and translates its contents: _('translatable string').
The translation files to look for end with the .mo extension and can be found in __DIR__ . '/../language'.
Thus, if you make sure to have the PHP gettext module enabled to use this.
To use this with just normal strings, even in config for a Fieldset or a form, you could have the following:
$this->add([
'name' => 'street',
'required' => true,
'type' => Text::class,
'options' => [
'label' => _('Street'), // <<-- translated using gettext
],
]);

TYPO3 Extbase how to empty ObjectStorage

I want to "empty" an ObjectStorage when updating a Object:
It's TYPO3 4.6 with a Extbase Extension which allows you to show/add/edit/delete datasets in the frontend. At first sight everything looks good.
I have one field referencing another table:
TCA:
'partner' => array(
'exclude' => 0,
'label' => 'LLL:EXT:toco3_marketingdb/Resources/Private/Language/locallang_db.xlf:tx_toco3marketingdb_domain_model_firma.partner',
'config' => array(
'type' => 'select',
'size' => 5,
'foreign_table' => 'tx_toco3marketingdb_domain_model_partner',
'foreign_table_where' => 'ORDER BY tx_toco3marketingdb_domain_model_partner.partnerpkey',
'minitems' => 0,
'maxitems' => 20,
),
),
Model:
/**
* Partner
*
* #var Tx_Extbase_Persistence_ObjectStorage<Tx_Toco3Marketingdb_Domain_Model_Partner>
* #lazy
*/
protected $partner;
/**
* Sets the partner
*
* #param Tx_Extbase_Persistence_ObjectStorage<Tx_Toco3Marketingdb_Domain_Model_Partner> $partner
* #return void
*/
public function setPartner(Tx_Extbase_Persistence_ObjectStorage $partner) {
$this->partner = $partner;
}
Controller:
$partner = new Tx_Extbase_Persistence_ObjectStorage();
if (count($partnerarr) > 0){
foreach($partnerarr as $p){
$partner->attach( $this->partnerRepository->findByUid($p));
}
}
$organisation = $this->organisationRepository->findByUid($uid)
$organisation->setPartner($partner);
This is working as long there is an Object in the ObjectStorage. So I can add/delete/change relations. But when $partnerarr is empty an no objects get attached an empty Tx_Extbase_Persistence_ObjectStorage is assigned, the old values do not get "deleted". I also tried to assign null or "" but the an error occures because an ObjectStorage is needed. If I assign the empty ObjectStorage I don't get an error, but the old values still maintain :(
Any idea?
Thank you
Christian
Call the detach or removeAll methods to remove certain or all objects of the storage.
/** #var \Tx_Extbase_Persistence_ObjectStorage $organisationPartners */
$organisationPartners = $organisation->getPartner();
foreach ($organisationPartners as $partner) {
$organisationPartners->detach($partner);
}
Thank you #Wolfgang for your message.
I added the following function to my model:
/**
* detach Partner
*
* #param Tx_Toco3Marketingdb_Domain_Model_Partner $partner
* #return void
*/
public function detachPartner($partner) {
$this->partner->detach($partner);
}
In the controller I added:
$persistanceManager = t3lib_div::makeInstance('Tx_Extbase_Persistence_Manager');
$organisation = $this->firmaRepository->findByUid($uid);
$organisationPartners = $organisation->getPartner();
foreach ($organisationPartners as $organisationPartner) {
$organisation->detachPartner($organisationPartner);
}
$persistanceManager->persistAll();
$organisation->setPartner($partner);
It is important to persist before setting the new (empty) value...

Extbase ObjectStorage returns a hashcode. But why?

I was just extending an existing Typo3 4.7 Extension with two own Model classes.
It runs quite well, Backendforms look like expected BUT when I try to access some SubObjects of my Model Classes in the templates via Object Accessor {class.subclass.attribute} I am not able to access the attribute. The problem that showed me, is that the Object for the Attribute "mainColor" for example in the Object Storage is a HashCode, which contains the Actual Object I want to access ( the object following the hashcode is the correct related object from the database ).
Does anyone of you have an Idea where the problem might be ?
If any more Code Snippets are needed, I will deliver them. But since I really don't know where the problem comes from, I prefer to not deliver a wall of Code.
Domain/Model/Cluster.php
/**
* Main Color of Cluster
* #var Tx_Extbase_Persistence_ObjectStorage<Tx_Alp_Domain_Model_ColorCombination> $mainColor
*/
protected $mainColor;
/**
* Subcolors of Cluster
* #var Tx_Extbase_Persistence_ObjectStorage<Tx_Alp_Domain_Model_ColorCombination> $subColors
*/
protected $subColors;
/**
* Constructor
* #return void
*/
public function __construct() {
$this->initStorageObjects();
}
/**
* Initializes all Tx_Extbase_Persistence_ObjectStorage properties.
* #return void
*/
protected function initStorageObjects() {
$this->subColors = new Tx_Extbase_Persistence_ObjectStorage();
$this->mainColor = new Tx_Extbase_Persistence_ObjectStorage();
}
TCA/Cluster.php
'sub_colors' => array(
'exclude' => 1,
'label' => 'Sub-Colors',
'config' => array(
// edited
'type' => 'inline',
'internal_type' => 'db',
'allowed' => 'tx_alp_domain_model_colorcombination',
'foreign_table' => 'tx_alp_domain_model_colorcombination',
'MM' => 'tx_alp_cluster_subcolorcombination_mm',
'MM_opposite_field' => 'parent_cluster',
'size' => 6,
'autoSizeMax' => 30,
'maxitems' => 9999,
'multiple' => 0,
'selectedListStyle' => 'width:250px;',
'wizards' => array(
'_PADDING' => 5,
'_VERTICAL' => 1,
'suggest' => array(
'type' => 'suggest',
),
),
),
),
Fluid Debug Output can be found here:
http://i60.tinypic.com/28kluub.jpg
Thanks for any help :(
And sorry for my bad English and this is my first question here, hope I did it right ;)
Unless you have a 1:1 relation from a model to a sub model, you cannot access the sub model because the sub model is an ObjectStorage.
Example:
Domain/Model/Cluster.php
/**
* Main Color of Cluster
* #var Tx_Alp_Domain_Model_ColorCombination $mainColor
*/
protected $mainColor;
This means that the Cluster model has exacly one main color (mind the annotation), this is a 1:1 relation.
Therefore using {cluster.mainColor.property} will work.
What you are doing is:
Domain/Model/Cluster.php
/**
* Main Color of Cluster
* #var Tx_Extbase_Persistence_ObjectStorage<Tx_Alp_Domain_Model_ColorCombination> $mainColor
*/
protected $mainColor;
This means that every Cluster can have multiple main colors, this is a 1:n relation. Therefore you must iterate through the mainColors (and call the property $mainColors):
<f:for each="{cluster.mainColors}" as="mainColor">
{mainColor.property}
</f:for>