Problem/Motivation
Quoting #3228465: Final pass over the public API prior to creating core merge request, point 6:
We should probably remove the
settingsForm()
method from\Drupal\ckeditor5\Plugin\CKEditor5PluginConfigurableInterface
and instead makeCKEditor5PluginConfigurableInterface
also extend\Drupal\Core\Plugin\PluginFormInterface
. It's not because CKEditor 4 had that in\Drupal\ckeditor\CKEditorPluginConfigurableInterface
that we need to repeat that pattern for CKEditor 5. (That interface was committed in February 2013, andPluginFormInterface
was committed in August 2013, which explains this.)
Steps to reproduce
N/A
Proposed resolution
TBD
Remaining tasks
TBD
User interface changes
TBD
API changes
TBD
Data model changes
None.
Comment | File | Size | Author |
---|---|---|---|
#12 | Screenshot 2021-08-18 at 15.48.17.png | 971.5 KB | Wim Leers |
Issue fork ckeditor5-3228477
Show commands
Start within a Git clone of the project using the version control instructions.
Or, if you do not have SSH keys set up on git.drupalcode.org:
Comments
Comment #2
Wim LeersI dug into this, first concluded it does not make sense, and then eventually after spending a few hours on a PoC, I concluded it totally makes sense, and is more consistent with the rest of Drupal core/follows more best practices.
Tried, this is impossible. We need these subforms to be aware of the Text Editor config entity this form is generated for. We can't add a new argument to this method.Well … what if
$form_state->getFormObject()->getEntity()
actually returned the text editor config entity it's supposed to return?!Furthermore, thesubmitConfigurationForm()
part definitely does not make sense — since this will not be submitted by itself.That's actually fine … plugin system maintainer @tim.plunkett has already set an example in 2014, in #1846172: Replace the actions API, where
\Drupal\Core\Action\Plugin\Action\EmailAction::submitConfigurationForm()
doesClearly, that's not saving anything! Same in the more recent
\Drupal\Core\Layout\LayoutDefault::submitConfigurationForm()
.Finally,We could simply not call the validation method…validateConfigurationForm()
actually invites writing form validation logic, but it really should have validation constraints.Comment #4
Wim LeersNote that this branch is relative to #3222852: <dl> <dt> <dd> by introducing "Manually editable HTML tags" configuration on Source Editing — i.e. it includes its full commit history, because that's where this problem became apparent. This should either land after #3222852, or it should be rebased to apply to latest
1.0.x
— either way works.Happy to push this forward more, but would first like reviews, especially from @tim.plunkett.
Comment #5
tim.plunkettTypeError: implode(): Argument #2 ($array) must be of type ?array, string given in implode() (line 29 of /Users/tim.plunkett/www/d8/modules/ckeditor5/src/Plugin/CKEditor5Plugin/SourceEditing.php)
Comment #6
tim.plunkettI pushed up a commit that starts down the path of ConfigurableInterface, but I did not finish everything. My worries now are about the synchronization of configuration from the plugin back to the editor.
In the standard ConfigEntity:plugins relationship, EntityWithPluginCollectionInterface is used.
See
\Drupal\Core\Config\Entity\ConfigEntityBase::preSave()
and\Drupal\Core\Config\Entity\ConfigEntityBase::set()
(and __sleep() and calculateDependencies() handle it too, which is nice!)
Comment #8
tim.plunkettAttempted a rebase after dl/dd/dt landed, but not confident enough to overwrite the existing branch. So posted a new one for now.
Comment #9
tim.plunkettHere's the last fail:
allowed_values
is sometimes an array when it should always be a string.This doesn't happen in HEAD because of a call to
HTMLRestrictionsUtilities::allowedElementsStringToPluginElementsArray()
in\Drupal\ckeditor5\AdminUi::getSubmittedEditorAndFilterFormat()
This MR moves that call to
\Drupal\ckeditor5\Plugin\CKEditor5Plugin\SourceEditing::validateConfigurationForm()
, which is semantically the best place for it. Except that is still somehow not taking effect (or happening too late, too early, etc).Because we constantly throw away and rebuild these plugins (not sure why, but we do), it pulls the values from the editor entity.
At some point we're getting a mismatch in data, the entity (incorrectly) has the allowed_values as a string, and the plugin is then created with invalid config.
I don't know how to best figure out where/why that is happening. Any advice/help is welcome.
Comment #10
Wim LeersI noticed that too while debugging… is it because we are not yet following certain best practices? Or is it because we did not yet optimize things at all?
Comment #11
tim.plunkettWhen you have a config entity containing a set of configured plugins, the best practices are clear: at runtime and in forms the plugins always know their correct configuration. This means having implementations of
submitConfigurationForm()
doing things like$this->configuration['foo'] = $form_state->getValue('foo');
and then whoever callssubmitConfigurationForm()
would then callgetConfiguration()
and store that.I already did the other part of instantiating the plugins with their config.
The final part would be storing the instantiated plugins so that the build*, validate*, and submit* calls are all to the same instance.
The tricky part here is that it's not a config entity calling to these plugins, it's a plugin itself (CKEditor5.php).
Comment #12
Wim LeersReproduced:
Digging deeper… found the root cause.
(introduced in https://git.drupalcode.org/project/ckeditor5/-/merge_requests/93/diffs?c...)
That is exactly it: it's happening "too late". Because we introduced
\Drupal\ckeditor5\AdminUi::getSubmittedEditorAndFilterFormat()
which operates on$form_state->getValues()
and/or$form_state->getUserInput()
to make the dynamic AJAX form stuff possible at all (at least back then), form validation logic has not yet run, and hence these transformations have not yet run.Note I said "at least back then". Maybe by now we can actually refactor
AdminUi::getSubmittedEditorAndFilterFormat()
away? 🤞🤞🤞But to keep scope manageable here, I'd say we could instead be invoking from within
AdminUi::getSubmittedEditorAndFilterFormat()
each plugin's::validateConfigurationForm()
and::submitConfigurationForm()
. Not pretty, not elegant, but a stepping stone?Actually, why is
::submitConfigurationForm()
not yet implemented? I thought we'd matchEmailAction::submitConfigurationForm()
's pattern I quoted in #2?Comment #13
Wim LeersMet with Tim, discussed this in detail, he's going to push it forward further :)
Updating issue metadata to reflect that.
Comment #14
tim.plunkettI think
AdminUi::getSubmittedEditorAndFilterFormat()
will live to see another day, but I've reduced its usage for now.I'm not really happy with what I came up with.
In HEAD, the $form_state is the canonical place to look for the values (except when
$editor->setSettings()
is called occasionally...)The ideal would be that the plugin config would be the place until the very end, when
$editor->setSettings()
is called to store it all.With this MR as it is, we're kinda in the middle of both.
But, the important thing IMO is that the API is correct, and the processing of that can be resolved later.
Pushed up what I had, I will be around for a bit to fix up small things, but I won't be able to fully pick this up again until Monday.
Comment #15
Wim Leers@tim.plunkett: That looks WONDERFUL! Review posted. Basically only nits.
I'm happy with this going as-is, so: RTBC.
I'm signing off for today. Feel free to merge. If you don't: are you okay with me reviewing and merging this tomorrow, and pushing this to completion to the best of my ability? It's green thanks to you, it's looking waaaaaay better than in HEAD thanks to you, and so I'd love to get it in ASAP :) (Ideally I wake up to this having been merged! :D)
Comment #17
tim.plunkettMerged! Thanks @Wim Leers for all of the help on this.
Comment #18
Wim LeersI did not like that last commit making the new
CKEditor5PluginManager::getPlugin()
public, but that's it. Tracking that in #3228465-7: Final pass over the public API prior to creating core merge request.Actually … I do not like the pre-existing
public function getPlugins()
onCKEditor5PluginManager
either. And I think you just made it easier to solve that. There's a lot of public API on the plugin manager that shouldn't be public API. That's already captured on #3228465 fortunately.In digging into this, I just realized something:
\Drupal\ckeditor5\Plugin\Editor\CKEditor5
is a plugin instance. Since this commit, it now containsBut those are constructed using
… which means
$this->plugins
is now tied to aEditorInterface
config entity: the plugin instances in the text editor plugin instance are coupled to that particular text editor config entity. But the text editor plugin instance is not coupled to a particular text editor config entity. Effectively we've made the Text Editor plugin instance stateful where it stateless before.Creating an issue…
Comment #19
Wim LeersCreated #3228945: CKEditor 5 plugin instances shenanigans: text editor plugin is statefully coupled + plugin instance methods on manager for #18.