View Javadoc

1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    * 
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   * 
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  package org.apache.jetspeed.layout.impl;
18  
19  import java.security.Principal;
20  import java.util.Map;
21  import java.util.Iterator;
22  
23  import javax.security.auth.Subject;
24  
25  import org.apache.commons.logging.Log;
26  import org.apache.commons.logging.LogFactory;
27  import org.apache.jetspeed.JetspeedActions;
28  import org.apache.jetspeed.administration.PortalConfiguration;
29  import org.apache.jetspeed.ajax.AJAXException;
30  import org.apache.jetspeed.ajax.AjaxAction;
31  import org.apache.jetspeed.ajax.AjaxBuilder;
32  import org.apache.jetspeed.components.portletregistry.PortletRegistry;
33  import org.apache.jetspeed.layout.Coordinate;
34  import org.apache.jetspeed.layout.PortletActionSecurityBehavior;
35  import org.apache.jetspeed.layout.PortletPlacementException;
36  import org.apache.jetspeed.layout.PortletPlacementContext;
37  import org.apache.jetspeed.om.page.Fragment;
38  import org.apache.jetspeed.om.page.Page;
39  import org.apache.jetspeed.page.PageManager;
40  import org.apache.jetspeed.page.document.NodeException;
41  import org.apache.jetspeed.portalsite.PortalSiteRequestContext;
42  import org.apache.jetspeed.profiler.impl.ProfilerValveImpl;
43  import org.apache.jetspeed.request.RequestContext;
44  import org.apache.jetspeed.security.RolePrincipal;
45  import org.apache.jetspeed.security.impl.RolePrincipalImpl;
46  import org.apache.jetspeed.Jetspeed;
47  
48  /***
49   * Move Portlet portlet placement action
50   *
51   * AJAX Parameters: 
52   *    id = the fragment id of the portlet to move
53   *    page = (implied in the URL)
54   * Additional Absolute Parameters:  
55   *    row = the new row to move to
56   *    col = the new column to move to
57   * Additional Relative Parameters: (move left, right, up, down)
58   *    none
59   *    
60   * @author <a>David Gurney</a>
61   * @author <a href="mailto:taylor@apache.org">David Sean Taylor</a>
62   * @version $Id: $
63   */
64  public class MovePortletAction 
65      extends BasePortletAction 
66      implements AjaxAction, AjaxBuilder, Constants
67  {
68      protected static final Log log = LogFactory.getLog(MovePortletAction.class);
69      protected static final String eol = System.getProperty( "line.separator" );
70      
71      private PortletRegistry registry;
72      private int iMoveType = -1;
73      private String sMoveType = null;
74  
75      public MovePortletAction( String template, 
76              				  String errorTemplate,
77                                PortletRegistry registry,
78                                String sMoveType )
79          throws AJAXException
80      {
81          this( template, errorTemplate, registry, sMoveType, null, null );
82      }
83      
84      public MovePortletAction( String template, 
85                                String errorTemplate,
86                                PortletRegistry registry,
87                                PageManager pageManager,
88                                PortletActionSecurityBehavior securityBehavior )
89          throws AJAXException
90      {
91          this( template, errorTemplate, registry, "moveabs", pageManager, securityBehavior );
92      }
93  
94      public MovePortletAction( String template,
95                                String errorTemplate,
96                                PortletRegistry registry,
97                                String sMoveType,
98                                PageManager pageManager,
99                                PortletActionSecurityBehavior securityBehavior )
100         throws AJAXException
101     {
102         super( template, errorTemplate, pageManager, securityBehavior );
103         setMoveType( sMoveType );
104         this.registry = registry;
105     }
106 
107     // Convert the move type into an integer
108     public void setMoveType(String p_sMoveType) throws AJAXException
109     {
110         sMoveType = p_sMoveType;
111 
112         if (p_sMoveType.equalsIgnoreCase("moveabs"))
113         {
114             iMoveType = ABS;
115         } 
116         else if (p_sMoveType.equalsIgnoreCase("moveup"))
117         {
118             iMoveType = UP;
119         } 
120         else if (p_sMoveType.equalsIgnoreCase("movedown"))
121         {
122             iMoveType = DOWN;
123         } 
124         else if (p_sMoveType.equalsIgnoreCase("moveleft"))
125         {
126             iMoveType = LEFT;
127         } 
128         else if (p_sMoveType.equalsIgnoreCase("moveright"))
129         {
130             iMoveType = RIGHT;
131         }
132         else if (p_sMoveType.equalsIgnoreCase("move"))
133         {
134             iMoveType = CARTESIAN;
135         }
136         else
137         {
138             throw new AJAXException("invalid move type of:" + p_sMoveType);
139         }
140     }
141 
142     public boolean runBatch(RequestContext requestContext, Map resultMap) throws AJAXException
143     {
144         return runAction(requestContext, resultMap, true);
145     }    
146     
147     public boolean run(RequestContext requestContext, Map resultMap)
148             throws AJAXException
149     {
150         return runAction(requestContext, resultMap, false);
151     }
152         
153     protected boolean runAction( RequestContext requestContext, Map resultMap, boolean batch )  throws AJAXException
154     {
155         boolean success = true;
156         String status = "success";
157         try
158         {
159             resultMap.put(ACTION, sMoveType);
160             // Get the necessary parameters off of the request
161             String moveFragmentId = getActionParameter(requestContext, FRAGMENTID);
162             String layoutId = getActionParameter(requestContext, LAYOUTID);
163             if ( moveFragmentId == null ) 
164             {
165                 throw new Exception( FRAGMENTID + " not provided; must specify portlet or layout id" ); 
166             }
167             resultMap.put(FRAGMENTID, moveFragmentId);
168                         
169             Fragment currentLayoutFragment = null;
170             Fragment moveToLayoutFragment = null;
171             // when layoutId is null we use old behavior, ignoring everything to do with multiple layout fragments
172             if ( layoutId != null && layoutId.length() > 0 && iMoveType != CARTESIAN )
173             {
174                 Page page = requestContext.getPage();
175                 currentLayoutFragment = page.getFragmentById( layoutId );
176                 if ( currentLayoutFragment == null )
177                 {
178                     throw new Exception("layout id not found: " + layoutId );
179                 }
180                 else
181                 {
182                     // determine if layoutId parameter refers to the current layout fragment or to some other layout fragment
183                     moveToLayoutFragment = currentLayoutFragment;
184                     Iterator layoutChildIter = moveToLayoutFragment.getFragments().iterator();
185                     while ( layoutChildIter.hasNext() )
186                     {
187                         Fragment childFrag = (Fragment)layoutChildIter.next();
188                         if ( childFrag != null )
189                         {
190                             if ( moveFragmentId.equals( childFrag.getId() ) )
191                             {
192                                 moveToLayoutFragment = null;
193                                 break;
194                             }
195                         }
196                     }
197                     if ( moveToLayoutFragment != null )
198                     {
199                         // figure out the current layout fragment - must know to be able to find the portlet
200                         //    fragment by row/col when a new page is created
201                         Fragment root = requestContext.getPage().getRootFragment();
202                         currentLayoutFragment = getParentFragmentById( moveFragmentId, root );
203                     }
204                 }
205                 if ( currentLayoutFragment == null )
206                 {
207                     // report error
208                     throw new Exception("parent layout id not found for portlet id:" + moveFragmentId );
209                 }
210             }
211             
212             if ( false == checkAccess( requestContext, JetspeedActions.EDIT ) )
213             {
214             	if ( ! isPageQualifiedForCreateNewPageOnEdit( requestContext ) )
215             	{
216             		success = false;
217                     resultMap.put(REASON, "Page is not qualified for create-new-page-on-edit");
218                     return success;
219             	}
220             	
221                 Page page = requestContext.getPage();
222                 Fragment fragment = page.getFragmentById( moveFragmentId );
223                 if ( fragment == null )
224                 {
225                     success = false;
226                     resultMap.put(REASON, "Fragment not found");
227                     return success;
228                 }
229                 NestedFragmentContext moveFragmentContext = null;
230                 NestedFragmentContext moveToFragmentContext = null;
231                 try
232                 {
233                 	moveFragmentContext = new NestedFragmentContext( fragment, page, registry );
234                 	//log.info( "moveFragmentContext original : " + eol + moveFragmentContext.toString() );
235                 	if ( moveToLayoutFragment != null )
236                 	{
237                 		moveToFragmentContext = new NestedFragmentContext( moveToLayoutFragment, page, registry );
238                 		//log.info( "moveToFragmentContext original : " + eol + moveToFragmentContext.toString() );
239                 	}
240                 }
241                 catch ( Exception ex )
242                 {
243                 	log.error( "Failure to construct nested context for fragment " + moveFragmentId, ex );
244                 	success = false;
245                     resultMap.put( REASON, "Cannot construct nested context for fragment" );
246                     return success;
247                 }
248                                 
249                 //log.info("before createNewPageOnEdit page-name=" + page.getName() + " page-path=" + page.getPath() + " page-url=" + page.getUrl() );
250                 if ( ! createNewPageOnEdit( requestContext ) )
251                 {
252                     success = false;
253                     resultMap.put(REASON, "Insufficient access to edit page");
254                     return success;
255                 }
256                 status = "refresh";
257                 
258                 Page newPage = requestContext.getPage();                
259                 //log.info("after createNewPageOnEdit page-name=" + newPage.getName() + " page-path=" + newPage.getPath() + " page-url=" + newPage.getUrl() );
260                 Fragment newPageRootFragment = newPage.getRootFragment();
261                 
262                 // using NestedFragmentContext, find portlet id for copy of target portlet in the new page 
263                 Fragment newFragment = null;
264                 try
265                 {
266                 	newFragment = moveFragmentContext.getFragmentOnNewPage( newPage, registry );
267                 	//log.info( "npe newFragment: " + newFragment.getType() + " " + newFragment.getId() );
268                 }
269                 catch ( Exception ex )
270                 {
271                 	log.error( "Failure to locate copy of fragment " + moveFragmentId, ex );
272                 	success = false;
273                     resultMap.put( REASON, "Failed to find new fragment for portlet id: " + moveFragmentId );
274                     return success;
275                 }
276                 
277                 moveFragmentId = newFragment.getId();
278                 currentLayoutFragment = getParentFragmentById( moveFragmentId, newPageRootFragment );
279                 if ( currentLayoutFragment == null )
280                 {
281                 	success = false;
282                     resultMap.put( REASON, "Failed to find parent layout for copied fragment " + moveFragmentId );
283                     return success;
284                 }
285                 //log.info( "npe newParentFragment: " + currentLayoutFragment.getType() + " " + currentLayoutFragment.getId() );
286                 if ( moveToLayoutFragment != null )
287                 {
288                 	Fragment newMoveToFragment = null;
289                     try
290                     {
291                     	newMoveToFragment = moveToFragmentContext.getFragmentOnNewPage( newPage, registry );
292                     	//log.info( "npe newMoveToFragment: " + newMoveToFragment.getType() + " " + newMoveToFragment.getId() );
293                     }
294                     catch ( Exception ex )
295                     {
296                     	log.error( "Failure to locate copy of destination fragment " + moveToLayoutFragment.getId(), ex );
297                     	success = false;
298                         resultMap.put( REASON, "Failed to find copy of destination fragment" );
299                         return success;
300                     }
301                     moveToLayoutFragment = newMoveToFragment;
302                 }
303             }
304             
305             if ( moveToLayoutFragment != null )
306             {
307                 success = moveToOtherLayoutFragment( requestContext,
308                                         			 batch,
309                                         			 resultMap,
310                                         			 moveFragmentId,
311                                         			 moveToLayoutFragment,
312                                         			 currentLayoutFragment ) ;
313             }
314             else
315             {
316             	PortletPlacementContext placement = null;
317             	Page page = requestContext.getPage();
318             	
319             	if ( currentLayoutFragment == null )
320             		currentLayoutFragment = getParentFragmentById( moveFragmentId, page.getRootFragment() );
321             	
322                 if ( currentLayoutFragment != null )
323                     placement = new PortletPlacementContextImpl( page, registry, currentLayoutFragment );
324                 else
325                     placement = new PortletPlacementContextImpl( page, registry );
326                 
327                 Fragment fragment = placement.getFragmentById(moveFragmentId);
328                 if ( fragment == null )
329                 {
330                     success = false;
331                     resultMap.put( REASON, "Failed to find fragment for portlet id: " + moveFragmentId );
332                     return success;
333                 }
334                 
335                 success = moveInFragment( requestContext, placement, fragment, null, resultMap, batch );
336             }
337             if ( success )
338             {
339             	resultMap.put( STATUS, status );
340             }
341         }
342         catch ( Exception e )
343         {
344             // Log the exception
345             log.error( "exception while moving a portlet", e );
346             resultMap.put( REASON, e.toString() );
347             // Return a failure indicator
348             success = false;
349         }
350 
351         return success;
352     }
353     
354     protected boolean moveInFragment( RequestContext requestContext, PortletPlacementContext placement, Fragment fragment, Fragment placeInLayoutFragment, Map resultMap, boolean batch )
355         throws PortletPlacementException, NodeException, AJAXException
356     {
357     	boolean success = true;
358 
359     	String moveFragmentId = fragment.getId();
360     	boolean addFragment = (placeInLayoutFragment != null);
361         Coordinate returnCoordinate = null;
362         float oldX = 0f, oldY = 0f, oldZ = 0f, oldWidth = 0f, oldHeight = 0f;
363         float x = -1f, y = -1f, z = -1f, width = -1f, height = -1f;
364         boolean absHeightChanged = false;
365 
366         // desktop extended
367         String posExtended = getActionParameter( requestContext, DESKTOP_EXTENDED );
368         if ( posExtended != null )
369         {
370             Map fragmentProperties = fragment.getProperties();
371             if ( fragmentProperties == null )
372             {
373                 success = false;
374                 resultMap.put(REASON, "Failed to acquire fragment properties map for portlet id: " + moveFragmentId );
375                 return success;
376             }
377             String oldDeskExt = (String)fragmentProperties.get( DESKTOP_EXTENDED );
378             resultMap.put( OLD_DESKTOP_EXTENDED, ( (oldDeskExt != null) ? oldDeskExt : "" ) );
379             fragmentProperties.put( DESKTOP_EXTENDED, posExtended );
380         }
381                 
382         // only required for moveabs
383         if ( iMoveType == ABS )
384         {
385             Coordinate newCoordinate = getCoordinateFromParams( requestContext );
386             returnCoordinate = placement.moveAbsolute( fragment, newCoordinate, addFragment );
387             String sHeight = getActionParameter( requestContext, HEIGHT );
388             if ( sHeight != null && sHeight.length() > 0 )
389             {
390                 oldHeight = fragment.getLayoutHeight();
391                 height = Float.parseFloat( sHeight );
392                 fragment.setLayoutHeight( height );
393                 absHeightChanged = true;
394             }
395         } 
396         else if ( iMoveType == LEFT )
397         {
398             returnCoordinate = placement.moveLeft( fragment );
399         } 
400         else if ( iMoveType == RIGHT )
401         {
402             returnCoordinate = placement.moveRight( fragment );
403         } 
404         else if ( iMoveType == UP )
405         {
406             returnCoordinate = placement.moveUp( fragment );
407         } 
408         else if ( iMoveType == DOWN )
409         {
410             returnCoordinate = placement.moveDown( fragment );
411         }
412         else if ( iMoveType == CARTESIAN )
413         {
414             String sx = getActionParameter( requestContext, X );
415             String sy = getActionParameter( requestContext, Y );
416             String sz = getActionParameter( requestContext, Z );
417             String sWidth = getActionParameter( requestContext, WIDTH );
418             String sHeight = getActionParameter( requestContext, HEIGHT );
419             if ( sx != null )
420             {
421                 oldX = fragment.getLayoutX();
422                 x = Float.parseFloat( sx ); 
423                 fragment.setLayoutX( x );
424             }
425             if ( sy != null )
426             {
427                 oldY = fragment.getLayoutY();                    
428                 y = Float.parseFloat( sy ); 
429                 fragment.setLayoutY( y );
430             }                
431             if ( sz != null )
432             {
433                 oldZ = fragment.getLayoutZ();                    
434                 z = Float.parseFloat( sz ); 
435                 fragment.setLayoutZ( z );
436             }                
437             if ( sWidth != null )
438             {
439                 oldWidth = fragment.getLayoutWidth();                    
440                 width = Float.parseFloat( sWidth ); 
441                 fragment.setLayoutWidth( width );
442             }
443             if ( sHeight != null )
444             {
445                 oldHeight = fragment.getLayoutHeight();                    
446                 height = Float.parseFloat( sHeight ); 
447                 fragment.setLayoutHeight( height );
448             }
449         }
450         
451         // synchronize back to the page layout root fragment
452         Page page = placement.syncPageFragments();
453     
454         if ( placeInLayoutFragment != null )
455         {
456             placeInLayoutFragment.getFragments().add( fragment );
457         }
458         
459         if ( pageManager != null && ! batch )
460         {
461             pageManager.updatePage( page );
462         }
463         
464         if ( iMoveType == CARTESIAN )
465         {
466             putCartesianResult( resultMap, x, oldX, X, OLD_X );
467             putCartesianResult( resultMap, y, oldY, Y, OLD_Y );                
468             putCartesianResult( resultMap, z, oldZ, Z, OLD_Z );
469             putCartesianResult( resultMap, width, oldWidth, WIDTH, OLD_WIDTH );
470             putCartesianResult( resultMap, height, oldHeight, HEIGHT, OLD_HEIGHT );
471         }
472         else
473         {
474             // Need to determine what the old col and row were
475             resultMap.put( OLDCOL, String.valueOf( returnCoordinate.getOldCol() ) );
476             resultMap.put( OLDROW, String.valueOf( returnCoordinate.getOldRow() ) );
477             // Need to determine what the new col and row were
478             resultMap.put( NEWCOL, String.valueOf( returnCoordinate.getNewCol() ) );
479             resultMap.put( NEWROW, String.valueOf( returnCoordinate.getNewRow() ) );
480             if ( absHeightChanged )
481             {
482                 putCartesianResult( resultMap, height, oldHeight, HEIGHT, OLD_HEIGHT );
483             }
484         }
485         
486         resultMap.put( FRAGMENTID, moveFragmentId );
487         
488         return success;
489     }
490 
491     protected boolean moveToOtherLayoutFragment( RequestContext requestContext,
492                                                  boolean batch,
493                                                  Map resultMap,
494                                                  String moveFragmentId,
495                                                  Fragment moveToLayoutFragment,
496                                                  Fragment removeFromLayoutFragment )
497         throws PortletPlacementException, NodeException, AJAXException
498     {
499         boolean success = true;
500         Fragment placeFragment = null;
501         if ( removeFromLayoutFragment != null )
502         {
503         	Page page = requestContext.getPage();
504             PortletPlacementContext placement = new PortletPlacementContextImpl( page, registry, removeFromLayoutFragment );
505         
506             placeFragment = placement.getFragmentById( moveFragmentId );
507             if ( placeFragment == null )
508             {
509                 success = false;
510                 resultMap.put( REASON, "Failed to find fragment to move to another layout for fragment id: " + moveFragmentId );
511                 return success;
512             }
513             placement.remove( placeFragment );
514             page = placement.syncPageFragments();
515             page.removeFragmentById( moveFragmentId );
516         }
517         if ( placeFragment != null )
518         {
519             return placeFragment( requestContext,
520                                   batch,
521                                   resultMap,
522                                   placeFragment,
523                                   moveToLayoutFragment );
524         }
525         return success;
526     }
527 
528     protected boolean placeFragment( RequestContext requestContext,
529                                      boolean batch,
530                                      Map resultMap,
531                                      Fragment placeFragment,
532                                      Fragment placeInLayoutFragment )
533         throws PortletPlacementException, NodeException, AJAXException
534     {
535         boolean success = true;
536         if ( placeFragment == null )
537         {
538             success = false;
539             return success;
540         }
541         
542         // add fragment
543         Page page = requestContext.getPage();
544         PortletPlacementContext placement = new PortletPlacementContextImpl( page, registry, placeInLayoutFragment );
545         //placement.add( placeFragment, getCoordinateFromParams( requestContext ) );
546         
547         success = moveInFragment( requestContext, placement, placeFragment, placeInLayoutFragment, resultMap, batch );
548 
549         return success;
550     }
551     
552     protected Coordinate getCoordinateFromParams(RequestContext requestContext)
553     {
554         String a_sCol = getActionParameter( requestContext, COL );
555         String a_sRow = getActionParameter( requestContext, ROW );
556 
557         int a_iCol = 0;
558         int a_iRow = 0;
559 
560         // Convert the col and row into integers
561         if ( a_sCol != null )
562         {
563             a_iCol = Integer.parseInt( a_sCol );
564         }
565         if ( a_sRow != null )
566         {
567             a_iRow = Integer.parseInt( a_sRow );
568         }
569 
570         Coordinate a_oCoordinate = new CoordinateImpl( 0, 0, a_iCol, a_iRow );
571         return a_oCoordinate;
572     }
573 
574     protected void putCartesianResult(Map resultMap, float value, float oldValue, String name, String oldName)
575     {    
576         if (value != -1F)
577         {
578             resultMap.put(oldName, new Float(oldValue));
579             resultMap.put(name, new Float(value));
580         }
581     }
582     
583     protected PortletRegistry getPortletRegistry()
584     {
585     	return this.registry;
586     }
587 }