Closed (fixed)
Project:
CKEditor 5
Version:
1.0.x-dev
Component:
Code
Priority:
Major
Category:
Task
Assigned:
Unassigned
Reporter:
Created:
4 Mar 2021 at 14:48 UTC
Updated:
20 Jul 2021 at 10:59 UTC
Jump to comment: Most recent, Most recent file
Comments
Comment #2
wim leersThe scary thing is that there is no upgrade/migration path. We physically can't retroactively modify every revision of every
processed_textfield.So it's an at-runtime risk.
Comment #3
tim.plunkettI think this means that without something else in place first, we can't automatically migrate existing text formats from CKE4 to CKE5
Comment #4
wim leersI think this is correct. We need to analyze the
filter_htmlfilter'sallowed_htmlsetting and cross-reference this against the HTML tags supported by CKEditor 5 plugins.Whenever we encounter a particular HTML tag, attribute or attribute value that CKEditor 5 does not support, we cannot automatically enable CKEditor 5.
However, when CKEditor 5 supports a superset of those HTML tags, then it is safe to update — automatically or manually.
Proposed auditing/analysis algorithm
editorconfig entity; if not: DONE, otherwise: CONTINUEeditorconfig entity usesckeditor; if not: DONE, otherwise: CONTINUE\Drupal\filter\FilterFormatInterface::getHtmlRestrictions()return value. Iff it'sFALSEany HTML is allowed, this means this format is (similar to) the infamous text format and we cannot make CKEditor 5 work for this, which means we're DONE. Otherwise: CONTINUEarray_intersect_key($html_restrictions['allowed'], array_flip($cke5_supported_tags))— if the intersection contains fewer tags than$html_restrictions['allowed'], this means CKEditor 5 does not yet support all these tags. WARN the user, and we're DONE.IMHO it's crucial we build this auditing logic soon so that we can ensure the CKEditor 5 infrastructure (i.e. this module) has the necessary level of detail in CKEditor 5 Plugin metadata to allow us to do this auditing at all.
EDIT: the cool thing is that because for CKE5 Plugins we have the necessary metadata in PHP (unlike in CKE4!), we can actually write a drush command that automatically analyzes this! And we can provide a Status Report entry too!
(Now: auditing, future: uneventful manual & automatic updates from CKE4 to CKE5.)
Comment #5
wim leersI referred to this over at #3200008: Validation and editor settings. See https://www.drupal.org/project/ckeditor5/issues/3200008#mr9-note16328.
So let's start implementing #4 by first ensuring that the manual upgrade path is working correctly: when either all HTML is allowed or not enough HTML is allowed, we should disallow using CKEditor 5.
\Drupal\ckeditor5\Plugin\CKEditor5PluginManager::getAllowedElements(). It is only inspecting thefilter_htmlfilter's HTML restrictions. But there can be any number of filters adding HTML restrictions! That's why this should be using\Drupal\filter\Entity\FilterFormat::getHtmlRestrictions()instead.(For example:
\Drupal\filter\Plugin\Filter\FilterHtmlEscapeallows no tags at all — meaning CKEditor cannot possibly work. The proposed change above would detect this.)\Drupal\ckeditor5\AdminUi::validateAllowedElements()has special logic to take overfilter_html. I 100% agree this is the 95% use case, so we should special case it: we should have logic to automatically configure it. But that doesn't mean that's enough. We need to verify that all HTML restrictions originate only from thefilter_htmlfilter. If that's not the case, we'll need to provide a form-level (instead of form element-level) validation warning too, which can and should say which filters are the ones that are conflicting.Pushed an initial commit for this. That already implements bullet 4, and part of #4.
When switching to CKEditor 5:
When switching to CKEditor 5:
Comment #6
wim leers@zrpnr: I've left three
// @todo Test coveragecomments and two// @todo Automatically deduce which filtercomments. Feel free to tackle those if you have the time — I won't have time to work on CKEditor 5 in the next few days.Comment #7
wim leers#5 does not yet handle the case of e.g. starting with , removing
<p> <br>from the list of allowed HTML tags, and then switching from CKEditor 4 to CKEditor 5. The code in https://git.drupalcode.org/issue/ckeditor5-3201637/-/commit/87b36391e47f... uses$form_state->getFormObject()->getEntity(), but it should really be using the submitted values — which includes potential filter settings changes.I run into two problems there WRT Form API:
\Drupal\ckeditor5\AdminUi::getSubmittedEditorAndFilterFormat(). But calling that triggers aexception from
\Drupal\Core\Form\SubformState::getParents(). That is a known core issue: #2798261: Using $form_state->getValue() in BlockBase's blockForm throws "subform and parent form must contain the #parents property" exception.I can work around it by doing
… but that's not ideal of course.
$form_state->values()does not contain thefilterstop-level key even though it was submitted. This is becauseeditor_form_filter_format_form_alter()sets#limit_validation_errors. We still want validation errors to be limited, we just need the current form values … and so … we can get them usingFormState::getUserInput().Both of these are complex/edge-case-y uses of Form API, and I'm hopeful @tim.plunkett can help out with both! 🤞 See https://git.drupalcode.org/issue/ckeditor5-3201637/-/commit/f8bc5f5ade47....
Comment #9
tim.plunkettBecause there was no MR open yet, the d.o/gitlab integration didn't show my single commit:
https://git.drupalcode.org/project/ckeditor5/-/merge_requests/17/diffs?c...
Because we always know that
\Drupal\ckeditor5\Plugin\Editor\CKEditor5::buildConfigurationForm()will have a SubFormState (as it is a plugin form), then we can move the call to getCompleteFormState here, and keep\Drupal\ckeditor5\AdminUi::getSubmittedEditorAndFilterFormat()clean of that workaround.I also don't think the getUserInput() approach is a hack. It seems like a correct usage of the API. Still always good to heavily document that it's not typically called...
One question: as a public method on a service, is there any reason to keep
getSubmittedEditorAndFilterFormat()as static?Comment #10
wim leersWFM and TIL 😄
Yes — it guarantees that this method does not depend on any other inputs than the parameters it is given.
Thanks to #9, both blockers in #7 have been addressed 🥳 That means
is fixed 👍
I've also implemented the logic to identify which filter is the culprit:
Comment #11
wim leersWith the changes I pushed today:
TYPE_MARKUP_LANGUAGEfilters edge case:My next steps are:
\Drupal\ckeditor5\AdminUi::validateAllowedElements()to no longer be tied tofilter_html, which will address #5.2:staticmethods I added to theCKEditor5class out of that class, into a separate one, perhaps calledCKEditor5CompatibilityChecker?#5.3 is out of scope here, #5.4 is already the case since #7 and is now even more solidly the case, #5.5 is specifically being taken into account as you can see in my report. :)
Expect things to really come together in the next few days 🤓
Comment #12
wim leers#11 said addressing #5.2 was next. That happened in 3832a57b128a7274144c73386bbe5bf8824a640a.
I have not yet moved the many static methods out of the
CKEditor5class into a separate class/service.But … I do have unified logic: I am now reusing the same logic for both validating CKEditor 5 within Drupal, and within a newly introduced
drush ckeditor5:auditcommand:Comment #13
wim leersI wrote this in #4, nearly two months ago:
Basically, the code necessary to do this now exists, except for the two last steps (for attributes & attribute values), plus one step I had not yet thought about yet back then: generating a CKEditor 5 toolbar configuration equivalent to the pre-existing CKEditor 4 toolbar configuration. (And the cool thing about that is that we'll also be able to use it in the UI! 😄)
Comment #14
wim leersToo silent too long here. Sorry. Expect progress Monday & Tuesday.
Comment #15
wim leersMy last commit broke the drush output:
obviously a HTML escaping problem.
Fixed in 0308b62.
Comment #16
wim leersThis can only happen once #3215466: Attribute values not accounted for in CKEditor5PluginManager::getProvidedElements is done.
And this is technically already supported, although it is currently not very helpful: it does not yet tell you which specific attributes are a problem. For example:
This is nowhere near specific enough yet. But improving that is out of scope here (otherwise the scope becomes wayy too big).
So … moving on to writing the necessary test coverage. Back when I started working on this, I was hoping others would write that but obviously that is not happening 🤓😅
I do not think that making the actual migration work already makes sense here. For now, the drush command proves that we have the necessary foundations. We still need at a minimum:
Once the test coverage is written here, I'll create follow-up issues for those key aspects (and possibly more).
Comment #17
wim leersCreated separate issue + MR for creating validation constraint infrastructure including tests, which this should be rebased on top of: #3216013: Move validation logic out of form into validation constraints ⇒ use config validation for form, audit command, upgrade path, et cetera.
Comment #18
wim leers#3216013: Move validation logic out of form into validation constraints ⇒ use config validation for form, audit command, upgrade path, et cetera landed! I can continue here. Hopefully I can push this forward again tomorrow. If not tomorrow, then by Tuesday at the latest.
Comment #21
wim leersThe Gitlab UX is really bad/confusing. And deleting stale branches is disallowed. Ah well. 😬
Closed https://git.drupalcode.org/project/ckeditor5/-/merge_requests/17 and opened https://git.drupalcode.org/project/ckeditor5/-/merge_requests/54.
It's rebased on a much more recent version. But more importantly, it takes advantage of the infrastructure that #3216013: Move validation logic out of form into validation constraints ⇒ use config validation for form, audit command, upgrade path, et cetera introduced and takes it further.
Whereas MR17 used to do everything in a way where it was adding more special snowflake methods to
\Drupal\ckeditor5\Plugin\Editor\CKEditor5and\Drupal\ckeditor5\Plugin\CKEditor5PluginManager, we now do the exact opposite: we make them simpler.I kept the entire MR 17 commit history. The first new commit is https://git.drupalcode.org/project/ckeditor5/-/merge_requests/54/diffs?c..., where I lay the foundations.
Key concepts:
ckeditor5.pair.schema.yml\Drupal\ckeditor5\Plugin\Editor\CKEditor5::validatePair()and\Drupal\ckeditor5\Plugin\Validation\Constraint\FundamentalCompatibilityConstraintValidatorare the beating heartHappy to walk anybody through this — Ben perhaps?
There's still lots of clean-up potential, but the architecture is solid, so I'm posting it now.
Total diffstat:
… of which
324 insertions(+), 2 deletions(-)in tests, 45 additions for the config schema, 107 additions for theckeditor5:auditdrush command, and a lot of documentation.After this lands, we can be 100% confident that if
CKEditor5::validatePair()returns no validation constraints, that there are no problems.Once this lands, #3216015: Generate CKEditor 5 configuration based on pre-existing text format configuration will make sense to work on, and we'll be able to use that both in the UI and in the drush command.
Comment #22
wim leersI've posted initial code at #3216015: Generate CKEditor 5 configuration based on pre-existing text format configuration — cool to see all this come together 😊
Comment #25
wim leersRebased on top of #3201641: Improve the HTML filter configuration UX. That was hard 😬 New branch for that, because a complete rebase is A) impossible, B) nonsense — since the code it was changing has been completely changed by #3201641. See https://git.drupalcode.org/project/ckeditor5/-/merge_requests/58
A mostly straight rebase caused the instantaneous warning message about fundamental incompatibilities to not show up 😞 See the screenshots in #10 and #11 and not pictured there, but equally important, this one:
Simply doing adding an early return to
ckeditor5_form_filter_format_form_alter()is enough to make it work again. This is because of the AJAX magic that was added in #3201641: Improve the HTML filter configuration UX. I had to explicitly render messages because of this.(I also managed to add a condition to not make it override the form item but even then it will refuse to render the message.)
On the bright side: the whole crazy work-around in #7 is no longer necessary now :)
Comment #26
wim leersFound the root cause, but not happy with the solution I pushed in 413bf6c2.
This is definitely related to the very complex "dynamically enable AJAX-updating everything in the form in an alter, depending on the value in a dropdown — oh and also change
#limit_validation_errors" 😬.This is also why we still have #3218985: CKEditor 5 admin UI's AJAX logic and form alterations are too complex and brittle and #3218900: Ensure only CKE5 gets the AJAXy live syncing of settings.
I think it'll really pay off to try to sort this out once and for all, to make this easier to reason about. Insofar that is possible, that is.
Comment #27
bnjmnmThe problem at the moment occurs when switching to CKEditor 5 from a new or existing text format when the allowed tags filter is on (likely additional scenarios, but definitely this one). When switching to CKEditor 5, the settings of the prior format do not pass validation (which is essentially a good thing!) but the validation pushes the form into a state that makes it impossible to implement the changes needed to fix the validation errors. This is partially due to the ckeditor 5 admin library not loading (i think that one is relatively addressable).
Another issue that I'm not yet sure how to address is the "allowed tags" field is updated the field itself on non-CK5 formats, while on CK5 it is updated dynamically based on plugins and toolbar buttons. The user could possibly manually update "allowed tags" to pass CK5 validation, but that would require changing it to what CK5 requires when it is first initialized - not the full set of tags a format might require. The mastermind behind this - @Wim Leers is away for a bit, but I have some ideas I'll try out and lets see how it goes...
Comment #28
bnjmnmAfter spending more time with this I think there are two primary issues that need to be addressed:
\Drupal\ckeditor5\Plugin\Editor\CKEditor5::buildConfigurationForm, which only fires if the form is well into the process of switching to CKEditor 5.$form_state->get('editor'is already CKEditor 5 for example, and the form is already in the process of being rebuilt is for CKEDitor 5. Aside from a few potential exceptions (discussed in the next point), the validator needs to happen earlier to prevent any CKEditor 5 switches from happening.I added the 3201637-ben-ui-atop-wim-backend branch. As the branch name suggests, it builds off of what's been provided as of #26, but as a separate branch so it's easier to cherry pick the parts that work as this nears completion.
Comment #29
bnjmnmIt's now possible to switch to CKEditor 5 in the UI once the form is in a state that is compatible with it. However, the text format will not save. There are no visible or logged errors, and my attempts to find the cause in XDebug have not yet surfaced anything.
Steps to reproduce what is happening:
admin/config/content/formats/add<p> <br> <h1> <h2> <h3> <h4> <h5> <h6> <strong> <i>Comment #30
bnjmnmValidation on a new editor , works great! https://youtu.be/_1shIf_M808
Validation when switching to CK5 on an existing editor has an issue, as seen in this video: https://youtu.be/d9cJgIJh9XA
The validator inside CKEditor5::buildConfigurationForm is what identifies the error. It seems to be checking against an editor that isn't representative of the form state, despite debugging confirming that $form_state has filter_html enabled. I did some debugging of
AdminUi::getSubmittedEditorAndFilterFormat(which is what the validator checks against)and it does seem to be returning a$submitted_editorwithout the current filter values. These are the final lines:That
$submitted_formatseems to have the accurate filter config, but the$submitted_editor;doesn't. I'm not 100% sure this is the origin of the problem, but it's the area I'll investigate further when I resume working on this.Comment #31
wim leers#30
1. HOLY SHIT YOUR VIDEO EVEN HAS MUSIC!!!!!! 😄
2. Wow, that is an epic step forward!
3. But wouldn't it be simpler to reset the value to automatically if CKEditor 5 is incompatible?
1. HOLY SHIT AGAIN MUSIC!!!!!! 😄
2. But wouldn't it be simpler to reset the value to the previous text editor (if any) if CKEditor 5 is incompatible?
3. I cannot reproduce the behavior in the screencast: if I try to switch from CKEditor 4 to 5, and then enablefilter_html(like you do in the screencast around 0:12) I do not get the button on the filter settings. EDIT: aha! This only works in Seven right now, in Claro it fails. I found the root cause: the AJAX request is still sending the old value forfilters[filter_html][settings][allowed_html]… but then again it appears the button does not actually do anything? I'm on commit 79ac82bb30c2b42bc1b4b8358347265630b4febf, which is the latest pushed commit.4. D'oh, I failed to notice that one is supposed to copy/paste the allowed tags and then click the button 🙈😅😂 — I got spoiled by the luxury of #3201641: Improve the HTML filter configuration UX! When I do that, I can reproduce the problem.
ckeditor5_validate_switching_from_different_editor()now indeed no longer has any validation errors — this is what you expect. But then when\Drupal\ckeditor5\Plugin\Editor\CKEditor5::buildConfigurationForm()finally runs (now that the fundamental compatibility validation is passing). Turns out the constructed text format is again the original text format, because the submitted filter values are ignored … because$switching_to_ck5is incorrectlyFALSE. Trying to work on a work-around…P.S.: the tags that the "Apply changes to allowed tags" button is adding/setting is not the minimum, but it is the set of tags that will be generated for
\Drupal\ckeditor5\Plugin\Editor\CKEditor5::getDefaultSettings(). #3216015: Generate CKEditor 5 configuration based on pre-existing text format configuration will improve that 😊 Therefore, I'm fine keeping this as-is. EDIT: oh and I even see this is just hard-coded for now — perfect!Comment #32
bnjmnm. I attempted to do this without success. Unfortunately, I already don't recall the specifics of how I was attempting to do it, and what the problems I ran into were (long week!). I think this would provide clearer UX if it's possible.
Comment #33
wim leersSo this is once again caused by the super tricky
$form_state->getUserInput()stuff 😬 (See #7, #9, #10.)I haven't been able to figure out a work-around yet 😔
@bnjmnm, wanna pair tomorrow? 🤓
Comment #34
bnjmnm@Wim Leers and I reviewed each others changes and all threads that came out of that co-review are resolved. I'm going to hold off on committing until I've at least had a moment away from this. I'd be comfortable committing Wim's part of the work, but some of the changes I just made are substantial enough (the mutation observer and changes to
getSubmittedEditorAndFilterFormatin particular) that I don't want to casually commit immediately after writing it.Comment #36
wim leers#34: I checked it in detail today. I can't find any problems with any of the changes you made. I went ahead and merged it 🤓
(I did some minor refactoring of the "root violation" checking: rather than duplicating logic, I added a new optional parameter to
\Drupal\ckeditor5\Plugin\Editor\CKEditor5::validatePair(). This reduces brittleness. In doing so I first caused a subtle regression and then spotted it — and in stumbling there I've accidentally proven that the tests are really solid 😊)diffstat: 1350 additions, 109 removals. 354 are in the validation constraint, 24 for the schema, 77 are providing infrastructure in the
CKEditor5plugin to use that validation constraint and 413 are in the test coverage for that validation constraint. That adds up to 868 of the 1350 additions. Another 107 for the drush command. Together that adds up to 72% of the new LoC. In doing so, @bnjmnm found half a dozen or so bugs in the pre-existing UI+test coverage that the validation constraints helped uncover. Overall, we agreed on Friday that this issue/merge request was more than worth it: it already helped stabilize the code base! (I admit I was pleasantly surprised that so many problems + improvements in the pre-existing test coverage were found! 😄)