Magento2: Magento 2.1 EE: simplexml_load_string() error in custom widget

Created on 13 Sep 2016  路  17Comments  路  Source: magento/magento2

Preconditions

  1. OS of the server: Debian (Virtualized, with 3GB of memory)
  2. OS of the client: Windows 7
  3. * Browser of the client*: Chrome Version 52.0.2743.116 m (64-bit). Also tried with Firefox 44.0.2
  4. MySQL: Ver 14.14 Distrib 5.6.31
  5. PHP: PHP 7.0.8-1~dotdeb+8.1 (cli) ( NTS )

    Steps to reproduce

  6. Create a custom widget from a XML file

  7. Put a text field
  8. In the backend Content->Widget section, add the widget to a page
  9. In the widget content, set some HTML code like this:
    <p>Bonjour &eacute;&agrave;&ugrave;&egrave; &ocirc;</p>

    Expected result

  10. The text should be displayed

    Actual result

A warning is raised :
Exception #0 (Exception): Warning: simplexml_load_string(): Entity: line 1: parser error : Entity 'eacute' not defined in /var/www/xx/vendor/magento/framework/View/Model/Layout/Merge.php on line 493

Notes:

  • if, instead of the text field, a I put a WYSIWYG one, and put some HTML code, it will generate entities encoded HTML
  • if I put only raw text: no problem.
  • more weird: if I put only raw text, then I save, everything is OK. And if I come later, put some HTML code (directly or via the WYSIWYG), and save, I have a warning:

image

Cms Fixed in 2.2.x Format is valid Ready for Work needs update bug report

Most helpful comment

I did find some sort of solution for this issue, although I'm not sure if its really a bug because technically those elements are invalid XML and shouldn't be use in widgets. However most clients do just put in these types of characters and then Magento converts them into HTML Entities on Save and this is just read straight into XML.

If you put a preference on the _loadXmlString() method in Magento\Framework\View\Model\Layout\Merge line 491

