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