001: /*
002: * Version: MPL 1.1/GPL 2.0/LGPL 2.1
003: *
004: * "The contents of this file are subject to the Mozilla Public License
005: * Version 1.1 (the "License"); you may not use this file except in
006: * compliance with the License. You may obtain a copy of the License at
007: * http://www.mozilla.org/MPL/
008: *
009: * Software distributed under the License is distributed on an "AS IS"
010: * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
011: * License for the specific language governing rights and limitations under
012: * the License.
013: *
014: * The Original Code is ICEfaces 1.5 open source software code, released
015: * November 5, 2006. The Initial Developer of the Original Code is ICEsoft
016: * Technologies Canada, Corp. Portions created by ICEsoft are Copyright (C)
017: * 2004-2006 ICEsoft Technologies Canada, Corp. All Rights Reserved.
018: *
019: * Contributor(s): _____________________.
020: *
021: * Alternatively, the contents of this file may be used under the terms of
022: * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"
023: * License), in which case the provisions of the LGPL License are
024: * applicable instead of those above. If you wish to allow use of your
025: * version of this file only under the terms of the LGPL License and not to
026: * allow others to use your version of this file under the MPL, indicate
027: * your decision by deleting the provisions above and replace them with
028: * the notice and other provisions required by the LGPL License. If you do
029: * not delete the provisions above, a recipient may use your version of
030: * this file under either the MPL or the LGPL License."
031: *
032: */
033:
034: package com.icesoft.faces.renderkit.dom_html_basic;
035:
036: import com.icesoft.faces.context.DOMContext;
037: import com.icesoft.faces.context.effects.CurrentStyle;
038: import com.icesoft.util.SeamUtilities;
039: import org.apache.commons.logging.Log;
040: import org.apache.commons.logging.LogFactory;
041: import org.w3c.dom.Element;
042:
043: import javax.faces.FacesException;
044: import javax.faces.component.UIComponent;
045: import javax.faces.component.UIForm;
046: import javax.faces.context.FacesContext;
047: import java.io.IOException;
048: import java.util.HashMap;
049: import java.util.Iterator;
050: import java.util.Map;
051:
052: public class FormRenderer extends DomBasicRenderer {
053:
054: public static final String COMMAND_LINK_HIDDEN_FIELD = "command_link_hidden_field";
055: private static final String COMMAND_LINK_HIDDEN_FIELDS_KEY = "com.icesoft.faces.FormRequiredHidden";
056: public static final String FOCUS_HIDDEN_FIELD = "focus_hidden_field";
057: private static final Log log = LogFactory
058: .getLog(FormRenderer.class);
059:
060: public void decode(FacesContext facesContext,
061: UIComponent uiComponent) {
062: validateParameters(facesContext, uiComponent, UIForm.class);
063: UIForm uiForm = (UIForm) uiComponent;
064: Map requestParameterMap = facesContext.getExternalContext()
065: .getRequestParameterMap();
066: String formClientId = uiForm.getClientId(facesContext);
067: if (requestParameterMap.containsKey(formClientId)
068: || uiComponent.getAttributes().containsKey(
069: "fileUploaded")) {
070: uiForm.setSubmitted(true);
071: } else {
072: uiForm.setSubmitted(false);
073: }
074: }
075:
076: public void encodeBegin(FacesContext facesContext,
077: UIComponent uiComponent) throws IOException {
078:
079: validateParameters(facesContext, uiComponent, UIForm.class);
080: validateNestingForm(uiComponent);
081: DOMContext domContext = DOMContext.attachDOMContext(
082: facesContext, uiComponent);
083: String formClientId = uiComponent.getClientId(facesContext);
084:
085: if (!domContext.isInitialized()) {
086: Element root = domContext.createElement("form");
087:
088: domContext.setRootNode(root);
089: root.setAttribute("id", formClientId);
090: root.setAttribute("method", "post");
091: String action = null;
092: // can't use a string constant we need to pass in the form id
093: // form id is not accessible in the form action attribute
094: action = "iceSubmit('" + formClientId + "');";
095: root.setAttribute("action", action);
096:
097: String styleClass = (String) uiComponent.getAttributes()
098: .get("styleClass");
099: if (styleClass != null) {
100: root.setAttribute("class", styleClass);
101: }
102: String acceptcharset = (String) uiComponent.getAttributes()
103: .get("acceptcharset");
104: if (acceptcharset != null) {
105: root.setAttribute("accept-charset", acceptcharset);
106: }
107: //redirect form submits
108: String redirectScript = "'"
109: + formClientId
110: + "'.asExtendedElement().captureAndRedirectSubmit();";
111: Element scriptElement = (Element) root
112: .appendChild(domContext.createElement("script"));
113: scriptElement.setAttribute("language", "javascript");
114: scriptElement.appendChild(domContext
115: .createTextNode(redirectScript));
116: root.appendChild(scriptElement);
117:
118: // this hidden field will be checked in the decode method to
119: // determine if this form has been submitted.
120: Element formHiddenField = domContext.createElement("input");
121: formHiddenField.setAttribute("type", "hidden");
122: formHiddenField.setAttribute("name", formClientId);
123: formHiddenField.setAttribute("value", formClientId);
124: root.appendChild(formHiddenField);
125:
126: // Only render the css update field once. Rendering more then one will cause
127: // a duplicate ID error
128: Element cssUpdateField = domContext
129: .createElement(HTML.INPUT_ELEM);
130: cssUpdateField.setAttribute(HTML.TYPE_ATTR,
131: HTML.INPUT_TYPE_HIDDEN);
132: cssUpdateField.setAttribute(HTML.NAME_ATTR,
133: CurrentStyle.CSS_UPDATE_FIELD);
134: cssUpdateField.setAttribute(HTML.VALUE_ATTR, "");
135: root.appendChild(cssUpdateField);
136: }
137:
138: // This has to occur outside the isInitialized test, as it has to happen
139: // all the time, even if the form otherwise has not changed.
140: Element root = (Element) domContext.getRootNode();
141:
142: String conversationId = SeamUtilities.getSeamConversationId();
143: if (conversationId != null) {
144: String conversationParamName = SeamUtilities
145: .getConversationIdParameterName();
146:
147: Element conversationIDElement = domContext
148: .createElement(HTML.INPUT_ELEM);
149: if (log.isDebugEnabled()) {
150: log.debug("Embedding Seam Param - name: "
151: + conversationParamName + ", value: "
152: + conversationId);
153: }
154: conversationIDElement.setAttribute(HTML.TYPE_ATTR,
155: HTML.INPUT_TYPE_HIDDEN);
156: conversationIDElement.setAttribute(HTML.NAME_ATTR,
157: conversationParamName);
158:
159: conversationIDElement.setAttribute(HTML.VALUE_ATTR,
160: conversationId);
161: root.appendChild(conversationIDElement);
162:
163: }
164:
165: String flowId = SeamUtilities.getSpringFlowId();
166: if (flowId != null) {
167: String flowParamName = SeamUtilities
168: .getFlowIdParameterName();
169:
170: Element flowIDElement = domContext
171: .createElement(HTML.INPUT_ELEM);
172: if (log.isDebugEnabled()) {
173: log.debug("Embedding Spring Param - name: "
174: + flowParamName + ", value: " + flowId);
175: }
176: String flowParamId = formClientId + ":" + flowParamName;
177: flowIDElement.setAttribute(HTML.TYPE_ATTR,
178: HTML.INPUT_TYPE_HIDDEN);
179: flowIDElement.setAttribute(HTML.NAME_ATTR, flowParamName);
180: flowIDElement.setAttribute(HTML.ID_ATTR, flowParamId);
181:
182: flowIDElement.setAttribute(HTML.VALUE_ATTR, flowId);
183: root.appendChild(flowIDElement);
184:
185: }
186:
187: String contextClass = facesContext.getClass().toString();
188: root.setAttribute("context_type", contextClass);
189:
190: PassThruAttributeRenderer.renderAttributes(facesContext,
191: uiComponent, null);
192: facesContext.getApplication().getViewHandler().writeState(
193: facesContext);
194:
195: // don't override user-defined value
196: String userDefinedValue = root.getAttribute("onsubmit");
197: if (userDefinedValue == null
198: || userDefinedValue.equalsIgnoreCase("")) {
199: root.setAttribute("onsubmit", "return false;");
200: }
201: addHiddenField(facesContext, FOCUS_HIDDEN_FIELD);
202:
203: try {
204: domContext.startNode(facesContext, uiComponent, root);
205: } catch (IOException ioe) {
206: ioe.printStackTrace();
207: }
208: domContext.stepInto(uiComponent);
209: }
210:
211: public void encodeChildren(FacesContext facesContext,
212: UIComponent uiComponent) {
213: validateParameters(facesContext, uiComponent, UIForm.class);
214: }
215:
216: public void encodeEnd(FacesContext facesContext,
217: UIComponent uiComponent) throws IOException {
218: validateParameters(facesContext, uiComponent, UIForm.class);
219:
220: // render needed hidden fields added by CommandLinkRenderer (and perhaps
221: // other renderers as well)
222: DOMContext domContext = DOMContext.getDOMContext(facesContext,
223: uiComponent);
224: // set static class variable for support of myfaces command link
225: renderCommandLinkHiddenFields(facesContext, uiComponent);
226: domContext.stepOver();
227: domContext.endNode(facesContext, uiComponent, domContext
228: .getRootNode());
229: }
230:
231: /**
232: * @param facesContext
233: * @param uiComponent Render any required hidden fields. There is a list
234: * (on the request map of the external context) of
235: * 'required hidden fields'. Hidden fields can be
236: * contributed by the CommandLinkRenderer. Contribution
237: * is made during rendering of this form's commandLink
238: * children so we have to wait for the child renderers
239: * to complete their work before we render the hidden
240: * fields. Therefore, this method should be called from
241: * the form's encodeEnd method. We can assume that the
242: * hidden fields are the last fields in the form because
243: * they are rendered in the FormRenderer's encodeEnd
244: * method. Note that the CommandLinkRenderer adds one
245: * hidden field that indicates the id of the link that
246: * was clicked to submit the form ( in case there are
247: * multiple commandLinks on a page) and one hidden field
248: * for each of its UIParameter children.
249: */
250: private static void renderCommandLinkHiddenFields(
251: FacesContext facesContext, UIComponent uiComponent) {
252: Map commandLinkHiddenFields = getCommandLinkFields(facesContext);
253: if (commandLinkHiddenFields != null) {
254: renderRequiredCommandLinkHiddenFields(uiComponent,
255: facesContext, commandLinkHiddenFields);
256: resetCommandLinkFieldsInRequestMap(facesContext);
257: }
258: }
259:
260: /**
261: * @param facesContext
262: */
263: private static void resetCommandLinkFieldsInRequestMap(
264: FacesContext facesContext) {
265: Map requestMap = facesContext.getExternalContext()
266: .getRequestMap();
267: requestMap.put(COMMAND_LINK_HIDDEN_FIELDS_KEY, null);
268: }
269:
270: /**
271: * @param uiComponent
272: * @param facesContext
273: * @param map
274: */
275: private static void renderRequiredCommandLinkHiddenFields(
276: UIComponent uiComponent, FacesContext facesContext, Map map) {
277: DOMContext domContext = DOMContext.getDOMContext(facesContext,
278: uiComponent);
279: Element root = (Element) domContext.getRootNode();
280: Iterator commandLinkFields = map.entrySet().iterator();
281: while (commandLinkFields.hasNext()) {
282: Map.Entry nextField = (Map.Entry) commandLinkFields.next();
283: if (COMMAND_LINK_HIDDEN_FIELD.equals(nextField.getValue())) {
284: Element next = domContext.createElement("input");
285: next.setAttribute("type", "hidden");
286: next
287: .setAttribute("name", nextField.getKey()
288: .toString());
289: root.appendChild(next);
290: }
291: }
292: }
293:
294: /**
295: * @param facesContext
296: * @param fieldName
297: */
298: public static void addHiddenField(FacesContext facesContext,
299: String fieldName) {
300: addHiddenField(facesContext, fieldName,
301: COMMAND_LINK_HIDDEN_FIELD);
302: }
303:
304: /**
305: * @param facesContext
306: * @param fieldName
307: * @param value
308: */
309: //make this method public when we modify the hidden field rendering
310: //to accept arbitrary hidden fields
311: private static void addHiddenField(FacesContext facesContext,
312: String fieldName, String value) {
313: Map hiddenFieldMap = getCommandLinkFields(facesContext);
314: if (hiddenFieldMap == null) {
315: hiddenFieldMap = createCommandLinkFieldsOnRequestMap(facesContext);
316: }
317: if (!hiddenFieldMap.containsKey(fieldName)) {
318: hiddenFieldMap.put(fieldName, value);
319: }
320: }
321:
322: /**
323: * @param facesContext
324: * @return Map the hiddenFieldMap
325: */
326: private static Map getCommandLinkFields(FacesContext facesContext) {
327: Map requestMap = facesContext.getExternalContext()
328: .getRequestMap();
329: Map hiddenFieldMap = (Map) requestMap
330: .get(COMMAND_LINK_HIDDEN_FIELDS_KEY);
331: if (hiddenFieldMap == null) {
332: hiddenFieldMap = new HashMap();
333: requestMap.put(COMMAND_LINK_HIDDEN_FIELDS_KEY,
334: hiddenFieldMap);
335: }
336: return hiddenFieldMap;
337: }
338:
339: /**
340: * @param facesContext
341: * @return Map hiddenFieldMap
342: */
343: private static Map createCommandLinkFieldsOnRequestMap(
344: FacesContext facesContext) {
345: Map requestMap = facesContext.getExternalContext()
346: .getRequestMap();
347: Map hiddenFieldMap = (Map) requestMap
348: .get(COMMAND_LINK_HIDDEN_FIELDS_KEY);
349: if (hiddenFieldMap == null) {
350: hiddenFieldMap = new HashMap();
351: requestMap.put(COMMAND_LINK_HIDDEN_FIELDS_KEY,
352: hiddenFieldMap);
353: }
354: return hiddenFieldMap;
355: }
356:
357: private void validateNestingForm(UIComponent uiComponent)
358: throws IOException {
359: UIComponent parent = uiComponent.getParent();
360: if (parent == null) {
361: return;
362: }
363: if (parent instanceof UIForm) {
364: throw new FacesException(
365: "Nested form found on the page. The form "
366: + "action element can not be nested");
367: }
368: validateNestingForm(parent);
369: }
370: }
|