diff --git a/config/alfresco/messages/webclient.properties b/config/alfresco/messages/webclient.properties
index 3641f99700..bc714587d5 100644
--- a/config/alfresco/messages/webclient.properties
+++ b/config/alfresco/messages/webclient.properties
@@ -106,8 +106,7 @@ close_search=Close Search
browse_spaces=Browse Spaces
browse_content=Content Items
location=Location
-toggle_shelf=Hide or Show the Shelf
-shelf=Shelf
+toggle_shelf=Hide or Show the Sidebar
actions=Actions
view=View
view_details=View Details
@@ -373,6 +372,13 @@ start_discussion=Start Discussion
discuss=View Discussions
discussion_for={0} discussion
+# Sidebar messages
+navigator=Navigator
+navigator_desc=Allows tree based navigation around the repository
+shelf=Shelf
+shelf_desc=Area that includes the clipboard, recent space and shortcuts
+reset_navigator=Re-initialise the navigator
+
# Common Wizard messages
steps=Steps
summary=Summary
diff --git a/config/alfresco/web-client-config-actions.xml b/config/alfresco/web-client-config-actions.xml
index 853ede4c6f..dcce1f3818 100644
--- a/config/alfresco/web-client-config-actions.xml
+++ b/config/alfresco/web-client-config-actions.xml
@@ -562,6 +562,13 @@
#{RulesBean.ignoreInheritedRules}
+
+
+ reset_navigator
+ /images/icons/reset.gif
+ #{NavigatorPluginBean.reset}
+
+
+
+
+
+
diff --git a/config/alfresco/web-client-config.xml b/config/alfresco/web-client-config.xml
index 6d4a92244a..67a1835d17 100644
--- a/config/alfresco/web-client-config.xml
+++ b/config/alfresco/web-client-config.xml
@@ -17,6 +17,7 @@
+
@@ -214,6 +215,20 @@
+
+
+
+
+
+
+
+
+ navigator
+
+
+
diff --git a/source/java/org/alfresco/web/app/Application.java b/source/java/org/alfresco/web/app/Application.java
index 0d3f7f25b3..e006353250 100644
--- a/source/java/org/alfresco/web/app/Application.java
+++ b/source/java/org/alfresco/web/app/Application.java
@@ -38,6 +38,7 @@ import org.alfresco.service.cmr.repository.StoreRef;
import org.alfresco.web.app.servlet.AuthenticationHelper;
import org.alfresco.web.app.servlet.FacesHelper;
import org.alfresco.web.bean.ErrorBean;
+import org.alfresco.web.bean.SidebarBean;
import org.alfresco.web.bean.dashboard.DashboardManager;
import org.alfresco.web.bean.dialog.DialogManager;
import org.alfresco.web.bean.repository.User;
@@ -203,6 +204,16 @@ public class Application
return (DashboardManager)FacesHelper.getManagedBean(FacesContext.getCurrentInstance(), "DashboardManager");
}
+ /**
+ * Retrieves the SidebarBean
+ *
+ * @return SidebarBean bean
+ */
+ public static SidebarBean getSidebarBean()
+ {
+ return (SidebarBean)FacesHelper.getManagedBean(FacesContext.getCurrentInstance(), "SidebarBean");
+ }
+
/**
* Retrieves the configured error page for the application
*
@@ -481,6 +492,7 @@ public class Application
* @param context FacesContext for current user
* @param code The ISO locale code to set
*/
+ @SuppressWarnings("unchecked")
public static void setLanguage(FacesContext context, String code)
{
Locale locale = parseLocale(code);
@@ -633,6 +645,7 @@ public class Application
*
* @return ResourceBundle for this user
*/
+ @SuppressWarnings("unchecked")
public static ResourceBundle getBundle(FacesContext context)
{
// get the resource bundle for the current locale
diff --git a/source/java/org/alfresco/web/app/context/IContextListener.java b/source/java/org/alfresco/web/app/context/IContextListener.java
index effb091079..7b9d8d1c3a 100644
--- a/source/java/org/alfresco/web/app/context/IContextListener.java
+++ b/source/java/org/alfresco/web/app/context/IContextListener.java
@@ -21,7 +21,10 @@ package org.alfresco.web.app.context;
*
* Beans supporting this interface should be register against the UIContextService. Then Beans
* which wish to indicate that the UI should refresh itself i.e. dump all cached data and settings,
- * call the UIContextService.notifyBeans() to inform all registered instances of the change.
+ * call the UIContextService.notifyBeans() to inform all registered instances of the change.
+ *
+ * Registered beans will also be informed of changes in location, for example when the current
+ * space changes or when the user has changed area i.e. from company home to my home.
*
* @author Kevin Roast
*/
@@ -32,4 +35,16 @@ public interface IContextListener
* all UI Beans should refresh dump all cached data and settings.
*/
public void contextUpdated();
+
+ /**
+ * Method called by UIContextService.spaceChanged() to inform all registered beans that
+ * the current space has changed.
+ */
+ public void spaceChanged();
+
+ /**
+ * Method called by UIContextService.areaChanged() to inform all registered beans that
+ * the user has changed area i.e. from company home to my home.
+ */
+ public void areaChanged();
}
diff --git a/source/java/org/alfresco/web/app/context/UIContextService.java b/source/java/org/alfresco/web/app/context/UIContextService.java
index 4335462df6..9ffe220187 100644
--- a/source/java/org/alfresco/web/app/context/UIContextService.java
+++ b/source/java/org/alfresco/web/app/context/UIContextService.java
@@ -24,7 +24,10 @@ import javax.faces.context.FacesContext;
/**
* Beans supporting the IContextListener interface are registered against this class. Then Beans
* which wish to indicate that the UI should refresh itself i.e. dump all cached data and settings,
- * call the UIContextService.notifyBeans() to inform all registered instances of the change.
+ * call the UIContextService.notifyBeans() to inform all registered instances of the change.
+ *
+ * Registered beans will also be informed of changes in location, for example when the current
+ * space changes or when the user has changed area i.e. from company home to my home.
*
* @author Kevin Roast
*/
@@ -42,6 +45,7 @@ public final class UIContextService
*
* @return UIContextService for this Thread
*/
+ @SuppressWarnings("unchecked")
public static UIContextService getInstance(FacesContext fc)
{
Map session = fc.getExternalContext().getSessionMap();
@@ -97,6 +101,29 @@ public final class UIContextService
}
}
+ /**
+ * Call to notify all register beans that the current space has changed and they should
+ * refresh themselves as appropriate.
+ */
+ public void spaceChanged()
+ {
+ for (IContextListener listener: this.registeredBeans.values())
+ {
+ listener.spaceChanged();
+ }
+ }
+
+ /**
+ * Call to notify all register beans that the area i.e. my home, has changed and they should
+ * refresh themselves as appropriate.
+ */
+ public void areaChanged()
+ {
+ for (IContextListener listener: this.registeredBeans.values())
+ {
+ listener.areaChanged();
+ }
+ }
/** key for the UI context service in the session */
private final static String CONTEXT_KEY = "__uiContextService";
diff --git a/source/java/org/alfresco/web/app/servlet/ajax/InvokeCommand.java b/source/java/org/alfresco/web/app/servlet/ajax/InvokeCommand.java
index 9810c33f37..b5da969d0a 100644
--- a/source/java/org/alfresco/web/app/servlet/ajax/InvokeCommand.java
+++ b/source/java/org/alfresco/web/app/servlet/ajax/InvokeCommand.java
@@ -52,9 +52,9 @@ import org.alfresco.web.bean.repository.Repository;
public class InvokeCommand extends BaseAjaxCommand
{
public void execute(final FacesContext facesContext,
- final String expression,
- final HttpServletRequest request,
- final HttpServletResponse response)
+ final String expression,
+ final HttpServletRequest request,
+ final HttpServletResponse response)
throws ServletException, IOException
{
// setup the JSF response writer.
@@ -64,6 +64,8 @@ public class InvokeCommand extends BaseAjaxCommand
// therefore, for now we will always return a content type of text/xml.
// In the future we may use annotations on the method to be called to specify what content
// type should be used for the response.
+ // NOTE: JSF only seems to support XML and HTML content types by default so this will
+ // also need to be addressed if other content types need to be returned i.e. JSON.
OutputStream os = response.getOutputStream();
UIViewRoot viewRoot = facesContext.getViewRoot();
@@ -76,7 +78,7 @@ public class InvokeCommand extends BaseAjaxCommand
facesContext.setResponseWriter(writer);
// must be text/xml otherwise IE doesn't parse the response properly into responseXML
response.setContentType(MimetypeMap.MIMETYPE_XML);
-
+
// create the JSF binding expression
String bindingExpr = makeBindingExpression(expression);
@@ -107,16 +109,19 @@ public class InvokeCommand extends BaseAjaxCommand
{
// rollback the transaction
try { if (tx != null) { tx.rollback(); } } catch (Exception ex) { }
- if (err instanceof EvaluationException)
- {
+
+ if (err instanceof EvaluationException)
+ {
final Throwable cause = ((EvaluationException)err).getCause();
- if (cause != null)
- err = cause;
- }
- logger.error(err);
+ if (cause != null)
+ {
+ err = cause;
+ }
+ }
+
+ logger.error(err);
throw new AlfrescoRuntimeException("Failed to execute method " + expression +
- ": " + err.getMessage(),
- err);
+ ": " + err.getMessage(), err);
}
// force the output back to the client
diff --git a/source/java/org/alfresco/web/bean/BrowseBean.java b/source/java/org/alfresco/web/bean/BrowseBean.java
index f81011a164..74b26c1b7c 100644
--- a/source/java/org/alfresco/web/bean/BrowseBean.java
+++ b/source/java/org/alfresco/web/bean/BrowseBean.java
@@ -25,7 +25,6 @@ import java.util.List;
import java.util.Map;
import java.util.Set;
-import javax.faces.application.FacesMessage;
import javax.faces.context.FacesContext;
import javax.faces.event.ActionEvent;
import javax.transaction.UserTransaction;
@@ -52,7 +51,6 @@ import org.alfresco.service.cmr.search.SearchParameters;
import org.alfresco.service.cmr.search.SearchService;
import org.alfresco.service.namespace.QName;
import org.alfresco.service.namespace.RegexQNamePattern;
-import org.alfresco.web.app.AlfrescoNavigationHandler;
import org.alfresco.web.app.Application;
import org.alfresco.web.app.context.IContextListener;
import org.alfresco.web.app.context.UIContextService;
@@ -485,10 +483,25 @@ public class BrowseBean implements IContextListener
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
*/
@@ -1547,6 +1560,9 @@ public class BrowseBean implements IContextListener
// set up the dispatch context for the navigation handler
this.navigator.setupDispatchContext(new Node(ref));
+ // inform any listeners that the current space has changed
+ UIContextService.getInstance(FacesContext.getCurrentInstance()).spaceChanged();
+
navigateBrowseScreen();
}
@@ -1652,6 +1668,9 @@ public class BrowseBean implements IContextListener
// 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");
}
diff --git a/source/java/org/alfresco/web/bean/CategoriesBean.java b/source/java/org/alfresco/web/bean/CategoriesBean.java
index 599410fd2e..7a44de6f92 100644
--- a/source/java/org/alfresco/web/bean/CategoriesBean.java
+++ b/source/java/org/alfresco/web/bean/CategoriesBean.java
@@ -658,6 +658,21 @@ public class CategoriesBean implements IContextListener
this.categoriesRichList.setValue(null);
}
+ /**
+ * @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
+ }
// ------------------------------------------------------------------------------
// Inner classes
diff --git a/source/java/org/alfresco/web/bean/GroupsBean.java b/source/java/org/alfresco/web/bean/GroupsBean.java
index 5ee6f4a2ef..9fa53f8f35 100644
--- a/source/java/org/alfresco/web/bean/GroupsBean.java
+++ b/source/java/org/alfresco/web/bean/GroupsBean.java
@@ -913,6 +913,21 @@ public class GroupsBean implements IContextListener
this.usersRichList.setValue(null);
}
+ /**
+ * @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
+ }
// ------------------------------------------------------------------------------
// Inner classes
diff --git a/source/java/org/alfresco/web/bean/LoginBean.java b/source/java/org/alfresco/web/bean/LoginBean.java
index 406f26b039..83969e8f87 100644
--- a/source/java/org/alfresco/web/bean/LoginBean.java
+++ b/source/java/org/alfresco/web/bean/LoginBean.java
@@ -39,6 +39,7 @@ import org.alfresco.service.cmr.security.AuthenticationService;
import org.alfresco.service.cmr.security.PersonService;
import org.alfresco.web.app.Application;
import org.alfresco.web.app.servlet.AuthenticationHelper;
+import org.alfresco.web.app.servlet.FacesHelper;
import org.alfresco.web.bean.repository.Repository;
import org.alfresco.web.bean.repository.User;
import org.alfresco.web.config.LanguagesConfigElement;
@@ -336,8 +337,16 @@ public class LoginBean
}
else
{
- // special case to handle jump to My Alfresco page initially
+ // setup the current location with the NavigationBean
String location = this.preferences.getStartLocation();
+ NavigationBean navBean = (NavigationBean)FacesHelper.getManagedBean(
+ fc, "NavigationBean");
+ if (navBean != null)
+ {
+ navBean.setToolbarLocation(location);
+ }
+
+ // special case to handle jump to My Alfresco page initially
if (NavigationBean.LOCATION_MYALFRESCO.equals(location))
{
return "myalfresco";
diff --git a/source/java/org/alfresco/web/bean/NavigationBean.java b/source/java/org/alfresco/web/bean/NavigationBean.java
index 9fcd318a13..5f85d6f6dd 100644
--- a/source/java/org/alfresco/web/bean/NavigationBean.java
+++ b/source/java/org/alfresco/web/bean/NavigationBean.java
@@ -44,6 +44,7 @@ import org.alfresco.service.cmr.security.PermissionService;
import org.alfresco.service.namespace.NamespaceService;
import org.alfresco.web.app.Application;
import org.alfresco.web.app.context.UIContextService;
+import org.alfresco.web.app.servlet.FacesHelper;
import org.alfresco.web.bean.repository.Node;
import org.alfresco.web.bean.repository.Repository;
import org.alfresco.web.bean.repository.User;
@@ -193,6 +194,24 @@ public class NavigationBean
*/
public String getToolbarLocation()
{
+ if (this.toolbarLocation == null)
+ {
+ // if the toolbar location has not been set yet, try and get the
+ // default via the user preferences object
+ UserPreferencesBean prefsBean = (UserPreferencesBean)FacesHelper.getManagedBean(
+ FacesContext.getCurrentInstance(), "UserPreferencesBean");
+ if (prefsBean != null)
+ {
+ this.toolbarLocation = prefsBean.getStartLocation();
+ }
+
+ // if we still haven't found a location default to my home
+ if (this.toolbarLocation == null)
+ {
+ this.toolbarLocation = LOCATION_HOME;
+ }
+ }
+
return this.toolbarLocation;
}
@@ -201,7 +220,7 @@ public class NavigationBean
*/
public void setToolbarLocation(String location)
{
- processToolbarLocation(location, true);
+ this.toolbarLocation = location;
}
/**
@@ -211,7 +230,8 @@ public class NavigationBean
* @param location Toolbar location constant
* @param navigate True to perform navigation, false otherwise
*/
- private void processToolbarLocation(String location, boolean navigate)
+ @SuppressWarnings("serial")
+ public void processToolbarLocation(String location, boolean navigate)
{
this.toolbarLocation = location;
@@ -224,6 +244,9 @@ public class NavigationBean
setLocation(elements);
setCurrentNodeId(companyHome.getId());
+ // inform registered beans that the current area has changed
+ UIContextService.getInstance(FacesContext.getCurrentInstance()).areaChanged();
+
// we need to force a navigation to refresh the browse screen breadcrumb
if (navigate)
{
@@ -240,6 +263,9 @@ public class NavigationBean
setLocation(elements);
setCurrentNodeId(homeSpaceRef.getId());
+ // inform registered beans that the current area has changed
+ UIContextService.getInstance(FacesContext.getCurrentInstance()).areaChanged();
+
// we need to force a navigation to refresh the browse screen breadcrumb
if (navigate)
{
@@ -254,6 +280,9 @@ public class NavigationBean
setLocation(elements);
setCurrentNodeId(guestHome.getId());
+ // inform registered beans that the current area has changed
+ UIContextService.getInstance(FacesContext.getCurrentInstance()).areaChanged();
+
// we need to force a navigation to refresh the browse screen breadcrumb
if (navigate)
{
@@ -274,6 +303,7 @@ public class NavigationBean
List elements = new ArrayList(1);
elements.add(new IBreadcrumbHandler()
{
+ @SuppressWarnings("unchecked")
public String navigationOutcome(UIBreadcrumb breadcrumb)
{
setLocation( (List)breadcrumb.getValue() );
@@ -287,6 +317,9 @@ public class NavigationBean
});
setLocation(elements);
+ // inform registered beans that the current area has changed
+ UIContextService.getInstance(FacesContext.getCurrentInstance()).areaChanged();
+
// we need to force a navigation to refresh the browse screen breadcrumb
if (navigate)
{
@@ -673,7 +706,7 @@ public class NavigationBean
{
UIModeList locationList = (UIModeList)event.getComponent();
String location = locationList.getValue().toString();
- setToolbarLocation(location);
+ processToolbarLocation(location, true);
}
catch (InvalidNodeRefException refErr)
{
@@ -864,7 +897,7 @@ public class NavigationBean
private Node companyHomeNode = null;
/** Current toolbar location */
- private String toolbarLocation = LOCATION_HOME;
+ private String toolbarLocation = null;
/** Search context object we are currently using or null for no search */
private SearchContext searchContext;
diff --git a/source/java/org/alfresco/web/bean/RecentSpacesBean.java b/source/java/org/alfresco/web/bean/RecentSpacesBean.java
index ddf1f931d6..b8ffa027b6 100644
--- a/source/java/org/alfresco/web/bean/RecentSpacesBean.java
+++ b/source/java/org/alfresco/web/bean/RecentSpacesBean.java
@@ -175,6 +175,25 @@ public class RecentSpacesBean implements IContextListener
this.recentSpaces.add(0, node);
}
+ /**
+ * @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
+ }
+
+ // ------------------------------------------------------------------------------
+ // Helper methods
+
/**
* @return the max number of recent spaces to show, retrieved from client config
*/
diff --git a/source/java/org/alfresco/web/bean/SidebarBean.java b/source/java/org/alfresco/web/bean/SidebarBean.java
new file mode 100644
index 0000000000..021de76d62
--- /dev/null
+++ b/source/java/org/alfresco/web/bean/SidebarBean.java
@@ -0,0 +1,169 @@
+package org.alfresco.web.bean;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.faces.context.FacesContext;
+import javax.faces.event.ActionEvent;
+
+import org.alfresco.config.Config;
+import org.alfresco.web.app.Application;
+import org.alfresco.web.config.SidebarConfigElement;
+import org.alfresco.web.config.SidebarConfigElement.SidebarPluginConfig;
+import org.alfresco.web.ui.common.component.UIListItem;
+import org.alfresco.web.ui.common.component.UIModeList;
+
+/**
+ * Managed bean used by the sidebar component to manage it's state.
+ *
+ * @author gavinc
+ */
+public class SidebarBean
+{
+ protected String activePlugin;
+ protected List plugins;
+ protected SidebarConfigElement sidebarConfig;
+
+ /**
+ * Default constructor
+ */
+ public SidebarBean()
+ {
+ // get the sidebar config object
+ this.sidebarConfig = getSidebarConfig(FacesContext.getCurrentInstance());
+
+ // make sure we found the config
+ if (this.sidebarConfig == null)
+ {
+ throw new IllegalStateException("Failed to find configuration for the sidebar");
+ }
+
+ // build the list of plugins available and check we have at least one
+ List items = this.getPlugins();
+ if (items.size() == 0)
+ {
+ throw new IllegalStateException("Failed to find configuration for any sidebar plugins, at least one must be defined!");
+ }
+
+ // determine the default plugin
+ this.activePlugin = this.sidebarConfig.getDefaultPlugin();
+ if (this.activePlugin == null)
+ {
+ this.activePlugin = (String)items.get(0).getValue();
+ }
+ }
+
+ // ------------------------------------------------------------------------------
+ // Event handlers
+
+ public void pluginChanged(ActionEvent event)
+ {
+ UIModeList pluginList = (UIModeList)event.getComponent();
+
+ // get the selected plugin
+ this.activePlugin = pluginList.getValue().toString();
+ }
+
+ // ------------------------------------------------------------------------------
+ // Bean Getters and Setters
+
+ /**
+ * Returns a list of configured plugins
+ *
+ * @return List of UIListItem's representing the plugins available
+ */
+ public List getPlugins()
+ {
+ if (this.plugins == null)
+ {
+ FacesContext context = FacesContext.getCurrentInstance();
+ this.plugins = new ArrayList();
+
+ // create a list entry for each configured plugin
+ for (String pluginId : this.sidebarConfig.getPlugins().keySet())
+ {
+ SidebarPluginConfig plugin = this.sidebarConfig.getPlugin(pluginId);
+
+ // resolve the label for the plugin
+ String label = plugin.getlabelId();
+ if (label != null)
+ {
+ label = Application.getMessage(context, label);
+ }
+ if (label == null)
+ {
+ label = plugin.getlabel();
+ }
+ if (label == null)
+ {
+ label = plugin.getId();
+ }
+
+ // resolve the description (tooltip for the plugin)
+ String tooltip = plugin.getDescriptionId();
+ if (tooltip != null)
+ {
+ tooltip = Application.getMessage(context, tooltip);
+ }
+ if (tooltip == null)
+ {
+ tooltip = plugin.getDescription();
+ }
+
+ UIListItem item = new UIListItem();
+ item.setValue(plugin.getId());
+ item.setLabel(label);
+ if (tooltip != null)
+ {
+ item.setTooltip(tooltip);
+ }
+
+ this.plugins.add(item);
+ }
+ }
+
+ return this.plugins;
+ }
+
+ /**
+ * Returns the id of the currently active plugin
+ *
+ * @return Id of the current plugin
+ */
+ public String getActivePlugin()
+ {
+ return activePlugin;
+ }
+
+ /**
+ * Returns the path of the JSP to use for the current plugin
+ *
+ * @return JSP to use for the current plugin
+ */
+ public String getActivePluginPage()
+ {
+ return this.sidebarConfig.getPlugin(this.activePlugin).getPage();
+ }
+
+ // ------------------------------------------------------------------------------
+ // Helper methods
+
+ /**
+ * Returns the SidebarConfigElement for the application
+ *
+ * @param context Faces context
+ * @return The SidebarConfigElement object or null if it's not found
+ */
+ public static SidebarConfigElement getSidebarConfig(FacesContext context)
+ {
+ SidebarConfigElement config = null;
+
+ Config cfg = Application.getConfigService(context).getConfig("Sidebar");
+ if (cfg != null)
+ {
+ config = (SidebarConfigElement)cfg.getConfigElement(SidebarConfigElement.CONFIG_ELEMENT_ID);
+ }
+
+ return config;
+ }
+}
diff --git a/source/java/org/alfresco/web/bean/TrashcanBean.java b/source/java/org/alfresco/web/bean/TrashcanBean.java
index eba6e8ee2f..e8748b585f 100644
--- a/source/java/org/alfresco/web/bean/TrashcanBean.java
+++ b/source/java/org/alfresco/web/bean/TrashcanBean.java
@@ -1255,4 +1255,20 @@ public class TrashcanBean implements IContextListener
}
this.showItems = false;
}
+
+ /**
+ * @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
+ }
}
diff --git a/source/java/org/alfresco/web/bean/ajax/NavigatorPluginBean.java b/source/java/org/alfresco/web/bean/ajax/NavigatorPluginBean.java
new file mode 100644
index 0000000000..16ef63176d
--- /dev/null
+++ b/source/java/org/alfresco/web/bean/ajax/NavigatorPluginBean.java
@@ -0,0 +1,610 @@
+package org.alfresco.web.bean.ajax;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import javax.faces.context.FacesContext;
+import javax.faces.context.ResponseWriter;
+
+import org.alfresco.model.ApplicationModel;
+import org.alfresco.model.ContentModel;
+import org.alfresco.service.cmr.dictionary.DictionaryService;
+import org.alfresco.service.cmr.dictionary.TypeDefinition;
+import org.alfresco.service.cmr.repository.ChildAssociationRef;
+import org.alfresco.service.cmr.repository.NodeRef;
+import org.alfresco.service.cmr.repository.NodeService;
+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.FacesHelper;
+import org.alfresco.web.bean.BrowseBean;
+import org.alfresco.web.bean.NavigationBean;
+import org.alfresco.web.bean.repository.Repository;
+import org.alfresco.web.ui.repo.component.UITree.TreeNode;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+/**
+ * Bean used by the navigator component to manage the tree data.
+ *
+ * @author gavinc
+ */
+public class NavigatorPluginBean implements IContextListener
+{
+ public static final String BEAN_NAME = "NavigatorPluginBean";
+
+ protected List companyHomeRootNodes;
+ protected List myHomeRootNodes;
+ protected List guestHomeRootNodes;
+ protected Map companyHomeNodes;
+ protected Map myHomeNodes;
+ protected Map guestHomeNodes;
+ protected NodeRef previouslySelectedNode;
+
+ private NodeService nodeService;
+ private DictionaryService dictionaryService;
+
+ private static final Log logger = LogFactory.getLog(NavigatorPluginBean.class);
+
+ public NavigatorPluginBean()
+ {
+ UIContextService.getInstance(FacesContext.getCurrentInstance()).registerBean(this);
+ }
+
+ // ------------------------------------------------------------------------------
+ // AJAX handler methods
+
+ /**
+ * Retrieves the child folders for the noderef given in the
+ * 'noderef' parameter and caches the nodes against the area in
+ * the 'area' parameter.
+ */
+ public void retrieveChildren() throws IOException
+ {
+ FacesContext context = FacesContext.getCurrentInstance();
+ ResponseWriter out = context.getResponseWriter();
+
+ Map params = context.getExternalContext().getRequestParameterMap();
+ String nodeRefStr = (String)params.get("nodeRef");
+ String area = (String)params.get("area");
+
+ if (logger.isDebugEnabled())
+ logger.debug("retrieveChildren: area = " + area + ", nodeRef = " + nodeRefStr);
+
+ // work out which list to cache the nodes in
+ Map currentNodes = getNodesMapForArea(area);
+
+ if (nodeRefStr != null && currentNodes != null)
+ {
+ // get the given node's details
+ NodeRef parentNodeRef = new NodeRef(nodeRefStr);
+ TreeNode parentNode = currentNodes.get(parentNodeRef.toString());
+ parentNode.setExpanded(true);
+
+ if (logger.isDebugEnabled())
+ logger.debug("retrieving children for noderef: " + parentNodeRef);
+
+ // remove any existing children as the latest ones will be added below
+ parentNode.removeChildren();
+
+ // get all the child folder objects for the parent
+ List childRefs = this.nodeService.getChildAssocs(parentNodeRef,
+ ContentModel.ASSOC_CONTAINS, RegexQNamePattern.MATCH_ALL);
+
+ StringBuilder xml = new StringBuilder("");
+ for (ChildAssociationRef ref: childRefs)
+ {
+ NodeRef nodeRef = ref.getChildRef();
+
+ if (isAddableChild(nodeRef))
+ {
+ // build the XML representation of the child node
+ TreeNode childNode = createTreeNode(nodeRef);
+ parentNode.addChild(childNode);
+ currentNodes.put(childNode.getNodeRef(), childNode);
+ xml.append(childNode.toXML());
+ }
+ }
+ xml.append("");
+
+ // send the generated XML back to the tree
+ out.write(xml.toString());
+
+ if (logger.isDebugEnabled())
+ logger.debug("returning XML: " + xml.toString());
+ }
+ }
+
+ /**
+ * Sets the state of the node given in the 'nodeRef' parameter to collapsed
+ */
+ public void nodeCollapsed() throws IOException
+ {
+ FacesContext context = FacesContext.getCurrentInstance();
+ ResponseWriter out = context.getResponseWriter();
+ Map params = context.getExternalContext().getRequestParameterMap();
+ String nodeRefStr = (String)params.get("nodeRef");
+ String area = (String)params.get("area");
+
+ if (logger.isDebugEnabled())
+ logger.debug("nodeCollapsed: area = " + area + ", nodeRef = " + nodeRefStr);
+
+ // work out which list to cache the nodes in
+ Map currentNodes = getNodesMapForArea(area);
+
+ if (nodeRefStr != null && currentNodes != null)
+ {
+ TreeNode treeNode = currentNodes.get(nodeRefStr);
+ if (treeNode != null)
+ {
+ treeNode.setExpanded(false);
+
+ // we need to return something for the client to be happy!
+ out.write("");
+
+ if (logger.isDebugEnabled())
+ logger.debug("Set node " + treeNode + " to collapsed state");
+ }
+ }
+ }
+
+ // ------------------------------------------------------------------------------
+ // IContextListener implementation
+
+ /**
+ * @see org.alfresco.web.app.context.IContextListener#contextUpdated()
+ */
+ public void contextUpdated()
+ {
+ // nothing to do
+ }
+
+ /**
+ * @see org.alfresco.web.app.context.IContextListener#areaChanged()
+ */
+ public void areaChanged()
+ {
+ // nothing to do
+
+ // NOTE: The code below is WIP for synchronizing the tree with
+ // the main navigation area of the application.
+
+ /*
+ this.resetSelectedNode();
+ */
+ }
+
+ /**
+ * @see org.alfresco.web.app.context.IContextListener#spaceChanged()
+ */
+ public void spaceChanged()
+ {
+ // nothing to do
+
+ // NOTE: The code below is WIP for synchronizing the tree with
+ // the main navigation area of the application.
+
+ /*
+ NavigationBean navBean = getNavigationBean();
+ if (navBean != null)
+ {
+ // get the current area and the new parent
+ String area = navBean.getToolbarLocation();
+ Node parent = navBean.getDispatchContextNode();
+
+ if (parent != null)
+ {
+ if (logger.isDebugEnabled())
+ logger.debug("Space changed, parent node is now: " + parent.getNodeRef().toString());
+
+ // select the new parent node
+ selectNode(parent.getNodeRef(), area);
+
+ // get the nodes for the current area
+ BrowseBean browseBean = getBrowseBean();
+ Map currentNodes = getNodesMapForArea(area);
+
+ if (browseBean != null && currentNodes != null)
+ {
+ // find the parent node in the cache
+ TreeNode parentNode = currentNodes.get(parent.getNodeRef().toString());
+ if (parentNode != null)
+ {
+ // reset the previously selected node
+ resetSelectedNode();
+
+ // set the parent to expanded and selected
+ parentNode.setExpanded(true);
+ selectNode(parent.getNodeRef(), area);
+
+ for (Node child : browseBean.getNodes())
+ {
+ NodeRef nodeRef = child.getNodeRef();
+
+ // check the child is applicable for the tree and is not already
+ // in the cache
+ if (isAddableChild(nodeRef) &&
+ currentNodes.containsKey(nodeRef.toString()) == false)
+ {
+ // create the child tree node and add to the cache
+ TreeNode childNode = createTreeNode(nodeRef);
+ parentNode.addChild(childNode);
+ currentNodes.put(childNode.getNodeRef(), childNode);
+ }
+ }
+ }
+ }
+ }
+ }
+ */
+ }
+
+ // ------------------------------------------------------------------------------
+ // Bean getters and setters
+
+ /**
+ * Returns the root nodes for the company home panel.
+ *
+ * As the user expands and collapses nodes in the client this
+ * cache will be updated with the appropriate nodes and states.
+ *
+ *
+ * @return List of root nodes for the company home panel
+ */
+ public List getCompanyHomeRootNodes()
+ {
+ if (this.companyHomeRootNodes == null)
+ {
+ this.companyHomeRootNodes = new ArrayList();
+ this.companyHomeNodes = new HashMap();
+
+ // query for the child nodes of company home
+ NodeRef root = new NodeRef(Repository.getStoreRef(),
+ Application.getCompanyRootId());
+ List childRefs = this.nodeService.getChildAssocs(root,
+ ContentModel.ASSOC_CONTAINS, RegexQNamePattern.MATCH_ALL);
+
+ for (ChildAssociationRef ref: childRefs)
+ {
+ NodeRef child = ref.getChildRef();
+
+ if (isAddableChild(child))
+ {
+ TreeNode node = createTreeNode(child);
+ this.companyHomeRootNodes.add(node);
+ this.companyHomeNodes.put(node.getNodeRef(), node);
+ }
+ }
+ }
+
+ return this.companyHomeRootNodes;
+ }
+
+ /**
+ * Returns the root nodes for the my home panel.
+ *
+ * As the user expands and collapses nodes in the client this
+ * cache will be updated with the appropriate nodes and states.
+ *
+ *
+ * @return List of root nodes for the my home panel
+ */
+ public List getMyHomeRootNodes()
+ {
+ if (this.myHomeRootNodes == null)
+ {
+ this.myHomeRootNodes = new ArrayList();
+ this.myHomeNodes = new HashMap();
+
+ // query for the child nodes of the user's home
+ NodeRef root = new NodeRef(Repository.getStoreRef(),
+ Application.getCurrentUser(FacesContext.getCurrentInstance()).getHomeSpaceId());
+ List childRefs = this.nodeService.getChildAssocs(root,
+ ContentModel.ASSOC_CONTAINS, RegexQNamePattern.MATCH_ALL);
+
+ for (ChildAssociationRef ref: childRefs)
+ {
+ NodeRef child = ref.getChildRef();
+
+ if (isAddableChild(child))
+ {
+ TreeNode node = createTreeNode(child);
+ this.myHomeRootNodes.add(node);
+ this.myHomeNodes.put(node.getNodeRef(), node);
+ }
+ }
+ }
+
+ return this.myHomeRootNodes;
+ }
+
+ /**
+ * Returns the root nodes for the guest home panel.
+ *
+ * As the user expands and collapses nodes in the client this
+ * cache will be updated with the appropriate nodes and states.
+ *
+ *
+ * @return List of root nodes for the guest home panel
+ */
+ public List getGuestHomeRootNodes()
+ {
+ if (this.guestHomeRootNodes == null)
+ {
+ this.guestHomeRootNodes = new ArrayList();
+ this.guestHomeNodes = new HashMap();
+
+ // query for the child nodes of the guest home space
+ NavigationBean navBean = getNavigationBean();
+ if (navBean != null)
+ {
+ NodeRef root = navBean.getGuestHomeNode().getNodeRef();
+ List childRefs = this.nodeService.getChildAssocs(root,
+ ContentModel.ASSOC_CONTAINS, RegexQNamePattern.MATCH_ALL);
+
+ for (ChildAssociationRef ref: childRefs)
+ {
+ NodeRef child = ref.getChildRef();
+
+ if (isAddableChild(child))
+ {
+ TreeNode node = createTreeNode(child);
+ this.guestHomeRootNodes.add(node);
+ this.guestHomeNodes.put(node.getNodeRef(), node);
+ }
+ }
+ }
+ }
+
+ return this.guestHomeRootNodes;
+ }
+
+ /**
+ * @param nodeService The NodeService to set.
+ */
+ public void setNodeService(NodeService nodeService)
+ {
+ this.nodeService = nodeService;
+ }
+
+ /**
+ * @param dictionaryService The DictionaryService to set.
+ */
+ public void setDictionaryService(DictionaryService dictionaryService)
+ {
+ this.dictionaryService = dictionaryService;
+ }
+
+ // ------------------------------------------------------------------------------
+ // Helper methods
+
+ /**
+ * Sets the currently selected node in the tree
+ *
+ * @param selectedNode The node that has been selected
+ */
+ public void selectNode(NodeRef selectedNode, String area)
+ {
+ // if there is a currently selected node, get hold of
+ // it (from any of the areas) and reset to unselected
+ if (this.previouslySelectedNode != null)
+ {
+ if (NavigationBean.LOCATION_COMPANY.equals(area) &&
+ this.companyHomeNodes != null)
+ {
+ TreeNode node = this.companyHomeNodes.get(this.previouslySelectedNode.toString());
+ if (node != null)
+ {
+ node.setSelected(false);
+ }
+ }
+ else if (NavigationBean.LOCATION_HOME.equals(area) &&
+ this.myHomeNodes != null)
+ {
+ TreeNode node = this.myHomeNodes.get(this.previouslySelectedNode.toString());
+ if (node != null)
+ {
+ node.setSelected(false);
+ }
+ }
+ else if (NavigationBean.LOCATION_GUEST.equals(area) &&
+ this.guestHomeNodes != null)
+ {
+ TreeNode node = this.guestHomeNodes.get(this.previouslySelectedNode.toString());
+ if (node != null)
+ {
+ node.setSelected(false);
+ }
+ }
+ }
+
+ // find the node just selected and set its state to selected
+ if (selectedNode != null)
+ {
+ if (NavigationBean.LOCATION_COMPANY.equals(area) &&
+ this.companyHomeNodes != null)
+ {
+ TreeNode node = this.companyHomeNodes.get(selectedNode.toString());
+ if (node != null)
+ {
+ node.setSelected(true);
+ }
+ }
+ else if (NavigationBean.LOCATION_HOME.equals(area) &&
+ this.myHomeNodes != null)
+ {
+ TreeNode node = this.myHomeNodes.get(selectedNode.toString());
+ if (node != null)
+ {
+ node.setSelected(true);
+ }
+ }
+ else if (NavigationBean.LOCATION_GUEST.equals(area) &&
+ this.guestHomeNodes != null)
+ {
+ TreeNode node = this.guestHomeNodes.get(selectedNode.toString());
+ if (node != null)
+ {
+ node.setSelected(true);
+ }
+ }
+ }
+
+ this.previouslySelectedNode = selectedNode;
+
+ if (logger.isDebugEnabled())
+ logger.debug("Selected node: " + selectedNode);
+ }
+
+ /**
+ * Resets the selected node
+ */
+ public void resetSelectedNode()
+ {
+ if (this.previouslySelectedNode != null)
+ {
+ if (this.companyHomeNodes != null)
+ {
+ TreeNode node = this.companyHomeNodes.get(this.previouslySelectedNode.toString());
+ if (node != null)
+ {
+ node.setSelected(false);
+ }
+ }
+ if (this.myHomeNodes != null)
+ {
+ TreeNode node = this.myHomeNodes.get(this.previouslySelectedNode.toString());
+ if (node != null)
+ {
+ node.setSelected(false);
+ }
+ }
+ if (this.guestHomeNodes != null)
+ {
+ TreeNode node = this.guestHomeNodes.get(this.previouslySelectedNode.toString());
+ if (node != null)
+ {
+ node.setSelected(false);
+ }
+ }
+
+ if (logger.isDebugEnabled())
+ logger.debug("Reset selected node: " + this.previouslySelectedNode);
+ }
+ }
+
+ /**
+ * Resets all the caches held by the bean.
+ */
+ public void reset()
+ {
+ this.companyHomeNodes = null;
+ this.companyHomeRootNodes = null;
+ this.myHomeNodes = null;
+ this.myHomeRootNodes = null;
+ this.guestHomeNodes = null;
+ this.guestHomeRootNodes = null;
+
+ resetSelectedNode();
+ }
+
+ /**
+ * Determines whether the given NodeRef can be added to the tree as
+ * a child for example, if it's a folder.
+ *
+ * @param nodeRef The NodeRef to check
+ * @return true if the node should be added to the tree
+ */
+ protected boolean isAddableChild(NodeRef nodeRef)
+ {
+ boolean addable = false;
+
+ if (this.nodeService.exists(nodeRef))
+ {
+ // find it's type so we can see if it's a node we are interested in
+ QName type = this.nodeService.getType(nodeRef);
+
+ // make sure the type is defined in the data dictionary
+ TypeDefinition typeDef = this.dictionaryService.getType(type);
+
+ if (typeDef != null)
+ {
+ // look for folder node types
+ if (this.dictionaryService.isSubClass(type, ContentModel.TYPE_FOLDER) == true &&
+ this.dictionaryService.isSubClass(type, ContentModel.TYPE_SYSTEM_FOLDER) == false)
+ {
+ addable = true;
+ }
+ }
+ }
+
+ return addable;
+ }
+
+ /**
+ * Creates a TreeNode object from the given NodeRef
+ *
+ * @param nodeRef The NodeRef to create the TreeNode from
+ */
+ protected TreeNode createTreeNode(NodeRef nodeRef)
+ {
+ TreeNode node = new TreeNode(nodeRef.toString(),
+ Repository.getNameForNode(this.nodeService, nodeRef),
+ (String)this.nodeService.getProperty(nodeRef, ApplicationModel.PROP_ICON));
+
+ return node;
+ }
+
+ /**
+ * Retrieves the instance of the NavigationBean being used by the application
+ *
+ * @return NavigationBean instance
+ */
+ protected NavigationBean getNavigationBean()
+ {
+ return (NavigationBean)FacesHelper.getManagedBean(
+ FacesContext.getCurrentInstance(), "NavigationBean");
+ }
+
+ /**
+ * Retrieves the instance of the BrowseBean being used by the application
+ *
+ * @return BrowseBean instance
+ */
+ protected BrowseBean getBrowseBean()
+ {
+ return (BrowseBean)FacesHelper.getManagedBean(
+ FacesContext.getCurrentInstance(), "BrowseBean");
+ }
+
+ /**
+ * Returns the map of tree nodes for the given area
+ *
+ * @param area The area to retrieve the map for
+ * @return The map of nodes
+ */
+ protected Map getNodesMapForArea(String area)
+ {
+ Map nodes = null;
+
+ if (NavigationBean.LOCATION_COMPANY.equals(area))
+ {
+ nodes = this.companyHomeNodes;
+ }
+ else if (NavigationBean.LOCATION_HOME.equals(area))
+ {
+ nodes = this.myHomeNodes;
+ }
+ else if (NavigationBean.LOCATION_GUEST.equals(area))
+ {
+ nodes = this.guestHomeNodes;
+ }
+
+ return nodes;
+ }
+}
diff --git a/source/java/org/alfresco/web/bean/forums/ForumsBean.java b/source/java/org/alfresco/web/bean/forums/ForumsBean.java
index 81d016fd86..b0a7220490 100644
--- a/source/java/org/alfresco/web/bean/forums/ForumsBean.java
+++ b/source/java/org/alfresco/web/bean/forums/ForumsBean.java
@@ -649,6 +649,21 @@ public class ForumsBean implements IContextListener
this.posts = null;
}
+ /**
+ * @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
+ }
// ------------------------------------------------------------------------------
// Navigation action event handlers
diff --git a/source/java/org/alfresco/web/bean/rules/RulesBean.java b/source/java/org/alfresco/web/bean/rules/RulesBean.java
index 2a45f58c61..61bf880e7f 100644
--- a/source/java/org/alfresco/web/bean/rules/RulesBean.java
+++ b/source/java/org/alfresco/web/bean/rules/RulesBean.java
@@ -388,6 +388,21 @@ public class RulesBean implements IContextListener
}
}
+ /**
+ * @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
+ }
/**
* Inner class to wrap the Rule objects so we can expose a flag to indicate whether
diff --git a/source/java/org/alfresco/web/bean/users/EmailSpaceUsersDialog.java b/source/java/org/alfresco/web/bean/users/EmailSpaceUsersDialog.java
index 1bf167d1ae..6ce362fa78 100644
--- a/source/java/org/alfresco/web/bean/users/EmailSpaceUsersDialog.java
+++ b/source/java/org/alfresco/web/bean/users/EmailSpaceUsersDialog.java
@@ -190,10 +190,25 @@ public class EmailSpaceUsersDialog extends BaseDialogBean implements IContextLis
this.userGroupLookup = new HashMap();
}
+ /**
+ * @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
+ }
// ------------------------------------------------------------------------------
// Bean Getters and Setters
-
+
/**
* @param permissionService The PermissionService to set
*/
diff --git a/source/java/org/alfresco/web/bean/users/UserMembersBean.java b/source/java/org/alfresco/web/bean/users/UserMembersBean.java
index 039b8d4d74..f6e2cfe5bd 100644
--- a/source/java/org/alfresco/web/bean/users/UserMembersBean.java
+++ b/source/java/org/alfresco/web/bean/users/UserMembersBean.java
@@ -424,6 +424,21 @@ public abstract class UserMembersBean implements IContextListener
}
}
+ /**
+ * @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
+ }
// ------------------------------------------------------------------------------
// Action event handlers
diff --git a/source/java/org/alfresco/web/bean/users/UsersBean.java b/source/java/org/alfresco/web/bean/users/UsersBean.java
index 5653d090f0..40134f4ddb 100644
--- a/source/java/org/alfresco/web/bean/users/UsersBean.java
+++ b/source/java/org/alfresco/web/bean/users/UsersBean.java
@@ -544,4 +544,20 @@ public class UsersBean implements IContextListener
this.users = null;
}
}
+
+ /**
+ * @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
+ }
}
diff --git a/source/java/org/alfresco/web/bean/wcm/AVMBrowseBean.java b/source/java/org/alfresco/web/bean/wcm/AVMBrowseBean.java
index ad15b9311d..46ac770d09 100644
--- a/source/java/org/alfresco/web/bean/wcm/AVMBrowseBean.java
+++ b/source/java/org/alfresco/web/bean/wcm/AVMBrowseBean.java
@@ -1184,6 +1184,21 @@ public class AVMBrowseBean implements IContextListener
this.folders = null;
}
+ /**
+ * @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
+ }
// ------------------------------------------------------------------------------
// Inner classes
diff --git a/source/java/org/alfresco/web/config/SidebarConfigElement.java b/source/java/org/alfresco/web/config/SidebarConfigElement.java
new file mode 100644
index 0000000000..fc031bff69
--- /dev/null
+++ b/source/java/org/alfresco/web/config/SidebarConfigElement.java
@@ -0,0 +1,241 @@
+/*
+ * 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.config;
+
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.alfresco.config.ConfigElement;
+import org.alfresco.config.ConfigException;
+import org.alfresco.config.element.ConfigElementAdapter;
+import org.alfresco.util.ParameterCheck;
+
+/**
+ * Custom config element that represents the config data for the sidebar
+ *
+ * @author gavinc
+ */
+public class SidebarConfigElement extends ConfigElementAdapter
+{
+ public static final String CONFIG_ELEMENT_ID = "sidebar";
+
+ private String defaultPlugin;
+ private Map plugins = new LinkedHashMap(8, 10f);
+
+ /**
+ * Default constructor
+ */
+ public SidebarConfigElement()
+ {
+ super(CONFIG_ELEMENT_ID);
+ }
+
+ /**
+ * Constructor
+ *
+ * @param name Name of the element this config element represents
+ */
+ public SidebarConfigElement(String name)
+ {
+ super(name);
+ }
+
+ /**
+ * @see org.alfresco.config.ConfigElement#getChildren()
+ */
+ public List getChildren()
+ {
+ throw new ConfigException("Reading the sidebar config via the generic interfaces is not supported");
+ }
+
+ /**
+ * @see org.alfresco.config.ConfigElement#combine(org.alfresco.config.ConfigElement)
+ */
+ public ConfigElement combine(ConfigElement configElement)
+ {
+ SidebarConfigElement newElement = (SidebarConfigElement)configElement;
+ SidebarConfigElement combinedElement = new SidebarConfigElement();
+
+ // add all the plugins from this element
+ for (SidebarPluginConfig plugin : this.getPlugins().values())
+ {
+ combinedElement.addPlugin(plugin);
+ }
+
+ // add all the plugins from the given element
+ for (SidebarPluginConfig plugin : newElement.getPlugins().values())
+ {
+ combinedElement.addPlugin(plugin);
+ }
+
+ // work out the default plugin
+ String newDefaultPlugin = newElement.getDefaultPlugin();
+ if (newDefaultPlugin != null)
+ {
+ combinedElement.setDefaultPlugin(newDefaultPlugin);
+ }
+ else
+ {
+ combinedElement.setDefaultPlugin(this.getDefaultPlugin());
+ }
+
+ return combinedElement;
+ }
+
+ /**
+ * Returns the named plugin
+ *
+ * @param id The id of the plugin to retrieve
+ * @return The SidebarPluginConfig object for the requested plugin or null if it doesn't exist
+ */
+ public SidebarPluginConfig getPlugin(String id)
+ {
+ return this.plugins.get(id);
+ }
+
+ /**
+ * @return Returns a map of the plugins. A linked hash map is used internally to
+ * preserve ordering.
+ */
+ public Map getPlugins()
+ {
+ return this.plugins;
+ }
+
+ /**
+ * @return The id of the default plugin, null if there isn't a default defined
+ */
+ public String getDefaultPlugin()
+ {
+ return this.defaultPlugin;
+ }
+
+ /**
+ * Sets the plugin to use as the default
+ *
+ * @param defaultPlugin Id of the default plugin
+ */
+ public void setDefaultPlugin(String defaultPlugin)
+ {
+ this.defaultPlugin = defaultPlugin;
+ }
+
+ /**
+ * Adds a plugin
+ *
+ * @param pluginConfig A pre-configured plugin config object
+ */
+ /*package*/ void addPlugin(SidebarPluginConfig pluginConfig)
+ {
+ this.plugins.put(pluginConfig.getId(), pluginConfig);
+ }
+
+ /**
+ * Inner class representing the configuration of a sidebar plugin
+ *
+ * @author gavinc
+ */
+ public static class SidebarPluginConfig
+ {
+ protected String id;
+ protected String page;
+ protected String actionsConfigId;
+ protected String icon;
+ protected String label;
+ protected String labelId;
+ protected String description;
+ protected String descriptionId;
+
+ public SidebarPluginConfig(String id, String page,
+ String label, String labelId,
+ String description, String descriptionId,
+ String actionsConfigId, String icon)
+ {
+ // check the mandatory parameters are present
+ ParameterCheck.mandatoryString("id", id);
+ ParameterCheck.mandatoryString("page", page);
+
+ this.id = id;
+ this.page = page;
+ this.icon = icon;
+ this.label = label;
+ this.labelId = labelId;
+ this.description = description;
+ this.descriptionId = descriptionId;
+ this.actionsConfigId = actionsConfigId;
+ }
+
+ public String getId()
+ {
+ return this.id;
+ }
+
+ public String getPage()
+ {
+ return this.page;
+ }
+
+ public String getIcon()
+ {
+ return this.icon;
+ }
+
+ public String getlabel()
+ {
+ return this.label;
+ }
+
+ public String getlabelId()
+ {
+ return this.labelId;
+ }
+
+ public String getDescription()
+ {
+ return this.description;
+ }
+
+ public String getDescriptionId()
+ {
+ return this.descriptionId;
+ }
+
+ public String getActionsConfigId()
+ {
+ return this.actionsConfigId;
+ }
+
+ /**
+ * @see java.lang.Object#toString()
+ */
+ @Override
+ public String toString()
+ {
+ StringBuilder buffer = new StringBuilder(super.toString());
+ buffer.append(" (id=").append(this.id);
+ buffer.append(" page=").append(this.page);
+ buffer.append(" icon=").append(this.icon);
+ buffer.append(" label=").append(this.label);
+ buffer.append(" labelId=").append(this.labelId);
+ buffer.append(" description=").append(this.description);
+ buffer.append(" descriptionId=").append(this.descriptionId);
+ buffer.append(" actions-config-id=").append(this.actionsConfigId).append(")");
+ return buffer.toString();
+ }
+ }
+}
diff --git a/source/java/org/alfresco/web/config/SidebarElementReader.java b/source/java/org/alfresco/web/config/SidebarElementReader.java
new file mode 100644
index 0000000000..834f791b55
--- /dev/null
+++ b/source/java/org/alfresco/web/config/SidebarElementReader.java
@@ -0,0 +1,103 @@
+/*
+ * 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.config;
+
+import java.util.Iterator;
+
+import org.alfresco.config.ConfigElement;
+import org.alfresco.config.ConfigException;
+import org.alfresco.config.xml.elementreader.ConfigElementReader;
+import org.dom4j.Element;
+
+/**
+ * Custom element reader to parse config for the sidebar
+ *
+ * @author gavinc
+ */
+public class SidebarElementReader implements ConfigElementReader
+{
+ public static final String ELEMENT_SIDEBAR = "sidebar";
+ public static final String ELEMENT_PLUGINS = "plugins";
+ public static final String ELEMENT_PLUGIN = "plugin";
+ public static final String ELEMENT_DEFAULT_PLUGIN = "default-plugin";
+ public static final String ATTR_ID = "id";
+ public static final String ATTR_LABEL = "label";
+ public static final String ATTR_LABEL_ID = "label-id";
+ public static final String ATTR_DESCRIPTION = "description";
+ public static final String ATTR_DESCRIPTION_ID = "description-id";
+ public static final String ATTR_PAGE = "page";
+ public static final String ATTR_ICON = "icon";
+ public static final String ATTR_ACTIONS_CONFIG_ID = "actions-config-id";
+
+ /**
+ * @see org.alfresco.config.xml.elementreader.ConfigElementReader#parse(org.dom4j.Element)
+ */
+ @SuppressWarnings("unchecked")
+ public ConfigElement parse(Element element)
+ {
+ SidebarConfigElement configElement = null;
+
+ if (element != null)
+ {
+ String elementName = element.getName();
+ if (elementName.equals(ELEMENT_SIDEBAR) == false)
+ {
+ throw new ConfigException("SidebarElementReader can only parse " +
+ ELEMENT_SIDEBAR + "elements, the element passed was '" +
+ elementName + "'");
+ }
+
+ configElement = new SidebarConfigElement();
+
+ // go through the plugins that make up the sidebar
+ Element pluginsElem = element.element(ELEMENT_PLUGINS);
+ if (pluginsElem != null)
+ {
+ Iterator plugins = pluginsElem.elementIterator(ELEMENT_PLUGIN);
+ while (plugins.hasNext())
+ {
+ Element plugin = plugins.next();
+
+ String id = plugin.attributeValue(ATTR_ID);
+ String page = plugin.attributeValue(ATTR_PAGE);
+ String label = plugin.attributeValue(ATTR_LABEL);
+ String labelId = plugin.attributeValue(ATTR_LABEL_ID);
+ String description = plugin.attributeValue(ATTR_DESCRIPTION);
+ String descriptionId = plugin.attributeValue(ATTR_DESCRIPTION_ID);
+ String actionsConfigId = plugin.attributeValue(ATTR_ACTIONS_CONFIG_ID);
+ String icon = plugin.attributeValue(ATTR_ICON);
+
+ SidebarConfigElement.SidebarPluginConfig cfg =
+ new SidebarConfigElement.SidebarPluginConfig(id, page,
+ label, labelId, description, descriptionId,
+ actionsConfigId, icon);
+
+ configElement.addPlugin(cfg);
+ }
+ }
+
+ // see if a default plugin is specified
+ Element defaultPlugin = element.element(ELEMENT_DEFAULT_PLUGIN);
+ if (defaultPlugin != null)
+ {
+ configElement.setDefaultPlugin(defaultPlugin.getTextTrim());
+ }
+ }
+
+ return configElement;
+ }
+}
diff --git a/source/java/org/alfresco/web/ui/common/Utils.java b/source/java/org/alfresco/web/ui/common/Utils.java
index 5d2f6f5b4d..adbbf039fe 100644
--- a/source/java/org/alfresco/web/ui/common/Utils.java
+++ b/source/java/org/alfresco/web/ui/common/Utils.java
@@ -90,6 +90,7 @@ public final class Utils
private static final String DEFAULT_FILE_IMAGE32 = IMAGE_PREFIX32 + "_default" + IMAGE_POSTFIX;
private static final String AJAX_SCRIPTS_WRITTEN = "_alfAjaxScriptsWritten";
+ private static final String YAHOO_SCRIPTS_WRITTEN = "_alfYahooScriptsWritten";
private static final Map s_fileExtensionMap = new HashMap(89, 1.0f);
@@ -412,7 +413,7 @@ public final class Utils
*/
public static String generateFormSubmit(FacesContext context, UIComponent component, String fieldId, String fieldValue)
{
- return generateFormSubmit(context, component, fieldId, fieldValue, null);
+ return generateFormSubmit(context, component, fieldId, fieldValue, false, null);
}
/**
@@ -429,7 +430,31 @@ public final class Utils
*
* @return JavaScript event code
*/
- public static String generateFormSubmit(FacesContext context, UIComponent component, String fieldId, String fieldValue, Map params)
+ public static String generateFormSubmit(FacesContext context, UIComponent component, String fieldId,
+ String fieldValue, Map params)
+ {
+ return generateFormSubmit(context, component, fieldId, fieldValue, false, params);
+ }
+
+ /**
+ * Generate the JavaScript to submit set the specified hidden Form field to the
+ * supplied value and submit the parent Form.
+ *
+ * NOTE: the supplied hidden field name is added to the Form Renderer map for output.
+ *
+ * @param context FacesContext
+ * @param component UIComponent to generate JavaScript for
+ * @param fieldId Hidden field id to set value for
+ * @param fieldValue Hidden field value to set hidden field too on submit
+ * @param valueIsParam Determines whether the fieldValue parameter should be treated
+ * as a parameter in the generated JavaScript, false will treat
+ * the value i.e. surround it with single quotes
+ * @param params Optional map of param name/values to output
+ *
+ * @return JavaScript event code
+ */
+ public static String generateFormSubmit(FacesContext context, UIComponent component, String fieldId,
+ String fieldValue, boolean valueIsParam, Map params)
{
UIForm form = Utils.getParentForm(context, component);
if (form == null)
@@ -446,9 +471,17 @@ public final class Utils
buf.append("'");
buf.append("]['");
buf.append(fieldId);
- buf.append("'].value='");
+ buf.append("'].value=");
+ if (valueIsParam == false)
+ {
+ buf.append("'");
+ }
buf.append(fieldValue);
- buf.append("';");
+ if (valueIsParam == false)
+ {
+ buf.append("'");
+ }
+ buf.append(";");
if (params != null)
{
@@ -474,9 +507,12 @@ public final class Utils
buf.append("'");
buf.append(formClientId);
buf.append("'");
- buf.append("].submit()");
+ buf.append("].submit();");
- buf.append(";return false;");
+ if (valueIsParam == false)
+ {
+ buf.append("return false;");
+ }
// weak, but this seems to be the way Sun RI do it...
//FormRenderer.addNeededHiddenField(context, fieldId);
@@ -1244,14 +1280,14 @@ public final class Utils
}
/**
- * Writes the script tags for including AJAX support, ensuring they
+ * Writes the script tags for including dojo support, ensuring they
* only get written once per page render.
*
* @param context Faces context
* @param out The response writer
*/
@SuppressWarnings("unchecked")
- public static void writeAjaxScripts(FacesContext context, ResponseWriter out)
+ public static void writeDojoScripts(FacesContext context, ResponseWriter out)
throws IOException
{
Object present = context.getExternalContext().getRequestMap().get(AJAX_SCRIPTS_WRITTEN);
@@ -1259,6 +1295,10 @@ public final class Utils
if (present == null)
{
// write out the scripts
+// out.write("\n");
+
out.write("\n\n");
@@ -1275,4 +1315,57 @@ public final class Utils
context.getExternalContext().getRequestMap().put(AJAX_SCRIPTS_WRITTEN, Boolean.TRUE);
}
}
+
+ /**
+ * Writes the scripts tags for using the Yahoo UI toolkit, ensuring they
+ * only get written once per page render.
+ *
+ * A comma separated list of scripts can also be passed to determine
+ * which components are to be used, again these are only written once per page.
+ *
+ * @param context Faces context
+ * @param out The response writer
+ * @param scripts Comma separated list of scripts to include, if null the
+ * base yahoo.js script only is included.
+ */
+ @SuppressWarnings("unchecked")
+ public static void writeYahooScripts(FacesContext context, ResponseWriter out,
+ String scripts) throws IOException
+ {
+ Object present = context.getExternalContext().getRequestMap().get(YAHOO_SCRIPTS_WRITTEN);
+
+ if (present == null)
+ {
+ // TODO: use the scripts parameter to determine which scripts to output
+ // also add an ajax debug flag to the config and output relevant file
+
+ // base yahoo file
+ out.write("\n\n");
+
+ // io handling (AJAX)
+ out.write("\n\n");
+
+ // event handling
+ out.write("\n\n");
+
+ // common alfresco util methods
+ out.write("\n");
+
+ // write out a global variable to hold the webapp context path
+ out.write("\n");
+
+ // add marker to request
+ context.getExternalContext().getRequestMap().put(YAHOO_SCRIPTS_WRITTEN, Boolean.TRUE);
+ }
+ }
}
diff --git a/source/java/org/alfresco/web/ui/common/renderer/ModeListRenderer.java b/source/java/org/alfresco/web/ui/common/renderer/ModeListRenderer.java
index 54cfaaa4d9..55678e7a3c 100644
--- a/source/java/org/alfresco/web/ui/common/renderer/ModeListRenderer.java
+++ b/source/java/org/alfresco/web/ui/common/renderer/ModeListRenderer.java
@@ -156,7 +156,28 @@ public class ModeListRenderer extends BaseRenderer
for (Iterator i=list.getChildren().iterator(); i.hasNext(); /**/)
{
UIComponent child = (UIComponent)i.next();
- if (child instanceof UIListItem && child.isRendered() == true)
+ if (child instanceof UIListItems)
+ {
+ // get the value of the list items component and iterate
+ // through it's collection
+ Object listItems = ((UIListItems)child).getValue();
+ if (listItems instanceof Collection)
+ {
+ Iterator iter = ((Collection)listItems).iterator();
+ while (iter.hasNext())
+ {
+ UIListItem item = (UIListItem)iter.next();
+
+ // if selected render as the label
+ if (item.getValue().equals(list.getValue()) == true)
+ {
+ label = item.getLabel();
+ break;
+ }
+ }
+ }
+ }
+ else if (child instanceof UIListItem && child.isRendered() == true)
{
// found a valid UIListItem child to render
UIListItem item = (UIListItem)child;
@@ -179,7 +200,7 @@ public class ModeListRenderer extends BaseRenderer
outputAttribute(out, attrs.get("labelStyleClass"), "class");
out.write('>');
out.write(Utils.encode(label));
- out.write("");
+ out.write(" ");
}
// output image
diff --git a/source/java/org/alfresco/web/ui/repo/component/UINavigator.java b/source/java/org/alfresco/web/ui/repo/component/UINavigator.java
new file mode 100644
index 0000000000..afbb920ef1
--- /dev/null
+++ b/source/java/org/alfresco/web/ui/repo/component/UINavigator.java
@@ -0,0 +1,387 @@
+package org.alfresco.web.ui.repo.component;
+
+import java.io.IOException;
+import java.text.MessageFormat;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import javax.faces.component.UIComponent;
+import javax.faces.context.FacesContext;
+import javax.faces.context.ResponseWriter;
+import javax.faces.el.ValueBinding;
+import javax.faces.event.AbortProcessingException;
+import javax.faces.event.ActionEvent;
+import javax.faces.event.FacesEvent;
+
+import org.alfresco.service.cmr.repository.InvalidNodeRefException;
+import org.alfresco.service.cmr.repository.NodeRef;
+import org.alfresco.web.app.Application;
+import org.alfresco.web.app.servlet.FacesHelper;
+import org.alfresco.web.bean.BrowseBean;
+import org.alfresco.web.bean.NavigationBean;
+import org.alfresco.web.bean.ajax.NavigatorPluginBean;
+import org.alfresco.web.bean.repository.Repository;
+import org.alfresco.web.ui.common.Utils;
+import org.alfresco.web.ui.common.component.SelfRenderingComponent;
+import org.alfresco.web.ui.repo.component.UITree.TreeNode;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+/**
+ * Navigator component that consists of 4 panels representing
+ * the main areas of the repository i.e. company home, my home,
+ * guest home and my alfresco.
+ *
+ * Each panel (apart from my alfresco) uses the tree component
+ * to allow navigation around that area of the repository.
+ *
+ *
+ * @author gavinc
+ */
+public class UINavigator extends SelfRenderingComponent
+{
+ public static final String COMPONENT_TYPE = "org.alfresco.faces.Navigator";
+
+ protected String activeArea;
+
+ private static final Log logger = LogFactory.getLog(UINavigator.class);
+ private static final String NAVIGATION_BEAN = "NavigationBean";
+ private static final String BROWSE_BEAN = "BrowseBean";
+ private static final String AJAX_URL_START = "/ajax/invoke/" + NavigatorPluginBean.BEAN_NAME;
+ private static final String PANEL_ACTION = "panel:";
+ private static final int PANEL_SELECTED = 1;
+ private static final int NODE_SELECTED = 2;
+
+ // ------------------------------------------------------------------------------
+ // Component Impl
+
+ @Override
+ public String getFamily()
+ {
+ return COMPONENT_TYPE;
+ }
+
+ @Override
+ public void restoreState(FacesContext context, Object state)
+ {
+ Object values[] = (Object[])state;
+ // standard component attributes are restored by the super class
+ super.restoreState(context, values[0]);
+ this.activeArea = (String)values[1];
+ }
+
+ @Override
+ public Object saveState(FacesContext context)
+ {
+ Object values[] = new Object[2];
+ // standard component attributes are saved by the super class
+ values[0] = super.saveState(context);
+ values[1] = this.activeArea;
+ return values;
+ }
+
+ /**
+ * @see javax.faces.component.UIComponentBase#decode(javax.faces.context.FacesContext)
+ */
+ public void decode(FacesContext context)
+ {
+ Map requestMap = context.getExternalContext().getRequestParameterMap();
+ String fieldId = getClientId(context);
+ String value = (String)requestMap.get(fieldId);
+
+ if (value != null && value.length() != 0)
+ {
+ if (logger.isDebugEnabled())
+ logger.debug("Received post back: " + value);
+
+ // work out whether a panel or a node was selected
+ int mode = NODE_SELECTED;
+ String item = value;
+ if (value.startsWith(PANEL_ACTION))
+ {
+ mode = PANEL_SELECTED;
+ item = value.substring(PANEL_ACTION.length());
+ }
+
+ // queue an event to be handled later
+ NavigatorEvent event = new NavigatorEvent(this, mode, item);
+ this.queueEvent(event);
+ }
+ }
+
+ /**
+ * @see javax.faces.component.UIInput#broadcast(javax.faces.event.FacesEvent)
+ */
+ public void broadcast(FacesEvent event) throws AbortProcessingException
+ {
+ if (event instanceof NavigatorEvent)
+ {
+ FacesContext context = FacesContext.getCurrentInstance();
+ NavigatorEvent navEvent = (NavigatorEvent)event;
+
+ // node or panel selected?
+ switch (navEvent.getMode())
+ {
+ case PANEL_SELECTED:
+ {
+ String panelSelected = navEvent.getItem();
+
+ // a panel was selected, setup the context to make the panel
+ // the focus
+ NavigationBean nb = (NavigationBean)FacesHelper.getManagedBean(
+ context, NAVIGATION_BEAN);
+ if (nb != null)
+ {
+ try
+ {
+ if (logger.isDebugEnabled())
+ logger.debug("Selecting panel: " + panelSelected);
+
+ nb.processToolbarLocation(panelSelected, true);
+ }
+ catch (InvalidNodeRefException refErr)
+ {
+ Utils.addErrorMessage(MessageFormat.format(Application.getMessage(
+ FacesContext.getCurrentInstance(), Repository.ERROR_NOHOME),
+ Application.getCurrentUser(context).getHomeSpaceId()), refErr );
+ }
+ catch (Exception err)
+ {
+ Utils.addErrorMessage(MessageFormat.format(Application.getMessage(
+ FacesContext.getCurrentInstance(), Repository.ERROR_GENERIC),
+ err.getMessage()), err);
+ }
+ }
+
+ break;
+ }
+ case NODE_SELECTED:
+ {
+ // a node was clicked in the tree
+ NodeRef nodeClicked = new NodeRef(navEvent.getItem());
+
+ // setup the context to make the node the current node
+ BrowseBean bb = (BrowseBean)FacesHelper.getManagedBean(
+ context, BROWSE_BEAN);
+ if (bb != null)
+ {
+ if (logger.isDebugEnabled())
+ logger.debug("Selected node: " + nodeClicked);
+
+ bb.clickSpace(nodeClicked);
+ }
+
+ break;
+ }
+ }
+ }
+ else
+ {
+ super.broadcast(event);
+ }
+ }
+
+ @Override
+ @SuppressWarnings("unchecked")
+ public void encodeBegin(FacesContext context) throws IOException
+ {
+ if (!isRendered()) return;
+
+ // TODO: pull width and height from user preferences and/or the main config,
+ // if present override below using the style attribute
+
+ ResponseWriter out = context.getResponseWriter();
+ NavigationBean navBean = (NavigationBean)FacesHelper.getManagedBean(
+ context, NAVIGATION_BEAN);
+ NavigatorPluginBean navPluginBean = (NavigatorPluginBean)FacesHelper.getManagedBean(
+ context, NavigatorPluginBean.BEAN_NAME);
+
+ List rootNodesForArea = null;
+ String area = this.getActiveArea();
+ String areaTitle = null;
+ boolean treePanel = true;
+ if (NavigationBean.LOCATION_COMPANY.equals(area))
+ {
+ rootNodesForArea = navPluginBean.getCompanyHomeRootNodes();
+ areaTitle = Application.getMessage(context, "company_home");
+ }
+ else if (NavigationBean.LOCATION_HOME.equals(area))
+ {
+ rootNodesForArea = navPluginBean.getMyHomeRootNodes();
+ areaTitle = Application.getMessage(context, "my_home");
+ }
+ else if (NavigationBean.LOCATION_GUEST.equals(area))
+ {
+ rootNodesForArea = navPluginBean.getGuestHomeRootNodes();
+ areaTitle = Application.getMessage(context, "guest_home");
+ }
+ else
+ {
+ treePanel = false;
+ areaTitle = Application.getMessage(context, "my_alfresco");
+ }
+
+ // generate the active panel title
+ out.write("
");
+ out.write("
");
+ out.write(areaTitle);
+ out.write("
");
+
+ // generate the javascript method to capture the tree node click events
+ if (treePanel)
+ {
+ out.write("\n\n");
+
+ // generate the active panel containing the tree
+ out.write("
");
+ }
+
+ @Override
+ public void encodeChildren(FacesContext context) throws IOException
+ {
+ if (!isRendered()) return;
+
+ for (Iterator i=this.getChildren().iterator(); i.hasNext(); /**/)
+ {
+ UIComponent child = (UIComponent)i.next();
+ Utils.encodeRecursive(context, child);
+ }
+ }
+
+ @Override
+ public boolean getRendersChildren()
+ {
+ return true;
+ }
+
+ // ------------------------------------------------------------------------------
+ // Strongly typed component property accessors
+
+ /**
+ * Returns the active area the navigator component is showing
+ *
+ * @return The active area
+ */
+ public String getActiveArea()
+ {
+ ValueBinding vb = getValueBinding("activeArea");
+ if (vb != null)
+ {
+ this.activeArea = (String)vb.getValue(getFacesContext());
+ }
+
+ if (this.activeArea == null)
+ {
+ this.activeArea = NavigationBean.LOCATION_HOME;
+ }
+
+ return this.activeArea;
+ }
+
+ /**
+ * Sets the active area for the navigator panel
+ *
+ * @param activeArea
+ */
+ public void setActiveArea(String activeArea)
+ {
+ this.activeArea = activeArea;
+ }
+
+ // ------------------------------------------------------------------------------
+ // Helper methods
+
+ /**
+ * Class representing the clicking of a tree node.
+ */
+ @SuppressWarnings("serial")
+ public static class NavigatorEvent extends ActionEvent
+ {
+ private int mode;
+ private String item;
+
+ public NavigatorEvent(UIComponent component, int mode, String item)
+ {
+ super(component);
+
+ this.mode = mode;
+ this.item = item;
+ }
+
+ public String getItem()
+ {
+ return item;
+ }
+
+ public int getMode()
+ {
+ return mode;
+ }
+ }
+}
diff --git a/source/java/org/alfresco/web/ui/repo/component/UINodeInfo.java b/source/java/org/alfresco/web/ui/repo/component/UINodeInfo.java
index e894e5bf19..7e516a071f 100644
--- a/source/java/org/alfresco/web/ui/repo/component/UINodeInfo.java
+++ b/source/java/org/alfresco/web/ui/repo/component/UINodeInfo.java
@@ -65,7 +65,7 @@ public class UINodeInfo extends SelfRenderingComponent
// output the scripts required by the component (checks are
// made to make sure the scripts are only written once)
- Utils.writeAjaxScripts(context, out);
+ Utils.writeDojoScripts(context, out);
// write out the JavaScript specific to the NodeInfo component,
// again, make sure it's only done once
diff --git a/source/java/org/alfresco/web/ui/repo/component/UISidebar.java b/source/java/org/alfresco/web/ui/repo/component/UISidebar.java
new file mode 100644
index 0000000000..705f7b8060
--- /dev/null
+++ b/source/java/org/alfresco/web/ui/repo/component/UISidebar.java
@@ -0,0 +1,223 @@
+package org.alfresco.web.ui.repo.component;
+
+import java.io.IOException;
+
+import javax.faces.component.UIComponent;
+import javax.faces.context.FacesContext;
+import javax.faces.context.ResponseWriter;
+import javax.faces.el.MethodBinding;
+import javax.faces.el.ValueBinding;
+import javax.faces.event.ActionEvent;
+
+import org.alfresco.web.bean.SidebarBean;
+import org.alfresco.web.config.SidebarConfigElement;
+import org.alfresco.web.config.SidebarConfigElement.SidebarPluginConfig;
+import org.alfresco.web.ui.common.PanelGenerator;
+import org.alfresco.web.ui.common.Utils;
+import org.alfresco.web.ui.common.component.SelfRenderingComponent;
+import org.alfresco.web.ui.common.component.UIListItems;
+import org.alfresco.web.ui.common.component.UIModeList;
+
+/**
+ * Component that represents the sidebar.
+ *
+ * A sidebar consists of multiple plugins, of which only
+ * one is active at one time. All registered plugins are
+ * displayed in a drop down allowing the user to
+ * change the active plugin. An action group can also be
+ * associated with a plugin, which get rendered in the
+ * sidebar header.
+ *
+ *
+ * @author gavinc
+ */
+public class UISidebar extends SelfRenderingComponent
+{
+ public static final String COMPONENT_TYPE = "org.alfresco.faces.Sidebar";
+
+ protected String activePlugin;
+
+ @Override
+ public String getFamily()
+ {
+ return COMPONENT_TYPE;
+ }
+
+ @Override
+ public void restoreState(FacesContext context, Object state)
+ {
+ Object values[] = (Object[])state;
+ // standard component attributes are restored by the super class
+ super.restoreState(context, values[0]);
+ this.activePlugin = (String)values[1];
+ }
+
+ @Override
+ public Object saveState(FacesContext context)
+ {
+ Object values[] = new Object[8];
+ // standard component attributes are saved by the super class
+ values[0] = super.saveState(context);
+ values[1] = this.activePlugin;
+ return values;
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public void encodeBegin(FacesContext context) throws IOException
+ {
+ if (!isRendered()) return;
+
+ ResponseWriter out = context.getResponseWriter();
+
+ out.write("
");
+
+ // render the start of the header panel
+ PanelGenerator.generatePanelStart(out,
+ context.getExternalContext().getRequestContextPath(),
+ "blue", "#D3E6FE");
+
+ // generate the required child components if not present
+ if (this.getChildCount() == 1)
+ {
+ // create the mode list component
+ UIModeList modeList = (UIModeList)context.getApplication().
+ createComponent("org.alfresco.faces.ModeList");
+ modeList.setId("sidebarPluginList");
+ modeList.setValue(this.getActivePlugin());
+ modeList.setIconColumnWidth(2);
+ modeList.setMenu(true);
+ modeList.setMenuImage("/images/icons/menu.gif");
+ modeList.getAttributes().put("itemSpacing", 4);
+ modeList.getAttributes().put("styleClass", "moreActionsMenu");
+ modeList.getAttributes().put("selectedStyleClass", "statusListHighlight");
+ MethodBinding listener = context.getApplication().createMethodBinding(
+ "#{SidebarBean.pluginChanged}", new Class[] {ActionEvent.class});
+ modeList.setActionListener(listener);
+
+ // create the child list items component
+ UIListItems items = (UIListItems)context.getApplication().
+ createComponent("org.alfresco.faces.ListItems");
+ ValueBinding binding = context.getApplication().createValueBinding(
+ "#{SidebarBean.plugins}");
+ items.setValueBinding("value", binding);
+
+ // add the list items to the mode list component
+ modeList.getChildren().add(items);
+
+ // create the actions component
+ UIActions actions = (UIActions)context.getApplication().
+ createComponent("org.alfresco.faces.Actions");
+ actions.setId("sidebarActions");
+ actions.setShowLink(false);
+ // TODO: we need to setup the context for the actions component
+ // but when the app first starts there is no context yet,
+ // also the tree will not update the context as it is
+ // navigated so what do we use? we may have to only support
+ // global non-context actions
+ String actionsGroupId = null;
+ SidebarConfigElement config = SidebarBean.getSidebarConfig(context);
+ if (config != null)
+ {
+ SidebarPluginConfig plugin = config.getPlugin(getActivePlugin());
+ if (plugin != null)
+ {
+ actionsGroupId = plugin.getActionsConfigId();
+ }
+ }
+ actions.setValue(actionsGroupId);
+
+ // add components to the sidebar
+ this.getChildren().add(0, modeList);
+ this.getChildren().add(1, actions);
+ }
+ }
+
+ @Override
+ public void encodeChildren(FacesContext context) throws IOException
+ {
+ if (!isRendered()) return;
+
+ // there should be 3 children, the modelist, the actions
+ // and the plugin, get them individually and render
+
+ if (getChildren().size() == 3)
+ {
+ ResponseWriter out = context.getResponseWriter();
+
+ out.write("
");
+
+ // render the list
+ UIModeList modeList = (UIModeList)getChildren().get(0);
+ Utils.encodeRecursive(context, modeList);
+
+ out.write("
");
+
+ // render the end of the header panel
+ PanelGenerator.generateTitledPanelMiddle(out,
+ context.getExternalContext().getRequestContextPath(),
+ "blue", "white", "white");
+
+ // render the plugin
+ UIComponent plugin = (UIComponent)getChildren().get(2);
+ Utils.encodeRecursive(context, plugin);
+ }
+ }
+
+ @Override
+ public void encodeEnd(FacesContext context) throws IOException
+ {
+ if (!isRendered()) return;
+
+ // render the end of the panel
+ ResponseWriter out = context.getResponseWriter();
+ PanelGenerator.generatePanelEnd(out,
+ context.getExternalContext().getRequestContextPath(),
+ "white");
+ out.write("
");
+ }
+
+ @Override
+ public boolean getRendersChildren()
+ {
+ return true;
+ }
+
+ // ------------------------------------------------------------------------------
+ // Strongly typed component property accessors
+
+ /**
+ * Returns the id of the plugin that is currently active
+ *
+ * @return The currently active plugin
+ */
+ public String getActivePlugin()
+ {
+ ValueBinding vb = getValueBinding("activePlugin");
+ if (vb != null)
+ {
+ this.activePlugin = (String)vb.getValue(getFacesContext());
+ }
+
+ return this.activePlugin;
+ }
+
+ /**
+ * Sets the active plugin the sidebar should show
+ *
+ * @param activePlugin Id of the plugin to make active
+ */
+ public void setActivePlugin(String activePlugin)
+ {
+ this.activePlugin = activePlugin;
+ }
+}
+
+
+
diff --git a/source/java/org/alfresco/web/ui/repo/component/UITree.java b/source/java/org/alfresco/web/ui/repo/component/UITree.java
new file mode 100644
index 0000000000..0980543cdd
--- /dev/null
+++ b/source/java/org/alfresco/web/ui/repo/component/UITree.java
@@ -0,0 +1,384 @@
+package org.alfresco.web.ui.repo.component;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+import javax.faces.component.UIComponentBase;
+import javax.faces.context.FacesContext;
+import javax.faces.el.ValueBinding;
+
+/**
+ * JSF component that renders an AJAX based tree for browsing the
+ * repository.
+ *
+ * @author gavinc
+ */
+public class UITree extends UIComponentBase
+{
+ public static final String COMPONENT_TYPE = "org.alfresco.faces.Tree";
+ public static final String DEFAULT_RENDERER = "org.alfresco.faces.Yahoo";
+
+ protected List rootNodes = null;
+ protected String retrieveChildrenUrl;
+ protected String nodeCollapsedUrl;
+ protected String nodeExpandedCallback;
+ protected String nodeCollapsedCallback;
+ protected String nodeSelectedCallback;
+
+ // ------------------------------------------------------------------------------
+ // Component Impl
+
+ public UITree()
+ {
+ setRendererType(DEFAULT_RENDERER);
+ }
+
+ @Override
+ public String getFamily()
+ {
+ return COMPONENT_TYPE;
+ }
+
+ @Override
+ @SuppressWarnings("unchecked")
+ public void restoreState(FacesContext context, Object state)
+ {
+ Object values[] = (Object[])state;
+ // standard component attributes are restored by the super class
+ super.restoreState(context, values[0]);
+ this.rootNodes = (List)values[1];
+ this.retrieveChildrenUrl = (String)values[2];
+ this.nodeCollapsedUrl = (String)values[3];
+ this.nodeExpandedCallback = (String)values[4];
+ this.nodeCollapsedCallback = (String)values[5];
+ this.nodeSelectedCallback = (String)values[6];
+ }
+
+ @Override
+ public Object saveState(FacesContext context)
+ {
+ Object values[] = new Object[7];
+ // standard component attributes are saved by the super class
+ values[0] = super.saveState(context);
+ values[1] = this.rootNodes;
+ values[2] = this.retrieveChildrenUrl;
+ values[3] = this.nodeCollapsedUrl;
+ values[4] = this.nodeExpandedCallback;
+ values[5] = this.nodeCollapsedCallback;
+ values[6] = this.nodeSelectedCallback;
+ return values;
+ }
+
+ // ------------------------------------------------------------------------------
+ // Strongly typed component property accessors
+
+ /**
+ * Get the root nodes for the tree
+ *
+ * @return the list of nodes representing the root nodes of the tree
+ */
+ @SuppressWarnings("unchecked")
+ public List getRootNodes()
+ {
+ ValueBinding vb = getValueBinding("rootNodes");
+ if (vb != null)
+ {
+ this.rootNodes = (List)vb.getValue(getFacesContext());
+ }
+
+ return this.rootNodes;
+ }
+
+ /**
+ * Set the root nodes for the tree to show
+ *
+ * @param rootNodes The list of node for the tree
+ */
+ public void setRootNodes(List rootNodes)
+ {
+ this.rootNodes = rootNodes;
+ }
+
+ /**
+ * Returns the Javascript function name to be used for node collapsed event
+ *
+ * @return Javascript function name to be used for node collapsed event
+ */
+ public String getNodeCollapsedCallback()
+ {
+ ValueBinding vb = getValueBinding("nodeCollapsedCallback");
+ if (vb != null)
+ {
+ this.nodeCollapsedCallback = (String)vb.getValue(getFacesContext());
+ }
+
+ return this.nodeCollapsedCallback;
+ }
+
+ /**
+ * Sets the name of the Javascript function to use for the node collapsed event
+ *
+ * @param nodeCollapsedCallback The Javascript function to use for the node collapsed event
+ */
+ public void setNodeCollapsedCallback(String nodeCollapsedCallback)
+ {
+ this.nodeCollapsedCallback = nodeCollapsedCallback;
+ }
+
+ /**
+ * Returns the Javascript function name to be used for node expanded event
+ *
+ * @return Javascript function name to be used for node expanded event
+ */
+ public String getNodeExpandedCallback()
+ {
+ ValueBinding vb = getValueBinding("nodeExpandedCallback");
+ if (vb != null)
+ {
+ this.nodeExpandedCallback = (String)vb.getValue(getFacesContext());
+ }
+
+ return this.nodeExpandedCallback;
+ }
+
+ /**
+ * Sets the name of the Javascript function to use for the expanded event
+ *
+ * @param nodeCollapsedCallback The Javascript function to use for the expanded event
+ */
+ public void setNodeExpandedCallback(String nodeExpandedCallback)
+ {
+ this.nodeExpandedCallback = nodeExpandedCallback;
+ }
+
+ /**
+ * Returns the Javascript function name to be used for node selected event
+ *
+ * @return Javascript function name to be used for node selected event
+ */
+ public String getNodeSelectedCallback()
+ {
+ ValueBinding vb = getValueBinding("nodeSelectedCallback");
+ if (vb != null)
+ {
+ this.nodeSelectedCallback = (String)vb.getValue(getFacesContext());
+ }
+
+ return this.nodeSelectedCallback;
+ }
+
+ /**
+ * Sets the name of the Javascript function to use for the node selected event
+ *
+ * @param nodeCollapsedCallback The Javascript function to use for the node selected event
+ */
+ public void setNodeSelectedCallback(String nodeSelectedCallback)
+ {
+ this.nodeSelectedCallback = nodeSelectedCallback;
+ }
+
+ /**
+ * Returns the URL to use for the AJAX call to retrieve the child nodea
+ *
+ * @return AJAX URL to get children
+ */
+ public String getRetrieveChildrenUrl()
+ {
+ ValueBinding vb = getValueBinding("retrieveChildrenUrl");
+ if (vb != null)
+ {
+ this.retrieveChildrenUrl = (String)vb.getValue(getFacesContext());
+ }
+
+ return this.retrieveChildrenUrl;
+ }
+
+ /**
+ * Sets the AJAX URL to use to retrive child nodes
+ *
+ * @param retrieveChildrenUrl The AJAX URL to use
+ */
+ public void setRetrieveChildrenUrl(String retrieveChildrenUrl)
+ {
+ this.retrieveChildrenUrl = retrieveChildrenUrl;
+ }
+
+ /**
+ * Returns the URL to use for the AJAX call to inform the server
+ * that a node has been collapsed
+ *
+ * @return AJAX URL to inform of node collapse
+ */
+ public String getNodeCollapsedUrl()
+ {
+ ValueBinding vb = getValueBinding("nodeCollapsedUrl");
+ if (vb != null)
+ {
+ this.nodeCollapsedUrl = (String)vb.getValue(getFacesContext());
+ }
+
+ return this.nodeCollapsedUrl;
+ }
+
+ /**
+ * Sets the AJAX URL to use to inform the server that a node
+ * has been collapsed
+ *
+ * @param nodeCollapsedUrl The AJAX URL to use
+ */
+ public void setNodeCollapsedUrl(String nodeCollapsedUrl)
+ {
+ this.nodeCollapsedUrl = nodeCollapsedUrl;
+ }
+
+ /**
+ * Inner class representing a node in the tree
+ *
+ * @author gavinc
+ */
+ public static class TreeNode
+ {
+ private String nodeRef;
+ private String name;
+ private String icon;
+ private boolean leafNode = false;
+ private boolean expanded = false;
+ private boolean selected = false;
+ private TreeNode parent;
+ private List children = new ArrayList();
+
+ /**
+ * Default constructor
+ *
+ * @param nodeRef The NodeRef of the item the node is representing
+ * @param name The name for the tree label
+ * @param icon The icon for the node
+ */
+ public TreeNode(String nodeRef, String name, String icon)
+ {
+ this.nodeRef = nodeRef;
+ this.name = name;
+ this.icon = icon;
+ }
+
+ public String getIcon()
+ {
+ return this.icon;
+ }
+
+ public void setIcon(String icon)
+ {
+ this.icon = icon;
+ }
+
+ public boolean isLeafNode()
+ {
+ return this.leafNode;
+ }
+
+ public void setLeafNode(boolean leafNode)
+ {
+ this.leafNode = leafNode;
+ }
+
+ public boolean isExpanded()
+ {
+ return this.expanded;
+ }
+
+ public void setExpanded(boolean expanded)
+ {
+ this.expanded = expanded;
+ }
+
+ public boolean isSelected()
+ {
+ return this.selected;
+ }
+
+ public void setSelected(boolean selected)
+ {
+ this.selected = selected;
+ }
+
+ public String getName()
+ {
+ return this.name;
+ }
+
+ public void setName(String name)
+ {
+ this.name = name;
+ }
+
+ public String getNodeRef()
+ {
+ return this.nodeRef;
+ }
+
+ public void setNodeRef(String nodeRef)
+ {
+ this.nodeRef = nodeRef;
+ }
+
+ public TreeNode getParent()
+ {
+ return this.parent;
+ }
+
+ public void setParent(TreeNode parent)
+ {
+ this.parent = parent;
+ }
+
+ public List getChildren()
+ {
+ return this.children;
+ }
+
+ public void addChild(TreeNode child)
+ {
+ child.setParent(this);
+ this.children.add(child);
+ }
+
+ public void removeChildren()
+ {
+ this.children = new ArrayList();
+ }
+
+ public String toString()
+ {
+ StringBuilder buffer = new StringBuilder(super.toString());
+ buffer.append(" (nodeRef=").append(this.nodeRef);
+ buffer.append(", name=").append(this.name);
+ buffer.append(", icon=").append(this.icon);
+ buffer.append(", expanded=").append(this.expanded);
+ buffer.append(", selected=").append(this.selected);
+ if (this.parent != null)
+ {
+ buffer.append(", parent=").append(this.parent.getNodeRef());
+ }
+ else
+ {
+ buffer.append(", parent=null");
+ }
+ buffer.append(", leafNode=").append(this.leafNode).append(")");
+ return buffer.toString();
+ }
+
+ public String toXML()
+ {
+ StringBuilder xml = new StringBuilder();
+ xml.append("");
+ return xml.toString();
+ }
+ }
+}
diff --git a/source/java/org/alfresco/web/ui/repo/renderer/YahooTreeRenderer.java b/source/java/org/alfresco/web/ui/repo/renderer/YahooTreeRenderer.java
new file mode 100644
index 0000000000..c2eccaf165
--- /dev/null
+++ b/source/java/org/alfresco/web/ui/repo/renderer/YahooTreeRenderer.java
@@ -0,0 +1,188 @@
+package org.alfresco.web.ui.repo.renderer;
+
+import java.io.IOException;
+import java.util.List;
+
+import javax.faces.component.UIComponent;
+import javax.faces.context.FacesContext;
+import javax.faces.context.ResponseWriter;
+
+import org.alfresco.web.ui.common.Utils;
+import org.alfresco.web.ui.common.renderer.BaseRenderer;
+import org.alfresco.web.ui.repo.component.UITree;
+import org.alfresco.web.ui.repo.component.UITree.TreeNode;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+/**
+ * Renderer for the UITree component that outputs the necessary
+ * JavaScript to use the Yahoo UI toolkit tree control.
+ *
+ * @author gavinc
+ */
+public class YahooTreeRenderer extends BaseRenderer
+{
+ protected int nodeCounter;
+
+ protected final static String TREE_SCRIPTS_WRITTEN = "_alfTreeScripts";
+
+ private static final Log logger = LogFactory.getLog(YahooTreeRenderer.class);
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public void encodeBegin(FacesContext context, UIComponent component) throws IOException
+ {
+ // get the root nodes
+ UITree tree = (UITree)component;
+ List rootNodes = tree.getRootNodes();
+
+ if (rootNodes != null && rootNodes.size() > 0)
+ {
+ ResponseWriter out = context.getResponseWriter();
+ String treeContainerId = component.getClientId(context) + "Container";
+
+ // output the scripts required by the component (checks are
+ // made to make sure the scripts are only written once)
+ Utils.writeYahooScripts(context, out, null);
+
+ // write out the JavaScript specific to the Tree component,
+ // again, make sure it's only done once
+ Object present = context.getExternalContext().getRequestMap().
+ get(TREE_SCRIPTS_WRITTEN);
+ if (present == null)
+ {
+ out.write("");
+
+ out.write("\n");
+
+ out.write("\n");
+
+ context.getExternalContext().getRequestMap().put(
+ TREE_SCRIPTS_WRITTEN, Boolean.TRUE);
+ }
+
+ // output the div container for the tree
+ out.write("\n");
+
+ // generate the startup
+ out.write("\n");
+ }
+ else if (logger.isDebugEnabled())
+ {
+ logger.debug("There weren't any nodes to render");
+ }
+ }
+
+ /**
+ * Generates the JavaScript required to create the branch of a tree from
+ * the given node.
+ *
+ * @param node The node to generate
+ * @param out Response writer
+ * @param parentVarName Name of the parent variable, null if the node has no parent
+ */
+ protected void generateNode(TreeNode node, ResponseWriter out, String parentVarName)
+ throws IOException
+ {
+ String currentVarName = getNextVarName();
+
+ // generate the Javascript to create the given node using the
+ // appropriate parent variable
+ out.write(" var ");
+ out.write(currentVarName);
+ out.write(" = createYahooTreeNode(");
+
+ if (node.getParent() == null)
+ {
+ out.write("root");
+ }
+ else
+ {
+ out.write(parentVarName);
+ }
+
+ out.write(", \"");
+ out.write(node.getNodeRef());
+ out.write("\", \"");
+ out.write(node.getName());
+ out.write("\", \"");
+ out.write(node.getIcon());
+ out.write("\", ");
+ out.write(Boolean.toString(node.isExpanded()));
+ out.write(", ");
+ out.write(Boolean.toString(node.isSelected()));
+ out.write(");\n");
+
+ // iterate through the child nodes and generate them
+ if (node.isExpanded() && node.getChildren().size() > 0)
+ {
+ for (TreeNode child : node.getChildren())
+ {
+ generateNode(child, out, currentVarName);
+ }
+ }
+ }
+
+ protected String getNextVarName()
+ {
+ this.nodeCounter++;
+ return "n" + Integer.toString(this.nodeCounter);
+ }
+}
diff --git a/source/java/org/alfresco/web/ui/repo/tag/NavigatorTag.java b/source/java/org/alfresco/web/ui/repo/tag/NavigatorTag.java
new file mode 100644
index 0000000000..487bfb9ee4
--- /dev/null
+++ b/source/java/org/alfresco/web/ui/repo/tag/NavigatorTag.java
@@ -0,0 +1,77 @@
+/*
+ * 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.ui.repo.tag;
+
+import javax.faces.component.UIComponent;
+
+import org.alfresco.web.ui.common.tag.HtmlComponentTag;
+
+/**
+ * Tag class for using the navigator component on a JSP page.
+ *
+ * @author gavinc
+ */
+public class NavigatorTag extends HtmlComponentTag
+{
+ private String activeArea;
+
+ /**
+ * @see javax.faces.webapp.UIComponentTag#getComponentType()
+ */
+ public String getComponentType()
+ {
+ return "org.alfresco.faces.Navigator";
+ }
+
+ /**
+ * @see javax.faces.webapp.UIComponentTag#getRendererType()
+ */
+ public String getRendererType()
+ {
+ return null;
+ }
+
+ /**
+ * @see javax.faces.webapp.UIComponentTag#setProperties(javax.faces.component.UIComponent)
+ */
+ protected void setProperties(UIComponent component)
+ {
+ super.setProperties(component);
+
+ setStringBindingProperty(component, "activeArea", this.activeArea);
+ }
+
+ /**
+ * @see org.alfresco.web.ui.common.tag.HtmlComponentTag#release()
+ */
+ public void release()
+ {
+ super.release();
+
+ this.activeArea = null;
+ }
+
+ /**
+ * Sets the active area of the navigator
+ *
+ * @param activeArea The active area
+ */
+ public void setActiveArea(String activeArea)
+ {
+ this.activeArea = activeArea;
+ }
+}
diff --git a/source/java/org/alfresco/web/ui/repo/tag/SidebarTag.java b/source/java/org/alfresco/web/ui/repo/tag/SidebarTag.java
new file mode 100644
index 0000000000..951387af67
--- /dev/null
+++ b/source/java/org/alfresco/web/ui/repo/tag/SidebarTag.java
@@ -0,0 +1,78 @@
+/*
+ * 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.ui.repo.tag;
+
+import javax.faces.component.UIComponent;
+
+import org.alfresco.web.ui.common.tag.HtmlComponentTag;
+import org.alfresco.web.ui.repo.component.UISidebar;
+
+/**
+ * Tag class for using the sidebar component on a JSP page.
+ *
+ * @author gavinc
+ */
+public class SidebarTag extends HtmlComponentTag
+{
+ private String activePlugin;
+
+ /**
+ * @see javax.faces.webapp.UIComponentTag#getComponentType()
+ */
+ public String getComponentType()
+ {
+ return UISidebar.COMPONENT_TYPE;
+ }
+
+ /**
+ * @see javax.faces.webapp.UIComponentTag#getRendererType()
+ */
+ public String getRendererType()
+ {
+ return null;
+ }
+
+ /**
+ * @see javax.faces.webapp.UIComponentTag#setProperties(javax.faces.component.UIComponent)
+ */
+ protected void setProperties(UIComponent component)
+ {
+ super.setProperties(component);
+
+ setStringBindingProperty(component, "activePlugin", this.activePlugin);
+ }
+
+ /**
+ * @see org.alfresco.web.ui.common.tag.HtmlComponentTag#release()
+ */
+ public void release()
+ {
+ super.release();
+
+ this.activePlugin = null;
+ }
+
+ /**
+ * Sets the activePlugin attribute
+ *
+ * @param activePlugin
+ */
+ public void setActivePlugin(String activePlugin)
+ {
+ this.activePlugin = activePlugin;
+ }
+}
diff --git a/source/java/org/alfresco/web/ui/repo/tag/YahooTreeTag.java b/source/java/org/alfresco/web/ui/repo/tag/YahooTreeTag.java
new file mode 100644
index 0000000000..424e6289a8
--- /dev/null
+++ b/source/java/org/alfresco/web/ui/repo/tag/YahooTreeTag.java
@@ -0,0 +1,142 @@
+/*
+ * 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.ui.repo.tag;
+
+import javax.faces.component.UIComponent;
+
+import org.alfresco.web.ui.common.tag.HtmlComponentTag;
+
+/**
+ * Tag class for using the Yahoo tree component on a JSP page.
+ *
+ * @author gavinc
+ */
+public class YahooTreeTag extends HtmlComponentTag
+{
+ private String rootNodes;
+ private String retrieveChildrenUrl;
+ private String nodeCollapsedUrl;
+ private String nodeExpandedCallback;
+ private String nodeCollapsedCallback;
+ private String nodeSelectedCallback;
+
+ /**
+ * @see javax.faces.webapp.UIComponentTag#getComponentType()
+ */
+ public String getComponentType()
+ {
+ return "org.alfresco.faces.Tree";
+ }
+
+ /**
+ * @see javax.faces.webapp.UIComponentTag#getRendererType()
+ */
+ public String getRendererType()
+ {
+ return "org.alfresco.faces.Yahoo";
+ }
+
+ /**
+ * @see javax.faces.webapp.UIComponentTag#setProperties(javax.faces.component.UIComponent)
+ */
+ protected void setProperties(UIComponent component)
+ {
+ super.setProperties(component);
+
+ setStringBindingProperty(component, "rootNodes", this.rootNodes);
+ setStringBindingProperty(component, "retrieveChildrenUrl", this.retrieveChildrenUrl);
+ setStringBindingProperty(component, "nodeCollapsedUrl", this.nodeCollapsedUrl);
+ setStringBindingProperty(component, "nodeExpandedCallback", this.nodeExpandedCallback);
+ setStringBindingProperty(component, "nodeCollapsedCallback", this.nodeCollapsedCallback);
+ setStringBindingProperty(component, "nodeSelectedCallback", this.nodeSelectedCallback);
+ }
+
+ /**
+ * @see org.alfresco.web.ui.common.tag.HtmlComponentTag#release()
+ */
+ public void release()
+ {
+ super.release();
+
+ this.rootNodes = null;
+ this.retrieveChildrenUrl = null;
+ this.nodeCollapsedUrl = null;
+ this.nodeExpandedCallback = null;
+ this.nodeCollapsedCallback = null;
+ this.nodeSelectedCallback = null;
+ }
+
+ /**
+ * Set the root nodes for the tree
+ *
+ * @param rootNodes
+ */
+ public void setRootNodes(String rootNodes)
+ {
+ this.rootNodes = rootNodes;
+ }
+
+ /**
+ * Set the name of the Javascript function to handle the node collapsed event
+ *
+ * @param nodeCollapsedCallback
+ */
+ public void setNodeCollapsedCallback(String nodeCollapsedCallback)
+ {
+ this.nodeCollapsedCallback = nodeCollapsedCallback;
+ }
+
+ /**
+ * Set the name of the Javascript function to handle the node expanded event
+ *
+ * @param nodeExpandedCallback
+ */
+ public void setNodeExpandedCallback(String nodeExpandedCallback)
+ {
+ this.nodeExpandedCallback = nodeExpandedCallback;
+ }
+
+ /**
+ * Set the name of the Javascript function to handle the node selected event
+ *
+ * @param nodeSelectedCallback
+ */
+ public void setNodeSelectedCallback(String nodeSelectedCallback)
+ {
+ this.nodeSelectedCallback = nodeSelectedCallback;
+ }
+
+ /**
+ * Set the URL to use to retrieve child nodes
+ *
+ * @param retrieveChildrenUrl
+ */
+ public void setRetrieveChildrenUrl(String retrieveChildrenUrl)
+ {
+ this.retrieveChildrenUrl = retrieveChildrenUrl;
+ }
+
+ /**
+ * Set the URL to use to inform the server that a node has been collapsed
+ *
+ * @param nodeCollapsedUrl
+ */
+ public void setNodeCollapsedUrl(String nodeCollapsedUrl)
+ {
+ this.nodeCollapsedUrl = nodeCollapsedUrl;
+ }
+}
diff --git a/source/web/WEB-INF/faces-config-beans.xml b/source/web/WEB-INF/faces-config-beans.xml
index 762f8ca573..e64876eeb4 100644
--- a/source/web/WEB-INF/faces-config-beans.xml
+++ b/source/web/WEB-INF/faces-config-beans.xml
@@ -2029,6 +2029,15 @@
org.alfresco.web.bean.UserPreferencesBeansession
+
+
+
+ The bean that backs up the Sidebar component
+
+ SidebarBean
+ org.alfresco.web.bean.SidebarBean
+ session
+
@@ -2968,6 +2977,23 @@
+
+
+ Bean that returns manages the tree data for the navigator component
+
+ NavigatorPluginBean
+ org.alfresco.web.bean.ajax.NavigatorPluginBean
+ session
+
+ nodeService
+ #{NodeService}
+
+
+ dictionaryService
+ #{DictionaryService}
+
+
+
Bean that returns information on a node
diff --git a/source/web/WEB-INF/faces-config-repo.xml b/source/web/WEB-INF/faces-config-repo.xml
index a798a656ee..ec9a223624 100644
--- a/source/web/WEB-INF/faces-config-repo.xml
+++ b/source/web/WEB-INF/faces-config-repo.xml
@@ -13,8 +13,8 @@
org.alfresco.faces.Propertyorg.alfresco.web.ui.repo.component.property.UIProperty
-
-
+
+ org.alfresco.faces.Separatororg.alfresco.web.ui.repo.component.property.UISeparator
@@ -158,7 +158,22 @@
org.alfresco.faces.NodeWorkflowInfoorg.alfresco.web.ui.repo.component.UINodeWorkflowInfo
-
+
+
+ org.alfresco.faces.Sidebar
+ org.alfresco.web.ui.repo.component.UISidebar
+
+
+
+ org.alfresco.faces.Tree
+ org.alfresco.web.ui.repo.component.UITree
+
+
+
+ org.alfresco.faces.Navigator
+ org.alfresco.web.ui.repo.component.UINavigator
+
+
org.alfresco.faces.PermissionEvaluator
@@ -195,8 +210,8 @@
org.alfresco.faces.PropertyRendererorg.alfresco.web.ui.repo.renderer.property.PropertyRenderer
-
-
+
+ org.alfresco.faces.Separatororg.alfresco.faces.SeparatorRendererorg.alfresco.web.ui.repo.renderer.property.SeparatorRenderer
@@ -225,6 +240,12 @@
org.alfresco.faces.Fieldorg.alfresco.web.ui.repo.renderer.MultiValueFieldRenderer
+
+
+ org.alfresco.faces.Tree
+ org.alfresco.faces.Yahoo
+ org.alfresco.web.ui.repo.renderer.YahooTreeRenderer
+
diff --git a/source/web/WEB-INF/repo.tld b/source/web/WEB-INF/repo.tld
index 101e1f9a36..372ff18981 100644
--- a/source/web/WEB-INF/repo.tld
+++ b/source/web/WEB-INF/repo.tld
@@ -1755,4 +1755,113 @@
+
+ sidebar
+ org.alfresco.web.ui.repo.tag.SidebarTag
+ JSP
+
+
+ Allows the sidebar component to be used on a JSP page.
+
+
+
+ id
+ false
+ true
+
+
+
+ activePlugin
+ false
+ true
+
+
+
+ rendered
+ false
+ true
+
+
+
+
+ navigator
+ org.alfresco.web.ui.repo.tag.NavigatorTag
+ JSP
+
+
+ Allows the navigator component to be used on a JSP page.
+
+
+
+ id
+ false
+ true
+
+
+
+ rendered
+ false
+ true
+
+
+
+ activeArea
+ false
+ true
+
+
+
+
+ yahooTree
+ org.alfresco.web.ui.repo.tag.YahooTreeTag
+ JSP
+
+
+ The tree tag allows the yahoo based tree component to be
+ added to a JSP page.
+
+
+
+ id
+ false
+ true
+
+
+
+ rootNodes
+ false
+ true
+
+
+
+ retrieveChildrenUrl
+ false
+ true
+
+
+
+ nodeCollapsedUrl
+ false
+ true
+
+
+
+ nodeExpandedCallback
+ false
+ true
+
+
+
+ nodeCollapsedCallback
+ false
+ true
+
+
+
+ nodeSelectedCallback
+ false
+ true
+
+
+
diff --git a/source/web/css/main.css b/source/web/css/main.css
index f0e10fb33f..f2228ebe6b 100644
--- a/source/web/css/main.css
+++ b/source/web/css/main.css
@@ -11,16 +11,16 @@ p
table.moduletable td
{
- font-family: Tahoma, Arial, Helvetica, sans-serif;
- font-size: 11px;
- font-weight: normal;
+ font-family: Tahoma, Arial, Helvetica, sans-serif;
+ font-size: 11px;
+ font-weight: normal;
}
td,tr,p,div
{
color: #003366;
- font-family: Tahoma, Arial, Helvetica, sans-serif;
- font-size: 11px;
+ font-family: Tahoma, Arial, Helvetica, sans-serif;
+ font-size: 11px;
}
th
@@ -40,68 +40,68 @@ p.status
a:link, a:visited
{
- font-size: 11px;
- color: #003366;
- text-decoration: none;
- font-family: Tahoma, Arial, Helvetica, sans-serif;
- font-weight: normal;
+ font-size: 11px;
+ color: #003366;
+ text-decoration: none;
+ font-family: Tahoma, Arial, Helvetica, sans-serif;
+ font-weight: normal;
}
a:hover
{
- color: #4272B4;
- text-decoration: underline;
- font-weight: normal;
+ color: #4272B4;
+ text-decoration: underline;
+ font-weight: normal;
}
a.path:link, a.path:visited
{
- font-size: 14px;
- color: #003366;
- text-decoration: none;
- font-weight: normal;
+ font-size: 14px;
+ color: #003366;
+ text-decoration: none;
+ font-weight: normal;
}
a.path:hover
{
font-size: 14px;
- color: #4272B4;
- text-decoration: underline;
- font-weight: normal;
+ color: #4272B4;
+ text-decoration: underline;
+ font-weight: normal;
}
a.header:link, a.header:visited
{
- font-weight: bold;
+ font-weight: bold;
}
a.header:hover
{
- font-weight: bold;
+ font-weight: bold;
}
a.title:link, a.title:visited
{
font-size: 12px;
- font-weight: bold;
+ font-weight: bold;
}
a.title:hover
{
font-size: 12px;
- font-weight: bold;
+ font-weight: bold;
}
a.moreactions:link, a.moreactions:visited
{
vertical-align: 30%;
- font-size: 10px;
+ font-size: 10px;
}
a.moreactions:hover
{
vertical-align: 30%;
- font-size: 10px;
+ font-size: 10px;
}
a.small:link, a.small:visited
@@ -336,7 +336,7 @@ input,textarea,select
.dialogButtonSpacing
{
- height: 5px;
+ height: 5px;
}
.topToolbar
@@ -434,7 +434,7 @@ a.topToolbarLinkHighlight, a.topToolbarLinkHighlight:link, a.topToolbarLinkHighl
.wizardButtonSpacing
{
- height: 5px;
+ height: 5px;
}
.propertiesLabel
@@ -486,12 +486,12 @@ a.topToolbarLinkHighlight, a.topToolbarLinkHighlight:link, a.topToolbarLinkHighl
.alignTop
{
- vertical-align: top;
+ vertical-align: top;
}
.alignMiddle
{
- vertical-align: middle;
+ vertical-align: middle;
}
.tableThirdWidth
@@ -514,10 +514,10 @@ a.topToolbarLinkHighlight, a.topToolbarLinkHighlight:link, a.topToolbarLinkHighl
.summaryPopupPanel
{
- background-color: #e9f0f4;
- border: 1px solid #999999;
- padding: 4px;
- -moz-border-radius: 4px;
+ background-color: #e9f0f4;
+ border: 1px solid #999999;
+ padding: 4px;
+ -moz-border-radius: 4px;
}
.userGroupPickerList
@@ -531,9 +531,9 @@ a.topToolbarLinkHighlight, a.topToolbarLinkHighlight:link, a.topToolbarLinkHighl
.workflowSelection
{
- border: 1px solid #676767;
- background-color: #efefef;
- padding: 6px 12px 12px 6px;
+ border: 1px solid #676767;
+ background-color: #efefef;
+ padding: 6px 12px 12px 6px;
}
.workflowSummary
@@ -549,15 +549,15 @@ a.topToolbarLinkHighlight, a.topToolbarLinkHighlight:link, a.topToolbarLinkHighl
.nodeWorkflowInfoTitle
{
- font-weight: bold;
- color: #003366;
- padding-top: 3px;
- padding-bottom: 3px;
+ font-weight: bold;
+ color: #003366;
+ padding-top: 3px;
+ padding-bottom: 3px;
}
.nodeWorkflowInfoText
{
- padding-bottom: 8px;
+ padding-bottom: 8px;
}
.selectListTable
@@ -589,3 +589,43 @@ a.topToolbarLinkHighlight, a.topToolbarLinkHighlight:link, a.topToolbarLinkHighl
border-style: solid;
border-color: #999999;
}
+
+#navigator
+{
+ border: 1px solid #aaaaaa;
+}
+
+.navigatorPanelTitle
+{
+ background-color: #efefef;
+ color: #003366;
+ padding: 5px;
+ border-top: 1px solid #aaaaaa;
+}
+
+.navigatorPanelTitle a
+{
+ font-weight: bold;
+}
+
+.navigatorPanelTitle a:hover
+{
+ color: #4272b4;
+ font-weight: bold;
+}
+
+.navigatorPanelTitleSelected
+{
+ width: 220px;
+ font-weight: bold;
+ background-color: #7b7b7b;
+ color: white;
+ padding: 5px;
+}
+
+.navigatorPanelBody
+{
+ height: 330px;
+ width: 220px;
+ overflow: auto;
+}
\ No newline at end of file
diff --git a/source/web/css/yahoo-tree.css b/source/web/css/yahoo-tree.css
new file mode 100644
index 0000000000..0250eededb
--- /dev/null
+++ b/source/web/css/yahoo-tree.css
@@ -0,0 +1,134 @@
+/* first or middle sibling, no children */
+.ygtvtn
+{
+ width:16px; height:18px;
+ cursor:pointer ;
+ background: url(../images/icons/arrow_closed.gif) 0 0 no-repeat;
+}
+
+/* first or middle sibling, collapsable */
+.ygtvtm
+{
+ width:16px; height:18px;
+ cursor:pointer ;
+ background: url(../images/icons/arrow_open.gif) 0 0 no-repeat;
+}
+
+/* first or middle sibling, collapsable, hover */
+.ygtvtmh
+{
+ width:16px; height:18px;
+ cursor:pointer ;
+ background: url(../images/icons/arrow_open.gif) 0 0 no-repeat;
+}
+
+/* first or middle sibling, expandable */
+.ygtvtp
+{
+ width:16px; height:18px;
+ cursor:pointer ;
+ background: url(../images/icons/arrow_closed.gif) 0 0 no-repeat;
+}
+
+/* first or middle sibling, expandable, hover */
+.ygtvtph
+{
+ width:16px; height:18px;
+ cursor:pointer ;
+ background: url(../images/icons/arrow_closed.gif) 0 0 no-repeat;
+}
+
+/* last sibling, no children */
+.ygtvln
+{
+ width:16px; height:18px;
+ cursor:pointer ;
+ background: url(../images/icons/arrow_closed.gif) 0 0 no-repeat;
+}
+
+/* Last sibling, collapsable */
+.ygtvlm
+{
+ width:16px; height:18px;
+ cursor:pointer ;
+ background: url(../images/icons/arrow_open.gif) 0 0 no-repeat;
+}
+
+/* Last sibling, collapsable, hover */
+.ygtvlmh
+{
+ width:16px; height:18px;
+ cursor:pointer ;
+ background: url(../images/icons/arrow_open.gif) 0 0 no-repeat;
+}
+
+/* Last sibling, expandable */
+.ygtvlp
+{
+ width:16px; height:18px;
+ cursor:pointer ;
+ background: url(../images/icons/arrow_closed.gif) 0 0 no-repeat;
+}
+
+/* Last sibling, expandable, hover */
+.ygtvlph
+{
+ width:16px; height:18px; cursor:pointer ;
+ background: url(../images/icons/arrow_closed.gif) 0 0 no-repeat;
+}
+
+/* Loading icon */
+.ygtvloading
+{
+ width:16px; height:18px;
+ background: url(../scripts/ajax/yahoo/treeview/assets/loading.gif) 0 0 no-repeat;
+}
+
+/* the style for the empty cells that are used for rendering the depth
+* of the node */
+.ygtvdepthcell
+{
+ width:16px; height:18px;
+ background: url() 0 0 no-repeat;
+}
+
+.ygtvblankdepthcell { width:16px; height:22px; }
+
+/* the style of the div around each node */
+.ygtvitem { }
+
+/* the style of the div around each node's collection of children */
+.ygtvchildren { }
+* html .ygtvchildren { height:2%; }
+
+/* the style of the text label in ygTextNode */
+.ygtvlabel, .ygtvlabel:link, .ygtvlabel:visited, .ygtvlabel:hover
+{
+ margin-left:2px;
+ text-decoration: none;
+ background-color: white;
+}
+
+.ygtvspacer
+{
+ height: 10px;
+ width: 10px;
+ margin: 2px;
+}
+
+.ygtvhtml
+{
+ cursor: pointer;
+}
+
+.alflabel
+{
+ white-space: nowrap;
+}
+
+.alflabelselected
+{
+ background-color: #d3e6fe;
+}
+
+
diff --git a/source/web/images/icons/arrow_closed.gif b/source/web/images/icons/arrow_closed.gif
new file mode 100644
index 0000000000..655a4d6a08
Binary files /dev/null and b/source/web/images/icons/arrow_closed.gif differ
diff --git a/source/web/images/icons/arrow_open.gif b/source/web/images/icons/arrow_open.gif
new file mode 100644
index 0000000000..85853aa6cf
Binary files /dev/null and b/source/web/images/icons/arrow_open.gif differ
diff --git a/source/web/images/icons/reset.gif b/source/web/images/icons/reset.gif
new file mode 100644
index 0000000000..c5102fee84
Binary files /dev/null and b/source/web/images/icons/reset.gif differ
diff --git a/source/web/jsp/dashboards/dashlets/getting-started.jsp b/source/web/jsp/dashboards/dashlets/getting-started.jsp
index c8b4eee5fc..385d891a80 100644
--- a/source/web/jsp/dashboards/dashlets/getting-started.jsp
+++ b/source/web/jsp/dashboards/dashlets/getting-started.jsp
@@ -24,31 +24,31 @@
<% PanelGenerator.generatePanelStart(out, request.getContextPath(), "yellow", "#ffffcc"); %>
-
-
+
+
-
+
-
+
-
+
-
+
-
+
-
+
@@ -56,21 +56,21 @@
-
+
-
+
-
+
-
+
-
+
@@ -82,9 +82,9 @@
-
+
-
+
@@ -96,9 +96,9 @@
-
+
-
+
diff --git a/source/web/jsp/parts/shelf.jsp b/source/web/jsp/parts/shelf.jsp
index c01324edd6..33c98f2eb4 100644
--- a/source/web/jsp/parts/shelf.jsp
+++ b/source/web/jsp/parts/shelf.jsp
@@ -14,60 +14,19 @@
language governing permissions and limitations under the
License.
--%>
-<%-- Shelf area --%>
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- <%-- Shelf component --%>
- <%-- IMPORTANT NOTE: All inner components must be given an explicit ID! --%>
- <%-- This is because they are wrapped in a Panel component --%>
-
-
-
-
-
- <%-- NOTE: this component is exanded=true as default so the RecentSpaces managed Bean is
- instantied early - otherwise it will not be seen until this shelf component is
- first expanded. There is no config setting to do this in JSF by default --%>
-
-
-
-
-
-
-
-
- <%-- TBD
-
-
-
-
-
-
-
- --%>
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+<%@ taglib uri="http://java.sun.com/jsf/html" prefix="h" %>
+<%@ taglib uri="http://java.sun.com/jsf/core" prefix="f" %>
+<%@ taglib uri="/WEB-INF/alfresco.tld" prefix="a" %>
+<%@ taglib uri="/WEB-INF/repo.tld" prefix="r" %>
+
+<%@ page import="org.alfresco.web.app.Application" %>
+
+<%-- Sidebar --%>
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/source/web/scripts/ajax/common.js b/source/web/scripts/ajax/common.js
index 0675817a56..8c56c4136d 100644
--- a/source/web/scripts/ajax/common.js
+++ b/source/web/scripts/ajax/common.js
@@ -4,7 +4,7 @@
//
/**
- * Default handler for errors
+ * Default handler for errors when using the dojo toolkit
*/
function handleErrorDojo(type, errObj)
{
@@ -19,4 +19,22 @@ function handleErrorDojo(type, errObj)
// TODO: Show a nicer error page, an alert will do for now!
alert(msg);
+}
+
+/**
+ * Default handler for errors when using the yahoo toolkit
+ */
+function handleErrorYahoo(msg)
+{
+ // TODO: Show a nicer error page, an alert will do for now!
+ alert(msg);
+}
+
+/**
+ * Calculates and returns the context path for the current page
+ */
+function getContextPath()
+{
+ var w = window.location;
+ alert(w.pathname);
}
\ No newline at end of file
diff --git a/source/web/scripts/ajax/yahoo-tree.js b/source/web/scripts/ajax/yahoo-tree.js
new file mode 100644
index 0000000000..6f552265f4
--- /dev/null
+++ b/source/web/scripts/ajax/yahoo-tree.js
@@ -0,0 +1,158 @@
+var loadDataUrl = null;
+var collapseUrl = null;
+var nodeSelectedHandler = null;
+
+/**
+ * Sets the URL to use to retrive child nodes
+ */
+function setLoadDataUrl(url)
+{
+ loadDataUrl = url;
+}
+
+/**
+ * Sets the URL to inform the server that a node collapsed
+ */
+function setCollapseUrl(url)
+{
+ collapseUrl = url;
+}
+
+/**
+ * Sets the name of the handler function to use for the node selected event
+ */
+function setNodeSelectedHandler(handler)
+{
+ nodeSelectedHandler = handler;
+}
+
+/**
+ * Callback method used by the tree to load the given node's children
+ */
+function loadDataForNode(node, onCompleteCallback)
+{
+ if (loadDataUrl == null)
+ {
+ alert("AJAX URL has not been set for retrieving child nodes, call setLoadDataUrl()!");
+ return;
+ }
+
+ var nodeRef = node.data.nodeRef;
+
+ // TODO: add method to add param to url
+ var transaction = YAHOO.util.Connect.asyncRequest('GET', WEBAPP_CONTEXT + loadDataUrl + "&nodeRef=" + nodeRef,
+ {
+ success: function(o)
+ {
+ var parentNode = o.argument[0];
+ var data = o.responseXML.documentElement;
+
+ // parse the child data to create the child nodes
+ parseChildData(parentNode, data);
+
+ // execute the callback method
+ o.argument[1]();
+ },
+ failure: function(o)
+ {
+ handleErrorYahoo("Error: Failed to retrieve children for node: " + o.argument[0].data.nodeRef);
+
+ // execute the callback method
+ o.argument[1]();
+ },
+ argument: [node, onCompleteCallback]
+ }
+ , null);
+}
+
+/**
+ * Parses the given data returned from the server into the required child nodes
+ */
+function parseChildData(parentNode, data)
+{
+ if (data != undefined && data != null)
+ {
+ var nodes = data.getElementsByTagName("node");
+
+ for (var i = 0; i < nodes.length; i++)
+ {
+ var node = nodes[i];
+ var nodeRef = node.getAttribute("ref");
+ var name = node.getAttribute("name");
+ var icon = node.getAttribute("icon");
+
+ // create the new node
+ createYahooTreeNode(parentNode, nodeRef, name, icon, false, false);
+ }
+ }
+ else
+ {
+ alert("No data returned from server!");
+ }
+}
+
+/**
+ * Generates an HTML tree node and adds it to the given parent node
+ */
+function createYahooTreeNode(parentNode, nodeRef, name, icon, expanded, selected)
+{
+ var nodeHtml = "
" + name + "
";
+
+ return new YAHOO.widget.HTMLNode({ html: nodeHtml, nodeRef: nodeRef, icon: icon }, parentNode, expanded, 1);
+}
+
+/**
+ * Callback used to inform the server that the given node was collapsed in the UI
+ */
+function informOfCollapse(node)
+{
+ if (collapseUrl == null)
+ {
+ alert("AJAX URL has not been set for collapsing nodes, call setCollapseUrl()!");
+ return;
+ }
+
+ var nodeRef = node.data.nodeRef;
+
+ // remove the children from the node so when it's expanded again it re-queries the server
+ node.childrenRendered = false;
+ node.dynamicLoadComplete = false;
+ while (node.children.length)
+ {
+ tree.removeNode(node.children[0], false);
+ }
+
+ // TODO: add method to add param to url
+ var transaction = YAHOO.util.Connect.asyncRequest('GET', WEBAPP_CONTEXT + collapseUrl + "&nodeRef=" + nodeRef,
+ {
+ success: function(o)
+ {
+ // nothing to do on the client
+ },
+ failure: function(o)
+ {
+ handleErrorYahoo("Error: Failed to collapse node: " + o.argument[0].data.nodeRef);
+ },
+ argument: [node]
+ }
+ , null);
+}
+
+