Completed
Push — master ( b28d44...6f2c13 )
by Marko
40:03 queued 25:04
created

src/Renderer/CKEditorRenderer.php (1 issue)

Severity
1
<?php
2
3
/*
4
 * This file is part of the FOSCKEditor Bundle.
5
 *
6
 * (c) 2018 - present  Friends of Symfony
7
 * (c) 2009 - 2017     Eric GELOEN <[email protected]>
8
 *
9
 * For the full copyright and license information, please read the LICENSE
10
 * file that was distributed with this source code.
11
 */
12
13
namespace FOS\CKEditorBundle\Renderer;
14
15
use FOS\CKEditorBundle\Builder\JsonBuilder;
16
use Symfony\Component\Asset\Packages;
17
use Symfony\Component\DependencyInjection\ContainerInterface;
18
use Symfony\Component\HttpFoundation\RequestStack;
19
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
20
use Symfony\Component\Routing\RouterInterface;
21
use Twig\Environment;
22
23
/**
24
 * @author GeLo <[email protected]>
25
 */
26
final class CKEditorRenderer implements CKEditorRendererInterface
27
{
28
    /**
29
     * @var JsonBuilder
30
     */
31
    private $jsonBuilder;
32
33
    /**
34
     * @var RouterInterface
35
     */
36
    private $router;
37
38
    /**
39
     * @var Packages
40
     */
41
    private $assetsPackages;
42
43
    /**
44
     * @var Environment
45
     */
46
    private $twig;
47
48
    /**
49
     * @var RequestStack
50
     */
51
    private $requestStack;
52
53
    /**
54
     * @var null|string
55
     */
56
    private $locale;
57
58
    /**
59
     * @param JsonBuilder|ContainerInterface $containerOrJsonBuilder
60
     * @param RouterInterface                $router
61
     * @param Packages                       $packages
62
     * @param RequestStack                   $requestStack
63
     * @param Environment                    $twig
64
     * @param null|string                    $locale
65
     */
66
    public function __construct(
67
        $containerOrJsonBuilder,
68
        RouterInterface $router = null,
69
        Packages $packages = null,
70
        RequestStack $requestStack = null,
71
        Environment $twig = null,
72
        $locale = null
73
    ) {
74
        if ($containerOrJsonBuilder instanceof ContainerInterface) {
75
            @trigger_error(sprintf(
76
                'Passing a %s as %s first argument is deprecated since FOSCKEditor 1.0, and will be removed in 2.0.'
77
                .' Use %s instead.',
78
                ContainerInterface::class,
79
                __METHOD__,
80
                JsonBuilder::class
81
            ), E_USER_DEPRECATED);
82
            $jsonBuilder = $containerOrJsonBuilder->get('fos_ck_editor.renderer.json_builder');
83
            $router = $containerOrJsonBuilder->get('router');
84
            $packages = $containerOrJsonBuilder->get('assets.packages');
85
            $requestStack = $containerOrJsonBuilder->get('request_stack');
86
            $twig = $containerOrJsonBuilder->get('twig');
87
        } elseif ($containerOrJsonBuilder instanceof JsonBuilder) {
0 ignored issues
show
$containerOrJsonBuilder is always a sub-type of FOS\CKEditorBundle\Builder\JsonBuilder.
Loading history...
88
            $jsonBuilder = $containerOrJsonBuilder;
89
            if (null === $router) {
90
                throw new \InvalidArgumentException(sprintf(
91
                    '%s 2nd argument must not be null when using %s as first argument',
92
                    __METHOD__,
93
                    JsonBuilder::class
94
                ));
95
            } elseif (null === $packages) {
96
                throw new \InvalidArgumentException(sprintf(
97
                    '%s 3rd argument must not be null when using %s as first argument',
98
                    __METHOD__,
99
                    JsonBuilder::class
100
                ));
101
            } elseif (null === $requestStack) {
102
                throw new \InvalidArgumentException(sprintf(
103
                    '%s 4th argument must not be null when using %s as first argument',
104
                    __METHOD__,
105
                    JsonBuilder::class
106
                ));
107
            } elseif (null === $twig) {
108
                throw new \InvalidArgumentException(sprintf(
109
                    '%s 5th argument must not be null when using %s as first argument',
110
                    __METHOD__,
111
                    JsonBuilder::class
112
                ));
113
            }
114
        } else {
115
            throw new \InvalidArgumentException(sprintf(
116
                '%s first argument must be an instance of %s or %s (%s given).',
117
                __METHOD__,
118
                ContainerInterface::class,
119
                JsonBuilder::class,
120
                is_object($containerOrJsonBuilder)
121
                    ? get_class($containerOrJsonBuilder)
122
                    : gettype($containerOrJsonBuilder)
123
            ));
124
        }
125
126
        $this->jsonBuilder = $jsonBuilder;
127
        $this->router = $router;
128
        $this->assetsPackages = $packages;
129
        $this->twig = $twig;
130
        $this->requestStack = $requestStack;
131
        $this->locale = $locale;
132
    }
133
134
    /**
135
     * {@inheritdoc}
136
     */
137
    public function renderBasePath($basePath)
138
    {
139
        return $this->fixPath($basePath);
140
    }
141
142
    /**
143
     * {@inheritdoc}
144
     */
145
    public function renderJsPath($jsPath)
146
    {
147
        return $this->fixPath($jsPath);
148
    }
149
150
    /**
151
     * {@inheritdoc}
152
     */
153
    public function renderWidget($id, array $config, array $options = [])
154
    {
155
        $config = $this->fixConfigLanguage($config);
156
        $config = $this->fixConfigContentsCss($config);
157
        $config = $this->fixConfigFilebrowsers(
158
            $config,
159
            isset($options['filebrowsers']) ? $options['filebrowsers'] : []
160
        );
161
162
        $autoInline = isset($options['auto_inline']) && !$options['auto_inline']
163
            ? 'CKEDITOR.disableAutoInline = true;'."\n"
164
            : null;
165
166
        $builder = $this->jsonBuilder->reset()->setValues($config);
167
        $this->fixConfigEscapedValues($builder, $config);
168
169
        $widget = sprintf(
170
            'CKEDITOR.%s("%s", %s);',
171
            isset($options['inline']) && $options['inline'] ? 'inline' : 'replace',
172
            $id,
173
            $this->fixConfigConstants($builder->build())
174
        );
175
176
        if (isset($options['input_sync']) && $options['input_sync']) {
177
            $variable = 'fos_ckeditor_'.$id;
178
            $widget = 'var '.$variable.' = '.$widget."\n";
179
180
            return $autoInline.$widget.$variable.'.on(\'change\', function() { '.$variable.'.updateElement(); });';
181
        }
182
183
        return $autoInline.$widget;
184
    }
185
186
    /**
187
     * {@inheritdoc}
188
     */
189
    public function renderDestroy($id)
190
    {
191
        return sprintf(
192
            'if (CKEDITOR.instances["%1$s"]) { '.
193
            'CKEDITOR.instances["%1$s"].destroy(true); '.
194
            'delete CKEDITOR.instances["%1$s"]; '.
195
            '}',
196
            $id
197
        );
198
    }
199
200
    /**
201
     * {@inheritdoc}
202
     */
203
    public function renderPlugin($name, array $plugin)
204
    {
205
        return sprintf(
206
            'CKEDITOR.plugins.addExternal("%s", "%s", "%s");',
207
            $name,
208
            $this->fixPath($plugin['path']),
209
            $plugin['filename']
210
        );
211
    }
212
213
    /**
214
     * {@inheritdoc}
215
     */
216
    public function renderStylesSet($name, array $stylesSet)
217
    {
218
        return sprintf(
219
            'if (CKEDITOR.stylesSet.get("%1$s") === null) { '.
220
            'CKEDITOR.stylesSet.add("%1$s", %2$s); '.
221
            '}',
222
            $name,
223
            $this->jsonBuilder->reset()->setValues($stylesSet)->build()
224
        );
225
    }
226
227
    /**
228
     * {@inheritdoc}
229
     */
230
    public function renderTemplate($name, array $template)
231
    {
232
        if (isset($template['imagesPath'])) {
233
            $template['imagesPath'] = $this->fixPath($template['imagesPath']);
234
        }
235
236
        if (isset($template['templates'])) {
237
            foreach ($template['templates'] as &$rawTemplate) {
238
                if (isset($rawTemplate['template'])) {
239
                    $rawTemplate['html'] = $this->twig->render(
240
                        $rawTemplate['template'],
241
                        isset($rawTemplate['template_parameters']) ? $rawTemplate['template_parameters'] : []
242
                    );
243
                }
244
245
                unset($rawTemplate['template'], $rawTemplate['template_parameters']);
246
            }
247
        }
248
249
        return sprintf(
250
            'CKEDITOR.addTemplates("%s", %s);',
251
            $name,
252
            $this->jsonBuilder->reset()->setValues($template)->build()
253
        );
254
    }
255
256
    /**
257
     * @param array $config
258
     *
259
     * @return array
260
     */
261
    private function fixConfigLanguage(array $config)
262
    {
263
        if (!isset($config['language']) && null !== ($language = $this->getLanguage())) {
264
            $config['language'] = $language;
265
        }
266
267
        if (isset($config['language'])) {
268
            $config['language'] = strtolower(str_replace('_', '-', $config['language']));
269
        }
270
271
        return $config;
272
    }
273
274
    /**
275
     * @param array $config
276
     *
277
     * @return array
278
     */
279
    private function fixConfigContentsCss(array $config)
280
    {
281
        if (isset($config['contentsCss'])) {
282
            $cssContents = (array) $config['contentsCss'];
283
284
            $config['contentsCss'] = [];
285
            foreach ($cssContents as $cssContent) {
286
                $config['contentsCss'][] = $this->fixPath($cssContent);
287
            }
288
        }
289
290
        return $config;
291
    }
292
293
    /**
294
     * @param array $config
295
     * @param array $filebrowsers
296
     *
297
     * @return array
298
     */
299
    private function fixConfigFilebrowsers(array $config, array $filebrowsers)
300
    {
301
        $filebrowsers = array_unique(array_merge([
302
            'Browse',
303
            'FlashBrowse',
304
            'ImageBrowse',
305
            'ImageBrowseLink',
306
            'Upload',
307
            'FlashUpload',
308
            'ImageUpload',
309
        ], $filebrowsers));
310
311
        foreach ($filebrowsers as $filebrowser) {
312
            $fileBrowserKey = 'filebrowser'.$filebrowser;
313
            $handler = $fileBrowserKey.'Handler';
314
            $url = $fileBrowserKey.'Url';
315
            $route = $fileBrowserKey.'Route';
316
            $routeParameters = $fileBrowserKey.'RouteParameters';
317
            $routeType = $fileBrowserKey.'RouteType';
318
319
            if (isset($config[$handler])) {
320
                $config[$url] = $config[$handler]($this->router);
321
            } elseif (isset($config[$route])) {
322
                $config[$url] = $this->router->generate(
323
                    $config[$route],
324
                    isset($config[$routeParameters]) ? $config[$routeParameters] : [],
325
                    isset($config[$routeType]) ? $config[$routeType] : UrlGeneratorInterface::ABSOLUTE_PATH
326
                );
327
            }
328
329
            unset($config[$handler], $config[$route], $config[$routeParameters], $config[$routeType]);
330
        }
331
332
        return $config;
333
    }
334
335
    /**
336
     * @param JsonBuilder $builder
337
     * @param array       $config
338
     */
339
    private function fixConfigEscapedValues(JsonBuilder $builder, array $config)
340
    {
341
        if (isset($config['protectedSource'])) {
342
            foreach ($config['protectedSource'] as $key => $value) {
343
                $builder->setValue(sprintf('[protectedSource][%s]', $key), $value, false);
344
            }
345
        }
346
347
        $escapedValueKeys = [
348
            'stylesheetParser_skipSelectors',
349
            'stylesheetParser_validSelectors',
350
        ];
351
352
        foreach ($escapedValueKeys as $escapedValueKey) {
353
            if (isset($config[$escapedValueKey])) {
354
                $builder->setValue(sprintf('[%s]', $escapedValueKey), $config[$escapedValueKey], false);
355
            }
356
        }
357
    }
358
359
    /**
360
     * @param string $json
361
     *
362
     * @return string
363
     */
364
    private function fixConfigConstants($json)
365
    {
366
        return preg_replace('/"(CKEDITOR\.[A-Z_]+)"/', '$1', $json);
367
    }
368
369
    /**
370
     * @param string $path
371
     *
372
     * @return string
373
     */
374
    private function fixPath($path)
375
    {
376
        if (null === $this->assetsPackages) {
377
            return $path;
378
        }
379
380
        $url = $this->assetsPackages->getUrl($path);
381
382
        if ('/' === substr($path, -1) && false !== ($position = strpos($url, '?'))) {
383
            $url = substr($url, 0, $position);
384
        }
385
386
        return $url;
387
    }
388
389
    /**
390
     * @return null|string
391
     */
392
    private function getLanguage()
393
    {
394
        $request = $this->requestStack->getCurrentRequest();
395
        if (null !== $request) {
396
            return $request->getLocale();
397
        }
398
399
        return $this->locale;
400
    }
401
}
402