nadar / quill-delta-parser

A PHP library to parse and render Quill WYSIWYG Deltas into HTML - Flexibel and extendible for custom elements.
MIT License
121 stars 20 forks source link

insertJsonKey not working #55

Closed anchetaWern closed 2 years ago

anchetaWern commented 2 years ago

Describe the bug I'm using this plugin to resize images: https://github.com/kensnyder/quill-image-resize-module and this one to upload images on the server: https://github.com/NoelOConnell/quill-image-uploader

These two generates the following:

Screen Shot 2022-02-22 at 4 24 23 PM

Now I'm trying to extend this library to allow me to display the image in its custom size:

<?php
namespace App\Library;

use nadar\quill\Line;
use nadar\quill\InlineListener;

class ImageSizeListener extends InlineListener {

    public function process(Line $line)
    {
        $image_styles = $line->getAttribute('style');
        $image = $line->insertJsonKey('image');
        if ($line->isJsonInsert()) {
            info('JSON insert');
        }

        if ($image_styles) {
            $this->updateInput($line, '<img src="' . $image . '" style="' . $image_styles . '" alt="" class="img-responsive img-fluid">');
        }
    }

}

I then call it this way:

use App\Library\ImageSizeListener;

$lexer = new Lexer($town->about_page_html);
$lexer->registerListener(new ImageSizeListener);
$html = $lexer->render();

The delta code which generates the Problem

{"attributes":{"style":"width: 136px;","data-size":"200,200"},"insert":{"image":"/about_images/UITBM"}}

The expected html output the delta should produce

Example:

<img src="/about_images/UITBM" style="width: 136px;" alt="" class="img-responsive img-fluid">

Expected behavior

I'm expecting to get the image source when this line gets called:

$image = $line->insertJsonKey('image');

Nothing gets called though. Because when I tried it this way, nothing is logged:

if ($line->isJsonInsert()) {
  info('JSON insert');
}

Screenshots

Screen Shot 2022-02-22 at 4 31 22 PM
nadar commented 2 years ago

Need time to take a look on your issue, but maybe at first place, disable all default listernes so we can ensure they don't conflict with each other

https://github.com/nadar/quill-delta-parser/blob/master/src/Lexer.php#L126-L156

use App\Library\ImageSizeListener;

$lexer = new Lexer($town->about_page_html, false); // set false to disabled the load of all default listeneres
$lexer->registerListener(new ImageSizeListener);
$html = $lexer->render();

Could you please the quill json part as json here, so i can create a unit test.

anchetaWern commented 2 years ago

thank you for the very quick response. wasn't expecting that. That resulted in a blank page but my "JSON insert" log got called.

nadar commented 2 years ago

And if you then just register the new Text listener along with your plugin?

anchetaWern commented 2 years ago

Thank you so much for your help! I managed to figure it out. They were indeed conflicting. My original custom listener was extending the InlineListener when it was supposed to extend the Image listener. But that approach still had the same problems as the original one as I still couldn't get the value for the image src. Overriding the built-in Image listener would have worked as well (as indicated in the example in the README), but then I wouldn't have access to the value needed for the styles attribute.

So what I did was to update the Image class in the vendor directory instead:

<?php

namespace nadar\quill\listener;

use nadar\quill\Line;
use nadar\quill\Lexer;
use nadar\quill\BlockListener;
use nadar\quill\InlineListener;

/**
 * Convert Image attributes into image element.
 *
 * @author Basil Suter <basil@nadar.io>
 * @since 1.0.2
 */
class Image extends InlineListener
{
    public $wrapper = '<img src="{src}" alt="" class="img-responsive img-fluid" />';

    /**
     * {@inheritDoc}
     */
    public function process(Line $line)
    {
        $embedUrl = $line->insertJsonKey('image');
        $imageStyles = $line->getAttribute('style');
        if ($embedUrl) {
            $this->updateInput($line, str_replace(['{src}'], [$line->getLexer()->escape($embedUrl)], $this->wrapper));
        }

        if ($imageStyles) {
            $this->updateInput($line, str_replace(['{src}', '{styles}'], [$line->getLexer()->escape($embedUrl), $imageStyles], $this->wrapper));
        }
    }
}

Then used it like so:

$image = new Image();
$image->wrapper = '<img src="{src}" style="{styles}" alt="" class="img-responsive img-fluid">';
$lexer->registerListener($image);

Obviously this is no good as I directly updated the file on the on the vendor directory.

But the alternative solution, which is to recreate the Image listener like so:

<?php
namespace App\Library;

use nadar\quill\Line;
use nadar\quill\Lexer;
use nadar\quill\BlockListener;
use nadar\quill\InlineListener;

class ImageSizeListener extends InlineListener {

    public $wrapper = '<img src="{src}" alt="" class="img-responsive img-fluid" />';

    public function process(Line $line)
    {
        $embedUrl = $line->insertJsonKey('image');
        $imageStyles = $line->getAttribute('style');
        if ($embedUrl) {
            $this->updateInput($line, str_replace(['{src}'], [$line->getLexer()->escape($embedUrl)], $this->wrapper));
        }

        if ($imageStyles) {
            $this->updateInput($line, str_replace(['{src}', '{styles}'], [$line->getLexer()->escape($embedUrl), $imageStyles], $this->wrapper));
        }
    }

}

