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