Files
alfresco-community-repo/source/java/org/alfresco/web/bean/BrowseBean.java
Kevin Roast 156ff56ed9 MySpaces webscript now shows Ajax wait animation while loading inner panel area.
Summary pop-up panel fade-out anim removed (improves user experience).
Reorg of broken action evaluator class hiearchy (after ML UI changes).
More fixes to ML UI (browse.jsp missing component IDs breaks screen when panels collapsed then expanded).
Code reorg in interceptors to help performance.

git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@5772 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
2007-05-24 11:08:53 +00:00

1826 lines
65 KiB
Java

/*
* Copyright (C) 2005-2007 Alfresco Software Limited.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* As a special exception to the terms and conditions of version 2.0 of
* the GPL, you may redistribute this Program in connection with Free/Libre
* and Open Source Software ("FLOSS") applications as described in Alfresco's
* FLOSS exception. You should have recieved a copy of the text describing
* the FLOSS exception, and it is also available here:
* http://www.alfresco.com/legal/licensing"
*/
package org.alfresco.web.bean;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import javax.faces.context.FacesContext;
import javax.faces.event.ActionEvent;
import javax.transaction.UserTransaction;
import org.alfresco.config.Config;
import org.alfresco.config.ConfigElement;
import org.alfresco.config.ConfigService;
import org.alfresco.model.ApplicationModel;
import org.alfresco.model.ContentModel;
import org.alfresco.service.cmr.dictionary.DictionaryService;
import org.alfresco.service.cmr.dictionary.TypeDefinition;
import org.alfresco.service.cmr.lock.LockService;
import org.alfresco.service.cmr.model.FileFolderService;
import org.alfresco.service.cmr.repository.ChildAssociationRef;
import org.alfresco.service.cmr.repository.ContentData;
import org.alfresco.service.cmr.repository.InvalidNodeRefException;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.NodeService;
import org.alfresco.service.cmr.search.LimitBy;
import org.alfresco.service.cmr.search.ResultSet;
import org.alfresco.service.cmr.search.ResultSetRow;
import org.alfresco.service.cmr.search.SearchParameters;
import org.alfresco.service.cmr.search.SearchService;
import org.alfresco.service.namespace.QName;
import org.alfresco.service.namespace.RegexQNamePattern;
import org.alfresco.web.app.Application;
import org.alfresco.web.app.context.IContextListener;
import org.alfresco.web.app.context.UIContextService;
import org.alfresco.web.app.servlet.DownloadContentServlet;
import org.alfresco.web.app.servlet.FacesHelper;
import org.alfresco.web.bean.repository.MapNode;
import org.alfresco.web.bean.repository.Node;
import org.alfresco.web.bean.repository.NodePropertyResolver;
import org.alfresco.web.bean.repository.QNameNodeMap;
import org.alfresco.web.bean.repository.Repository;
import org.alfresco.web.bean.spaces.CreateSpaceWizard;
import org.alfresco.web.config.ViewsConfigElement;
import org.alfresco.web.ui.common.Utils;
import org.alfresco.web.ui.common.Utils.URLMode;
import org.alfresco.web.ui.common.component.IBreadcrumbHandler;
import org.alfresco.web.ui.common.component.UIActionLink;
import org.alfresco.web.ui.common.component.UIBreadcrumb;
import org.alfresco.web.ui.common.component.UIModeList;
import org.alfresco.web.ui.common.component.UIStatusMessage;
import org.alfresco.web.ui.common.component.UIPanel.ExpandedEvent;
import org.alfresco.web.ui.common.component.data.UIRichList;
import org.alfresco.web.ui.repo.component.IRepoBreadcrumbHandler;
import org.alfresco.web.ui.repo.component.UINodeDescendants;
import org.alfresco.web.ui.repo.component.UINodePath;
import org.alfresco.web.ui.repo.component.UISimpleSearch;
import org.apache.log4j.Logger;
import org.apache.log4j.Priority;
/**
* Bean providing properties and behaviour for the main folder/document browse screen and
* search results screens.
*
* @author Kevin Roast
*/
public class BrowseBean implements IContextListener
{
/** Public JSF Bean name */
public static final String BEAN_NAME = "BrowseBean";
// ------------------------------------------------------------------------------
// Construction
/**
* Default Constructor
*/
public BrowseBean()
{
UIContextService.getInstance(FacesContext.getCurrentInstance()).registerBean(this);
initFromClientConfig();
}
// ------------------------------------------------------------------------------
// Bean property getters and setters
/**
* @param nodeService The NodeService to set.
*/
public void setNodeService(NodeService nodeService)
{
this.nodeService = nodeService;
}
/**
* @param searchService The Searcher to set.
*/
public void setSearchService(SearchService searchService)
{
this.searchService = searchService;
}
/**
* @param userPreferencesBean The UserPreferencesBean to set.
*/
public void setUserPreferencesBean(UserPreferencesBean userPreferencesBean)
{
this.userPreferencesBean = userPreferencesBean;
}
/**
* @param lockService The Lock Service to set.
*/
public void setLockService(LockService lockService)
{
this.lockService = lockService;
}
/**
* @param navigator The NavigationBean to set.
*/
public void setNavigator(NavigationBean navigator)
{
this.navigator = navigator;
}
/**
* @param dictionaryService The DictionaryService to set.
*/
public void setDictionaryService(DictionaryService dictionaryService)
{
this.dictionaryService = dictionaryService;
}
/**
* @param fileFolderService The FileFolderService to set.
*/
public void setFileFolderService(FileFolderService fileFolderService)
{
this.fileFolderService = fileFolderService;
}
/**
* @return Returns the browse View mode. See UIRichList
*/
public String getBrowseViewMode()
{
return this.browseViewMode;
}
/**
* @param browseViewMode The browse View mode to set. See UIRichList.
*/
public void setBrowseViewMode(String browseViewMode)
{
this.browseViewMode = browseViewMode;
}
/**
* @return Returns true if dashboard view is available for the current node.
*/
public boolean isDashboardView()
{
return this.dashboardView;
}
/**
* @param dashboardView The dashboard view mode to set.
*/
public void setDashboardView(boolean dashboardView)
{
this.dashboardView = dashboardView;
if (dashboardView == true)
{
FacesContext fc = FacesContext.getCurrentInstance();
fc.getApplication().getNavigationHandler().handleNavigation(fc, null, "dashboard");
}
}
public int getPageSizeContent()
{
return this.pageSizeContent;
}
public void setPageSizeContent(int pageSizeContent)
{
this.pageSizeContent = pageSizeContent;
this.pageSizeContentStr = Integer.toString(pageSizeContent);
}
public int getPageSizeSpaces()
{
return this.pageSizeSpaces;
}
public void setPageSizeSpaces(int pageSizeSpaces)
{
this.pageSizeSpaces = pageSizeSpaces;
this.pageSizeSpacesStr = Integer.toString(pageSizeSpaces);
}
public String getPageSizeContentStr()
{
return this.pageSizeContentStr;
}
public void setPageSizeContentStr(String pageSizeContentStr)
{
this.pageSizeContentStr = pageSizeContentStr;
}
public String getPageSizeSpacesStr()
{
return this.pageSizeSpacesStr;
}
public void setPageSizeSpacesStr(String pageSizeSpacesStr)
{
this.pageSizeSpacesStr = pageSizeSpacesStr;
}
/**
* @return Returns the minimum length of a valid search string.
*/
public int getMinimumSearchLength()
{
return Application.getClientConfig(FacesContext.getCurrentInstance()).
getSearchMinimum();
}
/**
* @return Returns the panels expanded state map.
*/
public Map<String, Boolean> getPanels()
{
return this.panels;
}
/**
* @param panels The panels expanded state map.
*/
public void setPanels(Map<String, Boolean> panels)
{
this.panels = panels;
}
/**
* @return Returns the Space Node being used for the current browse screen action.
*/
public Node getActionSpace()
{
return this.actionSpace;
}
/**
* @param actionSpace Set the Space Node to be used for the current browse screen action.
*/
public void setActionSpace(Node actionSpace)
{
if (actionSpace != null)
{
for (NodeEventListener listener : getNodeEventListeners())
{
listener.created(actionSpace, actionSpace.getType());
}
}
this.actionSpace = actionSpace;
}
/**
* @return The document node being used for the current operation
*/
public Node getDocument()
{
return this.document;
}
/**
* @param document The document node to be used for the current operation
*/
public void setDocument(Node document)
{
if (document != null)
{
for (NodeEventListener listener : getNodeEventListeners())
{
listener.created(document, document.getType());
}
}
this.document = document;
}
/**
* @param contentRichList The contentRichList to set.
*/
public void setContentRichList(UIRichList contentRichList)
{
this.contentRichList = contentRichList;
if (this.contentRichList != null)
{
this.contentRichList.setInitialSortColumn(
this.viewsConfig.getDefaultSortColumn(PAGE_NAME_BROWSE));
this.contentRichList.setInitialSortDescending(
this.viewsConfig.hasDescendingSort(PAGE_NAME_BROWSE));
}
// special case to handle an External Access URL
// these URLs restart the JSF lifecycle but an old UIRichList is restored from
// the component tree - which needs clearing "late" in the lifecycle process
if (externalForceRefresh)
{
this.contentRichList.setValue(null);
externalForceRefresh = false;
}
}
/**
* @return Returns the contentRichList.
*/
public UIRichList getContentRichList()
{
return this.contentRichList;
}
/**
* @param spacesRichList The spacesRichList to set.
*/
public void setSpacesRichList(UIRichList spacesRichList)
{
this.spacesRichList = spacesRichList;
if (this.spacesRichList != null)
{
// set the initial sort column and direction
this.spacesRichList.setInitialSortColumn(
this.viewsConfig.getDefaultSortColumn(PAGE_NAME_BROWSE));
this.spacesRichList.setInitialSortDescending(
this.viewsConfig.hasDescendingSort(PAGE_NAME_BROWSE));
}
if (externalForceRefresh)
{
this.spacesRichList.setValue(null);
}
}
/**
* @return Returns the spacesRichList.
*/
public UIRichList getSpacesRichList()
{
return this.spacesRichList;
}
/**
* @return Returns the statusMessage component.
*/
public UIStatusMessage getStatusMessage()
{
return this.statusMessage;
}
/**
* @param statusMessage The statusMessage component to set.
*/
public void setStatusMessage(UIStatusMessage statusMessage)
{
this.statusMessage = statusMessage;
}
/**
* @return Returns the deleteMessage.
*/
public String getDeleteMessage()
{
return this.deleteMessage;
}
/**
* @param deleteMessage The deleteMessage to set.
*/
public void setDeleteMessage(String deleteMessage)
{
this.deleteMessage = deleteMessage;
}
/**
* Page accessed bean method to get the container nodes currently being browsed
*
* @return List of container Node objects for the current browse location
*/
public List<Node> getNodes()
{
// the references to container nodes and content nodes are transient for one use only
// we do this so we only query/search once - as we cannot distinguish between node types
// until after the query. The logic is a bit confusing but otherwise we would need to
// perform the same query or search twice for every screen refresh.
if (this.containerNodes == null)
{
if (this.navigator.getSearchContext() == null)
{
queryBrowseNodes(this.navigator.getCurrentNodeId());
}
else
{
searchBrowseNodes(this.navigator.getSearchContext());
}
}
List<Node> result = this.containerNodes;
// we clear the member variable during invalidateComponents()
return result;
}
/**
* Page accessed bean method to get the content nodes currently being browsed
*
* @return List of content Node objects for the current browse location
*/
public List<Node> getContent()
{
// see comment in getNodes() above for reasoning here
if (this.contentNodes == null)
{
if (this.navigator.getSearchContext() == null)
{
queryBrowseNodes(this.navigator.getCurrentNodeId());
}
else
{
searchBrowseNodes(this.navigator.getSearchContext());
}
}
List<Node> result = this.contentNodes;
// we clear the member variable during invalidateComponents()
return result;
}
/**
* Setup the common properties required at data-binding time.
* <p>
* These are properties used by components on the page when iterating over the nodes.
* The properties are available as the Node is a Map so they can be accessed directly
* by name. Information such as download URL, size and filetype are provided etc.
* <p>
* We use a set of anonymous inner classes to provide the implemention for the property
* getters. The interfaces are only called when the properties are first requested.
*
* @param node Node to add the properties too
*/
public void setupCommonBindingProperties(Node node)
{
// special properties to be used by the value binding components on the page
node.addPropertyResolver("url", this.resolverUrl);
node.addPropertyResolver("webdavUrl", this.resolverWebdavUrl);
node.addPropertyResolver("cifsPath", this.resolverCifsPath);
node.addPropertyResolver("fileType16", this.resolverFileType16);
node.addPropertyResolver("fileType32", this.resolverFileType32);
node.addPropertyResolver("size", this.resolverSize);
node.addPropertyResolver("lang", this.resolverLang);
}
// ------------------------------------------------------------------------------
// IContextListener implementation
/**
* @see org.alfresco.web.app.context.IContextListener#contextUpdated()
*/
public void contextUpdated()
{
invalidateComponents();
}
/**
* @see org.alfresco.web.app.context.IContextListener#areaChanged()
*/
public void areaChanged()
{
// nothing to do
}
/**
* @see org.alfresco.web.app.context.IContextListener#spaceChanged()
*/
public void spaceChanged()
{
// nothing to do
}
// ------------------------------------------------------------------------------
// NodeEventListener listeners
/**
* Add a listener to those called by the BrowseBean when nodes are created
*/
public void addNodeEventListener(NodeEventListener listener)
{
getNodeEventListeners().add(listener);
}
/**
* Remove a listener from the list of those called by BrowseBean
*/
public void removeNodeEventListener(NodeEventListener listener)
{
getNodeEventListeners().remove(listener);
}
// ------------------------------------------------------------------------------
// Navigation action event handlers
/**
* Change the current view mode based on user selection
*
* @param event ActionEvent
*/
public void viewModeChanged(ActionEvent event)
{
UIModeList viewList = (UIModeList)event.getComponent();
// get the view mode ID
String viewMode = viewList.getValue().toString();
if (VIEWMODE_DASHBOARD.equals(viewMode) == false)
{
// set the page size based on the style of display
int pageSize = this.viewsConfig.getDefaultPageSize(PAGE_NAME_BROWSE, viewMode);
setPageSizeContent(pageSize);
setPageSizeSpaces(pageSize);
if (logger.isDebugEnabled())
logger.debug("Browse view page size set to: " + pageSize);
setDashboardView(false);
// push the view mode into the lists
setBrowseViewMode(viewMode);
// setup dispatch context for custom views
this.navigator.setupDispatchContext(this.navigator.getCurrentNode());
navigateBrowseScreen();
}
else
{
// special case for Dashboard view
setDashboardView(true);
}
}
// ------------------------------------------------------------------------------
// Helper methods
/**
* Query a list of nodes for the specified parent node Id
*
* @param parentNodeId Id of the parent node or null for the root node
*/
private void queryBrowseNodes(String parentNodeId)
{
long startTime = 0;
if (logger.isDebugEnabled())
startTime = System.currentTimeMillis();
UserTransaction tx = null;
try
{
FacesContext context = FacesContext.getCurrentInstance();
tx = Repository.getUserTransaction(context, true);
tx.begin();
NodeRef parentRef;
if (parentNodeId == null)
{
// no specific parent node specified - use the root node
parentRef = this.nodeService.getRootNode(Repository.getStoreRef());
}
else
{
// build a NodeRef for the specified Id and our store
parentRef = new NodeRef(Repository.getStoreRef(), parentNodeId);
}
List<ChildAssociationRef> childRefs = this.nodeService.getChildAssocs(parentRef,
ContentModel.ASSOC_CONTAINS, RegexQNamePattern.MATCH_ALL);
this.containerNodes = new ArrayList<Node>(childRefs.size());
this.contentNodes = new ArrayList<Node>(childRefs.size());
for (ChildAssociationRef ref: childRefs)
{
// create our Node representation from the NodeRef
NodeRef nodeRef = ref.getChildRef();
if (this.nodeService.exists(nodeRef))
{
// find it's type so we can see if it's a node we are interested in
QName type = this.nodeService.getType(nodeRef);
// make sure the type is defined in the data dictionary
TypeDefinition typeDef = this.dictionaryService.getType(type);
if (typeDef != null)
{
MapNode node = null;
// look for Space folder node
if (this.dictionaryService.isSubClass(type, ContentModel.TYPE_FOLDER) == true &&
this.dictionaryService.isSubClass(type, ContentModel.TYPE_SYSTEM_FOLDER) == false)
{
// create our Node representation
node = new MapNode(nodeRef, this.nodeService, true);
node.addPropertyResolver("icon", this.resolverSpaceIcon);
node.addPropertyResolver("smallIcon", this.resolverSmallIcon);
this.containerNodes.add(node);
}
// look for File content node
else if (this.dictionaryService.isSubClass(type, ContentModel.TYPE_CONTENT))
{
// create our Node representation
node = new MapNode(nodeRef, this.nodeService, true);
setupCommonBindingProperties(node);
this.contentNodes.add(node);
}
// look for File Link object node
else if (ApplicationModel.TYPE_FILELINK.equals(type))
{
// create our File Link Node representation
node = new MapNode(nodeRef, this.nodeService, true);
node.addPropertyResolver("url", this.resolverLinkUrl);
node.addPropertyResolver("webdavUrl", this.resolverLinkWebdavUrl);
node.addPropertyResolver("cifsPath", this.resolverLinkCifsPath);
node.addPropertyResolver("fileType16", this.resolverFileType16);
node.addPropertyResolver("fileType32", this.resolverFileType32);
node.addPropertyResolver("size", this.resolverSize);
node.addPropertyResolver("lang", this.resolverLang);
this.contentNodes.add(node);
}
else if (ApplicationModel.TYPE_FOLDERLINK.equals(type))
{
// create our Folder Link Node representation
node = new MapNode(nodeRef, this.nodeService, true);
node.addPropertyResolver("icon", this.resolverSpaceIcon);
node.addPropertyResolver("smallIcon", this.resolverSmallIcon);
this.containerNodes.add(node);
}
// inform any listeners that a Node wrapper has been created
if (node != null)
{
for (NodeEventListener listener : getNodeEventListeners())
{
listener.created(node, type);
}
}
}
else
{
if (logger.isEnabledFor(Priority.WARN))
logger.warn("Found invalid object in database: id = " + nodeRef + ", type = " + type);
}
}
}
// commit the transaction
tx.commit();
}
catch (InvalidNodeRefException refErr)
{
Utils.addErrorMessage(MessageFormat.format(Application.getMessage(
FacesContext.getCurrentInstance(), Repository.ERROR_NODEREF), new Object[] {refErr.getNodeRef()}), refErr );
this.containerNodes = Collections.<Node>emptyList();
this.contentNodes = Collections.<Node>emptyList();
try { if (tx != null) {tx.rollback();} } catch (Exception tex) {}
}
catch (Throwable err)
{
Utils.addErrorMessage(MessageFormat.format(Application.getMessage(
FacesContext.getCurrentInstance(), Repository.ERROR_GENERIC), err.getMessage()), err);
this.containerNodes = Collections.<Node>emptyList();
this.contentNodes = Collections.<Node>emptyList();
try { if (tx != null) {tx.rollback();} } catch (Exception tex) {}
}
if (logger.isDebugEnabled())
{
long endTime = System.currentTimeMillis();
logger.debug("Time to query and build map nodes: " + (endTime - startTime) + "ms");
}
}
/**
* Search for a list of nodes using the specific search context
*
* @param searchContext To use to perform the search
*/
private void searchBrowseNodes(SearchContext searchContext)
{
long startTime = 0;
if (logger.isDebugEnabled())
startTime = System.currentTimeMillis();
// get the searcher object to build the query
String query = searchContext.buildQuery(getMinimumSearchLength());
if (query == null)
{
// failed to build a valid query, the user probably did not enter the
// minimum text required to construct a valid search
Utils.addErrorMessage(MessageFormat.format(Application.getMessage(FacesContext.getCurrentInstance(), MSG_SEARCH_MINIMUM),
new Object[] {getMinimumSearchLength()}));
this.containerNodes = Collections.<Node>emptyList();
this.contentNodes = Collections.<Node>emptyList();
return;
}
// perform the search against the repo
UserTransaction tx = null;
ResultSet results = null;
try
{
tx = Repository.getUserTransaction(FacesContext.getCurrentInstance(), true);
tx.begin();
// Limit search to the first 100 matches
SearchParameters sp = new SearchParameters();
sp.setLanguage(SearchService.LANGUAGE_LUCENE);
sp.setQuery(query);
sp.addStore(Repository.getStoreRef());
int searchLimit = Application.getClientConfig(FacesContext.getCurrentInstance()).getSearchMaxResults();
if(searchLimit > 0)
{
sp.setLimitBy(LimitBy.FINAL_SIZE);
sp.setLimit(searchLimit);
}
results = this.searchService.query(sp);
if (logger.isDebugEnabled())
logger.debug("Search results returned: " + results.length());
// create a list of items from the results
this.containerNodes = new ArrayList<Node>(results.length());
this.contentNodes = new ArrayList<Node>(results.length());
if (results.length() != 0)
{
for (ResultSetRow row: results)
{
NodeRef nodeRef = row.getNodeRef();
if (this.nodeService.exists(nodeRef))
{
// find it's type so we can see if it's a node we are interested in
QName type = this.nodeService.getType(nodeRef);
// make sure the type is defined in the data dictionary
TypeDefinition typeDef = this.dictionaryService.getType(type);
if (typeDef != null)
{
MapNode node = null;
// look for Space or File nodes
if (this.dictionaryService.isSubClass(type, ContentModel.TYPE_FOLDER) &&
this.dictionaryService.isSubClass(type, ContentModel.TYPE_SYSTEM_FOLDER) == false)
{
// create our Node representation
node = new MapNode(nodeRef, this.nodeService, false);
node.addPropertyResolver("path", this.resolverPath);
node.addPropertyResolver("displayPath", this.resolverDisplayPath);
node.addPropertyResolver("icon", this.resolverSpaceIcon);
node.addPropertyResolver("smallIcon", this.resolverSmallIcon);
this.containerNodes.add(node);
}
else if (this.dictionaryService.isSubClass(type, ContentModel.TYPE_CONTENT))
{
// create our Node representation
node = new MapNode(nodeRef, this.nodeService, false);
setupCommonBindingProperties(node);
node.addPropertyResolver("path", this.resolverPath);
node.addPropertyResolver("displayPath", this.resolverDisplayPath);
this.contentNodes.add(node);
}
// look for File Link object node
else if (ApplicationModel.TYPE_FILELINK.equals(type))
{
// create our File Link Node representation
node = new MapNode(nodeRef, this.nodeService, false);
node.addPropertyResolver("url", this.resolverLinkUrl);
node.addPropertyResolver("webdavUrl", this.resolverLinkWebdavUrl);
node.addPropertyResolver("cifsPath", this.resolverLinkCifsPath);
node.addPropertyResolver("fileType16", this.resolverFileType16);
node.addPropertyResolver("fileType32", this.resolverFileType32);
node.addPropertyResolver("size", this.resolverSize);
node.addPropertyResolver("lang", this.resolverLang);
node.addPropertyResolver("path", this.resolverPath);
node.addPropertyResolver("displayPath", this.resolverDisplayPath);
this.contentNodes.add(node);
}
else if (ApplicationModel.TYPE_FOLDERLINK.equals(type))
{
// create our Folder Link Node representation
node = new MapNode(nodeRef, this.nodeService, false);
node.addPropertyResolver("icon", this.resolverSpaceIcon);
node.addPropertyResolver("smallIcon", this.resolverSmallIcon);
node.addPropertyResolver("path", this.resolverPath);
node.addPropertyResolver("displayPath", this.resolverDisplayPath);
this.containerNodes.add(node);
}
// inform any listeners that a Node wrapper has been created
if (node != null)
{
for (NodeEventListener listener : getNodeEventListeners())
{
listener.created(node, type);
}
}
}
else
{
if (logger.isEnabledFor(Priority.WARN))
logger.warn("Found invalid object in database: id = " + nodeRef + ", type = " + type);
}
}
else
{
if (logger.isEnabledFor(Priority.WARN))
logger.warn("Missing object returned from search indexes: id = " + nodeRef + " search query: " + query);
}
}
}
// commit the transaction
tx.commit();
}
catch (InvalidNodeRefException refErr)
{
Utils.addErrorMessage(MessageFormat.format(Application.getMessage(
FacesContext.getCurrentInstance(), Repository.ERROR_NODEREF), new Object[] {refErr.getNodeRef()}), refErr );
this.containerNodes = Collections.<Node>emptyList();
this.contentNodes = Collections.<Node>emptyList();
try { if (tx != null) {tx.rollback();} } catch (Exception tex) {}
}
catch (Throwable err)
{
logger.info("Search failed for: " + query);
Utils.addErrorMessage(MessageFormat.format(Application.getMessage(
FacesContext.getCurrentInstance(), Repository.ERROR_SEARCH), new Object[] {err.getMessage()}), err );
this.containerNodes = Collections.<Node>emptyList();
this.contentNodes = Collections.<Node>emptyList();
try { if (tx != null) {tx.rollback();} } catch (Exception tex) {}
}
finally
{
if (results != null)
{
results.close();
}
}
if (logger.isDebugEnabled())
{
long endTime = System.currentTimeMillis();
logger.debug("Time to query and build map nodes: " + (endTime - startTime) + "ms");
}
}
// ------------------------------------------------------------------------------
// Property Resolvers
public NodePropertyResolver resolverDownload = new NodePropertyResolver() {
public Object get(Node node) {
return DownloadContentServlet.generateDownloadURL(node.getNodeRef(), node.getName());
}
};
public NodePropertyResolver resolverUrl = new NodePropertyResolver() {
public Object get(Node node) {
return DownloadContentServlet.generateBrowserURL(node.getNodeRef(), node.getName());
}
};
public NodePropertyResolver resolverWebdavUrl = new NodePropertyResolver() {
public Object get(Node node) {
return Utils.generateURL(FacesContext.getCurrentInstance(), node, URLMode.WEBDAV);
}
};
public NodePropertyResolver resolverCifsPath = new NodePropertyResolver() {
public Object get(Node node) {
return Utils.generateURL(FacesContext.getCurrentInstance(), node, URLMode.CIFS);
}
};
public NodePropertyResolver resolverLinkDownload = new NodePropertyResolver() {
public Object get(Node node) {
NodeRef destRef = (NodeRef)node.getProperties().get(ContentModel.PROP_LINK_DESTINATION);
if (nodeService.exists(destRef) == true)
{
String destName = Repository.getNameForNode(nodeService, destRef);
return DownloadContentServlet.generateDownloadURL(node.getNodeRef(), destName);
}
else
{
// TODO: link object is missing - navigate to a page with appropriate message
return "#";
}
}
};
public NodePropertyResolver resolverLinkUrl = new NodePropertyResolver() {
public Object get(Node node) {
NodeRef destRef = (NodeRef)node.getProperties().get(ContentModel.PROP_LINK_DESTINATION);
if (nodeService.exists(destRef) == true)
{
String destName = Repository.getNameForNode(nodeService, destRef);
return DownloadContentServlet.generateBrowserURL(destRef, destName);
}
else
{
// TODO: link object is missing - navigate to a page with appropriate message
return "#";
}
}
};
public NodePropertyResolver resolverLinkWebdavUrl = new NodePropertyResolver() {
public Object get(Node node) {
NodeRef destRef = (NodeRef)node.getProperties().get(ContentModel.PROP_LINK_DESTINATION);
if (nodeService.exists(destRef) == true)
{
return Utils.generateURL(FacesContext.getCurrentInstance(), new Node(destRef), URLMode.WEBDAV);
}
else
{
// TODO: link object is missing - navigate to a page with appropriate message
return "#";
}
}
};
public NodePropertyResolver resolverLinkCifsPath = new NodePropertyResolver() {
public Object get(Node node) {
NodeRef destRef = (NodeRef)node.getProperties().get(ContentModel.PROP_LINK_DESTINATION);
if (nodeService.exists(destRef) == true)
{
return Utils.generateURL(FacesContext.getCurrentInstance(), new Node(destRef), URLMode.CIFS);
}
else
{
// TODO: link object is missing - navigate to a page with appropriate message
return "#";
}
}
};
public NodePropertyResolver resolverFileType16 = new NodePropertyResolver() {
public Object get(Node node) {
return Utils.getFileTypeImage(node.getName(), true);
}
};
public NodePropertyResolver resolverFileType32 = new NodePropertyResolver() {
public Object get(Node node) {
return Utils.getFileTypeImage(node.getName(), false);
}
};
public NodePropertyResolver resolverPath = new NodePropertyResolver() {
public Object get(Node node) {
return node.getNodePath();
}
};
public NodePropertyResolver resolverDisplayPath = new NodePropertyResolver() {
public Object get(Node node) {
// TODO: replace this with a method that shows the full display name - not QNames?
return Repository.getDisplayPath(node.getNodePath());
}
};
public NodePropertyResolver resolverSpaceIcon = new NodePropertyResolver() {
public Object get(Node node) {
QNameNodeMap props = (QNameNodeMap)node.getProperties();
String icon = (String)props.getRaw("app:icon");
return (icon != null ? icon : CreateSpaceWizard.DEFAULT_SPACE_ICON_NAME);
}
};
public NodePropertyResolver resolverSmallIcon = new NodePropertyResolver() {
public Object get(Node node) {
QNameNodeMap props = (QNameNodeMap)node.getProperties();
String icon = (String)props.getRaw("app:icon");
return (icon != null ? icon + "-16" : SPACE_SMALL_DEFAULT);
}
};
public NodePropertyResolver resolverMimetype = new NodePropertyResolver() {
public Object get(Node node) {
ContentData content = (ContentData)node.getProperties().get(ContentModel.PROP_CONTENT);
return (content != null ? content.getMimetype() : null);
}
};
public NodePropertyResolver resolverSize = new NodePropertyResolver() {
public Object get(Node node) {
ContentData content = (ContentData)node.getProperties().get(ContentModel.PROP_CONTENT);
return (content != null ? new Long(content.getSize()) : 0L);
}
};
public NodePropertyResolver resolverLang = new NodePropertyResolver() {
public Object get(Node node) {
String lang = null;
if (node.getAspects().contains(ContentModel.ASPECT_MULTILINGUAL_DOCUMENT))
{
Locale locale = (Locale) node.getProperties().get(ContentModel.PROP_LOCALE);
// the content filter lang defined by the user
String userLang = userPreferencesBean.getContentFilterLanguage();
// the node lang
String nodeLang = locale.getLanguage();
// if filter equals all languages : display the lang for each translation
if (nodeLang == null)
{
lang = nodeLang;
}
// if filter is different : display the lang
else if (!nodeLang.equalsIgnoreCase(userLang))
{
lang = nodeLang;
}
// else if the filter is equal to the lang node : nothing to do [lang = null]
}
return lang;
}
};
// ------------------------------------------------------------------------------
// Navigation action event handlers
/**
* Action called from the Simple Search component.
* Sets up the SearchContext object with the values from the simple search menu.
*/
public void search(ActionEvent event)
{
// setup the search text string on the top-level navigation handler
UISimpleSearch search = (UISimpleSearch)event.getComponent();
this.navigator.setSearchContext(search.getSearchContext());
navigateBrowseScreen();
}
/**
* Action called to Close the search dialog by returning to the last view node Id
*/
public void closeSearch(ActionEvent event)
{
// set the current node Id ready for page refresh
this.navigator.setCurrentNodeId( this.navigator.getCurrentNodeId() );
}
/**
* Update page size based on user selection
*/
public void updateSpacesPageSize(ActionEvent event)
{
try
{
int size = Integer.parseInt(this.pageSizeSpacesStr);
if (size >= 0)
{
this.pageSizeSpaces = size;
}
else
{
// reset to known value if this occurs
this.pageSizeSpacesStr = Integer.toString(this.pageSizeSpaces);
}
}
catch (NumberFormatException err)
{
// reset to known value if this occurs
this.pageSizeSpacesStr = Integer.toString(this.pageSizeSpaces);
}
}
/**
* Update page size based on user selection
*/
public void updateContentPageSize(ActionEvent event)
{
try
{
int size = Integer.parseInt(this.pageSizeContentStr);
if (size >= 0)
{
this.pageSizeContent = size;
}
else
{
// reset to known value if this occurs
this.pageSizeContentStr = Integer.toString(this.pageSizeContent);
}
}
catch (NumberFormatException err)
{
// reset to known value if this occurs
this.pageSizeContentStr = Integer.toString(this.pageSizeContent);
}
}
/**
* Action called when a folder space is clicked.
* Navigate into the space.
*/
public void clickSpace(ActionEvent event)
{
UIActionLink link = (UIActionLink)event.getComponent();
Map<String, String> params = link.getParameterMap();
String id = params.get("id");
if (id != null && id.length() != 0)
{
try
{
NodeRef ref = new NodeRef(Repository.getStoreRef(), id);
// handle special folder link node case
if (ApplicationModel.TYPE_FOLDERLINK.equals(this.nodeService.getType(ref)))
{
ref = (NodeRef)this.nodeService.getProperty(ref, ContentModel.PROP_LINK_DESTINATION);
}
clickSpace(ref);
}
catch (InvalidNodeRefException refErr)
{
Utils.addErrorMessage(MessageFormat.format(Application.getMessage(
FacesContext.getCurrentInstance(), Repository.ERROR_NODEREF), new Object[] {id}) );
}
}
}
/**
* Action called when a folder space is clicked.
*
* @param nodeRef The node being clicked
*/
public void clickSpace(NodeRef nodeRef)
{
// refresh UI based on node selection
updateUILocation(nodeRef);
}
/**
* Handler called when a path element is clicked - navigate to the appropriate Space
*/
public void clickSpacePath(ActionEvent event)
{
UINodePath.PathElementEvent pathEvent = (UINodePath.PathElementEvent)event;
NodeRef ref = pathEvent.NodeReference;
try
{
// refresh UI based on node selection
this.updateUILocation(ref);
}
catch (InvalidNodeRefException refErr)
{
Utils.addErrorMessage(MessageFormat.format(Application.getMessage(
FacesContext.getCurrentInstance(), Repository.ERROR_NODEREF), new Object[] {ref.getId()}) );
}
}
/**
* Action called when a folders direct descendant (in the 'list' browse mode) is clicked.
* Navigate into the the descendant space.
*/
public void clickDescendantSpace(ActionEvent event)
{
UINodeDescendants.NodeSelectedEvent nodeEvent = (UINodeDescendants.NodeSelectedEvent)event;
NodeRef nodeRef = nodeEvent.NodeReference;
if (nodeRef == null)
{
throw new IllegalStateException("NodeRef returned from UINodeDescendants.NodeSelectedEvent cannot be null!");
}
if (logger.isDebugEnabled())
logger.debug("Selected noderef Id: " + nodeRef.getId());
try
{
// user can either select a descendant of a node display on the page which means we
// must add the it's parent and itself to the breadcrumb
ChildAssociationRef parentAssocRef = nodeService.getPrimaryParent(nodeRef);
if (logger.isDebugEnabled())
{
logger.debug("Selected item getPrimaryParent().getChildRef() noderef Id: " + parentAssocRef.getChildRef().getId());
logger.debug("Selected item getPrimaryParent().getParentRef() noderef Id: " + parentAssocRef.getParentRef().getId());
logger.debug("Current value getNavigator().getCurrentNodeId() noderef Id: " + this.navigator.getCurrentNodeId());
}
if (nodeEvent.IsParent == false)
{
// a descendant of the displayed node was selected
// first refresh based on the parent and add to the breadcrumb
updateUILocation(parentAssocRef.getParentRef());
// now add our selected node
updateUILocation(nodeRef);
}
else
{
// else the parent ellipses i.e. the displayed node was selected
updateUILocation(nodeRef);
}
}
catch (InvalidNodeRefException refErr)
{
Utils.addErrorMessage(MessageFormat.format(Application.getMessage(
FacesContext.getCurrentInstance(), Repository.ERROR_NODEREF), new Object[] {nodeRef.getId()}) );
}
}
/**
* Action event called by all Browse actions that need to setup a Space context
* before an action page/wizard is called. The context will be a Node in setActionSpace() which
* can be retrieved on the action page from BrowseBean.getActionSpace().
*
* @param event ActionEvent
*/
public void setupSpaceAction(ActionEvent event)
{
UIActionLink link = (UIActionLink)event.getComponent();
Map<String, String> params = link.getParameterMap();
String id = params.get("id");
setupSpaceAction(id, true);
}
/**
* Public helper to setup action pages with Space context
*
* @param id of the Space node to setup context for
*/
public void setupSpaceAction(String id, boolean invalidate)
{
if (id != null && id.length() != 0)
{
if (logger.isDebugEnabled())
logger.debug("Setup for action, setting current space to: " + id);
try
{
// create the node ref, then our node representation
NodeRef ref = new NodeRef(Repository.getStoreRef(), id);
Node node = new Node(ref);
// resolve icon in-case one has not been set
node.addPropertyResolver("icon", this.resolverSpaceIcon);
// prepare a node for the action context
setActionSpace(node);
// setup the dispatch context in case it is required
this.navigator.setupDispatchContext(node);
}
catch (InvalidNodeRefException refErr)
{
Utils.addErrorMessage(MessageFormat.format(Application.getMessage(
FacesContext.getCurrentInstance(), Repository.ERROR_NODEREF), new Object[] {id}) );
}
}
else
{
setActionSpace(null);
}
// clear the UI state in preparation for finishing the next action
if (invalidate == true)
{
// use the context service to notify all registered beans
UIContextService.getInstance(FacesContext.getCurrentInstance()).notifyBeans();
}
}
/**
* Acrtion event called by Delete Space actions. We setup the action space as normal, then prepare
* any special case message string to be shown to the user if they are trying to delete specific spaces.
*/
public void setupDeleteAction(ActionEvent event)
{
String message = null;
setupSpaceAction(event);
Node node = getActionSpace();
if (node != null)
{
NodeRef companyRootRef = new NodeRef(Repository.getStoreRef(), Application.getCompanyRootId());
if (node.getNodeRef().equals(companyRootRef))
{
message = Application.getMessage(FacesContext.getCurrentInstance(), MSG_DELETE_COMPANYROOT);
}
}
setDeleteMessage(message);
}
/**
* Action event called by all actions that need to setup a Content Document context on the
* BrowseBean before an action page/wizard is called. The context will be a Node in
* setDocument() which can be retrieved on the action page from BrowseBean.getDocument().
*/
public void setupContentAction(ActionEvent event)
{
UIActionLink link = (UIActionLink)event.getComponent();
Map<String, String> params = link.getParameterMap();
setupContentAction(params.get("id"), true);
}
/**
* Public helper to setup action pages with content context
*
* @param id of the content node to setup context for
*/
public void setupContentAction(String id, boolean invalidate)
{
if (id != null && id.length() != 0)
{
if (logger.isDebugEnabled())
logger.debug("Setup for action, setting current document to: " + id);
try
{
// create the node ref, then our node representation
NodeRef ref = new NodeRef(Repository.getStoreRef(), id);
Node node = new Node(ref);
// store the URL to for downloading the content
if (ApplicationModel.TYPE_FILELINK.equals(node.getType()))
{
node.addPropertyResolver("url", this.resolverLinkDownload);
}
else
{
node.addPropertyResolver("url", this.resolverDownload);
}
node.addPropertyResolver("fileType32", this.resolverFileType32);
node.addPropertyResolver("mimetype", this.resolverMimetype);
node.addPropertyResolver("size", this.resolverSize);
node.addPropertyResolver("lang", this.resolverLang);
for (NodeEventListener listener : getNodeEventListeners())
{
listener.created(node, node.getType());
}
// get hold of the DocumentDetailsBean and reset it
DocumentDetailsBean docDetails = (DocumentDetailsBean)FacesContext.getCurrentInstance().
getExternalContext().getSessionMap().get("DocumentDetailsBean");
if (docDetails != null)
{
docDetails.reset();
}
// remember the document
setDocument(node);
// setup the dispatch context in case it is required
this.navigator.setupDispatchContext(node);
}
catch (InvalidNodeRefException refErr)
{
Utils.addErrorMessage(MessageFormat.format(Application.getMessage(
FacesContext.getCurrentInstance(), Repository.ERROR_NODEREF), new Object[] {id}) );
}
}
else
{
setDocument(null);
}
// clear the UI state in preparation for finishing the next action
if (invalidate == true)
{
// use the context service to notify all registered beans
UIContextService.getInstance(FacesContext.getCurrentInstance()).notifyBeans();
}
}
/**
* Removes the given node from the breadcrumb i.e. following a delete
*
* @param node The space to remove from the breadcrumb
*/
public void removeSpaceFromBreadcrumb(Node node)
{
List<IBreadcrumbHandler> location = navigator.getLocation();
IBreadcrumbHandler handler = location.get(location.size() - 1);
if (handler instanceof BrowseBreadcrumbHandler)
{
// see if the current breadcrumb location is our node
if ( ((BrowseBreadcrumbHandler)handler).getNodeRef().equals(node.getNodeRef()) == true )
{
location.remove(location.size() - 1);
// now work out which node to set the list to refresh against
if (location.size() != 0)
{
handler = location.get(location.size() - 1);
if (handler instanceof BrowseBreadcrumbHandler)
{
// change the current node Id
navigator.setCurrentNodeId(((BrowseBreadcrumbHandler)handler).getNodeRef().getId());
}
else
{
// TODO: shouldn't do this - but for now the user home dir is the root!
navigator.setCurrentNodeId(Application.getCurrentUser(FacesContext.getCurrentInstance()).getHomeSpaceId());
}
}
}
}
}
/**
* Support for refresh of lists via special case for an External Access URL.
* these URLs restart the JSF lifecycle but an old UIRichList is restored from
* the component tree - which needs clearing "late" in the lifecycle process.
*/
public void externalAccessRefresh()
{
this.externalForceRefresh = true;
}
/**
* Save the state of the panel that was expanded/collapsed
*/
public void expandPanel(ActionEvent event)
{
if (event instanceof ExpandedEvent)
{
String id = event.getComponent().getId();
this.panels.put(id, ((ExpandedEvent)event).State);
}
}
// ------------------------------------------------------------------------------
// Private helpers
/**
* Initialise default values from client configuration
*/
private void initFromClientConfig()
{
ConfigService config = Application.getConfigService(FacesContext.getCurrentInstance());
this.viewsConfig = (ViewsConfigElement)config.getConfig("Views").
getConfigElement(ViewsConfigElement.CONFIG_ELEMENT_ID);
this.browseViewMode = this.viewsConfig.getDefaultView(PAGE_NAME_BROWSE);
int pageSize = this.viewsConfig.getDefaultPageSize(PAGE_NAME_BROWSE, this.browseViewMode);
setPageSizeContent(pageSize);
setPageSizeSpaces(pageSize);
}
/**
* @return the Set of NodeEventListeners registered against this bean
*/
private Set<NodeEventListener> getNodeEventListeners()
{
if (this.nodeEventListeners == null)
{
this.nodeEventListeners = new HashSet<NodeEventListener>();
FacesContext fc = FacesContext.getCurrentInstance();
Config listenerConfig = Application.getConfigService(fc).getConfig("Node Event Listeners");
if (listenerConfig != null)
{
ConfigElement listenerElement = listenerConfig.getConfigElement("node-event-listeners");
if (listenerElement != null)
{
for (ConfigElement child : listenerElement.getChildren())
{
if (child.getName().equals("listener"))
{
// retrieved the JSF Managed Bean identified in the config
String listenerName = child.getValue().trim();
Object bean = FacesHelper.getManagedBean(fc, listenerName);
if (bean instanceof NodeEventListener)
{
addNodeEventListener((NodeEventListener)bean);
}
}
}
}
}
}
return this.nodeEventListeners;
}
/**
* Refresh the UI after a Space selection change. Adds the selected space to the breadcrumb
* location path and also updates the list components in the UI.
*
* @param ref NodeRef of the selected space
*/
public void updateUILocation(NodeRef ref)
{
// get the current breadcrumb location and append a new handler to it
// our handler know the ID of the selected node and the display label for it
List<IBreadcrumbHandler> location = this.navigator.getLocation();
if (location.size() != 0)
{
// attempt to find the ID - if it's already in the breadcrumb then we
// navigate directly to that node - rather than add duplication to the breadcrumb path
boolean foundNode = false;
for (int i=0; i<location.size(); i++)
{
IBreadcrumbHandler element = location.get(i);
if (element instanceof IRepoBreadcrumbHandler)
{
NodeRef nodeRef = ((IRepoBreadcrumbHandler)element).getNodeRef();
if (ref.equals(nodeRef) == true)
{
// TODO: we should be able to do this - but the UIBreadcrumb component modifies
// it's own internal value when clicked - then uses that from then on!
// the other ops are using the same List object and modding it directly.
//List<IBreadcrumbHandler> newLocation = new ArrayList<IBreadcrumbHandler>(i+1);
//newLocation.addAll(location.subList(0, i + 1));
//this.navigator.setLocation(newLocation);
// TODO: but instead for now we do this:
int count = location.size();
for (int n=i+1; n<count; n++)
{
location.remove(i+1);
}
foundNode = true;
break;
}
}
}
// add new node to the end of the existing breadcrumb
if (foundNode == false)
{
String name = Repository.getNameForNode(this.nodeService, ref);
location.add(new BrowseBreadcrumbHandler(ref, name));
}
}
else
{
// special case to add first item to the location
String name = Repository.getNameForNode(this.nodeService, ref);
location.add(new BrowseBreadcrumbHandler(ref, name));
}
// set the current node Id ready for page refresh
this.navigator.setCurrentNodeId(ref.getId());
// set up the dispatch context for the navigation handler
this.navigator.setupDispatchContext(new Node(ref));
// inform any listeners that the current space has changed
UIContextService.getInstance(FacesContext.getCurrentInstance()).spaceChanged();
navigateBrowseScreen();
}
/**
* Invalidate list component state after an action which changes the UI context
*/
private void invalidateComponents()
{
if (logger.isDebugEnabled())
logger.debug("Invalidating browse components...");
// clear the value for the list components - will cause re-bind to it's data and refresh
if (this.contentRichList != null)
{
this.contentRichList.setValue(null);
if (this.navigator.getSearchContext() != null)
{
// clear the sorting mode so the search results are displayed in default 'score' order
this.contentRichList.clearSort();
}
}
if (this.spacesRichList != null)
{
this.spacesRichList.setValue(null);
if (this.navigator.getSearchContext() != null)
{
// clear the sorting mode so the search results are displayed in default 'score' order
this.spacesRichList.clearSort();
}
}
// clear the storage of the last set of nodes
this.containerNodes = null;
this.contentNodes = null;
}
/**
* @return whether the current View ID is the "browse" screen
*/
private boolean isViewCurrent()
{
return (FacesContext.getCurrentInstance().getViewRoot().getViewId().equals(BROWSE_VIEW_ID));
}
/**
* Perform navigation to the browse screen if it is not already the current View
*/
private void navigateBrowseScreen()
{
String outcome = null;
if (isViewCurrent() == false)
{
outcome = "browse";
}
FacesContext fc = FacesContext.getCurrentInstance();
fc.getApplication().getNavigationHandler().handleNavigation(fc, null, outcome);
}
// ------------------------------------------------------------------------------
// Inner classes
/**
* Class to handle breadcrumb interaction for Browse pages
*/
private class BrowseBreadcrumbHandler implements IRepoBreadcrumbHandler
{
private static final long serialVersionUID = 3833183653173016630L;
/**
* Constructor
*
* @param NodeRef The NodeRef for this browse navigation element
* @param label Element label
*/
public BrowseBreadcrumbHandler(NodeRef nodeRef, String label)
{
this.label = label;
this.nodeRef = nodeRef;
}
/**
* @see java.lang.Object#toString()
*/
public String toString()
{
return this.label;
}
/**
* @see org.alfresco.web.ui.common.component.IBreadcrumbHandler#navigationOutcome(org.alfresco.web.ui.common.component.UIBreadcrumb)
*/
@SuppressWarnings("unchecked")
public String navigationOutcome(UIBreadcrumb breadcrumb)
{
// All browse breadcrumb element relate to a Node Id - when selected we
// set the current node id
navigator.setCurrentNodeId(this.nodeRef.getId());
navigator.setLocation( (List)breadcrumb.getValue() );
// setup the dispatch context
navigator.setupDispatchContext(new Node(this.nodeRef));
// inform any listeners that the current space has changed
UIContextService.getInstance(FacesContext.getCurrentInstance()).spaceChanged();
// return to browse page if required
return (isViewCurrent() ? null : "browse");
}
public NodeRef getNodeRef()
{
return this.nodeRef;
}
private NodeRef nodeRef;
private String label;
}
// ------------------------------------------------------------------------------
// Private data
/** Browse screen view ID */
public static final String BROWSE_VIEW_ID = "/jsp/browse/browse.jsp";
/** Small icon default name */
public static final String SPACE_SMALL_DEFAULT = "space_small";
private static final String VIEWMODE_DASHBOARD = "dashboard";
private static final String PAGE_NAME_BROWSE = "browse";
/** I18N messages */
private static final String MSG_DELETE_COMPANYROOT = "delete_companyroot_confirm";
private static final String MSG_SEARCH_MINIMUM = "search_minimum";
private static Logger logger = Logger.getLogger(BrowseBean.class);
/** The NodeService to be used by the bean */
protected NodeService nodeService;
/** The SearchService to be used by the bean */
protected SearchService searchService;
/** The LockService to be used by the bean */
protected LockService lockService;
/** The NavigationBean bean reference */
protected NavigationBean navigator;
/** The UserPreferencesBean to be used by the bean */
protected UserPreferencesBean userPreferencesBean;
/** The DictionaryService bean reference */
protected DictionaryService dictionaryService;
/** The file folder service */
protected FileFolderService fileFolderService;
/** Views configuration object */
protected ViewsConfigElement viewsConfig = null;
/** Listeners for Node events */
protected Set<NodeEventListener> nodeEventListeners = null;
/** Collapsable Panel state */
private Map<String, Boolean> panels = new HashMap<String, Boolean>(4, 1.0f);
/** Component references */
protected UIRichList spacesRichList;
protected UIRichList contentRichList;
private UIStatusMessage statusMessage;
/** Transient lists of container and content nodes for display */
protected List<Node> containerNodes = null;
protected List<Node> contentNodes = null;
/** The current space and it's properties - if any */
protected Node actionSpace;
/** The current document */
protected Node document;
/** Special message to display when user deleting certain folders e.g. Company Home */
private String deleteMessage;
/** The current browse view mode - set to a well known IRichListRenderer identifier */
private String browseViewMode;
/** The current browse view page sizes */
private int pageSizeSpaces;
private int pageSizeContent;
private String pageSizeSpacesStr;
private String pageSizeContentStr;
/** True if current space has a dashboard (template) view available */
private boolean dashboardView;
private boolean externalForceRefresh = false;
}