001/* 002 * (C) Copyright 2006-2007 Nuxeo SAS (http://nuxeo.com/) and contributors. 003 * 004 * All rights reserved. This program and the accompanying materials 005 * are made available under the terms of the GNU Lesser General Public License 006 * (LGPL) version 2.1 which accompanies this distribution, and is available at 007 * http://www.gnu.org/licenses/lgpl.html 008 * 009 * This library is distributed in the hope that it will be useful, 010 * but WITHOUT ANY WARRANTY; without even the implied warranty of 011 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 012 * Lesser General Public License for more details. 013 * 014 * Contributors: 015 * George Lefter 016 * 017 * $Id$ 018 */ 019 020package org.nuxeo.ecm.platform.ui.web.directory; 021 022import java.io.IOException; 023import java.util.ArrayList; 024import java.util.Arrays; 025import java.util.List; 026import java.util.Map; 027 028import javax.faces.component.UICommand; 029import javax.faces.component.UIGraphic; 030import javax.faces.component.html.HtmlInputHidden; 031import javax.faces.context.FacesContext; 032import javax.faces.context.ResponseWriter; 033 034import org.apache.commons.lang.StringUtils; 035import org.nuxeo.ecm.platform.ui.web.util.ComponentUtils; 036 037/** 038 * @author <a href="mailto:glefter@nuxeo.com">George Lefter</a> 039 */ 040public class ChainSelectMany extends ChainSelectBase { 041 042 public static final String ADD_BUTTON = "addButton"; 043 044 private static final String REMOVE_BUTTON = "removeButton"; 045 046 /* a hidden input set if the value of the component is empty */ 047 private static final String EMPTY_VALUE_MARKER = "emptyValueMarker"; 048 049 /* a hidden input to hold the remove entry id */ 050 private static final String REMOVE_HIDDEN = "removeHidden"; 051 052 /* a hidden input set if the user click the add button */ 053 private static final String ADD_HIDDEN = "addHidden"; 054 055 public ChainSelectMany() { 056 FacesContext context = FacesContext.getCurrentInstance(); 057 058 HtmlInputHidden emptyValueMarker = new HtmlInputHidden(); 059 emptyValueMarker.setId("emptyValueMarker"); 060 emptyValueMarker.setValue("true"); 061 getFacets().put(EMPTY_VALUE_MARKER, emptyValueMarker); 062 063 UICommand addButton = (UICommand) context.getApplication().createComponent("org.ajax4jsf.ajax.CommandButton"); 064 addButton.getAttributes().put("id", "addButton"); 065 addButton.getAttributes().put("value", "add"); 066 getFacets().put(ADD_BUTTON, addButton); 067 068 HtmlInputHidden addHidden = new HtmlInputHidden(); 069 addHidden.setId("addHidden"); 070 getFacets().put(ADD_HIDDEN, addHidden); 071 072 UICommand removeButton = (UICommand) context.getApplication().createComponent("org.ajax4jsf.ajax.CommandLink"); 073 UIGraphic image = new UIGraphic(); 074 image.setValue("/icons/delete.png"); 075 removeButton.getAttributes().put("id", "removeButton"); 076 removeButton.getChildren().add(image); 077 getFacets().put(REMOVE_BUTTON, removeButton); 078 079 HtmlInputHidden removeHidden = new HtmlInputHidden(); 080 removeHidden.setId("removeHidden"); 081 getFacets().put(REMOVE_HIDDEN, removeHidden); 082 } 083 084 @Override 085 public void decode(FacesContext context) { 086 if (getDisplayValueOnly()) { 087 return; 088 } 089 decodeSelection(context); 090 decodeValue(context); 091 setValid(true); 092 } 093 094 private void decodeValue(FacesContext context) { 095 String emptyValueMarkerClientId = getFacet(EMPTY_VALUE_MARKER).getClientId(context); 096 String removeEntryClientId = getFacet(REMOVE_HIDDEN).getClientId(context); 097 String addEntryClientId = getFacet(ADD_HIDDEN).getClientId(context); 098 Map<String, String> map = context.getExternalContext().getRequestParameterMap(); 099 100 String removeEntryId = map.get(removeEntryClientId); 101 boolean addButtonClicked = "true".equals(map.get(addEntryClientId)); 102 String allValues = map.get(emptyValueMarkerClientId); 103 104 String[] oldValue = StringUtils.split(allValues, ","); 105 106 List<String> valueList = new ArrayList<String>(Arrays.asList(oldValue)); 107 108 if (addButtonClicked) { 109 String[] selection = getSelection(); 110 if (validateEntry(context, selection)) { 111 valueList.add(StringUtils.join(selection, getKeySeparator())); 112 } 113 } 114 115 if (!StringUtils.isEmpty(removeEntryId)) { 116 valueList.remove(removeEntryId); 117 } 118 119 String[] newValue = valueList.toArray(new String[valueList.size()]); 120 setSubmittedValue(newValue); 121 } 122 123 @Override 124 public String[] getSelection() { 125 String clientId = getClientId(FacesContext.getCurrentInstance()); 126 String[] selection = selectionMap.get(clientId); 127 if (selection == null) { 128 selection = new String[0]; 129 selectionMap.put(clientId, selection); 130 } 131 return selection; 132 } 133 134 @Override 135 public void encodeBegin(FacesContext context) throws IOException { 136 if (getDisplayValueOnly()) { 137 encodeReadOnly(context); 138 } else { 139 encodeReadWrite(context); 140 } 141 } 142 143 public void encodeReadWrite(FacesContext context) throws IOException { 144 ResponseWriter writer = context.getResponseWriter(); 145 146 getChildren().clear(); 147 writer.startElement("div", this); 148 writer.writeAttribute("id", getClientId(context), "id"); 149 String style = getStyle(); 150 if (style != null) { 151 writer.writeAttribute("style", style, "style"); 152 } 153 String styleClass = getStyleClass(); 154 if (styleClass != null) { 155 writer.writeAttribute("class", styleClass, "class"); 156 } 157 158 String[] selectedKeys = getSelection(); 159 for (int level = 0; level < getDepth(); level++) { 160 encodeListbox(context, level, selectedKeys); 161 } 162 163 encodeAddButton(context); 164 encodeValue(context); 165 writer.endElement("div"); 166 } 167 168 public void encodeReadOnly(FacesContext context) throws IOException { 169 ResponseWriter writer = context.getResponseWriter(); 170 String[] values = (String[]) getSubmittedValue(); 171 if (values == null) { 172 values = (String[]) getValue(); 173 } 174 if (values != null) { 175 writer.startElement("div", this); 176 for (String value : values) { 177 String[] keys = StringUtils.split(value, getKeySeparator()); 178 List<DirectoryEntry> nodes = resolveKeys(keys); 179 List<String> labels = new ArrayList<String>(); 180 for (DirectoryEntry node : nodes) { 181 String itemValue = node.getId(); 182 String itemLabel = node.getLabel(); 183 itemLabel = computeItemLabel(context, itemValue, itemLabel); 184 labels.add(itemLabel); 185 } 186 String concatenatedLabel = StringUtils.join(labels.iterator(), getKeySeparator()); 187 188 writer.startElement("div", this); 189 writer.write(concatenatedLabel); 190 writer.endElement("div"); 191 } 192 writer.endElement("div"); 193 } 194 } 195 196 private void encodeAddButton(FacesContext context) throws IOException { 197 UICommand addButton = (UICommand) getFacet(ADD_BUTTON); 198 HtmlInputHidden addHidden = (HtmlInputHidden) getFacet(ADD_HIDDEN); 199 String addJs = String.format("document.getElementById('%s').value = '%s'", addHidden.getClientId(context), 200 "true"); 201 addButton.getAttributes().put("onclick", addJs); 202 addHidden.setValue(""); 203 204 String reRender = getReRender(); 205 if (reRender == null) { 206 reRender = getId(); 207 } 208 addButton.getAttributes().put("reRender", reRender); 209 ComponentUtils.encodeComponent(context, addButton); 210 ComponentUtils.encodeComponent(context, addHidden); 211 } 212 213 @Override 214 public boolean getRendersChildren() { 215 return true; 216 } 217 218 @Override 219 public void encodeChildren(FacesContext context) throws IOException { 220 } 221 222 private void encodeValue(FacesContext context) throws IOException { 223 ResponseWriter writer = context.getResponseWriter(); 224 225 String[] values = (String[]) getSubmittedValue(); 226 if (values == null) { 227 values = (String[]) getValue(); 228 } 229 230 String compValue = StringUtils.join(values, ","); 231 compValue = compValue == null ? "" : compValue; 232 HtmlInputHidden emptyValueMarker = (HtmlInputHidden) getFacet(EMPTY_VALUE_MARKER); 233 emptyValueMarker.setValue(compValue); 234 ComponentUtils.encodeComponent(context, emptyValueMarker); 235 236 if (values != null) { 237 HtmlInputHidden removeHidden = (HtmlInputHidden) getFacet(REMOVE_HIDDEN); 238 removeHidden.setValue(""); 239 ComponentUtils.encodeComponent(context, removeHidden); 240 UICommand removeButton = (UICommand) getFacet(REMOVE_BUTTON); 241 String reRender = getReRender(); 242 if (reRender == null) { 243 reRender = getId(); 244 } 245 removeButton.getAttributes().put("reRender", reRender); 246 writer.startElement("div", this); 247 for (String value : values) { 248 String[] keys = StringUtils.split(value, getKeySeparator()); 249 List<DirectoryEntry> nodes = resolveKeys(keys); 250 List<String> labels = new ArrayList<String>(); 251 for (DirectoryEntry node : nodes) { 252 String itemValue = node.getId(); 253 String itemLabel = node.getLabel(); 254 itemLabel = computeItemLabel(context, itemValue, itemLabel); 255 labels.add(itemLabel); 256 } 257 String concatenatedLabel = StringUtils.join(labels.iterator(), getKeySeparator()); 258 259 writer.startElement("div", this); 260 String removeJs = String.format("document.getElementById('%s').value = '%s'", 261 removeHidden.getClientId(context), value); 262 removeButton.getAttributes().put("onclick", removeJs); 263 ComponentUtils.encodeComponent(context, removeButton); 264 writer.write(concatenatedLabel); 265 writer.endElement("div"); 266 } 267 writer.endElement("div"); 268 } 269 } 270 271 /* 272 * the number of visible components 273 */ 274 public int getCurrentDepth() { 275 return getDepth(); 276 } 277 278 @Override 279 public String getFamily() { 280 return "nxdirectory.ChainSelectMany"; 281 } 282 283 @Override 284 public String getRendererType() { 285 return null; 286 } 287 288 @Override 289 public Object saveState(FacesContext context) { 290 Object[] values = new Object[1]; 291 values[0] = super.saveState(context); 292 return values; 293 } 294 295 @Override 296 public void restoreState(FacesContext context, Object state) { 297 Object[] values = (Object[]) state; 298 super.restoreState(context, values[0]); 299 } 300 301}