info.magnolia.cms.gui.dialog.DialogFckEdit.java Source code

Java tutorial

Introduction

Here is the source code for info.magnolia.cms.gui.dialog.DialogFckEdit.java

Source

/**
 *
 * Magnolia and its source-code is licensed under the LGPL.
 * You may copy, adapt, and redistribute this file for commercial or non-commercial use.
 * When copying, adapting, or redistributing this document in keeping with the guidelines above,
 * you are required to provide proper attribution to obinary.
 * If you reproduce or distribute the document without making any substantive modifications to its content,
 * please use the following attribution line:
 *
 * Copyright 1993-2006 obinary Ltd. (http://www.obinary.com) All rights reserved.
 *
 */
package info.magnolia.cms.gui.dialog;

import info.magnolia.cms.core.Content;
import info.magnolia.cms.gui.control.ControlImpl;
import info.magnolia.cms.util.LinkUtil;
import info.magnolia.context.MgnlContext;

import java.io.IOException;
import java.io.Writer;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.jcr.RepositoryException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * An Magnolia dialog for the universal usage and configuration of the fckeditor. Credits for FCKEditor:
 * http://www.fckeditor.net/<p/> The fckEditor is mainly configured in javascript files. Those files are configured
 * with the following attributes.
 * <ul>
 * <li>jsInitFile</li>
 * <li>customConfigurationPath</li>
 * </ul>
 * Where the default values are:
 * <ul>
 * <li>/admindocroot/fckeditor/custom/init/magnoliaStandard.js</li>
 * <li>/admindocroot/fckeditor/custom/config/magnoliaStandard.js</li>
 * </ul>
 * To make live simple we provide some attributes to configure the control in the magnolia configuration instead within
 * the javascript files. <table>
 * <tr>
 * <td>css</td>
 * <td>The css file to use. Default is /admindocroot/fckeditor/custom/css/magnoliaStandard.css </td>
 * </tr>
 * <tr>
 * <td>height</td>
 * <td>The height of the editor.</td>
 * </tr>
 * <tr>
 * <td>tables</td>
 * <td>The table editing features are available if true</td>
 * </tr>
 * <tr>
 * <td>lists</td>
 * <td>The list features are available if true</td>
 * </tr>
 * <tr>
 * <td>aligment</td>
 * <td>The aligment features are available if true</td>
 * </tr>
 * <tr>
 * <td>images</td>
 * <td>The image editing features including upload are available if true</td>
 * </tr>
 * <tr>
 * <td>fileUpload</td>
 * <td>The file upload features is enabled if true</td>
 * </tr>
 * <tr>
 * <td>styles</td>
 * <td>Defines the xml file defining the used styles. See
 * http://wiki.fckeditor.net/Developer%27s_Guide/Configuration/Styles for details</td>
 * </tr>
 * <tr>
 * <td>templates</td>
 * <td>Defines the xml file defining the used templates. See
 * http://wiki.fckeditor.net/Developer%27s_Guide/Configuration/Templates for details</td>
 * </tr>
 * <tr>
 * <td>fonts</td>
 * <td>A semicolon separated list of font names.</td>
 * </tr>
 * <tr>
 * <td>fontSizes</td>
 * <td>A semicolon separated list of font sizes</td>
 * </tr>
 * <tr>
 * <tr>
 * <td>colors</td>
 * <td>A comma separated list of colors. hex values without #.</td>
 * </tr>
 * <tr>
 * <td>source</td>
 * <td>Show the source button</td>
 * </tr>
 * </table>
 * @author bert schulzki
 * @author Fabrizio Giustina
 * @version
 */
public class DialogFckEdit extends DialogBox {

    /**
     * Logger.
     */
    private static Logger log = LoggerFactory.getLogger(DialogFckEdit.class);

    /**
     * The new .BasePath of the editor
     */
    public static final String FCKEDIT_PATH = "/.resources/fckeditor/"; //$NON-NLS-1$

    /**
     * Used to make sure that the javascript files are loaded only once
     */
    private static final String ATTRIBUTE_FCKED_LOADED = "info.magnolia.cms.gui.dialog.fckedit.loaded";

    /**
     * This parameter defines the startup script. This parameter is searched in the dialog configuration.
     */
    public static final String PARAM_JS_INIT_FILE = "jsInitFile"; //$NON-NLS-1$

