Commit 9c4f60e0 authored by Yassine Doghri's avatar Yassine Doghri
Browse files

fix(markdown-editor): remove unnecessary buttons for podcast and episode editors + add extensions

update CommonMark to v2 + add Autolink, SmartPunct and DisallowedRawHtml extensions
parent f304d97b
Loading
Loading
Loading
Loading
+18 −5
Original line number Diff line number Diff line
@@ -26,7 +26,12 @@ use CodeIgniter\Entity\Entity;
use CodeIgniter\Files\File;
use CodeIgniter\HTTP\Files\UploadedFile;
use CodeIgniter\I18n\Time;
use League\CommonMark\CommonMarkConverter;
use League\CommonMark\Environment\Environment;
use League\CommonMark\Extension\Autolink\AutolinkExtension;
use League\CommonMark\Extension\CommonMark\CommonMarkCoreExtension;
use League\CommonMark\Extension\DisallowedRawHtml\DisallowedRawHtmlExtension;
use League\CommonMark\Extension\SmartPunct\SmartPunctExtension;
use League\CommonMark\MarkdownConverter;
use RuntimeException;

/**
@@ -473,13 +478,21 @@ class Episode extends Entity

    public function setDescriptionMarkdown(string $descriptionMarkdown): static
    {
        $converter = new CommonMarkConverter([
            'html_input' => 'strip',
        $config = [
            'html_input' => 'escape',
            'allow_unsafe_links' => false,
        ]);
        ];

        $environment = new Environment($config);
        $environment->addExtension(new CommonMarkCoreExtension());
        $environment->addExtension(new AutolinkExtension());
        $environment->addExtension(new SmartPunctExtension());
        $environment->addExtension(new DisallowedRawHtmlExtension());

        $converter = new MarkdownConverter($environment);

        $this->attributes['description_markdown'] = $descriptionMarkdown;
        $this->attributes['description_html'] = $converter->convertToHtml($descriptionMarkdown);
        $this->attributes['description_html'] = $converter->convert($descriptionMarkdown);

        return $this;
    }
+17 −5
Original line number Diff line number Diff line
@@ -12,7 +12,12 @@ namespace App\Entities;

use CodeIgniter\Entity\Entity;
use CodeIgniter\I18n\Time;
use League\CommonMark\CommonMarkConverter;
use League\CommonMark\Environment\Environment;
use League\CommonMark\Extension\Autolink\AutolinkExtension;
use League\CommonMark\Extension\CommonMark\CommonMarkCoreExtension;
use League\CommonMark\Extension\DisallowedRawHtml\DisallowedRawHtmlExtension;
use League\CommonMark\Extension\SmartPunct\SmartPunctExtension;
use League\CommonMark\MarkdownConverter;

/**
 * @property int $id
@@ -49,13 +54,20 @@ class Page extends Entity

    public function setContentMarkdown(string $contentMarkdown): static
    {
        $converter = new CommonMarkConverter([
            'html_input' => 'strip',
        $config = [
            'allow_unsafe_links' => false,
        ]);
        ];

        $environment = new Environment($config);
        $environment->addExtension(new CommonMarkCoreExtension());
        $environment->addExtension(new AutolinkExtension());
        $environment->addExtension(new SmartPunctExtension());
        $environment->addExtension(new DisallowedRawHtmlExtension());

        $converter = new MarkdownConverter($environment);

        $this->attributes['content_markdown'] = $contentMarkdown;
        $this->attributes['content_html'] = $converter->convertToHtml($contentMarkdown);
        $this->attributes['content_html'] = $converter->convert($contentMarkdown);

        return $this;
    }
+30 −9
Original line number Diff line number Diff line
@@ -23,7 +23,12 @@ use CodeIgniter\Entity\Entity;
use CodeIgniter\Files\File;
use CodeIgniter\HTTP\Files\UploadedFile;
use CodeIgniter\I18n\Time;
use League\CommonMark\CommonMarkConverter;
use League\CommonMark\Environment\Environment;
use League\CommonMark\Extension\Autolink\AutolinkExtension;
use League\CommonMark\Extension\CommonMark\CommonMarkCoreExtension;
use League\CommonMark\Extension\DisallowedRawHtml\DisallowedRawHtmlExtension;
use League\CommonMark\Extension\SmartPunct\SmartPunctExtension;
use League\CommonMark\MarkdownConverter;
use Modules\Auth\Entities\User;
use RuntimeException;

@@ -375,13 +380,21 @@ class Podcast extends Entity

    public function setDescriptionMarkdown(string $descriptionMarkdown): static
    {
        $converter = new CommonMarkConverter([
            'html_input' => 'strip',
        $config = [
            'html_input' => 'escape',
            'allow_unsafe_links' => false,
        ]);
        ];

        $environment = new Environment($config);
        $environment->addExtension(new CommonMarkCoreExtension());
        $environment->addExtension(new AutolinkExtension());
        $environment->addExtension(new SmartPunctExtension());
        $environment->addExtension(new DisallowedRawHtmlExtension());

        $converter = new MarkdownConverter($environment);

        $this->attributes['description_markdown'] = $descriptionMarkdown;
        $this->attributes['description_html'] = $converter->convertToHtml($descriptionMarkdown);
        $this->attributes['description_html'] = $converter->convert($descriptionMarkdown);

        return $this;
    }
@@ -399,17 +412,25 @@ class Podcast extends Entity
            return $this;
        }

        $converter = new CommonMarkConverter([
            'html_input' => 'strip',
        $config = [
            'html_input' => 'escape',
            'allow_unsafe_links' => false,
        ]);
        ];

        $environment = new Environment($config);
        $environment->addExtension(new CommonMarkCoreExtension());
        $environment->addExtension(new AutolinkExtension());
        $environment->addExtension(new SmartPunctExtension());
        $environment->addExtension(new DisallowedRawHtmlExtension());

        $converter = new MarkdownConverter($environment);

        $this->attributes[
            'episode_description_footer_markdown'
        ] = $episodeDescriptionFooterMarkdown;
        $this->attributes[
            'episode_description_footer_html'
        ] = $converter->convertToHtml($episodeDescriptionFooterMarkdown);
        ] = $converter->convert($episodeDescriptionFooterMarkdown);

        return $this;
    }
+45 −0
Original line number Diff line number Diff line
<?php

declare(strict_types=1);

if (! function_exists('form_markdown_textarea')) {
    /**
     * Textarea field
     *
     * @param mixed $data
     * @param mixed $extra
     */
    function form_markdown_textarea($data = '', string $value = '', $extra = ''): string
    {
        $defaults = [
            'name' => is_array($data) ? '' : $data,
            'cols' => '40',
            'rows' => '10',
        ];
        if (! is_array($data) || ! isset($data['value'])) {
            $val = $value;
        } else {
            $val = $data['value'];
            unset($data['value']); // textareas don't use the value attribute
        }

        // Unsets default rows and cols if defined in extra field as array or string.
        if ((is_array($extra) && array_key_exists('rows', $extra)) || (is_string($extra) && stripos(
            preg_replace('~\s+~', '', $extra),
            'rows='
        ) !== false)) {
            unset($defaults['rows']);
        }

        if ((is_array($extra) && array_key_exists('cols', $extra)) || (is_string($extra) && stripos(
            preg_replace('~\s+~', '', $extra),
            'cols='
        ) !== false)) {
            unset($defaults['cols']);
        }

        return '<textarea ' . rtrim(parse_form_attributes($data, $defaults)) . stringify_attributes($extra) . '>'
                . $val
                . "</textarea>\n";
    }
}
+81 −33
Original line number Diff line number Diff line
@@ -6,6 +6,16 @@ namespace App\Views\Components\Forms;

