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}