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.model.PageState; 032 import org.apache.myfaces.tobago.model.PageStateImpl; 033 import org.apache.myfaces.tobago.webapp.TobagoMultipartFormdataRequest; 034 035 import javax.faces.application.FacesMessage; 036 import javax.faces.component.UIComponent; 037 import javax.faces.context.FacesContext; 038 import javax.faces.el.ValueBinding; 039 import javax.servlet.ServletRequest; 040 import javax.servlet.http.HttpServletRequestWrapper; 041 import java.io.IOException; 042 import java.util.ArrayList; 043 import java.util.Iterator; 044 import java.util.List; 045 import java.util.Set; 046 import java.util.StringTokenizer; 047 048 public class UIPage extends UIForm { 049 050 private static final Log LOG = LogFactory.getLog(UIPage.class); 051 052 public static final String COMPONENT_TYPE = "org.apache.myfaces.tobago.Page"; 053 public static final String ENCTYPE_KEY = UIPanel.class.getName() + ".enctype"; 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 Set<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 ListOrderedSet(); 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) { 163 // ignore 164 } 165 166 // TODO: remove this if block if prooven this never happens anymore 167 if (command == null 168 && currentActionId != null && currentActionId.matches(".*:\\d+:.*")) { 169 // If currentActionId component was inside a sheet the id contains the 170 // rowindex and is therefore not found here. 171 // We do not need the row here because we want just to find the 172 // related form, so removing the rowindex will help here. 173 currentActionId = currentActionId.replaceAll(":\\d+:", ":"); 174 try { 175 command = findComponent(currentActionId); 176 LOG.info("command = \"" + command + "\"", new Exception()); 177 } catch (Exception e) { 178 // ignore 179 } 180 } 181 182 if (LOG.isTraceEnabled()) { 183 LOG.trace(currentActionId); 184 LOG.trace(command); 185 LOG.trace(ComponentUtil.toString(facesContext.getViewRoot(), 0)); 186 } 187 188 if (command != null) { 189 UIForm form = ComponentUtil.findForm(command); 190 form.setSubmitted(true); 191 192 if (LOG.isTraceEnabled()) { 193 LOG.trace(form); 194 LOG.trace(form.getClientId(facesContext)); 195 } 196 } else { 197 if (LOG.isDebugEnabled()) { 198 LOG.debug("Illegal actionId! Rerender the view."); 199 } 200 facesContext.renderResponse(); 201 } 202 } 203 204 private void clearScriptsAndPopups() { 205 // clear script Set's 206 getOnloadScripts().clear(); 207 getOnunloadScripts().clear(); 208 getOnexitScripts().clear(); 209 getScriptBlocks().clear(); 210 getPopups().clear(); 211 } 212 213 private void checkTobagoRequest(FacesContext facesContext) { 214 // multipart/form-data must use TobagoMultipartFormdataRequest 215 String contentType = (String) facesContext.getExternalContext() 216 .getRequestHeaderMap().get("content-type"); 217 if (contentType != null && contentType.startsWith("multipart/form-data")) { 218 Object request = facesContext.getExternalContext().getRequest(); 219 boolean okay = false; 220 if (request instanceof TobagoMultipartFormdataRequest) { 221 okay = true; 222 } else if (request instanceof HttpServletRequestWrapper) { 223 ServletRequest wrappedRequest 224 = ((HttpServletRequestWrapper) request).getRequest(); 225 if (wrappedRequest instanceof TobagoMultipartFormdataRequest) { 226 okay = true; 227 } 228 } 229 // TODO PortletRequest ?? 230 if (!okay) { 231 LOG.error("Can't process multipart/form-data without TobagoRequest. " 232 + "Please check the web.xml and define a TobagoMultipartFormdataFilter. " 233 + "See documentation for <tc:file>"); 234 facesContext.addMessage(null, new FacesMessage("An error has occured!")); 235 } 236 } 237 } 238 239 public List<KeyValue> getPostfields() { 240 if (postfields == null) { 241 postfields = new ArrayList<KeyValue>(); 242 } 243 return postfields; 244 } 245 246 @Override 247 public void processUpdates(FacesContext context) { 248 super.processUpdates(context); 249 updatePageState(context); 250 } 251 252 public void updatePageState(FacesContext facesContext) { 253 PageState state = getPageState(facesContext); 254 decodePageState(facesContext, state); 255 } 256 257 @SuppressWarnings("unchecked") 258 private void decodePageState(FacesContext facesContext, PageState pageState) { 259 String name; 260 String value = null; 261 try { 262 name = getClientId(facesContext) 263 + SUBCOMPONENT_SEP + "form-clientDimension"; 264 value = (String) facesContext.getExternalContext() 265 .getRequestParameterMap().get(name); 266 if (value != null) { 267 StringTokenizer tokenizer = new StringTokenizer(value, ";"); 268 int width = Integer.parseInt(tokenizer.nextToken()); 269 int height = Integer.parseInt(tokenizer.nextToken()); 270 if (pageState != null) { 271 pageState.setClientWidth(width); 272 pageState.setClientHeight(height); 273 } 274 facesContext.getExternalContext().getRequestMap().put("tobago-page-clientDimension-width", width); 275 facesContext.getExternalContext().getRequestMap().put("tobago-page-clientDimension-height", height); 276 } 277 } catch (Exception e) { 278 LOG.error("Error in decoding state: value='" + value + "'", e); 279 } 280 } 281 282 public PageState getPageState(FacesContext facesContext) { 283 ValueBinding stateBinding = getValueBinding(ATTR_STATE); 284 if (stateBinding != null) { 285 PageState state = (PageState) stateBinding.getValue(facesContext); 286 if (state == null) { 287 state = new PageStateImpl(); 288 stateBinding.setValue(facesContext, state); 289 } 290 return state; 291 } else { 292 return null; 293 } 294 } 295 296 // ///////////////////////////////////////////// bean getter + setter 297 298 public String getFocusId() { 299 if (focusId != null) { 300 return focusId; 301 } 302 ValueBinding vb = getValueBinding(ATTR_FOCUS_ID); 303 if (vb != null) { 304 return (String) vb.getValue(getFacesContext()); 305 } else { 306 return null; 307 } 308 } 309 310 public void setFocusId(String focusId) { 311 this.focusId = focusId; 312 } 313 314 public String getActionId() { 315 return actionId; 316 } 317 318 public void setActionId(String actionId) { 319 this.actionId = actionId; 320 } 321 322 public String getDefaultActionId() { 323 return defaultActionId; 324 } 325 326 public void setDefaultActionId(String defaultActionId) { 327 this.defaultActionId = defaultActionId; 328 } 329 330 @SuppressWarnings("unchecked") 331 public List<String> getScriptFiles() { 332 return scriptFiles; 333 } 334 335 public Set<String> getScriptBlocks() { 336 return scriptBlocks; 337 } 338 339 public Set<String> getStyleFiles() { 340 return styleFiles; 341 } 342 343 public Set<String> getStyleBlocks() { 344 return styleBlocks; 345 } 346 347 public Set<String> getOnloadScripts() { 348 return onloadScripts; 349 } 350 351 public Set<String> getOnunloadScripts() { 352 return onunloadScripts; 353 } 354 355 public Set<String> getOnexitScripts() { 356 return onexitScripts; 357 } 358 359 public Set<String> getOnsubmitScripts() { 360 return onsubmitScripts; 361 } 362 363 public Set<UIPopup> getPopups() { 364 return popups; 365 } 366 367 public Integer getWidth() { 368 if (width != null) { 369 return width; 370 } 371 ValueBinding vb = getValueBinding(ATTR_WIDTH); 372 if (vb != null) { 373 return (Integer) vb.getValue(getFacesContext()); 374 } else { 375 Integer requestWidth = 376 (Integer) FacesContext.getCurrentInstance().getExternalContext(). 377 getRequestMap().get("tobago-page-clientDimension-width"); 378 if (requestWidth != null) { 379 return requestWidth; 380 } else { 381 return DEFAULT_WIDTH; 382 } 383 } 384 } 385 386 public void setWidth(Integer width) { 387 this.width = width; 388 } 389 390 public Integer getHeight() { 391 if (height != null) { 392 return height; 393 } 394 ValueBinding vb = getValueBinding(ATTR_HEIGHT); 395 if (vb != null) { 396 return (Integer) vb.getValue(getFacesContext()); 397 } else { 398 Integer requestHeight = 399 (Integer) FacesContext.getCurrentInstance().getExternalContext(). 400 getRequestMap().get("tobago-page-clientDimension-height"); 401 if (requestHeight != null) { 402 return requestHeight; 403 } else { 404 return DEFAULT_HEIGHT; 405 } 406 } 407 } 408 409 public void setHeight(Integer height) { 410 this.height = height; 411 } 412 413 public String getApplicationIcon() { 414 if (applicationIcon != null) { 415 return applicationIcon; 416 } 417 ValueBinding vb = getValueBinding(ATTR_APPLICATION_ICON); 418 if (vb != null) { 419 return (String) vb.getValue(getFacesContext()); 420 } else { 421 return null; 422 } 423 } 424 425 public void setApplicationIcon(String applicationIcon) { 426 this.applicationIcon = applicationIcon; 427 } 428 429 public void restoreState(FacesContext context, Object state) { 430 Object[] values = (Object[]) state; 431 super.restoreState(context, values[0]); 432 this.width = (Integer) values[1]; 433 this.height = (Integer) values[2]; 434 this.focusId = (String) values[3]; 435 this.applicationIcon = (String) values[4]; 436 } 437 438 public Object saveState(FacesContext context) { 439 Object[] values = new Object[5]; 440 values[0] = super.saveState(context); 441 values[1] = width; 442 values[2] = height; 443 values[3] = focusId; 444 values[4] = applicationIcon; 445 return values; 446 } 447 }