/* * This software is distributed under the terms of the FSF * Gnu Lesser General Public License (see lgpl.txt). * * This program is distributed WITHOUT ANY WARRANTY. See the * GNU General Public License for more details. */ package com.scooterframework.web.util; import java.io.IOException; import java.io.UnsupportedEncodingException; import java.net.URLDecoder; import java.net.URLEncoder; import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Locale; import java.util.Map; import java.util.Properties; import javax.servlet.RequestDispatcher; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import com.petebevin.markdown.MarkdownProcessor; import com.scooterframework.admin.Constants; import com.scooterframework.admin.EnvConfig; import com.scooterframework.common.logging.LogUtil; import com.scooterframework.common.util.Converters; import com.scooterframework.common.util.CurrentThreadCache; import com.scooterframework.common.util.CurrentThreadCacheClient; import com.scooterframework.common.util.ExpandedMessage; import com.scooterframework.common.util.Message; import com.scooterframework.common.util.StringUtil; import com.scooterframework.common.util.WordUtil; import com.scooterframework.i18n.Messages; import com.scooterframework.orm.activerecord.ActiveRecord; import com.scooterframework.orm.activerecord.ActiveRecordUtil; import com.scooterframework.orm.misc.Paginator; import com.scooterframework.web.controller.ACH; import com.scooterframework.web.controller.ActionContext; /** * <p>W(WebHelper) class has helper methods for web related requests, including * AJAX requests. </p> * * <p><tt>buttonKeys</tt> supported:</br> * disabled, name, type, value, id, class, title, style, dir, lang, accesskey, * tabindex, onblur, onclick, ondblclick, onfocus, onkeydown, onkeypress, * onkeyup, onmousedown, onmousemove, onmouseout, onmouseover, onmouseup * </p> * * <p><tt>imageKeys</tt> supported:</br> * align (deprecated in HTML4.01, not supported in XHTML 1.0 Strict DTD), alt, * border (deprecated in HTML4.01, not supported in XHTML 1.0 Strict DTD), class, * dir, height, hspace (deprecated in HTML4.01, not supported in XHTML 1.0 Strict DTD), * id, ismap, lang, longdesc, name, onclick, ondblclick, onkeydown, onkeypress, * onkeyup, onmousedown, onmousemove, onmouseout, onmouseover, onmouseup, src, * style, title, usemap, vspace (deprecated in HTML4.01, not supported in XHTML 1.0 Strict DTD), * width, * </p> * * <p><tt>inputKeys</tt> supported:</br> * disabled, name, type, value, id, class, title, style, dir, lang, accesskey, * tabindex, onblur, onclick, ondblclick, onfocus, onkeydown, onkeypress, onkeyup, * onmousedown, onmousemove, onmouseout, onmouseover, onmouseup, onselect, onchange * </p> * * <p><tt>linkKeys</tt> supported:</br> * accesskey, charset, class, coords, dir, hreflang, id, lang, name, onblue, * onclick, ondblclick, onfocus, onkeydown, onkeypress, onkeyup, onmousedown, * onmousemove, onmouseout, onmouseover, onmouseup, rel, rev, shape, tabindex, * target, title, type * </p> * * <p>AJAX related links are supported through <tt>data-*</tt> keys:</p> * <pre> * data-ajax: Its value is always true. This is the required property for AJAX. * data-confirm: A confirmation message to be popped up * data-method: HTTP method (GET, POST, UPDATE, DELETE) of AJAX link, "GET" is the default. * data-target: The target element in jQuery syntax, e.g. #posts_list. * data-handler: indicates how the returned message is handled, e.g. html. * data-type: The type of data expected from the server. See jQuery document. * </pre> * * @author (Fei) John Chen */ public class W { private static final LogUtil log = LogUtil.getLogger(W.class.getName()); public static final MarkdownProcessor mp = new MarkdownProcessor(); public static final List<String> html5Keys = new ArrayList<String>(); public static final List<String> buttonKeys = new ArrayList<String>(); public static final List<String> imageKeys = new ArrayList<String>(); public static final List<String> inputKeys = new ArrayList<String>(); public static final List<String> linkKeys = new ArrayList<String>(); public static final List<String> styleKeys = new ArrayList<String>(); private static final String DELETE_ADDON = "f.style.display = 'none'; " + "this.parentNode.appendChild(f); " + "f.method = 'POST'; " + "f.action = this.href; " + "var m = document.createElement('input'); " + "m.setAttribute('type', 'hidden'); " + "m.setAttribute('name', '_method'); " + "m.setAttribute('value', 'delete'); " + "f.appendChild(m); "; static { html5Keys.add("data-ajax"); html5Keys.add("data-confirm"); html5Keys.add("data-method"); html5Keys.add("data-target"); html5Keys.add("data-handler"); html5Keys.add("data-type"); buttonKeys.addAll(html5Keys); buttonKeys.add("disabled"); buttonKeys.add("name"); buttonKeys.add("type"); buttonKeys.add("value"); buttonKeys.add("id"); buttonKeys.add("class"); buttonKeys.add("title"); buttonKeys.add("style"); buttonKeys.add("dir"); buttonKeys.add("lang"); buttonKeys.add("accesskey"); buttonKeys.add("tabindex"); buttonKeys.add("onblur"); buttonKeys.add("onclick"); buttonKeys.add("ondblclick"); buttonKeys.add("onfocus"); buttonKeys.add("onkeydown"); buttonKeys.add("onkeypress"); buttonKeys.add("onkeyup"); buttonKeys.add("onmousedown"); buttonKeys.add("onmousemove"); buttonKeys.add("onmouseout"); buttonKeys.add("onmouseover"); buttonKeys.add("onmouseup"); imageKeys.addAll(html5Keys); imageKeys.add("align");//deprecated in HTML4.01, not supported in XHTML 1.0 Strict DTD imageKeys.add("alt"); imageKeys.add("border");//deprecated in HTML4.01, not supported in XHTML 1.0 Strict DTD imageKeys.add("class"); imageKeys.add("dir"); imageKeys.add("height"); imageKeys.add("hspace");//deprecated in HTML4.01, not supported in XHTML 1.0 Strict DTD imageKeys.add("id"); imageKeys.add("ismap"); imageKeys.add("lang"); imageKeys.add("longdesc"); imageKeys.add("name"); imageKeys.add("onclick"); imageKeys.add("ondblclick"); imageKeys.add("onkeydown"); imageKeys.add("onkeypress"); imageKeys.add("onkeyup"); imageKeys.add("onmousedown"); imageKeys.add("onmousemove"); imageKeys.add("onmouseout"); imageKeys.add("onmouseover"); imageKeys.add("onmouseup"); imageKeys.add("src"); imageKeys.add("style"); imageKeys.add("title"); imageKeys.add("usemap"); imageKeys.add("vspace");//deprecated in HTML4.01, not supported in XHTML 1.0 Strict DTD imageKeys.add("width"); inputKeys.addAll(html5Keys); inputKeys.add("disabled"); inputKeys.add("name"); inputKeys.add("type"); inputKeys.add("value"); //standard attributes: inputKeys.addAll(html5Keys); inputKeys.add("id"); inputKeys.add("class"); inputKeys.add("title"); inputKeys.add("style"); inputKeys.add("dir"); inputKeys.add("lang"); //event attributes: inputKeys.add("accesskey"); inputKeys.add("tabindex"); inputKeys.add("onblur"); inputKeys.add("onclick"); inputKeys.add("ondblclick"); inputKeys.add("onfocus"); inputKeys.add("onkeydown"); inputKeys.add("onkeypress"); inputKeys.add("onkeyup"); inputKeys.add("onmousedown"); inputKeys.add("onmousemove"); inputKeys.add("onmouseout"); inputKeys.add("onmouseover"); inputKeys.add("onmouseup"); inputKeys.add("onselect"); inputKeys.add("onchange"); linkKeys.addAll(html5Keys); linkKeys.add("accesskey"); linkKeys.add("charset"); linkKeys.add("class"); linkKeys.add("coords"); linkKeys.add("dir"); linkKeys.add("hreflang"); linkKeys.add("id"); linkKeys.add("lang"); linkKeys.add("name"); linkKeys.add("onblue"); linkKeys.add("onclick"); linkKeys.add("ondblclick"); linkKeys.add("onfocus"); linkKeys.add("onkeydown"); linkKeys.add("onkeypress"); linkKeys.add("onkeyup"); linkKeys.add("onmousedown"); linkKeys.add("onmousemove"); linkKeys.add("onmouseout"); linkKeys.add("onmouseover"); linkKeys.add("onmouseup"); linkKeys.add("rel"); linkKeys.add("rev"); linkKeys.add("shape"); linkKeys.add("tabindex"); linkKeys.add("target"); linkKeys.add("title"); linkKeys.add("type"); styleKeys.add("background-color"); styleKeys.add("background-image"); styleKeys.add("background-repeat"); styleKeys.add("border-color"); styleKeys.add("border-style"); styleKeys.add("border-width"); styleKeys.add("color"); styleKeys.add("font-family"); styleKeys.add("font-size"); styleKeys.add("font-style"); styleKeys.add("height"); styleKeys.add("list-style-image"); styleKeys.add("list-style-type"); styleKeys.add("margin"); styleKeys.add("outline-color"); styleKeys.add("outline-style"); styleKeys.add("outline-width"); styleKeys.add("padding"); styleKeys.add("text-align"); styleKeys.add("text-decoration"); styleKeys.add("vertical-align"); } /** * Returns a url-encoded string. * * @param s the input string * @return encoded string. */ public static String encode(String s) { if (s == null) return s; try { s = URLEncoder.encode(s, "UTF-8"); } catch(UnsupportedEncodingException uee) { ; } return s; } /** * Returns a url-decoded string. * * @param s the input string * @return decoded string. */ public static String decode(String s) { if (s == null) return s; try { s = URLDecoder.decode(s, "UTF-8"); } catch(UnsupportedEncodingException uee) { ; } return s; } /** * Returns an object associated with the key in thread scope. See method * {@link com.scooterframework.web.controller.ActionContext#getFromThreadData(String)}. * * @param key a string * @return object associated with the key */ public static Object thread(String key) { return ActionContext.getFromThreadData(key); } /** * Returns an object associated with the key in parameter scope. See method * {@link com.scooterframework.web.controller.ActionContext#getFromParameterData(String)}. * * @param key a string * @return object associated with the key */ public static Object param(String key) { return ACH.getWAC().getFromParameterData(key); } /** * Returns an object associated with the key in request scope. See method * {@link com.scooterframework.web.controller.ActionContext#getFromRequestData(String)}. * * @param key a string * @return object associated with the key */ public static Object request(String key) { return ACH.getWAC().getFromRequestData(key); } /** * Returns an object associated with the key in session scope. See method * {@link com.scooterframework.web.controller.ActionContext#getFromSessionData(String)}. * * @param key a string * @return object associated with the key */ public static Object session(String key) { return ACH.getWAC().getFromSessionData(key); } /** * Returns an object associated with the key in context scope. See method * {@link com.scooterframework.web.controller.ActionContext#getFromContextData(String)}. * * @param key a string * @return object associated with the key */ public static Object context(String key) { return ACH.getWAC().getFromContextData(key); } /** * Returns an object associated with the key in global scope. See method * {@link com.scooterframework.web.controller.ActionContext#getFromGlobalData(String)}. * * @param key a string * @return object associated with the key */ public static Object global(String key) { return ActionContext.getFromGlobalData(key); } /** * Returns an object associated with the key. See method * {@link com.scooterframework.web.controller.ActionContext#get(String)}. * * @param key a string * @return object associated with the key */ public static Object get(String key) { return ACH.getWAC().get(key); } /** * Returns an object associated with the key. See method * {@link com.scooterframework.web.controller.ActionContext#get(String)}. * * If there is no value associated with the key, a default value is returned. * * @param key a string * @param def default value * @return object associated with the key */ public static Object get(String key, Object def) { Object ret = get(key); return (ret != null)?ret:def; } /** * Returns a string value for a key. The returned value is html-escaped. * * @param key a string * @return string value associated with the key */ public static String value(String key) { Object o = get(key); String result = (o != null)?o.toString():""; return h(result); } /** * Returns a string value for a key. If there is no value found from the * <tt>request</tt>, the key is treated as a field name of the record * and its corresponding value in the record is returned. * * The returned value is html-escaped. * * @param key a string * @param request a http request * @param record an active record instance * @return string value associated with the key */ public static String value(String key, HttpServletRequest request, ActiveRecord record) { String result = request.getParameter(key); if (result == null) { result = (String) request.getAttribute(key); if (result == null && record != null) { Object o = record.getField(key); if (o != null) { result = o.toString(); } } } return h(result); } /** * Checks if the request is a local request. * * @return true if local request */ public static boolean isLocalRequest() { String s = (String) CurrentThreadCache.get(Constants.LOCAL_REQUEST); if ((Constants.VALUE_FOR_LOCAL_REQUEST).equals(s)) { return true; } return false; } /** * Checks whether the object represented by the key has property that is empty. * * @param key * @param property * @return true if the property value is empty. */ public static boolean isEmpty(String key, String property) { return isEmpty(O.getProperty(key, property)); } /** * Checks whether the object has property that is empty. * * @param object * @param property * @return true if the property value is empty. */ public static boolean isEmpty(Object object, String property) { return isEmpty(O.getProperty(object, property)); } /** * Checks whether the object is empty. * * @param object * @return true if the object is empty. */ public static boolean isEmpty(Object object) { return M.isEmpty(object); } /** * Checks whether the Object represented by the key has property value that * is equal to the desired value. * * @param key * @param property * @param desired * @return true if the property has the same value as the desired. */ public static boolean isEqual(String key, String property, Object desired) { return isEqual(O.getProperty(key, property), desired); } /** * Checks whether the Object's property has the same value as the desired value. * * @param object * @param property * @param desired * @return true if the property has the same value as the desired. */ public static boolean isEqual(Object object, String property, Object desired) { return isEqual(O.getProperty(object, property), desired); } /** * Checks whether the object has the same value as the desired value. * * @param object * @param desired * @return true if the object has the same value as the desired. */ public static boolean isEqual(Object object, Object desired) { if (object == null) { return (desired == null)?true:false; } else { if (desired == null) return false; } return (compare(object, desired) == 0)?true:false; } /** * Checks whether the Object represented by the key has property value that * is greater than the desired value. * * @param key * @param property * @param desired * @return true if the property value is greater than the desired. */ public static boolean isGreaterThan(String key, String property, Object desired) { return isGreaterThan(O.getProperty(key, property), desired); } /** * Checks whether the Object's property value is greater than the desired value. * * @param object * @param property * @param desired * @return true if the property value is greater than the desired. */ public static boolean isGreaterThan(Object object, String property, Object desired) { return isGreaterThan(O.getProperty(object, property), desired); } /** * Checks whether the object value is greater than the desired value. * * @param object * @param desired * @return true if the object value is greater than the desired. */ public static boolean isGreaterThan(Object object, Object desired) { return (compare(object, desired) == +1)?true:false; } /** * Checks whether the Object represented by the key has property value that * is greater than or equal to the desired value. * * @param key * @param property * @param desired * @return true if the property value is greater than or equal to the desired. */ public static boolean isGreaterThanOrEqual(String key, String property, Object desired) { return isGreaterThanOrEqual(O.getProperty(key, property), desired); } /** * Checks whether the Object's property value is greater than or equal to the desired value. * * @param object * @param property * @param desired * @return true if the property value is greater than or equal to the desired. */ public static boolean isGreaterThanOrEqual(Object object, String property, Object desired) { return isGreaterThanOrEqual(O.getProperty(object, property), desired); } /** * Checks whether the object value is greater than or equal to the desired value. * * @param object * @param desired * @return true if the object value is greater than or equal to the desired. */ public static boolean isGreaterThanOrEqual(Object object, Object desired) { int res = compare(object, desired); return (res == +1 || res == 0)?true:false; } /** * Checks whether the Object represented by the key has property value that * is less than the desired value. * * @param key * @param property * @param desired * @return true if the property value is less than the desired. */ public static boolean isLessThan(String key, String property, Object desired) { return isLessThan(O.getProperty(key, property), desired); } /** * Checks whether the Object's property value is less than the desired value. * * @param object * @param property * @param desired * @return true if the property value is less than the desired. */ public static boolean isLessThan(Object object, String property, Object desired) { return isLessThan(O.getProperty(object, property), desired); } /** * Checks whether the object value is less than the desired value. * * @param object * @param desired * @return true if the object value is less than the desired. */ public static boolean isLessThan(Object object, Object desired) { return (compare(object, desired) == -1)?true:false; } /** * Checks whether the Object represented by the key has property value that * is less than or equal to the desired value. * * @param key * @param property * @param desired * @return true if the property value is less than or equal to the desired. */ public static boolean isLessThanOrEqual(String key, String property, Object desired) { return isLessThanOrEqual(O.getProperty(key, property), desired); } /** * Checks whether the Object's property value is less than or equal to the desired value. * * @param object * @param property * @param desired * @return true if the property value is less than or equal to the desired. */ public static boolean isLessThanOrEqual(Object object, String property, Object desired) { return isLessThanOrEqual(O.getProperty(object, property), desired); } /** * Checks whether the object value is less than or equal to the desired value. * * @param object * @param desired * @return true if the object value is less than or equal to the desired. */ public static boolean isLessThanOrEqual(Object object, Object desired) { int res = compare(object, desired); return (res == -1 || res == 0)?true:false; } private static int compare(Object object, Object compared) { if (object == null || compared == null) throw new IllegalArgumentException("Either input object or compared object is null."); int result = 0; try { double dObject = Double.parseDouble(object.toString()); double dCompared = Double.parseDouble(compared.toString()); if (dObject < dCompared) { result = -1; } else if (dObject > dCompared) { result = +1; } } catch (NumberFormatException ed) { try { long lObject = Long.parseLong(object.toString()); long lCompared = Long.parseLong(compared.toString()); if (lObject < lCompared) { result = -1; } else if (lObject > lCompared) { result = +1; } } catch (NumberFormatException el) { result = object.toString().compareTo(compared.toString()); } } return result; } /** * Checks whether there is data represent by the key string in a scope. * * @param key * @return true if found */ public static boolean isPresent(String key) { return (ACH.getAC().get(key) != null)?true:false; } /** * <p>Checks whether there is data represent by the key string in a specific * scope.</p> * * <p>There are four accepted scope constants: parameter, request, sesison and * context. All are defined in ActionContext class.</p> * * @param key * @return true if found */ public static boolean isPresent(String key, String scope) { return (ACH.getAC().get(key, scope) != null)?true:false; } /** * Removes the following html unfriendly characters from the input * string:<br/> * double quote("), single quote('), lower than(<), * greater than(>), and &. */ public static String htmlEsacpe(String s) { if (s == null || s.length() == 0) return s; s = StringUtil.replace(s, "&", "&"); s = StringUtil.replace(s, "\"", """); s = StringUtil.replace(s, "'", "'"); s = StringUtil.replace(s, "<", "<"); s = StringUtil.replace(s, ">", ">"); return s; } /** * A short-hand alias of {@link #htmlEsacpe(java.lang.String) htmlEsacpe} method. * * @param s input string * @return html escaped string */ public static String h(String s) { return htmlEsacpe(s); } /** * <p>Returns html select code of a list of data and options. * Data items in the dataList can be any object.</p> * * <p>The <tt>options</tt> string contains name and value pairs of options. * The name and value are separated by colon, while each pair is separated by * semi-colon. </p> * * The follow option keys are supported: * <pre> * options: id:aaa; class:bbb; javascript:ccc; optionId:ddd; optionValue:eee; * selectedId:fff; useBlank:true; prompt:ggg * </pre> * * <pre> * Description of the option keys: * id: html select id * class: css class of the select * javascript: javascript codes embeded in the select * optionId: the property name of the data item object that will be used in the value portion of the option * optionValue: the property name of the data item object that will be used in the display portion of the option * selectedId: selected optionId or optionValue if optionId is not present * useBlank: add a blank option. If the useBlank is not "false", its value is display in the option. * prompt: a prompt option displayed only when useBlank if not present. For example, <option>Select a category</option> * </pre> * <pre> * Examples: * 1. nameList: Cindy, Dave, Eddy, Frank * displayHtmlSelect("person", nameList, "id:fname;class:big;useBlank:None") * The generated select html code is: * <select id=fname, class=big, name=person> * <option>None</option> * <option value="Cindy">Cindy</option> * <option value="Dave">Dave</option> * <option value="Eddy">Eddy</option> * <option value="Frank">Frank</option> * </select> * * 2. stateList: a list of states {(id=1, name=CA), (id=2, name=MA), (id=3, name=VA)} * displayHtmlSelect("state", stateList, "optionId:id;optionValue:name;prompt:Select a state;selectedId:3") * The generated select html code is: * <select id=fname, class=big, name=person> * <option>Select a state</option> * <option value="1">CA</option> * <option value="2">MA</option> * <option value="3" selected="selected">VA</option> * </select> * </pre> * @param name the name of the select * @param dataList the list of data to be used in the select * @param options configuration options of the select * @return html select string */ public static String displayHtmlSelect(String name, List<?> dataList, String options) { Map<String, String> optionsMap = Converters.convertStringToMap(options, ":", ";"); String id = optionsMap.get("id"); String cssClass = optionsMap.get("class"); String javascript = optionsMap.get("javascript"); String optionId = optionsMap.get("optionId"); String optionValue = optionsMap.get("optionValue"); String selectedId = optionsMap.get("selectedId"); String useBlank = optionsMap.get("useBlank"); if (useBlank == null) useBlank = "false"; String prompt = optionsMap.get("prompt"); StringBuilder selectSB = new StringBuilder(); selectSB.append("<select "); if (id != null && !"".equals(id)) selectSB.append("id=\"").append(id).append("\" "); if (cssClass != null && !"".equals(cssClass)) selectSB.append("class=\"").append(cssClass).append("\" "); if (name != null && !"".equals(name)) selectSB.append("name=\"").append(name).append("\" "); if (javascript != null && !"".equals(javascript)) selectSB.append(javascript).append(" "); selectSB.append(">"); if (!"false".equalsIgnoreCase(useBlank)) { selectSB.append("<option>").append(useBlank).append("</option>"); } else { if (prompt != null) { selectSB.append("<option>").append(prompt).append("</option>"); } } if (dataList != null) { Iterator<?> it = dataList.iterator(); while (it.hasNext()) { Object o = it.next(); String oid = ""; if (optionId != null) { oid = O.property(o, optionId); } else { oid = T.text(o); } selectSB.append("<option value=\"").append(oid).append("\""); if (selectedId != null && selectedId.equals(oid)) selectSB.append(" selected=\"selected\""); selectSB.append(">"); String ovalue = ""; if (optionValue != null) { ovalue = O.property(o, optionValue); } else { ovalue = T.text(o); } selectSB.append(ovalue); selectSB.append("</option>"); } } selectSB.append("</select>"); return selectSB.toString(); } @SuppressWarnings("unchecked") public static String displayHtmlSelect(String name, String dataListKey, String options) { return displayHtmlSelect(name, (List<Object>)get(dataListKey), options); } /** * Returns a string of Yahoo-style pagination page links. The following * is an example of the Yahoo-style page links for page limit of 10: * * <pre> * Showing 31 - 40 of 42 First | Previous | Next | Last * </pre> * * @param paginator The Paginator instance * @param actionPath Path to an action * @return a string of Yahoo-style pagination links */ public static String yahooStylePageLinks(Paginator paginator, String actionPath) { return yahooStylePageLinks(paginator, actionPath, (String)null); } /** * Returns a string of Yahoo-style pagination page links. The following * is an example of the Yahoo-style page links for page limit of 10: * * <pre> * Showing 31 - 40 of 42 First | Previous | Next | Last * </pre> * * @param paginator The Paginator instance * @param actionPath Path to an action * @param linkProperties string of link related properties * @return a string of Yahoo-style pagination links */ public static String yahooStylePageLinks(Paginator paginator, String actionPath, String linkProperties) { if (paginator == null || paginator.getTotalCount() == 0) return ""; String queryStringFirst = paginator.getQueryStringFirst(); String queryStringPrevious = paginator.getQueryStringPrevious(); String queryStringNext = paginator.getQueryStringNext(); String queryStringLast = paginator.getQueryStringLast(); StringBuilder linkSB = new StringBuilder(); linkSB.append("Showing ").append(paginator.getStartIndex()).append(" - "); linkSB.append(paginator.getEndIndex()).append(" of ").append(paginator.getTotalCount()).append(" "); if (linkProperties == null || "".equals(linkProperties)) { linkProperties = ""; } else { linkProperties += "; "; } if (queryStringFirst == null || "".equals(queryStringFirst)) { linkSB.append("First"); } else { linkSB.append(pageLink("First", actionPath, queryStringFirst, linkProperties)); } linkSB.append(" | "); if (queryStringPrevious == null || "".equals(queryStringPrevious)) { linkSB.append("Previous"); } else { linkSB.append(pageLink("Previous", actionPath, queryStringPrevious, linkProperties)); } linkSB.append(" | "); if (queryStringNext == null || "".equals(queryStringNext)) { linkSB.append("Next"); } else { linkSB.append(pageLink("Next", actionPath, queryStringNext, linkProperties)); } linkSB.append(" | "); if (queryStringLast == null || "".equals(queryStringLast)) { linkSB.append("Last"); } else { linkSB.append(pageLink("Last", actionPath, queryStringLast, linkProperties)); } return linkSB.toString(); } /** * Returns a string of pagination page links. * * @param paginator The Paginator instance * @param actionPath Path to an action * @return a string of pagination links */ public static String paginationLinks(Paginator paginator, String actionPath) { return paginationLinks(paginator, actionPath, (String)null); } /** * Returns a string of pagination page links. * * @param paginator The Paginator instance * @param actionPath Path to an action * @param linkProperties string of link related properties * @return a string of pagination links */ public static String paginationLinks(Paginator paginator, String actionPath, String linkProperties) { if (paginator == null || paginator.getTotalCount() == 0) return ""; int total = paginator.getPageCount(); int current = paginator.getCurrentPage(); int beginWindowIndex = 1; int endWindowIndex = total; if (linkProperties == null || "".equals(linkProperties)) { linkProperties = ""; } else { linkProperties += "; "; } StringBuilder selectSB = new StringBuilder(); //link for window for(int i=beginWindowIndex; i<=endWindowIndex; i++) { if (i==current) { //example: <span class="current">9</span> selectSB.append("<span class=\"current\">" + i + "</span>"); } else { //example: <a href="/page10" title="Go to page 10">10</a> selectSB.append(pageLink("" + i, actionPath, paginator.getQueryStringPage(i), linkProperties + "title:Go to page " + i)); } selectSB.append(" "); } return selectSB.toString(); } /** * Returns a string of window-style pagination page links. * * @param paginator The Paginator instance * @param actionPath Path to an action * @return a string of window-style pagination links */ public static String windowStylePageLinks(Paginator paginator, String actionPath) { return windowStylePageLinks(paginator, actionPath, (String)null); } /** * Returns a string of window-style pagination page links. * * @param paginator The Paginator instance * @param actionPath Path to an action * @param linkProperties string of link related properties * @return a string of window-style pagination links */ public static String windowStylePageLinks(Paginator paginator, String actionPath, String linkProperties) { return windowStylePageLinks(paginator, actionPath, -1, -1, linkProperties); } /** * Returns a string of pagination page links. The following is an example * of the window-style page links for size=4 and window=11: * * <pre> * � Previous 1 2 3 4 .... 6 7 8 9 10 11 12 13 14 15 16 .... 18 19 20 21 Next � * </pre> * * @param paginator The Paginator instance * @param actionPath Path to an action * @param side Size of side window * @param window Width of total page links shown * @return a string of window-style pagination links */ public static String windowStylePageLinks(Paginator paginator, String actionPath, int side, int window) { return windowStylePageLinks(paginator, actionPath, side, window, (String)null); } /** * Returns a string of pagination page links. The following is an example * of the window-style page links for size=4 and window=11: * * <pre> * � Previous 1 2 3 4 .... 6 7 8 9 10 11 12 13 14 15 16 .... 18 19 20 21 Next � * </pre> * * @param paginator The Paginator instance * @param actionPath Path to an action * @param side Size of side window * @param window Width of total page links shown * @param linkProperties string of link related properties * @return a string of window-style pagination links */ public static String windowStylePageLinks(Paginator paginator, String actionPath, int side, int window, String linkProperties) { if (paginator == null || paginator.getTotalCount() == 0) return ""; if (side <= 0) side = 0; int total = paginator.getPageCount(); if (window <= 0) window = total;//show all pages int current = paginator.getCurrentPage(); int beginWindowIndex = (current - (window-1)/2); int endWindowIndex = beginWindowIndex + window -1; boolean showLHS = ((side != 0) && (beginWindowIndex > (side + 1)))?true:false; if (!showLHS) beginWindowIndex = 1; boolean showRHS = ((side != 0) && (endWindowIndex < (total-side)))?true:false; if (!showRHS) endWindowIndex = total; boolean addLinkOnPrev = (current > 1)?true:false; boolean addLinkOnNext = (current < total)?true:false; if (linkProperties == null || "".equals(linkProperties)) { linkProperties = ""; } else { linkProperties += "; "; } StringBuilder selectSB = new StringBuilder(); //link for prev if (addLinkOnPrev) { //example: <a href="/page8" class="nextprev" title="Go to Previous Page">« Previous</a> selectSB.append(pageLink("« Previous", actionPath, paginator.getQueryStringPrevious(), linkProperties + "class:nextprev; title:Go to Previous Page")); } else { //example: <span class="nextprev">« Previous</span> selectSB.append("<span class=\"nextprev\">« Previous</span>"); } selectSB.append(" "); //link for LHS if (showLHS) { //example: <a href="/page1" title="Go to page 1">1</a> // <a href="/page2" title="Go to page 2">2</a> // <span>….</span> for (int i=1; i<=side; i++) { selectSB.append(pageLink("" + i, actionPath, paginator.getQueryStringPage(i), linkProperties + "title:Go to page " + i)); selectSB.append(" "); } selectSB.append("<span>….</span>").append(" "); } //link for window for(int i=beginWindowIndex; i<=endWindowIndex; i++) { if (i==current) { //example: <span class="current">9</span> selectSB.append("<span class=\"current\">" + i + "</span>"); } else { //example: <a href="/page10" title="Go to page 10">10</a> selectSB.append(pageLink("" + i, actionPath, paginator.getQueryStringPage(i), linkProperties + "title:Go to page " + i)); } selectSB.append(" "); } //link for RHS if (showRHS) { selectSB.append("<span>….</span>").append(" "); //example: <span>….</span> // <a href="/page193" title="Go to page 193">193</a> // <a href="/page194" title="Go to page 194">194</a> for (int i=total-side+1; i<=total; i++) { selectSB.append(pageLink("" + i, actionPath, paginator.getQueryStringPage(i), linkProperties + "title:Go to page " + i)); selectSB.append(" "); } } //link for next if (addLinkOnNext) { //example: <a href="/page10" class="nextprev" title="Go to Next Page">Next »</a> selectSB.append(pageLink("Next »", actionPath, paginator.getQueryStringNext(), linkProperties + "class:nextprev, title:Go to Next Page")); } else { //example: <span class="nextprev">Next »</span> selectSB.append("<span class=\"nextprev\">Next »</span>"); } return selectSB.toString(); } /** * <p>Sets a response header with the given name and value. If the header had * already been set, the new value overwrites the previous one. Otherwise, * the value is added to the response. </p> * * <p>Example: "Location:", "http://www.google.com/mypage.jsp"</p> * * @param name the name of the header * @param value the header value If it contains octet string, it should be * encoded according to RFC 2047. */ public static void setHTTPResponseHeader(String name, String value) { HttpServletResponse response = ACH.getWAC().getHttpServletResponse(); if (response != null) { if (response.containsHeader(name)) { response.setHeader(name, value); } else { response.addHeader(name, value); } } } /** * <p>Sets a response header with the given name and date-value. The date is * specified in terms of milliseconds since the epoch. If the header had * already been set, the new value overwrites the previous one.</p> * * @param name the name of the date header * @param value the header value If it contains octet string, it should be * encoded according to RFC 2047. */ public static void setHTTPResponseDateHeader(String name, long value) { HttpServletResponse response = ACH.getWAC().getHttpServletResponse(); if (response != null) { if (response.containsHeader(name)) { response.setDateHeader(name, value); } else { response.addDateHeader(name, value); } } } /** * Redirects to a new address. * * Examples of address: * <ol> * <li>Absolute url: "http://www.google.com"</li> * <li>Relative uri: "/shop/order.do"</li> * <li>Relative uri: "order.do"</li> * </ol> * * @param uri a new location to go * @return boolean status of the redirect. True if success. */ public static boolean redirect(String uri) { if (uri == null) return false; boolean success = true; try { String contextPath = getContextPath(); if (!uri.startsWith("/")) uri = "/" + uri; if (uri.indexOf(contextPath) == -1) { uri = contextPath + uri; } HttpServletResponse response = ACH.getWAC().getHttpServletResponse(); if (response != null) { response.sendRedirect(response.encodeRedirectURL(uri)); } } catch(Exception ex) { success = false; } return success; } public static void doForward( String uri, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { if (uri != null && !uri.startsWith("/")) uri = "/" + uri; RequestDispatcher rd = request.getSession().getServletContext().getRequestDispatcher(uri); if (rd == null) { uri = "/WEB-INF/views/404.jsp"; log.error("Unable to locate \"" + uri + "\", forward to " + uri); rd = request.getSession().getServletContext().getRequestDispatcher(uri); } rd.forward(request, response); } /** * Returns context name of the webapp. */ public static String getContextName() { String contextPath = getContextPath(); return (contextPath.startsWith("/"))?contextPath.substring(1):contextPath; } /** * Returns context path of a http request. Context path is the same as * document root. */ public static String getContextPath() { return getHttpRequest().getContextPath(); } /** * Returns document root of a http request. */ public static String getDocumentRoot() { return getContextPath(); } /** * <p>Returns real path of the http servlet context. </p> * * <p>Examples:</p> * <p> * If the myapp.war is running under E:\tomcat\webapps, the real path of the context * is E:\tomcat\webapps\myapp * </p> */ public static String getRealPath() { String rp = getHttpRequest().getSession().getServletContext().getRealPath(""); return rp; } /** * <p>Returns controller path associated with the servlet request.</p> * <pre> * Examples: * servletPath controllerPath * ---------------------------------------------- * /posts/list.do /posts * /report/posts/list.do /report/posts * /posts /posts * </pre> * * @return string current controller path */ public static String getCurrentControllerPath() { return CurrentThreadCacheClient.controllerPath(); } /** * Returns http servlet request instance. * * @return http servlet request instance */ public static HttpServletRequest getHttpRequest() { return ACH.getWAC().getHttpServletRequest(); } //========================================================= // helper methods for rendering a html form. //========================================================= /** * <p>Returns a html segment for selected choice. If the <tt>choice</tt> is * the same as the <tt>value</tt>, the returned html string will be * <tt>selected="selected"</tt>.</p> * * @return html element * @param value the value on the selection list * @param choice the value chosen by the user */ public static String checkSelect(String value, String choice) { if (value == null || choice == null) return ""; return value.equals(choice)?"selected=\"selected\"":""; } /** * <p>Returns a html segment for checked choice. If the <tt>choice</tt> is * the same as the <tt>value</tt>, the returned html string will be * <tt>checked="checked"</tt>. This is for html radio button and checkbox.</p> * * @return html element * @param value the value on the selection list * @param choice the value chosen by the user */ public static String checkCheckbox(String value, String choice) { if (value == null || choice == null) return ""; return value.equals(choice)?"checked=\"checked\"":""; } /** * <p>Returns a url. By default, this method returns relative url. </p> * * <p>See description of {@link #getURL(java.lang.String, * java.util.Map)} method for more details and examples.</p> * * @param actionPath path to an action * @return a url string */ public static String getURL(String actionPath) { return getURL(actionPath, (Map<String, String>)null); } /** * <p>Returns a url. By default, this method returns relative url. </p> * * <p>See description of {@link #getURL(java.lang.String, * java.util.Map)} method for more details and examples.</p> * * @param actionPath path to an action * @param options a string of options * @return a url string */ public static String getURL(String actionPath, String options) { return getURL(actionPath, Converters.convertStringToMap(options, ":", ";")); } /** * <p>Returns a url. By default, this method returns relative url. </p> * * <p> * The format of the generated url depends on values in options map. </p> * * <p> * The options string contains name and value pairs of options. The name and * value are separated by colon, while each pair is separated by semi-colon. </p> * * The following keys are supported in the options: * <pre> * <tt>extension</tt>: overrides the default (current) extension if provided * <tt>fullurl</tt>: if true, a fully qualified URL like http://example.com/posts/list.do is returned. Default is false. * <tt>protocol</tt>: overrides the default (http) protocol if provided * <tt>host</tt>: overrides the default (current) host if provided * <tt>port</tt>: overrides the default (current) port if provided * <tt>encode</tt>: if true, the url's query string is encoded. Default is false. * </pre> * * The generated url string may have the following format: * <pre> * actionPath options returned url * -------------------------------------------------------------------- * list /posts/list.do * /posts/list extension:dx /posts/list.dx * list extension:dx;fullurl:true http://example.com/demo/posts/list.dx * /posts/list fullurl:true;protocol:https; https://another.com:8080/demo/posts/list.do * host:another.com;port:8080 * </pre> * * <p> * If <tt>actionPath</tt> is empty, an empty string (not url) is returned.</p> * * @param actionPath path to an action * @param options a map of options * @return a url string */ public static String getURL(String actionPath, Map<String, String> options) { if (actionPath == null || "".equals(actionPath)) return ""; if (options == null) options = new HashMap<String, String>(); if (!actionPath.startsWith("/")) { String controllerPath = getCurrentControllerPath(); if (controllerPath != null) { if (!controllerPath.startsWith("/")) controllerPath = "/" + controllerPath; actionPath = controllerPath + "/" + actionPath; } else { actionPath = "/" + actionPath; } } if (actionPath.indexOf('.') == -1) { String extension = options.get("extension"); if (extension != null) { actionPath = actionPath + "." + extension; } else { actionPath += EnvConfig.getInstance().getActionExtension(); } } String url = actionPath; String al = actionPath.toLowerCase(); if (!al.startsWith("http")) { String contextPath = getContextPath(); String cp = (contextPath.endsWith("/"))?contextPath:(contextPath + "/"); if (!al.startsWith(cp.toLowerCase())) { url = contextPath + url; } } String fullurl = options.get("fullurl"); if ("true".equals(fullurl)) { String protocol = options.get("protocol"); if (protocol == null) protocol = "http"; HttpServletRequest request = getHttpRequest(); String host = options.get("host"); if (host == null) host = request.getServerName(); String port = options.get("port"); if (port == null) port = "" + request.getServerPort(); port = ("80".equals(port))?"":(":" + port); url = protocol + "://" + host + port + url; } String encode = options.get("encode"); if ("true".equals(encode)) { String pureURL = ""; String queryString = null; int qmark = url.indexOf('?'); if (qmark != -1) { pureURL = url.substring(0, qmark); queryString = url.substring(qmark + 1); try { queryString = URLEncoder.encode(queryString, "UTF-8"); } catch(UnsupportedEncodingException uee) { throw new IllegalArgumentException("The encoding char set is not valid."); } url = pureURL + "?" + queryString; } } return url; } /** * Returns non-query string portion of an url. A pure url string is * anything before the first <tt>?</tt> in an url. */ public static String getPureURLString(String url) { String pureURL = url; int qmark = url.indexOf('?'); if (qmark != -1) { pureURL = url.substring(0, qmark); } return pureURL; } /** * Returns query string portion of an url. A query string is anything after * the first <tt>?</tt> in an url. A <tt>null</tt> is returned if there is * no <tt>?</tt> character in the url. */ public static String getQueryString(String url) { String queryString = null; int qmark = url.indexOf('?'); if (qmark != -1) { queryString = url.substring(qmark + 1); } return queryString; } /** * <p>Returns a button tag.</p> * * <p>Example: </p> * For face = "<tt>Click Here!</tt>", the result button tag is * <button>Click Here!</button> * * @param face display of the button * @return a string of html button code */ public static String buttonTag(String face) { return buttonTag(face, (Map<String, String>)null); } /** * <p>Returns a button tag.</p> * * <p>The <tt>buttonProperties</tt> string contains name and value pairs * of options. The name and value are separated by colon, while each pair * is separated by semi-colon. </p> * * <p>Supported keys for <tt>buttonProperties</tt> are listed in the * description of this class.</p> * * <pre> * Example: * For face = "Click Here!", * buttonProperties = "id:fish; name:fish", the result button tag is * <button id="fish" name="fish">Click Here!</button> * </pre> * * @param face display of the button * @param buttonProperties string of button related properties * @return a string of html button code */ public static String buttonTag(String face, String buttonProperties) { return buttonTag(face, Converters.convertStringToMap(buttonProperties, ":", ";")); } /** * <p>Returns a button tag.</p> * * <p>The <tt>buttonProperties</tt> string contains name and value pairs * of options. The name and value are separated by colon, while each pair * is separated by semi-colon. </p> * * <p>Supported keys for <tt>buttonProperties</tt> are listed in the * description of this class.</p> * * <pre> * Example: * For face = "Click Here!", * buttonProperties = "id:fish; name:fish", the result button tag is * <button id="fish" name="fish">Click Here!</button> * </pre> * * @param face display of the button * @param buttonProperties map of button related properties * @return a string of html button code */ public static String buttonTag(String face, Map<String, String> buttonProperties) { StringBuilder isb = new StringBuilder(); isb.append("<button"); String s = convertButtonPropertiesToString(buttonProperties); if (s != null && !"".equals(s)) { isb.append(" ").append(s); } isb.append(">").append(face).append("</button>"); return isb.toString(); } /** * <p>Returns a url link on a button. This does not work well with IE7. Use * a corresponding <tt>submitButtonLink</tt> method instead. </p> * * <p>Supported keys for <tt>buttonProperties</tt> are listed in the * description of this class.</p> * * <p>See description of {@link #buttonLink(java.lang.String, java.util.Map, * java.lang.String, java.util.Map)} method for more details and examples.</p> * * @param face display of the button * @param buttonProperties button related properties * @param actionPath path to an action * @return a url link */ public static String buttonLink(String face, String buttonProperties, String actionPath) { return buttonLink(face, buttonProperties, actionPath, null); } /** * <p>Returns a url link on a button. This does not work well with IE7. Use * a corresponding <tt>submitButtonLink</tt> method instead. </p> * * <p>Supported keys for <tt>buttonProperties</tt> are listed in the * description of this class.</p> * * <p>Supported keys for <tt>linkProperties</tt> are listed in the * description of this class.</p> * * <p>See description of {@link #buttonLink(java.lang.String, java.util.Map, * java.lang.String, java.util.Map)} method for more details and examples.</p> * * @param face display of the button * @param buttonProperties button related properties * @param actionPath path to an action * @param linkProperties http link related properties * @return a url link */ public static String buttonLink(String face, String buttonProperties, String actionPath, String linkProperties) { return buttonLink(face, Converters.convertStringToMap(buttonProperties, ":", ";"), actionPath, Converters.convertStringToMap(linkProperties, ":", ";")); } /** * <p>Returns a url link on a button. This does not work well with IE7. Use * a corresponding <tt>submitButtonLink</tt> method instead. </p> * * <p>Supported keys for <tt>buttonProperties</tt> are listed in the * description of this class.</p> * * <p>Supported keys for <tt>linkProperties</tt> are listed in the * description of this class.</p> * * <pre> * Example: * For face = "Click Here!", * buttonProperties = "id:fish; name:fish", and * actionPath = "/category/show?catid=FISH", * linkProperties = "confirm:'Do you agree?';id:good", * the result button link is * <a href="/jpetstore/category/show?catid=FISH" onclick="return confirm('Do you agree?');" id="good"><button id="fish" name="fish">Click Here!</button></a> * </pre> * * @param face display of the button * @param buttonProperties button related properties * @param actionPath path to an action * @param linkProperties http link related properties * @return a url link */ public static String buttonLink(String face, Map<String, String> buttonProperties, String actionPath, Map<String, String> linkProperties) { return labelLink(buttonTag(face, buttonProperties), actionPath, linkProperties); } /** * Returns a html submit button. * * <p>See description of {@link #submitButtonLink(java.lang.String, * java.lang.String, java.util.Map)} method for more details and examples.</p> * * @param face display of the button * @param actionPath path to an action * @return a html submit button in a form */ public static String submitButtonLink(String face, String actionPath) { return submitButtonLink(face, actionPath, (Map<String, String>)null); } /** * Returns a html submit button. * * <p>See description of {@link #submitButtonLink(java.lang.String, * java.lang.String, java.util.Map)} method for more details and examples.</p> * * @param face display of the button * @param actionPath path to an action * @param options http input related properties * @return a html submit button in a form */ public static String submitButtonLink(String face, String actionPath, String options) { return submitButtonLink(face, actionPath, Converters.convertStringToMap(options, ":", ";")); } /** * <p>Returns a html submit button.</p> * * The <tt>options</tt> string contains name and value pairs of options. * The name and value are separated by colon <tt>:</tt>, while each pair * <p>is separated by semi-colon <tt>;</tt>. </p> * * <p>Supported options are:</p> * <ul> * <li>all options specified in {@link #getURL(java.lang.String, java.util.Map)} method.</li> * <li>confirm: This is the same as "onclick:return confirm('Do you agree?')". * A html part like <tt>onclick="return confirm('Do you agree?');"</tt> will be added to the link.</li> * <li>method: http operation to be used by the form. Default is "<tt>GET</tt>".</li> * <li>many other html and css key attributes--see the <tt>inputKeys</tt> section of the description of this class.</li> * </ul> * * <p> * Examples * <pre> * submitButtonLink("Back to Home", "/category/show?catid=FISH", "confirm:'Do you agree?';id:fish") * result submit: * <form method="GET" action="/jpetstore/category/show"> * <input type="hidden" name="catid" value="FISH"/> * <input value="Back to Home" type="submit" onclick="return confirm('Do you agree?');" id="fish" /> * </form> * * You can also use the following because <tt>onclick</tt> is a key attribute: * submitButtonLink("Back to Home", "/category/show?catid=FISH", "method:post;onclick:return confirm('Do you agree?');id:fish") * </pre> * </p> * * @param face display of the button * @param actionPath path to an action * @param options http input related properties * @return a html submit button in a form */ public static String submitButtonLink(String face, String actionPath, Map<String, String> options) { String url = getURL(actionPath, options); String confirm = getMapProperty(options, "confirm", null); if (confirm != null) { if (!confirm.startsWith("'") && !confirm.endsWith("'")) confirm = "'" + confirm + "'"; confirm = "return confirm(" + confirm + ");"; options.put("onclick", confirm); } String method = getMapProperty(options, "method", "GET"); StringBuilder sb = new StringBuilder(); String queryString = getQueryString(url); if (queryString != null && !"".equals(queryString)) { String pureURL = getPureURLString(url); sb.append("<form method=\"").append(method).append("\" action=\"").append(pureURL).append("\">"); sb.append(convertQueryStringToHiddenFields(queryString)); } else { sb.append("<form method=\"").append(method).append("\" action=\"").append(url).append("\">"); } sb.append("<input value=\"").append(face).append("\" type=\"submit\" "); sb.append(convertInputPropertiesToString(options)).append(" />"); sb.append("</form>"); return sb.toString(); } //<input type="hidden" name="ownerId" value="1"/> private static StringBuilder convertQueryStringToHiddenFields(String queryString) { StringBuilder sb = new StringBuilder(); Map<String, String> m = Converters.convertStringToMap(queryString, "=", "&"); for (Map.Entry<String, String> entry : m.entrySet()) { sb.append("<input type=\"hidden\" name=\"").append(entry.getKey()); sb.append("\" value=\"").append(entry.getValue()).append("\"/>"); } return sb; } /** * Returns a url link on a label related to a record. * * @param label link label * @param actionPath path to an action * @param record ActiveRecord instance * @return String label link */ public static String labelLinkForRecord(String label, String actionPath, ActiveRecord record) { return labelLinkForRecord(label, actionPath, record, null); } /** * Returns a url link on a label related to a record. * * @param label link label * @param actionPath path to an action * @param record ActiveRecord instance * @param linkProperties string of link related properties * @return url link on the label */ public static String labelLinkForRecord(String label, String actionPath, ActiveRecord record, String linkProperties) { if (isEmpty(label)) return ""; if (record != null) { String qStr = Converters.convertMapToUrlString(record.getPrimaryKeyDataMap()); if (qStr != null && qStr.length() > 0) { if (actionPath.indexOf('?') == -1) { actionPath = actionPath + "?" + qStr; } else { actionPath = actionPath + "&" + qStr; } } } return labelLink(label, actionPath, linkProperties); } /** * <p>Returns a url link on a label.</p> * * <p>See description of {@link #labelLink(java.lang.String, * java.lang.String, java.util.Map)} method for more details and examples.</p> * * @param label link label * @param actionPath path to an action * @return url link on the label */ public static String labelLink(String label, String actionPath) { return labelLink(label, actionPath, (Map<String, String>)null); } /** * <p>Returns a url link on a label.</p> * * <p>See description of {@link #labelLink(java.lang.String, * java.lang.String, java.util.Map)} method for more details and examples.</p> * * @param label link label * @param actionPath path to an action * @param linkProperties string of link related properties * @return url link on the label */ public static String labelLink(String label, String actionPath, String linkProperties) { return labelLink(label, actionPath, Converters.convertStringToMap(linkProperties, ":", ";")); } /** * <p>Returns a url link with query strings on a label.</p> * * <p>The <tt>linkProperties</tt> string contains name and value pairs of * options. The name and value are separated by colon, while each pair is * separated by semi-colon.</p> * * <p>Supported linkProperties are:</p> * <ul> * <li>all options specified in <tt>getURL(String actionPath, String options)</tt> method.</li> * <li>noLinkOnEmptyQueryString: if true, no link is added to the label if the query string is empty. </li> * <li>noLinkOnCurrentUri: if true, no link is added to the label if the current uri is the same as action path. </li> * <li>confirm: This is the same as "onclick:return confirm('Do you agree?')". * A html part like <tt>onclick="return confirm('Do you agree?');"</tt> will be added to the link.</li> * <li>popup: adds a pop-up window.</li> * <li>many other html and css key attributes--see the <tt>linkKeys</tt> section of the description of this class.</li> * </ul> * * <p> * Examples * <pre> * labelLink("Back to Home", "http://www.example.com", "", "confirm:'Do you agree?';id:good") * result link: <a href="http://www.example.com" onclick="return confirm('Do you agree?');" id="good">Back to Home</a> * * You can also use this because <tt>onclick</tt> is a key attribute: * labelLink("Back to Home", "http://www.example.com", "", "onclick:return confirm('Do you agree?');id:good") * result link: <a href="http://www.example.com" onclick="return confirm('Do you agree?');" id="good">Back to Home</a> * * labelLink("Back to Home", "http://www.example.com", "", "popup:true") * result link: <a href="http://www.example.com" onclick="window.open(this.href);return false;">Back to Home</a> * * labelLink("Back to Home", "http://www.example.com", "", "popup:'http://www.google.com','new_window_name','height=440,width=650,resizable,top=200,left=250,scrollbars=yes'") * result link: <a href="http://www.example.com" onclick="window.open('http://www.google.com','new_window_name','height=440,width=650,resizable,top=200,left=250,scrollbars=yes');">Back to Home</a> * </pre> * </p> * * @param label link label * @param actionPath path to an action * @param linkProperties map of link related properties * @return url link on the label */ public static String labelLink(String label, String actionPath, Map<String, String> linkProperties) { if (isEmpty(label)) return ""; if (linkProperties != null) { if ("true".equals(linkProperties.get(W.noLinkOnEmptyQueryString))) { String queryString = getQueryString(actionPath); if (queryString == null || "".equals(queryString.trim())) return label; } } String url = getURL(actionPath, linkProperties); if (linkProperties != null) { String uri = getHttpRequest().getRequestURI(); if ("true".equals(linkProperties.get(noLinkOnCurrentUri)) && url.startsWith(uri)) { return label; } } return createLabelLink(label, url, linkProperties); } private static String createLabelLink(String label, String url, Map<String, String> linkProperties) { if (url == null || "".equals(url)) return label; String confirm = getMapProperty(linkProperties, "confirm", null); if (confirm != null) { if (!confirm.startsWith("'") && !confirm.endsWith("'")) confirm = "'" + confirm + "'"; String method = getMapProperty(linkProperties, "method", null); if (method != null) { if ("delete".equalsIgnoreCase(method)) { StringBuilder sb = new StringBuilder(); sb.append("if (confirm(" + confirm + ")) { "); sb.append("var f = document.createElement('form'); "); sb.append(DELETE_ADDON); sb.append("f.submit(); "); sb.append("}; return false;"); confirm = sb.toString(); } else { confirm = "return confirm(" + confirm + ");"; } } else { confirm = "return confirm(" + confirm + ");"; } linkProperties.put("onclick", confirm); } String popup = getMapProperty(linkProperties, "popup", null); if (popup != null) { if ("true".equals(popup)) { popup = "window.open(this.href);return false"; } else { popup = "window.open(" + popup + ");return false;"; } linkProperties.put("onclick", popup); } StringBuilder sb = new StringBuilder(); sb.append("<a href=\"").append(url).append("\" ").append(convertLinkPropertiesToString(linkProperties)); sb.append(">").append(label).append("</a>"); return sb.toString(); } /** * <p>Returns an image tag.</p> * * Example: * <img src="../images/fish_icon.gif" /> * * @param imageSrc source path to image file * @return a string of html image code */ public static String imageTag(String imageSrc) { return imageTag(imageSrc, (Map<String, String>)null); } /** * <p>Returns an image tag.</p> * * <p>The <tt>imageProperties</tt> string contains name and value pairs * of options. The name and value are separated by colon, while each pair * is separated by semi-colon. </p> * * <p>Supported keys for <tt>imageProperties</tt> are listed in the * <tt>imageKeys</tt> section of the description of this class.</p> * * <pre> * Example: * For imageProperties = "alt:Fish; id:fish; name:fish", the result image tag is * <img src="../images/fish_icon.gif" alt="Fish" id="fish" name="fish" /> * </pre> * * @param imageSrc source path to image file * @param imageProperties string of image related properties * @return a string of html image code */ public static String imageTag(String imageSrc, String imageProperties) { return imageTag(imageSrc, Converters.convertStringToMap(imageProperties, ":", ";")); } /** * <p>Returns an image tag.</p> * * <p>Supported keys for <tt>imageProperties</tt> are in the * <tt>imageKeys</tt> section of the description of this class.</p> * * <pre> * Example: * For imageProperties = "alt:Fish; id:fish; name:fish", the result image tag is * <img src="../images/fish_icon.gif" alt="Fish" id="fish" name="fish" /> * </pre> * * @param imageSrc source path to image file * @param imageProperties map of image related properties * @return a string of html image code */ public static String imageTag(String imageSrc, Map<String, String> imageProperties) { StringBuilder isb = new StringBuilder(); isb.append("<img src=\"").append(imageSrc).append("\" "); isb.append(convertImagePropertiesToString(imageProperties)); isb.append(" />"); return isb.toString(); } /** * <p>Returns a url link on an image.</p> * * <p>See description of {@link #imageLink(java.lang.String, java.util.Map, * java.lang.String, java.util.Map)} method for more details and examples.</p> * * @param imageSrc source path to image file * @param imageProperties image related properties * @param actionPath path to an action * @return a url link */ public static String imageLink(String imageSrc, String imageProperties, String actionPath) { return imageLink(imageSrc, imageProperties, actionPath, null); } /** * <p>Returns a url link on an image.</p> * * <p>See description of {@link #imageLink(java.lang.String, java.util.Map, * java.lang.String, java.util.Map)} method for more details and examples.</p> * * @param imageSrc source path to image file * @param imageProperties image related properties * @param actionPath path to an action * @param linkProperties http link related properties * @return a url link */ public static String imageLink(String imageSrc, String imageProperties, String actionPath, String linkProperties) { return imageLink(imageSrc, Converters.convertStringToMap(imageProperties, ":", ";"), actionPath, Converters.convertStringToMap(linkProperties, ":", ";")); } /** * <p>Returns a url link on an image.</p> * * <p>Supported keys for <tt>imageProperties</tt> are in the <tt>imageKeys</tt> * section of the description of this class.</p> * * <p>Supported keys for <tt>linkProperties</tt> are in the <tt>linkKeys</tt> * section of the description of this class.</p> * * <pre> * Example: * For imageSrc = "../images/fish_icon.gif", * imageProperties = "alt:Fish; id:fish; name:fish", * actionPath = "/category/show?catid=FISH", * linkProperties = "confirm:'Do you agree?';id:good", * the result image link is * <a href="/jpetstore/category/show?catid=FISH" * onclick="return confirm('Do you agree?');" id="good"> * <img src="../images/fish_icon.gif" alt="Fish" id="fish" name="fish" /></a> * </pre> * * @param imageSrc source path to image file * @param imageProperties image related properties * @param actionPath path to an action * @param linkProperties http link related properties * @return a url link */ public static String imageLink(String imageSrc, Map<String, String> imageProperties, String actionPath, Map<String, String> linkProperties) { return labelLink(imageTag(imageSrc, imageProperties), actionPath, linkProperties); } /** * Returns a link to a stylesheet file. * * <link href="/$CONTEXTPATH/stylesheets/main.css" media="screen" rel="stylesheet" type="text/css" /> * @return String link */ public static String stylesheetLink(String stylesheetFileName) { String contextPath = getContextPath(); StringBuilder sb = new StringBuilder(); sb.append("<link href=\"").append(contextPath).append("/stylesheets/").append(stylesheetFileName); sb.append("\" media=\"screen\" rel=\"stylesheet\" type=\"text/css\" />"); return sb.toString(); } /** * Returns a link to a javascript file. * * <script src="/javascripts/prototype.js" type="text/javascript"></script> * @return String link */ public static String javascriptLink(String javascriptFileName) { String contextPath = getContextPath(); StringBuilder sb = new StringBuilder(); sb.append("<script src=\"").append(contextPath).append("/javascripts/").append(javascriptFileName); sb.append("\" type=\"text/javascript\"></script>"); return sb.toString(); } /** * Returns a url link for a page in pagination. * * @param label link label * @param actionPath path to an action * @param queryString query string * @return a url link */ public static String pageLink(String label, String actionPath, String queryString) { String linkProperties = noLinkOnEmptyQueryString + ":true"; return pageLink(label, actionPath, queryString, linkProperties); } /** * Returns a url link for a page in pagination. * * @param pageNumber page number * @param pageTitle title of a page * @param actionPath path to an action * @param queryString query string * @return a url link */ public static String pageLink(int pageNumber, String pageTitle, String actionPath, String queryString) { String linkProperties = "title:" + pageTitle + ";" + noLinkOnEmptyQueryString + ":true"; return pageLink(pageNumber+"", actionPath, queryString, linkProperties); } /** * Returns a url link for a page in pagination. * * @param label link label * @param actionPath path to an action * @param queryString query string * @param linkProperties string of link related properties * @return a url link */ public static String pageLink(String label, String actionPath, String queryString, String linkProperties) { if (linkProperties != null && !"".equals(linkProperties)) { linkProperties += "; " + noLinkOnEmptyQueryString + ":true"; } else { linkProperties = noLinkOnEmptyQueryString + ":true"; } if (actionPath.indexOf('?') != -1) { actionPath += "&" + queryString; } else { actionPath += "?" + queryString; } return labelLink(label, actionPath, linkProperties); } /** * <p>Key to embed in <tt>linkProperties</tt> map or string in order not to * show a link on a label.</p> * * <p> * For example, if it is desired that no link is displayed when query * string is empty, then developer can embed "noLinkOnEmptyQueryString:true" * in the <tt>linkProperties</tt> map or string. * </p> */ public static String noLinkOnEmptyQueryString = "noLinkOnEmptyQueryString"; /** * <p>Key to embed in <tt>linkProperties</tt> map or string in order not to * show a link on a label when the current uri is the same as the * action path.</p> * * <p> * For example, if it is desired that no link is displayed when current * uri is the same as actionPath, then developer can embed "noLinkOnCurrentUri:true" * in the <tt>linkProperties</tt> map or string. * </p> */ public static String noLinkOnCurrentUri = "noLinkOnCurrentUri"; /** * Converts a button properties map to string. * * @param properties a map of key value pairs * @return string */ public static String convertButtonPropertiesToString(Map<String, String> properties) { if (properties == null || properties.size() == 0) return ""; StringBuilder sb = new StringBuilder(); String propertiesString = Converters.convertMapToString(properties, buttonKeys, "=", " ", true); String stylesString = Converters.convertMapToString(properties, styleKeys, ":", "; ", false); if (!"".equals(propertiesString)) sb.append(propertiesString); if (!"".equals(stylesString)) sb.append(" style=\"").append(stylesString).append("\""); return sb.toString(); } /** * Converts an image properties map to string. * * @param properties a map of key value pairs * @return string */ public static String convertImagePropertiesToString(Map<String, String> properties) { if (properties == null || properties.size() == 0) return ""; StringBuilder sb = new StringBuilder(); String propertiesString = Converters.convertMapToString(properties, imageKeys, "=", " ", true); String stylesString = Converters.convertMapToString(properties, styleKeys, ":", "; ", false); if (!"".equals(propertiesString)) sb.append(propertiesString); if (!"".equals(stylesString)) sb.append(" style=\"").append(stylesString).append("\""); return sb.toString(); } /** * Converts a input properties map to string. * * @param properties a map of key value pairs * @return string */ public static String convertInputPropertiesToString(Map<String, String> properties) { if (properties == null || properties.size() == 0) return ""; StringBuilder sb = new StringBuilder(); String propertiesString = Converters.convertMapToString(properties, inputKeys, "=", " ", true); String stylesString = Converters.convertMapToString(properties, styleKeys, ":", "; ", false); if (!"".equals(propertiesString)) sb.append(propertiesString); if (!"".equals(stylesString)) sb.append(" style=\"").append(stylesString).append("\""); return sb.toString(); } /** * Converts a link properties map to string. * * @param properties a map of key value pairs * @return string */ public static String convertLinkPropertiesToString(Map<String, String> properties) { if (properties == null || properties.size() == 0) return ""; StringBuilder sb = new StringBuilder(); String propertiesString = Converters.convertMapToString(properties, linkKeys, "=", " ", true); String stylesString = Converters.convertMapToString(properties, styleKeys, ":", "; ", false); if (!"".equals(propertiesString)) sb.append(propertiesString); if (!"".equals(stylesString)) sb.append(" style=\"").append(stylesString).append("\""); return sb.toString(); } private static String getMapProperty(Map<String, String> options, String key, String def) { if (options == null) return def; String value = options.get(key); return (value == null)?def:value; } /** * <p>Returns a div block containing all the error messages for the * object located as an instance variable by the name of * <tt>model</tt>. </p> * * @param model model name * @return String formatted error message */ public static String errorMessage(String model) { return errorMessage(model, new Properties()); } /** * <p>Returns a div block containing all the error messages for the * object located as an instance variable by the name of * <tt>model</tt>. </p> * * <pre> * This div block can be tailored by the following options: * * <tt>headerTag</tt> - Used for the header of the error div (default: h2) * <tt>header</tt> - Used for the header of the error div (default: "error(s) happened:") * <tt>name</tt> - The name of the error div (default: errorDetails) * <tt>id</tt> - The id of the error div (default: errorDetails) * <tt>class</tt> - The css class of the error div (default: errorDetails) * </pre> * * @param model model name * @param options A Map of options * @return String formatted error message */ public static String errorMessage(String model, Properties options) { String result = ""; List<Message> errors = O.getErrorMessages(model); if (errors != null && errors.size() > 0) { String headerTag = options.getProperty("headerTag", "h2"); String header = options.getProperty("header", "error(s) happened:"); String name = options.getProperty("name", "errorDetails"); String id = options.getProperty("id", "errorDetails"); String css = options.getProperty("class", "errorDetails"); String divProperty = "id=\"" + id + "\" name=\"" + name + "\" class=\"" + css + "\""; result = taggedContent("div", taggedContent(headerTag, errors.size() + " " + header) + taggedContent("ul", parseErrorMessages(errors)), divProperty ); } return result; } private static String parseErrorMessages(List<Message> errors) { StringBuilder sb = new StringBuilder(); if (errors != null && errors.size() > 0) { Iterator<Message> it = errors.iterator(); while(it.hasNext()) { Message vm = (ExpandedMessage)it.next(); String key = vm.getId(); String content = vm.getContent(); String error = ""; if (key != null && !"".equals(key)) { error = key + ": "; } error += content; sb.append(taggedContent("li", error)); } } return sb.toString(); } /** * <p>Returns a formatted html string.</p> * * Example: * <div>3 Errors</div> * * @param tag * @param content * @return String formatted html string */ public static String taggedContent(String tag, String content) { return taggedContent(tag, content, ""); } /** * <p>Returns a formatted html string.</p> * * Example: * <div id="errorMessage" name="errorMessage">3 Errors</div> * * @param tag tag name * @param content content of the tag * @param properties string of properties * @return String formatted html string */ public static String taggedContent(String tag, String content, String properties) { return "<" + tag + " " + properties + ">" + content + "</" + tag + ">"; } /** * <p>Returns a formatted html string.</p> * * Example: * <div id="errorMessage" name="errorMessage">3 Errors</div> * * @param tag tag name * @param content content of the tag * @param properties map of properties * @return String formatted html string */ public static String taggedContent(String tag, String content, Map<String, String> properties) { StringBuilder sb = new StringBuilder(); for(Map.Entry<String, String> entry : properties.entrySet()) { sb.append(entry.getKey()).append("=\"").append(entry.getValue()).append("\" "); } return taggedContent(tag, content, sb.toString()); } /** * <p>Returns a formatted html string. The <tt>object</tt> is used to check if * the record has error. </p> * * Example: * <div id="errorMessage" name="errorMessage">3 Errors</div> * * @param object an object * @param field field name of the object * @param tag tag name * @param content content of the tag * @param properties map of properties * @return String formatted html string */ public static String taggedContent(Object object, String field, String tag, String content, Map<String, String> properties) { String result = taggedContent(tag, content, properties); boolean errorOccurred = false; if (object != null && object instanceof ActiveRecord) { ActiveRecord ar = (ActiveRecord)object; if (!ar.isValid() && ar.getValidationResults().hasErrorOn(field)) { errorOccurred = true; } } if (errorOccurred) { result = W.taggedContent("div", result, "class=\"fieldWithErrors\""); } return result; } /** * Returns an item from a list items. The name of the cycle is <tt>items</tt>. * * Examples: * <pre> * class=<%=WebHelper.cycle("odd, even")%> * -- use "red" class for odd rows and "blue" class for even rows. * </pre> * * @param items list of items to be cycled * @return an item value */ public static String cycle(String items) { return ACH.getWAC().cycle(items); } /** * Returns an item from a list items. * * @param items list of items to be cycled * @param name the cycle name * @return an item value */ public static String cycle(String items, String name) { return ACH.getWAC().cycle(items, name); } /** * Returns current item in the named cycle. * * @param name name of the cycle * @return current item in the named cycle. */ public static String currentCycle(String name) { return ACH.getWAC().currentCycle(name); } /** * Resets the cycle * * @param name cycle's name */ public static void resetCycle(String name) { ACH.getWAC().resetCycle(name); } /** * Returns label value stored in a property file based on user's current * locale. * * @param key label key * @return value associated with the key */ public static String label(String key) { return message(key); } /** * <p>Returns a message associated with the <tt>messageKey</tt>.</p> * * <p>If there is no message associated with the <tt>messageKey</tt> in * messages property files, the <tt>messageKey</tt> itself is used as the message.</p> * * @param messageKey a message key in messages resource files * @return a message string */ public static String message(String messageKey) { Locale locale = ACH.getAC().getLocale(); return message(messageKey, locale); } /** * <p>Returns a message associated with the <tt>messageKey</tt> in the * specific <tt>language</tt>.</p> * * <p>If there is no message associated with the <tt>messageKey</tt> in * messages property files, the <tt>messageKey</tt> itself is used as the message.</p> * * @param messageKey a message key in messages resource files * @param language ISO Language Code * @return a message string */ public static String message(String messageKey, String language) { Locale locale = new Locale(language); return message(messageKey, locale); } /** * <p>Returns a message associated with the <tt>messageKey</tt> in the * specific <tt>language</tt> of the specific <tt>country</tt>.</p> * * <p>If there is no message associated with the <tt>messageKey</tt> in * messages property files, the <tt>messageKey</tt> itself is used as the message.</p> * * @param messageKey a message key in messages resource files * @param language ISO Language Code * @param country ISO Country Code * @return a message string */ public static String message(String messageKey, String language, String country) { Locale locale = new Locale(language, country); return message(messageKey, locale); } /** * <p>Returns a message associated with the <tt>messageKey</tt> in the * specific <tt>language</tt> of the specific <tt>country</tt>'s * particular <tt>variant</tt>.</p> * * <p>If there is no message associated with the <tt>messageKey</tt> in * messages property files, the <tt>messageKey</tt> itself is used as the message.</p> * * @param messageKey a message key in messages resource files * @param language ISO Language Code * @param country ISO Country Code * @param variant Variant string * @return a message string */ public static String message(String messageKey, String language, String country, String variant) { Locale locale = new Locale(language, country, variant); return message(messageKey, locale); } /** * <p>Returns a message associated with the <tt>messageKey</tt> in the * specific <tt>locale</tt>.</p> * * <p>If there is no message associated with the <tt>messageKey</tt> in * messages property files, the <tt>messageKey</tt> itself is used as the message.</p> * * @param messageKey a message key in messages resource files * @param locale a specific locale object * @return a message string */ public static String message(String messageKey, Locale locale) { return Messages.get(messageKey, locale); } /** * Returns a message associated with the <tt>messageKey</tt> and the * <tt>values</tt>. * * <p>If there is no message associated with the <tt>messageKey</tt> in * messages property files, the <tt>messageKey</tt> itself is used as the message.</p> * * @param messageKey a message key in messages resource files * @param values an array of objects to be included in the message * @return a message string */ public static String message(String messageKey, Object[] values) { return Messages.get(messageKey, values); } /** * <p>Returns a message associated with the <tt>messageKey</tt> and the * <tt>values</tt> in a specific <tt>locale</tt>.</p> * * <p>If there is no message associated with the <tt>messageKey</tt> in * messages property files, the <tt>messageKey</tt> itself is used as the message.</p> * * @param messageKey a message key in messages resource files * @param locale a specific locale object * @param values an array of objects to be included in the message * @return a message string */ public static String message(String messageKey, Locale locale, Object[] values) { return Messages.get(messageKey, locale, values); } /** * Returns html text converted by Markdown. * * See http://daringfireball.net/projects/markdown for details. * * @param text a generic text * @return html text converted by Markdown */ public static String markdown(String text) { return mp.markdown(text); } /** * Returns a string formatted for html display by adding <tt>"<br />"</tt> * after each occurance of newlines <tt>"\n"</tt> in a string. * * <pre> * Examples: * "This is a little world. \r\n A little world." => * "This is a little world. \r\n<br /> A little world." * </pre> * * @param text a string with line break * @return altered string */ public static String nl2br(String text) { return text.replaceAll("\n", "\n<br />"); } /** * <p>Returns a label link on the <tt>columnName</tt> for <tt>columnValue</tt> * for <tt>show</tt> action.</p> * * <p>There are several restrictions of the use of this method. </p> * <p>First, the column name must be of the <tt>'{referencedModelName}_id'</tt> * format where the <tt>referencedModelName</tt> is the model name of the * referenced entity. Second, the primary key of the referenced model must * be <tt>id</tt>. </p> * * <p>If the above conditions are violated, the original * <tt>columnName</tt> is returned.</p> * * <pre> * Examples: * column value link * ------ ----- ---- * user_id 10 <a http="/blog/users/show?id=10">10</a> * </pre> * * <p>See method * {@link com.scooterframework.web.util.R#simpleForeignKeyResourceRecordLink(String, String)} * for resource case.</p> * * @param columnName a column name ended with "_id" * @param columnValue the value on the column * @return a label link */ public static String simpleForeignKeyRecordShowActionLink(String columnName, String columnValue) { if (columnName == null || !columnName.toLowerCase().endsWith("_id")) return columnName; String modelName = columnName.toLowerCase().substring(0, (columnName.length() - 3)); String modelClassName = EnvConfig.getInstance().getModelClassName(modelName); ActiveRecord foreignRecordHome = ActiveRecordUtil.getHomeInstance(modelClassName, modelName, ActiveRecordUtil.DEFAULT_RECORD_CLASS); String[] pkNames = foreignRecordHome.getPrimaryKeyNames(); if (pkNames == null || pkNames.length > 1 || !"id".equalsIgnoreCase(pkNames[0])) return columnValue; String controllerName = WordUtil.pluralize(modelName); return W.labelLink(columnValue, "/" + controllerName + "/show" + EnvConfig.getInstance().getActionExtension() + "?id=" + columnValue); } }