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 }