FluidTYPO3 / flux

TYPO3 extension Flux: Dynamic Fluid FlexForms
https://fluidtypo3.org
148 stars 214 forks source link

flux:field.file for TYPO3 11 and 12 #2139

Closed ms-wa closed 7 months ago

ms-wa commented 8 months ago

Is flux:field.file now supported in TYPO3 11 or 12?

When I read the history of the viewhelper the comment was removed with deprecate.

mdittberner commented 7 months ago

I also recognized that the deprecation hints were removed. The file field is still working. BUT: Because the changes in TYPO3 TCA the browse button tries to display db records instead of files. This is caused by a change where the TCA config "appearance" in not evaluated anymore (see https://docs.typo3.org/m/typo3/reference-tca/12.4/en-us/ColumnsConfig/Type/Group/Properties/Appearance.html). The according data-mode attribute of the browsebutton is always set to "db". Only for "real" file relations it is possible to browse files. Since we cannot use inline relations inside sections this sucks. The current version of flux tries to change it to "file" in FluidTYPO3\Flux\Form\Field\File but as mentioned the appearance is ignored. I spent many hours to solve the problem (even a migration wizard to create file references and use them with an own viewhelper). But I found no solution for inherited images in page templates.

So i thought in another way and found a workaround writing a little XClass which restores the original behaviour of the element browser.

class ElementBrowser extends \TYPO3\CMS\Backend\Form\FieldControl\ElementBrowser
{
    /**
     * Modify result of the original render function if TCA config myFilebrowserFix is set.
     *
     * @return array
     */
    public function render(): array
    {
        $fieldConfig = $this->data['parameterArray']['fieldConf']['config'];
        if($fieldConfig['myFilebrowserFix']) {
            $configArray = parent::render();
            if(is_array($configArray)) {
                if(array_key_exists('linkAttributes', $configArray)) {
                    // This makes the folder button a file selector instead a db selector.
                    $configArray['linkAttributes']['data-mode'] = 'file';
                }
            }
            return $configArray;
        } else {
            return parent::render();
        }
    }

}

In this way I can restore the original behaviour of the file browser for a flux field by adding config="{myFilebrowserFix: 'true'}" to the flux:field.file in the template. @NamelessCoder Maybe this could be worth a look to make flux file fields working again in TYPO3 12 LTS.

Regards, Michael

NamelessCoder commented 7 months ago

Is flux:field.file now supported in TYPO3 11 or 12?

It is supported, but there are limitations imposed by the TYPO3 core. Most notably that you cannot use it within sections / section objects. Historically this field has been a group with internal_type but TYPO3 no longer supports that - so it is now silently rewritten to become a file type which is supported on all TYPO3 versions.

The reason all the individual field types were initially deprecated in Flux was that heavy changes were being made to TCA constantly through all supported versions since 8.7, and keeping up with all of those changes to internally rewrite types on each version was becoming unfeasible. The substitute idea was to provide a more generic field ViewHelper where each editor was responsible for choosing the type, renderType and config (while being careful to select the right ones for their TYPO3 version) - but, since 10.4 the TCA specs seem to have at least partially stabilised enough that those deprecations did not need to be removed. So, the special field type ViewHelpers in Flux survive at least for now, but if the TCA specs are hit with drastic changes again, which would require heavy workarounds for multiple core versions, they may yet be deprecated and removed at some point. The safest option will always be flux:field with type and config specified in a way that's compatible with your TYPO3 version!

@mdittberner Thanks for posting your workaround. I know that this response won't be the one you (and many others) are looking for, but I should explain something about Flux that might help to explain why Flux won't be doing things like adding an XCLASS that overrides a core class to re-animate some feature that was (partially, incompletely) killed off by the TYPO3 core:

Flux fields are merely an abstraction over TCA. The purpose is to allow you to do literally anything that's possible to do with TCA, but with a proper object-and-ViewHelper API instead of a huge anonymous array. Flux is not, and never was, designed to change how TCA works and what is possible to do with TCA. This means that if the TYPO3 core decides that something is no longer possible (group with "file" type, wizards as sub-arrays, etc.) then Flux will simply follow suit and like with wizards straight up remove all of those ViewHelpers, or in some cases like with "file" choose the closest reasonably matching type of field and create a config array for that type instead (although sometimes that means some properties that were supported, are now ignored - like with appearance).

The mantra basically is: if you can do it with TCA, you can do it with Flux - and if you can do it with Flux, you can do it with TCA (but you'd need to write a hell of a lot more TCA than Flux code!). If you can't do it with TCA you also can't do it with Flux.

My advise will always be to migrate your implementations as soon as you discover that a certain thing - such as group with "file" type - will become impossible in the future. What this particular XCLASS will do, will just be to prolong the pain. The field type/combo is marked for death and it will die - it's only a coincidence that for some reason, the JS component is still capable of reading and using the data-mode="file" attribute; I can pretty much guarantee you that this will also be removed as soon as someone finds out it is still there. So while your XCLASS might allow you to keep going on v12 for now, you should fully expect that as soon as v13 is released, this too will break and it will become even more difficult, if not altogether impossible, to work around it. This is precisely the reason why Flux doesn't even begin to work around things this way: it starts a path towards regression and will inevitably end in a dead end, with each step making it more and more difficult for users to change their implementations to something that does work in TCA.

Lastly I should say that I do often strongly disagree with decisions made in the TYPO3 core especially concerning the removal of features or locking-in of API, but it's a reality that I've tried and failed to change many, many times with each attempt costing a lot of time and causing a lot of grief. Unfortunately, once it's decided to chop off parts of the API then nothing I can say or do will change it. Sometimes, an XCLASS re-animates what should be dead but inevitably it gets killed for good anyway. So it's best not to try, accept defeat and bite the migration bullet. I hate to say that, but it really is.

mdittberner commented 7 months ago

@NamelessCoder Have many thanks to Your detailed answer and your clear words. I supposed that there are good reasons for never touching core behaviour with an XClass. Your statements brought more light in the darkness. My favourite was " I can pretty much guarantee you that this will also be removed as soon as someone finds out it is still there.". Hart gefeiert! ^^

NamelessCoder commented 7 months ago

You're most welcome, @mdittberner - and sorry I couldn't give you better news. It's not a fun situation this one, all the way around!

grischpel commented 2 months ago

I have a solution:

<?php

  namespace Vendor\Extensionname\ViewHelpers\Data;

  use TYPO3\CMS\Extbase\Utility\LocalizationUtility;
  use TYPO3Fluid\Fluid\Core\Rendering\RenderingContextInterface;
  use TYPO3Fluid\Fluid\Core\ViewHelper\AbstractViewHelper;
  use TYPO3Fluid\Fluid\Core\ViewHelper\Exception;
  use TYPO3Fluid\Fluid\Core\ViewHelper\Traits\CompileWithRenderStatic;
  use TYPO3\CMS\Core\Utility\DebugUtility;

  class ImageconfigViewHelper extends AbstractViewHelper {

    use CompileWithRenderStatic;

    /**
     * Output is escaped already. We must not escape children, to avoid double encoding.
     *
     * @var bool
     */
    protected $escapeChildren = false;

    public function initializeArguments() {
        $this->registerArgument('type', 'string', 'File type');
    }

    /**
     * Return array element by key.
     *
     * @param array $arguments
     * @param \Closure $renderChildrenClosure
     * @param RenderingContextInterface $renderingContext
     * @throws Exception
     * @return string
     */
    public static function renderStatic(array $arguments, \Closure $renderChildrenClosure, RenderingContextInterface $renderingContext) {
      switch ($arguments['type']) {
        case "image":
          $type = array("gif", "jpg", "jpeg", "tif", "tiff", "bmp", "tga", "png", "ai", "svg", "webp");
          $title = "Image";
          break;
        case "video":
          $type = array("webm", "mp4", "mov", "asc", "youtube", "vimeo");
          $title = "Video";
          break;
        default:
          $type = array("*");
          $title = "File";
          break;
      }
      $config = [
        'type' => 'link',
        'required' => false,
        'size' => 20,
        'nullable' => true,
        'allowedTypes' => array('file'),
        'appearance' => [
          'browserTitle' => $title,
          'enableBrowser' => true,
          'allowedFileExtensions' => $type,
        ],
      ];
      return $config;
    }
  }

and in configuration:

<flux:field.input name="image" label="Image" config="{g:data.imageconfig(type: 'image')}" />

Unfortunately, the restriction for the file extensions does not work as intended

NamelessCoder commented 2 months ago

@grischpel Another problem with that solution is that if I am remembering things correctly, the data type stored by the field type you use is different from the data type stored by the old ViewHelper, to the point where they are incompatible. The field also (again, IIRC) does not support multiple values. The value it stores also isn't nice to work with when attempting to do anything except for linking to the file (does not store the file path or UID in raw format, cannot support f:image etc. and does not work with the transform="file" Flux data transformation).

As such it is most definitely not a drop-in replacement, but perhaps it can serve in some cases as an alternative, in particular for use cases with section containers.

By the way:

mdittberner commented 6 days ago

@NamelessCoder Hi Claus, I have a question concerning the usage of the generic field ViewHelper like . The reference is stored correctly but if I try to use {settings.image} as before I get only the value stored by TYPO3 which represents the count of relations. So legacy usages like <f:image src="{settings.image}"... /> or are not possible and I can't use {settings.image} directly anymore.

I wrote an own ViewHelper Class for this but now my question: Is there a simpler possibility to get the image in the flux FCE template? Or coud flux return the file reference itself instead of the reference count?

Many thanks for countless hours developing!

NamelessCoder commented 5 days ago

Is there a simpler possibility to get the image in the flux FCE template?

What follows assumes that type="file" actually results in a FAL IRRE field type which may not be the case on all TYPO3 versions. It should be the case on at least TYPO3v12.

There sure is. Try adding transform="filereference" or if you have multiple references, transform="filereferences" on the Flux field. Flux will extract the reference for you and assign the FileReference object instance to the template instead of the integer relation count. If you use the plural form then the assigned variable will be an array of FileReference objects, if you use the singular form it will be a single object instance. You can also use file or files as the value of transform if you want the target files instead of the references.

This is by far the easiest way to work with file fields in Flux.

Alternatively if you use VHS there is v:resource.record.fal which takes table, field and either uid or record (which would be the parent record, e.g. a content element record or UID of one). I'm guessing it does approximately what your own ViewHelper does. The VHS ViewHelper makes sense if your template only uses the image in some cases, becaue it can be put inside an f:if (thus saving you some cpu cycles and sql queries). The native transform feature of Flux will always extract and assign the file references no matter if they are used in the template or not.

Word of caution: do not use a field name attribute value which is also a name of a FAL-enabled field in the root table. For example, for content elements, image and media are such names that should not be used. Using those will cause TYPO3 to get confused and duplicate the relations to those fields as well in some contexts. Your chosen field name should be safe, but keep this "reserved names" aspect in mind if you use this feature in the future as well.

Many thanks for countless hours developing!

You're welcome! :)

mdittberner commented 5 days ago

Many thanks Claus, the transform="filereference" is exactly what I was looking for! If I know that such keywords exist, I can search the flux source code for it. But I didn't know. Maybe you could integrate this in the documentations - I didn't found this opportunity in the docs. At the moment there is only the description "Set this to transform your value to this type - integer, array (for csv values), float, DateTime, VendorMyExtDomainModelObject or ObjectStorage with type hint." which was not helpful for me. A part like "Use transform="filereference" to get references for ViewHelpers like f:image in your template" could be very helpful. Also thanks for the field naming hint. Since I mostly use flux for FCEs I accustomed to use always names like settings.image to avoid problems. But it's good to know that there are indeed risks using names like image and media.

NamelessCoder commented 5 days ago

The feature is brand new, IIRC added in version 10 - so it's not surprising at all that not many people know about it. You're right though that it should be mentioned in the field documentation like the other transform types that are mentioned.

mdittberner commented 5 days ago

In the BE FCE preview context the image is NULL, in FE I get a relation as desired. Is there any thing I forgot? `

...

{settings.bg} `
NamelessCoder commented 3 days ago

It doesn't look like you're doing anything wrong.

It's very annoying, but in FileRepository, TYPO3 does the resolving of FAL relations differently in BE than it does in FE and those two different methods don't always result in the same returns even with the same input arguments (see https://github.com/TYPO3/typo3/blob/402a65eabc5c09a2e31cffd23a36298f301c14c0/typo3/sysext/core/Classes/Resource/FileRepository.php#L86). In essence, TYPO3 restricts the resolving of FAL objects through FileRepository to only be possible for root TCA fields - as if whomever wrote this logic completely ignored that FAL relations are also possible through FlexForms.

Flux is probably going to have to duplicate this logic and work around the incomplete nature of FileRepository (sign, why does this happen so often with TYPO3?) but for the time being you cannot rely on transform to produce usable objects in backend context (which, luckily, is limited to affect only the Preview section in content records). You should however be able to use VHS's v:resource.record.fal ViewHelper to load those references - since this ViewHelper does not use FileRepository.

mdittberner commented 3 days ago

I almost suspected it. Ok, for the moment I handle the BE preview in an other way than FE where I can use the transform. Thanks for the explaination.

NamelessCoder commented 6 hours ago

https://forge.typo3.org/issues/104331