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