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("&");
266 break;
267 case '<':
268 sb.append("<");
269 break;
270 case '>':
271 sb.append(">");
272 break;
273 case '"':
274 sb.append(""");
275 break;
276 // XML actually defines ' 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("'");
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("&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 }