/* * 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.HashMap; import java.util.List; import java.util.Map; import javax.faces.context.FacesContext; import javax.faces.event.ActionEvent; import org.alfresco.config.ConfigService; import org.alfresco.error.AlfrescoRuntimeException; import org.alfresco.filesys.CIFSServer; import org.alfresco.filesys.server.filesys.DiskSharedDevice; import org.alfresco.filesys.smb.server.repo.ContentContext; import org.alfresco.filesys.smb.server.repo.ContentDiskInterface; import org.alfresco.model.ContentModel; 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.repository.TemplateImageResolver; import org.alfresco.service.cmr.repository.TemplateNode; import org.alfresco.service.cmr.search.SearchService; import org.alfresco.service.namespace.NamespaceService; import org.alfresco.web.app.AlfrescoNavigationHandler; import org.alfresco.web.app.Application; import org.alfresco.web.app.context.UIContextService; import org.alfresco.web.bean.repository.Node; import org.alfresco.web.bean.repository.Repository; import org.alfresco.web.bean.repository.User; 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.component.IBreadcrumbHandler; import org.alfresco.web.ui.common.component.UIBreadcrumb; import org.alfresco.web.ui.common.component.UIModeList; import org.alfresco.web.ui.repo.component.IRepoBreadcrumbHandler; import org.alfresco.web.ui.repo.component.shelf.UIShelf; import org.apache.log4j.Logger; /** * @author Kevin Roast */ public class NavigationBean { // ------------------------------------------------------------------------------ // Bean property getters and setters /** * @param nodeService The nodeService to set. */ public void setNodeService(NodeService nodeService) { this.nodeService = nodeService; } /** * @param searchService The searchService to set. */ public void setSearchService(SearchService searchService) { this.searchService = searchService; } /** * @param namespaceService The namespaceService to set. */ public void setNamespaceService(NamespaceService namespaceService) { this.namespaceService = namespaceService; } /** * @param cifsServer The cifsServer to set. */ public void setCifsServer(CIFSServer cifsServer) { this.cifsServer = cifsServer; } /** * @param contentDiskDriver The contentDiskDriver to set. */ public void setContentDiskDriver(ContentDiskInterface contentDiskDriver) { this.contentDiskDriver = contentDiskDriver; } /** * @param configService The ConfigService to set. */ public void setConfigService(ConfigService configService) { this.configService = configService; } /** * @return the User object representing the current instance for this user */ public User getCurrentUser() { return Application.getCurrentUser(FacesContext.getCurrentInstance()); } /** * Return the expanded state of the Shelf panel wrapper component * * @return the expanded state of the Shelf panel wrapper component */ public boolean isShelfExpanded() { return this.shelfExpanded; } /** * Set the expanded state of the Shelf panel wrapper component * * @param expanded true to expanded the Shelf panel area, false to hide it */ public void setShelfExpanded(boolean expanded) { this.shelfExpanded = expanded; } /** * @return Returns the array containing the expanded state of the shelf items */ public boolean[] getShelfItemExpanded() { return this.shelfItemExpanded; } /** * @param shelfItemExpanded The array containing the expanded state of the shelf items */ public void setShelfItemExpanded(boolean[] shelfItemExpanded) { this.shelfItemExpanded = shelfItemExpanded; } /** * @return Returns the toolbar Location. */ public String getToolbarLocation() { return this.toolbarLocation; } /** * @param toolbarLocation The toolbar Location to set. */ public void setToolbarLocation(String toolbarLocation) { this.toolbarLocation = toolbarLocation; } /** * @return Returns the helpUrl. */ public String getHelpUrl() { if (this.clientConfig == null) { initFromClientConfig(); } return this.helpUrl; } /** * @param helpUrl The helpUrl to set. */ public void setHelpUrl(String helpUrl) { this.helpUrl = helpUrl; } /** * @return Returns the search context object if any. */ public SearchContext getSearchContext() { return this.searchContext; } /** * @param searchContext The search context object to set or null to clear search. */ public void setSearchContext(SearchContext searchContext) { this.searchContext = searchContext; UIContextService.getInstance(FacesContext.getCurrentInstance()).notifyBeans(); } /** * @return Returns the currently browsing node Id. */ public String getCurrentNodeId() { return this.currentNodeId; } /** * Set the node Id of the current folder/space container node. *

* Setting this value causes the UI to update and display the specified node as current. * * @param currentNodeId The currently browsing node Id. */ public void setCurrentNodeId(String currentNodeId) { if (s_logger.isDebugEnabled()) s_logger.debug("Setting current node id to: " + currentNodeId); if (currentNodeId == null) { throw new AlfrescoRuntimeException("Can not set the current node id to null"); } // set the current Node Id for our UI context operations this.currentNodeId = currentNodeId; // clear other context that is based on or relevant to the Node id this.currentNode = null; this.searchContext = null; // inform any interested beans that the UI needs updating after this change UIContextService.getInstance(FacesContext.getCurrentInstance()).notifyBeans(); // clear current node context after the notify - this is to ensure that if any delegates // performed operations on the current node, that we have fresh data for the next View this.currentNode = null; } /** * @return true if the current node has a template view available */ public boolean getCurrentNodeHasTemplate() { boolean templateView = false; Node node = getCurrentNode(); if (node.hasAspect(ContentModel.ASPECT_TEMPLATABLE)) { NodeRef templateRef = (NodeRef)node.getProperties().get(ContentModel.PROP_TEMPLATE); templateView = (templateRef != null && this.nodeService.exists(templateRef)); } return templateView; } /** * @return the NodeRef.toString() for the current node template view if it has one set */ public String getCurrentNodeTemplate() { String strRef = null; if (getCurrentNodeHasTemplate() == true) { strRef = getCurrentNode().getProperties().get(ContentModel.PROP_TEMPLATE).toString(); } return strRef; } /** * Returns a model for use by a template on a space Dashboard page. * * @return model containing current current space info. */ public Map getTemplateModel() { HashMap model = new HashMap(1, 1.0f); FacesContext fc = FacesContext.getCurrentInstance(); TemplateNode spaceNode = new TemplateNode(getCurrentNode().getNodeRef(), Repository.getServiceRegistry(fc), new TemplateImageResolver() { public String resolveImagePathForName(String filename, boolean small) { return Utils.getFileTypeImage(filename, small); } }); model.put("space", spaceNode); return model; } /** * Clear state so that the current node properties cache for the next time they are requested */ public void resetCurrentNodeProperties() { this.currentNode = null; } /** * @return The Map of properties for the current Node. */ public Map getNodeProperties() { return getCurrentNode().getProperties(); } /** * @return The current Node object for UI context operations */ public Node getCurrentNode() { if (this.currentNode == null) { if (this.currentNodeId == null) { throw new AlfrescoRuntimeException("Cannot retrieve current Node if NodeId is null!"); } if (s_logger.isDebugEnabled()) s_logger.debug("Caching properties for node id: " + this.currentNodeId); NodeRef nodeRef; Node node; Map props; try { // build a node which components on the JSP page can bind too nodeRef = new NodeRef(Repository.getStoreRef(), this.currentNodeId); node = new Node(nodeRef); // early init properties for this node (by getProperties() call) // resolve icon in-case one has not been set props = node.getProperties(); } catch (InvalidNodeRefException refErr) { Utils.addErrorMessage(MessageFormat.format(Application.getMessage( FacesContext.getCurrentInstance(), ERROR_DELETED_FOLDER), new Object[] {this.currentNodeId}) ); nodeRef = new NodeRef(Repository.getStoreRef(), Application.getCompanyRootId()); node = new Node(nodeRef); props = node.getProperties(); } String icon = (String)props.get("app:icon"); props.put("icon", icon != null ? icon : NewSpaceWizard.SPACE_ICON_DEFAULT); Path path = this.nodeService.getPath(nodeRef); // resolve CIFS network folder location for this node DiskSharedDevice diskShare = cifsServer.getConfiguration().getPrimaryFilesystem(); if (diskShare != null) { ContentContext contentCtx = (ContentContext) diskShare.getContext(); NodeRef rootNode = contentCtx.getRootNode(); String cifsPath = Repository.getNamePath(this.nodeService, path, rootNode, "\\", "file:///" + getCIFSServerPath(diskShare)); node.getProperties().put("cifsPath", cifsPath); node.getProperties().put("cifsPathLabel", cifsPath.substring(8)); // strip file:/// part } this.currentNode = node; } return this.currentNode; } /** * @return Returns the breadcrumb handler elements representing the location path of the UI. */ public List getLocation() { if (this.location == null) { // init the location from the User object for the first time User user = Application.getCurrentUser(FacesContext.getCurrentInstance()); NodeRef homeSpaceRef = new NodeRef(Repository.getStoreRef(), user.getHomeSpaceId()); String homeSpaceName = Repository.getNameForNode(this.nodeService, homeSpaceRef); // set the current node to the users Home Space Id setCurrentNodeId(user.getHomeSpaceId()); // setup the breadcrumb with the same location List elements = new ArrayList(1); elements.add(new NavigationBreadcrumbHandler(homeSpaceRef, homeSpaceName)); setLocation(elements); } return this.location; } /** * @param location The UI location representation to set. */ public void setLocation(List location) { this.location = location; } /** * Sets up the dispatch context so that the navigation handler knows * what object is being acted upon * * @param node The node to be added to the dispatch context */ public void setupDispatchContext(Node node) { this.dispatchContext = node; } /** * Resets the dispatch context */ public void resetDispatchContext() { this.dispatchContext = null; } /** * Returns the node currently set in the dispatch context * * @return The node being dispatched or null if there is no * dispatch context */ public Node getDispatchContextNode() { return this.dispatchContext; } // ------------------------------------------------------------------------------ // Navigation action event handlers /** * Action handler to toggle the expanded state of the shelf. * The panel component wrapping the shelf area of the UI is value bound to the shelfExpanded property. */ public void toggleShelf(ActionEvent event) { this.shelfExpanded = !this.shelfExpanded; } /** * Action handler called after a Shelf Group has had its expanded state toggled by the user */ public void shelfGroupToggled(ActionEvent event) { UIShelf.ShelfEvent shelfEvent = (UIShelf.ShelfEvent)event; this.shelfItemExpanded[shelfEvent.Index] = shelfEvent.Expanded; } /** * Action to change the toolbar location * Currently this will changed the location from Company to the users Home space */ public void toolbarLocationChanged(ActionEvent event) { FacesContext context = FacesContext.getCurrentInstance(); try { UIModeList locationList = (UIModeList)event.getComponent(); String location = locationList.getValue().toString(); setToolbarLocation(location); if (LOCATION_COMPANY.equals(location)) { List elements = new ArrayList(1); NodeRef companyRootRef = new NodeRef(Repository.getStoreRef(), Application.getCompanyRootId()); String companySpaceName = Repository.getNameForNode(this.nodeService, companyRootRef); elements.add(new NavigationBreadcrumbHandler(companyRootRef, companySpaceName)); setLocation(elements); setCurrentNodeId(companyRootRef.getId()); } else if (LOCATION_HOME.equals(location)) { List elements = new ArrayList(1); String homeSpaceId = Application.getCurrentUser(context).getHomeSpaceId(); NodeRef homeSpaceRef = new NodeRef(Repository.getStoreRef(), homeSpaceId); String homeSpaceName = Repository.getNameForNode(this.nodeService, homeSpaceRef); elements.add(new NavigationBreadcrumbHandler(homeSpaceRef, homeSpaceName)); setLocation(elements); setCurrentNodeId(homeSpaceRef.getId()); } // we need to force a navigation to refresh the browse screen breadcrumb context.getApplication().getNavigationHandler().handleNavigation(context, null, "browse"); } catch (InvalidNodeRefException refErr) { Utils.addErrorMessage(MessageFormat.format(Application.getMessage( FacesContext.getCurrentInstance(), Repository.ERROR_NOHOME), Application.getCurrentUser(context).getHomeSpaceId()), refErr ); } catch (Exception err) { Utils.addErrorMessage(MessageFormat.format(Application.getMessage( FacesContext.getCurrentInstance(), Repository.ERROR_GENERIC), err.getMessage()), err); } } /** * @param diskShare Filesystem shared device * @return CIFS server path as network style string label */ public String getCIFSServerPath(DiskSharedDevice diskShare) { if (this.cifsServerPath == null) { StringBuilder buf = new StringBuilder(24); String serverName = this.cifsServer.getConfiguration().getServerName(); if (serverName != null && serverName.length() != 0) { buf.append("\\\\") .append(serverName) .append("\\"); buf.append(diskShare.getName()); } this.cifsServerPath = buf.toString(); } return this.cifsServerPath; } // ------------------------------------------------------------------------------ // Private helpers /** * Initialise default values from client configuration */ private void initFromClientConfig() { this.clientConfig = (ClientConfigElement)this.configService.getGlobalConfig().getConfigElement( ClientConfigElement.CONFIG_ELEMENT_ID); this.helpUrl = clientConfig.getHelpUrl(); } // ------------------------------------------------------------------------------ // Inner classes /** * Class to handle breadcrumb interaction for top-level navigation pages */ public class NavigationBreadcrumbHandler implements IRepoBreadcrumbHandler { private static final long serialVersionUID = 4833194653193016638L; /** * Constructor * * @param label Element label */ public NavigationBreadcrumbHandler(NodeRef ref, String label) { this.label = label; this.ref = ref; } /** * @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) */ public String navigationOutcome(UIBreadcrumb breadcrumb) { // set the current node to the specified top level node ID FacesContext fc = FacesContext.getCurrentInstance(); setCurrentNodeId(ref.getId()); setLocation( (List)breadcrumb.getValue() ); // setup the dispatch context setupDispatchContext(new Node(ref)); if (fc.getViewRoot().getViewId().equals(BrowseBean.BROWSE_VIEW_ID)) { return null; } else { return "browse"; } } public NodeRef getNodeRef() { return this.ref; } private String label; private NodeRef ref; } // ------------------------------------------------------------------------------ // Private data private static Logger s_logger = Logger.getLogger(NavigationBean.class); /** constant values used by the toolbar location modelist control */ private static final String LOCATION_COMPANY = "company"; private static final String LOCATION_HOME = "home"; private static final String ERROR_DELETED_FOLDER = "error_deleted_folder"; /** The NodeService to be used by the bean */ private NodeService nodeService; /** The SearchService to be used by the bean */ private SearchService searchService; /** NamespaceService bean reference */ private NamespaceService namespaceService; /** CIFSServer bean reference */ private CIFSServer cifsServer; /** CIFS content disk driver bean reference */ private ContentDiskInterface contentDiskDriver; /** ConfigService bean reference */ private ConfigService configService; /** Client configuration object */ private ClientConfigElement clientConfig = null; /** Cached path to our CIFS server and top level node DIR */ private String cifsServerPath; /** Node Id we are using for UI context operations */ private String currentNodeId; /** Node we are using for UI context operations */ private Node currentNode = null; /** Node we are using for dispatching */ private Node dispatchContext = null; /** Current toolbar location */ private String toolbarLocation = LOCATION_HOME; /** Search context object we are currently using or null for no search */ private SearchContext searchContext; /** expanded state of the Shelf panel wrapper component */ private boolean shelfExpanded = true; /** expanded state of the Shelf item components */ private boolean[] shelfItemExpanded = new boolean[] {true, true, true, false, false}; /** list of the breadcrumb handler elements representing the location path of the UI */ private List location = null; /** The client Help file url */ private String helpUrl; }