    /**
     * This parameter defines the configuration script
     */
    public static final String PARAM_CUSTOM_CONFIGURATION_PATH = "jsConfigFile"; //$NON-NLS-1$

    public static final String PARAM_CSS = "css"; //$NON-NLS-1$

    public static final String PARAM_HEIGHT = "height"; //$NON-NLS-1$

    public static final String PARAM_TABLES = "tables"; //$NON-NLS-1$

    private static final String PARAM_LISTS = "lists";

    private static final String PARAM_ALIGNMENT = "alignment";

    public static final String PARAM_IMAGES = "images"; //$NON-NLS-1$

    public static final String PARAM_STYLES = "styles"; //$NON-NLS-1$

    public static final String PARAM_TEMPLATES = "templates"; //$NON-NLS-1$

    public static final String PARAM_FONTS = "fonts"; //$NON-NLS-1$

    public static final String PARAM_FONT_SIZES = "fontSizes"; //$NON-NLS-1$

    private static final String PARAM_COLORS = "colors";

    public static final String PARAM_SOURCE = "source"; //$NON-NLS-1$

    /**
     * Default falues
     */
    public static final String PARAM_JS_INIT_FILE_DEFAULT = "/.resources/fckeditor/custom/init/magnoliaStandard.js"; //$NON-NLS-1$

    public static final String PARAM_CUSTOM_CONFIGURATION_PATH_DEFAULT = "/.resources/fckeditor/custom/config/magnoliaStandard.js"; //$NON-NLS-1$

    public static final String PARAM_CSS_DEFAULT = "/.resources/fckeditor/custom/css/magnoliaStandard.css"; //$NON-NLS-1$

    public static final String PARAM_HEIGHT_DEFAULT = ""; //$NON-NLS-1$

    public static final String PARAM_TABLES_DEFAULT = "false"; //$NON-NLS-1$

    public static final String PARAM_IMAGES_DEFAULT = "false"; //$NON-NLS-1$

    public static final String PARAM_STYLES_DEFAULT = ""; //$NON-NLS-1$

    public static final String PARAM_TEMPLATES_DEFAULT = ""; //$NON-NLS-1$

    public static final String PARAM_FONTS_DEFAULT = ""; //$NON-NLS-1$

    public static final String PARAM_FONT_SIZES_DEFAULT = ""; //$NON-NLS-1$

    public static final String PARAM_SOURCE_DEFAULT = "false"; //$NON-NLS-1$

    private static final String PARAM_COLORS_DEFAULT = "";

    private static final String PARAM_LISTS_DEFAULT = "true";

    private static final String PARAM_ALIGNMENT_DEFAULT = "false";

    /**
     * Empty constructor should only be used by DialogFactory.
     */
    protected DialogFckEdit() {
    }

    /**
     * @return The name of the variable for the editor object
     */
    public String getVarName() {
        String id = getId();
        if (id == null) {
            id = getName();
        }
        return "fck_" + id.replace('-', '_'); //$NON-NLS-1$
    }

    /**
     * @see info.magnolia.cms.gui.dialog.DialogControl#init(HttpServletRequest, HttpServletResponse, Content, Content)
     */
    public void init(HttpServletRequest request, HttpServletResponse response, Content websiteNode,
            Content configNode) throws RepositoryException {
        super.init(request, response, websiteNode, configNode);
    }

