FELIX-3769 Add support for top navigation categories

git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@1416231 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/webconsole/src/main/java/org/apache/felix/webconsole/AbstractWebConsolePlugin.java b/webconsole/src/main/java/org/apache/felix/webconsole/AbstractWebConsolePlugin.java
index 16d8e56..9162f60 100644
--- a/webconsole/src/main/java/org/apache/felix/webconsole/AbstractWebConsolePlugin.java
+++ b/webconsole/src/main/java/org/apache/felix/webconsole/AbstractWebConsolePlugin.java
@@ -114,6 +114,24 @@
 
 
     /**
+     * This method should return category string which will be used to render
+     * the plugin in the navigation menu. Default implementation returns null,
+     * which will result in the plugin link rendered as top level menu item.
+     * Concrete implementations wishing to be rendered as a sub-menu item under
+     * a category should override this method and return a string or define
+     * <code>felix.webconsole.category</code> OSGi property. Currently only
+     * single level categories are supported. So, this should be a simple
+     * String.
+     *
+     * @return category
+     */
+    public String getCategory()
+    {
+        return null;
+    }
+
+
+    /**
      * Renders the web console page for the request. This consist of the
      * following five parts called in order:
      * <ol>
@@ -610,61 +628,18 @@
     protected void renderTopNavigation( HttpServletRequest request, PrintWriter pw )
     {
         // assume pathInfo to not be null, else this would not be called
-        boolean linkToCurrent = true;
         String current = request.getPathInfo();
         int slash = current.indexOf( "/", 1 ); //$NON-NLS-1$
         if ( slash < 0 )
         {
             slash = current.length();
-            linkToCurrent = false;
         }
         current = current.substring( 1, slash );
 
-        boolean disabled = false;
         String appRoot = ( String ) request.getAttribute( WebConsoleConstants.ATTR_APP_ROOT );
-        Map labelMap = ( Map ) request.getAttribute( WebConsoleConstants.ATTR_LABEL_MAP );
-        if ( labelMap != null )
-        {
+    	Map menuMap = ( Map ) request.getAttribute( WebConsoleConstants.ATTR_LABEL_MAP );
 
-            // prepare the navigation
-            SortedMap map = new TreeMap( String.CASE_INSENSITIVE_ORDER );
-            for ( Iterator ri = labelMap.entrySet().iterator(); ri.hasNext(); )
-            {
-                Map.Entry labelMapEntry = ( Map.Entry ) ri.next();
-                if ( labelMapEntry.getKey() == null )
-                {
-                    // ignore renders without a label
-                }
-                else if ( disabled || current.equals( labelMapEntry.getKey() ) )
-                {
-                    if ( linkToCurrent )
-                    {
-                        map.put( labelMapEntry.getValue(), "<div class='ui-state-active'><a href='" + appRoot + "/" //$NON-NLS-1$ //$NON-NLS-2$
-                                + labelMapEntry.getKey() + "'>" + labelMapEntry.getValue() + "</a></div>"); //$NON-NLS-1$ //$NON-NLS-2$
-                    }
-                    else
-                    {
-                        map.put( labelMapEntry.getValue(), "<div class='ui-state-active'><span>" + labelMapEntry.getValue() //$NON-NLS-1$
-                            + "</span></div>"); //$NON-NLS-1$
-                    }
-                }
-                else
-                {
-                    map.put( labelMapEntry.getValue(), "<div class='ui-state-default'><a href='" + appRoot + "/" + labelMapEntry.getKey() + "'>" //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
-                        + labelMapEntry.getValue() + "</a></div>"); //$NON-NLS-1$
-                }
-            }
-
-            // render the navigation
-            pw.println("<div id='technav' class='ui-widget ui-widget-header'>"); //$NON-NLS-1$
-            for ( Iterator li = map.values().iterator(); li.hasNext(); )
-            {
-                pw.print(' ');
-                pw.println( li.next() );
-            }
-            pw.println( "</div>" ); //$NON-NLS-1$
-
-        }
+        this.renderMenu( menuMap, appRoot, pw );
 
         // render lang-box
         Map langMap = (Map) request.getAttribute(WebConsoleConstants.ATTR_LANG_MAP);
@@ -705,6 +680,49 @@
         }
     }
 
+
+    protected void renderMenu( Map menuMap, String appRoot, PrintWriter pw )
+    {
+        if ( menuMap != null )
+        {
+            pw.println( "<ul id=\"navmenu\">" );
+            renderSubmenu( menuMap, appRoot, pw, 0 );
+            pw.println( "</ul>" );
+        }
+    }
+
+
+    private void renderMenu( Map menuMap, String appRoot, PrintWriter pw, int level )
+    {
+        pw.println( "<ul class=\"navMenuLevel-" + level + "\">" );
+        renderSubmenu( menuMap, appRoot, pw, level );
+        pw.println( "</ul>" );
+    }
+
+
+    private void renderSubmenu( Map menuMap, String appRoot, PrintWriter pw, int level )
+    {
+        String liStyleClass = " class=\"navMenuItem-" + level + "\"";
+        Iterator itr = menuMap.keySet().iterator();
+        while ( itr.hasNext() )
+        {
+            String key = ( String ) itr.next();
+            if ( key.startsWith( "category." ) )
+            {
+                pw.println( "<li" + liStyleClass + "><a href=\"#\">" + key.substring( key.indexOf( '.' ) + 1 ) + "</a>" );
+                renderMenu( ( Map ) menuMap.get( key ), appRoot, pw, level + 1 );
+                pw.println( "</li>" );
+            }
+            else
+            {
+                String label = key;
+                String title = ( String ) menuMap.get( label );
+                pw.println( "<li" + liStyleClass + "><a href=\"" + appRoot + "/" + label + "\">" + title + "</a></li>" );
+            }
+        }
+    }
+
+
     private static final void printLocaleElement(PrintWriter pw, String appRoot,
         Object langCode, Object langName)
     {
@@ -847,7 +865,6 @@
         return FOOTER;
     }
 
-
     /**
      * Reads the <code>templateFile</code> as a resource through the class
      * loader of this class converting the binary data into a string using
diff --git a/webconsole/src/main/java/org/apache/felix/webconsole/SimpleWebConsolePlugin.java b/webconsole/src/main/java/org/apache/felix/webconsole/SimpleWebConsolePlugin.java
index 8bb3a60..6195ed2 100644
--- a/webconsole/src/main/java/org/apache/felix/webconsole/SimpleWebConsolePlugin.java
+++ b/webconsole/src/main/java/org/apache/felix/webconsole/SimpleWebConsolePlugin.java
@@ -48,6 +48,7 @@
     // used for standard AbstractWebConsolePlugin implementation
     private final String label;
     private final String title;
+    private final String category;
     private final String css[];
     private final String labelRes;
     private final int labelResLen;
@@ -61,7 +62,8 @@
 
 
     /**
-     * Creates new Simple Web Console Plugin.
+     * Creates new Simple Web Console Plugin with the default category
+     * ({@code null})
      *
      * @param label the front label. See
      *          {@link AbstractWebConsolePlugin#getLabel()}
@@ -72,6 +74,24 @@
      */
     public SimpleWebConsolePlugin( String label, String title, String css[] )
     {
+        this( label, title, null, css );
+    }
+
+
+    /**
+     * Creates new Simple Web Console Plugin with the given category.
+     *
+     * @param label the front label. See
+     *            {@link AbstractWebConsolePlugin#getLabel()}
+     * @param title the plugin title . See
+     *            {@link AbstractWebConsolePlugin#getTitle()}
+     * @param category the plugin's navigation category. See
+     *            {@link AbstractWebConsolePlugin#getCategory()}
+     * @param css the additional plugin CSS. See
+     *            {@link AbstractWebConsolePlugin#getCssReferences()}
+     */
+    public SimpleWebConsolePlugin( String label, String title, String category, String css[] )
+    {
         if ( label == null )
         {
             throw new NullPointerException( "Null label" );
@@ -82,6 +102,7 @@
         }
         this.label = label;
         this.title = title;
+        this.category = category;
         this.css = css;
         this.labelRes = '/' + label + '/';
         this.labelResLen = labelRes.length() - 1;
@@ -107,6 +128,15 @@
 
 
     /**
+     * @see org.apache.felix.webconsole.AbstractWebConsolePlugin#getCategory()
+     */
+    public String getCategory()
+    {
+        return category;
+    }
+
+
+    /**
      * @see org.apache.felix.webconsole.AbstractWebConsolePlugin#getCssReferences()
      */
     protected final String[] getCssReferences()
diff --git a/webconsole/src/main/java/org/apache/felix/webconsole/WebConsoleConstants.java b/webconsole/src/main/java/org/apache/felix/webconsole/WebConsoleConstants.java
index e4d3025..47168fc 100644
--- a/webconsole/src/main/java/org/apache/felix/webconsole/WebConsoleConstants.java
+++ b/webconsole/src/main/java/org/apache/felix/webconsole/WebConsoleConstants.java
@@ -62,6 +62,27 @@
     public static final String PLUGIN_TITLE = "felix.webconsole.title"; //$NON-NLS-1$
 
     /**
+     * The category under which the OSGi Manager plugin is listed in the top
+     * navigation by the OSGi Manager (value is "felix.webconsole.category").
+     * <p>
+     * For {@link #SERVICE_NAME Servlet} services not extending the
+     * {@link AbstractWebConsolePlugin} this property is required to declare a
+     * specific category. Otherwise the plugin is put into the default category.
+     * <p>
+     * For {@link #SERVICE_NAME Servlet} services extending from the
+     * {@link AbstractWebConsolePlugin} abstract class this property is not
+     * technically required. To support lazy service access with categorization,
+     * e.g. for plugins implemented using the OSGi <i>Service Factory</i>
+     * pattern, the use of this service registration property is strongly
+     * encouraged. If the property is missing the
+     * {@link AbstractWebConsolePlugin#getCategory()} is called which should be
+     * overritten.
+     *
+     * @since 3.1.3; Web Console Bundle 4.0.2
+     */
+    public static final String PLUGIN_CATEGORY = "felix.webconsole.category"; //$NON-NLS-1$
+
+    /**
      * The property marking a service as a configuration printer.
      * This can be any service having either a printConfiguration(PrintWriter)
      * or printConfiguration(PrintWriter, String) method - this is according
diff --git a/webconsole/src/main/java/org/apache/felix/webconsole/internal/OsgiManagerPlugin.java b/webconsole/src/main/java/org/apache/felix/webconsole/internal/OsgiManagerPlugin.java
index c34d338..35bdf04 100644
--- a/webconsole/src/main/java/org/apache/felix/webconsole/internal/OsgiManagerPlugin.java
+++ b/webconsole/src/main/java/org/apache/felix/webconsole/internal/OsgiManagerPlugin.java
@@ -31,6 +31,17 @@
 {
 
     /**
+     * Category used for Web Console specific plugins.
+     */
+    public static final String CATEGORY_OSGI_MANAGER = "Web Console";
+
+    /**
+     * Category used for Web Console plugins related to OSGi support such
+     * as bundles, configurations, etc.
+     */
+    public static final String CATEGORY_OSGI = "OSGi";
+
+    /**
      * This method is called from the Felix Web Console to ensure the
      * AbstractWebConsolePlugin is correctly setup.
      *
diff --git a/webconsole/src/main/java/org/apache/felix/webconsole/internal/compendium/ConfigManager.java b/webconsole/src/main/java/org/apache/felix/webconsole/internal/compendium/ConfigManager.java
index 4e4ece5..bc5b98c 100644
--- a/webconsole/src/main/java/org/apache/felix/webconsole/internal/compendium/ConfigManager.java
+++ b/webconsole/src/main/java/org/apache/felix/webconsole/internal/compendium/ConfigManager.java
@@ -99,7 +99,7 @@
     /** Default constructor */
     public ConfigManager()
     {
-        super(LABEL, TITLE, CSS);
+        super(LABEL, TITLE, CATEGORY_OSGI, CSS);
 
         // load templates
         TEMPLATE = readTemplateFile( "/templates/config.html" ); //$NON-NLS-1$
diff --git a/webconsole/src/main/java/org/apache/felix/webconsole/internal/compendium/ConfigManagerBase.java b/webconsole/src/main/java/org/apache/felix/webconsole/internal/compendium/ConfigManagerBase.java
index 8af2928..81f04f3 100644
--- a/webconsole/src/main/java/org/apache/felix/webconsole/internal/compendium/ConfigManagerBase.java
+++ b/webconsole/src/main/java/org/apache/felix/webconsole/internal/compendium/ConfigManagerBase.java
@@ -43,9 +43,9 @@
 abstract class ConfigManagerBase extends SimpleWebConsolePlugin implements OsgiManagerPlugin
 {
 
-    ConfigManagerBase(String label, String title, String[] css)
+    ConfigManagerBase(String label, String title, String category, String[] css)
     {
-        super(label, title, css);
+        super(label, title, category, css);
     }
 
     private static final long serialVersionUID = -6691093960031418130L;
diff --git a/webconsole/src/main/java/org/apache/felix/webconsole/internal/compendium/LogServlet.java b/webconsole/src/main/java/org/apache/felix/webconsole/internal/compendium/LogServlet.java
index 9853b21..33609cf 100644
--- a/webconsole/src/main/java/org/apache/felix/webconsole/internal/compendium/LogServlet.java
+++ b/webconsole/src/main/java/org/apache/felix/webconsole/internal/compendium/LogServlet.java
@@ -57,7 +57,7 @@
     /** Default constructor */
     public LogServlet()
     {
-        super(LABEL, TITLE, CSS);
+        super(LABEL, TITLE, CATEGORY_OSGI_MANAGER, CSS);
 
         // load templates
         TEMPLATE = readTemplateFile( "/templates/logs.html" ); //$NON-NLS-1$
diff --git a/webconsole/src/main/java/org/apache/felix/webconsole/internal/core/BundlesServlet.java b/webconsole/src/main/java/org/apache/felix/webconsole/internal/core/BundlesServlet.java
index a24700a..fe741db 100644
--- a/webconsole/src/main/java/org/apache/felix/webconsole/internal/core/BundlesServlet.java
+++ b/webconsole/src/main/java/org/apache/felix/webconsole/internal/core/BundlesServlet.java
@@ -125,7 +125,7 @@
     /** Default constructor */
     public BundlesServlet()
     {
-        super(NAME, TITLE, CSS);
+        super(NAME, TITLE, CATEGORY_OSGI, CSS);
 
         // load templates
         TEMPLATE_MAIN = readTemplateFile( "/templates/bundles.html" );
diff --git a/webconsole/src/main/java/org/apache/felix/webconsole/internal/core/ServicesServlet.java b/webconsole/src/main/java/org/apache/felix/webconsole/internal/core/ServicesServlet.java
index 2a41111..60f45f6 100644
--- a/webconsole/src/main/java/org/apache/felix/webconsole/internal/core/ServicesServlet.java
+++ b/webconsole/src/main/java/org/apache/felix/webconsole/internal/core/ServicesServlet.java
@@ -114,20 +114,20 @@
 
     /** Default constructor */
     public ServicesServlet() {
-        super(LABEL, TITLE, CSS);
+        super(LABEL, TITLE, CATEGORY_OSGI, CSS);
 
         // load templates
         TEMPLATE = readTemplateFile( "/templates/services.html" ); //$NON-NLS-1$
     }
-    
+
     private ServiceRegistration bipReg;
-    
-    public void activate(BundleContext bundleContext) 
+
+    public void activate(BundleContext bundleContext)
     {
         super.activate(bundleContext);
         bipReg = new ServicesUsedInfoProvider( bundleContext.getBundle() ).register( bundleContext );
     }
-    
+
     public void deactivate() {
 	if ( null != bipReg )
 	{
diff --git a/webconsole/src/main/java/org/apache/felix/webconsole/internal/misc/ConfigurationRender.java b/webconsole/src/main/java/org/apache/felix/webconsole/internal/misc/ConfigurationRender.java
index e000fac..5797fe9 100644
--- a/webconsole/src/main/java/org/apache/felix/webconsole/internal/misc/ConfigurationRender.java
+++ b/webconsole/src/main/java/org/apache/felix/webconsole/internal/misc/ConfigurationRender.java
@@ -73,7 +73,7 @@
     /** Default constructor */
     public ConfigurationRender( final ResourceBundleManager resourceBundleManager )
     {
-        super( LABEL, TITLE, CSS_REFS );
+        super( LABEL, TITLE, CATEGORY_OSGI_MANAGER, CSS_REFS );
         this.resourceBundleManager = resourceBundleManager;
     }
 
diff --git a/webconsole/src/main/java/org/apache/felix/webconsole/internal/misc/LicenseServlet.java b/webconsole/src/main/java/org/apache/felix/webconsole/internal/misc/LicenseServlet.java
index ca5c742..1e2bd07 100644
--- a/webconsole/src/main/java/org/apache/felix/webconsole/internal/misc/LicenseServlet.java
+++ b/webconsole/src/main/java/org/apache/felix/webconsole/internal/misc/LicenseServlet.java
@@ -69,7 +69,7 @@
      */
     public LicenseServlet()
     {
-        super(LABEL, TITLE, CSS);
+        super(LABEL, TITLE, CATEGORY_OSGI_MANAGER, CSS);
 
         // load templates
         TEMPLATE = readTemplateFile( "/templates/license.html" );
diff --git a/webconsole/src/main/java/org/apache/felix/webconsole/internal/servlet/PluginHolder.java b/webconsole/src/main/java/org/apache/felix/webconsole/internal/servlet/PluginHolder.java
index ade46d8..e3a0970 100644
--- a/webconsole/src/main/java/org/apache/felix/webconsole/internal/servlet/PluginHolder.java
+++ b/webconsole/src/main/java/org/apache/felix/webconsole/internal/servlet/PluginHolder.java
@@ -19,12 +19,15 @@
 package org.apache.felix.webconsole.internal.servlet;
 
 
+import java.util.Comparator;
 import java.util.Enumeration;
 import java.util.HashMap;
 import java.util.Locale;
 import java.util.Map;
 import java.util.NoSuchElementException;
 import java.util.ResourceBundle;
+import java.util.SortedMap;
+import java.util.TreeMap;
 
 import javax.servlet.Servlet;
 import javax.servlet.ServletConfig;
@@ -234,16 +237,30 @@
      */
     Map getLocalizedLabelMap( final ResourceBundleManager resourceBundleManager, final Locale locale )
     {
-        final Map map = new HashMap();
+        final SortedMap map = new TreeMap( MENU_ITEM_ORDER );
         Plugin[] plugins = getPlugins();
         for ( int i = 0; i < plugins.length; i++ )
         {
             final Plugin plugin = plugins[i];
 
-            if (!plugin.isEnabled()) {
+            if ( !plugin.isEnabled() )
+            {
                 continue;
             }
 
+            // support only one level for now
+            SortedMap categoryMap = null;
+            String category = plugin.getCategory();
+            if ( category == null || category.trim().length() == 0 )
+            {
+                // TODO: FELIX-3769; decide whether hard code or configurable
+                category = "Main";
+            }
+
+            // TODO: FELIX-3769; translate the Category
+
+            categoryMap = findCategoryMap( map, category );
+
             final String label = plugin.getLabel();
             String title = plugin.getTitle();
             if ( title.startsWith( "%" ) )
@@ -259,13 +276,39 @@
                     /* ignore missing resource - use default title */
                 }
             }
-            map.put( label, title );
+            categoryMap.put( label, title );
         }
 
         return map;
     }
 
 
+    private SortedMap findCategoryMap( Map map, String categoryPath )
+    {
+        SortedMap categoryMap = null;
+        Map searchMap = map;
+
+        String categories[] = categoryPath.split( "/" );
+
+        for ( int i = 0; i < categories.length; i++ )
+        {
+            String categoryKey = "category." + categories[i];
+            if ( searchMap.containsKey( categoryKey ) )
+            {
+                categoryMap = ( SortedMap ) searchMap.get( categoryKey );
+            }
+            else
+            {
+                categoryMap = new TreeMap( MENU_ITEM_ORDER );
+                searchMap.put( categoryKey, categoryMap );
+            }
+            searchMap = categoryMap;
+        }
+
+        return categoryMap;
+    }
+
+
     /**
      * Returns the bundle context of the Web Console itself.
      * @return the bundle context of the Web Console itself.
@@ -416,6 +459,26 @@
         return null;
     }
 
+    /**
+     * A comparator that will compare plugin menu items to other menu items including categories.
+     */
+    private static final Comparator MENU_ITEM_ORDER = new Comparator() {
+
+		public int compare(Object o1, Object o2) {
+			String s1 = getLabel(o1.toString());
+			String s2 = getLabel(o2.toString());
+			return s1.compareToIgnoreCase(s2);
+		}
+
+		private String getLabel(String s) {
+			if(s.startsWith("category."))
+				return s.substring(s.indexOf('.')+1);
+			else
+				return s;
+		}
+
+    };
+
     private static class Plugin implements ServletConfig
     {
         private final PluginHolder holder;
@@ -423,7 +486,6 @@
         private String title;
         private AbstractWebConsolePlugin consolePlugin;
 
-
         protected Plugin( final PluginHolder holder, final String label )
         {
             this.holder = holder;
@@ -515,7 +577,6 @@
             return title;
         }
 
-
         protected String doGetTitle()
         {
             // get the service now
@@ -527,6 +588,17 @@
             return ( consolePlugin != null ) ? consolePlugin.getTitle() : null;
         }
 
+        // methods added to support categories
+
+        final String getCategory() {
+        	return doGetCategory();
+        }
+
+        protected String doGetCategory() {
+        	// get the service now
+            final AbstractWebConsolePlugin consolePlugin = getConsolePlugin();
+            return ( consolePlugin != null ) ? consolePlugin.getCategory() : null;
+        }
 
         final AbstractWebConsolePlugin getConsolePlugin()
         {
@@ -643,6 +715,18 @@
             return super.doGetTitle();
         }
 
+        // added to support categories
+        protected String doGetCategory() {
+            // check service Reference
+            final String category = getProperty( serviceReference, WebConsoleConstants.PLUGIN_CATEGORY );
+            if ( category != null )
+            {
+                return category;
+            }
+
+            return super.doGetCategory();
+        }
+
         protected AbstractWebConsolePlugin doGetConsolePlugin()
         {
             Object service = getHolder().getBundleContext().getService( serviceReference );
diff --git a/webconsole/src/main/java/org/apache/felix/webconsole/internal/system/VMStatPlugin.java b/webconsole/src/main/java/org/apache/felix/webconsole/internal/system/VMStatPlugin.java
index 6da1852..6df41ac 100644
--- a/webconsole/src/main/java/org/apache/felix/webconsole/internal/system/VMStatPlugin.java
+++ b/webconsole/src/main/java/org/apache/felix/webconsole/internal/system/VMStatPlugin.java
@@ -73,7 +73,7 @@
     /** Default constructor */
     public VMStatPlugin()
     {
-        super( LABEL, TITLE, CSS );
+        super( LABEL, TITLE, CATEGORY_OSGI_MANAGER, CSS );
 
         // load templates
         TPL_VM_MAIN = readTemplateFile(  "/templates/vmstat.html"  ); //$NON-NLS-1$
@@ -201,7 +201,7 @@
         DateFormat format = DateFormat.getDateTimeInstance( DateFormat.LONG, DateFormat.LONG, request.getLocale() );
         final String startTime = format.format( new Date( startDate ) );
         final String upTime = formatPeriod( System.currentTimeMillis() - startDate );
-        
+
         JSONObject json = new JSONObject();
         try
         {
@@ -236,7 +236,7 @@
 
         response.getWriter().print( body );
     }
-    
+
     private static final String sysProp( String name )
     {
         String ret = System.getProperty( name );
diff --git a/webconsole/src/main/resources/res/ui/webconsole.css b/webconsole/src/main/resources/res/ui/webconsole.css
index fb1510a..7b1ce8a 100644
--- a/webconsole/src/main/resources/res/ui/webconsole.css
+++ b/webconsole/src/main/resources/res/ui/webconsole.css
@@ -66,6 +66,17 @@
 	white-space: nowrap;
 }
 
+/* New Category Based Navigation Tree (FELIX-3769) */
+#navmenu {
+     width: auto;
+}
+#navmenu li.ui-menu-item {
+     width: auto;
+}
+#navmenu li.navMenuItem-0 {
+    display: inline-block;
+}
+
 /* CENTRAL CONTENT AREA STYLING */
 #content, .ui-widget, .ui-widget input, .ui-widget select, .ui-widget textarea, .ui-widget button { font-size: 8pt }
 
diff --git a/webconsole/src/main/resources/templates/main_header.html b/webconsole/src/main/resources/templates/main_header.html
index fdf6eff..b6ddbfa 100644
--- a/webconsole/src/main/resources/templates/main_header.html
+++ b/webconsole/src/main/resources/templates/main_header.html
@@ -24,8 +24,22 @@
 	<script src="${appRoot}/res/lib/jquery.cookies-2.2.0.js" type="text/javascript"></script>
 	<script src="${appRoot}/res/lib/jquery.tablesorter-2.0.3.js" type="text/javascript"></script>
 	<script src="${appRoot}/res/lib/support.js" type="text/javascript"></script>
-	
-	   
+
+    <script type="text/javascript">
+    // <![CDATA[
+        $(document).ready(function(){
+            $( '#navmenu' ).menu({ 
+                position: {
+                    my: "left top",
+                    at: "left bottom",
+                    submenu: "ui-icon-carat-1-s"
+                } 
+            });
+        });
+        
+    // ]]>
+    </script>
+       
     <!-- FELIX-2188: backwards compatibility CSS -->
     <link href="${appRoot}/res/ui/admin_compat.css" rel="stylesheet" type="text/css" />
 </head>