001
002package org.opencms.editors.tinymce;
003
004import org.opencms.file.CmsFile;
005import org.opencms.file.CmsObject;
006import org.opencms.i18n.CmsEncoder;
007import org.opencms.json.JSONException;
008import org.opencms.json.JSONObject;
009import org.opencms.main.CmsException;
010import org.opencms.main.CmsLog;
011import org.opencms.main.OpenCms;
012import org.opencms.util.CmsStringUtil;
013import org.opencms.widgets.A_CmsHtmlWidget;
014import org.opencms.widgets.CmsHtmlWidget;
015import org.opencms.widgets.CmsHtmlWidgetOption;
016import org.opencms.widgets.I_CmsWidget;
017import org.opencms.widgets.I_CmsWidgetDialog;
018import org.opencms.widgets.I_CmsWidgetParameter;
019import org.opencms.workplace.CmsWorkplace;
020import org.opencms.workplace.editors.CmsEditorDisplayOptions;
021import org.opencms.workplace.editors.CmsTinyMceToolbarHelper;
022import org.opencms.workplace.editors.I_CmsEditorCssHandler;
023import org.opencms.xml.types.I_CmsXmlContentValue;
024
025import java.io.UnsupportedEncodingException;
026import java.util.ArrayList;
027import java.util.Iterator;
028import java.util.List;
029import java.util.Properties;
030
031import org.apache.commons.logging.Log;
032
033/**
034 * The TinyMCE implementation of the HTML widget.<p>
035 */
036public class CmsTinyMCEWidget extends A_CmsHtmlWidget {
037
038    /** Path of the base content CSS. */
039    public static final String BASE_CONTENT_CSS = "/system/workplace/editors/tinymce/base_content.css";
040
041    /** Request parameter name for the tool bar configuration parameter. */
042    public static final String PARAM_CONFIGURATION = "config";
043
044    /** The log object for this class. */
045    private static final Log LOG = CmsLog.getLog(org.opencms.editors.tinymce.CmsTinyMCEWidget.class);
046
047    /**
048     * Creates a new TinyMCE widget.<p>
049     */
050    public CmsTinyMCEWidget() {
051
052        // empty constructor is required for class registration
053        this("");
054    }
055
056    /**
057     * Creates a new TinyMCE widget with the given configuration.<p>
058     *
059     * @param configuration the configuration to use
060     */
061    public CmsTinyMCEWidget(String configuration) {
062
063        super(configuration);
064    }
065
066    /**
067     * @see org.opencms.widgets.I_CmsWidget#getDialogIncludes(org.opencms.file.CmsObject, org.opencms.widgets.I_CmsWidgetDialog)
068     */
069    @Override
070    public String getDialogIncludes(CmsObject cms, I_CmsWidgetDialog widgetDialog) {
071
072        StringBuilder result = new StringBuilder(128);
073        // general TinyMCE JS
074        result.append(
075            getJSIncludeFile(CmsWorkplace.getStaticResourceUri("editors/tinymce/jscripts/tinymce/tinymce.min.js")));
076        result.append("\n");
077        result.append(
078            getJSIncludeFile(
079                OpenCms.getLinkManager().substituteLinkForRootPath(
080                    cms,
081                    "/system/workplace/editors/tinymce/opencms_plugin.js")));
082        result.append("\n");
083        // special TinyMCE widget functions
084        result.append(getJSIncludeFile(CmsWorkplace.getStaticResourceUri("components/widgets/tinymce.js")));
085        String pluginCssUri = OpenCms.getLinkManager().substituteLinkForRootPath(
086            cms,
087            "/system/workplace/editors/tinymce/opencms_plugin.css");
088        result.append("<link type='text/css' rel='stylesheet' href='" + pluginCssUri + "'>");
089        String cssUri = CmsWorkplace.getStaticResourceUri("components/widgets/tinymce.css");
090        result.append("<link type='text/css' rel='stylesheet' href='" + cssUri + "'>");
091        return result.toString();
092    }
093
094    /**
095     * @see org.opencms.widgets.I_CmsWidget#getDialogWidget(org.opencms.file.CmsObject, org.opencms.widgets.I_CmsWidgetDialog, org.opencms.widgets.I_CmsWidgetParameter)
096     */
097    public String getDialogWidget(CmsObject cms, I_CmsWidgetDialog widgetDialog, I_CmsWidgetParameter param) {
098
099        String id = param.getId();
100        String value = param.getStringValue(cms);
101        StringBuilder result = new StringBuilder();
102
103        result.append("<td class=\"cmsTinyMCE xmlTd\">");
104
105        result.append("<textarea class=\"xmlInput maxwidth\" name=\"ta_");
106        result.append(id);
107        result.append("\" id=\"ta_");
108        result.append(id);
109        result.append("\" style=\"");
110        result.append("\" rows=\"20\" cols=\"60\">");
111        result.append(CmsEncoder.escapeXml(value));
112        result.append("</textarea>");
113        result.append("<input type=\"hidden\" name=\"");
114        result.append(id);
115        result.append("\" id=\"");
116        result.append(id);
117        result.append("\" value=\"");
118        result.append(CmsEncoder.encode(value));
119        result.append("\">");
120
121        result.append("<script >\n");
122
123        result.append("initTinyMCE(").append(getTinyMceConfiguration(cms, param)).append(");\n");
124        result.append("contentFields[contentFields.length] = document.getElementById(\"").append(id).append("\");\n");
125        result.append("</script>\n");
126        result.append("</td>");
127
128        return result.toString();
129    }
130
131    /**
132     * @see org.opencms.widgets.I_CmsWidget#newInstance()
133     */
134    public I_CmsWidget newInstance() {
135
136        return new CmsTinyMCEWidget(getConfiguration());
137    }
138
139    /**
140     * Returns the string representation of the tinyMCE options object.<p>
141     *
142     * @param cms the OpenCms context
143     * @param param the widget parameter
144     *
145     * @return the string representation of the tinyMCE options object
146     */
147    private String getTinyMceConfiguration(CmsObject cms, I_CmsWidgetParameter param) {
148
149        JSONObject result = new JSONObject();
150        CmsEditorDisplayOptions options = OpenCms.getWorkplaceManager().getEditorDisplayOptions();
151        Properties displayOptions = options.getDisplayOptions(cms);
152        CmsHtmlWidgetOption optionBean = parseWidgetOptions(cms);
153        try {
154            result.put("elements", "ta_" + param.getId());
155            String editorHeight = optionBean.getEditorHeight();
156            if (CmsStringUtil.isNotEmptyOrWhitespaceOnly(editorHeight)) {
157                editorHeight = editorHeight.replaceAll("px", "");
158                result.put("height", editorHeight);
159            }
160            if (options.showElement("gallery.enhancedoptions", displayOptions)) {
161                result.put("cmsGalleryEnhancedOptions", true);
162            }
163            if (options.showElement("gallery.usethickbox", displayOptions)) {
164                result.put("cmsGalleryUseThickbox", true);
165            }
166            Boolean pasteText = Boolean.valueOf(
167                OpenCms.getWorkplaceManager().getWorkplaceEditorManager().getEditorParameter(
168                    cms,
169                    "tinymce",
170                    "paste_text"));
171            result.put("paste_as_text", pasteText);
172
173            result.put("fullpage", optionBean.isFullPage());
174            result.merge(getToolbarJson(cms), true, false);
175
176            result.put("language", OpenCms.getWorkplaceManager().getWorkplaceLocale(cms).getLanguage());
177            // set CSS style sheet for current editor widget if configured
178            boolean cssConfigured = false;
179            String cssPath = "";
180            if (optionBean.useCss()) {
181                cssPath = optionBean.getCssPath();
182                // set the CSS path to null (the created configuration String passed to JS will not include this path then)
183                optionBean.setCssPath(null);
184                cssConfigured = true;
185            } else if (OpenCms.getWorkplaceManager().getEditorCssHandlers().size() > 0) {
186                Iterator<I_CmsEditorCssHandler> i = OpenCms.getWorkplaceManager().getEditorCssHandlers().iterator();
187                try {
188                    // cast parameter to I_CmsXmlContentValue
189                    I_CmsXmlContentValue contentValue = (I_CmsXmlContentValue)param;
190                    // now extract the absolute path of the edited resource
191                    CmsFile editedResource = contentValue.getDocument().getFile();
192                    String editedResourceSitePath = editedResource == null ? null : cms.getSitePath(editedResource);
193                    while (i.hasNext()) {
194                        I_CmsEditorCssHandler handler = i.next();
195                        if (handler.matches(cms, editedResourceSitePath)) {
196                            cssPath = handler.getUriStyleSheet(cms, editedResourceSitePath);
197                            if (CmsStringUtil.isNotEmptyOrWhitespaceOnly(cssPath)) {
198                                cssConfigured = true;
199                            }
200                            break;
201                        }
202                    }
203                } catch (Exception e) {
204                    // ignore, CSS could not be set
205                    LOG.debug(e.getLocalizedMessage(), e);
206                }
207            }
208
209            List<String> contentCssLinks = new ArrayList<String>();
210            contentCssLinks.add(OpenCms.getLinkManager().substituteLink(cms, BASE_CONTENT_CSS));
211            if (cssConfigured) {
212                contentCssLinks.add(OpenCms.getLinkManager().substituteLink(cms, cssPath));
213            }
214            result.put("content_css", CmsStringUtil.listAsString(contentCssLinks, ","));
215            if (optionBean.showStylesFormat()) {
216                try {
217                    CmsFile file = cms.readFile(optionBean.getStylesFormatPath());
218                    String characterEncoding = OpenCms.getSystemInfo().getDefaultEncoding();
219                    result.put("style_formats", new String(file.getContents(), characterEncoding));
220                } catch (CmsException cmsException) {
221                    LOG.error("Can not open file:" + optionBean.getStylesFormatPath(), cmsException);
222                } catch (UnsupportedEncodingException ex) {
223                    LOG.error(ex);
224                }
225            }
226            String formatSelectOptions = optionBean.getFormatSelectOptions();
227            if (!CmsStringUtil.isEmpty(formatSelectOptions)
228                && !optionBean.isButtonHidden(CmsHtmlWidgetOption.OPTION_FORMATSELECT)) {
229                result.put("block_formats", CmsHtmlWidget.getTinyMceBlockFormats(formatSelectOptions));
230            }
231            result.put("entity_encoding", "named");
232            result.put("entities", "160,nbsp");
233        } catch (JSONException e) {
234            LOG.error(e.getLocalizedMessage(), e);
235        }
236
237        return result.toString();
238    }
239
240    /**
241     * Builds the toolbar rows.<p>
242     *
243     * @return the toolbar button rows configuration
244     *
245     * @throws JSONException if something goes wrong manipulating the JSON object
246     */
247    private JSONObject getToolbarJson(CmsObject cms) throws JSONException {
248
249        JSONObject result = new JSONObject();
250        List<String> barItems = parseWidgetOptions(cms).getButtonBarShownItems();
251        String toolbar = CmsTinyMceToolbarHelper.createTinyMceToolbarStringFromGenericToolbarItems(barItems);
252        result.put("toolbar", toolbar);
253        String contextmenu = CmsTinyMceToolbarHelper.getContextMenuEntries(barItems);
254        if (CmsStringUtil.isNotEmptyOrWhitespaceOnly(contextmenu)) {
255            result.put("contextmenu", contextmenu);
256        }
257        return result;
258    }
259}