Problem/Motivation

Any WYSIWYG editor that supports "true WYSIWYG"/"inline editing" should automatically be available for Edit's in-place editing. WYSIWYG editors shouldn't have to implement a plugin for both editor.module and edit.module.

In other words: this adds edit.module support to editor.module.

Note: this issue builds on #1833716: WYSIWYG: Introduce "Text editors" as part of filter format configuration, and assumes that Editor.module (a very "light" version of the WYSIWYG API module in Drupal 5/6/7) will be in Drupal 8 core. Patches in this issue are relative to patches in that issue.

Proposed resolution

From #1833716-68: WYSIWYG: Introduce "Text editors" as part of filter format configuration:

Next thing I'm working on (and have started already): figuring out how and where I can simplify Edit module (#1824500: In-place editing for Fields) by making it leverage as much as possible from Editor module:

  • The CKEditor implementation for Edit already is leveraging Editor's metadata (see #1873500-1: CKEditor + Edit).
  • ProcessedTextEditorInterface::checkFormatCompatibility() can go away, since that is now something that can happen while configuring/associating a text editor with a text format.
  • Assuming we can add optional metadata to plug-ins implementing EditorInterface and maybe an optional method, we can have all the necessary "true WYSIWYG" text editor metadata in plug-ins written for Editor.
  • ProcessedTextEditorInterface::addJsSettings() can be replaced by a few lines of code in Edit.module plus leveraging EditorInterface::generateJsSettings()
  • Due to all of the above, we should be able to drop Edit's ProcessedTextEditorInterface entirely.

The heavy lifting (all the ProcessedTextEditorInterface stuff) to make this a reality needed to happen on the Edit.module side of things, and that's currently done, RTBC, it only needs to be committed: #1874936: Pluggable in-place editors (allow modules to define new Create.js PropertyEditor widgets).

Initial (rough, coupled) version of this patch were posted at #1833716-80: WYSIWYG: Introduce "Text editors" as part of filter format configuration and a very comparable patch was posted at #1833716-93: WYSIWYG: Introduce "Text editors" as part of filter format configuration. It was then decided to leave out this functionality of that patch, to keep the effort focused there. The patch posted here is just a cleaned up version of the latter patch, against the latest version of the patch in #1833716: WYSIWYG: Introduce "Text editors" as part of filter format configuration.

Tests are included.

Remaining tasks

User interface changes

None.

API changes

This introduces a few API changes to editor.module plugins, all of which are optional — they're only necessary if you want your editor.module plugin to also be available for in-place editing through edit.module:

  • Update the annotations of the editor.module plugin with supports_true_wysiwyg = TRUE
  • Drupal.editors.editorName.onChange()
  • Drupal.editors.editorName.attachTrueWysiwyg() (better name suggestions are of course welcome)
Support from Acquia helps fund testing for Drupal Acquia logo

Comments

Wim Leers’s picture

Issue summary: View changes

Updated issue summary.

Wim Leers’s picture

Assigned: Unassigned » Wim Leers
Status: Active » Needs review
FileSize
17.3 KB

Note that the patch is marked as do-not-test because it's a patch that depends on an uncommitted patch, so tests would evidently fail.

Also note that drupalwysiwygwidget.js' code will be cleaned up; it currently effectively (or rather: painfully) demonstrates how Edit's JS codebase is currently leaking implementation details, forcing Create.js PropertyEditor widgets to do things that it should not ever have to worry about. That will be addressed in other issues though.

EDIT: forgot to mention, this patch is relative to #1833716-144: WYSIWYG: Introduce "Text editors" as part of filter format configuration.

Wim Leers’s picture

#1874936: Pluggable in-place editors (allow modules to define new Create.js PropertyEditor widgets) and #1833716: WYSIWYG: Introduce "Text editors" as part of filter format configuration have been committed. We could commit this now. Note that I can't move this to the "editor.module" component because for some reason that component has not yet appeared.

But probably it's better to hold off on working on this until CKEditor.module has landed — see #1878344: Add CKEditor JS library to core.

Attached is an identical patch to the one in #1, but with a different filename so that it can be tested. It's now possible to let testbot do its thing because both #1874936: Pluggable in-place editors (allow modules to define new Create.js PropertyEditor widgets) and #1833716: WYSIWYG: Introduce "Text editors" as part of filter format configuration have been committed. Checking out a fresh Drupal 8 install with this patch applied still causes all tests to pass, including the ones added by the patch.

Wim Leers’s picture

Also: Spark 8.x-1.x includes this patch plus related patches, to show you Editor.module-powered CKEditor, for both back-end (form) editing and front-end (in-place/inline/"true WYSIWYG") editing. Set up a quick demo for yourself over at http://simplytest.me/project/spark/8.x-1.x.

nod_’s picture

Note that on D7 this approach and js works well. It's nice to work with. Will do a more proper review if I get some time this week.

quicksketch’s picture

I posted or read these comments in other issues, so I'll repost them here (mostly around terminology):

- The name "drupalwysiwygwidget.js" seems exceedingly odd for Druapal file name. We never smash everything all together like this. Is this some precedent set by Create.js?

