]> source.dussan.org Git - gitblit.git/commitdiff
Allow plugins to extend the top navbar and repository navbar 23/23/6
authorJames Moger <james.moger@gitblit.com>
Wed, 23 Apr 2014 02:53:06 +0000 (22:53 -0400)
committerJames Moger <james.moger@gitblit.com>
Mon, 5 May 2014 15:32:46 +0000 (11:32 -0400)
This change also ties the plugin manager into the Wicket framework and
allows plugins to contribute and mount new pages which are linked by the
top navbar and repository navbar extensions.

21 files changed:
src/main/java/com/gitblit/extensions/AdminMenuExtension.java [deleted file]
src/main/java/com/gitblit/extensions/GitblitWicketPlugin.java [new file with mode: 0644]
src/main/java/com/gitblit/extensions/NavLinkExtension.java [new file with mode: 0644]
src/main/java/com/gitblit/extensions/RepositoryNavLinkExtension.java [new file with mode: 0644]
src/main/java/com/gitblit/models/NavLink.java [new file with mode: 0644]
src/main/java/com/gitblit/wicket/GitBlitWebApp.java
src/main/java/com/gitblit/wicket/GitblitWicketApp.java [new file with mode: 0644]
src/main/java/com/gitblit/wicket/PageRegistration.java [deleted file]
src/main/java/com/gitblit/wicket/PluginClassResolver.java [new file with mode: 0644]
src/main/java/com/gitblit/wicket/UrlExternalFormComparator.java [new file with mode: 0644]
src/main/java/com/gitblit/wicket/pages/ActivityPage.java
src/main/java/com/gitblit/wicket/pages/DashboardPage.java
src/main/java/com/gitblit/wicket/pages/ProjectPage.java
src/main/java/com/gitblit/wicket/pages/ProjectsPage.java
src/main/java/com/gitblit/wicket/pages/RepositoriesPage.java
src/main/java/com/gitblit/wicket/pages/RepositoryPage.java
src/main/java/com/gitblit/wicket/pages/RootPage.java
src/main/java/com/gitblit/wicket/pages/UserPage.java
src/main/java/com/gitblit/wicket/panels/DropDownMenu.java
src/main/java/com/gitblit/wicket/panels/NavigationPanel.java
src/site/plugins_extensions.mkd

diff --git a/src/main/java/com/gitblit/extensions/AdminMenuExtension.java b/src/main/java/com/gitblit/extensions/AdminMenuExtension.java
deleted file mode 100644 (file)
index 8fe4288..0000000
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * Copyright 2014 gitblit.com.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * 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 com.gitblit.extensions;
-
-import java.util.List;
-
-import ro.fortsoft.pf4j.ExtensionPoint;
-
-import com.gitblit.models.Menu.MenuItem;
-import com.gitblit.models.UserModel;
-
-/**
- * Extension point to contribute administration menu items.
- *
- * @author James Moger
- * @since 1.6.0
- *
- */
-public abstract class AdminMenuExtension implements ExtensionPoint {
-
-       /**
-        * @param user
-        * @since 1.6.0
-        * @return a list of menu items
-        */
-       public abstract List<MenuItem> getMenuItems(UserModel user);
-}
diff --git a/src/main/java/com/gitblit/extensions/GitblitWicketPlugin.java b/src/main/java/com/gitblit/extensions/GitblitWicketPlugin.java
new file mode 100644 (file)
index 0000000..130f499
--- /dev/null
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2014 gitblit.com.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * 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 com.gitblit.extensions;
+
+import org.apache.wicket.Application;
+import org.apache.wicket.IInitializer;
+
+import ro.fortsoft.pf4j.PluginWrapper;
+
+import com.gitblit.wicket.GitblitWicketApp;
+
+/**
+ * A Gitblit plugin that is allowed to extend the Wicket webapp.
+ *
+ * @author James Moger
+ * @since 1.6.0
+ */
+public abstract class GitblitWicketPlugin extends GitblitPlugin implements IInitializer  {
+
+       public GitblitWicketPlugin(PluginWrapper wrapper) {
+               super(wrapper);
+       }
+
+       @Override
+       public final void init(Application application) {
+               init((GitblitWicketApp) application);
+       }
+
+       /**
+        * Allows plugins to extend the web application.
+        *
+        * @param app
+        * @since 1.6.0
+        */
+       protected abstract void init(GitblitWicketApp app);
+}
\ No newline at end of file
diff --git a/src/main/java/com/gitblit/extensions/NavLinkExtension.java b/src/main/java/com/gitblit/extensions/NavLinkExtension.java
new file mode 100644 (file)
index 0000000..c895860
--- /dev/null
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2014 gitblit.com.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * 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 com.gitblit.extensions;
+
+import java.util.List;
+
+import ro.fortsoft.pf4j.ExtensionPoint;
+
+import com.gitblit.models.NavLink;
+import com.gitblit.models.UserModel;
+
+/**
+ * Extension point to contribute top-level navigation links.
+ *
+ * @author James Moger
+ * @since 1.6.0
+ *
+ */
+public abstract class NavLinkExtension implements ExtensionPoint {
+
+       /**
+        * @param user
+        * @since 1.6.0
+        * @return a list of nav links
+        */
+       public abstract List<NavLink> getNavLinks(UserModel user);
+}
diff --git a/src/main/java/com/gitblit/extensions/RepositoryNavLinkExtension.java b/src/main/java/com/gitblit/extensions/RepositoryNavLinkExtension.java
new file mode 100644 (file)
index 0000000..2b05c5a
--- /dev/null
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2014 gitblit.com.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * 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 com.gitblit.extensions;
+
+import java.util.List;
+
+import ro.fortsoft.pf4j.ExtensionPoint;
+
+import com.gitblit.models.NavLink;
+import com.gitblit.models.RepositoryModel;
+import com.gitblit.models.UserModel;
+
+/**
+ * Extension point to contribute repository page navigation links.
+ *
+ * @author James Moger
+ * @since 1.6.0
+ *
+ */
+public abstract class RepositoryNavLinkExtension implements ExtensionPoint {
+
+       /**
+        * @param user
+        * @param repository
+        * @since 1.6.0
+        * @return a list of nav links
+        */
+       public abstract List<NavLink> getNavLinks(UserModel user, RepositoryModel repository);
+}
diff --git a/src/main/java/com/gitblit/models/NavLink.java b/src/main/java/com/gitblit/models/NavLink.java
new file mode 100644 (file)
index 0000000..993d695
--- /dev/null
@@ -0,0 +1,140 @@
+/*\r
+ * Copyright 2011 gitblit.com.\r
+ *\r
+ * Licensed under the Apache License, Version 2.0 (the "License");\r
+ * you may not use this file except in compliance with the License.\r
+ * You may obtain a copy of the License at\r
+ *\r
+ *     http://www.apache.org/licenses/LICENSE-2.0\r
+ *\r
+ * Unless required by applicable law or agreed to in writing, software\r
+ * distributed under the License is distributed on an "AS IS" BASIS,\r
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ * See the License for the specific language governing permissions and\r
+ * limitations under the License.\r
+ */\r
+package com.gitblit.models;\r
+\r
+import java.io.Serializable;\r
+import java.util.ArrayList;\r
+import java.util.List;\r
+\r
+import org.apache.wicket.PageParameters;\r
+import org.apache.wicket.markup.html.WebPage;\r
+\r
+import com.gitblit.models.Menu.MenuItem;\r
+\r
+/**\r
+ * Represents a navigation link for the navigation panel.\r
+ *\r
+ * @author James Moger\r
+ *\r
+ */\r
+public abstract class NavLink implements Serializable {\r
+       private static final long serialVersionUID = 1L;\r
+\r
+       public final String translationKey;\r
+       public final boolean hiddenPhone;\r
+\r
+       public NavLink(String translationKey, boolean hiddenPhone) {\r
+               this.translationKey = translationKey;\r
+               this.hiddenPhone = hiddenPhone;\r
+       }\r
+\r
+\r
+       /**\r
+        * Represents a Wicket page link.\r
+        *\r
+        * @author James Moger\r
+        *\r
+        */\r
+       public static class PageNavLink extends NavLink implements Serializable {\r
+               private static final long serialVersionUID = 1L;\r
+\r
+               public final Class<? extends WebPage> pageClass;\r
+               public final PageParameters params;\r
+\r
+               public PageNavLink(String translationKey, Class<? extends WebPage> pageClass) {\r
+                       this(translationKey, pageClass, null);\r
+               }\r
+\r
+               public PageNavLink(String translationKey, Class<? extends WebPage> pageClass,\r
+                               PageParameters params) {\r
+                       this(translationKey, pageClass, params, false);\r
+               }\r
+\r
+               public PageNavLink(String translationKey, Class<? extends WebPage> pageClass,\r
+                               PageParameters params, boolean hiddenPhone) {\r
+                       super(translationKey, hiddenPhone);\r
+                       this.pageClass = pageClass;\r
+                       this.params = params;\r
+               }\r
+       }\r
+\r
+       /**\r
+        * Represents an explicitly href link.\r
+        *\r
+        * @author James Moger\r
+        *\r
+        */\r
+       public static class ExternalNavLink extends NavLink implements Serializable {\r
+\r
+               private static final long serialVersionUID = 1L;\r
+\r
+               public final String url;\r
+\r
+               public ExternalNavLink(String keyOrText, String url) {\r
+                       super(keyOrText, false);\r
+                       this.url = url;\r
+               }\r
+\r
+               public ExternalNavLink(String keyOrText, String url, boolean hiddenPhone) {\r
+                       super(keyOrText,  hiddenPhone);\r
+                       this.url = url;\r
+               }\r
+       }\r
+\r
+       /**\r
+        * Represents a DropDownMenu for the current page.\r
+        *\r
+        * @author James Moger\r
+        *\r
+        */\r
+       public static class DropDownPageMenuNavLink extends PageNavLink implements Serializable {\r
+\r
+               private static final long serialVersionUID = 1L;\r
+\r
+               public final List<MenuItem> menuItems;\r
+\r
+               public DropDownPageMenuNavLink(String keyOrText, Class<? extends WebPage> pageClass) {\r
+                       this(keyOrText, pageClass, false);\r
+               }\r
+\r
+               public DropDownPageMenuNavLink(String keyOrText, Class<? extends WebPage> pageClass, boolean hiddenPhone) {\r
+                       super(keyOrText, pageClass, null, hiddenPhone);\r
+                       menuItems = new ArrayList<MenuItem>();\r
+               }\r
+       }\r
+\r
+       /**\r
+        * Represents a DropDownMenu.\r
+        *\r
+        * @author James Moger\r
+        *\r
+        */\r
+       public static class DropDownMenuNavLink extends NavLink implements Serializable {\r
+\r
+               private static final long serialVersionUID = 1L;\r
+\r
+               public final List<MenuItem> menuItems;\r
+\r
+               public DropDownMenuNavLink(String keyOrText) {\r
+                       this(keyOrText, false);\r
+               }\r
+\r
+               public DropDownMenuNavLink(String keyOrText, boolean hiddenPhone) {\r
+                       super(keyOrText, hiddenPhone);\r
+                       menuItems = new ArrayList<MenuItem>();\r
+               }\r
+       }\r
+}
\ No newline at end of file
index 3ca7d48fd89b69fced11d89cd2d0b0c58aa6dd7a..d3aa62fdd6de9c0eb3b3568df66380feb3335bbc 100644 (file)
@@ -28,8 +28,12 @@ import org.apache.wicket.Session;
 import org.apache.wicket.markup.html.WebPage;
 import org.apache.wicket.protocol.http.WebApplication;
 