You can do something like:
```
protected function _loadXmlString($xmlString)
{
$unescapedValues = [' ', 'é', 'à', 'ù', 'è', 'ô'];

    foreach ($unescapedValues as $value) {
        // wrap in CDATA to escape
        $xmlString = str_replace($value, "<![CDATA[$value]]>", $xmlString);
    }

    return simplexml_load_string($xmlString, 'Magento\Framework\View\Layout\Element');
}

All 17 comments

@enrico69 thank you for your feedback.
Please identify which version of Magento you are running.

Sorry. That's done.

Could you please paste your xml for custom widget here?

<?xml version="1.0" encoding="UTF-8"?>
<widgets xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Widget:etc/widget.xsd">
    <widget id="myclient_corporateWidget" class="Myclient\CorporateWidget\Block\Widget\CorporateWidget">
        <label translate="true">Myclient Corporate Widget</label>
        <description>Widget containing informations about Myclient</description>
        <parameters>
            <parameter name="widgetTitle" xsi:type="text"  visible="true" sort_order="10" >
                <label translate="true">Title</label>
            </parameter>
            <parameter name="videoHtmlCode" xsi:type="text"  visible="true" sort_order="10" >
                <label translate="true">Video Url</label>
            </parameter>
            <parameter name="logoFile" xsi:type="text"  visible="true" sort_order="10" >
                <label translate="true">Logo</label>
            </parameter>
            <parameter name="picture1File" xsi:type="select" source_model="Myclient\CorporateWidget\Model\Listing\Picture" visible="true" sort_order="10" >
        <label translate="true">Picture #1</label>
            </parameter>
            <parameter name="pic1Text" xsi:type="block"  visible="true" sort_order="10" >
                <label translate="true">Picture #1 description</label>
                <block class="Mycompany\Tools\Block\Adminhtml\Widget\TextArea" />
            </parameter>
            <parameter name="picture2File" xsi:type="select" source_model="Myclient\CorporateWidget\Model\Listing\Picture" visible="true" sort_order="10" >
        <label translate="true">Picture #2</label>
            </parameter>
            <parameter name="pic2Text" xsi:type="block"  visible="true" sort_order="10" >
                <label translate="true">Picture #2 description</label>
                <block class="Mycompany\Tools\Block\Adminhtml\Widget\TextArea" />
            </parameter>
            <parameter name="picture3File" xsi:type="select" source_model="Myclient\CorporateWidget\Model\Listing\Picture" visible="true" sort_order="10" >
        <label translate="true">Picture #3</label>
            </parameter>
            <parameter name="pic3Text" xsi:type="block"  visible="true" sort_order="10" >
                <label translate="true">Picture #3 description</label>
                <block class="Mycompany\Tools\Block\Adminhtml\Widget\TextArea" />
            </parameter>
        </parameters>
    </widget>
</widgets>

Can't reproduce with your xml but without your classes. How do you add content right to your widget without a block? What layout update do you select? Maybe you have some non-default locale set?

Hi
I use a standard locale (fr_FR). Below is the code of the block:

<?php
namespace Mycompany\Tools\Block\Adminhtml\Widget;

use Magento\Framework\Data\Form\Element\Factory;
use Magento\Backend\Block\Template\Context;
use Magento\Backend\Block\Widget\Form\Element;
use Magento\Framework\Module\Dir\Reader;
use Magento\Framework\Data\Form\Element\AbstractElement;

class TextArea extends Element
{
    /**
     * @var Factory
     */
    protected $factoryElement;

    /**
     * @var Magento\Framework\Module\Dir\Reader 
     */
    protected $reader;

    /**
     * 
     * @param Context $context
     * @param Factory $factoryElement
     * @param type $data
     */
    public function __construct(
        Reader $reader,
        Context $context,
        Factory $factoryElement,
        $data = []
    ) {
        $this->factoryElement = $factoryElement;
        $this->reader = $reader;
        parent::__construct($context, $data);
    }

    /**
     * Prepare chooser element HTML
     *
     * @param \Magento\Framework\Data\Form\Element\AbstractElement $element Form Element
     * @return \Magento\Framework\Data\Form\Element\AbstractElement
     */
    public function prepareElementHtml(AbstractElement $element)
    {
        $textArea = $this->factoryElement->create('textarea', ['data' => $element->getData()])
            ->setId($element->getId())
            ->setForm($element->getForm())
            ->setClass('widget-option input-textarea admin__control-text');

        if ($element->getRequired()) {
            $textArea->addClass('required-entry');
        }

        $element->setData(
            'after_element_html',
            $this->_getAfterElementHtml() . $textArea->getElementHtml()
        );

        return $element;
    }

    /**
     * @return string
     */
    protected function _getAfterElementHtml()
    {
        $filePath = $this->reader->getModuleDir('view', 'Mycompany_Tools');
        $filePath .= DIRECTORY_SEPARATOR . 'adminhtml' . DIRECTORY_SEPARATOR . 'templates' 
        . DIRECTORY_SEPARATOR . 'widget'. DIRECTORY_SEPARATOR . 'htmlAddition.php';

        ob_start();
        include $filePath;
        return ob_get_clean();
    }
}

And here the CSS I add :

<style>
    .admin__field-control.control .control-value {
        display: none !important;
    }
</style>

I followed a couple of tutorial in order to compensate the lack of TextArea field for the widgets.

Is this still reproducible in latest release branch?

We are closing this issue because it hasn鈥檛 been updated in more than two weeks. If you can still reproduce this issue, please create a new GitHub issue report.

How to fix bug?

@sevos1984 Can you please open this again? I'm on Magento 2.1.1 and I've got this error as well:

Example: I've got some German texts that are too long, in a widget text field, so I want to skip them to the next line by using ­ as a html entity.

This breaks the entire site.

{{1 exception(s):
Exception #0 (Exception): Warning: simplexml_load_string(): Entity: line 54: parser error : Entity 'shy' not defined in /data/web/magento2/vendor/magento/framework/View/Model/Layout/Merge.php on line 493}}

Now, problem is, this is added as a serialized array to the widget_instance. It instantly breaks your database.

What I also found out that this mostly happens when you add data through Content > Widgets, not through adding widgets through pages. I suspect TinyMCE does some escaping itself.

My guess is that the problem can be solved here, but I'm not sure.

Magento\Widget\Model\Widget\Instance:253

I did find some sort of solution for this issue, although I'm not sure if its really a bug because technically those elements are invalid XML and shouldn't be use in widgets. However most clients do just put in these types of characters and then Magento converts them into HTML Entities on Save and this is just read straight into XML.

If you put a preference on the _loadXmlString() method in Magento\Framework\View\Model\Layout\Merge line 491

You can do something like:
```
protected function _loadXmlString($xmlString)
{
$unescapedValues = [' ', 'é', 'à', 'ù', 'è', 'ô'];

    foreach ($unescapedValues as $value) {
        // wrap in CDATA to escape
        $xmlString = str_replace($value, "<![CDATA[$value]]>", $xmlString);
    }

    return simplexml_load_string($xmlString, 'Magento\Framework\View\Layout\Element');
}

