KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > alfresco > web > bean > BrowseBean


1 /*
2  * Copyright (C) 2005 Alfresco, Inc.
3  *
4  * Licensed under the Mozilla Public License version 1.1
5  * with a permitted attribution clause. You may obtain a
6  * copy of the License at
7  *
8  * http://www.alfresco.org/legal/license.txt
9  *
10  * Unless required by applicable law or agreed to in writing,
11  * software distributed under the License is distributed on an
12  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
13  * either express or implied. See the License for the specific
14  * language governing permissions and limitations under the
15  * License.
16  */

17 package org.alfresco.web.bean;
18
19 import java.text.MessageFormat JavaDoc;
20 import java.util.ArrayList JavaDoc;
21 import java.util.Collections JavaDoc;
22 import java.util.HashSet JavaDoc;
23 import java.util.List JavaDoc;
24 import java.util.Map JavaDoc;
25 import java.util.Set JavaDoc;
26
27 import javax.faces.application.FacesMessage;
28 import javax.faces.context.FacesContext;
29 import javax.faces.event.ActionEvent;
30 import javax.transaction.UserTransaction JavaDoc;
31
32 import org.alfresco.config.Config;
33 import org.alfresco.config.ConfigElement;
34 import org.alfresco.config.ConfigService;
35 import org.alfresco.model.ContentModel;
36 import org.alfresco.service.cmr.dictionary.DictionaryService;
37 import org.alfresco.service.cmr.dictionary.TypeDefinition;
38 import org.alfresco.service.cmr.lock.LockService;
39 import org.alfresco.service.cmr.model.FileFolderService;
40 import org.alfresco.service.cmr.repository.ChildAssociationRef;
41 import org.alfresco.service.cmr.repository.ContentData;
42 import org.alfresco.service.cmr.repository.InvalidNodeRefException;
43 import org.alfresco.service.cmr.repository.NodeRef;
44 import org.alfresco.service.cmr.repository.NodeService;
45 import org.alfresco.service.cmr.repository.Path;
46 import org.alfresco.service.cmr.search.LimitBy;
47 import org.alfresco.service.cmr.search.ResultSet;
48 import org.alfresco.service.cmr.search.ResultSetRow;
49 import org.alfresco.service.cmr.search.SearchParameters;
50 import org.alfresco.service.cmr.search.SearchService;
51 import org.alfresco.service.cmr.security.PermissionService;
52 import org.alfresco.service.namespace.QName;
53 import org.alfresco.service.namespace.RegexQNamePattern;
54 import org.alfresco.web.app.AlfrescoNavigationHandler;
55 import org.alfresco.web.app.Application;
56 import org.alfresco.web.app.context.IContextListener;
57 import org.alfresco.web.app.context.UIContextService;
58 import org.alfresco.web.app.servlet.DownloadContentServlet;
59 import org.alfresco.web.app.servlet.FacesHelper;
60 import org.alfresco.web.bean.repository.MapNode;
61 import org.alfresco.web.bean.repository.Node;
62 import org.alfresco.web.bean.repository.NodePropertyResolver;
63 import org.alfresco.web.bean.repository.QNameNodeMap;
64 import org.alfresco.web.bean.repository.Repository;
65 import org.alfresco.web.bean.wizard.NewSpaceWizard;
66 import org.alfresco.web.config.ViewsConfigElement;
67 import org.alfresco.web.ui.common.Utils;
68 import org.alfresco.web.ui.common.Utils.URLMode;
69 import org.alfresco.web.ui.common.component.IBreadcrumbHandler;
70 import org.alfresco.web.ui.common.component.UIActionLink;
71 import org.alfresco.web.ui.common.component.UIBreadcrumb;
72 import org.alfresco.web.ui.common.component.UIModeList;
73 import org.alfresco.web.ui.common.component.UIStatusMessage;
74 import org.alfresco.web.ui.common.component.data.UIRichList;
75 import org.alfresco.web.ui.repo.component.IRepoBreadcrumbHandler;
76 import org.alfresco.web.ui.repo.component.UINodeDescendants;
77 import org.alfresco.web.ui.repo.component.UINodePath;
78 import org.alfresco.web.ui.repo.component.UISimpleSearch;
79 import org.apache.log4j.Logger;
80 import org.apache.log4j.Priority;
81
82 /**
83  * Bean providing properties and behaviour for the main folder/document browse screen and
84  * search results screens.
85  *
86  * @author Kevin Roast
87  */

