Wysiwyg 3.x - Wrapping it all up.

sun's picture

Wysiwyg module, albeit relatively young, has come a long way in the meantime. Let's see where we are and where we are heading.

Wysiwyg 0.x-1.x was born as a fork of the obsolete/abandoned TinyMCE module. It started with the simple idea of collecting (valid) integration approaches from all other editor integration modules and shaping them up into a new module that builds a PHP layer around Drupal's API to integrate editors. Due to its origin, it mostly supported TinyMCE only.

The improvements in Wysiwyg 2.x are quite impressive. Most importantly, we introduced so called Drupal plugins, other modules can provide/expose, and which should work with any editor that supports Drupal plugins. Getting there was a step-by-step process, always figuring out how it could work, but without knowing whether it will ultimately work for all editors. As a matter of fact, the current implementation works better than I expected it would.

But we also refactored the JavaScript layer to increase compatibility with other editors. Since Wysiwyg 2.x, editors supporting Drupal plugins ship with an additional JavaScript object that provides methods to allow plugins as well as other JavaScripts to interact with the currently attached editor. These methods are contained in an "instance" object for each editor - which turned out to be one of the missing puzzle pieces in Wysiwyg's architectural design.

Wysiwyg module now mainly consists of two layers:

  1. At the PHP layer, we have editor implementations that provide the necessary functions and information to setup, configure, and load an editor via Drupal's API (tunneled via Wysiwyg's API).

  2. At the JavaScript layer, we have editor objects that provide methods to attach and detach a particular editor, on demand, and allowing to spawn multiple instances of the same editor (configuration). If present, the editor "instance" object (for Drupal plugins) additionally allows to register editor plugins/buttons of third-party Drupal modules, but also to insert content into the editor as well as process the editor's content prior attaching and detaching it.

The editor "instance" object is especially important, because it allows other Drupal modules to implement Drupal plugins. Drupal plugins are

  • registered on the PHP layer, via hook_wysiwyg_include_directory() (following Panels' lead on atomic plugins)
  • auto-exposed to the editor configuration through a (so called) proxy plugin, usually called "drupal"
  • registered, loaded, and processed on the JavaScript layer by the editor "instance" object, which processed all buttons provided via the proxy plugin.

This concept follows the common approach of editor libraries, which allow one plugin (here: the proxy "drupal" plugin) to expose multiple buttons to the editor. A real/full editor plugin would have to touch (and know) the editor's internal API. And since Drupal modules only want to expose buttons to do something, we are basically/simply replacing the editor's native plugin API with Wysiwyg's plugin API.

Actually, now that I write this all up, it would probably make sense to call it "Drupal buttons" instead of "Drupal plugins", and likewise, "Wysiwyg button API"... ;)

I want to repeat this again to make sure you get it:

Wysiwyg module provides two layers of indirection: One layer in PHP, the second layer in JavaScript. The purpose of these layers is to form a unique application programming interface (API) for other Drupal modules. So Drupal module authors do not have to (and should not have to) know any internals of any client-side editor.

This roughly summarizes the overall goal of Wysiwyg module. Since Drupal core never shipped with a client-side editor, its content-editing features are way behind of other content management systems (which may be ahead, because they ship with a dedicated editor and allow their modules/extensions to integrate with it). Given the amount of available client-side editors on the market and the amount of different use-cases, not including a dedicated editor in Drupal core was and is probably a very good decision.

Following Drupal's nature, we want to serve all possible use-cases without affecting the other use-cases negatively (and, no, I won't apply the pareto principle here). Since we want to serve all use-cases (which means that we have to support the full range between "no editor" and "a Flash/Flex-based editor"), Drupal modules need a way to communicate with "the" editor. Wysiwyg's API not only solves this challenge, but also takes Drupal far ahead of many other content management systems/frameworks, since we allow everything (every module) to integrate with anything (any editor).

But - aside from the existing bug reports (which mostly affect certain browsers only) - we are not there yet. The following is the roadmap for Wysiwyg 3.x (codename "rock" if you want to):

Libraries API

  • Require Libraries API for editor libraries.
  • Turn editor plugins into libraries: Native plugins and Drupal plugins are meant here. Especially native also need version information (min/max), hence we need to properly handle dependencies between libraries and plugins. Further, the current "default" buttons really belong to the editor, not to any plugin, in most cases.
  • Cache editors and plugin implementations.

