Closed (fixed)
Project:
Drupal core
Version:
8.0.x-dev
Component:
ckeditor.module
Priority:
Minor
Category:
Bug report
Assigned:
Unassigned
Issue tags:
Reporter:
Created:
20 Feb 2016 at 19:56 UTC
Updated:
21 Mar 2016 at 15:14 UTC
Jump to comment: Most recent, Most recent file
Comments
Comment #2
birk commentedI've included a patch to the CKEditorPluginManager class, that fixes this.
Comment #3
kurund commentedComment #4
kurund commentedI have reviewed the above patch and I feel it might have some potential issue. I am attaching an improved patch that fixes the problem.
Comment #5
birk commentedThe problem with patch #4 is the vertical tab is still added to the "CKEditor plugin settings" tab group, thats why the check (in #2) is placed before anything is added to the
$form.We should also add the form to a variable, instead of calling a potentially performance intensive
settingsForm()more than once.I get the
empty()idea instead of making an explicit notFALSEcheck. But if the code should follow the documentation an empty array should still show the vertical tab, andFALSEnot display anything at all. Although I have a hard time thinking of a usecase, where one would like an empty vertical tab, unless something is handled with JavaScript client side time.Comment #6
wim leers#1890502-53: WYSIWYG: Add CKEditor module to core introduced that:
… but I'm not sure it ever actually made sense. It slipped through the cracks, IMO.
IMO this is a bug in the API documentation, not a bug in the API. Unless you can describe a convincing use case.
Comment #7
birk commentedI'm making a module where I have a derived plugin that implements
CKEditorPluginConfigurableInterface, not every derivative actually has settings (some has conditional settings), but I don't want an empty vertical tab for every plugin with no settings.However returning an empty array should be sufficient, instead of explicitly returning
FALSE.I've updated the previous patch so it checks
!empty()instead of explicitFALSE, and removed the ", or FALSE if there is none" part of the documentation.Comment #8
wim leersHm… interesting! Can't you make two derivers, one for the configuration-less plugins and one for the plugins with configuration?
That'd feel much cleaner. Because if some CKEditor plugins aren't configurable, they shouldn't implement
CKEditorPluginConfigurableInterface.Note that the documentation of that interface says this:
i.e. this interface specifically signals that a CKEditor plugin can define a settings form.
Comment #9
birk commentedI thought about making more than one deriver, but I would have to make a deriver for all 3 (not counting
\Drupal\ckeditor\CKEditorPluginCssInterface) CKEditor Plugin interfaces, and for each combination as well, so 1 forButtons, 1 forConfigurable, 1 forButtons&Configurable, ending up with 7 classes. Not all of the combinations seems likely right now, but theoretically.That would be doable, but tedious, and it would explode as (if) more Interfaces are added.
The real problem (in my use case) is when a plugin's settings is depended on what other plugins is available and what settings the other plugins have.
So if a plugin has settings that are all conditional, but none of the conditions are met this plugin will show an empty vertical tab.
I haven't encountered this yet, but I can imagine it happening if conditional settings are used, even in Plugins that are not derived.
Comment #10
wim leersCan you tell a bit about what you're doing? Because needing derivers for all those combinations is indeed a pain. But on the other hand it's also difficult to imagine why you'd even need to do that. Having concrete examples to look at would make that a lot easier to understand. Especially this part is difficult to understand/imagine:
You also mention
Buttons, so do you also have examples of where you are implementingCKEditorPluginButtonsInterfacebut the CKEditor plugin does not have any buttons?Finally: note that the whole point of having all these interfaces is to allow for composition. What you're describing explicitly goes against composition.
Don't get me wrong, I totally want to accommodate you, but I also have to think about long-term maintainability and code/API understandability. I first want to properly understand your use case.
Comment #11
birk commentedOne concept I'm working on is mapping CKEditor plugins (from http://ckeditor.com/addons/plugins/all), so they easily can be added to the formats configuration page, and managed there.
I'm working with a single class that implements all 3 interfaces, and depending on the mapping I add a button, enable the plugin and add appropriate settings.
Not adding a button (by returning an empty array) works fine, but not adding a settingsForm by returning either an empty array or FALSE didn't work (FALSE resulted in an error and an empty array resulted in an empty vertical tab).
Verbalising this, and thinking more about my scenario, I'm able to fix it with the current version. I could simply make two classes one that implements
CKEditorPluginContextualInterfaceandCKEditorPluginButtonsInterfaceand one that implements all 3, and then I would be able to solve my current use case - where I don't work with conditional settings.Besides changing the documentation, it still seems (to me) more graceful to handle an empty array from the
settingsForm(), than adding an empty vertical tab, so the behavior was more similar to an emptygetButtons().But I see the difference of opinion and I'm not feeling as strongly about the empty vertical tab issue as when I created the thread.
Comment #12
wim leersWow! Did you talk to the CKEditor team? They were interested in doing exactly that some time ago, but I guess they got caught up in more urgent things in the mean time.
Like I said in #10, this means those interfaces being implemented by a certain plugin becomes meaningless. That is the big problem here.
CKEditorPluginManagerdoes this:And that could indeed conceivably be worked around by implementing those interfaces in a particular way. But note that that then just happens to work for
CKEditorPluginManager— there totally could be other code using those interfaces, and perhaps you'd break those cases. I can see that risk being low though.But, what about this code from
CKEditorPluginManager:How can you possibly work around that? AFAICT this edge case must already be broken in your current code?
It's not really about difference of opinion. It's that implementing interfaces that are actually irrelevant/unused by a particular CKEditor plugin leads to bugs and unmaintainability elsewhere.
I could absolutely accept this change to accommodate this use case (assuming core committers would agree, which I doubt), but then I'd be breaking the general principle/pattern/logic here. For a single use case.
I see your point/pain. I think this is D7-style-thinking, and that's fine in and of itself, but I think it's more appropriate to stick with the interface composition pattern
Also, you're doing something very advanced.
However, I have a good answer for you :) You can create one class for each combination and automatically select the appropriate one. See
\Drupal\Core\Entity\Plugin\Derivative\DefaultSelectionDeriver::getDerivativeDefinitions()for a very clear example. I think/guess you may still somewhat dislike that, but let's be honest: this is a super advanced use case, where you're essentially auto-generating (Drupal) plugins for many, many CKEditor plugins! So I think that's only fair.Other examples:
\Drupal\config_translation\Plugin\Derivative\ConfigTranslationContextualLinks::getDerivativeDefinitions()\Drupal\config_translation\Plugin\Derivative\ConfigTranslationLocalTasks::getDerivativeDefinitions()\Drupal\views\Plugin\Derivative\DefaultWizardDeriver::getDerivativeDefinitions()\Drupal\views\Plugin\Derivative\ViewsEntityArgumentValidator::getDerivativeDefinitions()Comment #13
birk commentedAhh, I see your point about interfaces becoming meaningless — how ever I still believe the
CKEditorPluginManagercould handle empty arrays differently. But I also understand the scope of catering for every single scenario is to big.On a side note, thank you for the examples, I'll make the different combinations as I need them, then properly use the same deriver base, to select the different classes. And yes the whole approach is most likely very D7 :) as this module is my first encounter with D8 and sort of my own Hello World, to familiarize myself.
But to get back to the issue, the only actual code change that needs to be done is removing the FALSE part of the documentation?
Comment #14
wim leersAn ambitious first encounter! :D :)
Yes, indeed.
Please keep me posted, feel free to ping me on IRC. I want to help you succeed, and I want to point to your module from the documentation at https://www.drupal.org/developing/api/8/ckeditor (being the most advanced use of the CKEditor module's plugins possible) and https://www.drupal.org/developing/api/8/plugins (being a good concrete example of a very advanced use case of the Plugin API.
Comment #15
birk commentedSo, to wrap this up I've uploaded a patch that simply removes the FALSE statement from the docblock.
Comment #16
wim leersThanks!
Comment #17
alexpottCommitted 01f0ab0 and pushed to 8.1.x and 8.2.x. Thanks!
I did not commit to 8.0.x because whilst this is only a documentation change - it is an interface return value change.
Comment #20
wim leers#17: But it doesn't work in 8.0.x either. It's a bug in the documentation. So I think it'd be better to also commit this to 8.0.x.
Comment #22
alexpott@Wim Leers okay - makes sense. Committed 2ea8c26 and pushed to 8.0.x. Thanks!
Comment #23
wim leersThanks :)