+import ro.fortsoft.pf4j.PluginState;
+import ro.fortsoft.pf4j.PluginWrapper;
+
 import com.gitblit.IStoredSettings;
 import com.gitblit.Keys;
+import com.gitblit.extensions.GitblitWicketPlugin;
 import com.gitblit.manager.IAuthenticationManager;
 import com.gitblit.manager.IFederationManager;
 import com.gitblit.manager.IGitblit;
@@ -83,7 +87,7 @@ import com.gitblit.wicket.pages.TreePage;
 import com.gitblit.wicket.pages.UserPage;
 import com.gitblit.wicket.pages.UsersPage;
 
-public class GitBlitWebApp extends WebApplication {
+public class GitBlitWebApp extends WebApplication implements GitblitWicketApp {
 
        private final Class<? extends WebPage> homePageClass = MyDashboardPage.class;
 
@@ -210,11 +214,29 @@ public class GitBlitWebApp extends WebApplication {
                mount("/forks", ForksPage.class, "r");
                mount("/fork", ForkPage.class, "r");
 
+               // allow started Wicket plugins to initialize
+               for (PluginWrapper pluginWrapper : pluginManager.getPlugins()) {
+                       if (PluginState.STARTED != pluginWrapper.getPluginState()) {
+                               continue;
+                       }
+                       if (pluginWrapper.getPlugin() instanceof GitblitWicketPlugin) {
+                               GitblitWicketPlugin wicketPlugin = (GitblitWicketPlugin) pluginWrapper.getPlugin();
+                               wicketPlugin.init(this);
+                       }
+               }
+
+                // customize the Wicket class resolver to load from plugins
+        PluginClassResolver classResolver = new PluginClassResolver(pluginManager);
+        getApplicationSettings().setClassResolver(classResolver);
+
                getMarkupSettings().setDefaultMarkupEncoding("UTF-8");
-               super.init();
        }
 
-       private void mount(String location, Class<? extends WebPage> clazz, String... parameters) {
+       /* (non-Javadoc)
+        * @see com.gitblit.wicket.Webapp#mount(java.lang.String, java.lang.Class, java.lang.String)
+        */
+       @Override
+       public void mount(String location, Class<? extends WebPage> clazz, String... parameters) {
                if (parameters == null) {
                        parameters = new String[] {};
                }
@@ -230,15 +252,26 @@ public class GitBlitWebApp extends WebApplication {
                }
        }
 
+       /* (non-Javadoc)
+        * @see com.gitblit.wicket.Webapp#getHomePage()
+        */
        @Override
        public Class<? extends WebPage> getHomePage() {
                return homePageClass;
        }
 
+       /* (non-Javadoc)
+        * @see com.gitblit.wicket.Webapp#isCacheablePage(java.lang.String)
+        */
+       @Override
        public boolean isCacheablePage(String mountPoint) {
                return cacheablePages.containsKey(mountPoint);
        }
 
+       /* (non-Javadoc)
+        * @see com.gitblit.wicket.Webapp#getCacheControl(java.lang.String)
+        */
+       @Override
        public CacheControl getCacheControl(String mountPoint) {
                return cacheablePages.get(mountPoint);
        }
@@ -254,15 +287,18 @@ public class GitBlitWebApp extends WebApplication {
                return gitBlitWebSession;
        }
 
+       /* (non-Javadoc)
+        * @see com.gitblit.wicket.Webapp#settings()
+        */
+       @Override
        public IStoredSettings settings() {
                return settings;
        }
 
-       /**
-        * Is Gitblit running in debug mode?
-        *
-        * @return true if Gitblit is running in debug mode
+       /* (non-Javadoc)
+        * @see com.gitblit.wicket.Webapp#isDebugMode()
         */
+       @Override
        public boolean isDebugMode() {
                return runtimeManager.isDebugMode();
        }
@@ -271,58 +307,114 @@ public class GitBlitWebApp extends WebApplication {
         * These methods look strange... and they are... but they are the first
         * step towards modularization across multiple commits.
         */
+       /* (non-Javadoc)
+        * @see com.gitblit.wicket.Webapp#getBootDate()
+        */
+       @Override
        public Date getBootDate() {
                return runtimeManager.getBootDate();
        }
 
+       /* (non-Javadoc)
+        * @see com.gitblit.wicket.Webapp#getLastActivityDate()
+        */
+       @Override
        public Date getLastActivityDate() {
                return repositoryManager.getLastActivityDate();
        }
 
+       /* (non-Javadoc)
+        * @see com.gitblit.wicket.Webapp#runtime()
+        */
+       @Override
        public IRuntimeManager runtime() {
                return runtimeManager;
        }
 
+       /* (non-Javadoc)
+        * @see com.gitblit.wicket.Webapp#plugins()
+        */
+       @Override
        public IPluginManager plugins() {
                return pluginManager;
        }
 
+       /* (non-Javadoc)
+        * @see com.gitblit.wicket.Webapp#notifier()
+        */
+       @Override
        public INotificationManager notifier() {
                return notificationManager;
        }
 
+       /* (non-Javadoc)
+        * @see com.gitblit.wicket.Webapp#users()
+        */
+       @Override
        public IUserManager users() {
                return userManager;
        }
 
+       /* (non-Javadoc)
+        * @see com.gitblit.wicket.Webapp#authentication()
+        */
+       @Override
        public IAuthenticationManager authentication() {
                return authenticationManager;
        }
 
+       /* (non-Javadoc)
+        * @see com.gitblit.wicket.Webapp#keys()
+        */
+       @Override
        public IPublicKeyManager keys() {
                return publicKeyManager;
        }
 
+       /* (non-Javadoc)
+        * @see com.gitblit.wicket.Webapp#repositories()
+        */
+       @Override
        public IRepositoryManager repositories() {
                return repositoryManager;
        }
 
+       /* (non-Javadoc)
+        * @see com.gitblit.wicket.Webapp#projects()
+        */
+       @Override
        public IProjectManager projects() {
                return projectManager;
        }
 
+       /* (non-Javadoc)
+        * @see com.gitblit.wicket.Webapp#federation()
+        */
+       @Override
        public IFederationManager federation() {
                return federationManager;
        }
 
+       /* (non-Javadoc)
+        * @see com.gitblit.wicket.Webapp#gitblit()
+        */
+       @Override
        public IGitblit gitblit() {
                return gitblit;
        }
 
+       /* (non-Javadoc)
+        * @see com.gitblit.wicket.Webapp#tickets()
+        */
+       @Override
        public ITicketService tickets() {
                return gitblit.getTicketService();
        }
 
+       /* (non-Javadoc)
+        * @see com.gitblit.wicket.Webapp#getTimezone()
+        */
+       @Override
        public TimeZone getTimezone() {
                return runtimeManager.getTimezone();
        }
diff --git a/src/main/java/com/gitblit/wicket/GitblitWicketApp.java b/src/main/java/com/gitblit/wicket/GitblitWicketApp.java
new file mode 100644 (file)
index 0000000..a56e699
--- /dev/null
@@ -0,0 +1,72 @@
+package com.gitblit.wicket;
+
+import java.util.Date;
+import java.util.TimeZone;
+
+import org.apache.wicket.markup.html.WebPage;
+
+import com.gitblit.IStoredSettings;
+import com.gitblit.manager.IAuthenticationManager;
+import com.gitblit.manager.IFederationManager;
+import com.gitblit.manager.IGitblit;
+import com.gitblit.manager.INotificationManager;
+import com.gitblit.manager.IPluginManager;
+import com.gitblit.manager.IProjectManager;
+import com.gitblit.manager.IRepositoryManager;
+import com.gitblit.manager.IRuntimeManager;
+import com.gitblit.manager.IUserManager;
+import com.gitblit.tickets.ITicketService;
+import com.gitblit.transport.ssh.IPublicKeyManager;
+
+public interface GitblitWicketApp {
+
+       public abstract void mount(String location, Class<? extends WebPage> clazz, String... parameters);
+
+       public abstract Class<? extends WebPage> getHomePage();
+
+       public abstract boolean isCacheablePage(String mountPoint);
+
+       public abstract CacheControl getCacheControl(String mountPoint);
+
+       public abstract IStoredSettings settings();
+
+       /**
+        * Is Gitblit running in debug mode?
+        *
+        * @return true if Gitblit is running in debug mode
+        */
+       public abstract boolean isDebugMode();
+
+       /*
+        * These methods look strange... and they are... but they are the first
+        * step towards modularization across multiple commits.
+        */
+       public abstract Date getBootDate();
+
+       public abstract Date getLastActivityDate();
+
+       public abstract IRuntimeManager runtime();
+
+       public abstract IPluginManager plugins();
+
+       public abstract INotificationManager notifier();
+
+       public abstract IUserManager users();
+
+       public abstract IAuthenticationManager authentication();
+
+       public abstract IPublicKeyManager keys();
+
+       public abstract IRepositoryManager repositories();
+
+       public abstract IProjectManager projects();
+
+       public abstract IFederationManager federation();
+
+       public abstract IGitblit gitblit();
+
+       public abstract ITicketService tickets();
+
+       public abstract TimeZone getTimezone();
+
+}
\ No newline at end of file
diff --git a/src/main/java/com/gitblit/wicket/PageRegistration.java b/src/main/java/com/gitblit/wicket/PageRegistration.java
deleted file mode 100644 (file)
index 9fd8f87..0000000
+++ /dev/null
@@ -1,99 +0,0 @@
-/*\r
- * Copyright 2011 gitblit.com.\r
- *\r
- * Licensed under the Apache License, Version 2.0 (the "License");\r
- * you may not use this file except in compliance with the License.\r
- * You may obtain a copy of the License at\r
- *\r
- *     http://www.apache.org/licenses/LICENSE-2.0\r
- *\r
- * Unless required by applicable law or agreed to in writing, software\r
- * distributed under the License is distributed on an "AS IS" BASIS,\r
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
- * See the License for the specific language governing permissions and\r
- * limitations under the License.\r
- */\r
-package com.gitblit.wicket;\r
-\r
-import java.io.Serializable;\r
-import java.util.ArrayList;\r
-import java.util.List;\r
-\r
-import org.apache.wicket.PageParameters;\r
-import org.apache.wicket.markup.html.WebPage;\r
-\r
-import com.gitblit.models.Menu.MenuItem;\r
-\r
-/**\r
- * Represents a page link registration for the topbar.\r
- *\r
- * @author James Moger\r
- *\r
- */\r
-public class PageRegistration implements Serializable {\r
-       private static final long serialVersionUID = 1L;\r
-\r
-       public final String translationKey;\r
-       public final Class<? extends WebPage> pageClass;\r
-       public final PageParameters params;\r
-       public final boolean hiddenPhone;\r
-\r
-       public PageRegistration(String translationKey, Class<? extends WebPage> pageClass) {\r
-               this(translationKey, pageClass, null);\r
-       }\r
-\r
-       public PageRegistration(String translationKey, Class<? extends WebPage> pageClass,\r
-                       PageParameters params) {\r
-               this(translationKey, pageClass, params, false);\r
-       }\r
-\r
-       public PageRegistration(String translationKey, Class<? extends WebPage> pageClass,\r
-                       PageParameters params, boolean hiddenPhone) {\r
-               this.translationKey = translationKey;\r
-               this.pageClass = pageClass;\r
-               this.params = params;\r
-               this.hiddenPhone = hiddenPhone;\r
-       }\r
-\r
-       /**\r
-        * Represents a page link to a non-Wicket page. Might be external.\r
-        *\r
-        * @author James Moger\r
-        *\r
-        */\r
-       public static class OtherPageLink extends PageRegistration {\r
-\r
-               private static final long serialVersionUID = 1L;\r
-\r
-               public final String url;\r
-\r
-               public OtherPageLink(String keyOrText, String url) {\r
-                       super(keyOrText, null);\r
-                       this.url = url;\r
-               }\r
-\r
-               public OtherPageLink(String keyOrText, String url, boolean hiddenPhone) {\r
-                       super(keyOrText, null, null, hiddenPhone);\r
-                       this.url = url;\r
-               }\r
-       }\r
-\r
-       /**\r
-        * Represents a DropDownMenu for the topbar\r
-        *\r
-        * @author James Moger\r
-        *\r
-        */\r
-       public static class DropDownMenuRegistration extends PageRegistration {\r
-\r
-               private static final long serialVersionUID = 1L;\r
-\r
-               public final List<MenuItem> menuItems;\r
-\r
-               public DropDownMenuRegistration(String keyOrText, Class<? extends WebPage> pageClass) {\r
-                       super(keyOrText, pageClass);\r
-                       menuItems = new ArrayList<MenuItem>();\r
-               }\r
-       }\r
-\r
-}
\ No newline at end of file
diff --git a/src/main/java/com/gitblit/wicket/PluginClassResolver.java b/src/main/java/com/gitblit/wicket/PluginClassResolver.java
new file mode 100644 (file)
index 0000000..ba53b04
--- /dev/null
@@ -0,0 +1,122 @@
+/*
+ * Copyright 2014 gitblit.com.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * 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 com.gitblit.wicket;
+
+import java.io.IOException;
+import java.net.URL;
+import java.util.Enumeration;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Set;
+import java.util.TreeSet;
+
+import org.apache.wicket.Application;
+import org.apache.wicket.WicketRuntimeException;
+import org.apache.wicket.application.IClassResolver;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import ro.fortsoft.pf4j.PluginState;
+import ro.fortsoft.pf4j.PluginWrapper;
+
+import com.gitblit.manager.IPluginManager;
+
+/**
+ * Resolves plugin classes and resources.
+ */
+public class PluginClassResolver implements IClassResolver {
+       private static final Logger logger = LoggerFactory.getLogger(PluginClassResolver.class);
+
+       private final IPluginManager pluginManager;
+
+       public PluginClassResolver(IPluginManager pluginManager) {
+               this.pluginManager = pluginManager;
+       }
+
+       @Override
+       public Class<?> resolveClass(final String className) throws ClassNotFoundException {
+               boolean debugEnabled = logger.isDebugEnabled();
+
+               for (PluginWrapper plugin : pluginManager.getPlugins()) {
+                       if (PluginState.STARTED != plugin.getPluginState()) {
+                               // ignore this plugin
+                               continue;
+                       }
+
+                       try {
+                               return plugin.getPluginClassLoader().loadClass(className);
+                       } catch (ClassNotFoundException cnfx) {
+                               if (debugEnabled) {
+                                       logger.debug("ClassResolver '{}' cannot find class: '{}'", plugin.getPluginId(), className);
+                               }
+                       }
+               }
+
+               throw new ClassNotFoundException(className);
+       }
+
+       @Override
+       public Iterator<URL> getResources(final String name) {
+               Set<URL> urls = new TreeSet<URL>(new UrlExternalFormComparator());
+
+               for (PluginWrapper plugin : pluginManager.getPlugins()) {
+                       if (PluginState.STARTED != plugin.getPluginState()) {
+                               // ignore this plugin
+                               continue;
+                       }
+
+                       Iterator<URL> it = getResources(name, plugin);
+                       while (it.hasNext()) {
+                               URL url = it.next();
+                               urls.add(url);
+                       }
+               }
+
+               return urls.iterator();
+       }
+
+       protected Iterator<URL> getResources(String name, PluginWrapper plugin) {
+               HashSet<URL> loadedFiles = new HashSet<URL>();
+               try {
+                       // Try the classloader for the wicket jar/bundle
+                       Enumeration<URL> resources = plugin.getPluginClassLoader().getResources(name);
+                       loadResources(resources, loadedFiles);
+
+                       // Try the classloader for the user's application jar/bundle
+                       resources = Application.get().getClass().getClassLoader().getResources(name);
+                       loadResources(resources, loadedFiles);
+
+                       // Try the context class loader
+                       resources = Thread.currentThread().getContextClassLoader().getResources(name);
+                       loadResources(resources, loadedFiles);
+               } catch (IOException e) {
+                       throw new WicketRuntimeException(e);
+               }
+
+               return loadedFiles.iterator();
+       }
+
+       private void loadResources(Enumeration<URL> resources, Set<URL> loadedFiles) {
+               if (resources != null) {
+                       while (resources.hasMoreElements()) {
+                               final URL url = resources.nextElement();
+                               if (!loadedFiles.contains(url)) {
+                                       loadedFiles.add(url);
+                               }
+                       }
+               }
+       }
+}
\ No newline at end of file
diff --git a/src/main/java/com/gitblit/wicket/UrlExternalFormComparator.java b/src/main/java/com/gitblit/wicket/UrlExternalFormComparator.java
new file mode 100644 (file)
index 0000000..90f4b32
--- /dev/null
@@ -0,0 +1,39 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * 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 com.gitblit.wicket;
+
+import java.net.URL;
+import java.util.Comparator;
+
+/**
+ * A comparator of URL instances.
+ *
+ * Comparing URLs with their implementation of #equals() is
+ * bad because it may cause problems like DNS resolving, or other
+ * slow checks. This comparator uses the external form of an URL
+ * to make a simple comparison of two Strings.
+ *
+ * @since 1.5.6
+ */
+public class UrlExternalFormComparator implements Comparator<URL>
+{
+       @Override
+       public int compare(URL url1, URL url2)
+       {
+               return url1.toExternalForm().compareTo(url2.toExternalForm());
+       }
+}
\ No newline at end of file
index 0870ff962fd57945ac00c10c6f4ad9f89a09db81..c505a6664f9e18d02497be6b19dfda61c3d66f70 100644 (file)
@@ -32,14 +32,14 @@ import org.apache.wicket.markup.html.panel.Fragment;
 import com.gitblit.Keys;\r
 import com.gitblit.models.Activity;\r
 import com.gitblit.models.Menu.ParameterMenuItem;\r
+import com.gitblit.models.NavLink.DropDownPageMenuNavLink;\r
 import com.gitblit.models.Metric;\r
+import com.gitblit.models.NavLink;\r
 import com.gitblit.models.RepositoryModel;\r
 import com.gitblit.utils.ActivityUtils;\r
 import com.gitblit.utils.StringUtils;\r
 import com.gitblit.wicket.CacheControl;\r
 import com.gitblit.wicket.CacheControl.LastModified;\r
-import com.gitblit.wicket.PageRegistration;\r
-import com.gitblit.wicket.PageRegistration.DropDownMenuRegistration;\r
 import com.gitblit.wicket.WicketUtils;\r
 import com.gitblit.wicket.charting.Chart;\r
 import com.gitblit.wicket.charting.Charts;\r
@@ -135,8 +135,8 @@ public class ActivityPage extends RootPage {
        }\r
 \r
        @Override\r
-       protected void addDropDownMenus(List<PageRegistration> pages) {\r
-               DropDownMenuRegistration filters = new DropDownMenuRegistration("gb.filters",\r
+       protected void addDropDownMenus(List<NavLink> navLinks) {\r
+               DropDownPageMenuNavLink filters = new DropDownPageMenuNavLink("gb.filters",\r
                                ActivityPage.class);\r
 \r
                PageParameters currentParameters = getPageParameters();\r
@@ -155,7 +155,7 @@ public class ActivityPage extends RootPage {
                        // Reset Filter\r
                        filters.menuItems.add(new ParameterMenuItem(getString("gb.reset")));\r
                }\r
-               pages.add(filters);\r
+               navLinks.add(filters);\r
        }\r
 \r
        /**\r
index 16b0b73400dec8ae5303716817b466ac8d102b49..9c10e01b2f34076b50d8010bea6f5a1ae641fd1d 100644 (file)
@@ -37,7 +37,9 @@ import org.eclipse.jgit.lib.Repository;
 import com.gitblit.Keys;\r
 import com.gitblit.models.DailyLogEntry;\r
 import com.gitblit.models.Menu.ParameterMenuItem;\r
+import com.gitblit.models.NavLink.DropDownPageMenuNavLink;\r
 import com.gitblit.models.Metric;\r
+import com.gitblit.models.NavLink;\r
 import com.gitblit.models.RefLogEntry;\r
 import com.gitblit.models.RepositoryCommit;\r
 import com.gitblit.models.RepositoryModel;\r
@@ -46,8 +48,6 @@ import com.gitblit.utils.ArrayUtils;
 import com.gitblit.utils.RefLogUtils;\r
 import com.gitblit.utils.StringUtils;\r
 import com.gitblit.wicket.GitBlitWebApp;\r
-import com.gitblit.wicket.PageRegistration;\r
-import com.gitblit.wicket.PageRegistration.DropDownMenuRegistration;\r
 import com.gitblit.wicket.charting.Chart;\r
 import com.gitblit.wicket.charting.Charts;\r
 import com.gitblit.wicket.charting.Flotr2Charts;\r
@@ -141,10 +141,10 @@ public abstract class DashboardPage extends RootPage {
        }\r
 \r
        @Override\r
-       protected void addDropDownMenus(List<PageRegistration> pages) {\r
+       protected void addDropDownMenus(List<NavLink> navLinks) {\r
                PageParameters params = getPageParameters();\r
 \r
-               DropDownMenuRegistration menu = new DropDownMenuRegistration("gb.filters",\r
+               DropDownPageMenuNavLink menu = new DropDownPageMenuNavLink("gb.filters",\r
                                GitBlitWebApp.get().getHomePage());\r
 \r
                // preserve repository filter option on time choices\r
@@ -155,7 +155,7 @@ public abstract class DashboardPage extends RootPage {
                        menu.menuItems.add(new ParameterMenuItem(getString("gb.reset")));\r
                }\r
 \r
-               pages.add(menu);\r
+               navLinks.add(menu);\r
        }\r
 \r
 \r
index 6c8aa4f4a8e55aed7779deaa252ba084af4b21ba..d358b775db80307bf53d0fb04661668db00c85eb 100644 (file)
@@ -29,6 +29,8 @@ import com.gitblit.Keys;
 import com.gitblit.models.Menu.MenuDivider;\r
 import com.gitblit.models.Menu.MenuItem;\r
 import com.gitblit.models.Menu.ParameterMenuItem;\r
+import com.gitblit.models.NavLink.DropDownPageMenuNavLink;\r
+import com.gitblit.models.NavLink;\r
 import com.gitblit.models.ProjectModel;\r
 import com.gitblit.models.RepositoryModel;\r
 import com.gitblit.models.UserModel;\r
@@ -40,8 +42,6 @@ import com.gitblit.wicket.CacheControl.LastModified;
 import com.gitblit.wicket.GitBlitWebApp;\r
 import com.gitblit.wicket.GitBlitWebSession;\r
 import com.gitblit.wicket.GitblitRedirectException;\r
-import com.gitblit.wicket.PageRegistration;\r
-import com.gitblit.wicket.PageRegistration.DropDownMenuRegistration;\r
 import com.gitblit.wicket.WicketUtils;\r
 import com.gitblit.wicket.panels.FilterableRepositoryList;\r
 \r
@@ -161,10 +161,10 @@ public class ProjectPage extends DashboardPage {
        }\r
 \r
        @Override\r
-       protected void addDropDownMenus(List<PageRegistration> pages) {\r
+       protected void addDropDownMenus(List<NavLink> navLinks) {\r
                PageParameters params = getPageParameters();\r
 \r
-               DropDownMenuRegistration menu = new DropDownMenuRegistration("gb.filters",\r
+               DropDownPageMenuNavLink menu = new DropDownPageMenuNavLink("gb.filters",\r
                                ProjectPage.class);\r
                // preserve time filter option on repository choices\r
                menu.menuItems.addAll(getRepositoryFilterItems(params));\r
@@ -177,12 +177,12 @@ public class ProjectPage extends DashboardPage {
                        menu.menuItems.add(new ParameterMenuItem(getString("gb.reset"), "p", WicketUtils.getProjectName(params)));\r
                }\r
 \r
-               pages.add(menu);\r
+               navLinks.add(menu);\r
 \r
-               DropDownMenuRegistration projects = new DropDownMenuRegistration("gb.projects",\r
+               DropDownPageMenuNavLink projects = new DropDownPageMenuNavLink("gb.projects",\r
                                ProjectPage.class);\r
                projects.menuItems.addAll(getProjectsMenu());\r
-               pages.add(projects);\r
+               navLinks.add(projects);\r
        }\r
 \r
        @Override\r
index c404ae6146d28318c16b65b70ac852d0d5a4c601..f04fa78a48170cc057f4d686cd4d77257faa7654 100644 (file)
@@ -25,10 +25,10 @@ import org.apache.wicket.markup.repeater.data.ListDataProvider;
 \r
 import com.gitblit.Keys;\r
 import com.gitblit.models.Menu.ParameterMenuItem;\r
+import com.gitblit.models.NavLink.DropDownPageMenuNavLink;\r
+import com.gitblit.models.NavLink;\r
 import com.gitblit.models.ProjectModel;\r
 import com.gitblit.wicket.GitBlitWebSession;\r
-import com.gitblit.wicket.PageRegistration;\r
-import com.gitblit.wicket.PageRegistration.DropDownMenuRegistration;\r
 import com.gitblit.wicket.WicketUtils;\r
 import com.gitblit.wicket.panels.LinkPanel;\r
 \r
@@ -115,10 +115,10 @@ public class ProjectsPage extends RootPage {
        }\r
 \r
        @Override\r
-       protected void addDropDownMenus(List<PageRegistration> pages) {\r
+       protected void addDropDownMenus(List<NavLink> navLinks) {\r
                PageParameters params = getPageParameters();\r
 \r
-               DropDownMenuRegistration menu = new DropDownMenuRegistration("gb.filters",\r
+               DropDownPageMenuNavLink menu = new DropDownPageMenuNavLink("gb.filters",\r
                                ProjectsPage.class);\r
                // preserve time filter option on repository choices\r
                menu.menuItems.addAll(getRepositoryFilterItems(params));\r
@@ -131,6 +131,6 @@ public class ProjectsPage extends RootPage {
                        menu.menuItems.add(new ParameterMenuItem(getString("gb.reset")));\r
                }\r
 \r
-               pages.add(menu);\r
+               navLinks.add(menu);\r
        }\r
 }\r
index 41fe057c0638113833c92c780d9f5d3b29569682..a0b15a8349cccd0617ce474c8dd8b4a7aa6a210f 100644 (file)
@@ -30,14 +30,14 @@ import org.eclipse.jgit.lib.Constants;
 \r
 import com.gitblit.Keys;\r
 import com.gitblit.models.Menu.ParameterMenuItem;\r
+import com.gitblit.models.NavLink.DropDownPageMenuNavLink;\r
+import com.gitblit.models.NavLink;\r
 import com.gitblit.models.RepositoryModel;\r
 import com.gitblit.utils.MarkdownUtils;\r
 import com.gitblit.utils.StringUtils;\r
 import com.gitblit.wicket.CacheControl;\r
 import com.gitblit.wicket.CacheControl.LastModified;\r
 import com.gitblit.wicket.GitBlitWebSession;\r
-import com.gitblit.wicket.PageRegistration;\r
-import com.gitblit.wicket.PageRegistration.DropDownMenuRegistration;\r
 import com.gitblit.wicket.WicketUtils;\r
 import com.gitblit.wicket.panels.RepositoriesPanel;\r
 \r
@@ -92,10 +92,10 @@ public class RepositoriesPage extends RootPage {
        }\r
 \r
        @Override\r
-       protected void addDropDownMenus(List<PageRegistration> pages) {\r
+       protected void addDropDownMenus(List<NavLink> navLinks) {\r
                PageParameters params = getPageParameters();\r
 \r
-               DropDownMenuRegistration menu = new DropDownMenuRegistration("gb.filters",\r
+               DropDownPageMenuNavLink menu = new DropDownPageMenuNavLink("gb.filters",\r
                                RepositoriesPage.class);\r
                // preserve time filter option on repository choices\r
                menu.menuItems.addAll(getRepositoryFilterItems(params));\r
@@ -108,7 +108,7 @@ public class RepositoriesPage extends RootPage {
                        menu.menuItems.add(new ParameterMenuItem(getString("gb.reset")));\r
                }\r
 \r
-               pages.add(menu);\r
+               navLinks.add(menu);\r
        }\r
 \r
        private String readMarkdown(String messageSource, String resource) {\r
index 5ea99fd8f9d2b61781621af2d86b7f2ec3f32027..165feedf8c73c20f5c2764b6186de63d65f2fd88 100644 (file)
@@ -21,7 +21,6 @@ import java.util.ArrayList;
 import java.util.Arrays;\r
 import java.util.Date;\r
 import java.util.HashMap;\r
-import java.util.LinkedHashMap;\r
 import java.util.LinkedHashSet;\r
 import java.util.List;\r
 import java.util.Map;\r
@@ -49,6 +48,10 @@ import org.slf4j.LoggerFactory;
 import com.gitblit.Constants;\r
 import com.gitblit.GitBlitException;\r
 import com.gitblit.Keys;\r
+import com.gitblit.extensions.RepositoryNavLinkExtension;\r
+import com.gitblit.models.NavLink;\r
+import com.gitblit.models.NavLink.ExternalNavLink;\r
+import com.gitblit.models.NavLink.PageNavLink;\r
 import com.gitblit.models.ProjectModel;\r
 import com.gitblit.models.RefModel;\r
 import com.gitblit.models.RepositoryModel;\r
@@ -66,8 +69,6 @@ import com.gitblit.utils.RefLogUtils;
 import com.gitblit.utils.StringUtils;\r
 import com.gitblit.wicket.CacheControl;\r
 import com.gitblit.wicket.GitBlitWebSession;\r
-import com.gitblit.wicket.PageRegistration;\r
-import com.gitblit.wicket.PageRegistration.OtherPageLink;\r
 import com.gitblit.wicket.SessionlessForm;\r
 import com.gitblit.wicket.TicketsUI;\r
 import com.gitblit.wicket.WicketUtils;\r
@@ -91,7 +92,6 @@ public abstract class RepositoryPage extends RootPage {
 \r
        private Map<String, SubmoduleModel> submodules;\r
 \r
-       private final Map<String, PageRegistration> registeredPages;\r
        private boolean showAdmin;\r
        private boolean isOwner;\r
 \r
@@ -150,12 +150,11 @@ public abstract class RepositoryPage extends RootPage {
                        }\r
                }\r
 \r
-               // register the available page links for this page and user\r
-               registeredPages = registerPages();\r
+               // register the available navigation links for this page and user\r
+               List<NavLink> navLinks = registerNavLinks();\r
 \r
-               // standard page links\r
-               List<PageRegistration> pages = new ArrayList<PageRegistration>(registeredPages.values());\r
-               NavigationPanel navigationPanel = new NavigationPanel("repositoryNavPanel", getRepoNavPageClass(), pages);\r
+               // standard navigation links\r
+               NavigationPanel navigationPanel = new NavigationPanel("repositoryNavPanel", getRepoNavPageClass(), navLinks);\r
                add(navigationPanel);\r
 \r
                add(new ExternalLink("syndication", SyndicationServlet.asLink(getRequest()\r
@@ -183,45 +182,56 @@ public abstract class RepositoryPage extends RootPage {
                return new BugtraqProcessor(app().settings());\r
        }\r
 \r
-       private Map<String, PageRegistration> registerPages() {\r
+       private List<NavLink> registerNavLinks() {\r
                PageParameters params = null;\r
                if (!StringUtils.isEmpty(repositoryName)) {\r
                        params = WicketUtils.newRepositoryParameter(repositoryName);\r
                }\r
-               Map<String, PageRegistration> pages = new LinkedHashMap<String, PageRegistration>();\r
+               List<NavLink> navLinks = new ArrayList<NavLink>();\r
 \r
                Repository r = getRepository();\r
                RepositoryModel model = getRepositoryModel();\r
 \r
                // standard links\r
                if (RefLogUtils.getRefLogBranch(r) == null) {\r
-                       pages.put("summary", new PageRegistration("gb.summary", SummaryPage.class, params));\r
+                       navLinks.add(new PageNavLink("gb.summary", SummaryPage.class, params));\r
                } else {\r
-                       pages.put("summary", new PageRegistration("gb.summary", SummaryPage.class, params));\r
+                       navLinks.add(new PageNavLink("gb.summary", SummaryPage.class, params));\r
 //                     pages.put("overview", new PageRegistration("gb.overview", OverviewPage.class, params));\r
-                       pages.put("reflog", new PageRegistration("gb.reflog", ReflogPage.class, params));\r
+                       navLinks.add(new PageNavLink("gb.reflog", ReflogPage.class, params));\r
                }\r
-               pages.put("commits", new PageRegistration("gb.commits", LogPage.class, params));\r
-               pages.put("tree", new PageRegistration("gb.tree", TreePage.class, params));\r
+               navLinks.add(new PageNavLink("gb.commits", LogPage.class, params));\r
+               navLinks.add(new PageNavLink("gb.tree", TreePage.class, params));\r
                if (app().tickets().isReady() && (app().tickets().isAcceptingNewTickets(getRepositoryModel()) || app().tickets().hasTickets(getRepositoryModel()))) {\r
                        PageParameters tParams = new PageParameters(params);\r
                        for (String state : TicketsUI.openStatii) {\r
                                tParams.add(Lucene.status.name(), state);\r
                        }\r
-                       pages.put("tickets", new PageRegistration("gb.tickets", TicketsPage.class, tParams));
+                       navLinks.add(new PageNavLink("gb.tickets", TicketsPage.class, tParams));
                }
-               pages.put("docs", new PageRegistration("gb.docs", DocsPage.class, params, true));\r
+               navLinks.add(new PageNavLink("gb.docs", DocsPage.class, params, true));\r
                if (app().settings().getBoolean(Keys.web.allowForking, true)) {\r
-                       pages.put("forks", new PageRegistration("gb.forks", ForksPage.class, params, true));\r
+                       navLinks.add(new PageNavLink("gb.forks", ForksPage.class, params, true));\r
                }\r
-               pages.put("compare", new PageRegistration("gb.compare", ComparePage.class, params, true));
+               navLinks.add(new PageNavLink("gb.compare", ComparePage.class, params, true));
 \r
                // conditional links\r
-               // per-repository extra page links\r
+               // per-repository extra navlinks\r
                if (JGitUtils.getPagesBranch(r) != null) {\r
-                       OtherPageLink pagesLink = new OtherPageLink("gb.pages", PagesServlet.asLink(\r
+                       ExternalNavLink pagesLink = new ExternalNavLink("gb.pages", PagesServlet.asLink(\r
                                        getRequest().getRelativePathPrefixToContextRoot(), repositoryName, null), true);\r
-                       pages.put("pages", pagesLink);\r
+                       navLinks.add(pagesLink);\r
+               }\r
+\r
+               UserModel user = UserModel.ANONYMOUS;\r
+               if (GitBlitWebSession.get().isLoggedIn()) {\r
+                       user = GitBlitWebSession.get().getUser();\r
+               }\r
+\r
+               // add repository nav link extensions\r
+               List<RepositoryNavLinkExtension> extensions = app().plugins().getExtensions(RepositoryNavLinkExtension.class);\r
+               for (RepositoryNavLinkExtension ext : extensions) {\r
+                       navLinks.addAll(ext.getNavLinks(user, model));\r
                }\r
 \r
                // Conditionally add edit link\r
@@ -233,9 +243,8 @@ public abstract class RepositoryPage extends RootPage {
                        showAdmin = app().settings().getBoolean(Keys.web.allowAdministration, false);\r
                }\r
                isOwner = GitBlitWebSession.get().isLoggedIn()\r
-                               && (model.isOwner(GitBlitWebSession.get()\r
-                                               .getUsername()));\r
-               return pages;\r
+                               && (model.isOwner(GitBlitWebSession.get().getUsername()));\r
+               return navLinks;\r
        }\r
 \r
        protected boolean allowForkControls() {\r
index 3003c70ec013bf8c50ca799c420118056e19b674..a2f3a497ffe5b73b30199a84f7c0d44a396367a4 100644 (file)
@@ -50,6 +50,7 @@ import org.apache.wicket.protocol.http.WebResponse;
 
 import com.gitblit.Constants;
 import com.gitblit.Keys;
+import com.gitblit.extensions.NavLinkExtension;
 import com.gitblit.extensions.UserMenuExtension;
 import com.gitblit.models.Menu.ExternalLinkMenuItem;
 import com.gitblit.models.Menu.MenuDivider;
@@ -57,13 +58,14 @@ import com.gitblit.models.Menu.MenuItem;
 import com.gitblit.models.Menu.PageLinkMenuItem;
 import com.gitblit.models.Menu.ParameterMenuItem;
 import com.gitblit.models.Menu.ToggleMenuItem;
+import com.gitblit.models.NavLink;
+import com.gitblit.models.NavLink.PageNavLink;
 import com.gitblit.models.RepositoryModel;
 import com.gitblit.models.TeamModel;
 import com.gitblit.models.UserModel;
 import com.gitblit.utils.ModelUtils;
 import com.gitblit.utils.StringUtils;
 import com.gitblit.wicket.GitBlitWebSession;
-import com.gitblit.wicket.PageRegistration;
 import com.gitblit.wicket.SessionlessForm;
 import com.gitblit.wicket.WicketUtils;
 import com.gitblit.wicket.panels.GravatarImage;
@@ -174,50 +176,37 @@ public abstract class RootPage extends BasePage {
                }
 
                // navigation links
-               List<PageRegistration> pages = new ArrayList<PageRegistration>();
+               List<NavLink> navLinks = new ArrayList<NavLink>();
                if (!authenticateView || (authenticateView && isLoggedIn)) {
-                       pages.add(new PageRegistration(isLoggedIn ? "gb.myDashboard" : "gb.dashboard", MyDashboardPage.class,
+                       navLinks.add(new PageNavLink(isLoggedIn ? "gb.myDashboard" : "gb.dashboard", MyDashboardPage.class,
                                        getRootPageParameters()));
                        if (isLoggedIn && app().tickets().isReady()) {
-                               pages.add(new PageRegistration("gb.myTickets", MyTicketsPage.class));
+                               navLinks.add(new PageNavLink("gb.myTickets", MyTicketsPage.class));
                        }
-                       pages.add(new PageRegistration("gb.repositories", RepositoriesPage.class,
+                       navLinks.add(new PageNavLink("gb.repositories", RepositoriesPage.class,
                                        getRootPageParameters()));
-                       pages.add(new PageRegistration("gb.activity", ActivityPage.class, getRootPageParameters()));
+                       navLinks.add(new PageNavLink("gb.activity", ActivityPage.class, getRootPageParameters()));
                        if (allowLucene) {
-                               pages.add(new PageRegistration("gb.search", LuceneSearchPage.class));
+                               navLinks.add(new PageNavLink("gb.search", LuceneSearchPage.class));
                        }
 
-                       UserModel user = GitBlitWebSession.get().getUser();
-
-                       if (showAdmin) {
-                               // admin dropdown menu
-                               DropDownMenuRegistration adminMenu = new DropDownMenuRegistration("gb.adminMenuItem", MyDashboardPage.class);
-
-                               adminMenu.menuItems.add(new PageLinkMenuItem(getString("gb.users"), UsersPage.class));
-
-                               boolean showRegistrations = app().federation().canFederate()
-                                               && app().settings().getBoolean(Keys.web.showFederationRegistrations, false);
-                               if (showRegistrations) {
-                                       adminMenu.menuItems.add(new PageLinkMenuItem(getString("gb.federation"), FederationPage.class));
-                               }
-
-                               // allow plugins to contribute admin menu items
-                               List<AdminMenuExtension> extensions = app().plugins().getExtensions(AdminMenuExtension.class);
-                               for (AdminMenuExtension ext : extensions) {
-                                       adminMenu.menuItems.add(new MenuDivider());
-                                       adminMenu.menuItems.addAll(ext.getMenuItems(user));
-                               }
+                       if (!authenticateView || (authenticateView && isLoggedIn)) {
+                               addDropDownMenus(navLinks);
+                       }
 
-                               pages.add(adminMenu);
+                       UserModel user = UserModel.ANONYMOUS;
+                       if (isLoggedIn) {
+                               user = GitBlitWebSession.get().getUser();
                        }
 
-                       if (!authenticateView || (authenticateView && isLoggedIn)) {
-                               addDropDownMenus(pages);
+                       // add nav link extensions
+                       List<NavLinkExtension> extensions = app().plugins().getExtensions(NavLinkExtension.class);
+                       for (NavLinkExtension ext : extensions) {
+                               navLinks.addAll(ext.getNavLinks(user));
                        }
                }
 
-               NavigationPanel navPanel = new NavigationPanel("navPanel", getRootNavPageClass(), pages);
+               NavigationPanel navPanel = new NavigationPanel("navPanel", getRootNavPageClass(), navLinks);
                add(navPanel);
 
                // display an error message cached from a redirect
@@ -309,7 +298,7 @@ public abstract class RootPage extends BasePage {
                return repositoryModels;
        }
 
-       protected void addDropDownMenus(List<PageRegistration> pages) {
+       protected void addDropDownMenus(List<NavLink> navLinks) {
 
        }
 
index 0767621c8cb0108b67665c107504a3cfeeabcf0e..6cb791eb99c841badd7ac025b63660383c3c2adc 100644 (file)
@@ -30,6 +30,8 @@ import org.eclipse.jgit.lib.PersonIdent;
 \r
 import com.gitblit.Keys;\r
 import com.gitblit.models.Menu.ParameterMenuItem;\r
+import com.gitblit.models.NavLink.DropDownPageMenuNavLink;\r
+import com.gitblit.models.NavLink;\r
 import com.gitblit.models.ProjectModel;\r
 import com.gitblit.models.RepositoryModel;\r
 import com.gitblit.models.UserModel;\r
@@ -37,8 +39,6 @@ import com.gitblit.utils.StringUtils;
 import com.gitblit.wicket.GitBlitWebApp;\r
 import com.gitblit.wicket.GitBlitWebSession;\r
 import com.gitblit.wicket.GitblitRedirectException;\r
-import com.gitblit.wicket.PageRegistration;\r
-import com.gitblit.wicket.PageRegistration.DropDownMenuRegistration;\r
 import com.gitblit.wicket.WicketUtils;\r
 import com.gitblit.wicket.panels.GravatarImage;\r
 import com.gitblit.wicket.panels.LinkPanel;\r
@@ -127,10 +127,10 @@ public class UserPage extends RootPage {
        }\r
 \r
        @Override\r
-       protected void addDropDownMenus(List<PageRegistration> pages) {\r
+       protected void addDropDownMenus(List<NavLink> navLinks) {\r
                PageParameters params = getPageParameters();\r
 \r
-               DropDownMenuRegistration menu = new DropDownMenuRegistration("gb.filters",\r
+               DropDownPageMenuNavLink menu = new DropDownPageMenuNavLink("gb.filters",\r
                                UserPage.class);\r
                // preserve time filter option on repository choices\r
                menu.menuItems.addAll(getRepositoryFilterItems(params));\r
@@ -143,6 +143,6 @@ public class UserPage extends RootPage {
                        menu.menuItems.add(new ParameterMenuItem(getString("gb.reset")));\r
                }\r
 \r
-               pages.add(menu);\r
+               navLinks.add(menu);\r
        }\r
 }\r
index f561143d38733293f054a604b7af76bb223ed1f3..4e7ae54c2990391765fd2859544390e6c5d08af0 100644 (file)
@@ -21,24 +21,24 @@ import org.apache.wicket.markup.repeater.Item;
 import org.apache.wicket.markup.repeater.data.DataView;\r
 import org.apache.wicket.markup.repeater.data.ListDataProvider;\r
 \r
-import com.gitblit.models.Menu.MenuDivider;\r
 import com.gitblit.models.Menu.ExternalLinkMenuItem;\r
+import com.gitblit.models.Menu.MenuDivider;\r
 import com.gitblit.models.Menu.MenuItem;\r
 import com.gitblit.models.Menu.PageLinkMenuItem;\r
 import com.gitblit.models.Menu.ParameterMenuItem;\r
-import com.gitblit.wicket.PageRegistration.DropDownMenuRegistration;\r
+import com.gitblit.models.NavLink.DropDownMenuNavLink;\r
+import com.gitblit.models.NavLink.DropDownPageMenuNavLink;\r
 import com.gitblit.wicket.WicketUtils;\r
 \r
 public class DropDownMenu extends Panel {\r
 \r
        private static final long serialVersionUID = 1L;\r
 \r
-       public DropDownMenu(String id, String label, final DropDownMenuRegistration menu) {\r
+       public DropDownMenu(String id, String label, final DropDownPageMenuNavLink menu) {\r
                super(id);\r
 \r
                add(new Label("label", label).setRenderBodyOnly(true));\r
-               ListDataProvider<MenuItem> items = new ListDataProvider<MenuItem>(\r
-                               menu.menuItems);\r
+               ListDataProvider<MenuItem> items = new ListDataProvider<MenuItem>(menu.menuItems);\r
                DataView<MenuItem> view = new DataView<MenuItem>("menuItems", items) {\r
                        private static final long serialVersionUID = 1L;\r
 \r
@@ -76,4 +76,39 @@ public class DropDownMenu extends Panel {
                add(view);\r
                setRenderBodyOnly(true);\r
        }\r
+\r
+       public DropDownMenu(String id, String label, final DropDownMenuNavLink menu) {\r
+               super(id);\r
+\r
+               add(new Label("label", label).setRenderBodyOnly(true));\r
+               ListDataProvider<MenuItem> items = new ListDataProvider<MenuItem>(menu.menuItems);\r
+               DataView<MenuItem> view = new DataView<MenuItem>("menuItems", items) {\r
+                       private static final long serialVersionUID = 1L;\r
+\r
+                       @Override\r
+                       public void populateItem(final Item<MenuItem> item) {\r
+                               MenuItem entry = item.getModelObject();\r
+                               if (entry instanceof PageLinkMenuItem) {\r
+                                       // link to another Wicket page\r
+                                       PageLinkMenuItem pageLink = (PageLinkMenuItem) entry;\r
+                                       item.add(new LinkPanel("menuItem", null, null, pageLink.toString(), pageLink.getPageClass(),\r
+                                                       pageLink.getPageParameters(), false).setRenderBodyOnly(true));\r
+                               } else if (entry instanceof ExternalLinkMenuItem) {\r
+                                       // link to a specified href\r
+                                       ExternalLinkMenuItem extLink = (ExternalLinkMenuItem) entry;\r
+                                       item.add(new LinkPanel("menuItem", null, extLink.toString(), extLink.getHref(),\r
+                                                       extLink.openInNewWindow()).setRenderBodyOnly(true));\r
+                               } else if (entry instanceof MenuDivider) {\r
+                                       // divider\r
+                                       item.add(new Label("menuItem").setRenderBodyOnly(true));\r
+                                       WicketUtils.setCssClass(item, "divider");\r
+                               } else {\r
+                                       throw new IllegalArgumentException(String.format("Unexpected menuitem type %s",\r
+                                                       entry.getClass().getSimpleName()));\r
+                               }\r
+                       }\r
+               };\r
+               add(view);\r
+               setRenderBodyOnly(true);\r
+       }\r
 }
\ No newline at end of file
index 7db29fa209ff069a0221f1b2d359ddc6c791c526..2bc92f4c49749f97d1be0833f23fc61ec215a31b 100644 (file)
@@ -23,9 +23,11 @@ import org.apache.wicket.markup.repeater.Item;
 import org.apache.wicket.markup.repeater.data.DataView;\r
 import org.apache.wicket.markup.repeater.data.ListDataProvider;\r
 \r
-import com.gitblit.wicket.PageRegistration;\r
-import com.gitblit.wicket.PageRegistration.DropDownMenuRegistration;\r
-import com.gitblit.wicket.PageRegistration.OtherPageLink;\r
+import com.gitblit.models.NavLink;\r
+import com.gitblit.models.NavLink.DropDownMenuNavLink;\r
+import com.gitblit.models.NavLink.DropDownPageMenuNavLink;\r
+import com.gitblit.models.NavLink.ExternalNavLink;\r
+import com.gitblit.models.NavLink.PageNavLink;\r
 import com.gitblit.wicket.WicketUtils;\r
 import com.gitblit.wicket.pages.BasePage;\r
 \r
@@ -34,52 +36,59 @@ public class NavigationPanel extends Panel {
        private static final long serialVersionUID = 1L;\r
 \r
        public NavigationPanel(String id, final Class<? extends BasePage> pageClass,\r
-                       List<PageRegistration> registeredPages) {\r
+                       List<NavLink> navLinks) {\r
                super(id);\r
 \r
-               ListDataProvider<PageRegistration> refsDp = new ListDataProvider<PageRegistration>(\r
-                               registeredPages);\r
-               DataView<PageRegistration> refsView = new DataView<PageRegistration>("navLink", refsDp) {\r
+               ListDataProvider<NavLink> refsDp = new ListDataProvider<NavLink>(navLinks);\r
+               DataView<NavLink> linksView = new DataView<NavLink>("navLink", refsDp) {\r
                        private static final long serialVersionUID = 1L;\r
 \r
                        @Override\r
-                       public void populateItem(final Item<PageRegistration> item) {\r
-                               PageRegistration entry = item.getModelObject();\r
-                               String linkText = entry.translationKey;\r
+                       public void populateItem(final Item<NavLink> item) {\r
+                               NavLink navLink = item.getModelObject();\r
+                               String linkText = navLink.translationKey;\r
                                try {\r
                                        // try to lookup translation key\r
-                                       linkText = getString(entry.translationKey);\r
+                                       linkText = getString(navLink.translationKey);\r
                                } catch (Exception e) {\r
                                }\r
 \r
-                               if (entry.hiddenPhone) {\r
+                               if (navLink.hiddenPhone) {\r
                                        WicketUtils.setCssClass(item, "hidden-phone");\r
                                }\r
-                               if (entry instanceof OtherPageLink) {\r
+                               if (navLink instanceof ExternalNavLink) {\r
                                        // other link\r
-                                       OtherPageLink link = (OtherPageLink) entry;\r
+                                       ExternalNavLink link = (ExternalNavLink) navLink;\r
                                        Component c = new LinkPanel("link", null, linkText, link.url);\r
                                        c.setRenderBodyOnly(true);\r
                                        item.add(c);\r
-                               } else if (entry instanceof DropDownMenuRegistration) {\r
+                               } else if (navLink instanceof DropDownPageMenuNavLink) {\r
                                        // drop down menu\r
-                                       DropDownMenuRegistration reg = (DropDownMenuRegistration) entry;\r
+                                       DropDownPageMenuNavLink reg = (DropDownPageMenuNavLink) navLink;\r
                                        Component c = new DropDownMenu("link", linkText, reg);\r
                                        c.setRenderBodyOnly(true);\r
                                        item.add(c);\r
                                        WicketUtils.setCssClass(item, "dropdown");\r
-                               } else {\r
+                               } else if (navLink instanceof DropDownMenuNavLink) {\r
+                                       // drop down menu\r
+                                       DropDownMenuNavLink reg = (DropDownMenuNavLink) navLink;\r
+                                       Component c = new DropDownMenu("link", linkText, reg);\r
+                                       c.setRenderBodyOnly(true);\r
+                                       item.add(c);\r
+                                       WicketUtils.setCssClass(item, "dropdown");\r
+                               } else if (navLink instanceof PageNavLink) {\r
+                                       PageNavLink reg = (PageNavLink) navLink;\r
                                        // standard page link\r
                                        Component c = new LinkPanel("link", null, linkText,\r
-                                                       entry.pageClass, entry.params);\r
+                                                       reg.pageClass, reg.params);\r
                                        c.setRenderBodyOnly(true);\r
-                                       if (entry.pageClass.equals(pageClass)) {\r
+                                       if (reg.pageClass.equals(pageClass)) {\r
                                                WicketUtils.setCssClass(item, "active");\r
                                        }\r
                                        item.add(c);\r
                                }\r
                        }\r
                };\r
-               add(refsView);\r
+               add(linksView);\r
        }\r
 }
\ No newline at end of file
index 18a7e325fd4bc4b593f5becfe3a0b4b9ece38a3f..7bf63c1702a8855eaf99cb029c7988dad0a2190e 100644 (file)
@@ -52,6 +52,37 @@ public class ExamplePlugin extends GitblitPlugin {
     public void onUninstall() {
     }
 }
+
+/**
+ * You can also create Webapp plugins that register mounted pages.
+ */
+public class ExampleWicketPlugin extends GitblitWicketPlugin {
+    @Override
+    public void start() {
+    }
+
+    @Override
+    public void stop() {
+    }
+
+    @Override
+    public void onInstall() {
+    }
+
+    @Override
+    public void onUpgrade(Version oldVersion) {
+    }
+
+    @Override
+    public void onUninstall() {
+    }
+
+    @Override
+    protected void init(GitblitWicketApp app) {
+        app.mount("/logo", LogoPage.class);
+        app.mount("/hello", HelloWorldPage.class);
+    }
+}
 ```
 
 ### SSH Dispatch Command
@@ -225,7 +256,32 @@ public class MyUserMenuContributor extends UserMenuExtension {
 
     @Override
     public List<MenuItem> getMenuItems(UserModel user) {
-        return Arrays.asList((MenuItem) new ExternalLinkMenuItem("Github", String.format("https://github.com/%s", user.username));
+        MenuItem item = new ExternalLinkMenuItem("Github", String.format("https://github.com/%s", user.username));
+        return Arrays.asList(item);
+    }
+}
+```
+
+### Navigation Links
+
+*SINCE 1.6.0*
+
+You can provide your own top-level navigation links by subclassing the *NavLinkExtension* class.
+
+```java
+import java.util.Arrays;
+import java.util.List;
+import ro.fortsoft.pf4j.Extension;
+import com.gitblit.extensions.NavLinkExtension;
+import com.gitblit.models.UserModel;
+
+@Extension
+public class MyNavLink extends NavLinkExtension {
+
+    @Override
+    public List<NavLink> getNavLinks(UserModel user) {
+        NavLink link = new ExternalLinkMenuItem("Github", String.format("https://github.com/%s", user.username));
+        return Arrays.asList(link);
     }
 }
 ```