Author: adrianc Date: Fri May 31 17:07:00 2013 New Revision: 1488315 URL: http://svn.apache.org/r1488315 Log: Fixed some bugs in the form widget column sorting code:
1. URL parameters were being mangled by clunky string manipulation code. 2. The sort column logic was piggy-backed on the pagination event. I created a new choice for the form widget <on-event-update-area> element - "sort-column" - so column sort events can update their own areas. 3. The parameter name for the sort column was hard-coded, so column sorting in multiple lists on the same screen would not work. I added a new <form> attribute - "sort-field-parameter-name" - so each list can be sorted separately. Modified: ofbiz/trunk/framework/widget/dtd/widget-form.xsd ofbiz/trunk/framework/widget/src/org/ofbiz/widget/form/MacroFormRenderer.java ofbiz/trunk/framework/widget/src/org/ofbiz/widget/form/ModelForm.java Modified: ofbiz/trunk/framework/widget/dtd/widget-form.xsd URL: http://svn.apache.org/viewvc/ofbiz/trunk/framework/widget/dtd/widget-form.xsd?rev=1488315&r1=1488314&r2=1488315&view=diff ============================================================================== --- ofbiz/trunk/framework/widget/dtd/widget-form.xsd (original) +++ ofbiz/trunk/framework/widget/dtd/widget-form.xsd Fri May 31 17:07:00 2013 @@ -115,6 +115,14 @@ under the License. <xs:attribute type="xs:string" name="default-widget-style" /> <xs:attribute type="xs:string" name="default-tooltip-style" /> <xs:attribute type="xs:string" name="default-required-field-style" /> + <xs:attribute type="xs:string" name="sort-field-parameter-name"> + <xs:annotation> + <xs:documentation> + The name of the request parameter that is used for specifying the sorted column. This is required when you + have more than one list on a screen - each list must use its own sort field parameter. Defaults to "sortField". + </xs:documentation> + </xs:annotation> + </xs:attribute> <xs:attribute type="xs:string" name="default-sort-field-style"> <xs:annotation> <xs:documentation>CSS style to used for form sort fields. Defaults to "sort-order".</xs:documentation> @@ -424,6 +432,7 @@ under the License. <xs:simpleType> <xs:restriction base="xs:token"> <xs:enumeration value="paginate" /> + <xs:enumeration value="sort-column" /> <xs:enumeration value="submit" /> </xs:restriction> </xs:simpleType> Modified: ofbiz/trunk/framework/widget/src/org/ofbiz/widget/form/MacroFormRenderer.java URL: http://svn.apache.org/viewvc/ofbiz/trunk/framework/widget/src/org/ofbiz/widget/form/MacroFormRenderer.java?rev=1488315&r1=1488314&r2=1488315&view=diff ============================================================================== --- ofbiz/trunk/framework/widget/src/org/ofbiz/widget/form/MacroFormRenderer.java (original) +++ ofbiz/trunk/framework/widget/src/org/ofbiz/widget/form/MacroFormRenderer.java Fri May 31 17:07:00 2013 @@ -353,7 +353,7 @@ public class MacroFormRenderer implement if (UtilValidate.isNotEmpty(textField.getMask())) { mask = textField.getMask(); } - String ajaxUrl = createAjaxParamsFromUpdateAreas(updateAreas, null, context); + String ajaxUrl = createAjaxParamsFromUpdateAreas(updateAreas, "", context); boolean disabled = textField.disabled; StringWriter sr = new StringWriter(); sr.append("<@renderTextField "); @@ -1091,7 +1091,7 @@ public class MacroFormRenderer implement boolean ajaxEnabled = (updateAreas != null || UtilValidate.isNotEmpty(backgroundSubmitRefreshTarget)) && this.javaScriptEnabled; String ajaxUrl = ""; if (ajaxEnabled) { - ajaxUrl = createAjaxParamsFromUpdateAreas(updateAreas, null, context); + ajaxUrl = createAjaxParamsFromUpdateAreas(updateAreas, "", context); } StringWriter sr = new StringWriter(); sr.append("<@renderSubmitField "); @@ -2037,7 +2037,7 @@ public class MacroFormRenderer implement this.appendContentUrl(imgSrc, "/images/fieldlookup.gif"); String ajaxUrl = ""; if (ajaxEnabled) { - ajaxUrl = createAjaxParamsFromUpdateAreas(updateAreas, null, context); + ajaxUrl = createAjaxParamsFromUpdateAreas(updateAreas, "", context); } String lookupPresentation = lookupField.getLookupPresentation(); if (UtilValidate.isEmpty(lookupPresentation)) { @@ -2679,21 +2679,21 @@ public class MacroFormRenderer implement public void renderSortField(Appendable writer, Map<String, Object> context, ModelFormField modelFormField, String titleText) throws IOException { boolean ajaxEnabled = false; ModelForm modelForm = modelFormField.getModelForm(); - List<ModelForm.UpdateArea> updateAreas = modelForm.getOnPaginateUpdateAreas(); - String targetService = modelForm.getPaginateTarget(context); + List<ModelForm.UpdateArea> updateAreas = modelForm.getOnSortColumnUpdateAreas(); + if (updateAreas == null) { + // For backward compatibility. + updateAreas = modelForm.getOnPaginateUpdateAreas(); + } if (this.javaScriptEnabled) { if (UtilValidate.isNotEmpty(updateAreas)) { ajaxEnabled = true; } } - if (targetService == null) { - targetService = "${targetService}"; - } - if (UtilValidate.isEmpty(targetService) && updateAreas == null) { - Debug.logWarning("Cannot sort because TargetService is empty for the form: " + modelForm.getName(), module); + String paginateTarget = modelForm.getPaginateTarget(context); + if (paginateTarget.isEmpty() && updateAreas == null) { + Debug.logWarning("Cannot sort because the paginate target URL is empty for the form: " + modelForm.getName(), module); return; } - String str = (String) context.get("_QBESTRING_"); String oldSortField = modelForm.getSortField(context); String sortFieldStyle = modelFormField.getSortFieldStyle(); // if the entry-name is defined use this instead of field name @@ -2701,7 +2701,7 @@ public class MacroFormRenderer implement if (UtilValidate.isEmpty(columnField)) { columnField = modelFormField.getFieldName(); } - // switch beetween asc/desc order + // switch between asc/desc order String newSortField = columnField; if (UtilValidate.isNotEmpty(oldSortField)) { if (oldSortField.equals(columnField)) { @@ -2712,36 +2712,31 @@ public class MacroFormRenderer implement sortFieldStyle = modelFormField.getSortFieldStyleAsc(); } } - // strip sortField param from the query string - HashSet<String> paramName = new HashSet<String>(); - paramName.add("sortField"); - String queryString = UtilHttp.stripNamedParamsFromQueryString(str, paramName); - String urlPath = UtilHttp.removeQueryStringFromTarget(targetService); - String prepLinkText = UtilHttp.getQueryStringFromTarget(targetService); - if (UtilValidate.isNotEmpty(queryString)) { - queryString = UtilHttp.encodeAmpersands(queryString); - } - if (prepLinkText == null) { - prepLinkText = ""; - } - if (prepLinkText.indexOf("?") < 0) { - prepLinkText += "?"; - } else if (!prepLinkText.endsWith("?")) { - prepLinkText += "&"; - } - if (!UtilValidate.isEmpty(queryString) && !queryString.equals("null")) { - prepLinkText += queryString + "&"; - } - prepLinkText += "sortField" + "=" + newSortField; - if (ajaxEnabled) { - prepLinkText = prepLinkText.replace("?", ""); - prepLinkText = prepLinkText.replace("&", "&"); - } - String linkUrl = ""; + String queryString = UtilHttp.getQueryStringFromTarget(paginateTarget).replace("?", ""); + Map<String, Object> paramMap = UtilHttp.getQueryStringOnlyParameterMap(queryString); + String qbeString = (String) context.get("_QBESTRING_"); + if (qbeString != null) { + qbeString = qbeString.replaceAll("&", "&"); + paramMap.putAll(UtilHttp.getQueryStringOnlyParameterMap(qbeString)); + } + paramMap.put(modelForm.getSortFieldParameterName(), newSortField); + UtilHttp.canonicalizeParameterMap(paramMap); + String linkUrl = null; if (ajaxEnabled) { - linkUrl = createAjaxParamsFromUpdateAreas(updateAreas, prepLinkText, context); + linkUrl = createAjaxParamsFromUpdateAreas(updateAreas, paramMap, null, context); } else { - linkUrl = rh.makeLink(this.request, this.response, urlPath + prepLinkText); + StringBuilder sb = new StringBuilder("?"); + Iterator<Map.Entry<String, Object>> iter = paramMap.entrySet().iterator(); + while (iter.hasNext()) { + Map.Entry<String, Object> entry = iter.next(); + sb.append(entry.getKey()).append("=").append(entry.getValue()); + if (iter.hasNext()) { + sb.append("&"); + } + } + String newQueryString = sb.toString(); + String urlPath = UtilHttp.removeQueryStringFromTarget(paginateTarget); + linkUrl = rh.makeLink(this.request, this.response, urlPath.concat(newQueryString)); } StringWriter sr = new StringWriter(); sr.append("<@renderSortField "); @@ -2755,7 +2750,47 @@ public class MacroFormRenderer implement sr.append(Boolean.toString(ajaxEnabled)); sr.append(" />"); executeMacro(writer, sr.toString()); + } + /** Create an ajaxXxxx JavaScript CSV string from a list of UpdateArea objects. See + * <code>selectall.js</code>. + * @param updateAreas + * @param extraParams Renderer-supplied additional target parameters + * @param context + * @return Parameter string or empty string if no UpdateArea objects were found + */ + private String createAjaxParamsFromUpdateAreas(List<ModelForm.UpdateArea> updateAreas, Map<String, Object> extraParams, String anchor, Map<String, ? extends Object> context) { + StringBuilder sb = new StringBuilder(); + Iterator<ModelForm.UpdateArea> updateAreaIter = updateAreas.iterator(); + while (updateAreaIter.hasNext()) { + ModelForm.UpdateArea updateArea = updateAreaIter.next(); + sb.append(updateArea.getAreaId()).append(","); + String ajaxTarget = updateArea.getAreaTarget(context); + String urlPath = UtilHttp.removeQueryStringFromTarget(ajaxTarget); + sb.append(this.rh.makeLink(this.request, this.response,urlPath)).append(","); + String queryString = UtilHttp.getQueryStringFromTarget(ajaxTarget).replace("?", ""); + Map<String, Object> parameters = UtilHttp.getQueryStringOnlyParameterMap(queryString); + Map<String, Object> ctx = UtilGenerics.checkMap(context); + Map<String, Object> updateParams = UtilGenerics.checkMap(updateArea.getParameterMap(ctx)); + parameters.putAll(updateParams); + UtilHttp.canonicalizeParameterMap(parameters); + parameters.putAll(extraParams); + Iterator<Map.Entry<String, Object>> paramIter = parameters.entrySet().iterator(); + while (paramIter.hasNext()) { + Map.Entry<String, Object> entry = paramIter.next(); + sb.append(entry.getKey()).append("=").append(entry.getValue()); + if (paramIter.hasNext()) { + sb.append("&"); + } + } + if (anchor != null) { + sb.append("#").append(anchor); + } + if (updateAreaIter.hasNext()) { + sb.append(","); + } + } + return sb.toString(); } /** Create an ajaxXxxx JavaScript CSV string from a list of UpdateArea objects. See Modified: ofbiz/trunk/framework/widget/src/org/ofbiz/widget/form/ModelForm.java URL: http://svn.apache.org/viewvc/ofbiz/trunk/framework/widget/src/org/ofbiz/widget/form/ModelForm.java?rev=1488315&r1=1488314&r2=1488315&view=diff ============================================================================== --- ofbiz/trunk/framework/widget/src/org/ofbiz/widget/form/ModelForm.java (original) +++ ofbiz/trunk/framework/widget/src/org/ofbiz/widget/form/ModelForm.java Fri May 31 17:07:00 2013 @@ -198,11 +198,14 @@ public class ModelForm extends ModelWidg protected FlexibleStringExpander rowCountExdr; protected List<ModelFormField> multiSubmitFields = FastList.newInstance(); protected int rowCount = 0; + private String sortFieldParameterName = "sortField"; /** On Submit areas to be updated. */ protected List<UpdateArea> onSubmitUpdateAreas; /** On Paginate areas to be updated. */ protected List<UpdateArea> onPaginateUpdateAreas; + /** On Sort Column areas to be updated. */ + protected List<UpdateArea> onSortColumnUpdateAreas; // ===== CONSTRUCTORS ===== /** Default Constructor */ @@ -305,6 +308,7 @@ public class ModelForm extends ModelWidg this.defaultViewSize = parent.defaultViewSize; this.onSubmitUpdateAreas = parent.onSubmitUpdateAreas; this.onPaginateUpdateAreas = parent.onPaginateUpdateAreas; + this.onSortColumnUpdateAreas = parent.onSortColumnUpdateAreas; this.altRowStyles = parent.altRowStyles; this.useWhenFields = parent.useWhenFields; @@ -324,6 +328,7 @@ public class ModelForm extends ModelWidg this.fieldGroupMap = parent.fieldGroupMap; this.fieldGroupList = parent.fieldGroupList; this.lastOrderFields = parent.lastOrderFields; + this.sortFieldParameterName = parent.sortFieldParameterName; } } @@ -462,7 +467,10 @@ public class ModelForm extends ModelWidg if (this.paginate == null || formElement.hasAttribute("paginate")) { this.paginate = FlexibleStringExpander.getInstance(formElement.getAttribute("paginate")); } - + String sortFieldParameterName = formElement.getAttribute("sort-field-parameter-name"); + if (!sortFieldParameterName.isEmpty()) { + this.sortFieldParameterName = sortFieldParameterName; + } this.skipStart = "true".equals(formElement.getAttribute("skip-start")); this.skipEnd = "true".equals(formElement.getAttribute("skip-end")); this.hideHeader = "true".equals(formElement.getAttribute("hide-header")); @@ -686,6 +694,8 @@ public class ModelForm extends ModelWidg addOnPaginateUpdateArea(updateArea); } else if ("submit".equals(updateArea.getEventType())) { addOnSubmitUpdateArea(updateArea); + } else if ("sort-column".equals(updateArea.getEventType())) { + addOnSortColumnUpdateArea(updateArea); } } @@ -718,6 +728,23 @@ public class ModelForm extends ModelWidg } } + protected void addOnSortColumnUpdateArea(UpdateArea updateArea) { + if (onSortColumnUpdateAreas == null) { + onSortColumnUpdateAreas = FastList.newInstance(); + } + int index = onSortColumnUpdateAreas.indexOf(updateArea); + if (index != -1) { + if (UtilValidate.isNotEmpty(updateArea.areaTarget)) { + onSortColumnUpdateAreas.set(index, updateArea); + } else { + // blank target indicates a removing override + onSortColumnUpdateAreas.remove(index); + } + } else { + onSortColumnUpdateAreas.add(updateArea); + } + } + public void addAutoFieldsFromService(AutoFieldsService autoFieldsService) { autoFieldsServices.add(autoFieldsService); @@ -2335,6 +2362,10 @@ public class ModelForm extends ModelWidg this.type = string; } + public List<UpdateArea> getOnSortColumnUpdateAreas() { + return this.onSortColumnUpdateAreas; + } + public List<UpdateArea> getOnPaginateUpdateAreas() { return this.onPaginateUpdateAreas; } @@ -2758,24 +2789,25 @@ public class ModelForm extends ModelWidg } public String getSortField(Map<String, Object> context) { - String field = "sortField"; String value = null; - try { - value = (String)context.get(field); + value = (String)context.get(this.sortFieldParameterName); if (value == null) { Map<String, String> parameters = UtilGenerics.cast(context.get("parameters")); if (parameters != null) { - value = parameters.get(field); + value = parameters.get(this.sortFieldParameterName); } } } catch (Exception e) { Debug.logWarning(e, "Error getting sortField: " + e.toString(), module); } - return value; } + public String getSortFieldParameterName() { + return this.sortFieldParameterName; + } + /* Returns the list of ModelForm.UpdateArea objects. */ public List<UpdateArea> getOnSubmitUpdateAreas() {