@antbates1991 your solution works.

This isssue should be reopned as the problem still exists with Magento 2.1.6.

@qbo-tech, the closed issues rarely get reopened here because they rarely get any attention from someone with an ability to reopen it.
If you want to bring any attention to it, creating a new issue with reference to this one might do the trick.

Internal ticket to track issue progress: MAGETWO-70469

For anyone who doesn't want to wait for 2.2.x you can use this small module to apply the fix https://github.com/WeareJH/m2-core-bug-simplexml-widget

@antbates1991 Thank you - this worked for me as well. Since the entity ndash is what was failing, I added that to the array, like:

    protected function _loadXmlString($xmlString)
    {

        // Added bugfix from https://github.com/magento/magento2/issues/6594#issuecomment-283650111
        $unescapedValues = ['&nbsp;', '&eacute;', '&agrave;', '&ugrave;', '&egrave;', '&ocirc;', '&ndash;'];


        foreach ($unescapedValues as $value) {
            // wrap in CDATA to escape
            $xmlString = str_replace($value, "<![CDATA[$value]]>", $xmlString);
        }

        return simplexml_load_string($xmlString, 'Magento\Framework\View\Layout\Element');

    }

Helpful info to anyone else having the issue - you may need to add an additional item to the array based on your err msg.

I run into this problem now after an update from Magento 2.1.5 to 2.2.6 :-(

I'm not sure if this is intended behaviour, but what I find strange is that all the content of my WYSIWYG-editor is parsed as being "layout". If I look at the content of $xmlString, I get something like this:




<?xml version="1.0"?><layout xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><p><img title="..." src="path/to/image.jpg" alt="..." width="650" height="434" /></p>
聽 | <p>Some text with a &hellip; Bla bla <a href="path/to/url"><strong>fruitwater</strong></a> is, een verse smoothie, &hellip; genieten zal je</p>
聽 | <h2>Header</h2>
聽 | <p>Lorem ipsum etc...</p>
聽

<body>
--
聽 | <referenceContainer name="root">
聽 | <block class="Ho\Templatehints\Block\Hints\Init" name="ho.templatehints"/>
聽 | </referenceContainer>
聽 | </body>
聽 | <body>
聽 | <referenceContainer name="header.panel">
聽 | <block class="Magento\Directory\Block\Currency" name="currency" before="store_language" template="Magento_Directory::currency.phtml"/>
聽 | </referenceContainer>
聽 | </body>
聽 | <head><script type="text/javascript">window.NREUM\|\|(NREUM={}),__nr_require=function(e,n,t){function r(t){if(!n[t]){var o=n[t]={exports:{}};e[t][0].call(o.exports,function(n){var o=e[t][1][n];return r(o\|\|n)},o,o.exports)}return n[t].exports}if("function"==typeof __nr_require)return __nr_require;for(var o=0;o<t.length;o++)r(t[o]);return r}({1:[function(e,n,t){function r(){}funct

... etc ...

Is this correct? Should my HTML be in the layout merger? Seems a little odd to me...

Was this page helpful?
0 / 5 - 0 ratings