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