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
introduced
by
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 |