Preventing Users From Pasting Images

Preventing users form inserting images requires two parts:

  1. Remove the insert image related items from the configuration file so the insert image dialog is unavailable.
  2. Prevent users from pasting images into the editor.

The first part is quite simple, just delete the “ImageServer” menu item and toolbar buttons from your configuration file.

The second part however isn’t directly supported by EditLive! Fortunately, we’ve now made a plugin available that automatically strips any images out of pasted content. Like all the plugins on LiveWorks! we don’t offer direct support for the plugin, but the full source code is included and help is generally available on the mailing list. The instructions for using the plugin are on the plugin page - it’s just a simple line of JavaScript.

If you look at the source code for the plugin you’ll find it’s a very simple PasteFilter (see this previous article). The main method simply uses a regular expression to filter out the img tags:

public String filterIn(String source) {
  String regex = "<img[^>]*";
  if (FILTER_ONLY_LOCAL_IMAGES) {
    regex += "src=[\"']file:[^>]*";
  }
  regex += "/?>";
  Pattern pattern = Pattern.compile(regex,
      Pattern.DOTALL | Pattern.CASE_INSENSITIVE);
  Matcher matcher = pattern.matcher(source);
  return matcher.replaceAll("");
}

FILTER_ONLY_LOCAL_IMAGES is a final variable declared at the top of the file. It’s set to false in the standard version so that all images are removed, but if you change it to true, only local images will be removed and remote images will be pasted normally. This is a great way to prevent users from pasting in local images but still allow them to use images from the repository or other sites. It also shows how simple it is to adapt the regular expression to be more selective about which images to filter out. With a little knowledge of regular expression and this plugin as a starting point you should be able to achieve whatever image filtering policy you need.

EditLive! Support For Safari

A number of our clients have been trying out Safari for Windows and finding that EditLive! doesn't load, instead falling back to a standard text area and thinking that we don’t support Safari at all. The reality is that Safari comes in two distinct versions - one for Mac OS X which we support and one for Windows which we don’t. While the two versions look almost identical, there are some key differences around the way they handle applets which is currently preventing EditLive! from running in the Windows version. We also want to make sure we’ve completely tested it out so there aren’t any nasty surprises.

So to be clear - Safari on Mac OS X is completely supported and works great. Safari on Windows isn’t yet supported and will use a text area instead.

Hidden Delights Of 6.4

While the CSS improvements in the upcoming 6.4 release may take center stage, there are a number of neat little quality of life improvements that you might be interested in:

Keyboard Shortcuts

Keyboard shortcuts are enabled for all items on the toolbar, even if they are not present in the menu or if there is no menu. This has been requested by clients a number of times. Sometimes space is extremely limited so you want to remove the menu bar entirely. Previously, this also disabled the keyboard shortcuts like control-B. With 6.4 adding items to the toolbar will also enable the keyboard shortcut.

Paragraphs In DIVs and Tables

We've improved the way content in DIVs and tables works when the user presses enter. Since the usual case for these elements is to have content directly inside them without any P tags, the editor inserted a BR tag when the user pressed enter. In 6.4 this has changed so that the existing content is wrapped in a P tag and a new empty paragraph tag is inserted. This makes the resulting content semantically correct and works a lot better when applying block styles or changing heading levels.

Navigation In Nested and Adjacent Elements

With previous versions of EditLive! it was difficult or impossible to position the caret between two adjacent tables or position it accurately in nested DIVs.  With 6.4 you can simply position the caret in the nested DIV and press up or down. EditLive! will automatically create a space for the caret to fit in so you can start typing. If you just keep pressing up to go past there it will remove the space again automatically. It's a bit hard to explain but we think it works really well.

Improved File Dialogs On OS X

This is my personal favorite. The open and save dialogs on OS X have always been pretty ordinary but in 6.4 we've dramatically improved them so they work just like the native ones (in fact, they are the native ones). While having a more consistent UI is a big benefit on it's own, the native dialogs let you easily browse your iPhoto library to select local images.

Gridlines Around DIVs

EditLive! 6.4 adds a lot of support for DIVs and one of the enhancements is that we make the visible to users by displaying gridlines around them just like we do for tables. Of course, if you've been working with DIVs already you might find those gridlines just get in the way so we've provided a simple switch to turn them off. Just set the wysiwygEdior attribute “showSectionGridlines” to “false”.

