result = this.parentContainerNodes;
-
- // we clear the member variable during invalidateComponents()
-
- return result;
- }
-
- /**
- * Determines whether the current space is a 'Sites' space
- *
- * @return true if the current space is a 'Sites' space
- */
- public boolean isSitesSpace()
- {
- boolean siteSpace = false;
-
- Node currentNode = this.navigator.getCurrentNode();
- if (currentNode != null)
- {
- // check the type of the node to see if it is a 'site' related space
- QName currentNodeType = currentNode.getType();
-
- if (SiteModel.TYPE_SITES.isMatch(currentNodeType) ||
- SiteModel.TYPE_SITE.isMatch(currentNodeType) ||
- getDictionaryService().isSubClass(currentNodeType, SiteModel.TYPE_SITE))
- {
- siteSpace = true;
- }
- }
-
- return siteSpace;
- }
-
- /**
- * Returns the HTML to display if a space is a 'Sites' space
- *
- * @return The HTML to display
- */
- public String getSitesSpaceWarningHTML()
- {
- FacesContext context = FacesContext.getCurrentInstance();
- String contextPath = context.getExternalContext().getRequestContextPath();
- StringBuilder html = new StringBuilder();
-
- try
- {
- html.append("");
- html.append(" | ");
-
- StringWriter writer = new StringWriter();
- PanelGenerator.generatePanelStart(writer, contextPath, "yellowInner", "#ffffcc");
- html.append(writer.toString());
-
- html.append("");
- html.append("");
- html.append(";
- html.append(contextPath);
- html.append("/images/icons/warning.gif) | ");
- html.append("");
- html.append(Application.getMessage(context, "sites_space_warning"));
- html.append(" | ");
-
- writer = new StringWriter();
- PanelGenerator.generatePanelEnd(writer, contextPath, "yellowInner");
- html.append(writer.toString());
-
- html.append(" | |
");
- }
- catch (IOException ioe)
- {
- logger.error(ioe);
- }
-
- return html.toString();
- }
-
- /**
- * Setup the common properties required at data-binding time.
- *
- * 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.
- *
- * 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);
-
- if (ApplicationModel.TYPE_FILELINK.equals(node.getType()))
- {
- node.addPropertyResolver("downloadUrl", this.resolverLinkDownload);
- }
- else
- {
- node.addPropertyResolver("downloadUrl", this.resolverDownload);
- }
-
- 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)
- {
- if (this.nodeEventListeners == null)
- {
- this.nodeEventListeners = new HashSet();
- }
- this.nodeEventListeners.add(listener);
- }
-
- /**
- * Remove a listener from the list of those called by BrowseBean
- */
- public void removeNodeEventListener(NodeEventListener listener)
- {
- if (this.nodeEventListeners != null)
- {
- this.nodeEventListeners.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());
-
- // browse to appropriate view
- FacesContext fc = FacesContext.getCurrentInstance();
- String outcome = null;
- String viewId = fc.getViewRoot().getViewId();
- if (viewId.equals(BROWSE_VIEW_ID) == false && viewId.equals(CATEGORY_VIEW_ID) == false)
- {
- outcome = "browse";
- }
- fc.getApplication().getNavigationHandler().handleNavigation(fc, null, outcome);
- }
- 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.getNodeService().getRootNode(Repository.getStoreRef());
- }
- else
- {
- // build a NodeRef for the specified Id and our store
- parentRef = new NodeRef(Repository.getStoreRef(), parentNodeId);
- }
-
- List children = this.getFileFolderService().list(parentRef);
- this.containerNodes = new ArrayList(children.size());
- this.contentNodes = new ArrayList(children.size());
-
- // in case of dynamic config, only lookup once
- Set nodeEventListeners = getNodeEventListeners();
-
- for (FileInfo fileInfo : children)
- {
- // create our Node representation from the NodeRef
- NodeRef nodeRef = fileInfo.getNodeRef();
-
- // find it's type so we can see if it's a node we are interested in
- QName type = this.getNodeService().getType(nodeRef);
-
- // make sure the type is defined in the data dictionary
- TypeDefinition typeDef = this.getDictionaryService().getType(type);
-
- if (typeDef != null)
- {
- MapNode node = null;
-
- // look for File content node
- if (this.getDictionaryService().isSubClass(type, ContentModel.TYPE_CONTENT))
- {
- // create our Node representation
- node = new MapNode(nodeRef, this.getNodeService(), fileInfo.getProperties());
- setupCommonBindingProperties(node);
-
- this.contentNodes.add(node);
- }
- // look for Space folder node
- else if (this.getDictionaryService().isSubClass(type, ContentModel.TYPE_FOLDER) == true &&
- this.getDictionaryService().isSubClass(type, ContentModel.TYPE_SYSTEM_FOLDER) == false)
- {
- // create our Node representation
- node = new MapNode(nodeRef, this.getNodeService(), fileInfo.getProperties());
- node.addPropertyResolver("icon", this.resolverSpaceIcon);
- node.addPropertyResolver("smallIcon", this.resolverSmallIcon);
-
- this.containerNodes.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.getNodeService(), fileInfo.getProperties());
- // only display the user has the permissions to navigate to the target of the link
- NodeRef destRef = (NodeRef)node.getProperties().get(ContentModel.PROP_LINK_DESTINATION);
- if (destRef != null && new Node(destRef).hasPermission(PermissionService.READ) == true)
- {
- node.addPropertyResolver("url", this.resolverLinkUrl);
- node.addPropertyResolver("downloadUrl", this.resolverLinkDownload);
- node.addPropertyResolver("webdavUrl", this.resolverLinkWebdavUrl);
- node.addPropertyResolver("cifsPath", this.resolverLinkCifsPath);
- node.addPropertyResolver("fileType16", this.resolverFileType16);
- node.addPropertyResolver("fileType32", this.resolverFileType32);
- 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.getNodeService(), fileInfo.getProperties());
- // only display the user has the permissions to navigate to the target of the link
- NodeRef destRef = (NodeRef)node.getProperties().get(ContentModel.PROP_LINK_DESTINATION);
- if (destRef != null && new Node(destRef).hasPermission(PermissionService.READ) == 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 : nodeEventListeners)
- {
- listener.created(node, type);
- }
- }
- }
- else
- {
- if (logger.isWarnEnabled())
- 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.emptyList();
- this.contentNodes = Collections.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.emptyList();
- this.contentNodes = Collections.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.emptyList();
- this.contentNodes = Collections.emptyList();
- return;
- }
-
- // perform the search against the repo
- UserTransaction tx = null;
- ResultSet results = null;
- try
- {
- tx = Repository.getUserTransaction(FacesContext.getCurrentInstance(), true);
- tx.begin();
-
- // build up the search parameters
- SearchParameters sp = new SearchParameters();
- sp.setLanguage(SearchService.LANGUAGE_LUCENE);
- sp.setQuery(query);
- sp.addStore(Repository.getStoreRef());
-
- // limit search results size as configured
- int searchLimit = Application.getClientConfig(FacesContext.getCurrentInstance()).getSearchMaxResults();
- if (searchLimit > 0)
- {
- sp.setLimitBy(LimitBy.FINAL_SIZE);
- sp.setLimit(searchLimit);
- }
-
- results = this.getSearchService().query(sp);
- if (logger.isDebugEnabled())
- logger.debug("Search results returned: " + results.length());
-
- // create a list of items from the results
- this.containerNodes = new ArrayList(results.length());
- this.contentNodes = new ArrayList(results.length());
- if (results.length() != 0)
- {
- // in case of dynamic config, only lookup once
- Set nodeEventListeners = getNodeEventListeners();
-
- for (ResultSetRow row: results)
- {
- NodeRef nodeRef = row.getNodeRef();
-
- if (this.getNodeService().exists(nodeRef))
- {
- // find it's type so we can see if it's a node we are interested in
- QName type = this.getNodeService().getType(nodeRef);
-
- // make sure the type is defined in the data dictionary
- TypeDefinition typeDef = this.getDictionaryService().getType(type);
-
- if (typeDef != null)
- {
- MapNode node = null;
-
- // look for Space or File nodes
- if (this.getDictionaryService().isSubClass(type, ContentModel.TYPE_FOLDER) &&
- this.getDictionaryService().isSubClass(type, ContentModel.TYPE_SYSTEM_FOLDER) == false)
- {
- // create our Node representation
- node = new MapNode(nodeRef, this.getNodeService(), 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.getDictionaryService().isSubClass(type, ContentModel.TYPE_CONTENT))
- {
- // create our Node representation
- node = new MapNode(nodeRef, this.getNodeService(), 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.getNodeService(), false);
- // only display the user has the permissions to navigate to the target of the link
- NodeRef destRef = (NodeRef)node.getProperties().get(ContentModel.PROP_LINK_DESTINATION);
- if (new Node(destRef).hasPermission(PermissionService.READ) == true)
- {
- node.addPropertyResolver("url", this.resolverLinkUrl);
- node.addPropertyResolver("downloadUrl", this.resolverLinkDownload);
- node.addPropertyResolver("webdavUrl", this.resolverLinkWebdavUrl);
- node.addPropertyResolver("cifsPath", this.resolverLinkCifsPath);
- node.addPropertyResolver("fileType16", this.resolverFileType16);
- node.addPropertyResolver("fileType32", this.resolverFileType32);
- 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.getNodeService(), false);
- // only display the user has the permissions to navigate to the target of the link
- NodeRef destRef = (NodeRef)node.getProperties().get(ContentModel.PROP_LINK_DESTINATION);
- if (new Node(destRef).hasPermission(PermissionService.READ) == true)
- {
- 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 : nodeEventListeners)
- {
- listener.created(node, type);
- }
- }
- }
- else
- {
- if (logger.isWarnEnabled())
- logger.warn("Found invalid object in database: id = " + nodeRef + ", type = " + type);
- }
- }
- else
- {
- if (logger.isWarnEnabled())
- 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.emptyList();
- this.contentNodes = Collections.emptyList();
- try { if (tx != null) {tx.rollback();} } catch (Exception tex) {}
- }
- catch (SearcherException serr)
- {
- logger.info("Search failed for: " + query, serr);
- Utils.addErrorMessage(Application.getMessage(
- FacesContext.getCurrentInstance(), Repository.ERROR_QUERY));
- this.containerNodes = Collections.emptyList();
- this.contentNodes = Collections.emptyList();
- try { if (tx != null) {tx.rollback();} } catch (Exception tex) {}
- }
- catch (Throwable err)
- {
- Utils.addErrorMessage(MessageFormat.format(Application.getMessage(
- FacesContext.getCurrentInstance(), Repository.ERROR_SEARCH), new Object[] {err.getMessage()}), err );
- this.containerNodes = Collections.emptyList();
- this.contentNodes = Collections.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() {
- private static final long serialVersionUID = 4048859853585650378L;
-
- public Object get(Node node) {
- return DownloadContentServlet.generateDownloadURL(node.getNodeRef(), node.getName());
- }
- };
-
- public NodePropertyResolver resolverUrl = new NodePropertyResolver() {
- private static final long serialVersionUID = -5264085143622470386L;
-
- public Object get(Node node) {
- return DownloadContentServlet.generateBrowserURL(node.getNodeRef(), node.getName());
- }
- };
-
- public NodePropertyResolver resolverWebdavUrl = new NodePropertyResolver() {
- private static final long serialVersionUID = 9127234483419089006L;
-
- public Object get(Node node) {
- return Utils.generateURL(FacesContext.getCurrentInstance(), node, URLMode.WEBDAV);
- }
- };
-
- public NodePropertyResolver resolverCifsPath = new NodePropertyResolver() {
- private static final long serialVersionUID = -5804924617772163104L;
-
- public Object get(Node node) {
- return Utils.generateURL(FacesContext.getCurrentInstance(), node, URLMode.CIFS);
- }
- };
-
- public NodePropertyResolver resolverLinkDownload = new NodePropertyResolver() {
- private static final long serialVersionUID = 7208696954599958859L;
-
- public Object get(Node node) {
- NodeRef destRef = (NodeRef)node.getProperties().get(ContentModel.PROP_LINK_DESTINATION);
- if (getNodeService().exists(destRef) == true)
- {
- String destName = Repository.getNameForNode(getNodeService(), destRef);
- return DownloadContentServlet.generateDownloadURL(destRef, destName);
- }
- else
- {
- // TODO: link object is missing - navigate to a page with appropriate message
- return "#";
- }
- }
- };
-
- public NodePropertyResolver resolverLinkUrl = new NodePropertyResolver() {
- private static final long serialVersionUID = -1280702397805414147L;
-
- public Object get(Node node) {
- NodeRef destRef = (NodeRef)node.getProperties().get(ContentModel.PROP_LINK_DESTINATION);
- if (getNodeService().exists(destRef) == true)
- {
- String destName = Repository.getNameForNode(getNodeService(), 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() {
- private static final long serialVersionUID = -3097558079118837397L;
-
- public Object get(Node node) {
- NodeRef destRef = (NodeRef)node.getProperties().get(ContentModel.PROP_LINK_DESTINATION);
- if (getNodeService().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() {
- private static final long serialVersionUID = 673020173327603487L;
-
- public Object get(Node node) {
- NodeRef destRef = (NodeRef)node.getProperties().get(ContentModel.PROP_LINK_DESTINATION);
- if (getNodeService().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() {
- private static final long serialVersionUID = -2690520488415178029L;
-
- public Object get(Node node) {
- return FileTypeImageUtils.getFileTypeImage(node.getName(), true);
- }
- };
-
- public NodePropertyResolver resolverFileType32 = new NodePropertyResolver() {
- private static final long serialVersionUID = 1991254398502584389L;
-
- public Object get(Node node) {
- return FileTypeImageUtils.getFileTypeImage(node.getName(), false);
- }
- };
-
- public NodePropertyResolver resolverPath = new NodePropertyResolver() {
- private static final long serialVersionUID = 8008094870888545035L;
-
- public Object get(Node node) {
- return node.getNodePath();
- }
- };
-
- public NodePropertyResolver resolverDisplayPath = new NodePropertyResolver() {
- private static final long serialVersionUID = -918422848579179425L;
-
- 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() {
- private static final long serialVersionUID = -5644418026591098018L;
-
- 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() {
- private static final long serialVersionUID = -150483121767183580L;
-
- 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() {
- private static final long serialVersionUID = -8864267975247235172L;
-
- public Object get(Node node) {
- ContentData content = (ContentData)node.getProperties().get(ContentModel.PROP_CONTENT);
- return (content != null ? content.getMimetype() : null);
- }
- };
-
- public NodePropertyResolver resolverEncoding = new NodePropertyResolver() {
- private static final long serialVersionUID = -1130974681844152101L;
-
- public Object get(Node node) {
- ContentData content = (ContentData)node.getProperties().get(ContentModel.PROP_CONTENT);
- return (content != null ? content.getEncoding() : null);
- }
- };
-
- public NodePropertyResolver resolverSize = new NodePropertyResolver() {
- private static final long serialVersionUID = 1273541660444385276L;
-
- 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() {
-
- private static final long serialVersionUID = 5412446489528560367L;
-
- public Object get(Node node) {
-
- String lang = null;
-
- if (node.getAspects().contains(ContentModel.ASPECT_MULTILINGUAL_DOCUMENT))
- {
- Locale locale = null;
-
- if(node.hasAspect(ContentModel.ASPECT_MULTILINGUAL_EMPTY_TRANSLATION))
- {
- // if the translation is empty, the lang of the content is the lang of it's pivot.
- NodeRef pivot = getMultilingualContentService().getPivotTranslation(node.getNodeRef());
- locale = (Locale) getNodeService().getProperty(pivot, ContentModel.PROP_LOCALE);
- }
- else
- {
- 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
- String currentNodeId = this.navigator.getCurrentNodeId();
- this.navigator.setCurrentNodeId(currentNodeId);
-
- // setup dispatch context so we go back to the right place
- NodeRef currentNodeRef = new NodeRef(Repository.getStoreRef(), currentNodeId);
- Node currentNode = new Node(currentNodeRef);
- this.navigator.setupDispatchContext(currentNode);
- }
-
- /**
- * 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 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.getNodeService().getType(ref)))
- {
- ref = (NodeRef)this.getNodeService().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 = getNodeService().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 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)
- {
- FacesContext fc = FacesContext.getCurrentInstance();
- NodeRef companyRootRef = new NodeRef(Repository.getStoreRef(), Application.getCompanyRootId(fc));
- if (node.getNodeRef().equals(companyRootRef))
- {
- message = Application.getMessage(fc, 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 params = link.getParameterMap();
- setupContentAction(params.get("id"), true);
- }
-
- /**
- * Action event called by all actions that need to setup a Multilingual 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 setupMLContainerContentAction(ActionEvent event)
- {
- UIActionLink link = (UIActionLink)event.getComponent();
- Map params = link.getParameterMap();
-
- String id = params.get("id");
-
- NodeRef translation = new NodeRef(Repository.getStoreRef(), id);
-
- // remember the bean from which the action comes
- FacesContext fc = FacesContext.getCurrentInstance();
- DocumentDetailsDialog docDetails = (DocumentDetailsDialog)FacesHelper.getManagedBean(fc, "DocumentDetailsDialog");
- docDetails.setTranslationDocument(new MapNode(translation));
- MultilingualManageDialog mmDialog = (MultilingualManageDialog)FacesHelper.getManagedBean(fc, "MultilingualManageDialog");
- mmDialog.setTranslationDocument(docDetails.getTranslationDocument());
-
- // set the ml container as the current document
- NodeRef mlContainer = getMultilingualContentService().getTranslationContainer(translation);
-
- setupContentAction(mlContainer.getId(), 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 MapNode(ref);
-
- // store the URL to for downloading the content
- if (ApplicationModel.TYPE_FILELINK.equals(node.getType()))
- {
- node.addPropertyResolver("url", this.resolverLinkDownload);
- node.addPropertyResolver("downloadUrl", this.resolverLinkDownload);
- }
- else
- {
- node.addPropertyResolver("url", this.resolverDownload);
- node.addPropertyResolver("downloadUrl", this.resolverDownload);
- }
- node.addPropertyResolver("webdavUrl", this.resolverWebdavUrl);
- node.addPropertyResolver("cifsPath", this.resolverCifsPath);
- node.addPropertyResolver("fileType32", this.resolverFileType32);
- node.addPropertyResolver("mimetype", this.resolverMimetype);
- node.addPropertyResolver("encoding", this.resolverEncoding);
- node.addPropertyResolver("size", this.resolverSize);
- node.addPropertyResolver("lang", this.resolverLang);
-
- for (NodeEventListener listener : getNodeEventListeners())
- {
- listener.created(node, node.getType());
- }
-
- // get hold of the DocumentDetailsDialog and reset it
- DocumentDetailsDialog docDetails = (DocumentDetailsDialog)FacesContext.getCurrentInstance().
- getExternalContext().getSessionMap().get("DocumentDetailsDialog");
- 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}) );
- throw new AbortProcessingException("Invalid node reference");
- }
- }
- 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 location = navigator.getLocation();
- IBreadcrumbHandler handler = location.get(location.size() - 1);
- if (handler instanceof IRepoBreadcrumbHandler)
- {
- // see if the current breadcrumb location is our node
- if ( ((IRepoBreadcrumbHandler)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 IRepoBreadcrumbHandler)
- {
- // change the current node Id
- navigator.setCurrentNodeId(((IRepoBreadcrumbHandler)handler).getNodeRef().getId());
- }
- else
- {
- // if we don't have access to the NodeRef to go to next then go to the home space
- navigator.processToolbarLocation(NavigationBean.LOCATION_HOME, false);
- }
- }
- else
- {
- // if there is no breadcrumb left go to the user's home space
- navigator.processToolbarLocation(NavigationBean.LOCATION_HOME, false);
- }
- }
- }
- }
-
- /**
- * 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()
- {
- // TODO - review implications of these default values on dynamic/MT client: viewsConfig & browseViewMode, as well as page size content/spaces ...
- 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 getNodeEventListeners()
- {
- if ((this.nodeEventListeners == null) || (Application.isDynamicConfig(FacesContext.getCurrentInstance())))
- {
- Set allNodeEventListeners = new HashSet();
-
- if (Application.isDynamicConfig(FacesContext.getCurrentInstance()) && (this.nodeEventListeners != null))
- {
- // for dynamic config, can add/remove node event listeners dynamically ...
- // however, in case anyone is using public methods (add/removeNodeEventListener)
- // we merge list here with list returned from the config
- allNodeEventListeners.addAll(this.nodeEventListeners);
- }
-
- 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)
- {
- allNodeEventListeners.add((NodeEventListener)bean);
- }
- }
- }
- }
- }
-
- if (Application.isDynamicConfig(FacesContext.getCurrentInstance()))
- {
- return allNodeEventListeners;
- }
- else
- {
- this.nodeEventListeners = allNodeEventListeners;
- }
- }
- 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 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 newLocation = new ArrayList(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 params = link.getParameterMap();
-
- String ref = params.get("ref");
- if (ref != null && ref.length() > 0)
- {
- NodeRef nodeRef = new NodeRef(ref);
-
- NodeRef workingCopyNodeRef = getCheckOutCheckInService().getWorkingCopy(nodeRef);
-
- if (workingCopyNodeRef != null)
- {
- // if node has a working copy setup error message and return
- Utils.addErrorMessage(MessageFormat.format(Application.getMessage(
- FacesContext.getCurrentInstance(), MSG_CANNOT_DELETE_NODE_HAS_WORKING_COPY),
- new Object[] {getNodeService().getProperty(nodeRef, ContentModel.PROP_NAME)}));
- return;
- }
-
- // if there isn't a working copy go to normal delete dialog
- boolean hasMultipleParents = false;
- boolean showDeleteAssocDialog = false;
-
- // get type of node being deleted
- Node node = this.getDocument();
- QName type = node.getType();
- TypeDefinition typeDef = this.dictionaryService.getType(type);
-
- // determine if the node being delete has multiple parents
- if (!type.equals(ContentModel.TYPE_MULTILINGUAL_CONTAINER) &&
- !node.hasAspect(ContentModel.ASPECT_MULTILINGUAL_EMPTY_TRANSLATION) &&
- !node.hasAspect(ContentModel.ASPECT_MULTILINGUAL_DOCUMENT) &&
- !type.equals(ContentModel.TYPE_LINK) &&
- !this.dictionaryService.isSubClass(typeDef.getName(), ContentModel.TYPE_LINK))
- {
- List parents = this.nodeService.getParentAssocs(node.getNodeRef(),
- ContentModel.ASSOC_CONTAINS, RegexQNamePattern.MATCH_ALL);
- if (parents != null && parents.size() > 1)
- {
- hasMultipleParents = true;
- }
- }
-
- // determine which delete dialog to display
- if (this.navigator.getSearchContext() == null && hasMultipleParents)
- {
- // if we are not in a search and the node has multiple parents
- // see if the current node has the primary parent association
- NodeRef parentSpace = this.navigator.getCurrentNode().getNodeRef();
- ChildAssociationRef assoc = this.nodeService.getPrimaryParent(node.getNodeRef());
-
- // show delete assoc dialog if the current space is not the primary parent for the node
- showDeleteAssocDialog = !parentSpace.equals(assoc.getParentRef());
- }
-
- // show the appropriate dialog
- FacesContext fc = FacesContext.getCurrentInstance();
- if (showDeleteAssocDialog)
- {
- fc.getApplication().getNavigationHandler().handleNavigation(fc, null, "dialog:deleteFileAssoc");
- }
- else
- {
- final Map dialogParams = new HashMap(1);
- dialogParams.put("hasMultipleParents", Boolean.toString(hasMultipleParents));
- Application.getDialogManager().setupParameters(dialogParams);
- fc.getApplication().getNavigationHandler().handleNavigation(fc, null, "dialog:deleteFile");
- }
- }
- }
-
- /**
- * Handles the deleteSpace action by deciding which delete dialog to display
- */
- public void deleteSpace(ActionEvent event)
- {
- setupDeleteAction(event);
-
- boolean hasMultipleParents = false;
- boolean showDeleteAssocDialog = false;
-
- // determine if the node being delete has multiple parents
- Node node = this.getActionSpace();
- List parents = this.nodeService.getParentAssocs(node.getNodeRef(),
- ContentModel.ASSOC_CONTAINS, RegexQNamePattern.MATCH_ALL);
- if (parents != null && parents.size() > 1)
- {
- hasMultipleParents = true;
- }
-
- // determine which delete dialog to display
- if (this.navigator.getSearchContext() == null && hasMultipleParents)
- {
- // if we are not in a search and the node has multiple parents
- // see if the current node has the primary parent association
- NodeRef parentSpace = this.navigator.getCurrentNode().getNodeRef();
- ChildAssociationRef assoc = this.nodeService.getPrimaryParent(node.getNodeRef());
-
- // show delete assoc dialog if the current space is not the primary parent for the node
- showDeleteAssocDialog = !parentSpace.equals(assoc.getParentRef());
- }
-
- // show the appropriate dialog
- FacesContext fc = FacesContext.getCurrentInstance();
- if (showDeleteAssocDialog)
- {
- fc.getApplication().getNavigationHandler().handleNavigation(fc, null, "dialog:deleteSpaceAssoc");
- }
- else
- {
- final Map dialogParams = new HashMap(1);
- dialogParams.put("hasMultipleParents", Boolean.toString(hasMultipleParents));
- Application.getDialogManager().setupParameters(dialogParams);
- fc.getApplication().getNavigationHandler().handleNavigation(fc, null, "dialog:deleteSpace");
- }
- }
-
- // ------------------------------------------------------------------------------
- // 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
-
- private static Log logger = LogFactory.getLog(BrowseBean.class);
-
- /** Browse screen view ID */
- public static final String BROWSE_VIEW_ID = "/jsp/browse/browse.jsp";
- public static final String CATEGORY_VIEW_ID = "/jsp/browse/category-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";
- public static final String MSG_SEARCH_MINIMUM = "search_minimum";
- private static final String MSG_CANNOT_DELETE_NODE_HAS_WORKING_COPY = "cannot_delete_node_has_working_copy";
-
- /** The NodeService to be used by the bean */
- private transient NodeService nodeService;
-
- /** The CheckOutCheckInService to be used by the bean */
- private transient CheckOutCheckInService checkOutCheckInService;
-
- /** The SearchService to be used by the bean */
- private transient SearchService searchService;
-
- /** The LockService to be used by the bean */
- private transient LockService lockService;
-
- /** The NavigationBean bean reference */
- protected NavigationBean navigator;
-
- /** The UserPreferencesBean to be used by the bean */
- protected UserPreferencesBean userPreferencesBean;
-
- /** The DictionaryService bean reference */
- private transient DictionaryService dictionaryService;
-
- /** The file folder service */
- private transient FileFolderService fileFolderService;
-
- /** The Multilingual Content Service */
- private transient MultilingualContentService multilingualContentService;
-
- /** Views configuration object */
- protected ViewsConfigElement viewsConfig = null;
-
- /** Listeners for Node events */
- protected Set nodeEventListeners = null;
-
- /** Collapsable Panel state */
- private Map panels = new HashMap(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 containerNodes = null;
- protected List contentNodes = null;
- protected List parentContainerNodes = 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;
+/*
+ * Copyright (C) 2005-2010 Alfresco Software Limited.
+ *
+ * This file is part of Alfresco
+ *
+ * Alfresco is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Alfresco 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with Alfresco. If not, see .
+ */
+package org.alfresco.web.bean;
+
+import java.io.IOException;
+import java.io.Serializable;
+import java.io.StringWriter;
+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.AbortProcessingException;
+import javax.faces.event.ActionEvent;
+import javax.transaction.UserTransaction;
+
+import org.alfresco.model.ApplicationModel;
+import org.alfresco.model.ContentModel;
+import org.alfresco.repo.search.SearcherException;
+import org.alfresco.repo.site.SiteModel;
+import org.alfresco.repo.web.scripts.FileTypeImageUtils;
+import org.alfresco.service.cmr.coci.CheckOutCheckInService;
+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.ml.MultilingualContentService;
+import org.alfresco.service.cmr.model.FileFolderService;
+import org.alfresco.service.cmr.model.FileInfo;
+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.cmr.security.PermissionService;
+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.content.DocumentDetailsDialog;
+import org.alfresco.web.bean.ml.MultilingualManageDialog;
+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.search.SearchContext;
+import org.alfresco.web.bean.spaces.CreateSpaceWizard;
+import org.alfresco.web.bean.users.UserPreferencesBean;
+import org.alfresco.web.config.ClientConfigElement;
+import org.alfresco.web.config.ViewsConfigElement;
+import org.alfresco.web.ui.common.PanelGenerator;
+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.UIPanel.ExpandedEvent;
+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.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.springframework.extensions.config.Config;
+import org.springframework.extensions.config.ConfigElement;
+import org.springframework.extensions.config.ConfigService;
+
+
+/**
+ * Bean providing properties and behaviour for the main folder/document browse screen and
+ * search results screens.
+ *
+ * @author Kevin Roast
+ */
+public class BrowseBean implements IContextListener, Serializable
+{
+ private static final long serialVersionUID = -3234262484615161360L;
+
+ /** 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;
+ }
+
+ protected NodeService getNodeService()
+ {
+ if (nodeService == null)
+ {
+ nodeService = Repository.getServiceRegistry(FacesContext.getCurrentInstance()).getNodeService();
+ }
+ return nodeService;
+ }
+
+ /**
+ * @param checkOutCheckInService The service for check-in and check-out.
+ */
+ public void setCheckOutCheckInService(CheckOutCheckInService checkOutCheckInService)
+ {
+ this.checkOutCheckInService = checkOutCheckInService;
+ }
+
+ protected CheckOutCheckInService getCheckOutCheckInService()
+ {
+ if (checkOutCheckInService == null)
+ {
+ checkOutCheckInService = Repository.getServiceRegistry(FacesContext.getCurrentInstance()).getCheckOutCheckInService();
+ }
+ return checkOutCheckInService;
+ }
+
+ /**
+ * @param searchService The Searcher to set.
+ */
+ public void setSearchService(SearchService searchService)
+ {
+ this.searchService = searchService;
+ }
+
+ protected SearchService getSearchService()
+ {
+ if (searchService == null)
+ {
+ searchService = Repository.getServiceRegistry(FacesContext.getCurrentInstance()).getSearchService();
+ }
+ return 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;
+ }
+
+ protected LockService getLockService()
+ {
+ if (lockService == null)
+ {
+ lockService = Repository.getServiceRegistry(FacesContext.getCurrentInstance()).getLockService();
+ }
+ return 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;
+ }
+
+ protected DictionaryService getDictionaryService()
+ {
+ if (dictionaryService == null)
+ {
+ dictionaryService = Repository.getServiceRegistry(FacesContext.getCurrentInstance()).getDictionaryService();
+ }
+ return dictionaryService;
+ }
+
+ /**
+ * @param multilingualContentService The Multilingual Content Service to set.
+ */
+ public void setMultilingualContentService(MultilingualContentService multilingualContentService)
+ {
+ this.multilingualContentService = multilingualContentService;
+ }
+
+ protected MultilingualContentService getMultilingualContentService()
+ {
+ if (multilingualContentService == null)
+ {
+ multilingualContentService = Repository.getServiceRegistry(FacesContext.getCurrentInstance()).getMultilingualContentService();
+ }
+ return multilingualContentService;
+ }
+
+ /**
+ * @param fileFolderService The FileFolderService to set.
+ */
+ public void setFileFolderService(FileFolderService fileFolderService)
+ {
+ this.fileFolderService = fileFolderService;
+ }
+
+ protected FileFolderService getFileFolderService()
+ {
+ if (fileFolderService == null)
+ {
+ fileFolderService = Repository.getServiceRegistry(FacesContext.getCurrentInstance()).getFileFolderService();
+ }
+ return 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 static int getMinimumSearchLength()
+ {
+ return Application.getClientConfig(FacesContext.getCurrentInstance()).getSearchMinimum();
+ }
+
+ /**
+ * @return Returns the panels expanded state map.
+ */
+ public Map getPanels()
+ {
+ return this.panels;
+ }
+
+ /**
+ * @param panels The panels expanded state map.
+ */
+ public void setPanels(Map 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 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 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 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 result = this.contentNodes;
+
+ // we clear the member variable during invalidateComponents()
+
+ return result;
+ }
+
+ /**
+ * Page accessed bean method to get the parent container nodes currently being browsed
+ *
+ * @return List of parent container Node objects for the current browse location
+ */
+ public List getParentNodes(NodeRef currNodeRef)
+ {
+ // As per AWC-1507 there are two scenarios for navigating to the space details. First
+ // scenario is to show space details of the current space. Second scenario is to show
+ // space details of a child space of the current space. For now, added an extra query
+ // so that existing context remains unaffected for second scenario, although it does
+ // mean that in first scenario there will be an extra query even though parentContainerNodes
+ // and containerNodes will contain the same list.
+
+ if (this.parentContainerNodes == null)
+ {
+ 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 = getNodeService().getPrimaryParent(currNodeRef).getParentRef();
+
+ List children = this.getFileFolderService().list(parentRef);
+ this.parentContainerNodes = new ArrayList(children.size());
+ for (FileInfo fileInfo : children)
+ {
+ // create our Node representation from the NodeRef
+ NodeRef nodeRef = fileInfo.getNodeRef();
+
+ // find it's type so we can see if it's a node we are interested in
+ QName type = this.getNodeService().getType(nodeRef);
+
+ // make sure the type is defined in the data dictionary
+ TypeDefinition typeDef = this.getDictionaryService().getType(type);
+
+ if (typeDef != null)
+ {
+ MapNode node = null;
+
+ // look for Space folder node
+ if (this.getDictionaryService().isSubClass(type, ContentModel.TYPE_FOLDER) == true &&
+ this.getDictionaryService().isSubClass(type, ContentModel.TYPE_SYSTEM_FOLDER) == false)
+ {
+ // create our Node representation
+ node = new MapNode(nodeRef, this.getNodeService(), fileInfo.getProperties());
+ node.addPropertyResolver("icon", this.resolverSpaceIcon);
+ node.addPropertyResolver("smallIcon", this.resolverSmallIcon);
+
+ this.parentContainerNodes.add(node);
+ }
+ else if (ApplicationModel.TYPE_FOLDERLINK.equals(type))
+ {
+ // create our Folder Link Node representation
+ node = new MapNode(nodeRef, this.getNodeService(), fileInfo.getProperties());
+ node.addPropertyResolver("icon", this.resolverSpaceIcon);
+ node.addPropertyResolver("smallIcon", this.resolverSmallIcon);
+
+ this.parentContainerNodes.add(node);
+ }
+ }
+ else
+ {
+ if (logger.isWarnEnabled())
+ 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.parentContainerNodes = Collections.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.parentContainerNodes = Collections.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 parent nodes: " + (endTime - startTime) + "ms");
+ }
+ }
+
+ List result = this.parentContainerNodes;
+
+ // we clear the member variable during invalidateComponents()
+
+ return result;
+ }
+
+ /**
+ * Determines whether the current space is a 'Sites' space
+ *
+ * @return true if the current space is a 'Sites' space
+ */
+ public boolean isSitesSpace()
+ {
+ boolean siteSpace = false;
+
+ Node currentNode = this.navigator.getCurrentNode();
+ if (currentNode != null)
+ {
+ // check the type of the node to see if it is a 'site' related space
+ QName currentNodeType = currentNode.getType();
+
+ if (SiteModel.TYPE_SITES.isMatch(currentNodeType) ||
+ SiteModel.TYPE_SITE.isMatch(currentNodeType) ||
+ getDictionaryService().isSubClass(currentNodeType, SiteModel.TYPE_SITE))
+ {
+ siteSpace = true;
+ }
+ }
+
+ return siteSpace;
+ }
+
+ /**
+ * Returns the HTML to display if a space is a 'Sites' space
+ *
+ * @return The HTML to display
+ */
+ public String getSitesSpaceWarningHTML()
+ {
+ FacesContext context = FacesContext.getCurrentInstance();
+ String contextPath = context.getExternalContext().getRequestContextPath();
+ StringBuilder html = new StringBuilder();
+
+ try
+ {
+ html.append("");
+ html.append(" | ");
+
+ StringWriter writer = new StringWriter();
+ PanelGenerator.generatePanelStart(writer, contextPath, "yellowInner", "#ffffcc");
+ html.append(writer.toString());
+
+ html.append("");
+ html.append("");
+ html.append(";
+ html.append(contextPath);
+ html.append("/images/icons/warning.gif) | ");
+ html.append("");
+ html.append(Application.getMessage(context, "sites_space_warning"));
+ html.append(" | ");
+
+ writer = new StringWriter();
+ PanelGenerator.generatePanelEnd(writer, contextPath, "yellowInner");
+ html.append(writer.toString());
+
+ html.append(" | |
");
+ }
+ catch (IOException ioe)
+ {
+ logger.error(ioe);
+ }
+
+ return html.toString();
+ }
+
+ /**
+ * Setup the common properties required at data-binding time.
+ *
+ * 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.
+ *
+ * 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);
+
+ if (ApplicationModel.TYPE_FILELINK.equals(node.getType()))
+ {
+ node.addPropertyResolver("downloadUrl", this.resolverLinkDownload);
+ }
+ else
+ {
+ node.addPropertyResolver("downloadUrl", this.resolverDownload);
+ }
+
+ 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)
+ {
+ if (this.nodeEventListeners == null)
+ {
+ this.nodeEventListeners = new HashSet();
+ }
+ this.nodeEventListeners.add(listener);
+ }
+
+ /**
+ * Remove a listener from the list of those called by BrowseBean
+ */
+ public void removeNodeEventListener(NodeEventListener listener)
+ {
+ if (this.nodeEventListeners != null)
+ {
+ this.nodeEventListeners.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());
+
+ // browse to appropriate view
+ FacesContext fc = FacesContext.getCurrentInstance();
+ String outcome = null;
+ String viewId = fc.getViewRoot().getViewId();
+ if (viewId.equals(BROWSE_VIEW_ID) == false && viewId.equals(CATEGORY_VIEW_ID) == false)
+ {
+ outcome = "browse";
+ }
+ fc.getApplication().getNavigationHandler().handleNavigation(fc, null, outcome);
+ }
+ 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.getNodeService().getRootNode(Repository.getStoreRef());
+ }
+ else
+ {
+ // build a NodeRef for the specified Id and our store
+ parentRef = new NodeRef(Repository.getStoreRef(), parentNodeId);
+ }
+
+ List children = this.getFileFolderService().removeHiddenFiles(this.getFileFolderService().list(parentRef));
+ this.containerNodes = new ArrayList(children.size());
+ this.contentNodes = new ArrayList(children.size());
+
+ // in case of dynamic config, only lookup once
+ Set nodeEventListeners = getNodeEventListeners();
+
+ for (FileInfo fileInfo : children)
+ {
+ // create our Node representation from the NodeRef
+ NodeRef nodeRef = fileInfo.getNodeRef();
+
+ // find it's type so we can see if it's a node we are interested in
+ QName type = this.getNodeService().getType(nodeRef);
+
+ // make sure the type is defined in the data dictionary
+ TypeDefinition typeDef = this.getDictionaryService().getType(type);
+
+ if (typeDef != null)
+ {
+ MapNode node = null;
+
+ // look for File content node
+ if (this.getDictionaryService().isSubClass(type, ContentModel.TYPE_CONTENT))
+ {
+ // create our Node representation
+ node = new MapNode(nodeRef, this.getNodeService(), fileInfo.getProperties());
+ setupCommonBindingProperties(node);
+
+ this.contentNodes.add(node);
+ }
+ // look for Space folder node
+ else if (this.getDictionaryService().isSubClass(type, ContentModel.TYPE_FOLDER) == true &&
+ this.getDictionaryService().isSubClass(type, ContentModel.TYPE_SYSTEM_FOLDER) == false)
+ {
+ // create our Node representation
+ node = new MapNode(nodeRef, this.getNodeService(), fileInfo.getProperties());
+ node.addPropertyResolver("icon", this.resolverSpaceIcon);
+ node.addPropertyResolver("smallIcon", this.resolverSmallIcon);
+
+ this.containerNodes.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.getNodeService(), fileInfo.getProperties());
+ // only display the user has the permissions to navigate to the target of the link
+ NodeRef destRef = (NodeRef)node.getProperties().get(ContentModel.PROP_LINK_DESTINATION);
+ if (destRef != null && new Node(destRef).hasPermission(PermissionService.READ) == true)
+ {
+ node.addPropertyResolver("url", this.resolverLinkUrl);
+ node.addPropertyResolver("downloadUrl", this.resolverLinkDownload);
+ node.addPropertyResolver("webdavUrl", this.resolverLinkWebdavUrl);
+ node.addPropertyResolver("cifsPath", this.resolverLinkCifsPath);
+ node.addPropertyResolver("fileType16", this.resolverFileType16);
+ node.addPropertyResolver("fileType32", this.resolverFileType32);
+ 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.getNodeService(), fileInfo.getProperties());
+ // only display the user has the permissions to navigate to the target of the link
+ NodeRef destRef = (NodeRef)node.getProperties().get(ContentModel.PROP_LINK_DESTINATION);
+ if (destRef != null && new Node(destRef).hasPermission(PermissionService.READ) == 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 : nodeEventListeners)
+ {
+ listener.created(node, type);
+ }
+ }
+ }
+ else
+ {
+ if (logger.isWarnEnabled())
+ 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.emptyList();
+ this.contentNodes = Collections.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.emptyList();
+ this.contentNodes = Collections.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.emptyList();
+ this.contentNodes = Collections.emptyList();
+ return;
+ }
+
+ // perform the search against the repo
+ UserTransaction tx = null;
+ ResultSet results = null;
+ try
+ {
+ tx = Repository.getUserTransaction(FacesContext.getCurrentInstance(), true);
+ tx.begin();
+
+ // build up the search parameters
+ SearchParameters sp = new SearchParameters();
+ sp.setLanguage(SearchService.LANGUAGE_LUCENE);
+ sp.setQuery(query);
+ sp.addStore(Repository.getStoreRef());
+
+ // limit search results size as configured
+ int searchLimit = Application.getClientConfig(FacesContext.getCurrentInstance()).getSearchMaxResults();
+ if (searchLimit > 0)
+ {
+ sp.setLimitBy(LimitBy.FINAL_SIZE);
+ sp.setLimit(searchLimit);
+ }
+
+ results = this.getSearchService().query(sp);
+ if (logger.isDebugEnabled())
+ logger.debug("Search results returned: " + results.length());
+
+ // create a list of items from the results
+ this.containerNodes = new ArrayList(results.length());
+ this.contentNodes = new ArrayList(results.length());
+ if (results.length() != 0)
+ {
+ // in case of dynamic config, only lookup once
+ Set nodeEventListeners = getNodeEventListeners();
+
+ for (ResultSetRow row: results)
+ {
+ NodeRef nodeRef = row.getNodeRef();
+
+ if (this.getNodeService().exists(nodeRef))
+ {
+ // find it's type so we can see if it's a node we are interested in
+ QName type = this.getNodeService().getType(nodeRef);
+
+ // make sure the type is defined in the data dictionary
+ TypeDefinition typeDef = this.getDictionaryService().getType(type);
+
+ if (typeDef != null)
+ {
+ MapNode node = null;
+
+ // look for Space or File nodes
+ if (this.getDictionaryService().isSubClass(type, ContentModel.TYPE_FOLDER) &&
+ this.getDictionaryService().isSubClass(type, ContentModel.TYPE_SYSTEM_FOLDER) == false)
+ {
+ // create our Node representation
+ node = new MapNode(nodeRef, this.getNodeService(), 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.getDictionaryService().isSubClass(type, ContentModel.TYPE_CONTENT))
+ {
+ // create our Node representation
+ node = new MapNode(nodeRef, this.getNodeService(), 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.getNodeService(), false);
+ // only display the user has the permissions to navigate to the target of the link
+ NodeRef destRef = (NodeRef)node.getProperties().get(ContentModel.PROP_LINK_DESTINATION);
+ if (new Node(destRef).hasPermission(PermissionService.READ) == true)
+ {
+ node.addPropertyResolver("url", this.resolverLinkUrl);
+ node.addPropertyResolver("downloadUrl", this.resolverLinkDownload);
+ node.addPropertyResolver("webdavUrl", this.resolverLinkWebdavUrl);
+ node.addPropertyResolver("cifsPath", this.resolverLinkCifsPath);
+ node.addPropertyResolver("fileType16", this.resolverFileType16);
+ node.addPropertyResolver("fileType32", this.resolverFileType32);
+ 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.getNodeService(), false);
+ // only display the user has the permissions to navigate to the target of the link
+ NodeRef destRef = (NodeRef)node.getProperties().get(ContentModel.PROP_LINK_DESTINATION);
+ if (new Node(destRef).hasPermission(PermissionService.READ) == true)
+ {
+ 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 : nodeEventListeners)
+ {
+ listener.created(node, type);
+ }
+ }
+ }
+ else
+ {
+ if (logger.isWarnEnabled())
+ logger.warn("Found invalid object in database: id = " + nodeRef + ", type = " + type);
+ }
+ }
+ else
+ {
+ if (logger.isWarnEnabled())
+ 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.emptyList();
+ this.contentNodes = Collections.emptyList();
+ try { if (tx != null) {tx.rollback();} } catch (Exception tex) {}
+ }
+ catch (SearcherException serr)
+ {
+ logger.info("Search failed for: " + query, serr);
+ Utils.addErrorMessage(Application.getMessage(
+ FacesContext.getCurrentInstance(), Repository.ERROR_QUERY));
+ this.containerNodes = Collections.emptyList();
+ this.contentNodes = Collections.emptyList();
+ try { if (tx != null) {tx.rollback();} } catch (Exception tex) {}
+ }
+ catch (Throwable err)
+ {
+ Utils.addErrorMessage(MessageFormat.format(Application.getMessage(
+ FacesContext.getCurrentInstance(), Repository.ERROR_SEARCH), new Object[] {err.getMessage()}), err );
+ this.containerNodes = Collections.emptyList();
+ this.contentNodes = Collections.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() {
+ private static final long serialVersionUID = 4048859853585650378L;
+
+ public Object get(Node node) {
+ return DownloadContentServlet.generateDownloadURL(node.getNodeRef(), node.getName());
+ }
+ };
+
+ public NodePropertyResolver resolverUrl = new NodePropertyResolver() {
+ private static final long serialVersionUID = -5264085143622470386L;
+
+ public Object get(Node node) {
+ return DownloadContentServlet.generateBrowserURL(node.getNodeRef(), node.getName());
+ }
+ };
+
+ public NodePropertyResolver resolverWebdavUrl = new NodePropertyResolver() {
+ private static final long serialVersionUID = 9127234483419089006L;
+
+ public Object get(Node node) {
+ return Utils.generateURL(FacesContext.getCurrentInstance(), node, URLMode.WEBDAV);
+ }
+ };
+
+ public NodePropertyResolver resolverCifsPath = new NodePropertyResolver() {
+ private static final long serialVersionUID = -5804924617772163104L;
+
+ public Object get(Node node) {
+ return Utils.generateURL(FacesContext.getCurrentInstance(), node, URLMode.CIFS);
+ }
+ };
+
+ public NodePropertyResolver resolverLinkDownload = new NodePropertyResolver() {
+ private static final long serialVersionUID = 7208696954599958859L;
+
+ public Object get(Node node) {
+ NodeRef destRef = (NodeRef)node.getProperties().get(ContentModel.PROP_LINK_DESTINATION);
+ if (getNodeService().exists(destRef) == true)
+ {
+ String destName = Repository.getNameForNode(getNodeService(), destRef);
+ return DownloadContentServlet.generateDownloadURL(destRef, destName);
+ }
+ else
+ {
+ // TODO: link object is missing - navigate to a page with appropriate message
+ return "#";
+ }
+ }
+ };
+
+ public NodePropertyResolver resolverLinkUrl = new NodePropertyResolver() {
+ private static final long serialVersionUID = -1280702397805414147L;
+
+ public Object get(Node node) {
+ NodeRef destRef = (NodeRef)node.getProperties().get(ContentModel.PROP_LINK_DESTINATION);
+ if (getNodeService().exists(destRef) == true)
+ {
+ String destName = Repository.getNameForNode(getNodeService(), 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() {
+ private static final long serialVersionUID = -3097558079118837397L;
+
+ public Object get(Node node) {
+ NodeRef destRef = (NodeRef)node.getProperties().get(ContentModel.PROP_LINK_DESTINATION);
+ if (getNodeService().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() {
+ private static final long serialVersionUID = 673020173327603487L;
+
+ public Object get(Node node) {
+ NodeRef destRef = (NodeRef)node.getProperties().get(ContentModel.PROP_LINK_DESTINATION);
+ if (getNodeService().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() {
+ private static final long serialVersionUID = -2690520488415178029L;
+
+ public Object get(Node node) {
+ return FileTypeImageUtils.getFileTypeImage(node.getName(), true);
+ }
+ };
+
+ public NodePropertyResolver resolverFileType32 = new NodePropertyResolver() {
+ private static final long serialVersionUID = 1991254398502584389L;
+
+ public Object get(Node node) {
+ return FileTypeImageUtils.getFileTypeImage(node.getName(), false);
+ }
+ };
+
+ public NodePropertyResolver resolverPath = new NodePropertyResolver() {
+ private static final long serialVersionUID = 8008094870888545035L;
+
+ public Object get(Node node) {
+ return node.getNodePath();
+ }
+ };
+
+ public NodePropertyResolver resolverDisplayPath = new NodePropertyResolver() {
+ private static final long serialVersionUID = -918422848579179425L;
+
+ 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() {
+ private static final long serialVersionUID = -5644418026591098018L;
+
+ 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() {
+ private static final long serialVersionUID = -150483121767183580L;
+
+ 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() {
+ private static final long serialVersionUID = -8864267975247235172L;
+
+ public Object get(Node node) {
+ ContentData content = (ContentData)node.getProperties().get(ContentModel.PROP_CONTENT);
+ return (content != null ? content.getMimetype() : null);
+ }
+ };
+
+ public NodePropertyResolver resolverEncoding = new NodePropertyResolver() {
+ private static final long serialVersionUID = -1130974681844152101L;
+
+ public Object get(Node node) {
+ ContentData content = (ContentData)node.getProperties().get(ContentModel.PROP_CONTENT);
+ return (content != null ? content.getEncoding() : null);
+ }
+ };
+
+ public NodePropertyResolver resolverSize = new NodePropertyResolver() {
+ private static final long serialVersionUID = 1273541660444385276L;
+
+ 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() {
+
+ private static final long serialVersionUID = 5412446489528560367L;
+
+ public Object get(Node node) {
+
+ String lang = null;
+
+ if (node.getAspects().contains(ContentModel.ASPECT_MULTILINGUAL_DOCUMENT))
+ {
+ Locale locale = null;
+
+ if(node.hasAspect(ContentModel.ASPECT_MULTILINGUAL_EMPTY_TRANSLATION))
+ {
+ // if the translation is empty, the lang of the content is the lang of it's pivot.
+ NodeRef pivot = getMultilingualContentService().getPivotTranslation(node.getNodeRef());
+ locale = (Locale) getNodeService().getProperty(pivot, ContentModel.PROP_LOCALE);
+ }
+ else
+ {
+ 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
+ String currentNodeId = this.navigator.getCurrentNodeId();
+ this.navigator.setCurrentNodeId(currentNodeId);
+
+ // setup dispatch context so we go back to the right place
+ NodeRef currentNodeRef = new NodeRef(Repository.getStoreRef(), currentNodeId);
+ Node currentNode = new Node(currentNodeRef);
+ this.navigator.setupDispatchContext(currentNode);
+ }
+
+ /**
+ * 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 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.getNodeService().getType(ref)))
+ {
+ ref = (NodeRef)this.getNodeService().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 = getNodeService().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 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)
+ {
+ FacesContext fc = FacesContext.getCurrentInstance();
+ NodeRef companyRootRef = new NodeRef(Repository.getStoreRef(), Application.getCompanyRootId(fc));
+ if (node.getNodeRef().equals(companyRootRef))
+ {
+ message = Application.getMessage(fc, 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 params = link.getParameterMap();
+ setupContentAction(params.get("id"), true);
+ }
+
+ /**
+ * Action event called by all actions that need to setup a Multilingual 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 setupMLContainerContentAction(ActionEvent event)
+ {
+ UIActionLink link = (UIActionLink)event.getComponent();
+ Map params = link.getParameterMap();
+
+ String id = params.get("id");
+
+ NodeRef translation = new NodeRef(Repository.getStoreRef(), id);
+
+ // remember the bean from which the action comes
+ FacesContext fc = FacesContext.getCurrentInstance();
+ DocumentDetailsDialog docDetails = (DocumentDetailsDialog)FacesHelper.getManagedBean(fc, "DocumentDetailsDialog");
+ docDetails.setTranslationDocument(new MapNode(translation));
+ MultilingualManageDialog mmDialog = (MultilingualManageDialog)FacesHelper.getManagedBean(fc, "MultilingualManageDialog");
+ mmDialog.setTranslationDocument(docDetails.getTranslationDocument());
+
+ // set the ml container as the current document
+ NodeRef mlContainer = getMultilingualContentService().getTranslationContainer(translation);
+
+ setupContentAction(mlContainer.getId(), 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 MapNode(ref);
+
+ // store the URL to for downloading the content
+ if (ApplicationModel.TYPE_FILELINK.equals(node.getType()))
+ {
+ node.addPropertyResolver("url", this.resolverLinkDownload);
+ node.addPropertyResolver("downloadUrl", this.resolverLinkDownload);
+ }
+ else
+ {
+ node.addPropertyResolver("url", this.resolverDownload);
+ node.addPropertyResolver("downloadUrl", this.resolverDownload);
+ }
+ node.addPropertyResolver("webdavUrl", this.resolverWebdavUrl);
+ node.addPropertyResolver("cifsPath", this.resolverCifsPath);
+ node.addPropertyResolver("fileType32", this.resolverFileType32);
+ node.addPropertyResolver("mimetype", this.resolverMimetype);
+ node.addPropertyResolver("encoding", this.resolverEncoding);
+ node.addPropertyResolver("size", this.resolverSize);
+ node.addPropertyResolver("lang", this.resolverLang);
+
+ for (NodeEventListener listener : getNodeEventListeners())
+ {
+ listener.created(node, node.getType());
+ }
+
+ // get hold of the DocumentDetailsDialog and reset it
+ DocumentDetailsDialog docDetails = (DocumentDetailsDialog)FacesContext.getCurrentInstance().
+ getExternalContext().getSessionMap().get("DocumentDetailsDialog");
+ 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}) );
+ throw new AbortProcessingException("Invalid node reference");
+ }
+ }
+ 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 location = navigator.getLocation();
+ IBreadcrumbHandler handler = location.get(location.size() - 1);
+ if (handler instanceof IRepoBreadcrumbHandler)
+ {
+ // see if the current breadcrumb location is our node
+ if ( ((IRepoBreadcrumbHandler)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 IRepoBreadcrumbHandler)
+ {
+ // change the current node Id
+ navigator.setCurrentNodeId(((IRepoBreadcrumbHandler)handler).getNodeRef().getId());
+ }
+ else
+ {
+ // if we don't have access to the NodeRef to go to next then go to the home space
+ navigator.processToolbarLocation(NavigationBean.LOCATION_HOME, false);
+ }
+ }
+ else
+ {
+ // if there is no breadcrumb left go to the user's home space
+ navigator.processToolbarLocation(NavigationBean.LOCATION_HOME, false);
+ }
+ }
+ }
+ }
+
+ /**
+ * 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()
+ {
+ // TODO - review implications of these default values on dynamic/MT client: viewsConfig & browseViewMode, as well as page size content/spaces ...
+ 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 getNodeEventListeners()
+ {
+ if ((this.nodeEventListeners == null) || (Application.isDynamicConfig(FacesContext.getCurrentInstance())))
+ {
+ Set allNodeEventListeners = new HashSet();
+
+ if (Application.isDynamicConfig(FacesContext.getCurrentInstance()) && (this.nodeEventListeners != null))
+ {
+ // for dynamic config, can add/remove node event listeners dynamically ...
+ // however, in case anyone is using public methods (add/removeNodeEventListener)
+ // we merge list here with list returned from the config
+ allNodeEventListeners.addAll(this.nodeEventListeners);
+ }
+
+ 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)
+ {
+ allNodeEventListeners.add((NodeEventListener)bean);
+ }
+ }
+ }
+ }
+ }
+
+ if (Application.isDynamicConfig(FacesContext.getCurrentInstance()))
+ {
+ return allNodeEventListeners;
+ }
+ else
+ {
+ this.nodeEventListeners = allNodeEventListeners;
+ }
+ }
+ 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 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 newLocation = new ArrayList(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 params = link.getParameterMap();
+
+ String ref = params.get("ref");
+ if (ref != null && ref.length() > 0)
+ {
+ NodeRef nodeRef = new NodeRef(ref);
+
+ NodeRef workingCopyNodeRef = getCheckOutCheckInService().getWorkingCopy(nodeRef);
+
+ if (workingCopyNodeRef != null)
+ {
+ // if node has a working copy setup error message and return
+ Utils.addErrorMessage(MessageFormat.format(Application.getMessage(
+ FacesContext.getCurrentInstance(), MSG_CANNOT_DELETE_NODE_HAS_WORKING_COPY),
+ new Object[] {getNodeService().getProperty(nodeRef, ContentModel.PROP_NAME)}));
+ return;
+ }
+
+ // if there isn't a working copy go to normal delete dialog
+ boolean hasMultipleParents = false;
+ boolean showDeleteAssocDialog = false;
+
+ // get type of node being deleted
+ Node node = this.getDocument();
+ QName type = node.getType();
+ TypeDefinition typeDef = this.dictionaryService.getType(type);
+
+ // determine if the node being delete has multiple parents
+ if (!type.equals(ContentModel.TYPE_MULTILINGUAL_CONTAINER) &&
+ !node.hasAspect(ContentModel.ASPECT_MULTILINGUAL_EMPTY_TRANSLATION) &&
+ !node.hasAspect(ContentModel.ASPECT_MULTILINGUAL_DOCUMENT) &&
+ !type.equals(ContentModel.TYPE_LINK) &&
+ !this.dictionaryService.isSubClass(typeDef.getName(), ContentModel.TYPE_LINK))
+ {
+ List parents = this.nodeService.getParentAssocs(node.getNodeRef(),
+ ContentModel.ASSOC_CONTAINS, RegexQNamePattern.MATCH_ALL);
+ if (parents != null && parents.size() > 1)
+ {
+ hasMultipleParents = true;
+ }
+ }
+
+ // determine which delete dialog to display
+ if (this.navigator.getSearchContext() == null && hasMultipleParents)
+ {
+ // if we are not in a search and the node has multiple parents
+ // see if the current node has the primary parent association
+ NodeRef parentSpace = this.navigator.getCurrentNode().getNodeRef();
+ ChildAssociationRef assoc = this.nodeService.getPrimaryParent(node.getNodeRef());
+
+ // show delete assoc dialog if the current space is not the primary parent for the node
+ showDeleteAssocDialog = !parentSpace.equals(assoc.getParentRef());
+ }
+
+ // show the appropriate dialog
+ FacesContext fc = FacesContext.getCurrentInstance();
+ if (showDeleteAssocDialog)
+ {
+ fc.getApplication().getNavigationHandler().handleNavigation(fc, null, "dialog:deleteFileAssoc");
+ }
+ else
+ {
+ final Map dialogParams = new HashMap(1);
+ dialogParams.put("hasMultipleParents", Boolean.toString(hasMultipleParents));
+ Application.getDialogManager().setupParameters(dialogParams);
+ fc.getApplication().getNavigationHandler().handleNavigation(fc, null, "dialog:deleteFile");
+ }
+ }
+ }
+
+ /**
+ * Handles the deleteSpace action by deciding which delete dialog to display
+ */
+ public void deleteSpace(ActionEvent event)
+ {
+ setupDeleteAction(event);
+
+ boolean hasMultipleParents = false;
+ boolean showDeleteAssocDialog = false;
+
+ // determine if the node being delete has multiple parents
+ Node node = this.getActionSpace();
+ List parents = this.nodeService.getParentAssocs(node.getNodeRef(),
+ ContentModel.ASSOC_CONTAINS, RegexQNamePattern.MATCH_ALL);
+ if (parents != null && parents.size() > 1)
+ {
+ hasMultipleParents = true;
+ }
+
+ // determine which delete dialog to display
+ if (this.navigator.getSearchContext() == null && hasMultipleParents)
+ {
+ // if we are not in a search and the node has multiple parents
+ // see if the current node has the primary parent association
+ NodeRef parentSpace = this.navigator.getCurrentNode().getNodeRef();
+ ChildAssociationRef assoc = this.nodeService.getPrimaryParent(node.getNodeRef());
+
+ // show delete assoc dialog if the current space is not the primary parent for the node
+ showDeleteAssocDialog = !parentSpace.equals(assoc.getParentRef());
+ }
+
+ // show the appropriate dialog
+ FacesContext fc = FacesContext.getCurrentInstance();
+ if (showDeleteAssocDialog)
+ {
+ fc.getApplication().getNavigationHandler().handleNavigation(fc, null, "dialog:deleteSpaceAssoc");
+ }
+ else
+ {
+ final Map dialogParams = new HashMap(1);
+ dialogParams.put("hasMultipleParents", Boolean.toString(hasMultipleParents));
+ Application.getDialogManager().setupParameters(dialogParams);
+ fc.getApplication().getNavigationHandler().handleNavigation(fc, null, "dialog:deleteSpace");
+ }
+ }
+
+ // ------------------------------------------------------------------------------
+ // 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
+
+ private static Log logger = LogFactory.getLog(BrowseBean.class);
+
+ /** Browse screen view ID */
+ public static final String BROWSE_VIEW_ID = "/jsp/browse/browse.jsp";
+ public static final String CATEGORY_VIEW_ID = "/jsp/browse/category-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";
+ public static final String MSG_SEARCH_MINIMUM = "search_minimum";
+ private static final String MSG_CANNOT_DELETE_NODE_HAS_WORKING_COPY = "cannot_delete_node_has_working_copy";
+
+ /** The NodeService to be used by the bean */
+ private transient NodeService nodeService;
+
+ /** The CheckOutCheckInService to be used by the bean */
+ private transient CheckOutCheckInService checkOutCheckInService;
+
+ /** The SearchService to be used by the bean */
+ private transient SearchService searchService;
+
+ /** The LockService to be used by the bean */
+ private transient LockService lockService;
+
+ /** The NavigationBean bean reference */
+ protected NavigationBean navigator;
+
+ /** The UserPreferencesBean to be used by the bean */
+ protected UserPreferencesBean userPreferencesBean;
+
+ /** The DictionaryService bean reference */
+ private transient DictionaryService dictionaryService;
+
+ /** The file folder service */
+ private transient FileFolderService fileFolderService;
+
+ /** The Multilingual Content Service */
+ private transient MultilingualContentService multilingualContentService;
+
+ /** Views configuration object */
+ protected ViewsConfigElement viewsConfig = null;
+
+ /** Listeners for Node events */
+ protected Set nodeEventListeners = null;
+
+ /** Collapsable Panel state */
+ private Map panels = new HashMap(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 containerNodes = null;
+ protected List contentNodes = null;
+ protected List parentContainerNodes = 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;
}
\ No newline at end of file