/********************************************************************************** * $URL: https://source.sakaiproject.org/svn/jsf/trunk/jsf-widgets/src/java/org/sakaiproject/jsf/renderer/InputRichTextRenderer.java $ * $Id: InputRichTextRenderer.java 120900 2013-03-07 20:21:44Z ottenhoff@longsight.com $ *********************************************************************************** * * Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008 The Sakai Foundation * * Licensed under the Educational Community License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.opensource.org/licenses/ECL-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * **********************************************************************************/ package org.sakaiproject.jsf.renderer; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.sakaiproject.component.cover.ComponentManager; import org.sakaiproject.component.api.ServerConfigurationService; import org.sakaiproject.tool.api.ToolManager; import org.sakaiproject.content.api.ContentHostingService; import org.sakaiproject.jsf.model.InitObjectContainer; import org.sakaiproject.jsf.util.ConfigurationResource; import org.sakaiproject.jsf.util.RendererUtil; import org.sakaiproject.util.EditorConfiguration; import org.sakaiproject.util.Web; import javax.faces.component.UIComponent; import javax.faces.component.UIInput; import javax.faces.component.ValueHolder; import javax.faces.context.FacesContext; import javax.faces.context.ResponseWriter; import javax.faces.model.SelectItem; import javax.faces.render.Renderer; import java.io.IOException; import java.io.StringWriter; import java.text.MessageFormat; import java.util.*; /** * <p>Formerly RichTextEditArea.java</p> * <p>Renders a rich text editor and toolbar within an HTML "textarea" element.</p> <p>The textarea is decorated using the HTMLArea JavaScript library.</p> <p> HTMLArea is a free, customizable online editor. It works inside your browser. It uses a non-standard feature implemented in Internet Explorer 5.5 or better for Windows and Mozilla 1.3 or better (any platform), therefore it will only work in one of these browsers. </p> <p> HTMLArea is copyright <a href="http://interactivetools.com">InteractiveTools.com</a> and released under a BSD-style license. HTMLArea is created and developed upto version 2.03 by InteractiveTools.com. Version 3.0 developed by <a href="http://students.infoiasi.ro/~mishoo/">Mihai Bazon</a> for InteractiveTools. It contains code sponsored by other companies as well. </p> * <p>Copyright: Copyright (c) 2004 Sakai</p> * @author cwen@iu.edu * @author Ed Smiley esmiley@stanford.edu (modifications) * @version $Id: InputRichTextRenderer.java 120900 2013-03-07 20:21:44Z ottenhoff@longsight.com $ */ public class InputRichTextRenderer extends Renderer { private static final String SCRIPT_PATH; private static final String HTMLAREA_SCRIPT_PATH; private static final String RESOURCE_PATH; private static final String TOOLBAR_SCRIPT_NONE; private static final String TOOLBAR_SCRIPT_SMALL; private static final String TOOLBAR_SCRIPT_MEDIUM; private static final String TOOLBAR_SCRIPT_LARGE; private static final int DEFAULT_WIDTH_PX; private static final int DEFAULT_HEIGHT_PX; private static final int DEFAULT_COLUMNS; private static final int DEFAULT_ROWS; private static final String INSERT_IMAGE_LOC; private static final MessageFormat LIST_ITEM_FORMAT_HTML = new MessageFormat("\"{0}\" : \"<a href=''{1}''>{0}</a>\""); private static final MessageFormat LIST_ITEM_FORMAT_FCK = new MessageFormat("[\"{0}\", \"{1}\"]"); private static final Log log = LogFactory.getLog(InputRichTextRenderer.class); // we have static resources for our script path and built-in toolbars etc. static { ConfigurationResource cr = new ConfigurationResource(); SCRIPT_PATH = cr.get("inputRichTextScript"); HTMLAREA_SCRIPT_PATH = cr.get("inputRichTextHTMLArea"); RESOURCE_PATH = cr.get("resources"); TOOLBAR_SCRIPT_NONE = makeToolbarScript(cr.get("inputRichText_none")); TOOLBAR_SCRIPT_SMALL = makeToolbarScript(cr.get("inputRichText_small")); TOOLBAR_SCRIPT_MEDIUM = makeToolbarScript(cr.get("inputRichText_medium")); TOOLBAR_SCRIPT_LARGE = makeToolbarScript(cr.get("inputRichText_large")); DEFAULT_WIDTH_PX = Integer.parseInt(cr.get("inputRichTextDefaultWidthPx").trim()); /*SAK-20809 if an erant white space is left after the value this could throw a * number format exception so we trim it */ DEFAULT_HEIGHT_PX = Integer.parseInt(cr.get("inputRichTextDefaultHeightPx").trim()); DEFAULT_COLUMNS = Integer.parseInt(cr.get("inputRichTextDefaultTextareaColumns").trim()); DEFAULT_ROWS = Integer.parseInt(cr.get("inputRichTextDefaultTextareaRows").trim()); INSERT_IMAGE_LOC = "/" + RESOURCE_PATH + "/" + cr.get("inputRichTextFileInsertImage"); } public boolean supportsComponentType(UIComponent component) { return (component instanceof org.sakaiproject.jsf.component.InputRichTextComponent); } public void encodeBegin(FacesContext context, UIComponent component) throws IOException { if (!component.isRendered()) { return; } String contextPath = context.getExternalContext().getRequestContextPath() + SCRIPT_PATH; String clientId = component.getClientId(context); ResponseWriter writer = context.getResponseWriter(); String value = null; if (component instanceof UIInput) value = (String) ((UIInput) component).getSubmittedValue(); if (value == null && component instanceof ValueHolder) value = (String) ((ValueHolder) component).getValue(); // SAK-23313 // The rich-text editor will interpret a string like <tag> as a real tag // So we double-escape the ampersand to create &lt; so CKEditor displays this as text if (value!=null) { java.util.regex.Pattern pattern = java.util.regex.Pattern.compile("&([^\\s])"); value = pattern.matcher(value).replaceAll("&$1"); } /////////////////////////////////////////////////////////////////////////// // attributes /////////////////////////////////////////////////////////////////////////// // If true, only the textarea will be rendered. Defaults to false. // If true, the rich text toolbar and external HTMLArea JavaScript will NOT. String textareaOnly = (String) RendererUtil.getAttribute(context, component, "textareaOnly"); // Specify toolbar from among a number precanned lists of command buttons. // Allowed values are: "none", "small", "medium", "large". String buttonSet = (String) RendererUtil.getAttribute(context, component, "buttonSet"); // Comma delimited list of toolbar command buttons registered with component. String buttonList = (String) RendererUtil.getAttribute(context, component, "buttonList"); /** * @todo need to do something with extensions. */ // URL to an external JavaScript file with additional JavaScript String javascriptLibraryExtensionURL = (String) RendererUtil.getAttribute(context, component, "javascriptLibraryExtensionURL"); // The URL to the directory of the HTMLArea JavaScript library. String javascriptLibraryURL = (String) RendererUtil.getAttribute(context, component, "javascriptLibraryURL"); // If true show XPath at bottom of editor. Default is true. String showXPath = (String) RendererUtil.getAttribute(context, component, "showXPath"); showXPath = RendererUtil.makeSwitchString(showXPath, true, true, true, false, false, true); /////////////////////////////////////////////////////////////////////////// // set up dimensions int widthPx = DEFAULT_WIDTH_PX; int heightPx = DEFAULT_HEIGHT_PX; int textareaColumns = DEFAULT_COLUMNS; int textareaRows = DEFAULT_ROWS; try { Integer cols = Integer.parseInt("" + RendererUtil.getAttribute(context, component, "cols")); Integer rows = Integer.parseInt("" + RendererUtil.getAttribute(context, component, "rows")); if (cols != null) textareaColumns = cols.intValue(); if (rows != null) textareaRows = rows.intValue(); // Width of the widget (in pixel units). // If this attribute is not specified, the width is controlled by the 'cols' attribute. Integer width = (Integer) RendererUtil.getAttribute(context, component, "width"); if (width != null) widthPx = width.intValue(); // Height of the widget (in pixel units). // If this attribute is not specified, the width is controlled by the 'rows' attribute. Integer height = (Integer) RendererUtil.getAttribute(context, component, "height"); if (height != null) heightPx = height.intValue(); } catch (Exception ex) { //default, whatever goes awry if (log.isDebugEnabled()) log.debug(ex); } if (widthPx == DEFAULT_WIDTH_PX && textareaColumns != DEFAULT_COLUMNS) widthPx = (DEFAULT_WIDTH_PX*textareaColumns)/DEFAULT_COLUMNS; if (heightPx == DEFAULT_HEIGHT_PX && textareaRows != DEFAULT_ROWS) heightPx = (DEFAULT_HEIGHT_PX*textareaRows)/DEFAULT_ROWS; Locale locale = Locale.getDefault(); ServerConfigurationService serverConfigurationService = (ServerConfigurationService)ComponentManager.get(ServerConfigurationService.class.getName()); String editor = serverConfigurationService.getString("wysiwyg.editor"); String collectionBase = (String) RendererUtil.getAttribute(context, component, "collectionBase"); String collectionId = ""; if (collectionBase != null) { collectionId="collectionId: '"+collectionBase.replaceAll("\"","\\\"")+"'"; } writer.write("<table border=\"0\"><tr><td>"); writer.write("<textarea name=\"" + clientId + "_inputRichText\" id=\"" + clientId + "_inputRichText\""); if (textareaColumns > 0) writer.write(" cols=\""+textareaColumns+"\""); if (textareaRows > 0) writer.write(" rows=\""+textareaRows+"\""); writer.write(">"); if (value != null) writer.write((String) value); writer.write("</textarea>"); if (!"true".equals(textareaOnly)) { writer.write("<script type=\"text/javascript\">sakai.editor.launch('" + clientId + "_inputRichText', {"+collectionId+"}, '" + widthPx + "','" + heightPx + "');</script>"); } writer.write("</td></tr></table>\n"); /* if(editor != null && !editor.equalsIgnoreCase("FCKeditor")) { // Render JavaScripts. writeExternalScripts(locale, writer); // Render base textarea. writeTextArea(clientId, value, textareaRows, textareaColumns, writer); // Make textarea rich text (unless textareaOnly is true). if (!"true".equals(textareaOnly)) { String toolbarScript; if (buttonList != null) { toolbarScript = makeToolbarScript(buttonList); } else { toolbarScript = getStandardToolbarScript(buttonSet); } // hook up configuration object writeConfigurationScript(context, component, clientId, toolbarScript, widthPx, heightPx, showXPath, locale, writer); } } else { ToolManager toolManager = (ToolManager)ComponentManager.get(ToolManager.class.getName()); ContentHostingService contentHostingService = (ContentHostingService)ComponentManager.get(ContentHostingService.class.getName()); //not as slick as the way htmlarea is rendered, but the difference in functionality doesn't all //make sense for FCK at this time since it's already got the ability to insert files and such. String collectionBase = (String) RendererUtil.getAttribute(context, component, "collectionBase"); String collectionId = null; String connector = "/sakai-fck-connector/filemanager/connector"; if (collectionBase != null) { collectionId = collectionBase; connector += collectionBase; } else { collectionId = contentHostingService.getSiteCollection(toolManager.getCurrentPlacement().getContext()); } writer.write("<table border=\"0\"><tr><td>"); writer.write("<textarea name=\"" + clientId + "_inputRichText\" id=\"" + clientId + "_inputRichText\""); if (textareaColumns > 0) writer.write(" cols=\""+textareaColumns+"\""); if (textareaRows > 0) writer.write(" rows=\""+textareaRows+"\""); writer.write(">"); if (value != null) writer.write((String) value); writer.write("</textarea>"); RendererUtil.writeExternalJSDependencies(context, writer, "inputrichtext.jsf.fckeditor.js", "/library/editor/FCKeditor/fckeditor.js"); //writer.write("<script type=\"text/javascript\" src=\"/library/editor/FCKeditor/fckeditor.js\"></script>\n"); writer.write("<script type=\"text/javascript\" language=\"JavaScript\">\n"); String attachmentVar = "attachment" + createSafeRandomNumber(); boolean hasAttachments = false; Object attchedFiles = RendererUtil.getAttribute(context, component, "attachedFiles"); if (attchedFiles != null && getSize(attchedFiles) > 0) { writeFilesArray(writer, attachmentVar, attchedFiles, LIST_ITEM_FORMAT_FCK, false); writer.write("\n"); hasAttachments = true; } writer.write("function chef_setupformattedtextarea(textarea_id){\n"); writer.write("var oFCKeditor = new FCKeditor(textarea_id);\n"); writer.write("oFCKeditor.BasePath = \"/library/editor/FCKeditor/\";\n"); if (widthPx < 0) widthPx = 600; if (heightPx < 0) heightPx = 400; //FCK's toolset is larger then htmlarea and this prevents tools from ending up with all toolbar //and no actual editing area. if (heightPx < 200) heightPx = 200; writer.write("oFCKeditor.Width = \"" + widthPx + "\" ;\n"); writer.write("oFCKeditor.Height = \"" + heightPx + "\" ;\n"); writer.write("\n\t\tvar collectionId = \"" + collectionId + "\";"); writer.write("\n\toFCKeditor.Config['ImageBrowserURL'] = oFCKeditor.BasePath + " + "\"editor/filemanager/browser/default/browser.html?Connector=" + connector + "&Type=Image&CurrentFolder=\" + collectionId;"); writer.write("\n\toFCKeditor.Config['LinkBrowserURL'] = oFCKeditor.BasePath + " + "\"editor/filemanager/browser/default/browser.html?Connector=" + connector + "&Type=Link&CurrentFolder=\" + collectionId;"); writer.write("\n\toFCKeditor.Config['FlashBrowserURL'] = oFCKeditor.BasePath + " + "\"editor/filemanager/browser/default/browser.html?Connector=" + connector + "&Type=Flash&CurrentFolder=\" + collectionId;"); writer.write("\n\toFCKeditor.Config['ImageUploadURL'] = oFCKeditor.BasePath + " + "\"" + connector + "?Type=Image&Command=QuickUpload&Type=Image&CurrentFolder=\" + collectionId;"); writer.write("\n\toFCKeditor.Config['FlashUploadURL'] = oFCKeditor.BasePath + " + "\"" + connector + "?Type=Flash&Command=QuickUpload&Type=Flash&CurrentFolder=\" + collectionId;"); writer.write("\n\toFCKeditor.Config['LinkUploadURL'] = oFCKeditor.BasePath + " + "\"" + connector + "?Type=File&Command=QuickUpload&Type=Link&CurrentFolder=\" + collectionId;"); writer.write("\n\n\toFCKeditor.Config['CurrentFolder'] = collectionId;"); boolean resourceSearch = EditorConfiguration.enableResourceSearch(); if(resourceSearch) { // need to set document.__pid to placementId String placementId = toolManager.getCurrentPlacement().getId(); writer.write("\t\tdocument.__pid=\"" + placementId + "\";\n"); // need to set document.__baseUrl to baseUrl String baseUrl = serverConfigurationService.getToolUrl() + "/" + Web.escapeUrl(placementId); writer.write("\t\tdocument.__baseUrl=\"" + baseUrl + "\";\n"); writer.write("\n\toFCKeditor.Config['CustomConfigurationsPath'] = \"/library/editor/FCKeditor/config_rs.js\";\n"); } else { writer.write("\n\toFCKeditor.Config['CustomConfigurationsPath'] = \"/library/editor/FCKeditor/config.js\";\n"); } if (hasAttachments) { writer.write("\n\n\toFCKeditor.Config['AttachmentsVariable'] = \"" + attachmentVar + "\";"); writer.write("\n\n\toFCKeditor.ToolbarSet = \"Attachments\";"); } writer.write("oFCKeditor.ReplaceTextarea();\n"); writer.write("} \n"); writer.write("</script>\n"); writer.write("<script type=\"text/javascript\" defer=\"1\">chef_setupformattedtextarea('"+clientId+"_inputRichText');</script>"); writer.write("</td></tr></table>\n"); } */ } /** * Return the config script portion for a standard button set. * @param buttonSet * @return */ private String getStandardToolbarScript(String buttonSet) { String toolbarScript; if ("none".equals(buttonSet)) { toolbarScript = TOOLBAR_SCRIPT_NONE; } else if ("small".equals(buttonSet)) { toolbarScript = TOOLBAR_SCRIPT_SMALL; } else if ("medium".equals(buttonSet)) { toolbarScript = TOOLBAR_SCRIPT_MEDIUM; } else if ("large".equals(buttonSet)) { toolbarScript = TOOLBAR_SCRIPT_LARGE; } else { toolbarScript = TOOLBAR_SCRIPT_MEDIUM; } return toolbarScript; } /** * Write out HTML rextarea * @param clientId * @param value the textarrea value * @param writer * @throws IOException */ private void writeTextArea(String clientId, String value, int rows, int cols, ResponseWriter writer) throws IOException { // wrap the textarea in a table, so that the HTMLArea toolbar doesn't bleed to the // edge of the screen. writer.write("<table border=\"0\"><tr><td>\n"); writer.write("<textarea name=\""); writer.write(clientId); writer.write("_inputRichText\" id=\""); writer.write(clientId); writer.write("_inputRichText\""); writer.write(" rows=\"" + rows + "\""); writer.write(" cols=\"" + cols + "\""); writer.write(">" + value + "</textarea>\n"); writer.write("</td></tr></table>\n"); } /** * @todo do these as a document.write after testing if done * @param contextPath * @param writer * @throws IOException */ protected void writeExternalScripts(Locale locale, ResponseWriter writer) throws IOException { writer.write("<script type=\"text/javascript\">var _editor_url = \"" + "/" + RESOURCE_PATH + "/" + HTMLAREA_SCRIPT_PATH + "/" + "\";</script>\n"); writer.write("<script type=\"text/javascript\" src=\"" + "/" + RESOURCE_PATH + "/" + HTMLAREA_SCRIPT_PATH + "/" + "htmlarea.js\"></script>\n"); writer.write("<script type=\"text/javascript\" src=\"" + "/" + RESOURCE_PATH + "/" + HTMLAREA_SCRIPT_PATH + "/" + "dialog.js\"></script>\n"); writer.write("<script type=\"text/javascript\" src=\"" + "/" + RESOURCE_PATH + "/" + HTMLAREA_SCRIPT_PATH + "/" + "popupwin.js\"></script>\n"); writer.write("<script type=\"text/javascript\" src=\"" + "/" + RESOURCE_PATH + "/" + HTMLAREA_SCRIPT_PATH + "/" + "lang/en.js\"></script>\n"); String language = locale.getLanguage(); if (!Locale.ENGLISH.getLanguage().equals(language)) { writer.write("<script type=\"text/javascript\" src=\"" + "/" + RESOURCE_PATH + "/" + HTMLAREA_SCRIPT_PATH + "/" + "lang/" + language + ".js\"></script>\n"); } writer.write("<script type=\"text/javascript\" src=\"" + "/" + RESOURCE_PATH + "/" + SCRIPT_PATH + "\"></script>\n"); } /** * Standard decode method. * @param context * @param component */ public void decode(FacesContext context, UIComponent component) { if( RendererUtil.isDisabledOrReadonly(context, component)) return; if (null == context || null == component || !(component instanceof org.sakaiproject.jsf.component.InputRichTextComponent)) { throw new IllegalArgumentException(); } String clientId = component.getClientId(context); Map requestParameterMap = context.getExternalContext() .getRequestParameterMap(); String newValue = (String) requestParameterMap.get(clientId + "_inputRichText"); org.sakaiproject.jsf.component.InputRichTextComponent comp = (org.sakaiproject.jsf.component.InputRichTextComponent) component; comp.setSubmittedValue(newValue); } /** * Write configuration script * * @param clientId the client id * @param toolbar the toolbar configuration string (i.e from makeToolbarScript()) * @param widthPx columns * @param heightPx rows */ protected void writeConfigurationScript(FacesContext context, UIComponent component, String clientId, String toolbar, int widthPx, int heightPx, String showXPath, Locale locale, ResponseWriter writer) throws IOException { // script creates unique Config object String configVar = "config" + createSafeRandomNumber(); writer.write("<script type=\"text/javascript\">\n"); writer.write(" sakaiSetLanguage(\"" + locale.getDisplayLanguage() + "\");"); writer.write(" var " + configVar + "=new HTMLArea.Config();\n"); writer.write(" sakaiRegisterButtons(" + configVar + ");\n"); writer.write(" " + configVar + ".toolbar = " + toolbar + ";\n"); writer.write(" " + configVar + ".width=\"" + widthPx + "px\";\n"); writer.write(" " + configVar + ".height=\"" + heightPx + "px\";\n"); writer.write(" " + configVar + ".statusBar=" + showXPath + ";\n"); writeAdditionalConfig(context, component, configVar, clientId, toolbar, widthPx, heightPx, locale, writer); writer.write( "sakaiSetupRichTextarea(\""); writer.write(clientId); writer.write("_inputRichText\"," + configVar + ");\n"); writer.write("</script>\n"); } /** * subclasses can override to provide additonal configuration such as add buttons, etc * @param context * @param component * @param configVar * @param clientId * @param toolbar * @param widthPx * @param heightPx * @param locale * @param writer */ protected void writeAdditionalConfig(FacesContext context, UIComponent component, String configVar, String clientId, String toolbar, int widthPx, int heightPx, Locale locale, ResponseWriter writer) throws IOException{ writeAttachedFiles(context, component, configVar, writer, toolbar); registerWithParent(component, configVar, clientId); } protected void writeAttachedFiles(FacesContext context, UIComponent component, String configVar, ResponseWriter writer, String toolbar) throws IOException { Object attchedFiles = RendererUtil.getAttribute(context, component, "attachedFiles"); if (attchedFiles != null && getSize(attchedFiles) > 0) { String arrayVar = configVar + "_Resources"; writeFilesArray(writer, arrayVar, attchedFiles, LIST_ITEM_FORMAT_HTML, true); writer.write( "sakaiRegisterResourceList("); writer.write(configVar + ",'" + INSERT_IMAGE_LOC + "'," + arrayVar); writer.write(");\n"); writer.write(" " + configVar + ".toolbar = " + addToolbar(toolbar) + ";\n"); } } protected void writeFilesArray(ResponseWriter writer, String arrayVar, Object attchedFiles, MessageFormat format, boolean includeLabel) throws IOException { StringWriter buffer = new StringWriter(); char startChar = '['; char endChar = ']'; if (format == LIST_ITEM_FORMAT_HTML) { startChar = '{'; endChar = '}'; } buffer.write(" var " + arrayVar + " = "+startChar+"\n"); if (includeLabel) { buffer.write("\"select a file url to insert\" : \"\""); } if (attchedFiles instanceof Map) { buffer.write(outputFiles((Map)attchedFiles, format, !includeLabel)); } else { buffer.write(outputFiles((List)attchedFiles, format, !includeLabel)); } buffer.write(endChar + ";\n"); String result = buffer.toString(); writer.write(result); } protected void registerWithParent(UIComponent component, String configVar, String clientId) { InitObjectContainer parentContainer = null; UIComponent testContainer = component.getParent(); while (testContainer != null) { if (testContainer instanceof InitObjectContainer) { parentContainer = (InitObjectContainer)testContainer; String script = " resetRichTextEditor(\"" + clientId + "_inputRichText\"," + configVar + ");\n"; parentContainer.addInitScript(script); } testContainer = testContainer.getParent(); } } protected String outputFiles(Map map, MessageFormat format, boolean first) { StringBuffer sb = new StringBuffer(); for (Iterator i=map.entrySet().iterator();i.hasNext();) { Map.Entry entry = (Map.Entry)i.next(); if (!first) { sb.append(','); } else { first = false; } format.format(new Object[]{entry.getValue(), entry.getKey()}, sb, null); } return sb.toString(); } protected String outputFiles(List list, MessageFormat format, boolean first) { StringBuffer sb = new StringBuffer(); for (Iterator i=list.iterator();i.hasNext();) { Object value = i.next(); String url; String label; if (value instanceof SelectItem) { SelectItem item = (SelectItem)value; url = item.getValue().toString(); label = item.getLabel(); } else { url = value.toString(); label = value.toString(); } if (!first) { sb.append(','); } else { first = false; } format.format(new Object[]{label, url}, sb, null); } return sb.toString(); } protected int getSize(Object attchedFiles) { if (attchedFiles instanceof Map) { return ((Map)attchedFiles).size(); } else { return ((List)attchedFiles).size(); } } protected String addToolbar(String toolbar) { int pos = toolbar.lastIndexOf("]"); toolbar = toolbar.substring(0, pos) + ",[\"filedropdown\", \"insertfile\", ]" + toolbar.substring(pos); return toolbar; } /** * Built toolbar part of configuration script for a list of button commands. * * @param buttonList csv list of buttons * @return String, e.g. * <code><pre> * [["fontname", "space",... ]] etc. * </pre></code> * */ private static String makeToolbarScript(String buttonList) { StringBuilder script = new StringBuilder(); String q = "\""; script.append("[["); StringTokenizer st = new StringTokenizer(buttonList, ",", false); while (st.hasMoreTokens()) { String command = st.nextToken(); if (!"linebreak".equals(command)) { script.append(q + command + q + ", "); } else { script.append("],["); } } script.append("]]"); return script.toString(); } private String createSafeRandomNumber() { return "" + (long)(Math.floor(Math.random() * 1000000000)); } }