1 /*
2 * FCKeditor - The text editor for Internet - http://www.fckeditor.net
3 * Copyright (C) 2004-2010 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}, {@code instanceName} and {@code inputName}
37 * will be checked for sanity.
38 *
39 * @version $Id: FCKeditor.java 4785 2009-12-21 20:10:28Z mosipov $
40 */
41 public class FCKeditor {
42
43 private FCKeditorConfig fckConfig = new FCKeditorConfig();
44 private String instanceName;
45 private String inputName;
46 private HttpServletRequest request;
47
48 // defaults
49 private String value = Utils.EMPTY_STRING;
50 private String toolbarSet = PropertiesLoader.getEditorToolbarSet();
51 private String width = PropertiesLoader.getEditorWidth();
52 private String height = PropertiesLoader.getEditorHeight();
53 private String basePath = PropertiesLoader.getEditorBasePath();
54
55 /**
56 * Class constructor with all parameters.
57 *
58 * @param request
59 * current user request instance
60 * @param instanceName
61 * the unique name of this editor
62 * @param inputName
63 * the name for the underlying input element. See
64 * {@link #setInputName(String)} for details.
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(HttpServletRequest request, String instanceName,
80 String inputName, String width, String height, String toolbarSet,
81 String value, String basePath) {
82
83 this(request, instanceName, inputName);
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 extended set of parameters.
94 *
95 * @param request
96 * current user request instance
97 * @param instanceName
98 * the unique name of this editor
99 * @param width
100 * the desired editor width (CSS-style value)
101 * @param height
102 * the desired editor height (CSS-style value)
103 * @param toolbarSet
104 * the desired toolbar set name
105 * @param value
106 * the HTML markup of this editor. Markup will be properly
107 * escaped.
108 * @param basePath
109 * the base path of this editor, absolute to the context
110 * @throws IllegalArgumentException
111 * if instanceName is empty or not a valid XHTML id
112 */
113 public FCKeditor(final HttpServletRequest request,
114 final String instanceName, final String width, final String height,
115 final String toolbarSet, final String value, final String basePath) {
116
117 this(request, instanceName, null, width, height, toolbarSet, value,
118 basePath);
119
120 }
121
122 /**
123 * Class constructor with a minimal set of parameters.
124 *
125 * Omitted parameters will be set to default values.
126 *
127 * @param request
128 * current user request instance
129 * @param instanceName
130 * the unique name of this editor
131 * @throws IllegalArgumentException
132 * if instanceName is empty or not a valid HTML id
133 */
134 public FCKeditor(final HttpServletRequest request, final String instanceName) {
135
136 if (request == null)
137 throw new NullPointerException("the request cannot be null");
138 this.request = request;
139
140 setInstanceName(instanceName);
141
142 }
143
144 /**
145 * Class constructor with a basic set of parameters.
146 *
147 * Omitted parameters will be set to default values.
148 *
149 * @param request
150 * current user request instance
151 * @param instanceName
152 * the unique name of this editor
153 * @param inputName
154 * the name for the underlying input element. See
155 * {@link #setInputName(String)} for details.
156 * @throws IllegalArgumentException
157 * if instanceName is empty or not a valid HTML id
158 */
159 public FCKeditor(HttpServletRequest request, String instanceName,
160 String inputName) {
161 this(request, instanceName);
162 setInputName(inputName);
163 }
164
165 /**
166 * Sets the unique name of this editor.
167 *
168 * @param instanceName
169 * the unique name of this editor
170 * @throws IllegalArgumentException
171 * if instanceName is empty or not a valid XHTML id
172 */
173 public void setInstanceName(final String instanceName) {
174 if (Utils.isEmpty(instanceName))
175 throw new IllegalArgumentException("instanceName cannot be empty");
176 if (!instanceName.matches("\\p{Alpha}[\\p{Alnum}:_.-]*"))
177 throw new IllegalArgumentException(
178 "instanceName must be a valid XHTML id containing only \"\\p{Alpha}[\\p{Alnum}:_.-]*\"");
179 this.instanceName = instanceName;
180 }
181
182 /**
183 * Sets the name for the underlying input element. Empty strings will be
184 * ignored and field will be reset to {@code instanceName}.
185 *
186 * @param inputName
187 * the name for the underlying input element
188 */
189 public void setInputName(final String inputName) {
190 if (Utils.isEmpty(inputName))
191 this.inputName = instanceName;
192 else
193 this.inputName = inputName;
194 }
195
196 /**
197 * Sets the initial value to be edited as HTML markup.
198 *
199 * @param value
200 * the HTML markup of this editor. Markup will be properly
201 * escaped.
202 */
203 public void setValue(final String value) {
204 this.value = value;
205 }
206
207 /**
208 * Sets the base path of this editor. The base path reflects the location of
209 * the editor files absolute to the context root <i>not</i> the server root.
210 *
211 * @param basePath
212 * the base path of this editor, absolute to the context
213 */
214 public void setBasePath(final String basePath) {
215 this.basePath = basePath;
216 }
217
218 /**
219 * Sets the name of the toolbar set of this editor.
220 *
221 * @param toolbarSet
222 * the desired toolbar set name
223 */
224 public void setToolbarSet(final String toolbarSet) {
225 this.toolbarSet = toolbarSet;
226 }
227
228 /**
229 * Sets the width of this editor. This value can be any valid CSS width
230 * value.
231 *
232 * @param width
233 * the desired editor width (CSS-style value)
234 */
235 public void setWidth(final String width) {
236 this.width = width;
237 }
238
239 /**
240 * Sets the height of this editor. This value can be any valid CSS height
241 * value.
242 *
243 * @param height
244 * the desired editor height (CSS-style value)
245 */
246 public void setHeight(final String height) {
247 this.height = height;
248 }
249
250 /**
251 * Returns a configuration option. See {@link FCKeditorConfig} for more
252 * details.
253 *
254 * @param name
255 * the name of the parameter (case-sensitive)
256 * @return the value represented by this parameter, else null
257 */
258 public String getConfig(String name) {
259 return fckConfig.get(name);
260 }
261
262 /**
263 * Sets a configuration option. See {@link FCKeditorConfig} for more
264 * details.
265 *
266 * @param name
267 * the name of the config option (case-sensitive)
268 * @param value
269 * the value of the config option. Null values will be ignored.
270 */
271 public void setConfig(String name, String value) {
272 if (value != null)
273 fckConfig.put(name, value);
274 }
275
276 /**
277 * Escapes base XML entities as specified <a
278 * href="http://en.wikipedia.org/wiki/Xml#Entity_references">here</a>.
279 *
280 * @param str
281 * string to escape, empty strings will be ignored
282 * @return escaped string
283 */
284 private String escapeXml(String str) {
285
286 if (Utils.isEmpty(str))
287 return str;
288
289 StringBuffer sb = new StringBuffer();
290
291 int len = str.length();
292 char c;
293
294 for (int i = 0; i < len; i++) {
295
296 c = str.charAt(i);
297 switch (c) {
298 case '&':
299 sb.append("&");
300 break;
301 case '<':
302 sb.append("<");
303 break;
304 case '>':
305 sb.append(">");
306 break;
307 case '"':
308 sb.append(""");
309 break;
310 // XML actually defines ' as entity for the apostrophe but we
311 // user rather the numerical reference to avoid XHTML 1.0 validation
312 // problems
313 case '\'':
314 sb.append("'");
315 break;
316 default:
317 sb.append(c);
318 break;
319 }
320 }
321
322 return sb.toString();
323 }
324
325 /**
326 * Creates the HTML representation of this editor instance. First of all,
327 * this method determines whether the request browser is supported.
328 * According to the result an appropriate HTML representation is assembled
329 * and returned.
330 *
331 * @return HTML representation of this editor instance
332 */
333 @Override
334 public String toString() {
335 StringBuffer strEditor = new StringBuffer();
336
337 strEditor.append("<div>");
338 String encodedValue = escapeXml(value);
339
340 /*
341 * We have to reset inputName to make sure that a potentially reassigned
342 * instanceName has been propagated to empty field.
343 */
344 setInputName(inputName);
345
346 if (Compatibility.isCompatibleBrowser(request)) {
347 strEditor.append(createInputForVariable(instanceName, inputName,
348 encodedValue));
349
350 // create config HTML
351 String configStr = fckConfig.getUrlParams();
352 if (Utils.isNotEmpty(configStr))
353 strEditor.append(createInputForVariable(instanceName
354 .concat("___Config"), null, configStr));
355
356 // create IFrame
357 StringBuffer editorLink = new StringBuffer(request.getContextPath());
358 editorLink.append(basePath);
359 editorLink.append("/editor/fckeditor.html?InstanceName=").append(
360 instanceName);
361 if (Utils.isNotEmpty(toolbarSet))
362 editorLink.append("&Toolbar=").append(toolbarSet);
363
364 XHtmlTagTool iframeTag = new XHtmlTagTool("iframe",
365 XHtmlTagTool.SPACE);
366 iframeTag.addAttribute("id", instanceName.concat("___Frame"));
367 iframeTag.addAttribute("src", editorLink.toString());
368 iframeTag.addAttribute("width", width);
369 iframeTag.addAttribute("height", height);
370 iframeTag.addAttribute("frameborder", "0");
371 iframeTag.addAttribute("scrolling", "no");
372 strEditor.append(iframeTag);
373
374 } else {
375 XHtmlTagTool textareaTag = new XHtmlTagTool("textarea",
376 encodedValue);
377 textareaTag.addAttribute("name", inputName);
378 textareaTag.addAttribute("rows", "4");
379 textareaTag.addAttribute("cols", "40");
380 textareaTag.addAttribute("wrap", "virtual");
381 textareaTag.addAttribute("style", "width: ".concat(width).concat(
382 "; height: ").concat(height));
383 }
384 strEditor.append("</div>");
385 return strEditor.toString();
386 }
387
388 /**
389 * Creates the HTML representation of this editor instance.
390 *
391 * @see #toString()
392 * @return HTML representation of this editor instance
393 */
394 public String createHtml() {
395 return toString();
396 }
397
398 /**
399 * Creates a hidden input element for the given attributes.
400 * @param id
401 * id attribute of the input tag
402 * @param name
403 * name attribute of the input tag
404 * @param value
405 * value attribute of the input tag
406 *
407 * @return the produced XHTML tag
408 */
409 private String createInputForVariable(final String id, final String name,
410 final String value) {
411 XHtmlTagTool tag = new XHtmlTagTool("input");
412 tag.addAttribute("id", id);
413 tag.addAttribute("name", name);
414 tag.addAttribute("value", value);
415 tag.addAttribute("type", "hidden");
416 return tag.toString();
417 }
418 }