Wysiwyg API

  • Use CTools. Turn editor configurations into objects to use CTools.
  • Use Form API for advanced settings: both for editors and all plugins. What we currently have is specific to TinyMCE and needs to be moved there. Questionable is whether this, along with turning plugins into libraries, will make plugin code so large that they should live in separate files.
  • Use jQuery UI sortable to sort and CTools to configure editor/plugins/buttons. Allow button grouping and separators if supported by editor.
  • Find a UI for "Use jQuery UI sortable to sort and CTools to configure editor/plugins/buttons": Here, we need some mockups first. Mockups could start from the current UI of Views module.
  • Allow multiple profiles per input format, depending on user roles: Actually, this makes no sense from a technical perspective. However, Drupal's filter system limits access to content for users that have access to the used input format. So users can't easily use separate input formats to edit content. But users do not want to expose all kind of buttons for novice users. This probably also needs a new UI widget to choose the editor (depending on the active input format).
  • Limit possible buttons to HTML filter's tag configuration
  • Rework and simplify editor/profile loading: Currently, we load each profile separately, so we need to ensure that each editor and all external native plugins are only loaded once. If we would build an array of profiles to load, then the loading would be much simpler.
  • Add hooks to allow other modules to alter most data.
  • Add global setting to configure preferred library variant (packed/minified/source)
  • Allow to configure editor skin and theme. Difference between skin and theme needs clarification.
  • Add lazy loading: Forms can appear via AHAH/AJAX, but the containing page does not have any wysiwyg scripts loaded. Certain applications (e.g. Panels) need to be able to initialize/load editors on the parent page. Normally, lazy loading would initialize/load editors also via AHAH, but I've heard that this won't work with all editors.
  • Auto-discover languages and themes.
  • Automatically set language depending on current locale. Additionally, derive language-direction from edited content.

JavaScript design

  • Implement Drupal.detachBehaviors(): via CTools. A patch that re-implements misc/ahah.js in CTools and additionally invokes Drupal.detachBehaviors() upon unloading of AHAH content already exists. Backtalk to merlinofchaos needed to get this done, because IIRC he wanted to merge that modification in his already existing ahah.js override.
  • Use JS 'base classes'/prototypes for editors and plugins: All editors and plugins should be instances of editor resp. plugin prototypes in JS. Especially for editors, init/attach/detach + all methods in 'instance' have to live in one editor instance of this prototype. And we want each instance to provide API methods like getInstance(), getInstances(), getContent([instance = activeEditor]), setContent(content [, instance = activeEditor]), ... The main wysiwyg object should be the API interface and include wrapper methods for public editor methods, defaulting to calling the active editor instance.
  • Turn everything in editor integration JavaScripts into one object.
  • Dialogs for Drupal plugins (Popups API or Modal Frame API)
  • Add global editor settings next to format-specific: Primarily for JS settings like the editor (base)path, which are the same for all formats. But also, some further editor settings could be applied to all profiles/formats, depending on the configuration (which should also make amount of data in Drupal.settings smaller).
  • Lots of data in Drupal.settings.wysiwyg.plugins.format#.pluginName is duplicated, find a way to minimize this overhead. Currently, most of the data there could be globally defined and then "overridden" in the previous location. We could add a getPluginSettings(plugin, format) or similar method to compile a single settings object using global/overridden settings.

Module integration

Support stuff

  • Add Debug module support: A hard-coded (forced) module dependency.
  • Extensive API documentation on creating editor implementations, Drupal plugins, and native plugins to encourage more users to participate.
  • A "feature chart" listing features supported by each implemented editor.

Utopian stuff

  • Allow to switch editors and markup language. Convert from one markup syntax to another. We'd need to screen out tags which don't actually belong to the syntax being used and keep them intact. Basically, what we want is to classify all input filters, so we can distinguish between "inline macro filters" and others. To convert a content, we do not want to apply macro filters, but all the others (i.e. BBCode -> HTML). Afterwards, we want to convert from HTML -> Markdown. But keeping all macros intact.

Quite a lot of todos. :)

Daniel F. Kudwien
unleashed mind


Inline vs Media?

DamienMcKenna's picture

Why not use the existing Media module infrastructure rather than building a new Inline API?

It's not "versus". For some

sun's picture

It's not "versus". For some reason, a very custom idea/design/implementation of macro filtering has been added directly to Media module. Since macro filtering is an extremely complex topic on its own, I predict with confidence that this direct inclusion will heavily slow down the development of Media module at some point.

Note that a related, perhaps more actionable discussion has been initiated on http://drupal.org/node/1019988

Daniel F. Kudwien
unleashed mind


Group organizers

Group categories

Group notifications

This group offers an RSS feed. Or subscribe to these personalized, sitewide feeds:

Hot content this week