The localized rich text editor

I'm working in an awesome component to be shipped from Liferay 6.2 onwards. Some of the features needed to implement it are rather reusable so they were implemented in the Liferay Porta itself. One of them is the new ability of the <liferay-ui:input-localized> tag of inserting a WYSIWYG editor in the page instead of an one-line input or a textarea.

How to use it

To use it, just set the type parameter of the <liferay-ui:input-localized> tag to editor, as below (source):

<portlet:renderURL var="savePostURL" />

<form action="<%= savePostURL %>" method="post">
    <liferay-ui:input-localized name="localizedEditor" xml="" type="editor" />
    <input type="submit" value="Submit post">
</form>

(Do not forget to put it inside a <form> element; it is mandatory to the <liferay-ui:input-localized> tag regardless of the type.)

Your portlet will be presented as below:

It works as any localized input: click on the flag corresponding to the language you want to translate to. The content of the editor will be cleared and you can enter your translation. If you click in a flag of a language which already has some content, the content will be shown in the editor.

To retrieve the value one can just use the usual steps of dealing with localized inputs. For example, if we want to present the translations inserted, we can use the LocalizationUtil class to retrieve the map with the submitted values. With the map, we can create e.g. a list of values, as in the lines below (source):

<ul>
<%
Map<Locale, String> translations = LocalizationUtil.getLocalizationMap(
    renderRequest, "localizedEditor");

for (Locale locale : translations.keySet()) {
    String translation = translations.get(locale);
    
        if (Validator.isNotNull(translation)) {
        %>
            <li>
                    In <%= locale.getDisplayLanguage() %>:
                    <div>
                        <%= translation %>
                    </div>
            </li>
        <%
        }
}
%>
</ul>

The result could be something like this:

A new model hint

The new feature, however, shows all of its awesomeness when dealing with AlloyUI Forms and the Service Builder because we can change the model hint  of a field to use a localized rich-text editor as its input. To see it at use, let us create a new Service Builder entity named Post (source):

    <entity name="Post">
        <column name="postId" type="long" primary="true" />

        <column name="title" type="String" localized="true" />
        <column name="content" type="String" localized="true" />
    </entity>

Once you generate the service files, you can edit the model hint of the content column at the file docroot/WEB-INF/src/META-INF/portlet-model-hint.xml to use the new EDITOR hint collection. Replace this line...

<field name="content" type="String" localized="true" />

by these lines (source - diff):

        <field name="content" type="String" localized="true">
    <hint-collection name="EDITOR" />
        </field>

Snippets for testing

For testing purposes, we can create a little form to edit it:

	<%
long postId = ParamUtil.getLong(renderRequest, "postId");
Post post = null;
if (postId > 0) {
    post = PostLocalServiceUtil.getPost(postId);
}
%>

<portlet:actionURL name="savePost" var="savePostURL" />

<aui:form action="<%= savePostURL %>" method="post">
    <aui:fieldset>
        <aui:model-context model="<%= Post.class %>" bean="<%= post %>" />    

        <aui:input name="postId" type="hidden" />

        <aui:input name="title" />
	        <aui:input name="content" />

        <aui:button name="submit" type="submit" />
    </aui:fieldset>
</aui:form>

We also create a portlet to process the action request (source)...

public class PostPortlet extends MVCPortlet {

    public void savePost(ActionRequest request, ActionResponse response) 
    			    throws PortalException, SystemException {
        		long postId = ParamUtil.getLong(request, "postId");

        		Map title =
                				LocalizationUtil.getLocalizationMap(request, "title");
        Map content =
				                LocalizationUtil.getLocalizationMap(request, "content");

        		Post post = (postId > 0) 
                				? PostLocalServiceUtil.getPost(postId) 
                				: new PostImpl();

        		post.setTitleMap(title);
        		post.setContentMap(content);

		        if (postId > 0) {
            post.setPostId(postId);
            			PostLocalServiceUtil.updatePost(post);
        }
        else {
            			post.setPostId(CounterLocalServiceUtil.increment(Post.class.getName()));
            			PostLocalServiceUtil.addPost(post);
        }
    }
}

...as well as some code to list the saved values (source):

<ul>
<%
List<Post> posts = PostLocalServiceUtil.getPosts(0, QueryUtil.ALL_POS);

for (Post curPost : posts) {
%>
    <portlet:renderURL var="postURL">
                <portlet:param name="postId" value="<%= String.valueOf(curPost.getPostId()) %>"/>
        </portlet:renderURL>
        <li>
                <a href="<%= postURL %>"><%= curPost.getTitle(LocaleUtil.getDefault()) %></a>
        </li>
    <%
}
%>
</ul>

Now, the input for the content will not be a line input or a textarea solely - it will be a localized rich text editor! After some input, for example, our portlet can look like this:

Summing up

This feature is not available on the bundles yet - it is coming in Liferay 6.2. Nonetheless, one can anticipate its usefulness. We hope it will make your lives easier! In fact, all model hints are pretty useful while not really widely known. Also, note that this feature allows one to define the input of a model field as a non-localized rich-text editor if one wants to.

As usual, the full source code is available at my repository of examples as the simple-localized-editor-portlet plugin.

Blogs
How do you resize the editor box? It is resisting all CSS overrides I try.
btw, I thought I would share my code that worked. I'm on 6.2 fyi.

First my portlet-model-hints.xml

<field name="paragraph1" type="String">
<hint name="max-length">5000</hint>
<hint name="editor">true</hint>
</field>

And now my JSP file snippet:

<aui:form action="<%= editContentURL %>" method="post">
<aui:fieldset>
<aui:model-context bean="<%= pc %>" model="<%= PageContent.class %>" />

<aui:input name="pageContentId" type="hidden" />

<aui:input name="title" label="" placeholder="title text" value="<%= title %>"/>

<aui:input name="subtitle1" label="" placeholder="subtitle text" value="<%= subtitle1 %>" />

<aui:input name="paragraph1" label="" placeholder="test" value="<%= paragraph1 %>" cssClass="editorAttrs" />

<aui:button name="submit" type="submit" />
</aui:fieldset>
</aui:form>
If the default language of the portal is set to something other than en_US, the localization seems to fail at least in 6.2sp14. If its fr_FR for example, any value input for FR gets saved into the US node in the translations XML.

Does the option of customizing the CKEditor config.js already exist? I haven't seen it but the screenshots reflect an edited editor instance. Specifically ACF settings would be Great to configure.