- I've said this in other places, but "attachTrueWywisyg" and "supports_true_wysiwyg" terminology I still find poorly named. Pretty much every time you say it, then it's immediately followed by an explanation of supporting in-place editing. In the code itself, always puts the phrase in quotes, "true WYSIWYG". So if we always explain it or it needs quotes around it, why don't we just use the explanation directly. Although "in-place editing is more accurate, "inline editing" is a common phrase, used by CKEditor and Aloha (and other projects). And it avoids awkward camel casing (inPlaceEditing, inplaceEditing?) Could we name these "attachInlineEditor" and "supports_inline_editor"? (The naming of things in a broader sense is also being discussed in #1874640: Rename edit module to quickedit).

sun’s picture

Title: Automatically make any WYSIWYG editor that supports "true WYSIWYG"/"inline editing" available in Edit's in-place editing » Make WYSIWYG editors available for in-place editing
Component: edit.module » editor.module
+++ b/core/modules/editor/editor.module
@@ -78,6 +78,20 @@ function editor_library_info() {
+  $libraries['edit.editor.editor'] = array(
...
+      $path . '/js/createjs/drupalwysiwygwidget.js' => array(

Any chance we can get back to sane filenames that are delimited by dots or so, and even more importantly, within the module namespace? :)

I hope it's not a CreateJS limitation or something?

The filename should start with "editor." (the namespace of the module that supplies it), and followed by whatever is in there; multiple terms can be separated by dots or hyphens.

2) I also don't quite get the library name... ;)

+++ b/core/modules/editor/js/createjs/drupalwysiwygwidget.js
@@ -0,0 +1,158 @@
+    /**
+     * Removes validation errors' markup changes, if any.
+     *
+     * Note: this only needs to happen for type=direct, because for type=direct,
+     * the property DOM element itself is modified; this is not the case for
+     * type=form.
+     */
+    _removeValidationErrors: function() {
+      this.element
+        .removeClass('edit-validation-error')
+        .next('.edit-validation-errors').remove();
+    },

The following applies not only to this code snippet, but in general to the code:

1) The "Note:" prefix is superfluous in almost all of the doc block descriptions.

2) The description refers to a "type", but there is no type involved in this function anywhere. Thus, anyone who's looking at this is left baffled. :)

3) In general, the code appears to be documented well, but when actually reading the docs, one has to realize that single aspects are just documented in a "wordy" way, and some aspects are about very detailed technical issues that are lacking context for someone else to make sense of. ;)

I wanted to provide a replacement example for this particular one, but I'm actually not familiar enough with the code base and APIs to know what this was trying to communicate for realz.

+++ b/core/modules/editor/lib/Drupal/editor/Plugin/edit/editor/Editor.php
@@ -0,0 +1,100 @@
+ * Definition of \Drupal\editor\Plugin\edit\editor\Editor.

1) Should be "Contains".

2) The separation between edit and editor is a bit weird... Did we already consider to merge the two modules?

+++ b/core/modules/editor/lib/Drupal/editor/Plugin/edit/editor/Editor.php
@@ -0,0 +1,100 @@
+ *   alternativeTo = {"direct"},

The alternativeTo declaration looks a bit odd to me. It sounds like it's trying to categorize Edit\editor plugins in a way that they're exclusive to each other when appearing in configuration. However, that kind of business logic shouldn't be part of the meta data.

Can we change the property into just "category" or "categories"?

Plural is better, if multiple values need to be supported — although I'm not sure I understand how an edit\editor plugin can apply to multiple in-place editing types.

+++ b/core/modules/editor/lib/Drupal/editor/Plugin/edit/editor/Editor.php
@@ -0,0 +1,100 @@
+      $format_id = $items[0]['format'];
+      if (isset($format_id) && $editor = editor_load($format_id)) {

This looks odd — normally you'd check isset() before accessing an array key, not after.

+++ b/core/modules/editor/lib/Drupal/editor/Plugin/edit/editor/Editor.php
@@ -0,0 +1,100 @@
+      if ($editor && isset($definitions[$editor->editor]) && isset($definitions[$editor->editor]['supports_true_wysiwyg']) && $definitions[$editor->editor]['supports_true_wysiwyg'] === TRUE) {

The "supports_true_wysiwyg" property name appears like a magic flag and doesn't really make sense to me. Can we choose a better and more concise name?

Given this issue title, which contains "true WYSIWYG", "inline editing", and "in-place editing", I've the impression we still didn't find the right terminology...

Btw, since editors can support and not support various things, it would probably make sense to replace that single string property into a "supports" array/hashmap definition; e.g.,:

supports:
  wysiwyg: true

[oh why oh why can't we use YAML in annotations... :P]

+++ b/core/modules/editor/tests/modules/lib/Drupal/editor_test/Plugin/editor/editor/UnicornEditor.php
@@ -18,7 +18,8 @@
+ *   supports_true_wysiwyg = TRUE

Unless I'm mistaken, TRUE must be lowercase in annotations.

Wim Leers’s picture

#5: Sorry for not incorporating that feedback just yet :( You (rightfully) wanted to exclude this functionality from the Editor.module patch, so I did, but in the process I forgot to take your feedback into account.
- File rename done. This naming scheme was chosen by frega+henri.bergius while they were refactoring things; we figured that the filename was far less important than working code :)
- Naming suggestions: implemented (also taking #6 into account).

#6:
RE: naming: already taken care of above. The library name has been slightly modified and the reasoning that I was using is now explained in the comment above.

RE: _removeValidationErrors:
1) Noted. (pun intended :P) — fact is that this should go away entirely. See what I said in #1:

Also note that drupalwysiwygwidget.js' code will be cleaned up; it currently effectively (or rather: painfully) demonstrates how Edit's JS codebase is currently leaking implementation details, forcing Create.js PropertyEditor widgets to do things that it should not ever have to worry about. That will be addressed in other issues though.

2) Before Edit moved to Create.js, there were different "types" of in-place editing, now there are different in-place "editors". This was just a remnant.
3) Agreed, see point 1 as to why that is.