And then using it (while disabling all listeners, then re-enabling everything except the Image):

use nadar\quill\Lexer;
use nadar\quill\listener\Text;
use nadar\quill\listener\Blockquote;
use nadar\quill\listener\Bold;
use nadar\quill\listener\CodeBlock;
use nadar\quill\listener\Font;
use nadar\quill\listener\Heading;
use nadar\quill\listener\Italic;
use nadar\quill\listener\Link;
use nadar\quill\listener\Lists;
use nadar\quill\listener\Strike;
use nadar\quill\listener\Underline;
use nadar\quill\listener\Video;

$lexer = new Lexer($town->about_page_html, false);
$lexer->registerListener(new Text);

$image = new ImageSizeListener();
$image->wrapper = '<img src="{src}" style="{styles}" alt="" class="img-responsive img-fluid">';

$lexer->registerListener(new Blockquote);
$lexer->registerListener(new Bold);
$lexer->registerListener(new CodeBlock);
$lexer->registerListener(new CodeBlock);
$lexer->registerListener(new Font);
$lexer->registerListener(new Heading);
$lexer->registerListener(new Italic);
$lexer->registerListener(new Link);
$lexer->registerListener(new Lists);
$lexer->registerListener(new Strike);
$lexer->registerListener(new Underline);
$lexer->registerListener(new Video);

$lexer->registerListener($image);

$html = $lexer->render();

Won't work either. It just outputs the text:

Screen Shot 2022-02-22 at 5 30 26 PM

Not sure if i just missed something crucial, but here's the JSON:

{"ops":[{"insert":"Eye is a jewel of a town in North Suffolk with a wealth of interesting places to visit and friendly places to shop and eat. Dominated by its outstanding Victorian flint and brick "},{"attributes":{"bold":true},"insert":"Town Hall "},{"insert":"commissioned in 1857 by E B Lamb, the town of Eye was until the 1970s the smallest borough in the country. The Town is built around "},{"attributes":{"bold":true},"insert":"Eye Castle"},{"insert":" which possibly dates back to the eleventh century and is well worth a visit. You can take a leisurely walk around the town trail and visit some of the wonderful historic buildings. Recent spurts in population may see a step change in the town’s future development.\n"},{"attributes":{"color":"#666666"},"insert":"The magnificent fifteenth century Church of St Peter and St Paul is featured in the Churches and Chapels section of “"},{"attributes":{"color":"#222222","bold":true},"insert":"Days Out in Suffolk"},{"attributes":{"color":"#666666"},"insert":"” available from "},{"attributes":{"color":"#222222","bold":true},"insert":"Mid Suffolk Tourist Information Centre"},{"attributes":{"color":"#666666"},"insert":"."},{"insert":"\n"},{"attributes":{"color":"#666666"},"insert":"Just beside the Church is the Guild Hall which again dates back to the 1400’s. The Guild’s activities could still be said to be alive represented by enthusiastic members of "},{"attributes":{"color":"#2d5c88","background":"#ffffff","link":"https://www.eyeartsguild.org.uk/"},"insert":"Eye Arts Guild"},{"attributes":{"color":"#666666"},"insert":". "},{"attributes":{"underline":true,"italic":true,"color":"#666666","bold":true},"insert":"Bararaq sei"},{"attributes":{"color":"#666666"},"insert":"."},{"insert":"\n"},{"attributes":{"style":"width: 136px;","data-size":"200,200"},"insert":{"image":"/about_images/UITBM"}},{"attributes":{"align":"center"},"insert":"\n"},{"attributes":{"color":"#666666"},"insert":"The town is twinned with "},{"attributes":{"color":"#222222","bold":true},"insert":"Pouzauges"},{"attributes":{"color":"#666666"},"insert":" in France and this promotes links to continental tourism. There is no official Tourist Office in Eye, although McColls newsagent and convenience store keeps an array of leaflets which are available to tourists including "},{"attributes":{"color":"#222222","bold":true},"insert":"“Eye Suffolk, A Tourist Guide”"},{"attributes":{"color":"#666666"},"insert":", "},{"attributes":{"color":"#222222","bold":true},"insert":"“Heart of Suffolk Treasures”"},{"attributes":{"color":"#666666"},"insert":", "},{"attributes":{"color":"#222222","bold":true},"insert":"“Eye Cycle Route” "},{"attributes":{"color":"#666666"},"insert":"and the "},{"attributes":{"color":"#222222","bold":true},"insert":"“Eye Town Trail”"},{"attributes":{"color":"#666666"},"insert":"."},{"insert":"\n"},{"insert":{"image":"/about_images/t3Cmt"}},{"attributes":{"align":"right"},"insert":"\n"}]}
nadar commented 2 years ago

hi @anchetaWern

I have added new method overwriteListener() this should fix your problem. See #56, i have also added an example with your data. I hope this will fix your problem.