mirror of
https://github.com/Alfresco/alfresco-community-repo.git
synced 2025-06-30 18:15:39 +00:00
Fixed AWC-407 Added helper to Application to get client config git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@2114 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
1500 lines
53 KiB
Java
1500 lines
53 KiB
Java
/*
|
|
* Copyright (C) 2005 Alfresco, Inc.
|
|
*
|
|
* Licensed under the Mozilla Public License version 1.1
|
|
* with a permitted attribution clause. You may obtain a
|
|
* copy of the License at
|
|
*
|
|
* http://www.alfresco.org/legal/license.txt
|
|
*
|
|
* Unless required by applicable law or agreed to in writing,
|
|
* software distributed under the License is distributed on an
|
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
|
|
* either express or implied. See the License for the specific
|
|
* language governing permissions and limitations under the
|
|
* License.
|
|
*/
|
|
package org.alfresco.web.bean;
|
|
|
|
import java.text.MessageFormat;
|
|
import java.util.ArrayList;
|
|
import java.util.Collections;
|
|
import java.util.List;
|
|
import java.util.Map;
|
|
|
|
import javax.faces.application.FacesMessage;
|
|
import javax.faces.context.FacesContext;
|
|
import javax.faces.event.ActionEvent;
|
|
import javax.transaction.UserTransaction;
|
|
|
|
import org.alfresco.model.ContentModel;
|
|
import org.alfresco.model.ForumModel;
|
|
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.repository.Path;
|
|
import org.alfresco.service.cmr.search.ResultSet;
|
|
import org.alfresco.service.cmr.search.ResultSetRow;
|
|
import org.alfresco.service.cmr.search.SearchService;
|
|
import org.alfresco.service.cmr.security.PermissionService;
|
|
import org.alfresco.service.namespace.QName;
|
|
import org.alfresco.service.namespace.RegexQNamePattern;
|
|
import org.alfresco.web.app.AlfrescoNavigationHandler;
|
|
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.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.wizard.NewSpaceWizard;
|
|
import org.alfresco.web.config.ClientConfigElement;
|
|
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.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
|
|
{
|
|
// ------------------------------------------------------------------------------
|
|
// 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 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");
|
|
}
|
|
else
|
|
{
|
|
navigateBrowseScreen();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @return Returns the browsePageSize.
|
|
*/
|
|
public int getBrowsePageSize()
|
|
{
|
|
return this.browsePageSize;
|
|
}
|
|
|
|
/**
|
|
* @param browsePageSize The browsePageSize to set.
|
|
*/
|
|
public void setBrowsePageSize(int browsePageSize)
|
|
{
|
|
this.browsePageSize = browsePageSize;
|
|
}
|
|
|
|
/**
|
|
* @return Returns the minimum length of a valid search string.
|
|
*/
|
|
public int getMinimumSearchLength()
|
|
{
|
|
return this.clientConfig.getSearchMinimum();
|
|
}
|
|
|
|
/**
|
|
* @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)
|
|
{
|
|
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)
|
|
{
|
|
this.document = document;
|
|
}
|
|
|
|
/**
|
|
* @param contentRichList The contentRichList to set.
|
|
*/
|
|
public void setContentRichList(UIRichList browseRichList)
|
|
{
|
|
this.contentRichList = browseRichList;
|
|
if (this.contentRichList != null)
|
|
{
|
|
this.contentRichList.setInitialSortColumn(
|
|
this.clientConfig.getDefaultSortColumn(PAGE_NAME_BROWSE));
|
|
this.contentRichList.setInitialSortDescending(
|
|
this.clientConfig.hasDescendingSort(PAGE_NAME_BROWSE));
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @return Returns the contentRichList.
|
|
*/
|
|
public UIRichList getContentRichList()
|
|
{
|
|
return this.contentRichList;
|
|
}
|
|
|
|
/**
|
|
* @param spacesRichList The spacesRichList to set.
|
|
*/
|
|
public void setSpacesRichList(UIRichList detailsRichList)
|
|
{
|
|
this.spacesRichList = detailsRichList;
|
|
if (this.spacesRichList != null)
|
|
{
|
|
// set the initial sort column and direction
|
|
this.spacesRichList.setInitialSortColumn(
|
|
this.clientConfig.getDefaultSortColumn(PAGE_NAME_BROWSE));
|
|
this.spacesRichList.setInitialSortDescending(
|
|
this.clientConfig.hasDescendingSort(PAGE_NAME_BROWSE));
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @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 additional properties required at data-binding time.
|
|
* <p>
|
|
* These are properties used by components on the page when iterating over the nodes.
|
|
* Information such as whether the node is locked, a working copy, download URL 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 required.
|
|
*
|
|
* @param node MapNode to add the properties too
|
|
*/
|
|
public void setupDataBindingProperties(MapNode node)
|
|
{
|
|
// special properties to be used by the value binding components on the page
|
|
node.addPropertyResolver("locked", this.resolverlocked);
|
|
node.addPropertyResolver("owner", this.resolverOwner);
|
|
node.addPropertyResolver("workingCopy", this.resolverWorkingCopy);
|
|
node.addPropertyResolver("url", this.resolverUrl);
|
|
node.addPropertyResolver("fileType16", this.resolverFileType16);
|
|
node.addPropertyResolver("fileType32", this.resolverFileType32);
|
|
node.addPropertyResolver("size", this.resolverSize);
|
|
node.addPropertyResolver("cancelCheckOut", this.resolverCancelCheckOut);
|
|
node.addPropertyResolver("checkIn", this.resolverCheckIn);
|
|
node.addPropertyResolver("beingDiscussed", this.resolverBeingDiscussed);
|
|
node.addPropertyResolver("editLinkType", this.resolverEditLinkType);
|
|
node.addPropertyResolver("webdavUrl", this.resolverWebdavUrl);
|
|
node.addPropertyResolver("cifsPath", this.resolverCifsPath);
|
|
}
|
|
|
|
|
|
// ------------------------------------------------------------------------------
|
|
// IContextListener implementation
|
|
|
|
/**
|
|
* @see org.alfresco.web.app.context.IContextListener#contextUpdated()
|
|
*/
|
|
public void contextUpdated()
|
|
{
|
|
invalidateComponents();
|
|
}
|
|
|
|
|
|
// ------------------------------------------------------------------------------
|
|
// 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
|
|
setBrowsePageSize(this.clientConfig.getDefaultPageSize(PAGE_NAME_BROWSE,
|
|
viewMode));
|
|
|
|
if (logger.isDebugEnabled())
|
|
logger.debug("Browse view page size set to: " + getBrowsePageSize());
|
|
|
|
// in case we left for dashboard
|
|
if (isDashboardView() == true)
|
|
{
|
|
setDashboardView(false);
|
|
}
|
|
|
|
// push the view mode into the lists
|
|
setBrowseViewMode(viewMode);
|
|
}
|
|
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)
|
|
{
|
|
// look for Space or File nodes
|
|
if (this.dictionaryService.isSubClass(type, ContentModel.TYPE_FOLDER) == true &&
|
|
this.dictionaryService.isSubClass(type, ContentModel.TYPE_SYSTEM_FOLDER) == false)
|
|
{
|
|
// create our Node representation
|
|
MapNode node = new MapNode(nodeRef, this.nodeService, true);
|
|
node.addPropertyResolver("icon", this.resolverSpaceIcon);
|
|
node.addPropertyResolver("beingDiscussed", this.resolverBeingDiscussed);
|
|
|
|
this.containerNodes.add(node);
|
|
}
|
|
else if (this.dictionaryService.isSubClass(type, ContentModel.TYPE_CONTENT))
|
|
{
|
|
// create our Node representation
|
|
MapNode node = new MapNode(nodeRef, this.nodeService, true);
|
|
|
|
setupDataBindingProperties(node);
|
|
|
|
this.contentNodes.add(node);
|
|
}
|
|
}
|
|
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()}) );
|
|
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();
|
|
|
|
results = this.searchService.query(
|
|
Repository.getStoreRef(),
|
|
"lucene", query, null, null);
|
|
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)
|
|
{
|
|
// 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
|
|
MapNode node = new MapNode(nodeRef, this.nodeService, true);
|
|
|
|
// construct the path to this Node
|
|
node.addPropertyResolver("path", this.resolverPath);
|
|
node.addPropertyResolver("displayPath", this.resolverDisplayPath);
|
|
node.addPropertyResolver("icon", this.resolverSpaceIcon);
|
|
node.addPropertyResolver("beingDiscussed", this.resolverBeingDiscussed);
|
|
|
|
this.containerNodes.add(node);
|
|
}
|
|
else if (this.dictionaryService.isSubClass(type, ContentModel.TYPE_CONTENT))
|
|
{
|
|
// create our Node representation
|
|
MapNode node = new MapNode(nodeRef, this.nodeService, true);
|
|
|
|
setupDataBindingProperties(node);
|
|
|
|
// construct the path to this Node
|
|
node.addPropertyResolver("path", this.resolverPath);
|
|
node.addPropertyResolver("displayPath", this.resolverDisplayPath);
|
|
|
|
this.contentNodes.add(node);
|
|
}
|
|
}
|
|
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()}) );
|
|
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 resolverlocked = new NodePropertyResolver() {
|
|
public Object get(Node node) {
|
|
return Repository.isNodeLocked(node, lockService);
|
|
}
|
|
};
|
|
|
|
public NodePropertyResolver resolverOwner = new NodePropertyResolver() {
|
|
public Object get(Node node) {
|
|
return Repository.isNodeOwner(node, lockService);
|
|
}
|
|
};
|
|
|
|
public NodePropertyResolver resolverCancelCheckOut = new NodePropertyResolver() {
|
|
public Object get(Node node) {
|
|
return node.hasAspect(ContentModel.ASPECT_WORKING_COPY) && node.hasPermission(PermissionService.CANCEL_CHECK_OUT);
|
|
}
|
|
};
|
|
|
|
public NodePropertyResolver resolverCheckIn = new NodePropertyResolver() {
|
|
public Object get(Node node) {
|
|
return node.hasAspect(ContentModel.ASPECT_WORKING_COPY) && node.hasPermission(PermissionService.CHECK_IN);
|
|
}
|
|
};
|
|
|
|
public NodePropertyResolver resolverWorkingCopy = new NodePropertyResolver() {
|
|
public Object get(Node node) {
|
|
return node.hasAspect(ContentModel.ASPECT_WORKING_COPY);
|
|
}
|
|
};
|
|
|
|
public NodePropertyResolver resolverBeingDiscussed = new NodePropertyResolver() {
|
|
public Object get(Node node) {
|
|
return node.hasAspect(ForumModel.ASPECT_DISCUSSABLE);
|
|
}
|
|
};
|
|
|
|
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 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 nodeService.getPath(node.getNodeRef());
|
|
}
|
|
};
|
|
|
|
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( (Path)node.getProperties().get("path") );
|
|
}
|
|
};
|
|
|
|
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 : NewSpaceWizard.SPACE_ICON_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()) : null);
|
|
}
|
|
};
|
|
|
|
public NodePropertyResolver resolverEditLinkType = new NodePropertyResolver() {
|
|
public Object get(Node node)
|
|
{
|
|
String editLinkType = "http";
|
|
|
|
// if the node is inline editable, the default http behaviour should
|
|
// always be used otherwise the configured approach is used
|
|
if (node.hasAspect(ContentModel.ASPECT_INLINEEDITABLE) == false)
|
|
{
|
|
editLinkType = clientConfig.getEditLinkType();
|
|
if (editLinkType == null)
|
|
{
|
|
editLinkType = "http";
|
|
}
|
|
}
|
|
|
|
return editLinkType;
|
|
}
|
|
};
|
|
|
|
|
|
// ------------------------------------------------------------------------------
|
|
// 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() );
|
|
}
|
|
|
|
/**
|
|
* 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);
|
|
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
|
|
List<IBreadcrumbHandler> location = this.navigator.getLocation();
|
|
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
|
|
node.addPropertyResolver("url", this.resolverDownload);
|
|
node.addPropertyResolver("fileType32", this.resolverFileType32);
|
|
node.addPropertyResolver("mimetype", this.resolverMimetype);
|
|
node.addPropertyResolver("size", this.resolverSize);
|
|
node.addPropertyResolver("cancelCheckOut", this.resolverCancelCheckOut);
|
|
node.addPropertyResolver("checkIn", this.resolverCheckIn);
|
|
|
|
// 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();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Handler called upon the completion of the Delete Space page
|
|
*
|
|
* @return outcome
|
|
*/
|
|
public String deleteSpaceOK()
|
|
{
|
|
String outcome = null;
|
|
|
|
Node node = getActionSpace();
|
|
if (node != null)
|
|
{
|
|
try
|
|
{
|
|
if (logger.isDebugEnabled())
|
|
logger.debug("Trying to delete space: " + node.getId());
|
|
|
|
this.nodeService.deleteNode(node.getNodeRef());
|
|
|
|
// remove this node from the breadcrumb if required
|
|
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());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// add a message to inform the user that the delete was OK
|
|
String statusMsg = MessageFormat.format(
|
|
Application.getMessage(FacesContext.getCurrentInstance(), "status_space_deleted"),
|
|
new Object[]{node.getName()});
|
|
Utils.addStatusMessage(FacesMessage.SEVERITY_INFO, statusMsg);
|
|
|
|
// clear action context
|
|
setActionSpace(null);
|
|
|
|
// setting the outcome will show the browse view again
|
|
outcome = AlfrescoNavigationHandler.CLOSE_DIALOG_OUTCOME +
|
|
AlfrescoNavigationHandler.DIALOG_SEPARATOR + "browse";
|
|
}
|
|
catch (Throwable err)
|
|
{
|
|
Utils.addErrorMessage(Application.getMessage(
|
|
FacesContext.getCurrentInstance(), MSG_ERROR_DELETE_SPACE) + err.getMessage(), err);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
logger.warn("WARNING: deleteSpaceOK called without a current Space!");
|
|
}
|
|
|
|
return outcome;
|
|
}
|
|
|
|
/**
|
|
* Handler called upon the completion of the Delete File page
|
|
*
|
|
* @return outcome
|
|
*/
|
|
public String deleteFileOK()
|
|
{
|
|
String outcome = null;
|
|
|
|
Node node = getDocument();
|
|
if (node != null)
|
|
{
|
|
try
|
|
{
|
|
if (logger.isDebugEnabled())
|
|
logger.debug("Trying to delete content node: " + node.getId());
|
|
|
|
this.nodeService.deleteNode(node.getNodeRef());
|
|
|
|
// clear action context
|
|
setDocument(null);
|
|
|
|
// setting the outcome will show the browse view again
|
|
outcome = AlfrescoNavigationHandler.CLOSE_DIALOG_OUTCOME +
|
|
AlfrescoNavigationHandler.DIALOG_SEPARATOR + "browse";
|
|
}
|
|
catch (Throwable err)
|
|
{
|
|
Utils.addErrorMessage(Application.getMessage(
|
|
FacesContext.getCurrentInstance(), MSG_ERROR_DELETE_FILE) + err.getMessage(), err);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
logger.warn("WARNING: deleteFileOK called without a current Document!");
|
|
}
|
|
|
|
return outcome;
|
|
}
|
|
|
|
|
|
// ------------------------------------------------------------------------------
|
|
// Private helpers
|
|
|
|
/**
|
|
* Initialise default values from client configuration
|
|
*/
|
|
private void initFromClientConfig()
|
|
{
|
|
this.clientConfig = Application.getClientConfig(FacesContext.getCurrentInstance());
|
|
this.browseViewMode = clientConfig.getDefaultView(PAGE_NAME_BROWSE);
|
|
this.browsePageSize = clientConfig.getDefaultPageSize(PAGE_NAME_BROWSE,
|
|
this.browseViewMode);
|
|
}
|
|
|
|
/**
|
|
* 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
|
|
*/
|
|
/*package*/ 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));
|
|
|
|
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));
|
|
|
|
// return to browse page if required
|
|
return (isViewCurrent() ? null : "browse");
|
|
}
|
|
|
|
public NodeRef getNodeRef()
|
|
{
|
|
return this.nodeRef;
|
|
}
|
|
|
|
private NodeRef nodeRef;
|
|
private String label;
|
|
}
|
|
|
|
|
|
// ------------------------------------------------------------------------------
|
|
// Private data
|
|
|
|
public static final String BROWSE_VIEW_ID = "/jsp/browse/browse.jsp";
|
|
|
|
private static final String VIEWMODE_DASHBOARD = "dashboard";
|
|
private static final String PAGE_NAME_BROWSE = "browse";
|
|
|
|
/** I18N messages */
|
|
private static final String MSG_ERROR_DELETE_FILE = "error_delete_file";
|
|
private static final String MSG_ERROR_DELETE_SPACE = "error_delete_space";
|
|
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 */
|
|
private NodeService nodeService;
|
|
|
|
/** The SearchService to be used by the bean */
|
|
private SearchService searchService;
|
|
|
|
/** The LockService to be used by the bean */
|
|
private LockService lockService;
|
|
|
|
/** The NavigationBean bean reference */
|
|
private NavigationBean navigator;
|
|
|
|
/** The DictionaryService bean reference */
|
|
private DictionaryService dictionaryService;
|
|
|
|
/** The file folder service */
|
|
private FileFolderService fileFolderService;
|
|
|
|
/** Client configuration object */
|
|
private ClientConfigElement clientConfig = null;
|
|
|
|
/** Component references */
|
|
private UIRichList spacesRichList;
|
|
private UIRichList contentRichList;
|
|
private UIStatusMessage statusMessage;
|
|
|
|
/** Transient lists of container and content nodes for display */
|
|
private List<Node> containerNodes = null;
|
|
private List<Node> contentNodes = null;
|
|
|
|
/** The current space and it's properties - if any */
|
|
private Node actionSpace;
|
|
|
|
/** The current document */
|
|
private 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 size */
|
|
private int browsePageSize;
|
|
|
|
/** True if current space has a dashboard (template) view available */
|
|
private boolean dashboardView;
|
|
}
|