Fail gracefully if CKEditor plugins fail to provide file URI

Created on 14 February 2022, over 2 years ago
Updated 26 December 2023, 5 months ago

Problem/Motivation

When a CKEditor plugin returns an empty string at \Drupal\ckeditor\CKEditorPluginInterface::getFile(), currently drupal breaks hard with:

TypeError: Argument 1 passed to Drupal\Core\File\FileUrlGenerator::generateString() must be of the type string, null given in Drupal\Core\File\FileUrlGenerator->generateString() (line 58 of /var/www/html/core/lib/Drupal/Core/File/FileUrlGenerator.php)
#0 [internal function]: Drupal\Core\File\FileUrlGenerator->generateString(NULL)
#1 /var/www/html/core/modules/ckeditor/src/Plugin/Editor/CKEditor.php(343): array_map(Array, Array)
#2 /var/www/html/core/modules/ckeditor/src/Plugin/Editor/CKEditor.php(265): Drupal\ckeditor\Plugin\Editor\CKEditor->getJSSettings(Object(Drupal\editor\Entity\Editor))
#3 /var/www/html/core/modules/editor/editor.module(177): Drupal\ckeditor\Plugin\Editor\CKEditor->buildConfigurationForm(Array, Object(Drupal\Core\Form\SubformState))
#4 /var/www/html/core/lib/Drupal/Core/Extension/ModuleHandler.php(539): editor_form_filter_format_form_alter(Array, Object(Drupal\Core\Form\FormState), 'filter_format_e...')
#5 /var/www/html/core/lib/Drupal/Core/Form/FormBuilder.php(835): Drupal\Core\Extension\ModuleHandler->alter('form', Array, Object(Drupal\Core\Form\FormState), 'filter_format_e...')
#6 /var/www/html/core/lib/Drupal/Core/Form/FormBuilder.php(279): Drupal\Core\Form\FormBuilder->prepareForm('filter_format_e...', Array, Object(Drupal\Core\Form\FormState))
#7 /var/www/html/core/lib/Drupal/Core/Controller/FormController.php(73): Drupal\Core\Form\FormBuilder->buildForm(Object(Drupal\filter\FilterFormatEditForm), Object(Drupal\Core\Form\FormState))
#8 [internal function]: Drupal\Core\Controller\FormController->getContentResult(Object(Symfony\Component\HttpFoundation\Request), Object(Drupal\Core\Routing\RouteMatch))
#9 /var/www/html/core/lib/Drupal/Core/EventSubscriber/EarlyRenderingControllerWrapperSubscriber.php(123): call_user_func_array(Array, Array)
#10 /var/www/html/core/lib/Drupal/Core/Render/Renderer.php(564): Drupal\Core\EventSubscriber\EarlyRenderingControllerWrapperSubscriber->Drupal\Core\EventSubscriber\{closure}()
#11 /var/www/html/core/lib/Drupal/Core/EventSubscriber/EarlyRenderingControllerWrapperSubscriber.php(124): Drupal\Core\Render\Renderer->executeInRenderContext(Object(Drupal\Core\Render\RenderContext), Object(Closure))
#12 /var/www/html/core/lib/Drupal/Core/EventSubscriber/EarlyRenderingControllerWrapperSubscriber.php(97): Drupal\Core\EventSubscriber\EarlyRenderingControllerWrapperSubscriber->wrapControllerExecutionInRenderContext(Array, Array)
#13 /var/www/html/vendor/symfony/http-kernel/HttpKernel.php(158): Drupal\Core\EventSubscriber\EarlyRenderingControllerWrapperSubscriber->Drupal\Core\EventSubscriber\{closure}()
#14 /var/www/html/vendor/symfony/http-kernel/HttpKernel.php(80): Symfony\Component\HttpKernel\HttpKernel->handleRaw(Object(Symfony\Component\HttpFoundation\Request), 1)
#15 /var/www/html/core/lib/Drupal/Core/StackMiddleware/Session.php(58): Symfony\Component\HttpKernel\HttpKernel->handle(Object(Symfony\Component\HttpFoundation\Request), 1, true)
#16 /var/www/html/core/lib/Drupal/Core/StackMiddleware/KernelPreHandle.php(48): Drupal\Core\StackMiddleware\Session->handle(Object(Symfony\Component\HttpFoundation\Request), 1, true)
#17 /var/www/html/core/modules/page_cache/src/StackMiddleware/PageCache.php(106): Drupal\Core\StackMiddleware\KernelPreHandle->handle(Object(Symfony\Component\HttpFoundation\Request), 1, true)
#18 /var/www/html/core/modules/page_cache/src/StackMiddleware/PageCache.php(85): Drupal\page_cache\StackMiddleware\PageCache->pass(Object(Symfony\Component\HttpFoundation\Request), 1, true)
#19 /var/www/html/core/lib/Drupal/Core/StackMiddleware/ReverseProxyMiddleware.php(48): Drupal\page_cache\StackMiddleware\PageCache->handle(Object(Symfony\Component\HttpFoundation\Request), 1, true)
#20 /var/www/html/core/lib/Drupal/Core/StackMiddleware/NegotiationMiddleware.php(51): Drupal\Core\StackMiddleware\ReverseProxyMiddleware->handle(Object(Symfony\Component\HttpFoundation\Request), 1, true)
#21 /var/www/html/vendor/stack/builder/src/Stack/StackedHttpKernel.php(23): Drupal\Core\StackMiddleware\NegotiationMiddleware->handle(Object(Symfony\Component\HttpFoundation\Request), 1, true)
#22 /var/www/html/core/lib/Drupal/Core/DrupalKernel.php(709): Stack\StackedHttpKernel->handle(Object(Symfony\Component\HttpFoundation\Request), 1, true)
#23 /var/www/html/index.php(19): Drupal\Core\DrupalKernel->handle(Object(Symfony\Component\HttpFoundation\Request))
#24 {main}

Although it's an error condition (a plugin is expected to return a string on that method), it's possible that misconfigured contrib/custom plugins could lead to that scenario, and core should fail more gracefully.

Steps to reproduce

1- Install Drupal with the standard profile
2- Add the contributed https://www.drupal.org/project/ckeditor_a11ychecker β†’ module and its dependency https://www.drupal.org/project/ckeditor_balloonpanel β†’ .
3- Install these modules without placing their library in /libraries. In other words, just download the drupal modules and enable them.
4- Visit /admin/config/content/formats/manage/full_html

Expected: You can use the filter and toolbar, ideally with an error message in the logs.

Actual: WSOD with the error above in the logs.

Proposed resolution

In \Drupal\ckeditor\Plugin\Editor\CKEditor::getJSSettings(), skip plugins that provide an empty file URI, and log a watchdog error message.

Remaining tasks

User interface changes

API changes

Data model changes

Release notes snippet

πŸ› Bug report
Status

Needs review

Version

1.0

Component

Code

Created by

πŸ‡ͺπŸ‡ΈSpain marcoscano Barcelona, Spain

Live updates comments and jobs are added and updated live.
Sign in to follow issues

Comments & Activities

Not all content is available!

It's likely this issue predates Contrib.social: some issue and comment data are missing.

Production build 0.69.0 2024