And More…

For all the details, including the long list of bug fixes, the change log is available on the early access page where you can also download the latest build to try out.

Inserting Content Between Uneditable Sections

One of our long time partners came to us with a problem recently - they use the contenteditable attribute heavily to make sections of the document uneditable for users. The problem was that when two uneditable sections were next to each other, users can't insert anything between them. For many of our clients this is the desired behavior but it doesn't suit everyone. To solve the problem we actually need a new method to temporarily ignore the contenteditable attribute and allow our code to make changes. That new method is now available in all the early access builds via the DocumentModifier.setOverrideContentEditable method. We'll also make use of the technique for finding specific parent elements described in last week's article.

The solution we'll create is to add a custom menu item that allows a user to insert content before the current element. In the process, we'll demonstrate a very powerful form of the insertHtml method available from DocumentModifier that enables a wide range of possibilities.

First we need to define the custom menu item in our XML configuration file:

<shrtMenuItem name="insertBefore" action="raiseEvent"
  value="insertBefore" text="Insert Before" />

Simple enough, when the menu's selected it will raise an event we can catch with the value "insertBefore". We need to register an editor event listener to catch that event:

bean.addEditorEventListener(this);

Then implement the EventListener interface with a raise event method:

public void raiseEvent(TextEvent e) {
  if (e.getActionCommand() == TextEvent.CUSTOM_ACTION &&
    e.getExtraInt() == TextEvent.CustomAction.RAISE_EVENT &&
    "insertBefore".equals(e.getExtraString())) {
      insertBeforeSection();
  }
}

We're looking for the text event with the TextEvent.CUSTOM_ACTION action command, TextEvent.CustomAction.RAISE_EVENT as the extra int and "insertBefore" as the extra string. When we get it we call the method that does all the work.

The basic algorithm we'll follow is:

  1. Find the element for the uneditable section.
  2. Override the contenteditable attribute.
  3. Insert an empty paragraph before the uneditable section.
  4. Put the caret into the new paragraph.
  5. Enable the contenteditable attribute again.

To find the uneditable section we use the getUneditableBlock method from the previous article.

Element uneditableBlock = getUneditableBlock(bean);

Next we use the new setOverrideContentEditable method to make the section editable temporarily.

DocumentModifier modifier = bean.getDocumentModifier();
modifier.setOverrideContentEditable(true);

Inserting the empty paragraph is a little complex because we need to insert it at the right point in the HTML element structure. If we used the normal insertHTMLAtCursor function, the paragraph would be inserted into the uneditable section:

What we really want is to insert the new paragraph into the parent element of the uneditable DIV so that it becomes a sibling of the DIV:

To achieve this, we need to use the full form of the insertHTML method in DocumentModifier:

modifier.insertHtml(uneditableBlock.getStartOffset(),
  "<p></p>", HTML.Tag.P, uneditableBlock.getParentElement());

The first two arguments are fairly simple - the character offset to insert the content at and the HTML string to insert. The next two arguments are what give this method so much power.

The HTML.Tag.P argument specifies the first tag to start inserting content from. When the parser parses the HTML string we're inserting, it always creates a complete, well-formed HTML document including the HTML and BODY tags. To insert just a snippet of HTML instead we have to specify when to start inserting. With the simple form of insertHTML this is automatically set to the first tag you actually specify in the HTML string, but complex insertions occasionally require manually setting this parameter. In this case, we want to start inserting from the P tag we specified so we pass in HTML.Tag.P.

The final argument is the one we really need in this case. It allows us to specify how far up the element tree to insert the new content. The simple form of insertHtml always inserts the content as close to the leaf elements as allowed in HTML - in our case that would insert the P tag into the uneditable DIV because that's the first element that is allowed to contain a P tag. If we were inserting an IMG tag it would be inserted into the P tag inside the DIV if there were one etc. By specifying the parent of the uneditable DIV here, we ensure that the inserted paragraph has the same parent element as the uneditable DIV, making them siblings.

Now we need to move the character into the newly inserted paragraph. Since the inserted paragraph is immediately before the uneditable DIV we can just set the caret one position before the start of the DIV and it will be in the new paragraph.

bean.getHTMLPane().setCaretPosition(uneditableBlock.getStartOffset() - 1);

Finally we enable the contenteditable attribute again.