class MarkdownEditor extends FormComponent
{
    /**
     * @var string[]
     */
    protected array $disallowList = [];

    public function setDisallowList(string $value): void
    {
        $this->disallowList = explode(',', $value);
    }

    public function render(): string
    {
        $editorClass = 'w-full flex flex-col bg-elevated border-3 border-contrast rounded-lg overflow-hidden focus-within:ring-accent ' . $this->class;
@@ -13,30 +23,83 @@ class MarkdownEditor extends FormComponent
        $this->attributes['class'] = 'bg-elevated border-none focus:border-none focus:outline-none focus:ring-0 w-full h-full';
        $this->attributes['rows'] = 6;

        // dd(htmlspecialchars_decode($this->value));
        $value = htmlspecialchars_decode($this->value);

        $textarea = form_textarea($this->attributes, old($this->name, $value, false));
        $icons = [
            'heading' => icon('heading'),
            'bold' => icon('bold'),
            'italic' => icon('italic'),
            'list-unordered' => icon('list-unordered'),
            'list-ordered' => icon('list-ordered'),
            'quote' => icon('quote'),
            'link' => icon('link'),
            'image-add' => icon('image-add'),
            'markdown' => icon(
        $oldValue = old($this->name);
        if ($oldValue === null) {
            $oldValue = $value;
        }
        $textarea = form_textarea($this->attributes, $oldValue);
        $markdownIcon = icon(
            'markdown',
            'mr-1 text-lg opacity-40'
            ),
        ];
        );
        $translations = [
            'write' => lang('Common.forms.editor.write'),
            'preview' => lang('Common.forms.editor.preview'),
            'help' => lang('Common.forms.editor.help'),
        ];

        $toolbarGroups = [
            [
                [
                    'name' => 'header',
                    'tag' => 'md-header',
                    'icon' => icon('heading'),
                ],
                [
                    'name' => 'bold',
                    'tag' => 'md-bold',
                    'icon' => icon('bold'),
                ],
                [
                    'name' => 'italic',
                    'tag' => 'md-italic',
                    'icon' => icon('italic'),
                ],
            ],
            [
                [
                    'name' => 'unordered-list',
                    'tag' => 'md-unordered-list',
                    'icon' => icon('list-unordered'),
                ],
                [
                    'name' => 'ordered-list',
                    'tag' => 'md-ordered-list ',
                    'icon' => icon('list-ordered'),
                ],
            ],
            [
                [
                    'name' => 'quote',
                    'tag' => 'md-quote',
                    'icon' => icon('quote'),
                ],
                [
                    'name' => 'link',
                    'tag' => 'md-link',
                    'icon' => icon('link'),
                ],
                [
                    'name' => 'image',
                    'tag' => 'md-image',
                    'icon' => icon('image-add'),
                ],
            ],
        ];

        $toolbarContent = '';
        foreach ($toolbarGroups as $buttonsGroup) {
            $toolbarContent .= '<div class="inline-flex text-2xl gap-x-1">';
            foreach ($buttonsGroup as $button) {
                if (! in_array($button['name'], $this->disallowList, true)) {
                    $toolbarContent .= '<' . $button['tag'] . ' class="opacity-50 hover:opacity-100 focus:ring-accent focus:opacity-100">' . $button['icon'] . '</' . $button['tag'] . '>';
                }
            }
            $toolbarContent .= '</div>';
        }

        return <<<HTML
            <div class="{$editorClass}">
                <header class="px-2">
@@ -45,22 +108,7 @@ class MarkdownEditor extends FormComponent
                            <button type="button" slot="write" class="px-2 font-semibold focus:ring-inset focus:ring-accent">{$translations['write']}</button>
                            <button type="button" slot="preview" class="px-2 font-semibold focus:ring-inset focus:ring-accent">{$translations['preview']}</button>
                        </markdown-write-preview>
                        <markdown-toolbar for="{$this->id}" class="flex gap-4 px-2 py-1">
                            <div class="inline-flex text-2xl gap-x-1">
                                <md-header class="opacity-50 hover:opacity-100 focus:ring-accent focus:opacity-100">{$icons['heading']}</md-header>
                                <md-bold class="opacity-50 hover:opacity-100 focus:ring-accent focus:opacity-100" data-hotkey-scope="{$this->id}" data-hotkey="Control+b,Meta+b">{$icons['bold']}</md-bold>
                                <md-italic class="opacity-50 hover:opacity-100 focus:ring-accent focus:opacity-100" data-hotkey-scope="{$this->id}" data-hotkey="Control+i,Meta+i">{$icons['italic']}</md-italic>
                            </div>
                            <div class="inline-flex text-2xl gap-x-1">
                                <md-unordered-list class="opacity-50 hover:opacity-100 focus:ring-accent focus:opacity-100">{$icons['list-unordered']}</md-unordered-list>
                                <md-ordered-list class="opacity-50 hover:opacity-100 focus:ring-accent focus:opacity-100">{$icons['list-ordered']}</md-ordered-list>
                            </div>
                            <div class="inline-flex text-2xl gap-x-1">
                                <md-quote class="opacity-50 hover:opacity-100 focus:ring-accent focus:opacity-100">{$icons['quote']}</md-quote>
                                <md-link class="opacity-50 hover:opacity-100 focus:ring-accent focus:opacity-100" data-hotkey-scope="{$this->id}" data-hotkey="Control+k,Meta+k">{$icons['link']}</md-link>
                                <md-image class="opacity-50 hover:opacity-100 focus:ring-accent focus:opacity-100">{$icons['image-add']}</md-image>
                            </div>
                        </markdown-toolbar>
                        <markdown-toolbar for="{$this->id}" class="flex gap-4 px-2 py-1">{$toolbarContent}</markdown-toolbar>
                    </div>
                </header>
                <div class="relative">
@@ -68,7 +116,7 @@ class MarkdownEditor extends FormComponent
                    <markdown-preview for="{$this->id}" class="absolute top-0 left-0 hidden w-full h-full max-w-full px-3 py-2 overflow-y-auto prose bg-base" showClass="bg-elevated" />
                </div>
                <footer class="flex px-2 py-1 border-t bg-base">
                    <a href="https://commonmark.org/help/" class="inline-flex items-center text-xs font-semibold text-skin-muted hover:text-skin-base" target="_blank" rel="noopener noreferrer">{$icons['markdown']}{$translations['help']}</a>
                    <a href="https://commonmark.org/help/" class="inline-flex items-center text-xs font-semibold text-skin-muted hover:text-skin-base" target="_blank" rel="noopener noreferrer">{$markdownIcon}{$translations['help']}</a>
                </footer>
            </div>
        HTML;
Loading