    /**
     * @see info.magnolia.cms.gui.dialog.DialogControl#drawHtml(Writer)
     */
    public void drawHtml(Writer out) throws IOException {
        // get the config values
        String jsInitFile = this.getConfigValue(PARAM_JS_INIT_FILE, PARAM_JS_INIT_FILE_DEFAULT);
        String customConfigurationPath = this.getConfigValue(PARAM_CUSTOM_CONFIGURATION_PATH,
                this.getConfigValue("customConfigurationPath", PARAM_CUSTOM_CONFIGURATION_PATH_DEFAULT));
        String height = this.getConfigValue(PARAM_HEIGHT, PARAM_HEIGHT_DEFAULT);

        this.drawHtmlPre(out);

        // load the script onece: if there are multiple instances
        if (getRequest().getAttribute(ATTRIBUTE_FCKED_LOADED) == null) {
            out.write("<script type=\"text/javascript\" src=\"" //$NON-NLS-1$
                    + this.getRequest().getContextPath() + "/.resources/fckeditor/fckeditor.js\"></script>"); //$NON-NLS-1$
            getRequest().setAttribute(ATTRIBUTE_FCKED_LOADED, "true"); //$NON-NLS-1$ 
        }

        String id = getName();

        if (id == null) {
            log.error("Missing id for fckEditor instance"); //$NON-NLS-1$
        }

        String var = getVarName();
        String value = convertToView(getValue());
        out.write("<script type=\"text/javascript\">"); //$NON-NLS-1$

        // make the configuration accessible to the config javascript
        writeMgnlFCKConfig(out, id);

        out.write("var " + var + " = null;"); //$NON-NLS-1$ //$NON-NLS-2$
        out.write("fckInstance = new FCKeditor( '" + id + "' );"); //$NON-NLS-1$ //$NON-NLS-2$
        out.write("fckInstance.Value = '" + escapeJsValue(value) + "';"); //$NON-NLS-1$ //$NON-NLS-2$
        out.write("fckInstance.BasePath = '" + this.getRequest().getContextPath() + FCKEDIT_PATH + "';"); //$NON-NLS-1$ //$NON-NLS-2$

        if (StringUtils.isNotEmpty(height)) {
            out.write("fckInstance.Height = '" + escapeJsValue(this.getConfigValue("height")) + "';"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
        }

        // now set the custom configuration path
        if (StringUtils.isNotEmpty(customConfigurationPath)) {
            out.write("fckInstance.Config['CustomConfigurationsPath'] = '" //$NON-NLS-1$
                    + this.getRequest().getContextPath() + customConfigurationPath + "';"); //$NON-NLS-1$
        }

        // here we pass the parameters to the custom configuration file --> via
        // javascript

        // start the initfile
        if (jsInitFile.length() > 0) {
            out.write("</script>"); //$NON-NLS-1$
            out.write("<script type=\"text/javascript\" src=\"" //$NON-NLS-1$
                    + this.getRequest().getContextPath() + jsInitFile + "\"></script>\n"); //$NON-NLS-1$
            out.write("<script type=\"text/javascript\">"); //$NON-NLS-1$
        }

        // finaly create the editor
        out.write("fckInstance.Create();"); //$NON-NLS-1$
        out.write(var + " = fckInstance;"); //$NON-NLS-1$
        out.write("</script>"); //$NON-NLS-1$

        // write the saveInfo for the writing back to the repository
        out.write("<input type='hidden' name='mgnlSaveInfo' value='" //$NON-NLS-1$
                + id + ",String," //$NON-NLS-1$
                + ControlImpl.VALUETYPE_SINGLE + "," //$NON-NLS-1$
                + ControlImpl.RICHEDIT_FCK + "," //$NON-NLS-1$
                + ControlImpl.ENCODING_NO + "' />"); //$NON-NLS-1$

        this.drawHtmlPost(out);
    }

    private void writeMgnlFCKConfig(Writer out, String id) throws IOException {
        String css = this.getConfigValue(PARAM_CSS, PARAM_CSS_DEFAULT);
        String fonts = this.getConfigValue(PARAM_FONTS, PARAM_FONTS_DEFAULT);
        String fontSizes = this.getConfigValue(PARAM_FONT_SIZES, PARAM_FONT_SIZES_DEFAULT);
        String colors = this.getConfigValue(PARAM_COLORS, PARAM_COLORS_DEFAULT);
        String styles = this.getConfigValue(PARAM_STYLES, PARAM_STYLES_DEFAULT);
        String templates = this.getConfigValue(PARAM_TEMPLATES, PARAM_TEMPLATES_DEFAULT);

        String lists = this.getConfigValue(PARAM_LISTS, PARAM_LISTS_DEFAULT);
        String alignment = this.getConfigValue(PARAM_ALIGNMENT, PARAM_ALIGNMENT_DEFAULT);
        String tables = this.getConfigValue(PARAM_TABLES, PARAM_TABLES_DEFAULT);
        String images = this.getConfigValue(PARAM_IMAGES, PARAM_IMAGES_DEFAULT);
        String source = this.getConfigValue(PARAM_SOURCE, PARAM_SOURCE_DEFAULT);

        // create the the holder of the editors configs if not yet done
        out.write("if( window.MgnlFCKConfigs == null)\n");
        out.write("    window.MgnlFCKConfigs = new Object();\n");

        // add the config for this editor

        out.write("MgnlFCKConfigs." + id + " = new Object();\n");
        // string values
        out.write("MgnlFCKConfigs." + id + ".language = '" + MgnlContext.getUser().getLanguage() + "';\n");
        out.write("MgnlFCKConfigs." + id + ".contextPath = '" + getRequest().getContextPath() + "';\n");

        out.write(
                "MgnlFCKConfigs." + id + ".repository = '" + getTopParent().getConfigValue("repository") + "';\n");
        out.write("MgnlFCKConfigs." + id + ".path = '" + getTopParent().getConfigValue("path") + "';\n");
        out.write("MgnlFCKConfigs." + id + ".nodeCollection = '" + getTopParent().getConfigValue("nodeCollection")
                + "';\n");
        out.write("MgnlFCKConfigs." + id + ".node = '" + getTopParent().getConfigValue("node") + "';\n");

        out.write("MgnlFCKConfigs." + id + ".css = '" + css + "';\n");
        out.write("MgnlFCKConfigs." + id + ".fonts = '" + fonts + "';\n");
        out.write("MgnlFCKConfigs." + id + ".fontSizes = '" + fontSizes + "';\n");
        out.write("MgnlFCKConfigs." + id + ".colors = '" + colors + "';\n");
        out.write("MgnlFCKConfigs." + id + ".styles = '" + styles + "';\n");
        out.write("MgnlFCKConfigs." + id + ".templates = '" + templates + "';\n");

        // boolean values
        out.write("MgnlFCKConfigs." + id + ".lists = " + lists + ";\n");
        out.write("MgnlFCKConfigs." + id + ".alignment = " + alignment + ";\n");
        out.write("MgnlFCKConfigs." + id + ".tables = " + tables + ";\n");
        out.write("MgnlFCKConfigs." + id + ".images = " + images + ";\n");
        out.write("MgnlFCKConfigs." + id + ".source = " + source + ";\n");
    }

    /**
     * @param value
     * @return
     */
    private String convertToView(String value) {
        String tmp = value;
        if (tmp != null) {
            tmp = tmp.replaceAll("\r\n", "<br />"); //$NON-NLS-1$ //$NON-NLS-2$
            tmp = tmp.replaceAll("\n", "<br />"); //$NON-NLS-1$ //$NON-NLS-2$

            value = LinkUtil.convertUUIDsToAbsoluteLinks(value);

            Pattern imagePattern = Pattern.compile("(<(a|img)[^>]+(src|href)[ ]*=[ ]*\")([^\"]*)(\"[^>]*>)"); //$NON-NLS-1$

            Matcher matcher = imagePattern.matcher(value);
            StringBuffer res = new StringBuffer();
            while (matcher.find()) {
                String src = matcher.group(4);

                // process only internal and relative links
                if (!Pattern.matches("^\\w*://.*", src) && !src.startsWith("/")) {
                    String link = this.getRequest().getContextPath() + this.getTopParent().getConfigValue("path")
                            + "/" + StringUtils.substringAfter(src, "/");

                    matcher.appendReplacement(res, "$1" + link + "$5"); //$NON-NLS-1$
                }
            }
            matcher.appendTail(res);
            return res.toString();
        }

        return StringUtils.EMPTY;
    }

    /**
     * Replacements:
     * 
     * <pre>
     *               ' -&gt; \'
     *               &quot; -&gt; \&quot;
     *               \r\n -&gt; \\r\\n
     *               \n -&gt; \\n
     *               \ -&gt; \\
     * </pre>
     * 
     * @param src
     * @return escaped js String
     */
    public static String escapeJsValue(String src) {
        if (src == null) {
            return null;
        }
        String escapedSrc = src.replaceAll("'", "\\\\'"); //$NON-NLS-1$ //$NON-NLS-2$
        escapedSrc = escapedSrc.replaceAll("\"", "\\\""); //$NON-NLS-1$ //$NON-NLS-2$
        escapedSrc = escapedSrc.replaceAll("\\r\\n", "\\\\r\\\\n"); //$NON-NLS-1$ //$NON-NLS-2$
        escapedSrc = escapedSrc.replaceAll("\\n", "\\\\n"); //$NON-NLS-1$ //$NON-NLS-2$

        return escapedSrc;
    }
}