1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.jetspeed.decoration;
18
19 import java.util.ArrayList;
20 import java.util.Collections;
21 import java.util.HashMap;
22 import java.util.Iterator;
23 import java.util.List;
24 import java.util.Properties;
25
26 import javax.portlet.PortletMode;
27 import javax.portlet.WindowState;
28
29 import org.apache.commons.logging.Log;
30 import org.apache.commons.logging.LogFactory;
31 import org.apache.jetspeed.JetspeedActions;
32 import org.apache.jetspeed.PortalReservedParameters;
33 import org.apache.jetspeed.cache.CacheElement;
34 import org.apache.jetspeed.cache.ContentCacheKey;
35 import org.apache.jetspeed.cache.JetspeedContentCache;
36 import org.apache.jetspeed.components.portletentity.PortletEntityNotStoredException;
37 import org.apache.jetspeed.container.url.PortalURL;
38 import org.apache.jetspeed.container.window.FailedToRetrievePortletWindow;
39 import org.apache.jetspeed.container.window.PortletWindowAccessor;
40 import org.apache.jetspeed.decoration.caches.SessionPathResolverCache;
41 import org.apache.jetspeed.om.common.portlet.PortletApplication;
42 import org.apache.jetspeed.om.common.portlet.PortletDefinitionComposite;
43 import org.apache.jetspeed.om.page.ContentFragment;
44 import org.apache.jetspeed.om.page.ContentPage;
45 import org.apache.jetspeed.om.page.Fragment;
46 import org.apache.jetspeed.om.page.Page;
47 import org.apache.jetspeed.pipeline.PipelineException;
48 import org.apache.jetspeed.pipeline.valve.AbstractValve;
49 import org.apache.jetspeed.pipeline.valve.Valve;
50 import org.apache.jetspeed.pipeline.valve.ValveContext;
51 import org.apache.jetspeed.request.RequestContext;
52 import org.apache.jetspeed.security.SecurityAccessController;
53 import org.apache.pluto.om.portlet.ContentTypeSet;
54 import org.apache.pluto.om.window.PortletWindow;
55
56 /***
57 * Assigns decorations and page actions to all of the portlet Fragments within
58 * the current request.
59 *
60 * @see org.apache.jetspeed.om.page.Fragment
61 * @see org.apache.jetspeed.om.page.Page
62 * @see org.apache.jetspeed.decoration.Decoration
63 * @see org.apache.jetspeed.decoration.LayoutDecoration
64 * @see org.apache.jetspeed.decoration.PortletDecoration
65 * @see org.apache.jetspeed.decoration.Theme
66 *
67 * @author <href a="mailto:weaver@apache.org">Scott T. Weaver</a>
68 * @author <href a="mailto:firevelocity@gmail.com">Vivek Kumar</a>
69 *
70 */
71 public class DecorationValve extends AbstractValve implements Valve
72 {
73 public static final String ACTION_IMAGE_EXTENSION_ATTR = "actionImageExtension";
74 public static final String IS_AJAX_DECORATION_REQUEST = "org.apache.jetspeed.decoration.ajax";
75
76 protected final static Log log = LogFactory.getLog(DecorationValve.class);
77
78 private final DecorationFactory decorationFactory;
79
80 private final PortletWindowAccessor windowAccessor;
81
82 private HashMap decoratorActionsAdapterCache = new HashMap();
83
84 private DecoratorActionsFactory defaultDecoratorActionsFactory;
85
86 private JetspeedContentCache cache = null;
87
88 private boolean useSessionForThemeCaching = false;
89
90 private boolean maxOnEdit = false;
91
92 /***
93 * For security constraint checks
94 */
95 protected SecurityAccessController accessController;
96
97 public DecorationValve(DecorationFactory decorationFactory, PortletWindowAccessor windowAccessor,SecurityAccessController accessController)
98 {
99 this(decorationFactory, windowAccessor, accessController, null);
100 }
101
102 public DecorationValve(DecorationFactory decorationFactory, PortletWindowAccessor windowAccessor,
103 SecurityAccessController accessController, JetspeedContentCache cache)
104 {
105 this(decorationFactory, windowAccessor, accessController, cache, false);
106 }
107
108 public DecorationValve(DecorationFactory decorationFactory, PortletWindowAccessor windowAccessor,
109 SecurityAccessController accessController, JetspeedContentCache cache,
110 boolean useSessionForThemeCaching)
111 {
112 this.decorationFactory = decorationFactory;
113 this.windowAccessor = windowAccessor;
114 this.defaultDecoratorActionsFactory = new DefaultDecoratorActionsFactory();
115
116 this.accessController = accessController;
117 this.cache = cache;
118 this.useSessionForThemeCaching = useSessionForThemeCaching;
119 }
120
121 public void invoke(RequestContext requestContext, ValveContext context) throws PipelineException
122 {
123
124 boolean isAjaxRequest = (context == null);
125 initFragments( requestContext, isAjaxRequest, null );
126
127
128 if (!isAjaxRequest)
129 {
130 context.invokeNext(requestContext);
131 }
132 }
133
134
135 public void initFragments( RequestContext requestContext, boolean isAjaxRequest, List fragments )
136 {
137 if (isAjaxRequest)
138 {
139 requestContext.setAttribute(IS_AJAX_DECORATION_REQUEST, new Boolean(true));
140 }
141
142 ContentPage page = requestContext.getPage();
143
144
145 if (requestContext
146 .getSessionAttribute(PortalReservedParameters.PAGE_THEME_OVERRIDE_ATTRIBUTE) != null)
147 {
148 String decoratorName = (String) requestContext
149 .getSessionAttribute(PortalReservedParameters.PAGE_THEME_OVERRIDE_ATTRIBUTE);
150 page.setDefaultDecorator(decoratorName, Fragment.LAYOUT);
151 }
152
153 PageActionAccess pageActionAccess = (PageActionAccess)requestContext.getAttribute(PortalReservedParameters.PAGE_EDIT_ACCESS_ATTRIBUTE);
154 String themeCacheKey = null;
155 ContentCacheKey themeContentCacheKey = null;
156 Theme theme = null;
157
158 if (useCache())
159 {
160 if (pageActionAccess.isEditing() == false)
161 {
162
163 if (this.useSessionForThemeCaching)
164 {
165 themeCacheKey = cache.createSessionKey(requestContext);
166 theme = (Theme) requestContext.getSessionAttribute(themeCacheKey);
167 }
168 else
169 {
170 themeContentCacheKey = cache.createCacheKey(requestContext, page.getId());
171 CacheElement themeCacheElem = cache.get(themeContentCacheKey);
172
173 if (themeCacheElem != null)
174 {
175 theme = (Theme) themeCacheElem.getContent();
176 }
177 }
178 }
179 }
180
181 if (theme != null)
182 {
183 theme.init(page, decorationFactory, requestContext);
184 requestContext.setAttribute(PortalReservedParameters.PAGE_THEME_ATTRIBUTE, theme);
185 boolean solo = isSoloMode(requestContext);
186 SessionPathResolverCache sessionPathResolver = new SessionPathResolverCache( requestContext.getRequest().getSession() );
187 initDepthFragmentDecorations(requestContext, theme, page.getRootContentFragment(),
188 pageActionAccess, isAjaxRequest,
189 ((DecorationFactoryImpl) decorationFactory).getResourceValidator(),
190 sessionPathResolver, (theme.isInvalidated() && !solo));
191
192 if (theme.isInvalidated() && !solo)
193 {
194 if (this.useSessionForThemeCaching)
195 {
196 requestContext.setSessionAttribute(themeCacheKey, theme);
197 }
198 else
199 {
200 CacheElement themeCacheElem = cache.createElement(themeContentCacheKey, theme);
201 cache.put(themeCacheElem);
202 }
203 theme.setInvalidated(false);
204 }
205 return;
206 }
207 theme = decorationFactory.getTheme(page, requestContext);
208 requestContext.setAttribute(PortalReservedParameters.PAGE_THEME_ATTRIBUTE, theme);
209 if ( fragments == null || fragments.size() == 0 )
210 {
211 ContentFragment rootFragment = page.getRootContentFragment();
212 initDepthFragments(requestContext, theme, rootFragment, pageActionAccess, isAjaxRequest, fragments);
213 }
214 else
215 {
216 Iterator fragmentsIter = fragments.iterator();
217 while ( fragmentsIter.hasNext() )
218 {
219 ContentFragment fragment = (ContentFragment)fragmentsIter.next();
220 initFragment(requestContext, theme, fragment, pageActionAccess, isAjaxRequest);
221 }
222 }
223
224 if (useCache() && !isSoloMode(requestContext))
225 {
226 if (themeContentCacheKey == null && themeCacheKey == null)
227 {
228 if (this.useSessionForThemeCaching)
229 {
230 themeCacheKey = cache.createSessionKey(requestContext);
231 requestContext.getRequest().getSession().removeAttribute(themeCacheKey);
232 }
233 else
234 {
235 themeContentCacheKey = cache.createCacheKey(requestContext, page.getId());
236 cache.remove(themeContentCacheKey);
237 }
238 }
239 else
240 {
241 if (this.useSessionForThemeCaching)
242 {
243 themeContentCacheKey = cache.createCacheKey(requestContext, page.getId());
244 requestContext.setSessionAttribute(themeCacheKey, theme);
245 }
246 else
247 {
248 CacheElement themeCacheElem = cache.createElement(themeContentCacheKey, theme);
249 cache.put(themeCacheElem);
250 }
251 }
252 }
253 }
254
255 protected boolean isSoloMode(RequestContext requestContext)
256 {
257 boolean solo = false;
258 PortletWindow window = requestContext.getPortalURL().getNavigationalState().getMaximizedWindow();
259 boolean maximized = (window != null);
260 if (maximized)
261 {
262 solo = JetspeedActions.SOLO_STATE.equals(requestContext.getPortalURL().getNavigationalState().getMappedState(window));
263 }
264 return solo;
265 }
266
267 protected boolean useCache()
268 {
269 return this.cache != null;
270 }
271
272 public String toString()
273 {
274 return "DecorationValve";
275 }
276
277 public DecoratorActionsFactory getDecoratorActionsAdapter(Decoration decoration)
278 {
279
280 String decoratorActionsAdapterClassName = decoration.getProperty("actions.factory");
281 if ( decoratorActionsAdapterClassName == null )
282 {
283 decoratorActionsAdapterClassName = defaultDecoratorActionsFactory.getClass().getName();
284 }
285 synchronized (decoratorActionsAdapterCache)
286 {
287 DecoratorActionsFactory adapter = (DecoratorActionsFactory)decoratorActionsAdapterCache.get(decoratorActionsAdapterClassName);
288 if ( adapter == null )
289 {
290 try
291 {
292 adapter = (DecoratorActionsFactory)Class.forName(decoratorActionsAdapterClassName).newInstance();
293 adapter.setMaximizeOnEdit(this.maxOnEdit);
294 }
295 catch (Exception e)
296 {
297 log.error("Failed to instantiate custom DecoratorActionsAdaptor "+decoratorActionsAdapterClassName+", falling back to default.",e);
298 adapter = (DecoratorActionsFactory)decoratorActionsAdapterCache.get(defaultDecoratorActionsFactory.getClass().getName());
299 if ( adapter == null )
300 {
301 adapter = defaultDecoratorActionsFactory;
302 }
303 }
304 decoratorActionsAdapterCache.put(decoratorActionsAdapterClassName,adapter);
305 }
306 return adapter;
307 }
308 }
309
310 /***
311 * Builds and assigns a list of available portlet modes and window states for
312 * the target <code>Fragment</code>.
313 *
314 * @param requestContext RequestContext of the current portal request.
315 * @param fragment Fragment to initialize modes and states for.
316 * @return
317 * @throws PortletEntityNotStoredException
318 * @throws FailedToRetrievePortletWindow
319 */
320 protected boolean initActionsForFragment(RequestContext requestContext,
321 ContentFragment fragment,
322 PageActionAccess pageActionAccess,
323 Decoration decoration,
324 boolean isAjaxRequest) throws FailedToRetrievePortletWindow, PortletEntityNotStoredException
325 {
326 boolean fragmentSupportsActions = false;
327 PortletWindow window = windowAccessor.getPortletWindow(fragment);
328 PortletDefinitionComposite portlet = (PortletDefinitionComposite) window.getPortletEntity().getPortletDefinition();
329
330 if (null == portlet)
331 {
332 return fragmentSupportsActions;
333 }
334
335 List actions = Collections.EMPTY_LIST;
336
337 PortletMode currentMode = requestContext.getPortalURL().getNavigationalState().getMode(window);
338 WindowState currentState = requestContext.getPortalURL().getNavigationalState().getState(window);
339 ContentTypeSet content = portlet.getContentTypeSet();
340
341 if ( fragment.equals(requestContext.getPage().getRootFragment()) )
342 {
343 fragmentSupportsActions = true;
344 actions = getPageModes(requestContext, window, content, currentMode, currentState, pageActionAccess, decoration, isAjaxRequest);
345 }
346 else if ( !Fragment.LAYOUT.equals(fragment.getType()) )
347 {
348 fragmentSupportsActions = true;
349 String fragmentId = fragment.getId();
350 PortletApplication pa = (PortletApplication)window.getPortletEntity().getPortletDefinition().getPortletApplicationDefinition();
351
352 String portletName = portlet.getUniqueName();
353
354 PortletMode currentMappedMode = pa.getMappedPortletMode(currentMode);
355 WindowState currentMappedState = pa.getMappedWindowState(currentState);
356
357 Object action;
358 PortletMode mappedMode;
359 PortletMode customMode;
360 WindowState mappedState;
361 WindowState customState;
362
363 ArrayList actionTemplates = new ArrayList();
364
365 DecoratorActionsFactory actionsAdapter = getDecoratorActionsAdapter(decoration);
366
367 Iterator iter = actionsAdapter.getSupportedActions(requestContext, pa, window, currentMappedMode, currentMappedState, decoration).iterator();
368
369 String currentModeAction = null;
370 String currentStateAction = null;
371
372 while ( iter.hasNext() )
373 {
374 action = iter.next();
375 if ( action instanceof PortletMode )
376 {
377 mappedMode = (PortletMode)action;
378 customMode = pa.getCustomPortletMode(mappedMode);
379
380 if ( customMode != null )
381 {
382 boolean equalsCurrentMode = customMode.equals(currentMode);
383 if ( equalsCurrentMode )
384 {
385 currentModeAction = mappedMode.toString();
386 }
387 if ( ! equalsCurrentMode || isAjaxRequest )
388 {
389 if ( content.supportsPortletMode(customMode)
390 && (!PortletMode.EDIT.equals(customMode) || pageActionAccess.isEditAllowed())
391 && pageActionAccess.checkPortletMode(fragmentId, portletName, mappedMode)
392 )
393 {
394 actionTemplates.add(new DecoratorActionTemplate(mappedMode, customMode));
395 }
396 }
397 }
398 }
399 else if ( action instanceof WindowState )
400 {
401 mappedState = (WindowState)action;
402 customState = pa.getCustomWindowState(mappedState);
403
404 if ( customState != null )
405 {
406 boolean equalsCurrentState = customState.equals(currentState);
407 if ( equalsCurrentState )
408 {
409 currentStateAction = mappedState.toString();
410 }
411 if ( ! equalsCurrentState || isAjaxRequest )
412 {
413 if ( pageActionAccess.checkWindowState(fragmentId, portletName, mappedState ) )
414 {
415 actionTemplates.add(new DecoratorActionTemplate(mappedState, customState));
416 }
417 }
418 }
419 }
420 }
421 actions = actionsAdapter.getDecoratorActions(requestContext, pa, window, currentMode, currentState, decoration, actionTemplates,portlet,fragment,accessController);
422
423 decoration.setCurrentModeAction( currentModeAction );
424 decoration.setCurrentStateAction( currentStateAction );
425 }
426
427 decoration.setActions( actions );
428
429 return fragmentSupportsActions;
430 }
431
432 /***
433 * Builds a list of portlet modes that can be executed on the current
434 * <code>fragment</code> excluding the portlet's current mode.
435 *
436 * @param requestContext RequestContext of the current portal request.
437 * @param pageActionAccess
438 * @param mode
439 * @param content
440 * @param portletName
441 * @param window
442 * @param fragment
443 * @return <code>java.util.List</code> of modes excluding the current one.
444 * @throws PortletEntityNotStoredException
445 */
446 protected List getPageModes(RequestContext requestContext, PortletWindow window, ContentTypeSet content,
447 PortletMode mode, WindowState state, PageActionAccess pageActionAccess, Decoration decoration,
448 boolean isAjaxRequest)
449 {
450 List pageModes = new ArrayList();
451
452
453 try
454 {
455 if (mode.equals(PortletMode.HELP) || !state.equals(WindowState.NORMAL))
456 {
457
458 PortalURL portalURL = requestContext.getPortalURL();
459 String action = requestContext.getResponse().encodeURL( (isAjaxRequest)
460 ? portalURL.createNavigationalEncoding(window, PortletMode.VIEW, WindowState.NORMAL)
461 : portalURL.createPortletURL(window, PortletMode.VIEW, WindowState.NORMAL, portalURL.isSecure()).toString() );
462 String actionName = PortletMode.VIEW.toString();
463 pageModes.add(new DecoratorAction(actionName, requestContext.getLocale(), decoration.getResource("images/" + actionName + ".gif"),action,DecoratorActionTemplate.ACTION_TYPE_MODE));
464 }
465 else if ( pageActionAccess.isEditAllowed() )
466 {
467 String targetMode = pageActionAccess.isEditing() ? PortletMode.VIEW.toString() : PortletMode.EDIT.toString();
468 PortalURL portalURL = requestContext.getPortalURL();
469 HashMap parameters = new HashMap();
470 String[] paramValues = new String[]{targetMode};
471 parameters.put("pageMode",paramValues);
472
473
474 String action = requestContext.getResponse().encodeURL( (isAjaxRequest)
475 ? portalURL.createNavigationalEncoding(window, parameters, PortletMode.VIEW, WindowState.NORMAL, true)
476 : portalURL.createPortletURL(window, parameters, PortletMode.VIEW, WindowState.NORMAL, true, portalURL.isSecure()).toString() );
477 pageModes.add(new DecoratorAction(targetMode, requestContext.getLocale(), decoration.getResource("images/" + targetMode + ".gif"), action,DecoratorActionTemplate.ACTION_TYPE_MODE));
478
479
480
481 if (content.supportsPortletMode(PortletMode.HELP))
482 {
483 if ( pageActionAccess.isEditing() )
484 {
485
486 paramValues[0] = PortletMode.VIEW.toString();
487 action = requestContext.getResponse().encodeURL( (isAjaxRequest)
488 ? portalURL.createNavigationalEncoding(window, parameters, PortletMode.HELP, WindowState.MAXIMIZED, true)
489 : portalURL.createPortletURL(window, parameters, PortletMode.HELP, WindowState.MAXIMIZED, true, portalURL.isSecure()).toString() );
490 }
491 else
492 {
493
494 action = requestContext.getResponse().encodeURL( (isAjaxRequest)
495 ? portalURL.createNavigationalEncoding(window, PortletMode.HELP, WindowState.MAXIMIZED)
496 : portalURL.createPortletURL(window,PortletMode.HELP, WindowState.MAXIMIZED, portalURL.isSecure()).toString() );
497 }
498 String actionName = PortletMode.HELP.toString();
499 pageModes.add(new DecoratorAction(actionName, requestContext.getLocale(), decoration.getResource("images/" + actionName + ".gif"), action,DecoratorActionTemplate.ACTION_TYPE_MODE));
500 }
501 }
502 }
503 catch (Exception e)
504 {
505 log.warn("Unable to initalize PageLayout actions", e);
506 pageModes = null;
507 }
508
509 return pageModes;
510 }
511
512 /***
513 * Intializes all fragments with there decorations and portlet modes
514 * and winodw states.
515 *
516 *
517 * @param requestContext RequestContext of the current portal request.
518 * @param theme
519 * @param fragment
520 * @param pageActionAccess
521 */
522 protected void initDepthFragments(RequestContext requestContext,
523 Theme theme,
524 ContentFragment fragment,
525 PageActionAccess pageActionAccess,
526 boolean isAjaxRequest,
527 List collectFragments )
528 {
529 final List contentFragments = fragment.getContentFragments();
530
531 if(contentFragments != null && contentFragments.size() > 0)
532 {
533 Iterator itr = contentFragments.iterator();
534 while(itr.hasNext())
535 {
536 ContentFragment aFragment = (ContentFragment) itr.next();
537 initDepthFragments(requestContext, theme, aFragment, pageActionAccess, isAjaxRequest, collectFragments);
538 }
539 }
540
541 if ( initFragment(requestContext, theme, fragment, pageActionAccess, isAjaxRequest) )
542 {
543 if ( collectFragments != null )
544 {
545 collectFragments.add( fragment );
546 }
547 }
548 }
549
550 protected boolean initFragment(RequestContext requestContext,
551 Theme theme,
552 ContentFragment fragment,
553 PageActionAccess pageActionAccess,
554 boolean isAjaxRequest)
555 {
556 boolean fragmentSupportsActions = false;
557 try
558 {
559 Decoration decoration = theme.getDecoration(fragment);
560 fragment.setDecoration(decoration);
561 fragmentSupportsActions = initActionsForFragment(requestContext, fragment, pageActionAccess, decoration, isAjaxRequest);
562 }
563 catch (Exception e)
564 {
565 log.warn("Unable to initalize actions for fragment "+fragment.getId(), e);
566 }
567 return fragmentSupportsActions;
568 }
569
570 /***
571 * Reintializes all fragments with there decorations and portlet modes
572 * and winodw states after theme is restored from cache.
573 *
574 * @param requestContext RequestContext of the current portal request.
575 * @param theme
576 * @param fragment
577 * @param pageActionAccess
578 * @param isAjaxRequest
579 * @param validator
580 * @param pathResolverCache
581 */
582 protected void initDepthFragmentDecorations(RequestContext requestContext,
583 Theme theme,
584 ContentFragment fragment,
585 PageActionAccess pageActionAccess,
586 boolean isAjaxRequest,
587 ResourceValidator validator,
588 PathResolverCache pathResolverCache,
589 boolean reloadActionList)
590 {
591 final List contentFragments = fragment.getContentFragments();
592
593 if(contentFragments != null && contentFragments.size() > 0)
594 {
595 Iterator itr = contentFragments.iterator();
596 while(itr.hasNext())
597 {
598 ContentFragment aFragment = (ContentFragment) itr.next();
599 initDepthFragmentDecorations(requestContext, theme, aFragment,
600 pageActionAccess, isAjaxRequest,
601 validator, pathResolverCache, reloadActionList);
602 }
603 }
604
605 try
606 {
607
608 Decoration decoration = theme.getDecoration(fragment);
609
610 Properties config = ((DecorationFactoryImpl) decorationFactory).getConfiguration(decoration.getName(), fragment.getType());
611 ((BaseDecoration) decoration).init(config, validator, pathResolverCache);
612
613 fragment.setDecoration(decoration);
614
615 if (reloadActionList)
616 {
617 initActionsForFragment(requestContext, fragment, pageActionAccess, decoration, isAjaxRequest);
618 }
619 }
620 catch (Exception e)
621 {
622 log.warn("Unable to initalize actions for fragment "+fragment.getId(), e);
623 }
624 }
625
626 public void setMaximizeOnEdit(boolean maxOnEdit)
627 {
628 this.maxOnEdit = maxOnEdit;
629 this.defaultDecoratorActionsFactory.setMaximizeOnEdit(maxOnEdit);
630 }
631
632 public boolean getMaximizeOnEdit()
633 {
634 return this.maxOnEdit;
635 }
636 }