View Javadoc

1   /*
2    * Copyright 2000-2001,2004 The Apache Software Foundation.
3    * 
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    * 
8    *      http://www.apache.org/licenses/LICENSE-2.0
9    * 
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  
17  package org.apache.jetspeed.modules.actions.portlets;
18  
19  // Jetspeed Stuff
20  import org.apache.jetspeed.modules.actions.portlets.CustomizeSetAction;
21  import org.apache.jetspeed.portal.Portlet;
22  import org.apache.jetspeed.portal.PortletConfig;
23  import org.apache.jetspeed.portal.PortletException;
24  import org.apache.jetspeed.portal.PortletSkin;
25  import org.apache.jetspeed.portal.portlets.VelocityPortlet;
26  import org.apache.jetspeed.services.persistence.PersistenceManager;
27  import org.apache.jetspeed.services.Registry;
28  import org.apache.jetspeed.services.rundata.JetspeedRunData;
29  import org.apache.jetspeed.om.BaseSecurityReference;
30  import org.apache.jetspeed.om.profile.Entry;
31  import org.apache.jetspeed.om.profile.Profile;
32  import org.apache.jetspeed.om.profile.Skin;
33  import org.apache.jetspeed.om.profile.psml.PsmlSkin;
34  import org.apache.jetspeed.om.SecurityReference;
35  import org.apache.jetspeed.om.registry.PortletEntry;
36  import org.apache.jetspeed.om.registry.Parameter;
37  import org.apache.jetspeed.om.registry.base.BaseParameter;
38  import org.apache.jetspeed.om.security.JetspeedUser;
39  import org.apache.jetspeed.services.JetspeedSecurity;
40  import org.apache.jetspeed.services.PortalToolkit;
41  import org.apache.jetspeed.services.logging.JetspeedLogFactoryService;
42  import org.apache.jetspeed.services.logging.JetspeedLogger;
43  import org.apache.jetspeed.services.security.PortalResource;
44  import org.apache.jetspeed.portal.PortletInstance;
45  import org.apache.jetspeed.util.MetaData;
46  import org.apache.jetspeed.services.statemanager.SessionState;
47  
48  // Turbine stuff
49  import org.apache.turbine.util.RunData;
50  import org.apache.turbine.modules.ActionLoader;
51  
52  // Velocity Stuff
53  import org.apache.velocity.context.Context;
54  
55  import java.util.Vector;
56  import java.util.Iterator;
57  
58  /***
59   * This action implements the default portlet behavior customizer
60   *
61   * <p>Don't call it from the URL, the Portlet and the Action are automatically
62   * associated through the registry PortletName
63   *
64   * @author <a href="mailto:raphael@apache.org">Raphaël Luta</a>
65   */
66  public class CustomizeAction extends VelocityPortletAction
67  {
68  
69      public static final String PARAM_NAMESPACE = "_param_";
70  
71      /***
72       * Static initialization of the logger for this class
73       */    
74      private static final JetspeedLogger logger = JetspeedLogFactoryService.getLogger(CustomizeAction.class.getName());    
75      
76      /***
77       * Subclasses must override this method to provide default behavior
78       * for the portlet action
79       *
80       * <table>
81       * <tr><th>Context      </th><th> Description</th></tr>
82       * <!-- ---------  ------------------------- -->
83       * <tr><td>action       </td><td> Action to use</td></tr>
84       * <tr><td>current_skin </td><td> Current skin for this portlet INSTANCE</td></tr>
85       * <tr><td>params       </td><td> List of configurable parameters from the REGISTRY entry.</td></tr>
86       * <tr><td>portlet      </td><td> Portlet, not the Portlet Instance!</td></tr>
87       * <tr><td>skins        </td><td> List of skins</td></tr>
88       * <tr><td>security     </td><td> List of security ref</td></tr>
89       * <tr><td>security_ref </td><td> Current securityRef for this portlet INSTANCE</td></tr>
90       * </table>
91       */
92      protected void buildNormalContext( VelocityPortlet portlet,
93                                         Context context,
94                                         RunData rundata )
95      {
96  
97          // generic context stuff
98          context.put("skins", CustomizeSetAction.buildList(rundata, Registry.SKIN));
99          context.put("securitys", CustomizeSetAction.buildList(rundata, Registry.SECURITY));
100 
101         // we should first retrieve the portlet to customize
102         Portlet p = ((JetspeedRunData)rundata).getCustomized();
103 
104         context.put("action", "portlets.CustomizeAction");
105 
106         PortletInstance instance = PersistenceManager.getInstance(p, rundata);
107         context.put("portlet_instance", PersistenceManager.getInstance(p, rundata));
108 
109         if (p==null) return;
110 
111         // retrieve the portlet parameters
112         PortletEntry entry = (PortletEntry)Registry.getEntry(Registry.PORTLET,p.getName());
113         // save the entry in the session
114         Vector params = new Vector();
115         Iterator i = entry.getParameterNames();
116 
117         //System.out.println("==========================================");
118         while(i.hasNext())
119         {
120             String name = (String)i.next();
121             Parameter param = entry.getParameter(name);
122 
123             // filter some "system" and hidden parameters
124             if (  (!param.isHidden()) && (name.charAt(0)!='_') )
125             {
126                 // check the user role
127                 if (JetspeedSecurity.checkPermission((JetspeedUser)rundata.getUser(), new PortalResource( entry, param), JetspeedSecurity.PERMISSION_CUSTOMIZE))
128                 {
129                     // Implementation of clone() is missing so we have do it "by hand"
130                     Parameter clone = new BaseParameter();
131                     clone.setName(param.getName());
132                     clone.setTitle(param.getTitle());
133                     clone.setDescription(param.getDescription());
134                     clone.setType(param.getType());
135                     if (instance.getAttribute(name, null) != null)
136                     {
137                         clone.setValue(instance.getAttribute(name));
138                         //System.out.println("Adding value from instance [" + name + "] = [" + clone.getValue() + "]");
139                     }
140                     else if (p.getPortletConfig().getInitParameter(name) != null)
141                     {
142                         clone.setValue(p.getPortletConfig().getInitParameter(name));
143                         //System.out.println("Adding value from init [" + name + "] = [" + clone.getValue() + "]");
144                     }
145                     else
146                     {
147                         clone.setValue(param.getValue());
148                         //System.out.println("Adding value from registry [" + name + "] = [" + clone.getValue() + "]");
149                     }
150                     params.add(clone);
151                 }
152             }
153         }
154 
155         // get the customization state for this page
156         SessionState customizationState = ((JetspeedRunData)rundata).getPageSessionState();
157         customizationState.setAttribute("customize-parameters", params);
158 
159         // populate the customizer context
160         context.put("parameters", params);
161         context.put("portlet", p);
162         context.put("customizer", portlet);
163 
164         if (p.getPortletConfig().getSecurityRef() != null)
165           context.put("security_ref", p.getPortletConfig().getSecurityRef().getParent());
166         if (p.getPortletConfig().getSkin() != null)
167             context.put("current_skin", p.getPortletConfig().getPortletSkin().getName());
168 
169         Profile profile = ((JetspeedRunData)rundata).getCustomizedProfile();
170         String currentTitle = profile.getDocument().getEntryById(p.getID()).getTitle();
171         if (currentTitle == null && p.getPortletConfig().getMetainfo() != null)
172         {
173             currentTitle = p.getPortletConfig().getMetainfo().getTitle();
174         }
175         context.put("current_title", currentTitle);
176 
177     }
178 
179     /*** Clean up the customization state */
180     public void doCancel(RunData rundata, Context context)
181     {
182         ((JetspeedRunData)rundata).setCustomized(null);
183         if (((JetspeedRunData)rundata).getCustomized()==null)
184         {
185             try
186             {
187                 ActionLoader.getInstance().exec( rundata, "controls.EndCustomize" );
188             }
189             catch (Exception e)
190             {
191                 logger.error("Unable to load action controls.EndCustomize ",e);
192             }
193         }
194     }
195 
196     /***
197      * Resets the portlet settings to default
198      * 
199      * @param rundata
200      * @param context
201      */
202     public void doDefault(RunData rundata, Context context)
203     {                    
204         // we should first retrieve the portlet to customize and its parameters
205         // definition
206         Portlet p = ((JetspeedRunData) rundata).getCustomized();
207 
208         // Update paramaters
209         try
210         {
211             PortletInstance instance = PersistenceManager.getInstance(p, rundata);
212 
213             instance.removeAllAttributes();
214 
215             try
216             {
217                 ((JetspeedRunData) rundata).getCustomizedProfile().store();
218             }
219             catch (Exception e)
220             {
221                 logger.error("Unable to save profile ",e);
222             }
223 
224             //FIXME: this hack is due to the corrupted lifecycle of the portlet in the
225             //current API when caching is activated
226             try
227             {
228                 org.apache.jetspeed.util.PortletSessionState.setPortletConfigChanged(p, rundata);
229                 p.init();
230             }
231             catch (PortletException e)
232             {
233                 logger.error("Customizer failed to reinitialize the portlet "+p.getName(), e);
234             }
235 
236             // we're done, make sure clean up the
237             // session
238             doCancel(rundata, context);
239         }
240         catch (Exception e)
241         {
242             logger.error("Exception", e);
243         }
244     }
245 
246 
247     /*** Updates the customized portlet entry */
248     public void doUpdate(RunData rundata, Context context)
249     {
250         // get the customization state for this page
251         SessionState customizationState = ((JetspeedRunData)rundata).getPageSessionState();
252 
253         // we should first retrieve the portlet to customize and its parameters
254         // definition
255         Portlet p = ((JetspeedRunData)rundata).getCustomized();
256         Vector params = (Vector) customizationState.getAttribute("customize-parameters");
257         String newSecurityParent = rundata.getParameters().getString("_security_ref");
258         String newSkinName = (String) rundata.getParameters().getString("_skin");
259         String newTitle = (String) rundata.getParameters().getString("current_title");
260 
261         boolean changeRequested = ( (params != null) || (newSkinName != null) || (newSecurityParent != null) || (newTitle != null));
262         boolean madePsChange = false;
263         boolean madePcChange = false;
264 
265         if ((p==null) || (changeRequested == false ))
266         {
267             doCancel(rundata, context);
268             return;
269         }
270         PortletConfig pc = p.getPortletConfig();
271         Profile profile = ((JetspeedRunData)rundata).getCustomizedProfile();
272         Entry entry = profile.getDocument().getEntryById(p.getID());
273 
274         // Only update the security ref if the parent changed
275         if ((newSecurityParent != null))
276         {
277             boolean securityChanged = false;
278             SecurityReference currentSecurityRef = pc.getSecurityRef();
279             if (currentSecurityRef != null)
280             {
281                 securityChanged = (newSecurityParent.equals(currentSecurityRef.getParent()) == false);
282             }
283             else
284             {
285                 securityChanged = (newSecurityParent.trim().length() > 0);
286             }
287             if (securityChanged == true)
288             {
289                 SecurityReference securityRef = null;
290                 if ((newSecurityParent.trim().length() > 0))
291                 {
292                     securityRef = new BaseSecurityReference();
293                     securityRef.setParent( newSecurityParent);
294                 }
295                 // Note: setting the portlet's config may not be a good idea -
296                 // it might be used as the Portlet for other PSMLDocument Entries that
297                 // have a different idea of security - and the caching of Portlets does
298                 // NOT include security -ggolden.
299                 pc.setSecurityRef(securityRef);
300                 entry.setSecurityRef(securityRef);
301                 madePcChange = true;
302             }
303         }
304 
305         // Only update the skin if the name changed
306         if (newSkinName != null)
307         {
308             boolean skinChanged = false;
309             String currentSkinName = null;
310 
311             if  (pc.getSkin() != null)
312                 currentSkinName = pc.getPortletSkin().getName();
313 
314             if (currentSkinName != null)
315             {
316                 skinChanged = (newSkinName.equals(currentSkinName) == false);
317             }
318             else
319             {
320                 skinChanged = (newSkinName.trim().length() > 0);
321             }
322 
323             if (skinChanged == true)
324             {
325                 PortletSkin skin = null;
326                 if ((newSkinName.trim().length() > 0))
327                 {
328                     skin = PortalToolkit.getSkin(newSkinName);
329                     if (skin != null)
330                     {
331                         // Note: setting the portlet's config may not be a good idea -
332                         // it might be used as the Portlet for other PSMLDocument Entries that
333                         // have a different idea of skin - and the caching of Portlets does
334                         // NOT include skin -ggolden.
335                         pc.setPortletSkin(skin);
336 
337                         Skin psmlSkin = entry.getSkin();
338                         if (psmlSkin == null)
339                         {
340                             entry.setSkin(new PsmlSkin());
341                         }
342                         entry.getSkin().setName(newSkinName);
343                     }
344                     else
345                     {
346                         logger.warn( "Unable to update skin for portlet entry " + entry.getId() + " because skin " + skin + " does not exist.");
347                     }
348                 }
349                 else
350                 {
351                     // Note: setting the portlet's config may not be a good idea -
352                     // it might be used as the Portlet for other PSMLDocument Entries that
353                     // have a different idea of skin - and the caching of Portlets does
354                     // NOT include skin -ggolden.
355                     pc.setPortletSkin( null);
356                     entry.setSkin(null);
357                 }
358                 madePcChange = true;
359             }
360         }
361 
362         // Only update the title if the title changed
363         if (newTitle != null)
364         {
365             boolean titleChanged = false;
366             String currentTitle = entry.getTitle();
367 
368             MetaData md = pc.getMetainfo();
369             if  (currentTitle == null && md != null && md.getTitle() != null)
370                 currentTitle = md.getTitle();
371 
372             if (currentTitle != null)
373             {
374                 titleChanged = (newTitle.equals(currentTitle) == false);
375             }
376             else
377             {
378                 titleChanged = (newTitle.trim().length() > 0);
379             }
380 
381             if (titleChanged == true)
382             {
383 
384                 if ((newTitle.trim().length() > 0))
385                 {
386                     // Note: setting the portlet's config may not be a good idea -
387                     // it might be used as the Portlet for other PSMLDocument Entries that
388                     // have a different idea of title - and the caching of Portlets does
389                     // NOT include title -ggolden.
390                     if (md == null) {
391                         md = new MetaData();
392                         pc.setMetainfo(md);
393                     }
394                     md.setTitle(newTitle);
395                     entry.setTitle(newTitle);
396                     madePcChange = true;
397                 }
398             }
399         }
400 
401         // Update paramaters
402         try
403         {
404             PortletInstance instance = PersistenceManager.getInstance(p, rundata);
405             PortletEntry regEntry = (PortletEntry) Registry.getEntry(Registry.PORTLET, p.getName());
406 
407             Iterator i = params.iterator();
408 
409             //System.out.println("==========================================");
410             while(i.hasNext())
411             {
412                 Parameter param = (Parameter)i.next();
413                 String name = param.getName();
414                 String newValue = null;
415                 String[] testArray = rundata.getParameters().getStrings(name);
416                 if (testArray != null && testArray.length > 1)
417                 {
418                     newValue = org.apache.jetspeed.util.StringUtils.arrayToString(testArray, ",");
419                 }
420                 else 
421                 {
422                     newValue = rundata.getParameters().getString(name);
423                     if (newValue == null)
424                     {
425                         newValue = "";
426                     }
427                 }
428                 String regValue = regEntry.getParameter(name).getValue(); //param.getValue();
429                 String psmlValue = instance.getAttribute(name);
430 
431                 //System.out.println(name + "= [" + psmlValue + "] in psml");
432                 //System.out.println(name + "= [" + regValue + "] in registry");
433 
434                 // New value for this parameter exists
435                 if (newValue != null)
436                 {
437                     //System.out.println(name + "= [" + newValue + "] in request");
438                     // New value differs from registry - record it in psml
439                     if (!regValue.equals(newValue) || !psmlValue.equals(newValue))
440                     {
441                         instance.setAttribute(name, newValue);
442                         psmlValue = newValue;
443                         //System.out.println("setting attribute for [" + name + "] to [" + newValue + "]");
444                     }
445                     madePsChange = true;
446                 }
447                 // Remove duplicate parameters from psml
448                 if (psmlValue != null && psmlValue.equals(regValue))
449                 {
450                     //System.out.println("removing attribute for [" + name + "]");
451                     instance.removeAttribute(name);
452                     madePsChange = true;
453                 }
454 
455             }
456 
457             // save all the changes
458             if ((madePsChange == true) || (madePcChange == true))
459             {
460                 try
461                 {
462                     JetspeedRunData jdata = (JetspeedRunData) rundata;
463                     profile.store();
464                     //FIXME: this hack is due to the corrupted lifecycle of the portlet in the
465                     //current API when caching is activated
466                     p.init();
467                     org.apache.jetspeed.util.PortletSessionState.setPortletConfigChanged(p, rundata);
468                 }
469                 catch (PortletException e)
470                 {
471                     logger.error("Customizer failed to reinitialize the portlet "+p.getName(), e);
472                 }
473                 catch (Exception e)
474                 {
475                     logger.error("Unable to save profile ",e);
476                 }
477             }
478 
479             // we're done, make sure clean up the
480             // session
481             doCancel(rundata, context);
482         }
483         catch (Exception e)
484         {
485             logger.error("Exception", e);
486         }
487     }
488 }