WYSIWYG editor based on GWT
Description
- Issues with current TinyMCE editor
- New Architecture proposal
- New Interface Proposal
- Requirements
- Open questions/Todos
- Agreed stuff
- Task List
- Deadlines
- Requirements from Guillaume
- The new architecture
- A general view
- The API
- org.xwiki.editor.client.core.impl.def
- org.xwiki.editor.client.core.impl
- org.xwiki.editor.client.core
- org.xwiki.editor.client.plugin.impl.def
- org.xwiki.editor.client.plugin.impl
- org.xwiki.editor.client.plugin
- org.xwiki.editor.server.core.impl.def
- org.xwiki.editor.server.plugin.impl.def
- org.xwiki.editor.server.plugin.impl
- org.xwiki.editor.server.plugin
- Various links
Issues with current TinyMCE editor
- Slow
- Complex code using complex regexs. This leads to a fragility that makes it break somewhere whenever someone adds a modification to it.
- Lots of bug reports consistently for the past year, showing a non-optimal design
New Architecture proposal
- Use GWT
- High performance (AJAX + no popups)
- Lean and mean code
- Written in Java
- Only one parser
- Better XWiki integeration (images, attachments, links, link suggest, spell checking)
- Conversions are done on the server side
- Use WikiModel as the underlying tool for:
- Wiki syntax to HTML conversions
- HTML conversions to wiki syntax
- Ability to section pages: sections for edited only in WYSIWYG mode, and sections only allowing wiki markup and code editing so a user can edit wiki markup and code directly (using wiki editor within the WYSIWYG editor) without it affecting the WYSIWYG markup (so you only need the WYSIWYG editor with the embedded wiki editor) preventing the need to be able to flip between the WYSIWYG editor and wiki editors to do coding, etc, and the associated problems with converting between the two editors (problems such as the WYSIWYG markup being fairly unreadable and uneditable in the wiki editor). This architecture should help provide more control and flexibility for editing XWiki pages.
New Interface Proposal
Requirements
- Iso-feature with current TinyMCE editor
For the future:
- Ability to import Office documents (Word, Excel) and OpenOffice ones too
- Real Time editing
- Wysiwyg Macros support
- Support importing of other wikis syntax with WikiModel
- Cut and paste from Word and web pages easily i.e. without having to go back and adjust the formatting of what's been pasted onto an XWiki page
- Cut and paste images without attaching first (i.e. when you cut and paste images are automatically attached the page)
- Improved appearance of tables
- Ability to layout pages to improve page appearance so text can be grouped in columns and boxes more easily
Open questions/Todos
- Is there a GWT editor component we can/should reuse? To be checked with Ludovic since he's the one with the most experience on GWT so far
- Check WikiModel capabilities for conversions in both directions and especially from HTML to wiki syntax.
- How to integrate WikiModel in the Rendering layer of XWiki? (Vincent to work on this)
- Is there any code to be salvaged from the work done by Christian and Nam in that branch: svn+ssh://svn.forge.objectweb.org/svnroot/xwiki/xwiki-platform/core/branches/XWIKI_WYSIWYG_NEWARCHI
Agreed stuff
- Ludovic and Marius work on it. Vincent work on the rendering component.
Initial prototype demonstrating the editor and possible real-time featureshttps://svn.xwiki.org/svnroot/sandbox/wysiwyg/Need GWT 1.4https://svn.xwiki.org/svnroot/xwiki/xwiki-platform/web/branches/xwiki-web-gwt-gwt14/Branch/Sandbox to createhttps://svn.xwiki.org/svnroot/sandbox/wysiwyg/
Task List
Deadlines
TBD
Requirements from Guillaume
Description
Introduction
The current WYSIWYG editor has to be replaced. Indeed, many problems have been identified while using it, among which the following:
- The editor loads too slowly
- It is missing key features and is not easy to extend
- It has far (FAR) too many bugs even though lot of work has been done on it
- Its community does not supprot it any longer
- The way it handles WYSIWYG to XWiki markup and other syntaxes & text editors to XWiki markup is very poor
- Actions are not handled with enough clarity
The aim is to build a new text editor that would provide an efficient way for end-users to interact with the wiki in Edit mode.
The demographics of XWiki users is twofolds:
- Newbies -> users of the WYSIWYG editor
- Advanced Users -> users of the XWiki syntax & other advanced stuff
Our end-user aim is to make XWiki edition available & easy for both groups. Our technical aim is to make the editor's code extensible (making potential new features easier to add), well integrated in the core and its execution in a browser as fast as possible.
The underlying technology choice is to use GWT to generate clean code and make it easy to maintain since it will be written in Java once and for all, making testing and integration easier.
Architecture & Ergonomics Principles Speed & Effectiveness
The idea would be to use GWT to create a powerful AJAX editor. To increase its effectiveness & speed, a completely modular architecture would be better. That is, the content of elements of the interface have to be loaded only when required.
Toolbar buttons & toolbar behavior have to be selected carefully so as not to trigger the load of elements that are not required to complete a specific task.
Example: I highlight a word, click on the "weblink" button > the link editor opens in the "weblink" tab > i change my mind and want to make it a wikilink instead > I click on the "wikilink" tab > the contents of that tab are loaded & displayed at that moment only.
The coordination & complementarity between the toolbar & right-click menus is important too and needs to be given proper thought.
Example: I have written a new "Fireworks" macro that displays text in a box with a black & red background and a small fireworks icon. I want users to be able to use it right away. What code do I need to add and where so that it can be used by newbies through the editor? > they can choose it in a list of visual macros and immediately see what their text will look like in the visual box.
Features
The editor main features will include the following:
Editing a page
Editing a page is the single most important experience of a wiki user. A wiki is basically all about letting people contribute content to the website - and then handling that content.
The editor must provide both types of users with a good experience. Another point to keep in mind is that with XEclipse power users have an alternative to using XWiki's usual wiki edition mode.
When people will want to add content to the wiki they will typically either paste it from another text editor or type it directly in the editor. Therefore it should cater for both situations.
We need to decide how to support text coming from MS Word & OO Writer -> use plain-text pasting, use a HTML filter to convert their output to wiki syntax before pasting it on a page...
While editing a page, visual macros have to be easy to add and to edit.
Link creation
This is the second most important feature of the editor. It shall provide users with a quick and intuitive way to create links between documents on the wiki. The current link creator offers some good features but its implementation through popups is too slow. Shortcuts to specific types of links (weblinks for instance) could be doe through a button directly on the toobar rather than stepping into the wikilink mode first.
The link editor will provide an interface to access many elements inside XWiki, namely documents (XWiki pages) & attachments (PDFs, Images...). I'd suggest a specific toolbar button for the following:
- WikiLink
- WebLink
- Image
- Other type of attachment
- ? (email) ?
Then the link creator has to cater for every situation, offering quick access (using a drop-down list view) to all docs and attachments in the wiki with features such as:
- Browse / Search all docs
- Filter by whole wiki / space / page
- Recently viewed / Recently modified
- Browse / Search only for:
- Pages
- Pages with a specific object within
- Images
- PDFs
- Word Docs
- Attachments -> offering the ability to bulk upload attachments to a page
- Add here...
Ability to start a real-time edition session
We will be confronted to 4 types of situations:
Wiki | WYSIWYG | |
---|---|---|
1 people | Fast | Semi-fast |
More than 1 | Light real-time session | Real-time session |
The aim is to be able to load the right editor modules so as to cater for the following scenarios:
- Single people page edition: load the WYSIWYG part only when necessary
- Multi-people page edition: load the real time module only when the 2nd person arrives on the page, load the WYSIWYG only if needed.
Saving
One of the frequently encountered problems while using a wiki is the data loss issue -> eg, loosing unsaved text because the connexion failed just before saving the page. We need to choose a way to prevent this from happening, by automatically saving the page on a regular basis and providing a connexion indicator to notify users when the connexion is lost.\
This will require a decision to be made on the format used for those temporary versions: will each version take the place of the previous one, will they be kept as minor or even sub-minor versions ?
Tables
Limited advanced table support would be great. It would look like the current table editor, with the addition of a few functions being supported (division, sum, multiplication, selection handle...)
The table sorter included in XWiki would be available on the GUI (rather than through html code only) -> it could then be used easily to decide what columns to sort, what kind of filter to use and so on (see this page for an example of the table sorter.)...
Another point: tables can be used for layout purposes. Specific macros could be created / used to display basic layout shapes without actually showing table borders on a page (say, a square divided in 4 smaller squares of equal size with a given margin). Content could then be added in those sub-squares (see the features page on XWiki.org to see what I mean more precisely).
Detailed Features List
Style | Lists | Tables | Links | Macros |
---|---|---|---|---|
Bold | Bulleted | Creation / Insertion | Wiki Link | Information |
Italics | Numbered | Add / delete row / column | Browse | Warning |
Underlined | Greek | Sum | Search | Error |
Striked | Hirigana | Substraction | Recently Viewed | Floating Box |
Subscript | Katanaka | Multiplication | Recently Modified | Code |
Superscript | Roman | Division | Web Link | Generic Box (using a custom div) |
Quote | Uppercase roman | Selection handle | File Link | + other potential macros |
Headings | Alphabetical | Merge cells | ||
Font family | Uppercase alphabetical | Cell properties | Images | |
Font size | Table deletion | Images upload (+ bulk upload) | ||
Text color | Cut / Paste / Copy row / column | Attachments | ||
Background color | Attachments upload (+ bulk ) | |||
Align left / right | ||||
Centered / Justified | ||||
Indent left / right | ||||
Special characters | ||||
Smileys | ||||
Horizontal line | ||||
Remove formatting |
Actions | Code | Syntaxes | Architecture |
---|---|---|---|
Undo / Redo | Velocity | XWiki | GWT |
Cut / Copy / Paste | Groovy | Wiki Creole | AJAX |
Spellchecking | HTML | Wiki Model | Cross-browser |
Real - time edition | JS | Media Wiki | WikiModel Engine |
Find & Replace | Snippets | TWiki | Integrated in core |
Auto - suggest | Widgets | Confluence | Modules |
Import from... | Clean Code | ||
Autosave | Full Link Parser | ||
Save | WYSIWYG Development Interface | ||
Minor save | Class fields Drag & Drop on a page | ||
Cancel | MS Access of the Web | ||
Preview | |||
Wiki Mode | |||
Full Screen | |||
Chat |
See the following for example of other WYSIWYG web editors for other ideas for editor features:
- An interesting summary of various editors here http://ajaxpatterns.org/archive/Rich_Text_Editor.php
- A good source of ideas of web text editors: http://www.htmlarea.com/
- An interesting idea: double click on a section to bring up an editor: http://demo.wikiwyg.net/wikiwyg/demo/standalone/
- Good implementation of multi-user edits https://docs.google.com/document/d/1JU1cLdn8iRvatw6ee0rf3H-yljvuyUN0O9G7_8oQc60/edit (works best when several users open the link)
- Multi-user edits + good in-browser spread-sheets editing https://docs.google.com/spreadsheet/ccc?key=0Ak_g3oatTLNfdHdxWENrQ0VweDR0empkdDRDN0FIMGc#gid=0
The new architecture
A general view
The idea behind this design proposal is to make a lightweight and extensible editor, both for WYSIWYG editing and for Wiki-syntax editing. More precisely, the editor should have a lightweight core and should offer the possibility of adding new features (plug-ins), making it extensible.
The Core
The core's architecture is split in two: the client side and the server side. Both of them will be written in Java, but the client side will be translated in JavaScript with the help of GWT.
The client side has the following components:
- The tool bar handles a collection of action widgets, affecting the way we view and edit the content of the text area. It should be displayed at the top of the text area.
- The rich text area allows us to view and edit formatted text, as in the case of a word processor application.
- The status bar is a way of displaying to the user information such as: number of lines, connection status, time elapsed since last save operation etc. In a way, it is similar to the tool bar, but having only static widgets. The status bar should be positioned at the bottom of the text area.
- The context menu has a list of menu entries which allow us to do context sensible actions, such as 'Paste here'. It should become visible on mouse right click.
- The floating context tool bar is needed when we do a complex operation (like editing a table or an image) which has some options we can chose from and these options can't be found on the tool bar. Being context sensitive, it should be displayed when we 'activate' some object from the text area, and must be draggable.
- The plug-in configuration panel should offer the possibility of configuring the editor's available features. For instance, in the case of the auto save feature, the user might want to set the save interval.
Each of the components above will have an interface and at least one implementation of it. For instance, in the case of the rich text area one implementation could wrap the RichTextArea widget provided by the last version of the GWT.
The server-side will be implemented as a GWT service (a servlet in reality). Here we have two options: we either create a new service facade (something like XWikiEditorServiceFacade) specific to our needs, or extend the XWikiService and XWikiGWTApp interfaces (which are used within XWiki Watch) with the needed functionality.
The editor's core shouldn't offer any feature at all. Instead, it should allows us to install and uninstall features (plug-ins) depending on the displayed/edited content (WYSIWYG/Wiki-syntax). These plug-ins could be developed independent of the core.
The Plug-ins
The plug-ins add functionality to the core. They are registered for a type of content: some plug-ins are available for the WYSIWYG editing (HTML content) and some are for the Wiki-syntax editing (XWiki, Creole, Confluence etc.). Also, the plug-ins can either act on the client-side only (make text bold, change font-family) or act both on the client side and on the server-side (auto save, add macro).
Before the editor loads a document it installs all the available plug-ins associated with its content. When a plug-in is being installed it can: add an action widget to tool bar, add an entry to the context menu, add a static widget to the status bar, add a page (composite widget) to the configuration panel.
Some plug-ins depend on a specific functionality on the server-side and because of this they need a way of accessing it. The editor's core should provide an API for this: Object executePlugin(String pluginName, Object args); this way the 'add macro' plug-in could retrieve from the server the list of the available macros and the 'auto save' plug-in could send the new content to the server, to be saved.
The API
I've defined an API for the new GWT editor. You should be aware that all package, interface, class, method, field names are subject to change and I'm open to your suggestions. We should use self-explanatory and natural names as much as we can.
org.xwiki.editor.client.core.impl.def
}
public class DefaultContextMenu extends AbstractContextMenu {
}
public class DefaultEditor extends AbstractEditor{
public DefaultEditor(PlugInManager plugInManager, EditorServiceAsync editorService) {
}
}
public class DefaultFloatingToolBar extends AbstractFloatingToolBar {
}
public class DefaultRichTextArea extends AbstractRichTextArea {
}
public class DefaultStatusBar extends AbstractStatusBar {
}
public class DefaultToolBar extends AbstractToolBar {
}
org.xwiki.editor.client.core.impl
}
public abstract class AbstractContextMenu extends Composite implements ContextMenu {
}
public abstract class AbstractEditor extends Composite implements Editor {
protected EditorServiceAsync editorService;
protected PlugInManager plugInManager;
protected AbstractConfigPanel configPanel;
protected AbstractContextMenu contextMenu;
protected AbstractFloatingToolBar floatingToolBar;
protected AbstractRichTextArea richTextArea;
protected AbstractStatusBar statusBar;
protected AbstractToolBar toolBar;
}
public abstract class AbstractFloatingToolBar extends Composite implements FloatingToolBar {
}
public abstract class AbstractPropertyManager implements PropertyManager {
protected boolean dirty;
protected abstract void doApplyChanges();
protected abstract void doDiscardChanges();
public boolean isDirty() {}
protected abstract void doRestoreDefaults();
public void setDirty(boolean dirty) {}
}
public abstract class AbstractRichTextArea extends Composite implements RichTextArea {
}
public abstract class AbstractStatusBar extends Composite implements StatusBar {
}
public abstract class AbstractToolBar extends Composite implements ToolBar {
}
org.xwiki.editor.client.core
void addPage(String name, Widget page, PropertyManager manager);
}
public enum ContentType {HTML, XWIKI, CREOLE, CONFLUENCE}
public interface ContextMenu {
void addCheckBox(String label, Command cmd);
void addImage(AbstractImagePrototype imagePrototype, String label, Command cmd);
void addRadioButton(String groupName, String label, Command cmd);
ContextMenu addSubMenu(AbstractImagePrototype imagePrototype, String label);
}
public interface Editor {
ConfigPanel getConfigPanel();
ContextMenu getContextMenu();
EditorServiceAsync getEditorService();
FloatingToolBar getFloatingToolBar();
RichTextArea getRichTextArea();
StatusBar getStatusBar();
ToolBar getToolBar();
}
public interface EditorService {
Serializable invoke(String plugInServiceName, Serializable args);
Map<String, String> restoreProperties(String plugInName);
void storeProperties(String plugInName, Map<String, String> properties);
}
public interface EditorServiceAsync {
void invoke(String plugInServiceName, Serializable args, AsyncCallback callback);
void restoreProperties(String plugInName, AsyncCallback callback);
void storeProperties(String plugInName, Map<String, String> properties, AsyncCallback callback);
}
public interface FloatingToolBar extends ToolBar {
}
public interface PropertyManager {
void applyChanges();
void discardChanges();
void restoreDefaults();
}
public interface RichTextArea extends SourcesClickEvents, SourcesFocusEvents, SourcesKeyboardEvents, SourcesMouseEvents, SourcesMouseWheelEvents, SourcesScrollEvents, HasHTML, HasText, com.google.gwt.user.client.ui.RichTextArea.ExtendedFormatter {
}
public interface StatusBar extends HasWidgets {
}
public interface ToolBar extends HasWidgets {
}
org.xwiki.editor.client.plugin.impl.def
private Map<ContentType, List<PlugIn>> plugInMap;
}
public class DefaultPlugInPropertyManager extends AbstractPropertyManager {
private PlugIn plugIn;
private Map<String, String> properties;
private Map<String, String> defaults;
public DefaultPlugInPropertyManager(PlugIn plugIn, Map<String, String> defaults) {}
}
org.xwiki.editor.client.plugin.impl
protected Editor editor;
protected PropertyManager propertyManager;
protected Map<String, String> properties;
protected abstract void installConfigPanel();
protected abstract void installContextMenu();
protected abstract void installFloatingToolbar();
protected abstract void installRichTextArea();
protected abstract void installStatusBar();
protected abstract void installToolBar();
protected abstract void uninstallConfigPanel();
protected abstract void uninstallContextMenu();
protected abstract void uninstallFloatingToolbar();
protected abstract void uninstallRichTextArea();
protected abstract void uninstallStatusBar();
protected abstract void uninstallToolBar();
}
public abstract class AbstractPlugInManager implements PlugInManager {
}
org.xwiki.editor.client.plugin
String getName();
String getProperty(String name);
PropertyManager getPropertyManager();
Iterator<String> getPropertyNames();
void install(Editor editor);
void setProperty(String name, String value);
void uninstall();
}
public interface PlugInManager {
void register(PlugIn plugIn, ContentType contentType);
void register(PlugIn plugIn, ContentType[] contentTypes);
void unregister(PlugIn plugIn, ContentType contentType);
void unregister(PlugIn plugIn, ContentType[] contentTypes);
Iterator iterator(ContentType contentType);
}
org.xwiki.editor.server.core.impl.def
private PlugInServiceManager plugInServiceManager;
}
org.xwiki.editor.server.plugin.impl.def
private Map<String, PlugInService> plugInServiceMap;
}
org.xwiki.editor.server.plugin.impl
}
org.xwiki.editor.server.plugin
Serializable invoke(Serializable args);
String getName();
}
public interface PlugInServiceManager {
PlugInService get(String plugInServiceName);
Iterator iterator();
void register(PlugInService plugInService);
void unregister(PlugInService plugInService);
}