Completed
Push — master ( b5813c...f1bfc1 )
by Maxime
16s queued 14s
created

RichEditorExtension   A

Complexity

Total Complexity 23

Size/Duplication

Total Lines 208
Duplicated Lines 0 %

Importance

Changes 23
Bugs 0 Features 0
Metric Value
wmc 23
eloc 62
c 23
b 0
f 0
dl 0
loc 208
rs 10

12 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 10 1
A getFilters() 0 6 1
A getElements() 0 16 3
A renderField() 0 12 3
A renderElements() 0 12 3
A getDefaultElementDataField() 0 3 1
A getYoutubeIdFromLink() 0 9 3
A listUiElements() 0 3 1
A getFunctions() 0 9 1
A getDefaultElement() 0 3 1
A convertYoutubeEmbeddedLink() 0 7 2
A renderElement() 0 18 3
1
<?php
2
3
/*
4
 * This file is part of Monsieur Biz' Rich Editor plugin for Sylius.
5
 *
6
 * (c) Monsieur Biz <[email protected]>
7
 *
8
 * For the full copyright and license information, please view the LICENSE
9
 * file that was distributed with this source code.
10
 */
11
12
declare(strict_types=1);
13
14
namespace MonsieurBiz\SyliusRichEditorPlugin\Twig;
15
16
use MonsieurBiz\SyliusRichEditorPlugin\Exception\UiElementNotFoundException;
17
use MonsieurBiz\SyliusRichEditorPlugin\UiElement\RegistryInterface;
18
use MonsieurBiz\SyliusRichEditorPlugin\Validator\Constraints\YoutubeUrlValidator;
19
use Twig\Environment;
20
use Twig\Error\LoaderError;
21
use Twig\Error\RuntimeError;
22
use Twig\Error\SyntaxError;
23
use Twig\Extension\AbstractExtension;
24
use Twig\TwigFilter;
25
use Twig\TwigFunction;
26
27
final class RichEditorExtension extends AbstractExtension
28
{
29
    private RegistryInterface $uiElementRegistry;
30
31
    private Environment $twig;
32
    private string $defaultElement;
33
34
    private string $defaultElementDataField;
35
36
    /**
37
     * RichEditorExtension constructor.
38
     *
39
     * @param RegistryInterface $uiElementRegistry
40
     * @param Environment $twig
41
     * @param string $monsieurbizRicheditorDefaultElement
42
     * @param string $monsieurbizRicheditorDefaultElementDataField
43
     */
44
    public function __construct(
45
        RegistryInterface $uiElementRegistry,
46
        Environment $twig,
47
        string $monsieurbizRicheditorDefaultElement,
48
        string $monsieurbizRicheditorDefaultElementDataField
49
    ) {
50
        $this->uiElementRegistry = $uiElementRegistry;
51
        $this->twig = $twig;
52
        $this->defaultElement = $monsieurbizRicheditorDefaultElement;
53
        $this->defaultElementDataField = $monsieurbizRicheditorDefaultElementDataField;
54
    }
55
56
    /**
57
     * @return TwigFilter[]
58
     */
59
    public function getFilters(): array
60
    {
61
        return [
62
            new TwigFilter('monsieurbiz_richeditor_render_field', [$this, 'renderField'], ['is_safe' => ['html']]),
63
            new TwigFilter('monsieurbiz_richeditor_render_elements', [$this, 'renderElements'], ['is_safe' => ['html']]),
64
            new TwigFilter('monsieurbiz_richeditor_render_element', [$this, 'renderElement'], ['is_safe' => ['html']]),
65
        ];
66
    }
67
68
    /**
69
     * @return array|TwigFunction[]
70
     */
71
    public function getFunctions(): array
72
    {
73
        return [
74
            new TwigFunction('monsieurbiz_richeditor_list_elements', [$this, 'listUiElements'], ['is_safe' => ['html', 'js']]),
75
            new TwigFunction('monsieurbiz_richeditor_youtube_link', [$this, 'convertYoutubeEmbeddedLink'], ['is_safe' => ['html', 'js']]),
76
            new TwigFunction('monsieurbiz_richeditor_youtube_id', [$this, 'getYoutubeIdFromLink'], ['is_safe' => ['html', 'js']]),
77
            new TwigFunction('monsieurbiz_richeditor_get_elements', [$this, 'getElements'], ['is_safe' => ['html']]),
78
            new TwigFunction('monsieurbiz_richeditor_get_default_element', [$this, 'getDefaultElement'], ['is_safe' => ['html']]),
79
            new TwigFunction('monsieurbiz_richeditor_get_default_element_data_field', [$this, 'getDefaultElementDataField'], ['is_safe' => ['html']]),
80
        ];
81
    }
82
83
    /**
84
     * @param string|null $content
85
     *
86
     * @throws LoaderError
87
     * @throws RuntimeError
88
     * @throws SyntaxError
89
     *
90
     * @return string
91
     */
92
    public function renderField(?string $content): string
93
    {
94
        if (null === $content) {
95
            return '';
96
        }
97
98
        $elements = json_decode($content, true);
99
        if (!\is_array($elements)) {
100
            return $content;
101
        }
102
103
        return $this->renderElements($elements);
104
    }
105
106
    /**
107
     * @param string|null $content
108
     *
109
     * @return array
110
     */
111
    public function getElements(?string $content): array
112
    {
113
        if (null === $content) {
114
            return [];
115
        }
116
117
        $elements = json_decode($content, true);
118
        if (!\is_array($elements)) {
119
            // If the JSON decode failed, return a new UIElement with default configuration
120
            return [
121
                'type' => $this->getDefaultElement(),
122
                'data' => [$this->getDefaultElementDataField() => $content],
123
            ];
124
        }
125
126
        return $elements;
127
    }
128
129
    /**
130
     * @param array $elements
131
     *
132
     * @throws LoaderError
133
     * @throws RuntimeError
134
     * @throws SyntaxError
135
     *
136
     * @return string
137
     */
138
    public function renderElements(array $elements): string
139
    {
140
        $html = '';
141
        foreach ($elements as $element) {
142
            try {
143
                $html .= $this->renderElement($element);
144
            } catch (UiElementNotFoundException $e) {
145
                continue;
146
            }
147
        }
148
149
        return $html;
150
    }
151
152
    /**
153
     * @param array $element
154
     *
155
     * @throws UiElementNotFoundException
156
     * @throws LoaderError [twig.render] When the template cannot be found
157
     * @throws SyntaxError [twig.render] When an error occurred during compilation
158
     * @throws RuntimeError [twig.render] When an error occurred during rendering
159
     *
160
     * @return string
161
     */
162
    public function renderElement(array $element): string
163
    {
164
        if (!isset($element['code'])) {
165
            if (!isset($element['type'], $element['fields'])) {
166
                throw new UiElementNotFoundException('unknown');
167
            }
168
            $element = [
169
                'code' => $element['type'],
170
                'data' => $element['fields'],
171
            ];
172
        }
173
174
        $uiElement = $this->uiElementRegistry->getUiElement($element['code']);
175
        $template = $uiElement->getFrontRenderTemplate();
176
177
        return $this->twig->render($template, [
178
            'ui_element' => $uiElement,
179
            'element' => $element['data'],
180
        ]);
181
    }
182
183
    /**
184
     * List available Ui Elements in JSON.
185
     *
186
     * @return string
187
     */
188
    public function listUiElements(): string
189
    {
190
        return (string) json_encode($this->uiElementRegistry);
191
    }
192
193
    /**
194
     * Convert Youtube link to embed URL.
195
     *
196
     * @param string $url
197
     *
198
     * @return string|null
199
     */
200
    public function convertYoutubeEmbeddedLink(string $url): ?string
201
    {
202
        if (null === $id = $this->getYoutubeIdFromLink($url)) {
203
            return null;
204
        }
205
206
        return sprintf('https://www.youtube.com/embed/%s', $id);
207
    }
208
209
    /**
210
     * Retrieve the Youtube ID from a Youtube link.
211
     *
212
     * @param string $url
213
     *
214
     * @return string|null
215
     */
216
    public function getYoutubeIdFromLink(string $url): ?string
217
    {
218
        $isValid = (bool) preg_match(YoutubeUrlValidator::YOUTUBE_REGEX_VALIDATOR, $url, $matches);
219
220
        if (!$isValid || !isset($matches[1])) {
221
            return null;
222
        }
223
224
        return $matches[1];
225
    }
226
227
    public function getDefaultElement(): string
228
    {
229
        return $this->defaultElement;
230
    }
231
232
    public function getDefaultElementDataField(): string
233
    {
234
        return $this->defaultElementDataField;
235
    }
236
}
237