I wanted to provide a replacement example for this particular one, but I'm actually not familiar enough with the code base and APIs to know what this was trying to communicate for realz.

Eh… I think you're saying you wanted to try to provide similar integration with the Edit module from the WYSIWYG API module?

RE: \Drupal\editor\Plugin\edit\editor\Editor
1) Fixed.
2) No, merging the Edit and Editor modules does not make any sense. The Editor module provides text format <-> text editor bindings. The Edit module provides field <-> in-place editing bindings. They're completely separate things. What this patch does, is this: instead of forcing "rich text fields" (aka "processed text fields" or "filtered text fields") to use form-based Field API Widgets, allowing them to use a text editor on the text field directly, without any form, IF the text editors supports inline editing.
The Editor module is the general repository of all text editors, so it makes sense that instead of forcing each module that provides a text editor to implement an in-place editor (\Drupal\edit\Plugin\EditorInterface) plugin, they can simply implement two optional methods, set a flag in their text editor (\Drupal\editor\Plugin\EditorInterface) plugin and be done with it.

RE: alternativeTo
This allows an in-place editor to describe itself as an alternative to another in-place editor. E.g. to support a use case that another in-place editor does not support. Note that this is not new functionality being introduced here, this was introduced at #1874936: Pluggable in-place editors (allow modules to define new Create.js PropertyEditor widgets). For a detailed explanation/rationale, see #1874936-15: Pluggable in-place editors (allow modules to define new Create.js PropertyEditor widgets).13.1, second bullet.

This looks odd — normally you'd check isset() before accessing an array key, not after.

True, but that's because the filter module/field modules set it this way. $items[0]['format']; is always set, but it might contain NULL instead of a format ID, because the user still must choose a text format — this happens whenever creating new content.

RE: "supports_true_wysiwyg"
I implemented quicksketch's suggestion.

(And yes, the plugin annotations syntax is horrible!)

I verified it, and TRUE both works fine and is used elsewhere in core, see e.g. BlockPluginUI

Wim Leers’s picture

Forgot to update one spot that caused the JS lib to not load.

Wim Leers’s picture

Issue tags: +CKEditor in core

Tagging; this helps the CKEditor folks track the "CKEditor in core" status on the Drupal side. On the CKEditor side, see http://dev.ckeditor.com/query?keywords=~Drupal.

sun’s picture

Issue tags: -wysiwyg, -Spark, -CKEditor in core

#8: editor_true_wysiwyg-1886566-8.patch queued for re-testing.

Status: Needs review » Needs work
Issue tags: +wysiwyg, +Spark, +CKEditor in core

The last submitted patch, editor_true_wysiwyg-1886566-8.patch, failed testing.

mgifford’s picture

Issue tags: +Accessibility

There are going to be accessibility implications here. Really looking forward to it though as it's going to be a game changer.

Wim Leers’s picture

Status: Needs work » Needs review
Issue tags: -wysiwyg, -Accessibility, -Spark, -CKEditor in core

#8: editor_true_wysiwyg-1886566-8.patch queued for re-testing.

Status: Needs review » Needs work
Issue tags: +wysiwyg, +Accessibility, +Spark, +CKEditor in core

The last submitted patch, editor_true_wysiwyg-1886566-8.patch, failed testing.

Wim Leers’s picture

Status: Needs work » Needs review
FileSize
1.81 KB
16.77 KB

Reroll for text format as config entities (#1779026: Convert Text Formats to Configuration System), which includes … extremely weird, non-trivial set-up requirements to get the Filter module to work in DUTB tests, which I think/hope are transitional.

Wim Leers’s picture

#12:

Regarding a11y, I think that doesn't really belong in this issue; this is about making it technically possible. I'm not sure it makes sense to test the a11y for that here, because 1) we don't have CKEditor in core yet, 2) it may well require changes in the WYSIWYG editor, not in the code being introduced here.

All that being said, you can test this right now in Spark D8 — set up a quick demo at http://simplytest.me/project/spark/8.x-1.x, go to node/1 or node/2, enable Edit mode, edit a body field in place. Feedback is of course most welcome :)

mgifford’s picture

Issue tags: -Accessibility

Ok, that's a good distinction. Dropping the tag.

Gábor Hojtsy’s picture

Wim Leers’s picture

Priority: Normal » Major
effulgentsia’s picture

Status: Needs review » Needs work

It makes sense that instead of forcing each module that provides a text editor to implement an in-place editor (\Drupal\edit\Plugin\EditorInterface) plugin, they can simply implement two optional methods, set a flag in their text editor (\Drupal\editor\Plugin\EditorInterface) plugin and be done with it.

I totally agree with this. But I don't think it makes sense for this JavaScript code and the new Editor plugin to live in editor.module. Instead, I think it should live in edit.module, which already has JS editing widgets and PHP plugins for the 'form' and 'direct' editors, so this would be a natural addition of a 3rd editor/widget there.

