5

So am working on a widget which allows the client to choose an image from their library of images and then insert it into the widget's params. Here is the block class code I am using for the image chooser and below is the widget parameter declaration in widget.xml

<parameter name="widget_image_chooser" xsi:type="block" visible="true" sort_order="20" required="true">
    <label translate="true">Category</label>
    <block class="Company\Module\Block\Widget\Image\Chooser">
        <data>
            <item name="button" xsi:type="array">
                <item name="open" xsi:type="string">Select Image</item>
            </item>
        </data>
    </block>
</parameter>

Now the problem is when the widget is inserted to a CMS Page or Block, it is inserted as a media widget instance, breaking the code because there can't be double quotes within double quotes, see below.

{{widget type="Company\Module\Block\Widget\LityVideo" identifier="https://vimeo.com/169559548" widget_image_chooser="{{media url="wysiwyg/13876414_1349854728361522_4781756811153391947_n.jpg"}}"}}

Is there a way of solving this without having to use javascript? I haven't been able to find the Model which renders the widgets in the CMS in the backend.

12

There seems to be a lot of people looking for a solution for this so here is my solution to my own answer.

The first thing you need to do is create a new plugin (interceptor) under [COMPANY]/[MODULE]/etc/di.xml

<?xml version="1.0" encoding="UTF-8" ?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd">
    <type name="Magento\Widget\Model\Widget">
        <plugin name="test1" type="[COMPANY]\[MODULE]\Model\Widget" sortOrder="1" disabled="false"/>
    </type>
</config>

Then create the interceptor logic, you need to use the before method, this way you can change the parameters being passed to \Magento\Widget\Model\Widget, getWidgetDeclaration so something like this.

namespace [COMPANY]\[MODULE]\Model;

use \Magento\Widget\Model\Widget as BaseWidget;

class Widget
{
    public function beforeGetWidgetDeclaration(BaseWidget $subject, $type, $params = [], $asIs = true)
    {
        // I rather do a check for a specific parameters
        if(key_exists("widget_image_chooser", $params)) {

            $url = $params["widget_image_chooser"];
            if(strpos($url,'/directive/___directive/') !== false) {

                $parts = explode('/', $url);
                $key   = array_search("___directive", $parts);
                if($key !== false) {

                    $url = $parts[$key+1];
                    $url = base64_decode(strtr($url, '-_,', '+/='));

                    $parts = explode('"', $url);
                    $key   = array_search("{{media url=", $parts);
                    $url   = $parts[$key+1];

                    $params["widget_image_chooser"] = $url;
                }
            }
        }

        return array($type, $params, $asIs);
    }
}
1

A improved solution to this as of magento 2.2.2 would be to do the below. within the plugin, to filter the directive for frontend use.

[COMPANY][MODULE]\etc\di.xml

<?xml version="1.0" encoding="UTF-8" ?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd">
    <type name="Magento\Widget\Model\Widget">
        <plugin name="test1" type="[COMPANY]\[MODULE]\Model\Widget" sortOrder="1" disabled="false"/>
    </type>
</config>

[COMPANY][MODULE]\Model\Widget.php

namespace [COMPANY]\[MODULE]\Model;

use \Magento\Widget\Model\Widget as BaseWidget;

class Widget
{
    protected $_filter;

    public function __construct(
        \Magento\Cms\Model\Template\Filter $filter
    )
    {
        $this->_filter = $filter;
    }

    public function beforeGetWidgetDeclaration(BaseWidget $subject, $type, $params = [], $asIs = true)
    {

        if ($type == "[COMPANY]\[MODULE]\Block\Widget\[ELEMENT CLASS]") {
            // I rather do a check for a specific parameters
            foreach ($params as $paramKey => $value) {

                $url = $params[$paramKey];
                if (strpos($url, '/directive/___directive/') !== false) {

                    $parts = explode('/', $url);
                    $key = array_search("___directive", $parts);
                    if ($key !== false) {

                        $url = $parts[$key + 1];
                        $url = base64_decode(strtr($url, '-_,', '+/='));

                        $parts = explode('"', $url);
                        $key = array_search("{{media url=", $parts);
                        $url = $parts[$key + 1];

                        $imagePath = $this->_filter->filter($url);
                        $params[$paramKey] = $imagePath;
                    }
                }
            }
        }

        return array($type, $params, $asIs);
    }
}
-1

Thanks for posting your solution. It did not do what I excpected it to do. For me, it changed the $params['widget_image_chooser'] value to: wysiwyg/Boat.jpg. Ofcourse you can use this in your template.

Because I do not know the consequences of changing the url, I used your interceptor to replace the double quotes by single quotes. For me, this solves my problem.

My modified interceptor:

public function beforeGetWidgetDeclaration(BaseWidget $subject, $type, $params = [], $asIs = true)
{
    $imageParmeterName = 'photo'; //The parameter name as defined in widget.xml

    // I rather do a check for a specific parameters
    if (key_exists($imageParmeterName, $params)) {

        $url = $params[$imageParmeterName];
        if (strpos($url, '/directive/___directive/') !== false) {

            $parts = explode('/', $url);
            $key = array_search("___directive", $parts);
            if ($key !== false) {

                $key++;
                $url = $parts[$key];
                $imgSrc = base64_decode(strtr($url, '-_,', '+/=')); //{{media url="wysiwyg/Boat.jpg"}}
                $imgSrc = str_replace('"', "'", $imgSrc); //Replace " with ' to avoid breaking html
                $parts[$key] = base64_encode($imgSrc); //{{media url='wysiwyg/Boat.jpg'}}
                $url = implode('/', $parts); //Rebuild url

                $params[$imageParmeterName] = $url;
            }
        }
    }

    return array($type, $params, $asIs);
}

Edit: It seems this does not work as intended for some reason. Eventhough I do not modify the structure of the url, this 'solution' causes a backend image link to appear on the frontend.. I suspect this might be another issue.

  • Yeah i got the backend image link issue, was only in production however within my dev area everything seemed fine. Did you ever solve that? – harri Jun 22 '18 at 12:42

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.