001 package org.apache.myfaces.tobago.renderkit.html; 002 003 /* 004 * Licensed to the Apache Software Foundation (ASF) under one or more 005 * contributor license agreements. See the NOTICE file distributed with 006 * this work for additional information regarding copyright ownership. 007 * The ASF licenses this file to You under the Apache License, Version 2.0 008 * (the "License"); you may not use this file except in compliance with 009 * the License. You may obtain a copy of the License at 010 * 011 * http://www.apache.org/licenses/LICENSE-2.0 012 * 013 * Unless required by applicable law or agreed to in writing, software 014 * distributed under the License is distributed on an "AS IS" BASIS, 015 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 016 * See the License for the specific language governing permissions and 017 * limitations under the License. 018 */ 019 020 import org.apache.commons.lang.StringUtils; 021 import org.apache.commons.logging.Log; 022 import org.apache.commons.logging.LogFactory; 023 import org.apache.myfaces.tobago.TobagoConstants; 024 import static org.apache.myfaces.tobago.TobagoConstants.ATTR_ACTION_LINK; 025 import static org.apache.myfaces.tobago.TobagoConstants.ATTR_ACTION_ONCLICK; 026 import static org.apache.myfaces.tobago.TobagoConstants.ATTR_DEFAULT_COMMAND; 027 import static org.apache.myfaces.tobago.TobagoConstants.ATTR_DISABLED; 028 import static org.apache.myfaces.tobago.TobagoConstants.ATTR_POPUP_CLOSE; 029 import static org.apache.myfaces.tobago.TobagoConstants.ATTR_RESOURCE; 030 import static org.apache.myfaces.tobago.TobagoConstants.ATTR_TARGET; 031 import static org.apache.myfaces.tobago.TobagoConstants.ATTR_TRANSITION; 032 import static org.apache.myfaces.tobago.TobagoConstants.FACET_CONFIRMATION; 033 import static org.apache.myfaces.tobago.TobagoConstants.FACET_POPUP; 034 import org.apache.myfaces.tobago.component.ComponentUtil; 035 import org.apache.myfaces.tobago.component.UIPopup; 036 import org.apache.myfaces.tobago.context.ClientProperties; 037 import org.apache.myfaces.tobago.context.ResourceManagerUtil; 038 import org.apache.myfaces.tobago.event.PopupActionListener; 039 040 import javax.faces.application.Application; 041 import javax.faces.application.ViewHandler; 042 import javax.faces.component.UICommand; 043 import javax.faces.component.UIComponent; 044 import javax.faces.component.UIParameter; 045 import javax.faces.component.ValueHolder; 046 import javax.faces.context.ExternalContext; 047 import javax.faces.context.FacesContext; 048 import java.net.URLDecoder; 049 import java.util.Arrays; 050 import java.util.List; 051 052 public class CommandRendererHelper { 053 054 private static final Log LOG = LogFactory.getLog(CommandRendererHelper.class); 055 056 private String onclick; 057 private boolean disabled; 058 private String href; 059 private String target; 060 061 public CommandRendererHelper(FacesContext facesContext, UICommand component) { 062 initOnclick(facesContext, component, null); 063 } 064 065 public CommandRendererHelper(FacesContext facesContext, UICommand component, Tag tag) { 066 initOnclick(facesContext, component, tag); 067 } 068 069 private void initOnclick(FacesContext facesContext, UICommand command, Tag tag) { 070 071 disabled = ComponentUtil.getBooleanAttribute(command, ATTR_DISABLED); 072 href = getEmptyHref(facesContext); 073 074 if (disabled) { 075 onclick = ""; 076 href = ""; 077 } else { 078 079 UIPopup popup = (UIPopup) command.getFacet(FACET_POPUP); 080 if (popup != null) { 081 if (!ComponentUtil.containsPopupActionListener(command)) { 082 command.addActionListener(new PopupActionListener(popup)); 083 } 084 } 085 086 String clientId = command.getClientId(facesContext); 087 boolean defaultCommand = ComponentUtil.getBooleanAttribute(command, ATTR_DEFAULT_COMMAND); 088 boolean transition = ComponentUtil.getBooleanAttribute(command, ATTR_TRANSITION); 089 090 if (command.getAttributes().get(ATTR_ACTION_LINK) != null 091 || command.getAttributes().get(ATTR_RESOURCE) != null) { 092 String url = generateUrl(facesContext, command); 093 if (tag == Tag.ANCHOR) { 094 onclick = null; 095 href = url; 096 target = ComponentUtil.getStringAttribute(command, ATTR_TARGET); 097 } else { 098 onclick = "Tobago.navigateToUrl('" + url + "');"; 099 } 100 } else if (command.getAttributes().get(ATTR_ACTION_ONCLICK) != null) { 101 onclick = prepareOnClick(facesContext, command); 102 } else if (command instanceof org.apache.myfaces.tobago.component.UICommand 103 && ((org.apache.myfaces.tobago.component.UICommand) command).getRenderedPartially().length > 0) { 104 105 String[] componentId = ((org.apache.myfaces.tobago.component.UICommand) command).getRenderedPartially(); 106 107 if (componentId != null && componentId.length == 1) { 108 // TODO find a better way 109 boolean popupAction = ComponentUtil.containsPopupActionListener(command); 110 if (popupAction) { 111 onclick = "Tobago.openPopupWithAction2(this, '" 112 + HtmlRendererUtil.getComponentId(facesContext, command, componentId[0]) 113 + "', '" + clientId + "', null)"; 114 } else { 115 onclick = "Tobago.reloadComponent2(this, '" 116 + HtmlRendererUtil.getComponentId(facesContext, command, componentId[0]) 117 + "','" + clientId + "', {});"; 118 } 119 } else { 120 LOG.error("more than one partially rendered component is currently not supported " 121 + Arrays.toString(componentId)); 122 onclick = "Tobago.submitAction2(this, '" + clientId + "', " + transition + ", null);"; 123 } 124 125 } else if (defaultCommand) { 126 ComponentUtil.findPage(facesContext, command).setDefaultActionId(clientId); 127 onclick = null; 128 } else { 129 String target = ComponentUtil.getStringAttribute(command, ATTR_TARGET); 130 if (target == null) { 131 onclick = "Tobago.submitAction2(this, '" + clientId + "', " + transition + ", null);"; 132 } else { 133 onclick = "Tobago.submitAction2(this, '" + clientId + "', " + transition + ", '" + target + "');"; 134 } 135 } 136 137 if (command.getAttributes().get(ATTR_POPUP_CLOSE) != null 138 && ComponentUtil.isInPopup(command)) { 139 String value = (String) command.getAttributes().get(ATTR_POPUP_CLOSE); 140 if (value.equals("immediate")) { 141 onclick = "Tobago.closePopup(this);"; 142 } else if (value.equals("afterSubmit") 143 && command instanceof org.apache.myfaces.tobago.component.UICommand 144 && ((org.apache.myfaces.tobago.component.UICommand) command).getRenderedPartially().length > 0) { 145 onclick += "Tobago.closePopup(this);"; 146 } 147 148 } 149 150 onclick = appendConfirmationScript(onclick, command); 151 } 152 } 153 154 private String getEmptyHref(FacesContext facesContext) { 155 ClientProperties clientProperties = ClientProperties.getInstance(facesContext); 156 return clientProperties.getUserAgent().isMsie() ? "#" : "javascript:;"; 157 } 158 159 private String prepareOnClick(FacesContext facesContext, UIComponent component) { 160 String onclick; 161 onclick = (String) component.getAttributes().get(ATTR_ACTION_ONCLICK); 162 if (onclick.contains("@autoId")) { 163 onclick = StringUtils.replace(onclick, "@autoId", component.getClientId(facesContext)); 164 } 165 return onclick; 166 } 167 168 private String appendConfirmationScript(String onclick, UIComponent component) { 169 ValueHolder confirmation = (ValueHolder) component.getFacet(FACET_CONFIRMATION); 170 if (confirmation != null) { 171 StringBuilder script = new StringBuilder(); 172 script.append("return confirm('"); 173 script.append(confirmation.getValue()); 174 script.append("')"); 175 if (onclick != null) { 176 script.append(" && "); 177 script.append(onclick); 178 } 179 onclick = script.toString(); 180 } 181 return onclick; 182 } 183 184 private String generateUrl(FacesContext facesContext, UIComponent component) { 185 String url; 186 Application application = facesContext.getApplication(); 187 ViewHandler viewHandler = application.getViewHandler(); 188 ExternalContext externalContext = facesContext.getExternalContext(); 189 190 if (component.getAttributes().get(ATTR_RESOURCE) != null) { 191 String resource = (String) component.getAttributes().get(ATTR_RESOURCE); 192 boolean jsfResource = ComponentUtil.getBooleanAttribute(component, TobagoConstants.ATTR_JSF_RESOURCE); 193 url = ResourceManagerUtil.getPageWithoutContextPath(facesContext, resource); 194 if (url != null) { 195 if (jsfResource) { 196 url = viewHandler.getActionURL(facesContext, url); 197 url = externalContext.encodeActionURL(url); 198 } else { 199 url = viewHandler.getResourceURL(facesContext, url); 200 url = externalContext.encodeResourceURL(url); 201 } 202 } else { 203 url = ""; 204 } 205 } else if (component.getAttributes().get(ATTR_ACTION_LINK) != null) { 206 207 String link = (String) component.getAttributes().get(ATTR_ACTION_LINK); 208 if (link.startsWith("/")) { // internal absolute link 209 url = viewHandler.getActionURL(facesContext, link); 210 url = externalContext.encodeActionURL(url); 211 } else if (link.contains(":")) { // external link 212 url = link; 213 } else { // internal relative link 214 url = externalContext.encodeResourceURL(link); 215 } 216 217 StringBuilder builder = new StringBuilder(url); 218 boolean firstParameter = !url.contains("?"); 219 for (UIComponent child : (List<UIComponent>) component.getChildren()) { 220 if (child instanceof UIParameter) { 221 UIParameter parameter = (UIParameter) child; 222 if (firstParameter) { 223 builder.append("?"); 224 firstParameter = false; 225 } else { 226 builder.append("&"); 227 } 228 builder.append(parameter.getName()); 229 builder.append("="); 230 Object value = parameter.getValue(); 231 // TODO encoding 232 builder.append(value != null ? URLDecoder.decode(value.toString()) : null); 233 } 234 } 235 url = builder.toString(); 236 } else { 237 throw new AssertionError("Needed " + ATTR_ACTION_LINK + " or " + ATTR_RESOURCE); 238 } 239 240 return url; 241 } 242 243 244 public String getOnclick() { 245 return onclick; 246 } 247 248 public String getOnclickDoubleQuoted() { 249 return onclick.replace('\'', '\"'); 250 } 251 252 public boolean isDisabled() { 253 return disabled; 254 } 255 256 public String getHref() { 257 return href; 258 } 259 260 public String getTarget() { 261 return target; 262 } 263 264 public static enum Tag { 265 ANCHOR, BUTTON 266 } 267 }