View Javadoc

1   /*
2    * FCKeditor - The text editor for Internet - http://www.fckeditor.net
3    * Copyright (C) 2004-2009 Frederico Caldeira Knabben
4    * 
5    * == BEGIN LICENSE ==
6    * 
7    * Licensed under the terms of any of the following licenses at your
8    * choice:
9    * 
10   *  - GNU General Public License Version 2 or later (the "GPL")
11   *    http://www.gnu.org/licenses/gpl.html
12   * 
13   *  - GNU Lesser General Public License Version 2.1 or later (the "LGPL")
14   *    http://www.gnu.org/licenses/lgpl.html
15   * 
16   *  - Mozilla Public License Version 1.1 or later (the "MPL")
17   *    http://www.mozilla.org/MPL/MPL-1.1.html
18   * 
19   * == END LICENSE ==
20   */
21  package net.fckeditor;
22  
23  import javax.servlet.http.HttpServletRequest;
24  
25  import net.fckeditor.handlers.PropertiesLoader;
26  import net.fckeditor.tool.Compatibility;
27  import net.fckeditor.tool.Utils;
28  import net.fckeditor.tool.XHtmlTagTool;
29  
30  /**
31   * Java representation of the <a href="http://docs.fckeditor.net/FCKeditor_2.x/Developers_Guide/Server_Side_Integration#FCKeditor_Creator"
32   * target="_blank">FCKeditor</a>. This representation reflects the editor in an
33   * object-oriented way. It can be configured as any other JavaBean type class.
34   * The final output of this class is HTML code.<br />
35   * <strong>Note:</strong> It's your responsibility to supply reasonable and
36   * valid values, only {@code request} and {@code instanceName} will be checked
37   * for sanity.
38   * 
39   * @version $Id: FCKeditor.java 3832 2009-07-08 17:44:17Z mosipov $
40   */
41  public class FCKeditor {
42  
43  	private FCKeditorConfig fckConfig = new FCKeditorConfig();
44  	private String instanceName;
45  	private HttpServletRequest request;
46  
47  	// defaults
48  	private String value = Utils.EMPTY_STRING;
49  	private String toolbarSet = PropertiesLoader.getEditorToolbarSet();
50  	private String width = PropertiesLoader.getEditorWidth();
51  	private String height = PropertiesLoader.getEditorHeight();
52  	private String basePath = PropertiesLoader.getEditorBasePath();
53  
54  	/**
55  	 * Class constructor with all basic parameters.
56  	 * 
57  	 * A constructors which handles basic FCKeditor initialization with a few
58  	 * parameters. If you omit basic parameters, default ones will be taken from
59  	 * the {@link PropertiesLoader properties file}.
60  	 * 
61  	 * @param request
62  	 *            current user request instance
63  	 * @param instanceName
64  	 *            the unique name of this editor
65  	 * @param width
66  	 *            the desired editor width (CSS-style value)
67  	 * @param height
68  	 *            the desired editor height (CSS-style value)
69  	 * @param toolbarSet
70  	 *            the desired toolbar set name
71  	 * @param value
72  	 *            the HTML markup of this editor. Markup will be properly
73  	 *            escaped.
74  	 * @param basePath
75  	 *            the base path of this editor, absolute to the context
76  	 * @throws IllegalArgumentException
77  	 *             if instanceName is empty or not a valid XHTML id
78  	 */
79  	public FCKeditor(final HttpServletRequest request,
80  			final String instanceName, final String width, final String height,
81  			final String toolbarSet, final String value, final String basePath) {
82  
83  		this(request, instanceName);
84  		this.width = width;
85  		this.height = height;
86  		this.toolbarSet = toolbarSet;
87  		this.value = value;
88  		this.basePath = basePath;
89  
90  	}
91  
92  	/**
93  	 * Class constructor with a minimal set of parameters.
94  	 * 
95  	 * Omitted parameters will be set to default values.
96  	 * 
97  	 * @param request
98  	 *            current user request instance
99  	 * @param instanceName
100 	 *            the unique name of this editor
101 	 * @throws IllegalArgumentException
102 	 *             if instanceName is empty or not a valid HTML id
103 	 */
104 	public FCKeditor(final HttpServletRequest request, final String instanceName) {
105 
106 		if (request == null)
107 			throw new NullPointerException("the request cannot be null");
108 		this.request = request;
109 
110 		setInstanceName(instanceName);
111 
112 	}
113 
114 	/**
115 	 * Sets the unique name of this editor.
116 	 * 
117 	 * @param instanceName
118 	 *            the unique name of this editor
119 	 * @throws IllegalArgumentException
120 	 *             if instanceName is empty or not a valid XHTML id
121 	 */
122 	public void setInstanceName(final String instanceName) {
123 		if (Utils.isEmpty(instanceName))
124 			throw new IllegalArgumentException("instanceName cannot be empty");
125 		if (!instanceName.matches("\\p{Alpha}[\\p{Alnum}:_.-]*"))
126 			throw new IllegalArgumentException(
127 					"instanceName must be a valid XHTML id containing only \"\\p{Alpha}[\\p{Alnum}:_.-]*\"");
128 		this.instanceName = instanceName;
129 	}
130 
131 	/**
132 	 * Sets the initial value to be edited as HTML markup.
133 	 * 
134 	 * @param value
135 	 *            the HTML markup of this editor. Markup will be properly
136 	 *            escaped.
137 	 */
138 	public void setValue(final String value) {
139 		this.value = value;
140 	}
141 
142 	/**
143 	 * Sets the base path of this editor. The base path reflects the location of
144 	 * the editor files absolute to the context root <i>not</i> the server root.
145 	 * 
146 	 * @param basePath
147 	 *            the base path of this editor, absolute to the context
148 	 */
149 	public void setBasePath(final String basePath) {
150 		this.basePath = basePath;
151 	}
152 
153 	/**
154 	 * Sets the name of the toolbar set of this editor.
155 	 * 
156 	 * @param toolbarSet
157 	 *            the desired toolbar set name
158 	 */
159 	public void setToolbarSet(final String toolbarSet) {
160 		this.toolbarSet = toolbarSet;
161 	}
162 
163 	/**
164 	 * Sets the width of this editor. This value can be any valid CSS width
165 	 * value.
166 	 * 
167 	 * @param width
168 	 *            the desired editor width (CSS-style value)
169 	 */
170 	public void setWidth(final String width) {
171 		this.width = width;
172 	}
173 
174 	/**
175 	 * Sets the height of this editor. This value can be any valid CSS height
176 	 * value.
177 	 * 
178 	 * @param height
179 	 *            the desired editor height (CSS-style value)
180 	 */
181 	public void setHeight(final String height) {
182 		this.height = height;
183 	}
184 
185 	/**
186 	 * Gets the advanced configuration map. Each configuration element has to be
187 	 * set individually in this map.<br />
188 	 * The editor provides already a system-wide configuration through the
189 	 * <code>config.js</code> file. By adding elements to this map you can
190 	 * override the configuration for each editor instance.
191 	 * 
192 	 * @deprecated Method will be removed in FCKeditor.Java 2.6, use
193 	 *             {@link FCKeditor#getConfig(String)}.
194 	 * @see #getConfig(String)
195 	 * @return configuration configuration map for this editor instance
196 	 */
197 	@Deprecated
198 	public FCKeditorConfig getConfig() {
199 		return fckConfig;
200 	}
201 
202 	/**
203 	 * Returns a configuration option. See {@link FCKeditorConfig} for more
204 	 * details.
205 	 * 
206 	 * @param name
207 	 *            the name of the parameter (case-sensitive)
208 	 * @return the value represented by this parameter, else null
209 	 */
210 	public String getConfig(String name) {
211 		return fckConfig.get(name);
212 	}
213 
214 	/**
215 	 * Sets a configuration option. See {@link FCKeditorConfig} for more
216 	 * details.
217 	 * 
218 	 * @param name
219 	 *            the name of the config option (case-sensitive)
220 	 * @param value
221 	 *            the value of the config option. Null values will be ignored.
222 	 */
223 	public void setConfig(String name, String value) {
224 		if (value != null)
225 			fckConfig.put(name, value);
226 	}
227 
228 	/**
229 	 * Sets the advanced configuration maps. <strong>Note:</strong> previously
230 	 * 
231 	 * @deprecated Method will be removed in FCKeditor.Java 2.6, use
232 	 *             {@link #setConfig(String, String)}.
233 	 * @see #setConfig(String, String)
234 	 * @param config
235 	 *            configuration collection
236 	 */
237 	@Deprecated
238 	public void setConfig(FCKeditorConfig config) {
239 		this.fckConfig = config;
240 	}
241 
242 	/**
243 	 * Escapes base XML entities as specified <a
244 	 * href="http://en.wikipedia.org/wiki/Xml#Entity_references">here</a>.
245 	 * 
246 	 * @param str
247 	 *            string to escape, empty strings will be ignored
248 	 * @return escaped string
249 	 */
250 	private String escapeXml(String str) {
251 
252 		if (Utils.isEmpty(str))
253 			return str;
254 
255 		StringBuffer sb = new StringBuffer();
256 
257 		int len = str.length();
258 		char c;
259 
260 		for (int i = 0; i < len; i++) {
261 
262 			c = str.charAt(i);
263 			switch (c) {
264 			case '&':
265 				sb.append("&amp;");
266 				break;
267 			case '<':
268 				sb.append("&lt;");
269 				break;
270 			case '>':
271 				sb.append("&gt;");
272 				break;
273 			case '"':
274 				sb.append("&quot;");
275 				break;
276 			// XML actually defines &apos; as entity for the apostrophe but we
277 			// user rather the numerical reference to avoid XHTML 1.0 validation
278 			// problems
279 			case '\'':
280 				sb.append("&#39;");
281 				break;
282 			default:
283 				sb.append(c);
284 				break;
285 			}
286 		}
287 
288 		return sb.toString();
289 	}
290 
291 	/**
292 	 * Creates the HTML representation of this editor instance. First of all,
293 	 * this method determines whether the request browser is supported.
294 	 * According to the result an appropriate HTML representation is assembled
295 	 * and returned.
296 	 * 
297 	 * @return HTML representation of this editor instance
298 	 */
299 	@Override
300 	public String toString() {
301 		StringBuffer strEditor = new StringBuffer();
302 
303 		strEditor.append("<div>");
304 		String encodedValue = escapeXml(value);
305 
306 		if (Compatibility.isCompatibleBrowser(request)) {
307 			strEditor.append(createInputForVariable(instanceName, instanceName,
308 					encodedValue));
309 
310 			// create config HTML
311 			String configStr = fckConfig.getUrlParams();
312 			if (Utils.isNotEmpty(configStr))
313 				strEditor.append(createInputForVariable(null, instanceName
314 						.concat("___Config"), configStr));
315 
316 			// create IFrame
317 			StringBuffer editorLink = new StringBuffer(request.getContextPath());
318 			editorLink.append(basePath);
319 			editorLink.append("/editor/fckeditor.html?InstanceName=").append(
320 					instanceName);
321 			if (Utils.isNotEmpty(toolbarSet))
322 				editorLink.append("&amp;Toolbar=").append(toolbarSet);
323 
324 			XHtmlTagTool iframeTag = new XHtmlTagTool("iframe",
325 					XHtmlTagTool.SPACE);
326 			iframeTag.addAttribute("id", instanceName.concat("___Frame"));
327 			iframeTag.addAttribute("src", editorLink.toString());
328 			iframeTag.addAttribute("width", width);
329 			iframeTag.addAttribute("height", height);
330 			iframeTag.addAttribute("frameborder", "0");
331 			iframeTag.addAttribute("scrolling", "no");
332 			strEditor.append(iframeTag);
333 
334 		} else {
335 			XHtmlTagTool textareaTag = new XHtmlTagTool("textarea",
336 					encodedValue);
337 			textareaTag.addAttribute("name", instanceName);
338 			textareaTag.addAttribute("rows", "4");
339 			textareaTag.addAttribute("cols", "40");
340 			textareaTag.addAttribute("wrap", "virtual");
341 			textareaTag.addAttribute("style", "width: ".concat(width).concat(
342 					"; height: ").concat(height));
343 		}
344 		strEditor.append("</div>");
345 		return strEditor.toString();
346 	}
347 
348 	/**
349 	 * Creates the HTML representation of this editor instance.
350 	 * 
351 	 * @see #toString()
352 	 * @return HTML representation of this editor instance
353 	 */
354 	public String createHtml() {
355 		return toString();
356 	}
357 
358 	/**
359 	 * Creates a hidden input element for the given attributes.
360 	 * 
361 	 * @param name
362 	 *            name attribute of the input tag
363 	 * @param id
364 	 *            id attribute of the input tag
365 	 * @param value
366 	 *            value attribute of the input tag
367 	 * @return the produced XHTML tag
368 	 */
369 	private String createInputForVariable(final String name, final String id,
370 			final String value) {
371 		XHtmlTagTool tag = new XHtmlTagTool("input");
372 		if (Utils.isNotEmpty(id))
373 			tag.addAttribute("id", id);
374 		if (Utils.isNotEmpty(name))
375 			tag.addAttribute("name", name);
376 		tag.addAttribute("value", value);
377 		tag.addAttribute("type", "hidden");
378 		return tag.toString();
379 	}
380 }