modifier.setOverrideContentEditable(false);

When we put the method together it winds up looking like:

private void insertBeforeSection() {
  Element uneditableBlock = getUneditableBlock(bean);
  DocumentModifier modifier = bean.getDocumentModifier();
  modifier.setOverrideContentEditable(true);
  try {
    modifier.insertHtml(uneditableBlock.getStartOffset(),
      "<p></p>", HTML.Tag.P,
      uneditableBlock.getParentElement());
  } catch (BadLocationException e1) {
    e1.printStackTrace();
  }
  bean.getHTMLPane().setCaretPosition(uneditableBlock.getStartOffset() - 1);
  modifier.setOverrideContentEditable(false);
}

While some of the concepts involved are quite foreign to developers who haven't worked with the Swing text APIs before, the resulting code is fairly simple and our users can now insert content between uneditable sections with a simple menu item.

Finding Specific Parent Elements

When developing custom functionality within EditLive! you occasionally want to work with a specific element in the tree. For example, you might want to find the element that wraps an uneditable section that the user has clicked within. Other examples might be implementing a properties dialog for a custom block tag you've registered or finding the table tag that the caret is within.

We'll use the uneditable section example in this article, but it's simple to adjust this technique for whatever type of element you need to find.

Set Up

We'll assume you've already got a plugin set up and have access to the ELJBean instance for the editor. If not, take a look at our previous article on creating plugins.

Main Loop

The basic idea is to start from the character element (the leaf element in the tree) and work our way back up until we find an element that matches our criteria. If there isn't a matching element we'll eventually reach the root of the tree and return null.

public Element getUneditableBlock(ELJBean bean) {
  JTextPane pane = bean.getHTMLPane();
  HTMLDocument document = (HTMLDocument) pane.getDocument();
  Element element = document.getCharacterElement(pane.getCaretPosition());
  while (element != null && !isUneditableBlock(element)) {
    element = element.getParentElement();
  }
  return element;
}

Note that the getHTMLPane() method returns a standard JTextPane so all the usual Swing text component methods are available. Iterating up the tree is very similar to iterating up a standard DOM.

Conditional Selection

All that's left is to implement the isUneditableBlock method which provides the conditional logic for selecting the element we're looking for. This is the method that will vary based on exactly what you're looking for. In this case we're looking for an element that has a "contenteditable" attribute that's set to "false".

private boolean isUneditableBlock(Element element) {
  AttributeSet attributes = element.getAttributes();
  return attributes.isDefined("contenteditable") &&
    attributes.getAttribute("contenteditable").equals("false");
}

Note that we check attributes.isDefined("contenteditable") as well as that it's set to false because getAttribute will search through the parent elements as well. Also note that the contenteditable attribute isn't recognized by the parser so it appears as a string - recognized attributes use usually instances of HTML.Attribute or CSS.Attribute, for example HTML.Attribute.WIDTH or CSS.Attribute.COLOR.

An alternate implementation of the method that finds the table element would be:

private boolean isUneditableBlock(Element element) {
  return HTML.Tag.TABLE ==
    element.getAttributes().getAttribute(AttributeSet.NameAttribute);
}

Doing Something Useful

Just to round out our example, here's a mouseClicked implementation that would use these methods to select the entire uneditable section whenever the user clicks within it.

public void mouseClicked(MouseEvent e) {
  Element block = getUneditableBlock(bean);
  if (block != null) {
    bean.raiseEvent(new TextEvent(this, TextEvent.SELECT_NODE_ACTION, block, -1));
  }
}

Note that you have to wait for the LOADING_COMPLETE event to fire before adding the mouse listener, otherwise ELJBean.getHTMLPane will return null. For more information, see the previous article on initializing plugins.

Creating A “See Also” Panel

With the improved styles support in EditLive! 6.4 (grab the latest build from early access), and a little CSS, it's easy for users to make their content look great and better engage users. Let's look at a fairly simple example where users can add a set of links to related resources in their document. Currently, users would simply add a plain bullet list which is pretty ugly. For example:

See Also

That does the job, but with the improvements in EditLive! 6.4 and some fairly simple CSS we can make it look much more compelling. The first step is to group the heading and the list together in a group. EditLive! 6.4 provides a simple way to do that - just select them both and choose "Create Section" (the default configuration includes it in the Format menu). In terms of the HTML, this wraps a DIV element around the heading and the list which gives us some extra structure to work with but doesn't change the actual rendering.

