Passed
Pull Request — master (#156)
by David
05:04
created

RichEditorExtension::getCurrentFilePath()   A

Complexity

Conditions 5
Paths 4

Size

Total Lines 17
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 10
c 0
b 0
f 0
dl 0
loc 17
rs 9.6111
cc 5
nc 4
nop 1
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'], 'needs_context' => true]),
63
            new TwigFilter('monsieurbiz_richeditor_render_elements', [$this, 'renderElements'], ['is_safe' => ['html'], 'needs_context' => true]),
64
            new TwigFilter('monsieurbiz_richeditor_render_element', [$this, 'renderElement'], ['is_safe' => ['html'], 'needs_context' => true]),
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
            new TwigFunction('monsieurbiz_richeditor_get_current_file_path', [$this, 'getCurrentFilePath'], ['needs_context' => true, 'is_safe' => ['html']]),
81
        ];
82
    }
83
84
    /**
85
     * @param array $context
86
     * @param string|null $content
87
     *
88
     * @throws LoaderError
89
     * @throws RuntimeError
90
     * @throws SyntaxError
91
     *
92
     * @return string
93
     */
94
    public function renderField(array $context, ?string $content): string
95
    {
96
        if (null === $content) {
97
            return '';
98
        }
99
100
        $elements = json_decode($content, true);
101
        if (!\is_array($elements)) {
102
            return $content;
103
        }
104
105
        return $this->renderElements($context, $elements);
106
    }
107
108
    /**
109
     * @param string|null $content
110
     *
111
     * @return array
112
     */
113
    public function getElements(?string $content): array
114
    {
115
        if (null === $content) {
116
            return [];
117
        }
118
119
        $elements = json_decode($content, true);
120
        if (!\is_array($elements)) {
121
            // If the JSON decode failed, return a new UIElement with default configuration
122
            return [
123
                'type' => $this->getDefaultElement(),
124
                'data' => [$this->getDefaultElementDataField() => $content],
125
            ];
126
        }
127
128
        return $elements;
129
    }
130
131
    /**
132
     * @param array $context
133
     * @param array $elements
134
     *
135
     * @throws LoaderError
136
     * @throws RuntimeError
137
     * @throws SyntaxError
138
     *
139
     * @return string
140
     */
141
    public function renderElements(array $context, array $elements): string
142
    {
143
        $html = '';
144
        foreach ($elements as $element) {
145
            try {
146
                $html .= $this->renderElement($context, $element);
147
            } catch (UiElementNotFoundException $e) {
148
                continue;
149
            }
150
        }
151
152
        return $html;
153
    }
154
155
    /**
156
     * @param array $context
157
     * @param array $element
158
     *
159
     * @throws UiElementNotFoundException
160
     * @throws LoaderError [twig.render] When the template cannot be found
161
     * @throws SyntaxError [twig.render] When an error occurred during compilation
162
     * @throws RuntimeError [twig.render] When an error occurred during rendering
163
     *
164
     * @return string
165
     */
166
    public function renderElement(array $context, array $element): string
167
    {
168
        if (!isset($element['code'])) {
169
            if (!isset($element['type'], $element['fields'])) {
170
                throw new UiElementNotFoundException('unknown');
171
            }
172
            $element = [
173
                'code' => $element['type'],
174
                'data' => $element['fields'],
175
            ];
176
        }
177
178
        $uiElement = $this->uiElementRegistry->getUiElement($element['code']);
179
        $template = $uiElement->getFrontRenderTemplate();
180
181
        $context = array_merge($context, [
182
            'ui_element' => $uiElement,
183
            'element' => $element['data'],
184
        ]);
185
186
        return $this->twig->render($template, $context);
187
    }
188
189
    /**
190
     * List available Ui Elements in JSON.
191
     *
192
     * @return string
193
     */
194
    public function listUiElements(): string
195
    {
196
        return (string) json_encode($this->uiElementRegistry);
197
    }
198
199
    /**
200
     * Convert Youtube link to embed URL.
201
     *
202
     * @param string $url
203
     *
204
     * @return string|null
205
     */
206
    public function convertYoutubeEmbeddedLink(string $url): ?string
207
    {
208
        if (null === $id = $this->getYoutubeIdFromLink($url)) {
209
            return null;
210
        }
211
212
        return sprintf('https://www.youtube.com/embed/%s', $id);
213
    }
214
215
    /**
216
     * Retrieve the Youtube ID from a Youtube link.
217
     *
218
     * @param string $url
219
     *
220
     * @return string|null
221
     */
222
    public function getYoutubeIdFromLink(string $url): ?string
223
    {
224
        $isValid = (bool) preg_match(YoutubeUrlValidator::YOUTUBE_REGEX_VALIDATOR, $url, $matches);
225
226
        if (!$isValid || !isset($matches[1])) {
227
            return null;
228
        }
229
230
        return $matches[1];
231
    }
232
233
    public function getDefaultElement(): string
234
    {
235
        return $this->defaultElement;
236
    }
237
238
    public function getDefaultElementDataField(): string
239
    {
240
        return $this->defaultElementDataField;
241
    }
242
243
    /**
244
     * @param array $context
245
     *
246
     * @return string|null
247
     */
248
    public function getCurrentFilePath(array $context): ?string
249
    {
250
        $form = $context['form'];
251
        $app = $context['app'];
252
        if (empty($form) || empty($app)) {
253
            return null;
254
        }
255
256
        $path = $form->vars['data'];
257
        if (!empty($app->getRequest()->get('rich_editor_uploaded_files'))) {
258
            $uploadedFile = $app->getRequest()->get('rich_editor_uploaded_files');
259
            if (null !== ($fullName = $uploadedFile['full_name'] ?? null)) {
260
                return $fullName;
261
            }
262
        }
263
264
        return $path;
265
    }
266
}
267