diff --git a/source/java/org/alfresco/web/bean/wcm/AVMBrowseBean.java b/source/java/org/alfresco/web/bean/wcm/AVMBrowseBean.java index 0acf1f626c..606aed0e5d 100644 --- a/source/java/org/alfresco/web/bean/wcm/AVMBrowseBean.java +++ b/source/java/org/alfresco/web/bean/wcm/AVMBrowseBean.java @@ -17,26 +17,35 @@ package org.alfresco.web.bean.wcm; import java.text.MessageFormat; +import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Map; import javax.faces.context.FacesContext; import javax.faces.event.ActionEvent; +import javax.transaction.UserTransaction; import org.alfresco.model.ContentModel; +import org.alfresco.repo.avm.AVMNodeConverter; +import org.alfresco.service.cmr.avm.AVMNodeDescriptor; +import org.alfresco.service.cmr.avm.AVMService; import org.alfresco.service.cmr.dictionary.DictionaryService; -import org.alfresco.service.cmr.repository.ContentService; import org.alfresco.service.cmr.repository.NodeService; import org.alfresco.service.cmr.search.SearchService; import org.alfresco.service.namespace.NamespaceService; import org.alfresco.web.app.Application; import org.alfresco.web.app.context.IContextListener; import org.alfresco.web.app.context.UIContextService; +import org.alfresco.web.app.servlet.DownloadContentServlet; import org.alfresco.web.bean.BrowseBean; import org.alfresco.web.bean.NavigationBean; import org.alfresco.web.bean.repository.Node; +import org.alfresco.web.bean.repository.Repository; +import org.alfresco.web.ui.common.Utils; +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.data.UIRichList; import org.alfresco.web.ui.wcm.WebResources; import org.apache.commons.logging.Log; @@ -57,10 +66,16 @@ public class AVMBrowseBean implements IContextListener private String sandbox; private String username; private String sandboxTitle = null; + private String currentPath = null; private UIRichList foldersRichList; private UIRichList filesRichList; + private List files = null; + private List folders = null; + + private List location = null; + /** The NodeService to be used by the bean */ protected NodeService nodeService; @@ -79,6 +94,9 @@ public class AVMBrowseBean implements IContextListener /** The NavigationBean bean reference */ protected NavigationBean navigator; + /** AVM service bean reference */ + protected AVMService avmService; + /** * Default Constructor @@ -94,6 +112,14 @@ public class AVMBrowseBean implements IContextListener // ------------------------------------------------------------------------------ // Bean property getters and setters + /** + * @param avmService The AVMService to set. + */ + public void setAvmService(AVMService avmService) + { + this.avmService = avmService; + } + /** * @param nodeService The NodeService to set. */ @@ -259,12 +285,77 @@ public class AVMBrowseBean implements IContextListener public List getFolders() { - return Collections.emptyList(); + if (this.folders == null) + { + getNodes(); + } + return this.folders; } public List getFiles() { - return Collections.emptyList(); + if (this.files == null) + { + getNodes(); + } + return this.files; + } + + private void getNodes() + { + UserTransaction tx = null; + try + { + FacesContext context = FacesContext.getCurrentInstance(); + tx = Repository.getUserTransaction(context, true); + tx.begin(); + + Map nodes = this.avmService.getDirectoryListing(-1, getCurrentPath()); + this.files = new ArrayList(nodes.size()); + this.folders = new ArrayList(nodes.size()); + for (String name : nodes.keySet()) + { + AVMNodeDescriptor avmRef = nodes.get(name); + + // build the client representation of the AVM node + AVMNode node = new AVMNode(avmRef); + + // add any common properties + + // properties specific to folders or files + if (avmRef.isDirectory()) + { + node.getProperties().put("smallIcon", BrowseBean.SPACE_SMALL_DEFAULT); + this.folders.add(node); + } + else + { + node.getProperties().put("fileType16", Utils.getFileTypeImage(name, true)); + node.getProperties().put("url", DownloadContentServlet.generateBrowserURL( + AVMNodeConverter.ToNodeRef(-1, avmRef.getPath()), name)); + this.files.add(node); + } + } + + // commit the transaction + tx.commit(); + } + catch (Throwable err) + { + Utils.addErrorMessage(MessageFormat.format(Application.getMessage( + FacesContext.getCurrentInstance(), Repository.ERROR_GENERIC), err.getMessage()), err); + this.folders = Collections.emptyList(); + this.files = Collections.emptyList(); + try { if (tx != null) {tx.rollback();} } catch (Exception tex) {} + } + } + + public void clickFolder(ActionEvent event) + { + UIActionLink link = (UIActionLink)event.getComponent(); + Map params = link.getParameterMap(); + String path = params.get("id"); + updateUILocation(path); } /** @@ -295,9 +386,65 @@ public class AVMBrowseBean implements IContextListener this.sandboxTitle = null; // update UI state ready for return to the previous screen + this.location = null; + setCurrentPath(null); + } + + /** + * @return Breadcrumb location list + */ + public List getLocation() + { + if (this.location == null) + { + List loc = new ArrayList(8); + loc.add(new AVMBreadcrumbHandler(getCurrentPath())); + + this.location = loc; + } + return this.location; + } + + /** + * @param location Breadcrumb location list + */ + public void setLocation(List location) + { + this.location = location; + } + + /** + * @return the internal AVM path to the current folder for browsing + */ + private String getCurrentPath() + { + if (this.currentPath == null) + { + this.currentPath = AVMConstants.buildAVMStoreRootPath(getSandbox()); + } + return this.currentPath; + } + + /** + * @param path the internal AVM path to the current folder for browsing + */ + private void setCurrentPath(String path) + { + this.currentPath = path; + + // update UI state ready for screen refresh UIContextService.getInstance(FacesContext.getCurrentInstance()).notifyBeans(); } + /** + * Update the breadcrumb with the clicked Group location + */ + private void updateUILocation(String path) + { + this.location.add(new AVMBreadcrumbHandler(path)); + setCurrentPath(path); + } + // ------------------------------------------------------------------------------ // IContextListener implementation @@ -311,20 +458,56 @@ public class AVMBrowseBean implements IContextListener { this.foldersRichList.setValue(null); } - /* - // clear the value for the list components - will cause re-bind to it's data and refresh - if (this.forumsRichList != null) + if (this.filesRichList != null) { - this.forumsRichList.setValue(null); - if (this.forumsRichList.getInitialSortColumn() == null) + this.filesRichList.setValue(null); + } + + this.files = null; + this.folders = null; + } + + + // ------------------------------------------------------------------------------ + // Inner classes + + /** + * Class to handle breadcrumb interaction for AVM page + */ + private class AVMBreadcrumbHandler implements IBreadcrumbHandler + { + private String path; + + AVMBreadcrumbHandler(String path) + { + this.path = path; + } + + public String navigationOutcome(UIBreadcrumb breadcrumb) + { + setCurrentPath(path); + setLocation((List)breadcrumb.getValue()); + return null; + } + + @Override + public String toString() + { + if (AVMConstants.buildAVMStoreRootPath(getSandbox()).equals(path)) { - // set the initial sort column and direction - this.forumsRichList.setInitialSortColumn( - this.viewsConfig.getDefaultSortColumn(PAGE_NAME_FORUMS)); - this.forumsRichList.setInitialSortDescending( - this.viewsConfig.hasDescendingSort(PAGE_NAME_FORUMS)); + // don't display the 'root' webapps path as this will confuse users + // instead display which sandbox we are in + String label = username; + if (label == null) + { + label = Application.getMessage(FacesContext.getCurrentInstance(), MSG_SANDBOXSTAGING); + } + return label; + } + else + { + return path.substring(path.lastIndexOf('/') + 1); } } - */ } } diff --git a/source/java/org/alfresco/web/bean/wcm/AVMConstants.java b/source/java/org/alfresco/web/bean/wcm/AVMConstants.java index 0185ceec7d..b497ee0101 100644 --- a/source/java/org/alfresco/web/bean/wcm/AVMConstants.java +++ b/source/java/org/alfresco/web/bean/wcm/AVMConstants.java @@ -50,7 +50,7 @@ public final class AVMConstants public static String buildAVMStoreRootPath(String store) { - return store + ":/" + DIR_APPBASE + '/' + DIR_WEBAPPS + '/'; + return store + ":/" + DIR_APPBASE + '/' + DIR_WEBAPPS; } // names of the stores representing the layers for an AVM website diff --git a/source/java/org/alfresco/web/bean/wcm/AVMNode.java b/source/java/org/alfresco/web/bean/wcm/AVMNode.java new file mode 100644 index 0000000000..d0d94878b6 --- /dev/null +++ b/source/java/org/alfresco/web/bean/wcm/AVMNode.java @@ -0,0 +1,220 @@ +/* + * Copyright (C) 2005 Alfresco, Inc. + * + * Licensed under the Mozilla Public License version 1.1 + * with a permitted attribution clause. You may obtain a + * copy of the License at + * + * http://www.alfresco.org/legal/license.txt + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific + * language governing permissions and limitations under the + * License. + */ +package org.alfresco.web.bean.wcm; + +import java.io.Serializable; +import java.util.Collection; +import java.util.Map; +import java.util.Set; +import java.util.Map.Entry; + +import javax.faces.context.FacesContext; + +import org.alfresco.repo.domain.PropertyValue; +import org.alfresco.service.ServiceRegistry; +import org.alfresco.service.cmr.avm.AVMNodeDescriptor; +import org.alfresco.service.namespace.QName; +import org.alfresco.service.namespace.QNameMap; +import org.alfresco.web.bean.repository.NodePropertyResolver; +import org.alfresco.web.bean.repository.QNameNodeMap; +import org.alfresco.web.bean.repository.Repository; + +/** + * @author Kevin Roast + */ +public class AVMNode implements Map +{ + private QNameMap properties = null; + private ServiceRegistry services = null; + private AVMNodeDescriptor avmRef; + private String path; + private int version; + + + /** + * Constructor + */ + public AVMNode(AVMNodeDescriptor avmRef) + { + this.avmRef = avmRef; + this.version = -1; // TODO: why does avmNode.getVersionID() return 1? + this.path = avmRef.getPath(); + + getProperties(); + } + + /** + * @return All the properties known about this node. + */ + public final Map getProperties() + { + if (this.properties == null) + { + Map props = getServiceRegistry().getAVMService().getNodeProperties(this.version, this.path); + + this.properties = new QNameMap(getServiceRegistry().getNamespaceService()); + for (QName qname: props.keySet()) + { + PropertyValue propValue = props.get(qname); + this.properties.put(qname.toString(), propValue.getSerializableValue()); + } + + this.properties.put("id", this.path); + this.properties.put("path", this.path); + this.properties.put("name", this.avmRef.getName()); + this.properties.put("created", this.avmRef.getCreateDate()); + this.properties.put("modified", this.avmRef.getModDate()); + } + + return this.properties; + } + + /** + * Determines whether the given property name is held by this node + * + * @param propertyName Property to test existence of + * @return true if property exists, false otherwise + */ + public final boolean hasProperty(String propertyName) + { + return getProperties().containsKey(propertyName); + } + + private ServiceRegistry getServiceRegistry() + { + if (this.services == null) + { + this.services = Repository.getServiceRegistry(FacesContext.getCurrentInstance()); + } + return this.services; + } + + + // ------------------------------------------------------------------------------------ + // Map implementation - allows the Node bean to be accessed using JSF expression syntax + + /** + * @see java.util.Map#clear() + */ + public void clear() + { + getProperties().clear(); + } + + /** + * @see java.util.Map#containsKey(java.lang.Object) + */ + public boolean containsKey(Object key) + { + return getProperties().containsKey(key); + } + + /** + * @see java.util.Map#containsValue(java.lang.Object) + */ + public boolean containsValue(Object value) + { + return getProperties().containsKey(value); + } + + /** + * @see java.util.Map#entrySet() + */ + public Set entrySet() + { + return getProperties().entrySet(); + } + + /** + * @see java.util.Map#get(java.lang.Object) + */ + public Object get(Object key) + { + Object obj = null; + + // there are some things that aren't available as properties + // but from method calls, so for these handle them individually + Map props = getProperties(); + /*if (propsInitialised == false) + { + // well known properties required as publically accessable map attributes + props.put("id", this.getId()); + props.put("name", this.getName()); // TODO: perf test pulling back single prop here instead of all! + props.put("nodeRef", this.getNodeRef()); + + propsInitialised = true; + }*/ + + return props.get(key); + } + + /** + * @see java.util.Map#isEmpty() + */ + public boolean isEmpty() + { + return getProperties().isEmpty(); + } + + /** + * @see java.util.Map#keySet() + */ + public Set keySet() + { + return getProperties().keySet(); + } + + /** + * @see java.util.Map#put(K, V) + */ + public Object put(String key, Object value) + { + return getProperties().put(key, value); + } + + /** + * @see java.util.Map#putAll(java.util.Map) + */ + public void putAll(Map t) + { + getProperties().putAll(t); + } + + /** + * @see java.util.Map#remove(java.lang.Object) + */ + public Object remove(Object key) + { + return getProperties().remove(key); + } + + /** + * @see java.util.Map#size() + */ + public int size() + { + return getProperties().size(); + } + + /** + * @see java.util.Map#values() + */ + public Collection values() + { + return getProperties().values(); + } +} diff --git a/source/web/WEB-INF/faces-config-beans.xml b/source/web/WEB-INF/faces-config-beans.xml index 3a51125d2b..8e8a202152 100644 --- a/source/web/WEB-INF/faces-config-beans.xml +++ b/source/web/WEB-INF/faces-config-beans.xml @@ -595,6 +595,10 @@ AVMBrowseBean org.alfresco.web.bean.wcm.AVMBrowseBean session + + avmService + #{AVMService} + navigationBean #{NavigationBean} diff --git a/source/web/jsp/wcm/browse-sandbox.jsp b/source/web/jsp/wcm/browse-sandbox.jsp index 03ea044621..0f6ab13059 100644 --- a/source/web/jsp/wcm/browse-sandbox.jsp +++ b/source/web/jsp/wcm/browse-sandbox.jsp @@ -88,6 +88,15 @@ + <%-- Website Path Breadcrumb --%> + + + + + + + + <%-- Details - Folders --%> @@ -100,6 +109,60 @@ styleClass="recordSet" headerStyleClass="recordSetHeader" rowStyleClass="recordSetRow" altRowStyleClass="recordSetRowAlt" width="100%" value="#{AVMBrowseBean.folders}" var="r"> + <%-- Primary column with folder name --%> + + + + + + + + + + + + + + + <%-- Description column for all view modes --%> + + + + + + + + <%-- Created Date column for details view mode --%> + + + + + + + + + + <%-- Modified Date column for details/icons view modes --%> + + + + + + + + + + <%-- Space Actions column --%> + + + + + + <%-- actions are configured in web-client-config-actions.xml --%> + <%----%> + + + @@ -120,6 +183,68 @@ styleClass="recordSet" headerStyleClass="recordSetHeader" rowStyleClass="recordSetRow" altRowStyleClass="recordSetRowAlt" width="100%" value="#{AVMBrowseBean.files}" var="r"> + <%-- Primary column for details view mode --%> + + + + + + + + + + + + <%-- Description column for all view modes --%> + + + + + + + + <%-- Size for details/icons view modes --%> + + + + + + + + + + <%-- Created Date column for details view mode --%> + + + + + + + + + + <%-- Modified Date column for details/icons view modes --%> + + + + + + + + + + <%-- Content Actions column --%> + + + + + + <%-- actions are configured in web-client-config-actions.xml --%> + <%-- --%> + + + +