This suggestion will result in an unfortunately named plugin class of Drupal\edit\Plugin\edit\editor\EditorEditor, but I suggest that we accept that as a temporary measure while we figure out how to adjust our terminology. If I understand it right, the issue is that Create.js uses the terms 'editor' and 'widget' to mean certain things, and meanwhile, Drupal uses both of these terms to mean something else, so when we want to say "a Drupal text editor acting as a Create.js editing widget", we end up with EditorEditor until we resolve the overlap.

Wim Leers’s picture

Status: Needs work » Needs review

I'm not yet convinced. Why should edit.module contain code that would never make sense without editor.module? Why should edit.module ship code that logically belongs in editor.module, which would introduce a implicit circular dependency (though a weak one, fortunately)?

effulgentsia’s picture

Why should edit.module ship code that logically belongs in editor.module

Why do you consider integration between edit.module and editor.module to logically belong in editor.module rather than edit.module? edit.module already has a Direct editor, so it knows about and integrates with Drupal's filter system. editor.module is Drupal core's official way for enhancing Drupal's filter system with a client-side editor. Why shouldn't edit.module make direct use of this? Meanwhile, editor.module currently has no knowledge of field.module, but #15 would add it.

which would introduce a implicit circular dependency

Where would there be a circular dependency?

Wim Leers’s picture

edit.module makes it possible to do in-place editing. It makes it also possible for modules to introduce custom in-place editors. There are only two possible simple cases: contentEditable and form-based in-place editing. Everything else requires "JavaScript magic".
The Direct editor == contentEditable, and it can only be used when a piece of text does *not* use the filter system!
Your point about introducing field.module knowledge into editor.module is correct, of course.

To support in-place WYSIWYG editing, in-depth knowledge of the WYSIWYG editor is required. Hence that doesn't really belong in edit.module, but in editor.module.
Let me explain in more detail. The "WYSIWYG in-place editor" *must* know about editor.module's plugin structure. Combine that with the fact that the "WYSIWYG in-place editor" can only work if editor.module is enabled. Together that implies that if edit.module were to provide the "WYSIWYG in-place editor", then it would have to have a dependency on editor.module. But we can't force edit.module users to also enable editor.module… so it'd be an "enhanced by" instead of "requires" relationship, which AFAIK we cannot and don't want to have in core.

effulgentsia’s picture

The Direct editor == contentEditable, and it can only be used when a piece of text does *not* use the filter system!

In that case, would it make sense to move everything related to edit.module's filter system knowledge to editor.module? I'm thinking things like FieldRenderedWithoutTransformationFiltersCommand, the edit_text route and controller, and the Drupal.edit.util.loadRerenderedProcessedText() function. If yes, then that would make sense to me. Otherwise, I'm not clear on the current split between edit.module and editor.module responsibilities in regards to the filter system.

(sorry for being a pain on this)

Wim Leers’s picture

#24:

You're not at all being a pain; you're rightfully challenging why I want to do it this way. You're absolutely right that all filter system-related things should be moved from edit.module to editor.module. In fact, I've wanting to do this, but forgot about it; so thanks for setting me straight here!

I did that in this reroll.

Wim Leers’s picture

Note that combined with #1873500-17: CKEditor + Edit, you can see it in action. Spark 8.x-1.x includes both; test it at http://simplytest.me/project/spark/8.x-1.x.

(Maybe it makes sense to merge that patch into this patch, to be able to better consider/review the whole?)

effulgentsia’s picture

Per #1873500-22: CKEditor + Edit, yes I do think that issue should be merged into this one. I think they make more sense together than separate, and aren't too big a review burden when combined.

sun’s picture