88 public class BrowseBean implements IContextListener
89 {
90    // ------------------------------------------------------------------------------
91
// Construction
92

93    /**
94     * Default Constructor
95     */

96    public BrowseBean()
97    {
98       UIContextService.getInstance(FacesContext.getCurrentInstance()).registerBean(this);
99       
100       initFromClientConfig();
101    }
102    
103    
104    // ------------------------------------------------------------------------------
105
// Bean property getters and setters
106

107    /**
108     * @param nodeService The NodeService to set.
109     */

110    public void setNodeService(NodeService nodeService)
111    {
112       this.nodeService = nodeService;
113    }
114
115    /**
116     * @param searchService The Searcher to set.
117     */

118    public void setSearchService(SearchService searchService)
119    {
120       this.searchService = searchService;
121    }
122    
123    /**
124     * @param lockService The Lock Service to set.
125     */

126    public void setLockService(LockService lockService)
127    {
128       this.lockService = lockService;
129    }
130    
131    /**
132     * @param navigator The NavigationBean to set.
133     */

134    public void setNavigator(NavigationBean navigator)
135    {
136       this.navigator = navigator;
137    }
138    
139    /**
140     * @param dictionaryService The DictionaryService to set.
141     */

142    public void setDictionaryService(DictionaryService dictionaryService)
143    {
144       this.dictionaryService = dictionaryService;
145    }
146    
147    /**
148     * @param fileFolderService The FileFolderService to set.
149     */

150    public void setFileFolderService(FileFolderService fileFolderService)
151    {
152       this.fileFolderService = fileFolderService;
153    }
154    
155    /**
156     * @return Returns the browse View mode. See UIRichList
157     */

158    public String JavaDoc getBrowseViewMode()
159    {
160       return this.browseViewMode;
161    }
162    
163    /**
164     * @param browseViewMode The browse View mode to set. See UIRichList.
165     */

166    public void setBrowseViewMode(String JavaDoc browseViewMode)
167    {
168       this.browseViewMode = browseViewMode;
169    }
170    
171    /**
172     * @return Returns true if dashboard view is available for the current node.
173     */

174    public boolean isDashboardView()
175    {
176       return this.dashboardView;
177    }
178
179    /**
180     * @param dashboardView The dashboard view mode to set.
181     */

182    public void setDashboardView(boolean dashboardView)
183    {
184       this.dashboardView = dashboardView;
185       if (dashboardView == true)
186       {
187          FacesContext fc = FacesContext.getCurrentInstance();
188          fc.getApplication().getNavigationHandler().handleNavigation(fc, null, "dashboard");
189       }
190       else
191       {
192          navigateBrowseScreen();
193       }
194    }
195    
196    /**
197     * @return Returns the browsePageSize.
198     */

199    public int getBrowsePageSize()
200    {
201       return this.browsePageSize;
202    }
203    
204    /**
205     * @param browsePageSize The browsePageSize to set.
206     */

207    public void setBrowsePageSize(int browsePageSize)
208    {
209       this.browsePageSize = browsePageSize;
210    }
211    
212    /**
213     * @return Returns the minimum length of a valid search string.
214     */

215    public int getMinimumSearchLength()
216    {
217       return Application.getClientConfig(FacesContext.getCurrentInstance()).
218             getSearchMinimum();
219    }
220    
221    /**
222     * @return Returns the Space Node being used for the current browse screen action.
223     */

224    public Node getActionSpace()
225    {
226       return this.actionSpace;
227    }
228    
229    /**
230     * @param actionSpace Set the Space Node to be used for the current browse screen action.
231     */

232    public void setActionSpace(Node actionSpace)
233    {
234       if (actionSpace != null)
235       {
236          for (NodeEventListener listener : getNodeEventListeners())
237          {
238             listener.created(actionSpace, actionSpace.getType());
239          }
240       }
241       this.actionSpace = actionSpace;
242    }
243    
244    /**
245     * @return The document node being used for the current operation
246     */

247    public Node getDocument()
248    {
249       return this.document;
250    }
251
252    /**
253     * @param document The document node to be used for the current operation
254     */

255    public void setDocument(Node document)
256    {
257       if (document != null)
258       {
259          for (NodeEventListener listener : getNodeEventListeners())
260          {
261             listener.created(document, document.getType());
262          }
263       }
264       this.document = document;
265    }
266
267    /**
268     * @param contentRichList The contentRichList to set.
269     */

270    public void setContentRichList(UIRichList browseRichList)
271    {
272       this.contentRichList = browseRichList;
273       if (this.contentRichList != null)
274       {
275          this.contentRichList.setInitialSortColumn(
276                this.viewsConfig.getDefaultSortColumn(PAGE_NAME_BROWSE));
277          this.contentRichList.setInitialSortDescending(
278                this.viewsConfig.hasDescendingSort(PAGE_NAME_BROWSE));
279       }
280       // special case to handle an External Access URL
281
// these URLs restart the JSF lifecycle but an old UIRichList is restored from
282
// the component tree - which needs clearing "late" in the lifecycle process
283
if (externalForceRefresh)
284       {
285          this.contentRichList.setValue(null);
286          externalForceRefresh = false;
287       }
288    }
289    
290    /**
291     * @return Returns the contentRichList.
292     */

293    public UIRichList getContentRichList()
294    {
295       return this.contentRichList;
296    }
297    
298    /**
299     * @param spacesRichList The spacesRichList to set.
300     */

301    public void setSpacesRichList(UIRichList detailsRichList)
302    {
303       this.spacesRichList = detailsRichList;
304       if (this.spacesRichList != null)
305       {
306          // set the initial sort column and direction
307
this.spacesRichList.setInitialSortColumn(
308                this.viewsConfig.getDefaultSortColumn(PAGE_NAME_BROWSE));
309          this.spacesRichList.setInitialSortDescending(
310                this.viewsConfig.hasDescendingSort(PAGE_NAME_BROWSE));
311       }
312       if (externalForceRefresh)
313       {
314          this.spacesRichList.setValue(null);
315       }
316    }
317    
318    /**
319     * @return Returns the spacesRichList.
320     */

321    public UIRichList getSpacesRichList()
322    {
323       return this.spacesRichList;
324    }
325    
326    /**
327     * @return Returns the statusMessage component.
328     */

329    public UIStatusMessage getStatusMessage()
330    {
331       return this.statusMessage;
332    }
333
334    /**
335     * @param statusMessage The statusMessage component to set.
336     */

337    public void setStatusMessage(UIStatusMessage statusMessage)
338    {
339       this.statusMessage = statusMessage;
340    }
341    
342    /**
343     * @return Returns the deleteMessage.
344     */

345    public String JavaDoc getDeleteMessage()
346    {
347       return this.deleteMessage;
348    }
349
350    /**
351     * @param deleteMessage The deleteMessage to set.
352     */

353    public void setDeleteMessage(String JavaDoc deleteMessage)
354    {
355       this.deleteMessage = deleteMessage;
356    }
357    
358    /**
359     * Page accessed bean method to get the container nodes currently being browsed
360     *
361     * @return List of container Node objects for the current browse location
362     */

363    public List JavaDoc<Node> getNodes()
364    {
365       // the references to container nodes and content nodes are transient for one use only
366
// we do this so we only query/search once - as we cannot distinguish between node types
367
// until after the query. The logic is a bit confusing but otherwise we would need to
368
// perform the same query or search twice for every screen refresh.
369
if (this.containerNodes == null)
370       {
371          if (this.navigator.getSearchContext() == null)
372          {
373             queryBrowseNodes(this.navigator.getCurrentNodeId());
374          }
375          else
376          {
377             searchBrowseNodes(this.navigator.getSearchContext());
378          }
379       }
380       List JavaDoc<Node> result = this.containerNodes;
381       
382       // we clear the member variable during invalidateComponents()
383

384       return result;
385    }
386    
387    /**
388     * Page accessed bean method to get the content nodes currently being browsed
389     *
390     * @return List of content Node objects for the current browse location
391     */

392    public List JavaDoc<Node> getContent()
393    {
394       // see comment in getNodes() above for reasoning here
395
if (this.contentNodes == null)
396       {
397          if (this.navigator.getSearchContext() == null)
398          {
399             queryBrowseNodes(this.navigator.getCurrentNodeId());
400          }
401          else
402          {
403             searchBrowseNodes(this.navigator.getSearchContext());
404          }
405       }
406       List JavaDoc<Node> result = this.contentNodes;
407       
408       // we clear the member variable during invalidateComponents()
409

410       return result;
411    }
412    
413    /**
414     * Setup the common properties required at data-binding time.
415     * <p>
416     * These are properties used by components on the page when iterating over the nodes.
417     * Information such as whether the node is locked, a working copy, download URL etc.
418     * <p>
419     * We use a set of anonymous inner classes to provide the implemention for the property
420     * getters. The interfaces are only called when the properties are first requested.
421     *
422     * @param node Node to add the properties too
423     */

424    public void setupCommonBindingProperties(Node node)
425    {
426       // special properties to be used by the value binding components on the page
427
node.addPropertyResolver("locked", this.resolverlocked);
428       node.addPropertyResolver("owner", this.resolverOwner);
429       node.addPropertyResolver("workingCopy", this.resolverWorkingCopy);
430       node.addPropertyResolver("url", this.resolverUrl);
431       node.addPropertyResolver("fileType16", this.resolverFileType16);
432       node.addPropertyResolver("fileType32", this.resolverFileType32);
433       node.addPropertyResolver("size", this.resolverSize);
434       node.addPropertyResolver("cancelCheckOut", this.resolverCancelCheckOut);
435       node.addPropertyResolver("checkIn", this.resolverCheckIn);
436       node.addPropertyResolver("editLinkType", this.resolverEditLinkType);
437       node.addPropertyResolver("webdavUrl", this.resolverWebdavUrl);
438       node.addPropertyResolver("cifsPath", this.resolverCifsPath);
439    }
440    
441    
442    // ------------------------------------------------------------------------------
443
// IContextListener implementation
444

445    /**
446     * @see org.alfresco.web.app.context.IContextListener#contextUpdated()
447     */

448    public void contextUpdated()
449    {
450       invalidateComponents();
451    }
452    
453    
454    // ------------------------------------------------------------------------------
455
// NodeEventListener listeners
456

457    /**
458     * Add a listener to those called by the BrowseBean when nodes are created
459     */

460    public void addNodeEventListener(NodeEventListener listener)
461    {
462       getNodeEventListeners().add(listener);
463    }
464    
465    /**
466     * Remove a listener from the list of those called by BrowseBean
467     */

468    public void removeNodeEventListener(NodeEventListener listener)
469    {
470       getNodeEventListeners().remove(listener);
471    }
472    
473    
474    // ------------------------------------------------------------------------------
475
// Navigation action event handlers
476

477    /**
478     * Change the current view mode based on user selection
479     *
480     * @param event ActionEvent
481     */

482    public void viewModeChanged(ActionEvent event)
483    {
484       UIModeList viewList = (UIModeList)event.getComponent();
485       
486       // get the view mode ID
487
String JavaDoc viewMode = viewList.getValue().toString();
488       
489       if (VIEWMODE_DASHBOARD.equals(viewMode) == false)
490       {
491          // set the page size based on the style of display
492
setBrowsePageSize(this.viewsConfig.getDefaultPageSize(PAGE_NAME_BROWSE,
493                viewMode));
494          
495          if (logger.isDebugEnabled())
496             logger.debug("Browse view page size set to: " + getBrowsePageSize());
497          
498          // in case we left for dashboard
499
if (isDashboardView() == true)
500          {
501             setDashboardView(false);
502          }
503          
504          // push the view mode into the lists
505
setBrowseViewMode(viewMode);
506       }
507       else
508       {
509          // special case for Dashboard view
510
setDashboardView(true);
511       }
512    }
513    
514    
515    // ------------------------------------------------------------------------------
516
// Helper methods
517

518    /**
519     * Query a list of nodes for the specified parent node Id
520     *
521     * @param parentNodeId Id of the parent node or null for the root node
522     */

523    private void queryBrowseNodes(String JavaDoc parentNodeId)
524    {
525       long startTime = 0;
526       if (logger.isDebugEnabled())
527          startTime = System.currentTimeMillis();
528       
529       UserTransaction JavaDoc tx = null;
530       try
531       {
532          FacesContext context = FacesContext.getCurrentInstance();
533          tx = Repository.getUserTransaction(context, true);
534          tx.begin();
535          
536          NodeRef parentRef;
537          if (parentNodeId == null)
538          {
539             // no specific parent node specified - use the root node
540
parentRef = this.nodeService.getRootNode(Repository.getStoreRef());
541          }
542          else
543          {
544             // build a NodeRef for the specified Id and our store
545
parentRef = new NodeRef(Repository.getStoreRef(), parentNodeId);
546          }
547
548          List JavaDoc<ChildAssociationRef> childRefs = this.nodeService.getChildAssocs(parentRef,
549                ContentModel.ASSOC_CONTAINS, RegexQNamePattern.MATCH_ALL);
550          this.containerNodes = new ArrayList JavaDoc<Node>(childRefs.size());
551          this.contentNodes = new ArrayList JavaDoc<Node>(childRefs.size());
552          for (ChildAssociationRef ref: childRefs)
553          {
554             // create our Node representation from the NodeRef
555
NodeRef nodeRef = ref.getChildRef();
556             
557             if (this.nodeService.exists(nodeRef))
558             {
559                // find it's type so we can see if it's a node we are interested in
560
QName type = this.nodeService.getType(nodeRef);
561                
562                // make sure the type is defined in the data dictionary
563
TypeDefinition typeDef = this.dictionaryService.getType(type);
564                
565                if (typeDef != null)
566                {
567                   // look for Space or File nodes
568
if (this.dictionaryService.isSubClass(type, ContentModel.TYPE_FOLDER) == true &&
569                      this.dictionaryService.isSubClass(type, ContentModel.TYPE_SYSTEM_FOLDER) == false)
570                   {
571                      // create our Node representation
572
MapNode node = new MapNode(nodeRef, this.nodeService, true);
573                      node.addPropertyResolver("icon", this.resolverSpaceIcon);
574                      node.addPropertyResolver("smallIcon", this.resolverSmallIcon);
575                      
576                      for (NodeEventListener listener : getNodeEventListeners())
577                      {
578                         listener.created(node, type);
579                      }
580                      
581                      this.containerNodes.add(node);
582                   }
583                   else if (this.dictionaryService.isSubClass(type, ContentModel.TYPE_CONTENT))
584                   {
585                      // create our Node representation
586
MapNode node = new MapNode(nodeRef, this.nodeService, true);
587                      
588                      setupCommonBindingProperties(node);
589                      
590                      for (NodeEventListener listener : getNodeEventListeners())
591                      {
592                         listener.created(node, type);
593                      }
594                      
595                      this.contentNodes.add(node);
596                   }
597                }
598                else
599                {
600                   if (logger.isEnabledFor(Priority.WARN))
601                      logger.warn("Found invalid object in database: id = " + nodeRef + ", type = " + type);
602                }
603             }
604          }
605          
606          // commit the transaction
607
tx.commit();
608       }
609       catch (InvalidNodeRefException refErr)
610       {
611          Utils.addErrorMessage(MessageFormat.format(Application.getMessage(
612                FacesContext.getCurrentInstance(), Repository.ERROR_NODEREF), new Object JavaDoc[] {refErr.getNodeRef()}) );
613          this.containerNodes = Collections.<Node>emptyList();
614          this.contentNodes = Collections.<Node>emptyList();
615          try { if (tx != null) {tx.rollback();} } catch (Exception JavaDoc tex) {}
616       }
617       catch (Throwable JavaDoc err)
618       {
619          Utils.addErrorMessage(MessageFormat.format(Application.getMessage(
620                FacesContext.getCurrentInstance(), Repository.ERROR_GENERIC), err.getMessage()), err);
621          this.containerNodes = Collections.<Node>emptyList();
622          this.contentNodes = Collections.<Node>emptyList();
623          try { if (tx != null) {tx.rollback();} } catch (Exception JavaDoc tex) {}
624       }
625       
626       if (logger.isDebugEnabled())
627       {
628          long endTime = System.currentTimeMillis();
629          logger.debug("Time to query and build map nodes: " + (endTime - startTime) + "ms");
630       }
631    }
632    
633    /**
634     * Search for a list of nodes using the specific search context
635     *
636     * @param searchContext To use to perform the search
637     */

638    private void searchBrowseNodes(SearchContext searchContext)
639    {
640       long startTime = 0;
641       if (logger.isDebugEnabled())
642          startTime = System.currentTimeMillis();
643       
644       // get the searcher object to build the query
645
String JavaDoc query = searchContext.buildQuery(getMinimumSearchLength());
646       if (query == null)
647       {
648          // failed to build a valid query, the user probably did not enter the
649
// minimum text required to construct a valid search
650
Utils.addErrorMessage(MessageFormat.format(Application.getMessage(FacesContext.getCurrentInstance(), MSG_SEARCH_MINIMUM),
651                new Object JavaDoc[] {getMinimumSearchLength()}));
652          this.containerNodes = Collections.<Node>emptyList();
653          this.contentNodes = Collections.<Node>emptyList();
654          return;
655       }
656       
657       // perform the search against the repo
658
UserTransaction JavaDoc tx = null;
659       ResultSet results = null;
660       try
661       {
662          tx = Repository.getUserTransaction(FacesContext.getCurrentInstance(), true);
663          tx.begin();
664          
665          // Limit search to the first 100 matches
666
SearchParameters sp = new SearchParameters();
667          sp.setLanguage(SearchService.LANGUAGE_LUCENE);
668          sp.setQuery(query);
669          sp.addStore(Repository.getStoreRef());
670          
671          int searchLimit = Application.getClientConfig(FacesContext.getCurrentInstance()).getSearchMaxResults();
672          if(searchLimit > 0)
673          {
674             sp.setLimitBy(LimitBy.FINAL_SIZE);
675             sp.setLimit(searchLimit);
676          }
677          
678          results = this.searchService.query(sp);
679          if (logger.isDebugEnabled())
680             logger.debug("Search results returned: " + results.length());
681          
682          // create a list of items from the results
683
this.containerNodes = new ArrayList JavaDoc<Node>(results.length());
684          this.contentNodes = new ArrayList JavaDoc<Node>(results.length());
685          if (results.length() != 0)
686          {
687             for (ResultSetRow row: results)
688             {
689                NodeRef nodeRef = row.getNodeRef();
690                
691                if (this.nodeService.exists(nodeRef))
692                {
693                   // find it's type so we can see if it's a node we are interested in
694
QName type = this.nodeService.getType(nodeRef);
695                   
696                   // make sure the type is defined in the data dictionary
697
TypeDefinition typeDef = this.dictionaryService.getType(type);
698                
699                   if (typeDef != null)
700                   {
701                      // look for Space or File nodes
702
if (this.dictionaryService.isSubClass(type, ContentModel.TYPE_FOLDER) &&
703                          this.dictionaryService.isSubClass(type, ContentModel.TYPE_SYSTEM_FOLDER) == false)
704                      {
705                         // create our Node representation
706
MapNode node = new MapNode(nodeRef, this.nodeService, true);
707                         
708                         node.addPropertyResolver("path", this.resolverPath);
709                         node.addPropertyResolver("displayPath", this.resolverDisplayPath);
710                         node.addPropertyResolver("icon", this.resolverSpaceIcon);
711                         node.addPropertyResolver("smallIcon", this.resolverSmallIcon);
712                         
713                         for (NodeEventListener listener : getNodeEventListeners())
714                         {
715                            listener.created(node, type);
716                         }
717                         
718                         this.containerNodes.add(node);
719                      }
720                      else if (this.dictionaryService.isSubClass(type, ContentModel.TYPE_CONTENT))
721                      {
722                         // create our Node representation
723
MapNode node = new MapNode(nodeRef, this.nodeService, true);
724                         
725                         setupCommonBindingProperties(node);
726                         
727                         node.addPropertyResolver("path", this.resolverPath);
728                         node.addPropertyResolver("displayPath", this.resolverDisplayPath);
729                         
730                         for (NodeEventListener listener : getNodeEventListeners())
731                         {
732                            listener.created(node, type);
733                         }
734                         
735                         this.contentNodes.add(node);
736                      }
737                   }
738                   else
739                   {
740                      if (logger.isEnabledFor(Priority.WARN))
741                         logger.warn("Found invalid object in database: id = " + nodeRef + ", type = " + type);
742                   }
743                }
744                else
745                {
746                   if (logger.isEnabledFor(Priority.WARN))
747                      logger.warn("Missing object returned from search indexes: id = " + nodeRef + " search query: " + query);
748                }
749             }
750          }
751          
752          // commit the transaction
753
tx.commit();
754       }
755       catch (InvalidNodeRefException refErr)
756       {
757          Utils.addErrorMessage(MessageFormat.format(Application.getMessage(
758                FacesContext.getCurrentInstance(), Repository.ERROR_NODEREF), new Object JavaDoc[] {refErr.getNodeRef()}) );
759          this.containerNodes = Collections.<Node>emptyList();
760          this.contentNodes = Collections.<Node>emptyList();
761          try { if (tx != null) {tx.rollback();} } catch (Exception JavaDoc tex) {}
762       }
763       catch (Throwable JavaDoc err)
764       {
765          logger.info("Search failed for: " + query);
766          Utils.addErrorMessage(MessageFormat.format(Application.getMessage(
767                FacesContext.getCurrentInstance(), Repository.ERROR_SEARCH), new Object JavaDoc[] {err.getMessage()}), err );
768          this.containerNodes = Collections.<Node>emptyList();
769          this.contentNodes = Collections.<Node>emptyList();
770          try { if (tx != null) {tx.rollback();} } catch (Exception JavaDoc tex) {}
771       }
772       finally
773       {
774          if (results != null)
775          {
776             results.close();
777          }
778       }
779       
780       if (logger.isDebugEnabled())
781       {
782          long endTime = System.currentTimeMillis();
783          logger.debug("Time to query and build map nodes: " + (endTime - startTime) + "ms");
784       }
785    }
786    
787    
788    // ------------------------------------------------------------------------------
789
// Property Resolvers
790

791    public NodePropertyResolver resolverlocked = new NodePropertyResolver() {
792       public Object JavaDoc get(Node node) {
793          return Repository.isNodeLocked(node, lockService);
794       }
795    };
796    
797    public NodePropertyResolver resolverOwner = new NodePropertyResolver() {
798       public Object JavaDoc get(Node node) {
799          return Repository.isNodeOwner(node, lockService);
800       }
801    };
802    
803    public NodePropertyResolver resolverCancelCheckOut = new NodePropertyResolver() {
804       public Object JavaDoc get(Node node) {
805          return node.hasAspect(ContentModel.ASPECT_WORKING_COPY) && node.hasPermission(PermissionService.CANCEL_CHECK_OUT);
806       }
807    };
808    
809    public NodePropertyResolver resolverCheckIn = new NodePropertyResolver() {
810       public Object JavaDoc get(Node node) {
811          return node.hasAspect(ContentModel.ASPECT_WORKING_COPY) && node.hasPermission(PermissionService.CHECK_IN);
812       }
813    };
814    
815    public NodePropertyResolver resolverWorkingCopy = new NodePropertyResolver() {
816       public Object JavaDoc get(Node node) {
817          return node.hasAspect(ContentModel.ASPECT_WORKING_COPY);
818       }
819    };
820    
821    public NodePropertyResolver resolverDownload = new NodePropertyResolver() {
822       public Object JavaDoc get(Node node) {
823          return DownloadContentServlet.generateDownloadURL(node.getNodeRef(), node.getName());
824       }
825    };
826    
827    public NodePropertyResolver resolverUrl = new NodePropertyResolver() {
828       public Object JavaDoc get(Node node) {
829          return DownloadContentServlet.generateBrowserURL(node.getNodeRef(), node.getName());
830       }
831    };
832    
833    public NodePropertyResolver resolverWebdavUrl = new NodePropertyResolver() {
834       public Object JavaDoc get(Node node)
835       {
836          return Utils.generateURL(FacesContext.getCurrentInstance(), node, URLMode.WEBDAV);
837       }
838    };
839    
840    public NodePropertyResolver resolverCifsPath = new NodePropertyResolver() {
841       public Object JavaDoc get(Node node)
842       {
843          return Utils.generateURL(FacesContext.getCurrentInstance(), node, URLMode.CIFS);
844       }
845    };
846    
847    public NodePropertyResolver resolverFileType16 = new NodePropertyResolver() {
848       public Object JavaDoc get(Node node) {
849          return Utils.getFileTypeImage(node.getName(), true);
850       }
851    };
852    
853    public NodePropertyResolver resolverFileType32 = new NodePropertyResolver() {
854       public Object JavaDoc get(Node node) {
855          return Utils.getFileTypeImage(node.getName(), false);
856       }
857    };
858    
859    public NodePropertyResolver resolverPath = new NodePropertyResolver() {
860       public Object JavaDoc get(Node node) {
861          return nodeService.getPath(node.getNodeRef());
862       }
863    };
864    
865    public NodePropertyResolver resolverDisplayPath = new NodePropertyResolver() {
866       public Object JavaDoc get(Node node) {
867          // TODO: replace this with a method that shows the full display name - not QNames
868
return Repository.getDisplayPath( (Path)node.getProperties().get("path") );
869       }
870    };
871    
872    public NodePropertyResolver resolverSpaceIcon = new NodePropertyResolver() {
873       public Object JavaDoc get(Node node) {
874          QNameNodeMap props = (QNameNodeMap)node.getProperties();
875          String JavaDoc icon = (String JavaDoc)props.getRaw("app:icon");
876          return (icon != null ? icon : NewSpaceWizard.SPACE_ICON_DEFAULT);
877       }
878    };
879    
880    public NodePropertyResolver resolverSmallIcon = new NodePropertyResolver() {
881       public Object JavaDoc get(Node node) {
882          QNameNodeMap props = (QNameNodeMap)node.getProperties();
883          String JavaDoc icon = (String JavaDoc)props.getRaw("app:icon");
884          return (icon != null ? icon + "-16" : SPACE_SMALL_DEFAULT);
885       }
886    };
887    
888    public NodePropertyResolver resolverMimetype = new NodePropertyResolver() {
889       public Object JavaDoc get(Node node) {
890          ContentData content = (ContentData)node.getProperties().get(ContentModel.PROP_CONTENT);
891          return (content != null ? content.getMimetype() : null);
892       }
893    };
894    
895    public NodePropertyResolver resolverSize = new NodePropertyResolver() {
896       public Object JavaDoc get(Node node) {
897          ContentData content = (ContentData)node.getProperties().get(ContentModel.PROP_CONTENT);
898          return (content != null ? new Long JavaDoc(content.getSize()) : null);
899       }
900    };
901    
902    public NodePropertyResolver resolverEditLinkType = new NodePropertyResolver() {
903       public Object JavaDoc get(Node node)
904       {
905          String JavaDoc editLinkType = "http";
906          
907          // if the node is inline editable, the default http behaviour should
908
// always be used otherwise the configured approach is used
909
if (node.hasAspect(ContentModel.ASPECT_INLINEEDITABLE) == false)
910          {
911             editLinkType = Application.getClientConfig(
912                   FacesContext.getCurrentInstance()).getEditLinkType();
913             if (editLinkType == null)
914             {
915                editLinkType = "http";
916             }
917          }
918          
919          return editLinkType;
920       }
921    };
922    
923    
924    // ------------------------------------------------------------------------------
925
// Navigation action event handlers
926

927    /**
928     * Action called from the Simple Search component.
929     * Sets up the SearchContext object with the values from the simple search menu.
930     */

931    public void search(ActionEvent event)
932    {
933       // setup the search text string on the top-level navigation handler
934
UISimpleSearch search = (UISimpleSearch)event.getComponent();
935       this.navigator.setSearchContext(search.getSearchContext());
936       
937       navigateBrowseScreen();
938    }
939    
940    /**
941     * Action called to Close the search dialog by returning to the last view node Id
942     */

943    public void closeSearch(ActionEvent event)
944    {
945       // set the current node Id ready for page refresh
946
this.navigator.setCurrentNodeId( this.navigator.getCurrentNodeId() );
947    }
948    
949    /**
950     * Action called when a folder space is clicked.
951     * Navigate into the space.
952     */

953    public void clickSpace(ActionEvent event)
954    {
955       UIActionLink link = (UIActionLink)event.getComponent();
956       Map JavaDoc<String JavaDoc, String JavaDoc> params = link.getParameterMap();
957       String JavaDoc id = params.get("id");
958       if (id != null && id.length() != 0)
959       {
960          try
961          {
962             NodeRef ref = new NodeRef(Repository.getStoreRef(), id);
963             clickSpace(ref);
964          }
965          catch (InvalidNodeRefException refErr)
966          {
967             Utils.addErrorMessage(MessageFormat.format(Application.getMessage(
968                FacesContext.getCurrentInstance(), Repository.ERROR_NODEREF), new Object JavaDoc[] {id}) );
969          }
970       }
971    }
972    
973    /**
974     * Action called when a folder space is clicked.
975     *
976     * @param nodeRef The node being clicked
977     */

978    public void clickSpace(NodeRef nodeRef)
979    {
980       // refresh UI based on node selection
981
updateUILocation(nodeRef);
982    }
983    
984    /**
985     * Handler called when a path element is clicked - navigate to the appropriate Space
986     */

987    public void clickSpacePath(ActionEvent event)
988    {
989       UINodePath.PathElementEvent pathEvent = (UINodePath.PathElementEvent)event;
990       NodeRef ref = pathEvent.NodeReference;
991       try
992       {
993          // refresh UI based on node selection
994
this.updateUILocation(ref);
995       }
996       catch (InvalidNodeRefException refErr)
997       {
998          Utils.addErrorMessage(MessageFormat.format(Application.getMessage(
999                FacesContext.getCurrentInstance(), Repository.ERROR_NODEREF), new Object JavaDoc[] {ref.getId()}) );
1000      }
1001   }
1002   
1003   /**
1004    * Action called when a folders direct descendant (in the 'list' browse mode) is clicked.
1005    * Navigate into the the descendant space.
1006    */

1007   public void clickDescendantSpace(ActionEvent event)
1008   {
1009      UINodeDescendants.NodeSelectedEvent nodeEvent = (UINodeDescendants.NodeSelectedEvent)event;
1010      NodeRef nodeRef = nodeEvent.NodeReference;
1011      if (nodeRef == null)
1012      {
1013         throw new IllegalStateException JavaDoc("NodeRef returned from UINodeDescendants.NodeSelectedEvent cannot be null!");
1014      }
1015      
1016      if (logger.isDebugEnabled())
1017         logger.debug("Selected noderef Id: " + nodeRef.getId());
1018      
1019      try
1020      {
1021         // user can either select a descendant of a node display on the page which means we
1022
// must add the it's parent and itself to the breadcrumb
1023
List JavaDoc<IBreadcrumbHandler> location = this.navigator.getLocation();
1024         ChildAssociationRef parentAssocRef = nodeService.getPrimaryParent(nodeRef);
1025         
1026         if (logger.isDebugEnabled())
1027         {
1028            logger.debug("Selected item getPrimaryParent().getChildRef() noderef Id: " + parentAssocRef.getChildRef().getId());
1029            logger.debug("Selected item getPrimaryParent().getParentRef() noderef Id: " + parentAssocRef.getParentRef().getId());
1030            logger.debug("Current value getNavigator().getCurrentNodeId() noderef Id: " + this.navigator.getCurrentNodeId());
1031         }
1032         
1033         if (nodeEvent.IsParent == false)
1034         {
1035            // a descendant of the displayed node was selected
1036
// first refresh based on the parent and add to the breadcrumb
1037
updateUILocation(parentAssocRef.getParentRef());
1038            
1039            // now add our selected node
1040
updateUILocation(nodeRef);
1041         }
1042         else
1043         {
1044            // else the parent ellipses i.e. the displayed node was selected
1045
updateUILocation(nodeRef);
1046         }
1047      }
1048      catch (InvalidNodeRefException refErr)
1049      {
1050         Utils.addErrorMessage(MessageFormat.format(Application.getMessage(
1051               FacesContext.getCurrentInstance(), Repository.ERROR_NODEREF), new Object JavaDoc[] {nodeRef.getId()}) );
1052      }
1053   }
1054   
1055   /**
1056    * Action event called by all Browse actions that need to setup a Space context
1057    * before an action page/wizard is called. The context will be a Node in setActionSpace() which
1058    * can be retrieved on the action page from BrowseBean.getActionSpace().
1059    *
1060    * @param event ActionEvent
1061    */

1062   public void setupSpaceAction(ActionEvent event)
1063   {
1064      UIActionLink link = (UIActionLink)event.getComponent();
1065      Map JavaDoc<String JavaDoc, String JavaDoc> params = link.getParameterMap();
1066      String JavaDoc id = params.get("id");
1067      setupSpaceAction(id, true);
1068   }
1069   
1070   /**
1071    * Public helper to setup action pages with Space context
1072    *
1073    * @param id of the Space node to setup context for
1074    */

1075   public void setupSpaceAction(String JavaDoc id, boolean invalidate)
1076   {
1077      if (id != null && id.length() != 0)
1078      {
1079         if (logger.isDebugEnabled())
1080            logger.debug("Setup for action, setting current space to: " + id);
1081         
1082         try
1083         {
1084            // create the node ref, then our node representation
1085
NodeRef ref = new NodeRef(Repository.getStoreRef(), id);
1086            Node node = new Node(ref);
1087            
1088            // resolve icon in-case one has not been set
1089
node.addPropertyResolver("icon", this.resolverSpaceIcon);
1090            
1091            // prepare a node for the action context
1092
setActionSpace(node);
1093            
1094            // setup the dispatch context in case it is required
1095
this.navigator.setupDispatchContext(node);
1096         }
1097         catch (InvalidNodeRefException refErr)
1098         {
1099            Utils.addErrorMessage(MessageFormat.format(Application.getMessage(
1100               FacesContext.getCurrentInstance(), Repository.ERROR_NODEREF), new Object JavaDoc[] {id}) );
1101         }
1102      }
1103      else
1104      {
1105         setActionSpace(null);
1106      }
1107      
1108      // clear the UI state in preparation for finishing the next action
1109
if (invalidate == true)
1110      {
1111         // use the context service to notify all registered beans
1112
UIContextService.getInstance(FacesContext.getCurrentInstance()).notifyBeans();
1113      }
1114   }
1115   
1116   /**
1117    * Acrtion event called by Delete Space actions. We setup the action space as normal, then prepare
1118    * any special case message string to be shown to the user if they are trying to delete specific spaces.
1119    */

1120   public void setupDeleteAction(ActionEvent event)
1121   {
1122      String JavaDoc message = null;
1123      
1124      setupSpaceAction(event);
1125      
1126      Node node = getActionSpace();
1127      if (node != null)
1128      {
1129         NodeRef companyRootRef = new NodeRef(Repository.getStoreRef(), Application.getCompanyRootId());
1130         if (node.getNodeRef().equals(companyRootRef))
1131         {
1132            message = Application.getMessage(FacesContext.getCurrentInstance(), MSG_DELETE_COMPANYROOT);
1133         }
1134      }
1135      
1136      setDeleteMessage(message);
1137   }
1138   
1139   /**
1140    * Action event called by all actions that need to setup a Content Document context on the
1141    * BrowseBean before an action page/wizard is called. The context will be a Node in
1142    * setDocument() which can be retrieved on the action page from BrowseBean.getDocument().
1143    */

1144   public void setupContentAction(ActionEvent event)
1145   {
1146      UIActionLink link = (UIActionLink)event.getComponent();
1147      Map JavaDoc<String JavaDoc, String JavaDoc> params = link.getParameterMap();
1148      setupContentAction(params.get("id"), true);
1149   }
1150   
1151   /**
1152    * Public helper to setup action pages with content context
1153    *
1154    * @param id of the content node to setup context for
1155    */

1156   public void setupContentAction(String JavaDoc id, boolean invalidate)
1157   {
1158      if (id != null && id.length() != 0)
1159      {
1160         if (logger.isDebugEnabled())
1161            logger.debug("Setup for action, setting current document to: " + id);
1162         
1163         try
1164         {
1165            // create the node ref, then our node representation
1166
NodeRef ref = new NodeRef(Repository.getStoreRef(), id);
1167            Node node = new Node(ref);
1168            
1169            // store the URL to for downloading the content
1170
node.addPropertyResolver("url", this.resolverDownload);
1171            node.addPropertyResolver("fileType32", this.resolverFileType32);
1172            node.addPropertyResolver("mimetype", this.resolverMimetype);
1173            node.addPropertyResolver("size", this.resolverSize);
1174            node.addPropertyResolver("cancelCheckOut", this.resolverCancelCheckOut);
1175            node.addPropertyResolver("checkIn", this.resolverCheckIn);
1176            
1177            for (NodeEventListener listener : getNodeEventListeners())
1178            {
1179               listener.created(node, node.getType());
1180            }
1181            
1182            // get hold of the DocumentDetailsBean and reset it
1183
DocumentDetailsBean docDetails = (DocumentDetailsBean)FacesContext.getCurrentInstance().
1184               getExternalContext().getSessionMap().get("DocumentDetailsBean");
1185            if (docDetails != null)
1186            {
1187               docDetails.reset();
1188            }
1189            
1190            // remember the document
1191
setDocument(node);
1192            
1193            // setup the dispatch context in case it is required
1194
this.navigator.setupDispatchContext(node);
1195         }
1196         catch (InvalidNodeRefException refErr)
1197         {
1198            Utils.addErrorMessage(MessageFormat.format(Application.getMessage(
1199               FacesContext.getCurrentInstance(), Repository.ERROR_NODEREF), new Object JavaDoc[] {id}) );
1200         }
1201      }
1202      else
1203      {
1204         setDocument(null);
1205      }
1206      
1207      // clear the UI state in preparation for finishing the next action
1208
if (invalidate == true)
1209      {
1210         // use the context service to notify all registered beans
1211
UIContextService.getInstance(FacesContext.getCurrentInstance()).notifyBeans();
1212      }
1213   }
1214   
1215   /**
1216    * Handler called upon the completion of the Delete Space page
1217    *
1218    * @return outcome
1219    */

1220   public String JavaDoc deleteSpaceOK()
1221   {
1222      String JavaDoc outcome = null;
1223      
1224      Node node = getActionSpace();
1225      if (node != null)
1226      {
1227         try
1228         {
1229            if (logger.isDebugEnabled())
1230               logger.debug("Trying to delete space: " + node.getId());
1231            
1232            this.nodeService.deleteNode(node.getNodeRef());
1233            
1234            // remove this node from the breadcrumb if required
1235
List JavaDoc<IBreadcrumbHandler> location = navigator.getLocation();
1236            IBreadcrumbHandler handler = location.get(location.size() - 1);
1237            if (handler instanceof BrowseBreadcrumbHandler)
1238            {
1239               // see if the current breadcrumb location is our node
1240
if ( ((BrowseBreadcrumbHandler)handler).getNodeRef().equals(node.getNodeRef()) == true )
1241               {
1242                  location.remove(location.size() - 1);
1243                  
1244                  // now work out which node to set the list to refresh against
1245
if (location.size() != 0)
1246                  {
1247                     handler = location.get(location.size() - 1);
1248                     if (handler instanceof BrowseBreadcrumbHandler)
1249                     {
1250                        // change the current node Id
1251
navigator.setCurrentNodeId(((BrowseBreadcrumbHandler)handler).getNodeRef().getId());
1252                     }
1253                     else
1254                     {
1255                        // TODO: shouldn't do this - but for now the user home dir is the root!
1256
navigator.setCurrentNodeId(Application.getCurrentUser(FacesContext.getCurrentInstance()).getHomeSpaceId());
1257                     }
1258                  }
1259               }
1260            }
1261            
1262            // add a message to inform the user that the delete was OK
1263
String JavaDoc statusMsg = MessageFormat.format(
1264                  Application.getMessage(FacesContext.getCurrentInstance(), "status_space_deleted"),
1265                  new Object JavaDoc[]{node.getName()});
1266            Utils.addStatusMessage(FacesMessage.SEVERITY_INFO, statusMsg);
1267            
1268            // clear action context
1269
setActionSpace(null);
1270            
1271            // setting the outcome will show the browse view again
1272
outcome = AlfrescoNavigationHandler.CLOSE_DIALOG_OUTCOME +
1273                      AlfrescoNavigationHandler.DIALOG_SEPARATOR + "browse";
1274         }
1275         catch (Throwable JavaDoc err)
1276         {
1277            Utils.addErrorMessage(Application.getMessage(
1278                  FacesContext.getCurrentInstance(), MSG_ERROR_DELETE_SPACE) + err.getMessage(), err);
1279         }
1280      }
1281      else
1282      {
1283         logger.warn("WARNING: deleteSpaceOK called without a current Space!");
1284      }
1285      
1286      return outcome;
1287   }
1288   
1289   /**
1290    * Handler called upon the completion of the Delete File page
1291    *
1292    * @return outcome
1293    */

1294   public String JavaDoc deleteFileOK()
1295   {
1296      String JavaDoc outcome = null;
1297      
1298      Node node = getDocument();
1299      if (node != null)
1300      {
1301         try
1302         {
1303            if (logger.isDebugEnabled())
1304               logger.debug("Trying to delete content node: " + node.getId());
1305            
1306            this.nodeService.deleteNode(node.getNodeRef());
1307            
1308            // clear action context
1309
setDocument(null);
1310            
1311            // setting the outcome will show the browse view again
1312
outcome = AlfrescoNavigationHandler.CLOSE_DIALOG_OUTCOME +
1313                      AlfrescoNavigationHandler.DIALOG_SEPARATOR + "browse";
1314         }
1315         catch (Throwable JavaDoc err)
1316         {
1317            Utils.addErrorMessage(Application.getMessage(
1318                  FacesContext.getCurrentInstance(), MSG_ERROR_DELETE_FILE) + err.getMessage(), err);
1319         }
1320      }
1321      else
1322      {
1323         logger.warn("WARNING: deleteFileOK called without a current Document!");
1324      }
1325      
1326      return outcome;
1327   }
1328   
1329   /**
1330    * Support for refresh of lists via special case for an External Access URL.
1331    * these URLs restart the JSF lifecycle but an old UIRichList is restored from
1332    * the component tree - which needs clearing "late" in the lifecycle process.
1333    */

1334   public void externalAccessRefresh()
1335   {
1336      this.externalForceRefresh = true;
1337   }
1338   
1339   
1340   // ------------------------------------------------------------------------------
1341
// Private helpers
1342

1343   /**
1344    * Initialise default values from client configuration
1345    */

1346   private void initFromClientConfig()
1347   {
1348      ConfigService config = Application.getConfigService(FacesContext.getCurrentInstance());
1349      
1350      this.viewsConfig = (ViewsConfigElement)config.getConfig("Views").
1351            getConfigElement(ViewsConfigElement.CONFIG_ELEMENT_ID);
1352      
1353      this.browseViewMode = this.viewsConfig.getDefaultView(PAGE_NAME_BROWSE);
1354      this.browsePageSize = this.viewsConfig.getDefaultPageSize(PAGE_NAME_BROWSE,
1355            this.browseViewMode);
1356   }
1357   
1358   /**
1359    * @return the Set of NodeEventListeners registered against this bean
1360    */

1361   private Set JavaDoc<NodeEventListener> getNodeEventListeners()
1362   {
1363      if (this.nodeEventListeners == null)
1364      {
1365         this.nodeEventListeners = new HashSet JavaDoc<NodeEventListener>();
1366         
1367         FacesContext fc = FacesContext.getCurrentInstance();
1368         
1369         Config listenerConfig = Application.getConfigService(fc).getConfig("Node Event Listeners");
1370         if (listenerConfig != null)
1371         {
1372            ConfigElement listenerElement = listenerConfig.getConfigElement("node-event-listeners");
1373            if (listenerElement != null)
1374            {
1375               for (ConfigElement child : listenerElement.getChildren())
1376               {
1377                  if (child.getName().equals("listener"))
1378                  {
1379                     // retrieved the JSF Managed Bean identified in the config
1380
String JavaDoc listenerName = child.getValue().trim();
1381                     Object JavaDoc bean = FacesHelper.getManagedBean(fc, listenerName);
1382                     if (bean instanceof NodeEventListener)
1383                     {
1384                        addNodeEventListener((NodeEventListener)bean);
1385                     }
1386                  }
1387               }
1388            }
1389         }
1390      }
1391      return this.nodeEventListeners;
1392   }
1393   
1394   /**
1395    * Refresh the UI after a Space selection change. Adds the selected space to the breadcrumb
1396    * location path and also updates the list components in the UI.
1397    *
1398    * @param ref NodeRef of the selected space
1399    */

1400   public void updateUILocation(NodeRef ref)
1401   {
1402      // get the current breadcrumb location and append a new handler to it
1403
// our handler know the ID of the selected node and the display label for it
1404
List JavaDoc<IBreadcrumbHandler> location = this.navigator.getLocation();
1405      if (location.size() != 0)
1406      {
1407         // attempt to find the ID - if it's already in the breadcrumb then we
1408
// navigate directly to that node - rather than add duplication to the breadcrumb path
1409
boolean foundNode = false;
1410         for (int i=0; i<location.size(); i++)
1411         {
1412            IBreadcrumbHandler element = location.get(i);
1413            if (element instanceof IRepoBreadcrumbHandler)
1414            {
1415               NodeRef nodeRef = ((IRepoBreadcrumbHandler)element).getNodeRef();
1416               if (ref.equals(nodeRef) == true)
1417               {
1418                  // TODO: we should be able to do this - but the UIBreadcrumb component modifies
1419
// it's own internal value when clicked - then uses that from then on!
1420
// the other ops are using the same List object and modding it directly.
1421
//List<IBreadcrumbHandler> newLocation = new ArrayList<IBreadcrumbHandler>(i+1);
1422
//newLocation.addAll(location.subList(0, i + 1));
1423
//this.navigator.setLocation(newLocation);
1424
// TODO: but instead for now we do this:
1425
int count = location.size();
1426                  for (int n=i+1; n<count; n++)
1427                  {
1428                     location.remove(i+1);
1429                  }
1430                  
1431                  foundNode = true;
1432                  break;
1433               }
1434            }
1435         }
1436         
1437         // add new node to the end of the existing breadcrumb
1438
if (foundNode == false)
1439         {
1440            String JavaDoc name = Repository.getNameForNode(this.nodeService, ref);
1441            location.add(new BrowseBreadcrumbHandler(ref, name));
1442         }
1443      }
1444      else
1445      {
1446         // special case to add first item to the location
1447
String JavaDoc name = Repository.getNameForNode(this.nodeService, ref);
1448         location.add(new BrowseBreadcrumbHandler(ref, name));
1449      }
1450      
1451      // set the current node Id ready for page refresh
1452
this.navigator.setCurrentNodeId(ref.getId());
1453      
1454      // set up the dispatch context for the navigation handler
1455
this.navigator.setupDispatchContext(new Node(ref));
1456      
1457      navigateBrowseScreen();
1458   }
1459   
1460   /**
1461    * Invalidate list component state after an action which changes the UI context
1462    */

1463   private void invalidateComponents()
1464   {
1465      if (logger.isDebugEnabled())
1466         logger.debug("Invalidating browse components...");
1467      
1468      // clear the value for the list components - will cause re-bind to it's data and refresh
1469
if (this.contentRichList != null)
1470      {
1471         this.contentRichList.setValue(null);
1472         if (this.navigator.getSearchContext() != null)
1473         {
1474            // clear the sorting mode so the search results are displayed in default 'score' order
1475
this.contentRichList.clearSort();
1476         }
1477      }
1478      if (this.spacesRichList != null)
1479      {
1480         this.spacesRichList.setValue(null);
1481         if (this.navigator.getSearchContext() != null)
1482         {
1483            // clear the sorting mode so the search results are displayed in default 'score' order
1484
this.spacesRichList.clearSort();
1485         }
1486      }
1487      
1488      // clear the storage of the last set of nodes
1489
this.containerNodes = null;
1490      this.contentNodes = null;
1491   }
1492   
1493   /**
1494    * @return whether the current View ID is the "browse" screen
1495    */

1496   private boolean isViewCurrent()
1497   {
1498      return (FacesContext.getCurrentInstance().getViewRoot().getViewId().equals(BROWSE_VIEW_ID));
1499   }
1500   
1501   /**
1502    * Perform navigation to the browse screen if it is not already the current View
1503    */

1504   private void navigateBrowseScreen()
1505   {
1506      String JavaDoc outcome = null;
1507      
1508      if (isViewCurrent() == false)
1509      {
1510         outcome = "browse";
1511      }
1512      
1513      FacesContext fc = FacesContext.getCurrentInstance();
1514      fc.getApplication().getNavigationHandler().handleNavigation(fc, null, outcome);
1515   }
1516   
1517   
1518   // ------------------------------------------------------------------------------
1519
// Inner classes
1520

1521   /**
1522    * Class to handle breadcrumb interaction for Browse pages
1523    */

1524   private class BrowseBreadcrumbHandler implements IRepoBreadcrumbHandler
1525   {
1526      private static final long serialVersionUID = 3833183653173016630L;
1527      
1528      /**
1529       * Constructor
1530       *
1531       * @param NodeRef The NodeRef for this browse navigation element
1532       * @param label Element label
1533       */

1534      public BrowseBreadcrumbHandler(NodeRef nodeRef, String JavaDoc label)
1535      {
1536         this.label = label;
1537         this.nodeRef = nodeRef;
1538      }
1539      
1540      /**
1541       * @see java.lang.Object#toString()
1542       */

1543      public String JavaDoc toString()
1544      {
1545         return this.label;
1546      }
1547
1548      /**
1549       * @see org.alfresco.web.ui.common.component.IBreadcrumbHandler#navigationOutcome(org.alfresco.web.ui.common.component.UIBreadcrumb)
1550       */

1551      @SuppressWarnings JavaDoc("unchecked")
1552      public String JavaDoc navigationOutcome(UIBreadcrumb breadcrumb)
1553      {
1554         // All browse breadcrumb element relate to a Node Id - when selected we
1555
// set the current node id
1556
navigator.setCurrentNodeId(this.nodeRef.getId());
1557         navigator.setLocation( (List JavaDoc)breadcrumb.getValue() );
1558         
1559         // setup the dispatch context
1560
navigator.setupDispatchContext(new Node(this.nodeRef));
1561         
1562         // return to browse page if required
1563
return (isViewCurrent() ? null : "browse");
1564      }
1565      
1566      public NodeRef getNodeRef()
1567      {
1568         return this.nodeRef;
1569      }
1570      
1571      private NodeRef nodeRef;
1572      private String JavaDoc label;
1573   }
1574
1575   
1576   // ------------------------------------------------------------------------------
1577
// Private data
1578

1579   /** Browse screen view ID */
1580   public static final String JavaDoc BROWSE_VIEW_ID = "/jsp/browse/browse.jsp";
1581   
1582   /** Small icon default name */
1583   public static final String JavaDoc SPACE_SMALL_DEFAULT = "space_small";
1584   
1585   private static final String JavaDoc VIEWMODE_DASHBOARD = "dashboard";
1586   private static final String JavaDoc PAGE_NAME_BROWSE = "browse";
1587   
1588   /** I18N messages */
1589   private static final String JavaDoc MSG_ERROR_DELETE_FILE = "error_delete_file";
1590   private static final String JavaDoc MSG_ERROR_DELETE_SPACE = "error_delete_space";
1591   private static final String JavaDoc MSG_DELETE_COMPANYROOT = "delete_companyroot_confirm";
1592   private static final String JavaDoc MSG_SEARCH_MINIMUM = "search_minimum";
1593   
1594   private static Logger logger = Logger.getLogger(BrowseBean.class);
1595   
1596   /** The NodeService to be used by the bean */
1597   protected NodeService nodeService;
1598   
1599   /** The SearchService to be used by the bean */
1600   protected SearchService searchService;
1601   
1602   /** The LockService to be used by the bean */
1603   protected LockService lockService;
1604   
1605   /** The NavigationBean bean reference */
1606   protected NavigationBean navigator;
1607   
1608   /** The DictionaryService bean reference */
1609   protected DictionaryService dictionaryService;
1610   
1611   /** The file folder service */
1612   protected FileFolderService fileFolderService;
1613
1614   /** Views configuration object */
1615   protected ViewsConfigElement viewsConfig = null;
1616   
1617   /** Listeners for Node events */
1618   private Set JavaDoc<NodeEventListener> nodeEventListeners = null;
1619   
1620   /** Component references */
1621   protected UIRichList spacesRichList;
1622   protected UIRichList contentRichList;
1623   private UIStatusMessage statusMessage;
1624   
1625   /** Transient lists of container and content nodes for display */
1626   private List JavaDoc<Node> containerNodes = null;
1627   private List JavaDoc<Node> contentNodes = null;
1628   
1629   /** The current space and it's properties - if any */
1630   private Node actionSpace;
1631   
1632   /** The current document */
1633   private Node document;
1634   
1635   /** Special message to display when user deleting certain folders e.g. Company Home */
1636   private String JavaDoc deleteMessage;
1637   
1638   /** The current browse view mode - set to a well known IRichListRenderer identifier */
1639   private String JavaDoc browseViewMode;
1640   
1641   /** The current browse view page size */
1642   private int browsePageSize;
1643   
1644   /** True if current space has a dashboard (template) view available */
1645   private boolean dashboardView;
1646   
1647   private boolean externalForceRefresh = false;
1648}
1649
Popular Tags