001: /******************************************************************************
002: * JBoss, a division of Red Hat *
003: * Copyright 2006, Red Hat Middleware, LLC, and individual *
004: * contributors as indicated by the @authors tag. See the *
005: * copyright.txt in the distribution for a full listing of *
006: * individual contributors. *
007: * *
008: * This is free software; you can redistribute it and/or modify it *
009: * under the terms of the GNU Lesser General Public License as *
010: * published by the Free Software Foundation; either version 2.1 of *
011: * the License, or (at your option) any later version. *
012: * *
013: * This software is distributed in the hope that it will be useful, *
014: * but WITHOUT ANY WARRANTY; without even the implied warranty of *
015: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
016: * Lesser General Public License for more details. *
017: * *
018: * You should have received a copy of the GNU Lesser General Public *
019: * License along with this software; if not, write to the Free *
020: * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA *
021: * 02110-1301 USA, or see the FSF site: http://www.fsf.org. *
022: ******************************************************************************/package org.jboss.portal.faces.component.portlet;
023:
024: import org.jboss.portal.Mode;
025: import org.jboss.portal.WindowState;
026: import org.jboss.portal.faces.util.ActionEventDispatcher;
027: import org.jboss.portal.portlet.Portlet;
028: import org.jboss.portal.portlet.PortletContext;
029: import org.jboss.portal.portlet.PortletInvoker;
030: import org.jboss.portal.portlet.PortletInvokerException;
031: import org.jboss.portal.portlet.PortletParameters;
032: import org.jboss.portal.portlet.PortletParametersStateString;
033: import org.jboss.portal.portlet.impl.PortletRequestDecoder;
034: import org.jboss.portal.portlet.invocation.response.FragmentResponse;
035: import org.jboss.portal.portlet.invocation.response.InsufficientPrivilegesResponse;
036: import org.jboss.portal.portlet.invocation.response.PortletInvocationResponse;
037:
038: import javax.faces.component.UICommand;
039: import javax.faces.context.FacesContext;
040: import javax.faces.context.ResponseWriter;
041: import javax.faces.el.MethodBinding;
042: import javax.faces.el.ValueBinding;
043: import javax.faces.event.AbortProcessingException;
044: import javax.faces.event.FacesEvent;
045: import javax.faces.event.PhaseId;
046: import java.io.IOException;
047: import java.util.HashMap;
048: import java.util.Iterator;
049: import java.util.Map;
050: import java.util.Set;
051:
052: /**
053: * @author <a href="mailto:julien@jboss.org">Julien Viet</a>
054: * @version $Revision: 9125 $
055: * @todo: - add validation of initial mode and window state against the supported values - add validation of mode and
056: * window state on portlet events against the supported values - add handling of non expected results from render and
057: * process action - add handling when portlet does not exist (probably show the nested content of the tag as markup)
058: * @todo but less important: - integrate user profile - clean up the facelet portlet handler - make a JSP tag
059: * @todo later: - form rewriting so it can work in a JSF form
060: */
061: public class UIPortlet extends UICommand {
062:
063: /** . */
064: private final ActionEventDispatcher dispatcher = new ActionEventDispatcher(
065: this );
066:
067: /** The portlet invoker. */
068: private PortletInvoker portletInvoker;
069:
070: /** The portlet id. */
071: private String portletId;
072:
073: /** The event listener if not null. */
074: private MethodBinding portletListener;
075:
076: /** The supported window states. */
077: private Set supportedWindowStates;
078:
079: /** The supported modes. */
080: private Set supportedModes;
081:
082: /** The initial window state value if not null, otherwise NORMAL is used. */
083: private String initialWindowState;
084:
085: /** The initial mode value if not null, otherwise VIEW is used. */
086: private String initialMode;
087:
088: /** . */
089: private String onClick;
090:
091: /** The internal nav state. */
092: private PortletParameters internalNavState;
093:
094: /** The internal window state value. */
095: private WindowState internalWindowState;
096:
097: /** The internal mode value. */
098: private Mode internalMode;
099:
100: public PortletInvoker getPortletInvoker() {
101: if (portletInvoker != null) {
102: return portletInvoker;
103: }
104:
105: //
106: ValueBinding vb = getValueBinding("portletInvoker");
107: if (vb != null) {
108: return (PortletInvoker) vb.getValue(getFacesContext());
109: }
110:
111: //
112: return null;
113: }
114:
115: public void setPortletInvoker(PortletInvoker portletInvoker) {
116: this .portletInvoker = portletInvoker;
117: }
118:
119: public String getPortletId() {
120: if (portletId != null) {
121: return portletId;
122: }
123:
124: //
125: ValueBinding vb = getValueBinding("portletId");
126: if (vb != null) {
127: String id = (String) vb.getValue(getFacesContext());
128: if (id == null) {
129: return null;
130: } else {
131: // getValue will return an empty string if the binding exists but is not bound
132: return "".equals(id) ? null : id;
133: }
134: }
135:
136: //
137: return null;
138: }
139:
140: public void setPortletId(String portletId) {
141: this .portletId = portletId;
142: }
143:
144: public Set getSupportedModes() {
145: if (supportedModes != null) {
146: return supportedModes;
147: }
148:
149: //
150: ValueBinding vb = getValueBinding("supportedModes");
151: if (vb != null) {
152: return (Set) vb.getValue(getFacesContext());
153: }
154:
155: //
156: return null;
157: }
158:
159: public void setSupportedModes(Set supportedModes) {
160: this .supportedModes = supportedModes;
161: }
162:
163: public Set getSupportedWindowStates() {
164: if (supportedWindowStates != null) {
165: return supportedWindowStates;
166: }
167:
168: //
169: ValueBinding vb = getValueBinding("supportedWindowStates");
170: if (vb != null) {
171: return (Set) vb.getValue(getFacesContext());
172: }
173:
174: //
175: return null;
176: }
177:
178: public void setSupportedWindowStates(Set supportedWindowStates) {
179: this .supportedWindowStates = supportedWindowStates;
180: }
181:
182: public String getInitialWindowState() {
183: if (initialWindowState != null) {
184: return initialWindowState;
185: }
186:
187: //
188: ValueBinding vb = getValueBinding("initialWindowState");
189: if (vb != null) {
190: return (String) vb.getValue(getFacesContext());
191: }
192:
193: //
194: return null;
195: }
196:
197: public void setInitialWindowState(String initialWindowState) {
198: this .initialWindowState = initialWindowState;
199: }
200:
201: public String getInitialMode() {
202: if (initialMode != null) {
203: return initialMode;
204: }
205:
206: //
207: ValueBinding vb = getValueBinding("initialMode");
208: if (vb != null) {
209: return (String) vb.getValue(getFacesContext());
210: }
211:
212: //
213: return null;
214: }
215:
216: public void setInitialMode(String initialMode) {
217: this .initialMode = initialMode;
218: }
219:
220: public String getOnClick() {
221: return onClick;
222: }
223:
224: public void setOnClick(String onClick) {
225: this .onClick = onClick;
226: }
227:
228: WindowState getInternalWindowState() {
229: if (internalWindowState == null) {
230: if (initialWindowState != null) {
231: return WindowState.create(initialWindowState);
232: } else {
233: return WindowState.NORMAL;
234: }
235: } else {
236: return internalWindowState;
237: }
238: }
239:
240: void setInternalWindowState(WindowState windowState) {
241: this .internalWindowState = windowState;
242: }
243:
244: Mode getInternalMode() {
245: if (internalMode == null) {
246: if (initialMode != null) {
247: return Mode.create(initialMode);
248: } else {
249: return Mode.VIEW;
250: }
251: } else {
252: return internalMode;
253: }
254: }
255:
256: void setInternalMode(Mode mode) {
257: this .internalMode = mode;
258: }
259:
260: void setInternalNavState(PortletParameters navState) {
261: ValueBinding vb = getValueBinding("renderParameters");
262: if (vb != null) {
263: Map renderParameters = (Map) vb.getValue(getFacesContext());
264: renderParameters.clear();
265: renderParameters.putAll(navState);
266: } else {
267: this .internalNavState = navState;
268: }
269: }
270:
271: PortletParameters getInternalNavState() {
272: ValueBinding vb = getValueBinding("renderParameters");
273: if (vb != null) {
274: Map renderParameters = (Map) vb.getValue(getFacesContext());
275: return PortletParameters.wrap(renderParameters);
276: } else {
277: return internalNavState;
278: }
279: }
280:
281: Portlet getPortlet() {
282: String portletId = getPortletId();
283:
284: //
285: if (portletId != null) {
286: try {
287: PortletInvoker portletInvoker = getPortletInvoker();
288: return portletInvoker.getPortlet(PortletContext
289: .createPortletContext(portletId));
290: } catch (PortletInvokerException e) {
291: e.printStackTrace();
292: }
293: }
294:
295: //
296: return null;
297: }
298:
299: public String getFamily() {
300: return "org.jboss.portal.core.Portlet";
301: }
302:
303: public void broadcast(FacesEvent event)
304: throws AbortProcessingException {
305: if (event instanceof PortletEvent) {
306: PortletEvent pevent = (PortletEvent) event;
307:
308: //
309: FacesContext faces = getFacesContext();
310:
311: //
312: dispatcher.dispatch(faces, pevent);
313:
314: //
315: pevent.execute(faces);
316: } else {
317: super .broadcast(event);
318: }
319: }
320:
321: public Object saveState(FacesContext faces) {
322: String opaqueValue = null;
323: if (internalNavState != null) {
324: opaqueValue = new PortletParametersStateString(
325: internalNavState).getStringValue();
326: }
327:
328: //
329: Object values[] = new Object[11];
330: values[0] = super .saveState(faces);
331: values[1] = portletId;
332: values[2] = opaqueValue;
333: values[3] = saveAttachedState(faces, portletListener);
334: values[4] = supportedWindowStates;
335: values[5] = supportedModes;
336: values[6] = initialWindowState;
337: values[7] = initialMode;
338: values[8] = internalNavState;
339: values[9] = internalMode;
340: values[10] = onClick;
341: return values;
342: }
343:
344: public void restoreState(FacesContext faces, Object state) {
345: Object values[] = (Object[]) state;
346: super .restoreState(faces, values[0]);
347: String serializedNavState = (String) values[2];
348:
349: //
350: portletId = (String) values[1];
351: internalNavState = serializedNavState != null ? new PortletParametersStateString(
352: serializedNavState).getParameters()
353: : null;
354: portletListener = (MethodBinding) restoreAttachedState(faces,
355: values[3]);
356: supportedWindowStates = (Set) values[4];
357: supportedModes = (Set) values[5];
358: initialWindowState = (String) values[6];
359: initialMode = (String) values[7];
360: internalNavState = (PortletParameters) values[8];
361: internalMode = (Mode) values[9];
362: onClick = (String) values[10];
363: }
364:
365: public void decode(FacesContext faces) {
366: String clientId = getClientId(faces);
367: String clientIdValue = (String) faces.getExternalContext()
368: .getRequestParameterMap().get(clientId);
369:
370: // We have been targetted
371: if ("jbp".equals(clientIdValue)) {
372: Map jsfParams = faces.getExternalContext()
373: .getRequestParameterValuesMap();
374: Map portletParams = new HashMap();
375: for (Iterator i = jsfParams.entrySet().iterator(); i
376: .hasNext();) {
377: Map.Entry entry = (Map.Entry) i.next();
378: String name = (String) entry.getKey();
379: if (clientId.equals(name)) {
380: // Skip the client id
381: } else {
382: String[] values = (String[]) ((String[]) entry
383: .getValue()).clone();
384: portletParams.put(name, values);
385: }
386: }
387:
388: // Decode the request
389: PortletRequestDecoder decoder = new PortletRequestDecoder();
390: decoder.decode(portletParams, null);
391: Mode mode = decoder.getMode();
392: WindowState windowState = decoder.getWindowState();
393:
394: //
395: switch (decoder.getType()) {
396: case PortletRequestDecoder.RENDER_TYPE:
397: PortletRenderEvent prevent = new PortletRenderEvent(
398: this , ((PortletParametersStateString) decoder
399: .getNavigationalState())
400: .getParameters(), mode, windowState);
401: prevent.setPhaseId(PhaseId.INVOKE_APPLICATION);
402: queueEvent(prevent);
403: break;
404: case PortletRequestDecoder.ACTION_TYPE:
405: if (mode == null) {
406: mode = getInternalMode();
407: }
408: if (windowState == null) {
409: windowState = getInternalWindowState();
410: }
411: PortletActionEvent paevent = new PortletActionEvent(
412: this ,
413: ((PortletParametersStateString) decoder
414: .getInteractionState()).getParameters(),
415: mode, windowState);
416: paevent.setPhaseId(PhaseId.INVOKE_APPLICATION);
417: queueEvent(paevent);
418: break;
419: }
420: }
421: }
422:
423: public void encodeBegin(FacesContext faces) throws IOException {
424: Portlet portlet = getPortlet();
425:
426: //
427: if (portlet != null) {
428: //
429: JSFInvocation invocation = new JSFInvocation(faces, this );
430:
431: //
432: try {
433: PortletInvocationResponse pir = invocation
434: .render(portlet, getInternalMode(),
435: getInternalWindowState(),
436: getInternalNavState());
437:
438: //
439: if (pir instanceof FragmentResponse) {
440: FragmentResponse fragment = (FragmentResponse) pir;
441: ResponseWriter writer = faces.getResponseWriter();
442: writer.startElement("div", this );
443: writer.writeAttribute("id", "bilto", null);
444: writer.write(fragment.getContent());
445: writer.endElement("div");
446:
447: //
448: if (onClick != null) {
449: writer.startElement("script", this );
450: writer
451: .write(
452:
453: "var isIE = /msie/i.test(navigator.userAgent);\n"
454: +
455:
456: "function attachEvent(elm, eventName, eventHandler) {\n"
457: + " if (isIE) {\n"
458: + " elm.attachEvent(\"on\" + eventName, eventHandler);\n"
459: + " } else {\n"
460: + " elm.addEventListener(eventName, eventHandler, false);\n"
461: + " }\n"
462: + "}\n"
463: +
464:
465: "function getEventTarget(event) {\n"
466: + " var targetElement = isIE ? event.srcElement : event.target;\n"
467: + " while (targetElement.nodeType == 3 && targetElement.parentNode != null) {\n"
468: + " targetElement = targetElement.parentNode;\n"
469: + " }\n"
470: + " return targetElement;\n"
471: + "}\n"
472: +
473:
474: "\n"
475: +
476:
477: "function PortletURL(url) {\n"
478: + " this.params = new Object();\n"
479: + " this.url = url;\n"
480: + " this.setParameter = function(name, value) {;\n"
481: + " this.params[name] = value;\n"
482: + " return this;\n"
483: + " }\n"
484: + " this.renderURL = function() {;\n"
485: + " var tmp = this.url;\n"
486: + " for (var i in this.params) {\n"
487: + " tmp = tmp + ('&' + i + '=' + this.params[i]);\n"
488: + " };\n"
489: + " return tmp;\n"
490: + " }\n"
491: +
492:
493: // Temporary hack for Sun RI which encodes contextual state on the page
494: " var viewStateId = document.getElementById('javax.faces.ViewState')\n"
495: + " if (viewStateId != null) {\n"
496: + " this.params['javax.faces.ViewState'] = viewStateId.value\n"
497: + " }\n"
498: +
499:
500: "}\n"
501: +
502:
503: "function fx(event) {\n"
504: + " var target = getEventTarget(event);\n"
505: + " if (target.nodeName == 'INPUT' && target.type == 'submit') {\n"
506: + " var current = target.parentNode;\n"
507: + " while (current.nodeName != 'FORM' && current.nodeName != 'BODY') {\n"
508: + " current = current.parentNode;\n"
509: + " }\n"
510: + " if (current.nodeName == 'FORM') {\n"
511: + " var action = current.action;\n"
512: + " var portletURL = new PortletURL(action);\n"
513: + " (function(url){"
514: + onClick
515: + "})(portletURL);\n"
516: + " current.action = portletURL.renderURL();\n"
517: + " } else {\n"
518: + " // Really can't do nothing for now\n"
519: + " }\n"
520: + " }\n"
521: + " else if (target.nodeName == 'A') {\n"
522: + " var href = target.href;\n"
523: + " var portletURL = new PortletURL(href);\n"
524: + " (function(url){"
525: + onClick
526: + "})(portletURL);\n"
527: + " target.href = portletURL.renderURL();\n"
528: + " }\n"
529: + " \n"
530: + "}\n"
531: +
532:
533: "var elt = document.getElementById('bilto');\n"
534: + "attachEvent(elt, 'click', fx);\n");
535: writer.endElement("script");
536: }
537: writer.flush();
538: } else if (pir instanceof InsufficientPrivilegesResponse) {
539: ResponseWriter writer = faces.getResponseWriter();
540: writer.startElement("div", this );
541: writer
542: .write("<p class=\"portlet-msg-error\">You don't have the privileges to access this resource.</p>");
543: writer.endElement("div");
544: writer.flush();
545: }
546: } catch (PortletInvokerException e) {
547: e.printStackTrace();
548: }
549: } else {
550: System.out.println("Portlet not found: " + portletId);
551: }
552: }
553:
554: public void encodeChildren(FacesContext facesContext)
555: throws IOException {
556: }
557:
558: public void encodeEnd(FacesContext facesContext) throws IOException {
559: }
560:
561: public String getRendererType() {
562: // Returns null as we handle rendering ourselves
563: return null;
564: }
565: }
|