Completed
Pull Request — master (#40)
by Marko
116:46 queued 51:48
created

CKEditorRenderer   B

Complexity

Total Complexity 52

Size/Duplication

Total Lines 345
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 7

Importance

Changes 0
Metric Value
wmc 52
lcom 1
cbo 7
dl 0
loc 345
rs 7.9487
c 0
b 0
f 0

15 Methods

Rating   Name   Duplication   Size   Complexity  
C __construct() 0 32 8
A renderBasePath() 0 4 1
A renderJsPath() 0 4 1
C renderWidget() 0 32 8
A renderDestroy() 0 10 1
A renderPlugin() 0 9 1
A renderStylesSet() 0 10 1
B renderTemplate() 0 26 6
A fixConfigLanguage() 0 12 4
A fixConfigContentsCss() 0 13 3
B fixConfigFilebrowsers() 0 38 6
B fixConfigEscapedValues() 0 19 5
A fixConfigConstants() 0 4 1
A fixPath() 0 14 4
A getLanguage() 0 9 2

How to fix   Complexity   

Complex Class

Complex classes like CKEditorRenderer often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use CKEditorRenderer, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
/*
4
 * This file is part of the Ivory CKEditor package.
5
 *
6
 * (c) Eric GELOEN <[email protected]>
7
 *
8
 * For the full copyright and license information, please read the LICENSE
9
 * file that was distributed with this source code.
10
 */
11
12
namespace FOS\CKEditorBundle\Renderer;
13
14
use Ivory\JsonBuilder\JsonBuilder;
15
use Symfony\Component\Asset\Packages;
16
use Symfony\Component\DependencyInjection\ContainerInterface;
17
use Symfony\Component\HttpFoundation\RequestStack;
18
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
19
use Symfony\Component\Routing\RouterInterface;
20
use Symfony\Component\Templating\EngineInterface;
21
22
/**
23
 * @author GeLo <[email protected]>
24
 */
25
class CKEditorRenderer implements CKEditorRendererInterface
26
{
27
    /**
28
     * @var JsonBuilder
29
     */
30
    private $jsonBuilder;
31
32
    /**
33
     * @var RouterInterface
34
     */
35
    private $router;
36
37
    /**
38
     * @var Packages
39
     */
40
    private $assetsPackages;
41
42
    /**
43
     * @var EngineInterface
44
     */
45
    private $templating;
46
47
    /**
48
     * @var RequestStack
49
     */
50
    private $requestStack;
51
52
    /**
53
     * @var null|string
54
     */
55
    private $locale;
56
57
    /**
58
     * @param JsonBuilder|ContainerInterface $containerOrJsonBuilder
59
     * @param RouterInterface                $router
60
     * @param Packages                       $packages
61
     * @param RequestStack                   $requestStack
62
     * @param EngineInterface                $templating
63
     * @param null|string                    $locale
64
     */
65
    public function __construct($containerOrJsonBuilder, RouterInterface $router = null, Packages $packages = null, RequestStack $requestStack = null, EngineInterface
66
    $templating = null, $locale = null)
67
    {
68
        if ($containerOrJsonBuilder instanceof ContainerInterface) {
69
            @trigger_error(sprintf('Passing a %s as %s first argument is deprecated since FOSCKEditor 6.1, and will be removed in 7.0. Use %s instead.', ContainerInterface::class, __METHOD__, JsonBuilder::class), E_USER_DEPRECATED);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
70
            $jsonBuilder = $containerOrJsonBuilder->get('fos_ck_editor.renderer.json_builder');
71
            $router = $containerOrJsonBuilder->get('router');
72
            $packages = $containerOrJsonBuilder->get('assets.packages');
73
            $requestStack = $containerOrJsonBuilder->get('request_stack');
74
            $templating = $containerOrJsonBuilder->get('templating');
75
        } elseif ($containerOrJsonBuilder instanceof JsonBuilder) {
76
            $jsonBuilder = $containerOrJsonBuilder;
77
            if ($router === null) {
78
                throw new \InvalidArgumentException(sprintf('%s 2nd argument must not be null when using %s as first argument', __METHOD__, JsonBuilder::class));
79
            } elseif ($packages === null) {
80
                throw new \InvalidArgumentException(sprintf('%s 3rd argument must not be null when using %s as first argument', __METHOD__, JsonBuilder::class));
81
            } elseif ($requestStack === null) {
82
                throw new \InvalidArgumentException(sprintf('%s 4th argument must not be null when using %s as first argument', __METHOD__, JsonBuilder::class));
83
            } elseif ($templating === null) {
84
                throw new \InvalidArgumentException(sprintf('%s 5th argument must not be null when using %s as first argument', __METHOD__, JsonBuilder::class));
85
            }
86
        } else {
87
            throw new \InvalidArgumentException(sprintf('%s first argument must be an instance of %s or %s (%s given).', __METHOD__, ContainerInterface::class, JsonBuilder::class, is_object($containerOrJsonBuilder) ? get_class($containerOrJsonBuilder) : gettype($containerOrJsonBuilder)));
88
        }
89
90
        $this->jsonBuilder = $jsonBuilder;
91
        $this->router = $router;
92
        $this->assetsPackages = $packages;
93
        $this->templating = $templating;
94
        $this->requestStack = $requestStack;
95
        $this->locale = $locale;
96
    }
97
98
    /**
99
     * {@inheritdoc}
100
     */
101
    public function renderBasePath($basePath)
102
    {
103
        return $this->fixPath($basePath);
104
    }
105
106
    /**
107
     * {@inheritdoc}
108
     */
109
    public function renderJsPath($jsPath)
110
    {
111
        return $this->fixPath($jsPath);
112
    }
113
114
    /**
115
     * {@inheritdoc}
116
     */
117
    public function renderWidget($id, array $config, array $options = [])
118
    {
119
        $config = $this->fixConfigLanguage($config);
120
        $config = $this->fixConfigContentsCss($config);
121
        $config = $this->fixConfigFilebrowsers(
122
            $config,
123
            isset($options['filebrowsers']) ? $options['filebrowsers'] : []
124
        );
125
126
        $autoInline = isset($options['auto_inline']) && !$options['auto_inline']
127
            ? 'CKEDITOR.disableAutoInline = true;'."\n"
128
            : null;
129
130
        $builder = $this->jsonBuilder->reset()->setValues($config);
131
        $this->fixConfigEscapedValues($builder, $config);
132
133
        $widget = sprintf(
134
            'CKEDITOR.%s("%s", %s);',
135
            isset($options['inline']) && $options['inline'] ? 'inline' : 'replace',
136
            $id,
137
            $this->fixConfigConstants($builder->build())
138
        );
139
140
        if (isset($options['input_sync']) && $options['input_sync']) {
141
            $variable = 'ivory_ckeditor_'.$id;
142
            $widget = 'var '.$variable.' = '.$widget."\n";
143
144
            return $autoInline.$widget.$variable.'.on(\'change\', function() { '.$variable.'.updateElement(); });';
145
        }
146
147
        return $autoInline.$widget;
148
    }
149
150
    /**
151
     * {@inheritdoc}
152
     */
153
    public function renderDestroy($id)
154
    {
155
        return sprintf(
156
            'if (CKEDITOR.instances["%1$s"]) { '.
157
            'CKEDITOR.instances["%1$s"].destroy(true); '.
158
            'delete CKEDITOR.instances["%1$s"]; '.
159
            '}',
160
            $id
161
        );
162
    }
163
164
    /**
165
     * {@inheritdoc}
166
     */
167
    public function renderPlugin($name, array $plugin)
168
    {
169
        return sprintf(
170
            'CKEDITOR.plugins.addExternal("%s", "%s", "%s");',
171
            $name,
172
            $this->fixPath($plugin['path']),
173
            $plugin['filename']
174
        );
175
    }
176
177
    /**
178
     * {@inheritdoc}
179
     */
180
    public function renderStylesSet($name, array $stylesSet)
181
    {
182
        return sprintf(
183
            'if (CKEDITOR.stylesSet.get("%1$s") === null) { '.
184
            'CKEDITOR.stylesSet.add("%1$s", %2$s); '.
185
            '}',
186
            $name,
187
            $this->jsonBuilder->reset()->setValues($stylesSet)->build()
188
        );
189
    }
190
191
    /**
192
     * {@inheritdoc}
193
     */
194
    public function renderTemplate($name, array $template)
195
    {
196
        if (isset($template['imagesPath'])) {
197
            $template['imagesPath'] = $this->fixPath($template['imagesPath']);
198
        }
199
200
        if (isset($template['templates'])) {
201
            foreach ($template['templates'] as &$rawTemplate) {
0 ignored issues
show
Bug introduced by
The expression $template['templates'] of type string is not traversable.
Loading history...
202
                if (isset($rawTemplate['template'])) {
203
                    $rawTemplate['html'] = $this->templating->render(
204
                        $rawTemplate['template'],
205
                        isset($rawTemplate['template_parameters']) ? $rawTemplate['template_parameters'] : []
206
                    );
207
                }
208
209
                unset($rawTemplate['template']);
210
                unset($rawTemplate['template_parameters']);
211
            }
212
        }
213
214
        return sprintf(
215
            'CKEDITOR.addTemplates("%s", %s);',
216
            $name,
217
            $this->jsonBuilder->reset()->setValues($template)->build()
218
        );
219
    }
220
221
    /**
222
     * @param array $config
223
     *
224
     * @return array
225
     */
226
    private function fixConfigLanguage(array $config)
227
    {
228
        if (!isset($config['language']) && ($language = $this->getLanguage()) !== null) {
229
            $config['language'] = $language;
230
        }
231
232
        if (isset($config['language'])) {
233
            $config['language'] = strtolower(str_replace('_', '-', $config['language']));
234
        }
235
236
        return $config;
237
    }
238
239
    /**
240
     * @param array $config
241
     *
242
     * @return array
243
     */
244
    private function fixConfigContentsCss(array $config)
245
    {
246
        if (isset($config['contentsCss'])) {
247
            $cssContents = (array) $config['contentsCss'];
248
249
            $config['contentsCss'] = [];
250
            foreach ($cssContents as $cssContent) {
251
                $config['contentsCss'][] = $this->fixPath($cssContent);
252
            }
253
        }
254
255
        return $config;
256
    }
257
258
    /**
259
     * @param array $config
260
     * @param array $filebrowsers
261
     *
262
     * @return array
263
     */
264
    private function fixConfigFilebrowsers(array $config, array $filebrowsers)
265
    {
266
        $filebrowsers = array_unique(array_merge([
267
            'Browse',
268
            'FlashBrowse',
269
            'ImageBrowse',
270
            'ImageBrowseLink',
271
            'Upload',
272
            'FlashUpload',
273
            'ImageUpload',
274
        ], $filebrowsers));
275
276
        foreach ($filebrowsers as $filebrowser) {
277
            $fileBrowserKey = 'filebrowser'.$filebrowser;
278
            $handler = $fileBrowserKey.'Handler';
279
            $url = $fileBrowserKey.'Url';
280
            $route = $fileBrowserKey.'Route';
281
            $routeParameters = $fileBrowserKey.'RouteParameters';
282
            $routeType = $fileBrowserKey.'RouteType';
283
284
            if (isset($config[$handler])) {
285
                $config[$url] = $config[$handler]($this->router);
286
            } elseif (isset($config[$route])) {
287
                $config[$url] = $this->router->generate(
288
                    $config[$route],
289
                    isset($config[$routeParameters]) ? $config[$routeParameters] : [],
290
                    isset($config[$routeType]) ? $config[$routeType] : UrlGeneratorInterface::ABSOLUTE_PATH
291
                );
292
            }
293
294
            unset($config[$handler]);
295
            unset($config[$route]);
296
            unset($config[$routeParameters]);
297
            unset($config[$routeType]);
298
        }
299
300
        return $config;
301
    }
302
303
    /**
304
     * @param JsonBuilder $builder
305
     * @param array       $config
306
     */
307
    private function fixConfigEscapedValues(JsonBuilder $builder, array $config)
308
    {
309
        if (isset($config['protectedSource'])) {
310
            foreach ($config['protectedSource'] as $key => $value) {
311
                $builder->setValue(sprintf('[protectedSource][%s]', $key), $value, false);
312
            }
313
        }
314
315
        $escapedValueKeys = [
316
            'stylesheetParser_skipSelectors',
317
            'stylesheetParser_validSelectors',
318
        ];
319
320
        foreach ($escapedValueKeys as $escapedValueKey) {
321
            if (isset($config[$escapedValueKey])) {
322
                $builder->setValue(sprintf('[%s]', $escapedValueKey), $config[$escapedValueKey], false);
323
            }
324
        }
325
    }
326
327
    /**
328
     * @param string $json
329
     *
330
     * @return string
331
     */
332
    private function fixConfigConstants($json)
333
    {
334
        return preg_replace('/"(CKEDITOR\.[A-Z_]+)"/', '$1', $json);
335
    }
336
337
    /**
338
     * @param string $path
339
     *
340
     * @return string
341
     */
342
    private function fixPath($path)
343
    {
344
        if ($this->assetsPackages === null) {
345
            return $path;
346
        }
347
348
        $url = $this->assetsPackages->getUrl($path);
349
350
        if (substr($path, -1) === '/' && ($position = strpos($url, '?')) !== false) {
351
            $url = substr($url, 0, $position);
352
        }
353
354
        return $url;
355
    }
356
357
    /**
358
     * @return null|string
359
     */
360
    private function getLanguage()
361
    {
362
        $request = $this->requestStack->getCurrentRequest();
363
        if ($request !== null) {
364
            return $request->getLocale();
365
        }
366
367
        return $this->locale;
368
    }
369
}
370