That extra structure gives us a great place to add styles to control the see also panel from one place. The goal is that the user can simply select one item from the styles drop down and all of our formatting is applied. Let's start by adding some simple styles to the DIV itself:

div.SeeAlso {
    border: 1px solid #D4D4D4;
    width: 20em;
    margin: 0px 10px 0px 10px;
    float: right;
}

Those styles add a simple gray border, set the width of the DIV to 20em (ems are a relative sizing that's newly supported in 6.4 and is much better for accessibility than using px) and floats the DIV to the right so content can wrap around it. The result is on the right, titled "See Also (2)".

Now we want to make the heading and the list itself look better, but we don't want to make the user apply styles to them separately. Fortunately, CSS provides a descendant selector which fits this use case perfectly. We add the styles below which will automatically apply to the content inside the DIV:

div.SeeAlso h4 {
    margin: 0px;
    padding: 5px;
    font-size: 1em;
    background-image: url(largeTitleBar.png);
    color: #FFFFFF;
    background-color: #5E657B;
    background-repeat: repeat-x;
    background-position: center;
    background-attachment: scroll;
}

div.SeeAlso ul {
    margin: 0px;
    padding: 2px 5px 2px 35px;
    list-style-image: url(bluearrow.png);
}

div.SeeAlso li {
    margin: 0px;
    padding: 3px;
}

div.SeeAlso a {
    font-weight: bold;
    text-decoration: none;
    color: #2B3D72;
}

div.SeeAlso a:hover {
    text-decoration: none;
    text-decoration: underline;
    color: #8599D3;
}

See Also Panel With StylesWhile there's a fair bit of CSS there, it's all standard techniques that web designers are quite used to using for styling content. It also uses a couple of background images to provide nice gradients so that the final result looks like the image on the right. Here's the important elements that make it work well with the editor. Keep these in minds when you develop style sheets for your site:

  • Make it easy for users to apply by using a single class on the div element and then apply styles via descendant selectors whenever possible.
  • The class name you use will appear in the styles drop down, so make it self explanatory to help users know when to apply it.
  • The styles combo is context sensitive so it will show an accurate preview of how the style will look in the current context. So with the stylesheet above, the "Heading 4" entry would appear differently in the styles drop down if the current selection is within a "SeeAlso" div because the descendant style applies. Elsewhere, the default rendering of h4 elements would apply. This lets your users know what things will look like without having to try out each style. You could use this to provide different types of rendering for the header based on different heading levels and the user can pick the most appropriate one.

 

Using EditLive! 6.4 (beta) in IWWCM 6.0

Several of our EditLive! for IWWCM clients have asked me recently how they can get hold of EditLive! 6.4 so they can try out the new styling capabilities in the release.  This article will give you the information that you need to install EditLive! 6.4 in IWWCM.

The steps in this article require you to customize an EditLive! for IWWCM installer.  Before making changes to your IWWCM installer I'd recommend that you check out the series of articles I wrote previously on Custom IWWCM installers.  

  1. Get a copy of the EditLive! for IWWCM installer - You can obtain a copy from either the Ephox website, or you can use a copy of an existing EditLive! for IWWCM 6.0 installer that you have.  
    • The advantage of using your own installer is that you will be able to install any customizations you've done along with EditLive! 6.4.
    • If you're downloading a new copy of the installer, unzip it to a location on your server so it's ready to be installed.
  2. Backup - If you're using an existing installer that you've customized then you'll want to back it up before working with EditLive! 6.4.  EditLive! 6.4 is currently a beta and there are bugs, so you will want to make sure you back up your EditLive! for IWWCM installer with EditLive! 6.3 before starting this process.  If you need to go back to using EditLive! 6.3 then having that backup will be important.
  3. Get a copy of EditLive! 6.4 - EditLive! 6.4 beta is available right now from the LiveWorks! web site in the Early Access section, you'll find a link to the EditLive! 6.4 downloads there.  In order to update EditLive! for IWWCM all you will need to download is the update package (the third download link in the list for EditLive! 6.4).  Unzip this package to a separate location on the machine with the EditLive! for IWWCM installer.
  4. Add EditLive! 6.4 to the IWWCM installer
    1. Open the EditLive! update package you downloaded and select all the files except the sample_eljconfig.xml file (we don't want to overwrite this in case you've made customizations).  
    2. Copy these files into the EDITLILVE_IWWCM/web/res/editlivejava folder and overwrite the existing files when prompted, where EDITLIVE_IWWCM folder is the root directory of your IWWCM installer.
  5. Run the installer - The installer is now ready to run.  Set the IBM Portal directory in the build.properties file in the installer's root directory if necessary and then run the install script.

You can find more information about what's in EditLive! 6.4 in the changelog.

If you have any questions or issues with the EditLive! 6.4 beta please let us know.  You can get in contact with us and the EditLive! community via the LiveWorks! mailing list.

 

Using Track Changes With Version History

There is a common misconception that EditLive's track changes functionality is intended to be a substitute for the version history built into most content management systems. In reality, the two systems work together to achieve different aims.

Version history is the corporate compliance that you need, track changes is the collaboration that you want.

So where version history precisely tracks every change so that you can ensure accountability, track changes puts users in control, giving them a tool they can use to highlight important changes that need review. One of the common problems people have with version history is that when you make formatting changes to a large section of the document, that entire section shows up as a change and smaller changes to the actual content are often missed. With track changes, the author making the change can leave track changes off while making the formatting changes and then turn it on so just the important content changes are marked as changed. This ensures the reviewer can see the important changes and consider them appropriately.

Track changes also handles multiple people collaborating on a single document at once, with each person's changes being shown in a separate color it's easy to see who changed what. While version history can reveal this information by looking at what changed in each individual revision, but track changes allows you to see that information altogether in one place. When documents go through an extended collaboration process this can be extremely useful. You can also accept and reject track changes during the collaboration process as consensus is formed and the document takes shape, again focusing authors on just the changes that really matter, regardless of which version the change was made in.

Finally, the track changes data is shown right in the editing interface so users don't have to flip between editing the document and reviewing the previous changes.

So how does track changes work with version history at the technical level? Seamlessly usually. The track changes data is stored right in the HTML so it's versioned along with the content without any changes to the CMS. Additionally, the track changes mark up is designed to be completely hidden when viewed in a browser so you can see what the intended final version of the document was without the track changes markup getting in the way. We've also documented the track changes format so you can parse it and manipulate it in any way you want (it's based on the open document format's track changes but modified to work with HTML instead of ODF).

If you want to see track changes in action, try out our demos or watch the recording of our recent quarterly product update webinar which steps through a common use case for track changes.

URL Encoding And Character Sets

Most people don't realize it, but URL encoding is actually different depending on which character set is used at the time. Characters can be corrupted if a string is URL encoded using one character set and decoded using a different character set.

Take for example the Ä character. Using the default character encoding for Windows in the US (cp1252) it's encoded as %C4, however using UTF-8 it's encoded as %C3%86. If you encode it with UTF-8 and decode it with cp1252 you actually wind up with two characters instead of one.

Most URL encoding functions unfortunately don't require a specific character set to use but instead use the character encoding for the page. JavaScript. PHP and ASP all act this way so make sure that the page's encoding is set to the same encoding as the document you're encoding. As always, check that EditLive! is using the same encoding either by specifying it in your configuration file or with a meta tag in the head of the document.

Related documents: Specifying Character Sets For Internationalization

Customizing Built-In Commands

As you're probably aware, you can customize which menu items and toolbar buttons appear in EditLive! by editing your configuration file. However, most people don't realize they can also customize how those items appear while preserving the underlying functionality.

For example, if you wanted to replace the icons so they better matched your particular application's look and feel, you could do so by specifying the imageURL attribute on any or all of the item entries in your config file. So to change the "cut" menu item to use the image file "http://example.com/myCut.gif" the configuration file element would be:

<menuItem name="cut" imageURL="http://example.com/myCut.gif" />

Similarly the toolbarButton element would be:

<toolbarButton name="cut" imageURL="http://example.com/myCut.gif" />

When the user selected one of these the standard Cut routine would still be executed and the default text for the menu item and toolbar button tooltip would still be used. For best results, make sure the image you specify is 16×16 pixels and has a transparent background.

If you wanted to change the text or tooltip, just add a text="My New Cut Text" to the menuItem and toolbarButton element respectively. It's just the same as if you were using a custom menu item or custom toolbar button except that the behaviour is already implemented for you.