Status: Needs review » Needs work
+++ b/core/modules/editor/editor.module
@@ -78,11 +78,48 @@ function editor_library_info() {
+  // Create.js PropertyEditor widget library names begin with "edit.editor".
+  $libraries['edit.editor.wysiwyg'] = array(

Tohrröööö!

Hey, according to this inline comment, "wysiwyg" is going to be our contrib namespace, right? :)

+++ b/core/modules/editor/editor.module
@@ -78,11 +78,48 @@ function editor_library_info() {
 /**
+ * Implements hook_custom_theme().
+ *
+ * @todo Add an event subscriber to the Ajax system to automatically set the
+ *   base page theme for all Ajax requests, and then remove this one off.
+ */
+function editor_custom_theme() {
+  if (substr(current_path(), 0, 7) === 'editor/') {
+    return ajax_base_page_theme();
+  }
+}

If I get this right, then this force-resets the theme back to the front-end theme to prevent the admin theme from kicking in?

In-place editing with a WYSIWYG editor definitely needs to happen in the front-end theme.

So I suspect that this is what happens here? (A comment would be useful.)

That said, the entire front/admin theme drama became so utterly complex in the meantime that I won't stop to call for its complete removal without replacement. Was an inane idea in the first place.

+++ b/core/modules/editor/js/editor.createjs.js
@@ -0,0 +1,187 @@
+ * Depends on Editor.module. Works with any (WYSIWYG) editor that implements the
+ * attachInlineEditor(), detach() and onChange() methods.

Outdated.

Could also use some clarification with regard to on which JS "class" the stated methods are found.

+++ b/core/modules/editor/js/editor.createjs.js
@@ -0,0 +1,187 @@
+      return { padding: true, unifiedToolbar: true, fullWidthToolbar: true };

Can we transform these into multi-line syntax, plz?

+++ b/core/modules/editor/js/editor.createjs.js
@@ -0,0 +1,187 @@
+     * @todo: POSTPONED_ON(Create.js, https://github.com/bergie/create/issues/142)
+     * Get rid of this once that issue is solved.

Weird syntax.

@todo Remove after removal of...
@see http:...

^^

+++ b/core/modules/editor/js/editor.createjs.js
@@ -0,0 +1,187 @@
+      var that = this;

In all cases like in this code, "self" is much more appropriate than "that".

"that" is commonly used when referring to parent objects of nested closures. And, "that" is most often a poor programming pattern to being with; the variable name can be self-descriptive instead. In short, "that" is an artifact of poor coding style and programming habits; it can always be replaced with something better.

"self" is appropriate when referring to class-alike JS objects. +/- in the same way you'd refer to self:: in PHP, which always means the current class.

self is the case here.

+++ b/core/modules/editor/js/editor.createjs.js
@@ -0,0 +1,187 @@
+     * Makes this PropertyEditor widget react to state changes.

"Reacts to state changes."

That said, where are the parameters from and to documented?

If there is an "interface" for this handler, then we should at least clarify so. Either by @see, or by using a terminology similar to "Implements Foo.Bar.Baz."

Looking further through this code, it looks and feels weird that both variables are containing strings... (instead of events) Is that something under our control?

+++ b/core/modules/editor/js/editor.createjs.js
@@ -0,0 +1,187 @@
+        case 'inactive':
+          break;
+        case 'candidate':
...
+          break;
+        case 'highlighted':
+          break;
+        case 'activating':

Like PHP, JS supports to "fall through" from one case to another case in switch control structures.

There should be a blank line between the effective switch cases, so as to quickly and inherently make clear, which case is falling through to another case.

+++ b/core/modules/editor/js/editor.createjs.js
@@ -0,0 +1,187 @@
+            if (from !== 'highlighted') {
+              this.element.attr('contentEditable', 'false');
+              this.textEditor.detach(this.element.get(0), this.textFormat);
+            }

What is happening here, and why? (could use a leading inline comment upfront)

+++ b/core/modules/editor/js/editor.createjs.js
@@ -0,0 +1,187 @@
+        case 'active':
+          this.element.attr('contentEditable', 'true');
+          this.textEditor.attachInlineEditor(

Why do we need to set the contentEditable attribute manually...?

+++ b/core/modules/editor/js/editor.createjs.js
@@ -0,0 +1,187 @@
+    /**
+     * Removes validation errors' markup changes, if any.
+     *
+     * @todo: this should not be necessary, will be obviated by edit.module.
+     */
+    _removeValidationErrors: function () {
+      this.element
+        .removeClass('edit-validation-error')
+        .next('.edit-validation-errors').remove();
+    },
...
+    /**
+     * Cleans up after the widget has been saved.
+     *
+     * @todo: this should not be necessary, will be obviated by edit.module.
+     */
+    _cleanUp: function () {
+      Drupal.edit.util.form.unajaxifySaving(jQuery('#edit_backstage form .edit-form-submit'));
+      jQuery('#edit_backstage form').remove();
+    },

Obsolete?

Alternatively, where is the core follow-up issue for that? Both @todos should be replaced by:

@todo D8: Remove this.
@see http:...
+++ b/core/modules/editor/js/editor.createjs.js
@@ -0,0 +1,187 @@
+     * Loads rerendered processed text for a given property.
...
+    _loadRerenderedProcessedText: function (options) {

One of these two words is too much: "rerendered" or "processed".

Something that is rerendered is processed already. Something that is processed may not be rendered yet.

But both together are a needless stack of adjectives.

+++ b/core/modules/editor/js/editor.createjs.js
@@ -0,0 +1,187 @@
+     * Leverages Drupal.ajax' ability to have scoped (per-instance) command
+     * implementations to be able to call a callback.

I can only remotely imagine what this comment tries to hint at, as I have a relatively good understanding of the APIs and facilities we have in core. 99% of other developers will not be able to make sense of this. Can we clarify what is actually leveraged, and how exactly, and what kind of consequences that has vs. not doing so? :)

+++ b/core/modules/editor/js/editor.createjs.js
@@ -0,0 +1,187 @@
+     * @param options
+     *   An object with the following keys:
+     *    - $editorElement (required): the PredicateEditor DOM element.
+     *    - propertyID (required): the property ID that uniquely identifies the
+     *      property for which this form will be loaded.
+     *    - callback (required: A callback function that will receive the
+     *      rerendered processed text.

If all of these are required, why aren't they separate arguments instead?

+++ b/core/modules/editor/lib/Drupal/editor/Ajax/FieldRenderedWithoutTransformationFiltersCommand.php
@@ -22,7 +23,7 @@ class FieldRenderedWithoutTransformationFiltersCommand extends BaseCommand {
+    parent::__construct('editorFieldRenderedWithoutTransformationFilters', $data);

I seriously hope that I will never have to specific this Ajax command class name anywhere... yes? :)

+++ b/core/modules/editor/lib/Drupal/editor/EditorController.php
@@ -0,0 +1,47 @@
+   * @param string $view_mode
+   *   The view mode the processed text field should be rerendered in.
+   * @return \Drupal\Core\Ajax\AjaxResponse

There should be a blank line between @param and @return directives.

+++ b/core/modules/editor/lib/Drupal/editor/Plugin/edit/editor/Editor.php
@@ -0,0 +1,100 @@
+  function isCompatible(FieldInstance $instance, array $items) {

This validation method does not check whether $instance is a text field.

+++ b/core/modules/editor/lib/Drupal/editor/Plugin/edit/editor/Editor.php
@@ -0,0 +1,100 @@
+    // This editor is incompatible with multivalued fields.
+    if ($field['cardinality'] != 1) {
+      return FALSE;
+    }

I wonder why we still have this limitation...?

Is there an issue to remove this?

+++ b/core/modules/editor/lib/Drupal/editor/Plugin/edit/editor/Editor.php
@@ -0,0 +1,100 @@
+  protected function textFormatHasTransformationFilters($format_id) {
+    return (bool) count(array_intersect(array(FILTER_TYPE_TRANSFORM_REVERSIBLE, FILTER_TYPE_TRANSFORM_IRREVERSIBLE), filter_get_filter_types_by_format($format_id)));
+  }

Do we have an issue to get rid of this procedural function?

+++ b/core/modules/editor/lib/Drupal/editor/Plugin/edit/editor/Editor.php
@@ -0,0 +1,100 @@
+  public function getAttachments() {
...
+    // Get the attachments for all text editors that the user might use.
+    $attachments = $manager->getAttachments($formats);

Hm. I did not really expect this on the edit\Editor plugin. It looks more like an operation the plugin.manager.editor should be responsible for. Apparently, that's partially the case already, but only partially.

+++ b/core/modules/editor/lib/Drupal/editor/Plugin/edit/editor/Editor.php
@@ -0,0 +1,100 @@
+    // Filter the current user's text to those that support inline editing.

s/text/formats/

+++ b/core/modules/editor/lib/Drupal/editor/Tests/EditIntegrationTest.php
@@ -0,0 +1,200 @@
+    $this->createFieldWithInstance(

Just FYI: The more we introduce of this, the more we'll have to convert/migrate later on. This all-in-one multi-purpose helper wasn't a particularly good idea ;)

@see #1902098: Introduce DUTB helper methods for working with rendered content + FieldPluginUnitTestBase

Wim Leers’s picture

Thanks for your most excellent and helpful review, @sun! I will address it ASAP, hopefully tonight.

Wim Leers’s picture

Issue tags: +sprint

I've been working on a reroll to address everything raised in #28. I most likely won't finish it today. I'll try to wrap it up over the weekend, definitely expect it to be posted on Monday.

Wim Leers’s picture

Status: Needs work » Needs review
FileSize
33.46 KB
44.3 KB

As per #27, #1873500: CKEditor + Edit was merged into this patch.

Thanks to #1874664: Introduce toolbar level "Edit" mode that shows all contextual links, slight simplifications are possible.


#28

First of all: I very much agree that all Create.js-related JS code needs *a lot* of love. But that's blocked on upstream work, so before we can achieve the code quality (including documentation) we're used to in the Drupal world, we'll need to help make Create.js better. For that, there is #1874934: Revise Create.js-related code when upstream issues have been fixed.

I addressed almost all of your concerns. Where meaningful, I provided feedback below. Whenever I didn't address something, there is definitely feedback for it.

Thanks for your review — the code is now noticeably better :)

RE: editor_custom_theme()
I can't stress enough how much I hate this. But, alas, we cannot use 'theme callback' => 'ajax_base_page_theme', because this is one of the few modules ahead of the pack: it's using the routing system exclusively instead of the menu system, hence we're forced to have such a work-around. This was conceived by effulgentsia btw, for the Edit module. The Text Editor module simply duplicates this.

RE: "Can we transform these into multi-line syntax, plz?"
I don't see why. They're perfectly legible, and they fit on one line. Finally — and importantly — no other k/v pairs will be added.

RE: that vs. self
Personally, I agree. But apparently, var self = this; is a bad practice. I don't remember the full story, but this is at least part of the reason: self is a pointer to the current window: self === window, see http://www.nczonline.net/blog/2007/06/03/javascript-variable-names-you-s....

RE: "I can only remotely imagine what this comment tries to hint at […]"
In short: this is nigh impossible to understand, because Drupal.ajax sucks if you want to use it directly from other JS. It's only designed to be easy to use via #ajax in render arrays. See #1533366: Simplify and optimize Drupal.ajax() instantiation and implementation to improve its DX.

(I also didn't know this was possible, props go to @frega for discovering this. I guess it's a well-kept secret. Edit.module also uses this to achieve a Backbone.sync-implementation-on-top-of-Form-API-with-#ajax.)

It's briefly mentioned in ajax.js:

/**
 * Ajax object.
 *
 * All Ajax objects on a page are accessible through the global Drupal.ajax
 * object and are keyed by the submit button's ID. You can access them from
 * your module's JavaScript file to override properties or functions.
 *
 * For example, if your Ajax enabled button has the ID 'edit-submit', you can
 * redefine the function that is called to insert the new content like this
 * (inside a Drupal.behaviors attach block):
 * @code
 *    Drupal.behaviors.myCustomAJAXStuff = {
 *      attach: function (context, settings) {
 *        Drupal.ajax['edit-submit'].commands.insert = function (ajax, response, status) {
 *          new_content = $(response.data);
 *          $('#my-wrapper').append(new_content);
 *          alert('New content was appended to #my-wrapper');
 *        }
 *      }
 *    };
 * @endcode
 */

Once you've read that, it'll probably be clear to you as well.

All that being said: it did not really belong in the docblock for that function; it belongs in the function body comments, where it actually already lives. So I just removed this comment, it was superfluous.

RE: "I seriously hope that I will never have to specific this Ajax command class name anywhere... yes? :)"
Hah :) No :) (It's now only editorGetUntransformedText though.) It's prefixed with "editor", much like Views' AJAX commands are prefixed with "views" — you would probably also never want to use Views' AJAX commands.

RE: "This validation method does not check whether $instance is a text field."
Indeed. This is correct, because this is only ever called if and only if the field type is marked to use the "direct" Create.js PropertyEditor widget. It is up to the field type class annotations to be correct; the isCompatible method only needs to ensure that this field instance (i.e. its config/settings) are compatible with the Create.js PropertyEditor widget for which it is implemented.

RE: "I wonder why we still have this limitation...? Is there an issue to remove this?"
This is 100% unrelated to this issue.
(A brief explanation: multivalued fields in Drupal are always re-orderable. The only way we can make fields re-orderable via in-place editing is by using the "form" Create.js ProperytEditor widget. Hence, any field with cardinality >1 will automatically fall back to using a form.)

RE: "Do we have an issue to get rid of this procedural function?"
Why?

Status: Needs review » Needs work
Issue tags: -wysiwyg, -sprint, -Spark, -CKEditor in core

The last submitted patch, editor_true_wysiwyg-1886566-31.patch, failed testing.

Wim Leers’s picture

Status: Needs work » Needs review
Issue tags: +wysiwyg, +sprint, +Spark, +CKEditor in core
Wim Leers’s picture

CKEditor 4.1 RC introduces the "Source Dialog" plug-in, which makes it possible to have the "Source" mode while doing in-place WYSIWYG editing as well.

Issue to add CKEditor 4.1 RC: #1937484: Update CKEditor library to 4.1 RC and remove CKEditor default style configuration.

Once that issue is committed, we can leverage it by doing the following in settingsOverride:

        extraPlugins: 'sharedspace,sourcedialog',
        removePlugins: 'floatingspace,elementspath,sourcearea',

instead of

        extraPlugins: 'sharedspace',
        removePlugins: 'floatingspace,elementspath',
danielnolde’s picture

Status: Needs review » Needs work

Patch in #31 applies but results in the following error when viewing a simple article node as admin (fresh D8 install from 2013-03-08, created article node 1, applied patch #31, enabled modules CKEditor, edit and Text Editor, configured CKEditor to be used for text formats):

Error

The website has encountered an error. Please try again later.
Error message
Doctrine\Common\Annotations\AnnotationException: [Semantical Error] The annotation "@Drupal\Core\Annotation\Plugin" in class Drupal\editor\Plugin\edit\editor\Editor does not exist, or could not be auto-loaded. in Doctrine\Common\Annotations\AnnotationException::semanticalError() (line 52 of /Volumes/daten2/projekte/drupal8/code/www/core/vendor/doctrine/common/lib/Doctrine/Common/Annotations/AnnotationException.php).

Besides i realize that Drupal is getting way more complex to debug:
Can anyone reproduce this error?

Wim Leers’s picture

Status: Needs work » Needs review
FileSize
44.31 KB

#35: That's because of a recently rename(space)d class.

Attached patch changes Drupal\Core\Annotation\Plugin to Drupal\Component\Annotation\Plugin and should pass again.

Thanks! :)

quicksketch’s picture

+  _loadExternalPlugins: function(format) {
+    var externalPlugins = format.editorSettings.drupalExternalPlugins;
+    // Register and load additional CKEditor plugins as necessary.
+    if (externalPlugins) {
+      for (var pluginName in externalPlugins) {
+        if (externalPlugins.hasOwnProperty(pluginName)) {
+          CKEDITOR.plugins.addExternal(pluginName, externalPlugins[pluginName], '');
+        }
+      }
+      delete format.editorSettings.drupalExternalPlugins;
+    }
   }

It's a good idea to split out this logic since we now need it multiple places, but the fact that this function requires format.editorSettings.drupalExternalPlugins to be set, but then deletes that property after its done is some confusing behavior. Although it's more repetition, moving the deletion of this variable outside the function makes the code significantly easier to follow:

Instead of:

this._loadExternalPlugins(format);

Use:

if (format.editorSettings.drupalExternalPlugins) {
  this._loadExternalPlugins(format.editorSettings.drupalExternalPlugins));
  delete format.editorSettings.drupalExternalPlugins;
}

But then all that's left in that function is a loop... this bit of code is difficult to optimize without making it difficult to understand.

-(function (Drupal, CKEDITOR) {
+(function (Drupal, drupalSettings, CKEDITOR, $) {

This isn't necessarily a complant, but drupalSettings is never used in the ckeditor.js file. This will throw a JSLint warning by default. Is it our policy to *always* scope drupalSettings even if it's not used?

+  // Create.js PropertyEditor widget library names begin with "edit.editor".
+  $libraries['edit.editor.editor'] = array(

Oh dear... talk about a namespace mess. I reviewed our general pattern for naming libraries (i.e. $libraries['jquery.ui.draggable'], $libraries['jquery.effects.shake']) and it seems like this is following the correct pattern: edit = name of library, editor = category within parent library, editor = individual implementation within category. Resulting in "edit.editor.editor".

Edit module named these libraries before "editor.module" was created, so the name-spacing isn't entirely its fault. Given the description texts for each of these libraries, I think we might be able to rename the category to avoid name-spacing confusion. Currently the descriptions for each library edit.editor.form, edit.editor.direct and now edit.editor.editor all use "X Create.js PropertyEditor widget". Based on this, I think it may make sense if we renamed these libraries to use "widgets" instead of "editor", resulting in the library names edit.widgets.form, edit.widgets.direct and edit.widgets.editor.

(Additionally thanks for renaming drupalwysiwygwidget.js to editor.createjs.js, that's much better.)

But, alas, we cannot use 'theme callback' => 'ajax_base_page_theme', because this is one of the few modules ahead of the pack: it's using the routing system exclusively instead of the menu system, hence we're forced to have such a work-around.

Yeesh. After researching the new routing system I have to agree that this work-around is the only available solution we have unless we convert the router entries *back* to hook_menu(). As that seems like it would be moving backwards from current trends, we're stuck with the work-around for now.

+      case 'changed':
+        break;
+
+      case 'saving':
+        break;
+
+      case 'saved':
+        break;
+
+      case 'invalid':
+        break;

This seems wasteful that we're checking cases but not actually doing anything with them, but I'm not opposed to keeping them in here. It's just a little strange. If they seem worth keeping then let's leave as-is.

+ * @Plugin(
+ *   id = "editor",
+ *   jsClassName = "editor",
+ *   alternativeTo = {"direct"},
+ *   module = "editor"
+ * )

jsClassName and alternativeTo don't have anything to do with the discovery of these classes does it? Is this issue related enough to fix these definitions and move the non-discovery information into a dedicated method?

Thanks for the progress on this issue Wim. Overall it's looking great. I really appreciate the change from "supports_true_wysiwyg" to "supports_inline_editing". I'll try to give this a functional review followup soon. This issue has probably seen a disproportionate number of code reviews to end-user reviews.

Wim Leers’s picture

#37:
- RE: _loadExternalPlugins() weirdness: agreed. And I also don't know of an obvious way to make the code more clear. We could of course make it so that instead of deleting variables, it tracks their state. Then it'd be slightly less weird, I think?
- RE: drupalSettings in ckeditor.js: that's not a policy, that's a mistake :) Good catch!
- RE: library names: Yes, it's hard to make this clear. Then again, very few people will ever see this. "Widgets" doesn't say anything, and more importantly, everything in Create.js is a widget. That's why I specifically wanted to avoid "widget". How about something like "editorWidget"?
- RE: switch statement with no-op cases: I simply list all possible Create.js PropertyEditor widget states. This makes it very explicit and easy to discover which part of the Edit JS code reacts to which state change.
- RE: jsClassName & alternativeTo: they're both vital. The former is to let the JS code know the JS class name, so that we can pass that to Create.js, so that Create.js knows how to instantiate such PropertyEditor widgets. The latter is for discovery, and was introduced at #1874936: Pluggable in-place editors (allow modules to define new Create.js PropertyEditor widgets). If you want to change that, you're welcome to propose changes to that, but please do so in a separate issue, because that's off-topic for this issue :)

So, in short: please provide "final suggestions" for _loadExternalPlugins() and the library names, and I'll fix them. drupalSettings in ckeditor.js I'll fix. For the switch statement and plugin annotation, I'm convinced they're good to go.

Wim Leers’s picture

Reroll that fixes the closure and improves filenames. The _loadExternalPlugins() weirdness is not yet solved. I don't believe this is critical to solve either, since pretty much nobody will ever have to read this code. Note that it's *identical* to the existing code in its behavior, so this does not introduce a deterioration.

effulgentsia’s picture

Issue tags: -wysiwyg, -sprint, -Spark, -CKEditor in core

Status: Needs review » Needs work
Issue tags: +wysiwyg, +sprint, +Spark, +CKEditor in core

The last submitted patch, editor_true_wysiwyg-1886566-39.patch, failed testing.

Wim Leers’s picture

Status: Needs work » Needs review
FileSize
45.49 KB

Straight reroll.

(Hoping it will pass; #1942000: Node NG broke Edit module introduced changes to the tests, so not 100% certain.)

Status: Needs review » Needs work

The last submitted patch, editor_true_wysiwyg-1886566-42.patch, failed testing.

Wim Leers’s picture

Status: Needs work » Needs review
FileSize
5.17 KB
45.33 KB

Like #1942000: Node NG broke Edit module, also needed Entity/Node NG adjustments.

effulgentsia’s picture

Status: Needs review » Reviewed & tested by the community

AFAICT, all significant feedback has been addressed. Some code style and naming nits haven't been, but whoever cares enough about those can open follow ups for them.

Would be nice for the issue summary to be updated for posterity: either before or after commit.

webchick’s picture

Status: Reviewed & tested by the community » Fixed
Issue tags: -sprint

Excellent!! So nice to see this RTBC, so we can really show off the new features of D8 together! :)

Committed and pushed to 8.x. Thanks!

Wim Leers’s picture

YAAAAAY :D

Very glad to finally get this one off my plate :)

Thanks!

Oddly, precisely the backporting of this to the Drupal 7 version of the Edit module was completed yesterday: http://drupalcode.org/project/edit.git/commit/2934168

Automatically closed -- issue fixed for 2 weeks with no activity.

Anonymous’s picture

Issue summary: View changes

Add clarification about component.