001 package org.apache.myfaces.tobago.component; 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 static org.apache.myfaces.tobago.TobagoConstants.ATTR_APPLICATION_ICON; 021 import org.apache.commons.collections.KeyValue; 022 import org.apache.commons.collections.list.SetUniqueList; 023 import org.apache.commons.collections.set.ListOrderedSet; 024 import org.apache.commons.logging.Log; 025 import org.apache.commons.logging.LogFactory; 026 027 import static org.apache.myfaces.tobago.TobagoConstants.ATTR_FOCUS_ID; 028 import static org.apache.myfaces.tobago.TobagoConstants.ATTR_HEIGHT; 029 import static org.apache.myfaces.tobago.TobagoConstants.ATTR_STATE; 030 import static org.apache.myfaces.tobago.TobagoConstants.ATTR_WIDTH; 031 import static org.apache.myfaces.tobago.TobagoConstants.SUBCOMPONENT_SEP; 032 import org.apache.myfaces.tobago.model.PageState; 033 import org.apache.myfaces.tobago.model.PageStateImpl; 034 import org.apache.myfaces.tobago.webapp.TobagoMultipartFormdataRequest; 035 036 import javax.faces.application.FacesMessage; 037 import javax.faces.component.UIComponent; 038 import javax.faces.context.FacesContext; 039 import javax.faces.el.ValueBinding; 040 import javax.servlet.ServletRequest; 041 import javax.servlet.http.HttpServletRequestWrapper; 042 import java.io.IOException; 043 import java.util.ArrayList; 044 import java.util.Iterator; 045 import java.util.List; 046 import java.util.Set; 047 import java.util.StringTokenizer; 048 049 public class UIPage extends UIForm { 050 051 private static final Log LOG = LogFactory.getLog(UIPage.class); 052 053 public static final String COMPONENT_TYPE = "org.apache.myfaces.tobago.Page"; 054 055 public static final String DEFAULT_STYLE = "style/style.css"; 056 057 private static final int DEFAULT_WIDTH = 1024; 058 059 private static final int DEFAULT_HEIGHT = 768; 060 061 private String formId; 062 063 private String focusId; 064 065 private String actionId; 066 067 private String defaultActionId; 068 069 private List<KeyValue> postfields; 070 071 private SetUniqueList scriptFiles; 072 073 private Set<String> scriptBlocks; 074 075 private Set<String> styleFiles; 076 077 private Set<String> styleBlocks; 078 079 private Set<String> onloadScripts; 080 081 private Set<String> onunloadScripts; 082 083 private Set<String> onexitScripts; 084 085 private Set<String> onsubmitScripts; 086 087 private List<UIPopup> popups; 088 089 private Integer width; 090 091 private Integer height; 092 093 private String applicationIcon; 094 095 @SuppressWarnings({"unchecked"}) 096 public UIPage() { 097 scriptFiles = SetUniqueList.decorate(new ArrayList()); 098 scriptBlocks = new ListOrderedSet(); 099 styleFiles = new ListOrderedSet(); 100 styleFiles.add(DEFAULT_STYLE); 101 styleBlocks = new ListOrderedSet(); 102 onloadScripts = new ListOrderedSet(); 103 onunloadScripts = new ListOrderedSet(); 104 onexitScripts = new ListOrderedSet(); 105 onsubmitScripts = new ListOrderedSet(); 106 popups = new ArrayList<UIPopup>(); 107 } 108 109 @Override 110 public void encodeBegin(FacesContext facesContext) throws IOException { 111 // TODO change this should be renamed to DimensionUtils.prepare!!! 112 UILayout.getLayout(this).layoutBegin(facesContext, this); 113 super.encodeBegin(facesContext); 114 } 115 116 117 @Override 118 public void encodeChildren(FacesContext context) throws IOException { 119 } 120 121 public String getFormId(FacesContext facesContext) { 122 if (formId == null) { 123 formId = getClientId(facesContext) 124 + SUBCOMPONENT_SEP + "form"; 125 } 126 return formId; 127 } 128 129 @Override 130 public void processDecodes(FacesContext facesContext) { 131 132 checkTobagoRequest(facesContext); 133 134 decode(facesContext); 135 136 clearScriptsAndPopups(); 137 138 markSubmittedForm(facesContext); 139 140 // invoke processDecodes() on children 141 for (Iterator kids = getFacetsAndChildren(); kids.hasNext();) { 142 UIComponent kid = (UIComponent) kids.next(); 143 kid.processDecodes(facesContext); 144 } 145 } 146 147 public void markSubmittedForm(FacesContext facesContext) { 148 // find the form of the action command and set submitted to it and all 149 // children 150 151 // reset old submitted state 152 setSubmitted(false); 153 154 String currentActionId = getActionId(); 155 if (LOG.isDebugEnabled()) { 156 LOG.debug("actionId = '" + currentActionId + "'"); 157 } 158 159 UIComponent command = null; 160 try { 161 command = findComponent(currentActionId); 162 } catch (Exception e) {/* ignore */} 163 164 // TODO: remove this if block if prooved this never happens anymore 165 if (command == null 166 && currentActionId != null && currentActionId.matches(".*:\\d+:.*")) { 167 // If currentActionId component was inside a sheet the id contains the 168 // rowindex and is therefore not found here. 169 // We do not need the row here because we want just to find the 170 // related form, so removing the rowindex will help here. 171 currentActionId = currentActionId.replaceAll(":\\d+:", ":"); 172 try { 173 command = findComponent(currentActionId); 174 LOG.info("command = \"" + command + "\"", new Exception()); 175 } catch (Exception e) {/* ignore */} 176 } 177 178 if (LOG.isTraceEnabled()) { 179 LOG.trace(currentActionId); 180 LOG.trace(command); 181 LOG.trace(ComponentUtil.toString(facesContext.getViewRoot(), 0)); 182 } 183 184 if (command != null) { 185 UIForm form = ComponentUtil.findForm(command); 186 form.setSubmitted(true); 187 188 if (LOG.isTraceEnabled()) { 189 LOG.trace(form); 190 LOG.trace(form.getClientId(facesContext)); 191 } 192 } else { 193 if (LOG.isDebugEnabled()) { 194 LOG.debug("Illegal actionId! Rerender the view."); 195 } 196 facesContext.renderResponse(); 197 } 198 } 199 200 private void clearScriptsAndPopups() { 201 // clear script Set's 202 getOnloadScripts().clear(); 203 getOnunloadScripts().clear(); 204 getOnexitScripts().clear(); 205 getScriptBlocks().clear(); 206 getPopups().clear(); 207 } 208 209 private void checkTobagoRequest(FacesContext facesContext) { 210 // multipart/form-data must use TobagoMultipartFormdataRequest 211 String contentType = (String) facesContext.getExternalContext() 212 .getRequestHeaderMap().get("content-type"); 213 if (contentType != null && contentType.startsWith("multipart/form-data")) { 214 Object request = facesContext.getExternalContext().getRequest(); 215 boolean okay = false; 216 if (request instanceof TobagoMultipartFormdataRequest) { 217 okay = true; 218 } else if (request instanceof HttpServletRequestWrapper) { 219 ServletRequest wrappedRequest 220 = ((HttpServletRequestWrapper) request).getRequest(); 221 if (wrappedRequest instanceof TobagoMultipartFormdataRequest) { 222 okay = true; 223 } 224 } 225 // TODO PortletRequest ?? 226 if (!okay) { 227 LOG.error("Can't process multipart/form-data without TobagoRequest. " 228 + "Please check the web.xml and define a TobagoMultipartFormdataFilter. " 229 + "See documentation for <tc:file>"); 230 facesContext.addMessage(null, new FacesMessage("An error has occured!")); 231 } 232 } 233 } 234 235 public List<KeyValue> getPostfields() { 236 if (postfields == null) { 237 postfields = new ArrayList<KeyValue>(); 238 } 239 return postfields; 240 } 241 242 @Override 243 public void processUpdates(FacesContext context) { 244 super.processUpdates(context); 245 updatePageState(context); 246 } 247 248 public void updatePageState(FacesContext facesContext) { 249 PageState state = getPageState(facesContext); 250 decodePageState(facesContext, state); 251 } 252 253 @SuppressWarnings({"unchecked"}) 254 private void decodePageState(FacesContext facesContext, PageState pageState) { 255 String name; 256 String value = null; 257 try { 258 name = getClientId(facesContext) 259 + SUBCOMPONENT_SEP + "form-clientDimension"; 260 value = (String) facesContext.getExternalContext() 261 .getRequestParameterMap().get(name); 262 if (value != null) { 263 StringTokenizer tokenizer = new StringTokenizer(value, ";"); 264 int width = Integer.parseInt(tokenizer.nextToken()); 265 int height = Integer.parseInt(tokenizer.nextToken()); 266 if (pageState != null) { 267 pageState.setClientWidth(width); 268 pageState.setClientHeight(height); 269 } 270 facesContext.getExternalContext().getRequestMap().put("tobago-page-clientDimension-with", width); 271 facesContext.getExternalContext().getRequestMap().put("tobago-page-clientDimension-height", height); 272 } 273 } catch (Exception e) { 274 LOG.error("Error in decoding state: value='" + value + "'", e); 275 } 276 } 277 278 public PageState getPageState(FacesContext facesContext) { 279 ValueBinding stateBinding = getValueBinding(ATTR_STATE); 280 if (stateBinding != null) { 281 PageState state = (PageState) stateBinding.getValue(facesContext); 282 if (state == null) { 283 state = new PageStateImpl(); 284 stateBinding.setValue(facesContext, state); 285 } 286 return state; 287 } else { 288 return null; 289 } 290 } 291 292 // ///////////////////////////////////////////// bean getter + setter 293 294 public String getFocusId() { 295 if (focusId != null) { 296 return focusId; 297 } 298 ValueBinding vb = getValueBinding(ATTR_FOCUS_ID); 299 if (vb != null) { 300 return (String) vb.getValue(getFacesContext()); 301 } else { 302 return null; 303 } 304 } 305 306 public void setFocusId(String focusId) { 307 this.focusId = focusId; 308 } 309 310 public String getActionId() { 311 return actionId; 312 } 313 314 public void setActionId(String actionId) { 315 this.actionId = actionId; 316 } 317 318 public String getDefaultActionId() { 319 return defaultActionId; 320 } 321 322 public void setDefaultActionId(String defaultActionId) { 323 this.defaultActionId = defaultActionId; 324 } 325 326 @SuppressWarnings({"unchecked"}) 327 public List<String> getScriptFiles() { 328 return scriptFiles; 329 } 330 331 public Set<String> getScriptBlocks() { 332 return scriptBlocks; 333 } 334 335 public Set<String> getStyleFiles() { 336 return styleFiles; 337 } 338 339 public Set<String> getStyleBlocks() { 340 return styleBlocks; 341 } 342 343 public Set<String> getOnloadScripts() { 344 return onloadScripts; 345 } 346 347 public Set<String> getOnunloadScripts() { 348 return onunloadScripts; 349 } 350 351 public Set<String> getOnexitScripts() { 352 return onexitScripts; 353 } 354 355 public Set<String> getOnsubmitScripts() { 356 return onsubmitScripts; 357 } 358 359 public List<UIPopup> getPopups() { 360 return popups; 361 } 362 363 public Integer getWidth() { 364 if (width != null) { 365 return width; 366 } 367 ValueBinding vb = getValueBinding(ATTR_WIDTH); 368 if (vb != null) { 369 return (Integer) vb.getValue(getFacesContext()); 370 } else { 371 Integer requestWidth = 372 (Integer) FacesContext.getCurrentInstance().getExternalContext(). 373 getRequestMap().get("tobago-page-clientDimension-with"); 374 if (requestWidth != null) { 375 return requestWidth; 376 } else { 377 return DEFAULT_WIDTH; 378 } 379 } 380 } 381 382 public void setWidth(Integer width) { 383 this.width = width; 384 } 385 386 public Integer getHeight() { 387 if (height != null) { 388 return height; 389 } 390 ValueBinding vb = getValueBinding(ATTR_HEIGHT); 391 if (vb != null) { 392 return (Integer) vb.getValue(getFacesContext()); 393 } else { 394 Integer requestHeight = 395 (Integer) FacesContext.getCurrentInstance().getExternalContext(). 396 getRequestMap().get("tobago-page-clientDimension-height"); 397 if (requestHeight != null) { 398 return requestHeight; 399 } else { 400 return DEFAULT_HEIGHT; 401 } 402 } 403 } 404 405 public void setHeight(Integer height) { 406 this.height = height; 407 } 408 409 public String getApplicationIcon() { 410 if (applicationIcon != null) { 411 return applicationIcon; 412 } 413 ValueBinding vb = getValueBinding(ATTR_APPLICATION_ICON); 414 if (vb != null) { 415 return (String) vb.getValue(getFacesContext()); 416 } else { 417 return null; 418 } 419 } 420 421 public void setApplicationIcon(String applicationIcon) { 422 this.applicationIcon = applicationIcon; 423 } 424 425 public void restoreState(FacesContext context, Object state) { 426 Object[] values = (Object[]) state; 427 super.restoreState(context, values[0]); 428 this.width = (Integer) values[1]; 429 this.height = (Integer) values[2]; 430 this.focusId = (String) values[3]; 431 this.applicationIcon = (String) values[4]; 432 } 433 434 public Object saveState(FacesContext context) { 435 Object[] values = new Object[5]; 436 values[0] = super.saveState(context); 437 values[1] = width; 438 values[2] = height; 439 values[3] = focusId; 440 values[4] = applicationIcon; 441 return values; 442 } 443 }