]> source.dussan.org Git - gitblit.git/commitdiff
Preliminary implementation of server-side forking (issue 137)
authorJames Moger <james.moger@gitblit.com>
Sun, 30 Sep 2012 03:40:46 +0000 (23:40 -0400)
committerJames Moger <james.moger@gitblit.com>
Sun, 30 Sep 2012 03:40:46 +0000 (23:40 -0400)
The fork mechanism clones the repository , access restrictions, and
other config options.  The app has been updated throughout to handle
personal repositories and to properly display origin/fork links.

In order to fork a repository the user account must have the #fork role,
the origin repository must permit forking, and the user account must
have standard clone permissions to the repository.

Because forking introduces a new user role no existing user accounts can
automatically begin forking a repository.  This is both a pro and a con.

Since the fork has the same access restrictions as the origin repository,
those who can access the origin may also access the fork.  This is intentional
to facilitate integration-manager workflow.  The fork owner does have the
power to completely change the access restrictions of his/her fork.

44 files changed:
.project
docs/04_releases.mkd
docs/05_roadmap.mkd
resources/gitblit.css
src/com/gitblit/ConfigUserService.java
src/com/gitblit/Constants.java
src/com/gitblit/FileUserService.java
src/com/gitblit/GitBlit.java
src/com/gitblit/SyndicationServlet.java
src/com/gitblit/models/ProjectModel.java
src/com/gitblit/models/RepositoryModel.java
src/com/gitblit/models/UserModel.java
src/com/gitblit/utils/ActivityUtils.java
src/com/gitblit/utils/JGitUtils.java
src/com/gitblit/utils/StringUtils.java
src/com/gitblit/wicket/GitBlitWebApp.java
src/com/gitblit/wicket/GitBlitWebApp.properties
src/com/gitblit/wicket/pages/BasePage.java
src/com/gitblit/wicket/pages/EditRepositoryPage.html
src/com/gitblit/wicket/pages/EditRepositoryPage.java
src/com/gitblit/wicket/pages/EditUserPage.html
src/com/gitblit/wicket/pages/EditUserPage.java
src/com/gitblit/wicket/pages/ForksPage.html [new file with mode: 0644]
src/com/gitblit/wicket/pages/ForksPage.java [new file with mode: 0644]
src/com/gitblit/wicket/pages/GitSearchPage.java
src/com/gitblit/wicket/pages/HistoryPage.java
src/com/gitblit/wicket/pages/LogPage.java
src/com/gitblit/wicket/pages/ProjectPage.html
src/com/gitblit/wicket/pages/ProjectPage.java
src/com/gitblit/wicket/pages/ProjectsPage.java
src/com/gitblit/wicket/pages/RepositoryPage.html
src/com/gitblit/wicket/pages/RepositoryPage.java
src/com/gitblit/wicket/pages/RootPage.java
src/com/gitblit/wicket/pages/SummaryPage.java
src/com/gitblit/wicket/pages/UserPage.html [new file with mode: 0644]
src/com/gitblit/wicket/pages/UserPage.java [new file with mode: 0644]
src/com/gitblit/wicket/panels/GravatarImage.java
src/com/gitblit/wicket/panels/HistoryPanel.java
src/com/gitblit/wicket/panels/LogPanel.java
src/com/gitblit/wicket/panels/ProjectRepositoryPanel.html [new file with mode: 0644]
src/com/gitblit/wicket/panels/ProjectRepositoryPanel.java [new file with mode: 0644]
src/com/gitblit/wicket/panels/RepositoriesPanel.html
src/com/gitblit/wicket/panels/RepositoriesPanel.java
src/com/gitblit/wicket/panels/SearchPanel.java

index 65c86e838ed20d543f3b7a3757cac067a6b2132e..90c747bcb3f46abc49708fd1a713450bcc2ad4c9 100644 (file)
--- a/.project
+++ b/.project
@@ -1,23 +1,23 @@
-<?xml version="1.0" encoding="UTF-8"?>\r
-<projectDescription>\r
-       <name>gitblit</name>\r
-       <comment></comment>\r
-       <projects>\r
-       </projects>\r
-       <buildSpec>\r
-               <buildCommand>\r
-                       <name>org.eclipse.jdt.core.javabuilder</name>\r
-                       <arguments>\r
-                       </arguments>\r
-               </buildCommand>\r
-               <buildCommand>\r
-                       <name>net.sf.eclipsecs.core.CheckstyleBuilder</name>\r
-                       <arguments>\r
-                       </arguments>\r
-               </buildCommand>\r
-       </buildSpec>\r
-       <natures>\r
-               <nature>org.eclipse.jdt.core.javanature</nature>\r
-               <nature>net.sf.eclipsecs.core.CheckstyleNature</nature>\r
-       </natures>\r
-</projectDescription>\r
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+       <name>Gitblit</name>
+       <comment></comment>
+       <projects>
+       </projects>
+       <buildSpec>
+               <buildCommand>
+                       <name>org.eclipse.jdt.core.javabuilder</name>
+                       <arguments>
+                       </arguments>
+               </buildCommand>
+               <buildCommand>
+                       <name>net.sf.eclipsecs.core.CheckstyleBuilder</name>
+                       <arguments>
+                       </arguments>
+               </buildCommand>
+       </buildSpec>
+       <natures>
+               <nature>org.eclipse.jdt.core.javanature</nature>
+               <nature>net.sf.eclipsecs.core.CheckstyleNature</nature>
+       </natures>
+</projectDescription>
index 454ce65c438ff9e75240fc583b2f9eeb34917112..30582d53e8263a7222f369b6f1a4e4970c31f9c0 100644 (file)
@@ -17,10 +17,15 @@ If you are updating from an earlier release AND you have indexed branches with t
 \r
 #### additions\r
 \r
-- added support for X-Forwarded-Context for Apache subdomain proxy configurations (issue 135)\r
-- delete branch feature (issue 121, Github/ajermakovics)\r
-- added line links to blob view at the expense of zebra striping (issue 130)\r
-- added RedmineUserService (github/mallowlabs)\r
+- Added simple project pages.  A project is a subfolder off the *git.repositoriesFolder*.\r
+- Added support for personal repositories.  This builds on the simple project pages.  \r
+Personal repositories are stored in *git.repositoriesFolder*/*~username*.  Each user with personal repositories will have a user page, something like the GitHub profile page.  Personal repositories have all the same features as common repositories.\r
+- Added support for server-side forking of a repository to a personal repository (issue 137)  \r
+In order to fork a repository to a personal clone, the user account must have the *fork* permission **and** the repository must *allow forks*.  The clone inherits the access restrictions of its origin.  i.e. if Team A has access to the origin repository, then by default Team A also has access to the fork.  This is to facilitate collaboration.  However, the fork owner may change access to the fork and add/remove users/teams, etc as required.\r
+- Added support for X-Forwarded-Context for Apache subdomain proxy configurations (issue 135)\r
+- Delete branch feature (issue 121, Github/ajermakovics)\r
+- Added line links to blob view at the expense of zebra striping (issue 130)\r
+- Added RedmineUserService (github/mallowlabs)\r
 \r
 #### changes\r
 \r
index 3238f732e3caa8027e9042a1fdc9797a881884e4..562cc7e7626e801fe35491a83fdecd7d532bf7cc 100644 (file)
@@ -26,7 +26,6 @@ This list is volatile.
 ### IDEAS\r
 \r
 * Gitblit: Re-use the EGit branch visualization table cell renderer as some sort of servlet\r
-* Gitblit: Support personal repositories (~username/repo)\r
 * Gitblit: diff should highlight inserted/removed fragment compared to original line\r
 * Gitblit: implement branch permission controls as Groovy pre-receive script.  \r
 *Maintain permissions text file similar to a gitolite configuration file or svn authz file.*\r
index fc948b64dce608ad84b8c92ac1d6b80521db8445..66387d1ea49b32b91870116556516481aef180f5 100644 (file)
@@ -20,6 +20,11 @@ a:focus {
        outline: none;\r
 }\r
 \r
+[class^="icon-"], [class*=" icon-"] a i {\r
+       /* override for a links that look like bootstrap buttons */\r
+       vertical-align: text-bottom;\r
+}\r
+\r
 hr {\r
        margin-top: 10px;\r
        margin-bottom: 10px;\r
@@ -127,6 +132,47 @@ navbar div>ul .menu-dropdown li a:hover,.nav .menu-dropdown li a:hover,.navbar d
        font-weight: bold;\r
 }\r
 \r
+.pageTitle {\r
+       color: #888;\r
+       font-size: 18px;\r
+       line-height: 27px;\r
+}\r
+.pageTitle .project, .pageTitle .repository {\r
+       font-family: Helvetica, arial, freesans, clean, sans-serif;\r
+       font-size: 22px;\r
+}\r
+\r
+.pageTitle .controls {\r
+       font-size: 12px;\r
+}\r
+\r
+.pageTitle .repository {\r
+       font-weight: bold;\r
+}\r
+\r
+.originRepository {\r
+       font-family: Helvetica, arial, freesans, clean, sans-serif;\r
+       color: #888;\r
+       font-size: 12px;\r
+       line-height: 14px;\r
+       margin: 0px;\r
+}\r
+\r
+.forkSource, .forkEntry {\r
+       color: #888;\r
+}\r
+\r
+.forkSource {\r
+       font-size: 18px;\r
+       line-height: 20px;\r
+       padding: 5px 0px;\r
+}\r
+\r
+.forkEntry {\r
+       font-size: 14px;\r
+       padding: 2px 0px;\r
+}\r
+\r
 div.page_footer {\r
        clear: both;\r
        height: 17px;\r
index faad6915e3dfb3ed151a539ec98cb70a1d3370bb..f526835d77945b1a197e27c958d62fe5eb11ca89 100644 (file)
@@ -750,6 +750,9 @@ public class ConfigUserService implements IUserService {
                        if (model.canAdmin) {\r
                                roles.add(Constants.ADMIN_ROLE);\r
                        }\r
+                       if (model.canFork) {\r
+                               roles.add(Constants.FORK_ROLE);\r
+                       }\r
                        if (model.excludeFromFederation) {\r
                                roles.add(Constants.NOT_FEDERATED_ROLE);\r
                        }\r
@@ -858,6 +861,7 @@ public class ConfigUserService implements IUserService {
                                        Set<String> roles = new HashSet<String>(Arrays.asList(config.getStringList(\r
                                                        USER, username, ROLE)));\r
                                        user.canAdmin = roles.contains(Constants.ADMIN_ROLE);\r
+                                       user.canFork = roles.contains(Constants.FORK_ROLE);\r
                                        user.excludeFromFederation = roles.contains(Constants.NOT_FEDERATED_ROLE);\r
 \r
                                        // repository memberships\r
index 55d49800534317b9c5d20c96aa785f972ecdcc18..c53119b2917c27209949b581397cf26bf812e9a7 100644 (file)
@@ -41,6 +41,8 @@ public class Constants {
        public static final String JGIT_VERSION = "JGit 2.1.0 (201209190230-r)";\r
 \r
        public static final String ADMIN_ROLE = "#admin";\r
+       \r
+       public static final String FORK_ROLE = "#fork";\r
 \r
        public static final String NOT_FEDERATED_ROLE = "#notfederated";\r
        \r
index 7705dfd8953887ee8c8b7d15edb80244fb959e65..40bc3f60769534e0da1c5722aed2d4e6bc84ecc1 100644 (file)
@@ -234,6 +234,8 @@ public class FileUserService extends FileSettings implements IUserService {
                                // Permissions\r
                                if (role.equalsIgnoreCase(Constants.ADMIN_ROLE)) {\r
                                        model.canAdmin = true;\r
+                               } else if (role.equalsIgnoreCase(Constants.FORK_ROLE)) {\r
+                                       model.canFork = true;\r
                                } else if (role.equalsIgnoreCase(Constants.NOT_FEDERATED_ROLE)) {\r
                                        model.excludeFromFederation = true;\r
                                }\r
@@ -283,6 +285,9 @@ public class FileUserService extends FileSettings implements IUserService {
                        if (model.canAdmin) {\r
                                roles.add(Constants.ADMIN_ROLE);\r
                        }\r
+                       if (model.canFork) {\r
+                               roles.add(Constants.FORK_ROLE);\r
+                       }\r
                        if (model.excludeFromFederation) {\r
                                roles.add(Constants.NOT_FEDERATED_ROLE);\r
                        }\r
index c7586544cbda556a2947fd6d76950f2c8c73f621..699bbacbad4614a585106ed6d3f2c62e4ffe952a 100644 (file)
@@ -22,6 +22,8 @@ import java.io.IOException;
 import java.io.InputStream;\r
 import java.io.InputStreamReader;\r
 import java.lang.reflect.Field;\r
+import java.net.URI;\r
+import java.net.URISyntaxException;\r
 import java.text.MessageFormat;\r
 import java.text.SimpleDateFormat;\r
 import java.util.ArrayList;\r
@@ -747,6 +749,14 @@ public class GitBlit implements ServletContextListener {
        private void addToCachedRepositoryList(String name, RepositoryModel model) {\r
                if (settings.getBoolean(Keys.git.cacheRepositoryList, true)) {\r
                        repositoryListCache.put(name, model);\r
+                       \r
+                       // update the fork origin repository with this repository clone\r
+                       if (!StringUtils.isEmpty(model.originRepository)) {\r
+                               if (repositoryListCache.containsKey(model.originRepository)) {\r
+                                       RepositoryModel origin = repositoryListCache.get(model.originRepository);\r
+                                       origin.addFork(name);\r
+                               }\r
+                       }\r
                }\r
        }\r
        \r
@@ -754,12 +764,13 @@ public class GitBlit implements ServletContextListener {
         * Removes the repository from the list of cached repositories.\r
         * \r
         * @param name\r
+        * @return the model being removed\r
         */\r
-       private void removeFromCachedRepositoryList(String name) {\r
+       private RepositoryModel removeFromCachedRepositoryList(String name) {\r
                if (StringUtils.isEmpty(name)) {\r
-                       return;\r
+                       return null;\r
                }\r
-               repositoryListCache.remove(name);\r
+               return repositoryListCache.remove(name);\r
        }\r
 \r
        /**\r
@@ -988,7 +999,7 @@ public class GitBlit implements ServletContextListener {
                        if (model == null) {\r
                                return null;\r
                        }\r
-                       addToCachedRepositoryList(repositoryName, model);                       \r
+                       addToCachedRepositoryList(repositoryName, model);\r
                        return model;\r
                }\r
                \r
@@ -1034,7 +1045,7 @@ public class GitBlit implements ServletContextListener {
         * @return project config map\r
         */\r
        private Map<String, ProjectModel> getProjectConfigs() {\r
-               if (projectConfigs.isOutdated()) {\r
+               if (projectCache.isEmpty() || projectConfigs.isOutdated()) {\r
                        \r
                        try {\r
                                projectConfigs.load();\r
@@ -1077,9 +1088,10 @@ public class GitBlit implements ServletContextListener {
         * Returns a list of project models for the user.\r
         * \r
         * @param user\r
+        * @param includeUsers\r
         * @return list of projects that are accessible to the user\r
         */\r
-       public List<ProjectModel> getProjectModels(UserModel user) {\r
+       public List<ProjectModel> getProjectModels(UserModel user, boolean includeUsers) {\r
                Map<String, ProjectModel> configs = getProjectConfigs();\r
 \r
                // per-user project lists, this accounts for security and visibility\r
@@ -1104,10 +1116,25 @@ public class GitBlit implements ServletContextListener {
                }\r
                \r
                // sort projects, root project first\r
-               List<ProjectModel> projects = new ArrayList<ProjectModel>(map.values());\r
-               Collections.sort(projects);\r
-               projects.remove(map.get(""));\r
-               projects.add(0, map.get(""));\r
+               List<ProjectModel> projects;\r
+               if (includeUsers) {\r
+                       // all projects\r
+                       projects = new ArrayList<ProjectModel>(map.values());\r
+                       Collections.sort(projects);\r
+                       projects.remove(map.get(""));\r
+                       projects.add(0, map.get(""));\r
+               } else {\r
+                       // all non-user projects\r
+                       projects = new ArrayList<ProjectModel>();\r
+                       ProjectModel root = map.remove("");\r
+                       for (ProjectModel model : map.values()) {\r
+                               if (!model.isUserProject()) {\r
+                                       projects.add(model);\r
+                               }\r
+                       }\r
+                       Collections.sort(projects);\r
+                       projects.add(0, root);\r
+               }\r
                return projects;\r
        }\r
        \r
@@ -1119,7 +1146,7 @@ public class GitBlit implements ServletContextListener {
         * @return a project model, or null if it does not exist\r
         */\r
        public ProjectModel getProjectModel(String name, UserModel user) {\r
-               for (ProjectModel project : getProjectModels(user)) {\r
+               for (ProjectModel project : getProjectModels(user, true)) {\r
                        if (project.name.equalsIgnoreCase(name)) {\r
                                return project;\r
                        }\r
@@ -1137,15 +1164,37 @@ public class GitBlit implements ServletContextListener {
                Map<String, ProjectModel> configs = getProjectConfigs();\r
                ProjectModel project = configs.get(name.toLowerCase());\r
                if (project == null) {\r
-                       return null;\r
+                       project = new ProjectModel(name);\r
+                       if (name.length() > 0 && name.charAt(0) == '~') {\r
+                               UserModel user = getUserModel(name.substring(1));\r
+                               if (user != null) {\r
+                                       project.title = user.getDisplayName();\r
+                                       project.description = "personal repositories";\r
+                               }\r
+                       }\r
+               } else {\r
+                       // clone the object\r
+                       project = DeepCopier.copy(project);\r
                }\r
-               // clone the object\r
-               project = DeepCopier.copy(project);\r
-               String folder = name.toLowerCase() + "/";\r
-               for (String repository : getRepositoryList()) {\r
-                       if (repository.toLowerCase().startsWith(folder)) {\r
-                               project.addRepository(repository);\r
+               if (StringUtils.isEmpty(name)) {\r
+                       // get root repositories\r
+                       for (String repository : getRepositoryList()) {\r
+                               if (repository.indexOf('/') == -1) {\r
+                                       project.addRepository(repository);\r
+                               }\r
                        }\r
+               } else {\r
+                       // get repositories in subfolder\r
+                       String folder = name.toLowerCase() + "/";\r
+                       for (String repository : getRepositoryList()) {\r
+                               if (repository.toLowerCase().startsWith(folder)) {\r
+                                       project.addRepository(repository);\r
+                               }\r
+                       }\r
+               }\r
+               if (project.repositories.size() == 0) {\r
+                       // no repositories == no project\r
+                       return null;\r
                }\r
                return project;\r
        }\r
@@ -1189,18 +1238,26 @@ public class GitBlit implements ServletContextListener {
                model.hasCommits = JGitUtils.hasCommits(r);\r
                model.lastChange = JGitUtils.getLastChange(r);\r
                model.isBare = r.isBare();\r
+               if (repositoryName.indexOf('/') == -1) {\r
+                       model.projectPath = "";\r
+               } else {\r
+                       model.projectPath = repositoryName.substring(0, repositoryName.indexOf('/'));\r
+               }\r
                \r
                StoredConfig config = r.getConfig();\r
+               boolean hasOrigin = !StringUtils.isEmpty(config.getString("remote", "origin", "url"));\r
+               \r
                if (config != null) {\r
                        model.description = getConfig(config, "description", "");\r
                        model.owner = getConfig(config, "owner", "");\r
                        model.useTickets = getConfig(config, "useTickets", false);\r
                        model.useDocs = getConfig(config, "useDocs", false);\r
+                       model.allowForks = getConfig(config, "allowForks", true);\r
                        model.accessRestriction = AccessRestrictionType.fromName(getConfig(config,\r
                                        "accessRestriction", settings.getString(Keys.git.defaultAccessRestriction, null)));\r
                        model.authorizationControl = AuthorizationControl.fromName(getConfig(config,\r
                                        "authorizationControl", settings.getString(Keys.git.defaultAuthorizationControl, null)));\r
-                       model.showRemoteBranches = getConfig(config, "showRemoteBranches", false);\r
+                       model.showRemoteBranches = getConfig(config, "showRemoteBranches", hasOrigin);\r
                        model.isFrozen = getConfig(config, "isFrozen", false);\r
                        model.showReadme = getConfig(config, "showReadme", false);\r
                        model.skipSizeCalculation = getConfig(config, "skipSizeCalculation", false);\r
@@ -1229,6 +1286,23 @@ public class GitBlit implements ServletContextListener {
                model.HEAD = JGitUtils.getHEADRef(r);\r
                model.availableRefs = JGitUtils.getAvailableHeadTargets(r);\r
                r.close();\r
+               \r
+               if (model.origin != null && model.origin.startsWith("file://")) {\r
+                       // repository was cloned locally... perhaps as a fork\r
+                       try {\r
+                               File folder = new File(new URI(model.origin));\r
+                               String originRepo = com.gitblit.utils.FileUtils.getRelativePath(getRepositoriesFolder(), folder);\r
+                               if (!StringUtils.isEmpty(originRepo)) {\r
+                                       // ensure origin still exists\r
+                                       File repoFolder = new File(getRepositoriesFolder(), originRepo);\r
+                                       if (repoFolder.exists()) {\r
+                                               model.originRepository = originRepo;\r
+                                       }\r
+                               }\r
+                       } catch (URISyntaxException e) {\r
+                               logger.error("Failed to determine fork for " + model, e);\r
+                       }\r
+               }\r
                return model;\r
        }\r
        \r
@@ -1425,9 +1499,27 @@ public class GitBlit implements ServletContextListener {
                                                        "Failed to rename repository permissions ''{0}'' to ''{1}''.",\r
                                                        repositoryName, repository.name));\r
                                }\r
+                               \r
+                               // rename fork origins in their configs\r
+                               if (!ArrayUtils.isEmpty(repository.forks)) {\r
+                                       for (String fork : repository.forks) {\r
+                                               Repository rf = getRepository(fork);\r
+                                               try {\r
+                                                       StoredConfig config = rf.getConfig();\r
+                                                       String origin = config.getString("remote", "origin", "url");\r
+                                                       origin = origin.replace(repositoryName, repository.name);\r
+                                                       config.setString("remote", "origin", "url", origin);\r
+                                                       config.save();\r
+                                               } catch (Exception e) {\r
+                                                       logger.error("Failed to update repository fork config for " + fork, e);\r
+                                               }\r
+                                               rf.close();\r
+                                       }\r
+                               }\r
 \r
                                // clear the cache\r
                                clearRepositoryMetadataCache(repositoryName);\r
+                               repository.resetDisplayName();\r
                        }\r
 \r
                        // load repository\r
@@ -1483,6 +1575,7 @@ public class GitBlit implements ServletContextListener {
                config.setString(Constants.CONFIG_GITBLIT, null, "owner", repository.owner);\r
                config.setBoolean(Constants.CONFIG_GITBLIT, null, "useTickets", repository.useTickets);\r
                config.setBoolean(Constants.CONFIG_GITBLIT, null, "useDocs", repository.useDocs);\r
+               config.setBoolean(Constants.CONFIG_GITBLIT, null, "allowForks", repository.allowForks);\r
                config.setString(Constants.CONFIG_GITBLIT, null, "accessRestriction", repository.accessRestriction.name());\r
                config.setString(Constants.CONFIG_GITBLIT, null, "authorizationControl", repository.authorizationControl.name());\r
                config.setBoolean(Constants.CONFIG_GITBLIT, null, "showRemoteBranches", repository.showRemoteBranches);\r
@@ -1558,7 +1651,11 @@ public class GitBlit implements ServletContextListener {
                        closeRepository(repositoryName);\r
                        // clear the repository cache\r
                        clearRepositoryMetadataCache(repositoryName);\r
-                       removeFromCachedRepositoryList(repositoryName);\r
+                       \r
+                       RepositoryModel model = removeFromCachedRepositoryList(repositoryName);\r
+                       if (!ArrayUtils.isEmpty(model.forks)) {\r
+                               resetRepositoryListCache();\r
+                       }\r
 \r
                        File folder = new File(repositoriesFolder, repositoryName);\r
                        if (folder.exists() && folder.isDirectory()) {\r
@@ -2423,4 +2520,52 @@ public class GitBlit implements ServletContextListener {
                scheduledExecutor.shutdownNow();\r
                luceneExecutor.close();\r
        }\r
+       \r
+       /**\r
+        * Creates a personal fork of the specified repository. The clone is view\r
+        * restricted by default and the owner of the source repository is given\r
+        * access to the clone. \r
+        * \r
+        * @param repository\r
+        * @param user\r
+        * @return true, if successful\r
+        */\r
+       public boolean fork(RepositoryModel repository, UserModel user) {\r
+               String cloneName = MessageFormat.format("~{0}/{1}.git", user.username, StringUtils.stripDotGit(StringUtils.getLastPathElement(repository.name)));\r
+               String fromUrl = MessageFormat.format("file://{0}/{1}", repositoriesFolder.getAbsolutePath(), repository.name);\r
+               try {\r
+                       // clone the repository\r
+                       JGitUtils.cloneRepository(repositoriesFolder, cloneName, fromUrl, true, null);\r
+                       \r
+                       // create a Gitblit repository model for the clone\r
+                       RepositoryModel cloneModel = repository.cloneAs(cloneName);\r
+                       cloneModel.owner = user.username;\r
+                       updateRepositoryModel(cloneName, cloneModel, false);\r
+                       \r
+                       if (AuthorizationControl.NAMED.equals(cloneModel.authorizationControl)) {\r
+                               // add the owner of the source repository to the clone's access list\r
+                               if (!StringUtils.isEmpty(repository.owner)) {\r
+                                       UserModel owner = getUserModel(repository.owner);\r
+                                       if (owner != null) {\r
+                                               owner.repositories.add(cloneName);\r
+                                               updateUserModel(owner.username, owner, false);\r
+                                       }\r
+                               }\r
+                               \r
+                               // inherit origin's access lists\r
+                               List<String> users = getRepositoryUsers(repository);\r
+                               setRepositoryUsers(cloneModel, users);\r
+                               \r
+                               List<String> teams = getRepositoryTeams(repository);\r
+                               setRepositoryTeams(cloneModel, teams);\r
+                       }\r
+                       \r
+                       // add this clone to the cached model\r
+                       addToCachedRepositoryList(cloneModel.name, cloneModel);\r
+                       return true;\r
+               } catch (Exception e) {\r
+                       logger.error("failed to fork", e);\r
+               }\r
+               return false;\r
+       }\r
 }\r
index 4c542b6d18471e03e851abd05cc96eabde367bdb..a36f5839aa98ffa6adcc819756d03273de818e17 100644 (file)
@@ -227,7 +227,7 @@ public class SyndicationServlet extends HttpServlet {
                                commits = JGitUtils.searchRevlogs(repository, objectId, searchString, searchType,\r
                                                offset, length);\r
                        }\r
-                       Map<ObjectId, List<RefModel>> allRefs = JGitUtils.getAllRefs(repository);\r
+                       Map<ObjectId, List<RefModel>> allRefs = JGitUtils.getAllRefs(repository, model.showRemoteBranches);\r
 \r
                        // convert RevCommit to SyndicatedEntryModel\r
                        for (RevCommit commit : commits) {\r
index bc359037025f8dbfc7c06bf53a67a745c8678a11..189a409b5b5c531ea2eab2aaf5eeabaa75326f48 100644 (file)
@@ -53,6 +53,10 @@ public class ProjectModel implements Serializable, Comparable<ProjectModel> {
                this.title = "";\r
                this.description = "";\r
        }\r
+       \r
+       public boolean isUserProject() {\r
+               return name.charAt(0) == '~';\r
+       }\r
 \r
        public boolean hasRepository(String name) {\r
                return repositories.contains(name.toLowerCase());\r
index 27196635dde03df14262687d64f3f1941f33b13d..44aba1d8698615a14988974d4516debf708602ba 100644 (file)
@@ -20,6 +20,8 @@ import java.util.ArrayList;
 import java.util.Date;\r
 import java.util.List;\r
 import java.util.Map;\r
+import java.util.Set;\r
+import java.util.TreeSet;\r
 \r
 import com.gitblit.Constants.AccessRestrictionType;\r
 import com.gitblit.Constants.AuthorizationControl;\r
@@ -68,7 +70,11 @@ public class RepositoryModel implements Serializable, Comparable<RepositoryModel
        public List<String> postReceiveScripts;\r
        public List<String> mailingLists;\r
        public Map<String, String> customFields;\r
+       public String projectPath;\r
        private String displayName;\r
+       public boolean allowForks;\r
+       public Set<String> forks;\r
+       public String originRepository;\r
        \r
        public RepositoryModel() {\r
                this("", "", "", new Date(0));\r
@@ -97,6 +103,24 @@ public class RepositoryModel implements Serializable, Comparable<RepositoryModel
                }\r
                return localBranches;\r
        }\r
+       \r
+       public void addFork(String repository) {\r
+               if (forks == null) {\r
+                       forks = new TreeSet<String>();\r
+               }\r
+               forks.add(repository);\r
+       }\r
+       \r
+       public void removeFork(String repository) {\r
+               if (forks == null) {\r
+                       return;\r
+               }\r
+               forks.remove(repository);\r
+       }\r
+       \r
+       public void resetDisplayName() {\r
+               displayName = null;\r
+       }\r
 \r
        @Override\r
        public String toString() {\r
@@ -110,4 +134,29 @@ public class RepositoryModel implements Serializable, Comparable<RepositoryModel
        public int compareTo(RepositoryModel o) {\r
                return StringUtils.compareRepositoryNames(name, o.name);\r
        }\r
+       \r
+       public boolean isPersonalRepository() {\r
+               return !StringUtils.isEmpty(projectPath) && projectPath.charAt(0) == '~';\r
+       }\r
+       \r
+       public boolean isUsersPersonalRepository(String username) {\r
+               return !StringUtils.isEmpty(projectPath) && projectPath.equalsIgnoreCase("~" + username);\r
+       }\r
+       \r
+       public RepositoryModel cloneAs(String cloneName) {\r
+               RepositoryModel clone = new RepositoryModel();\r
+               clone.name = cloneName;\r
+               clone.description = description;\r
+               clone.accessRestriction = accessRestriction;\r
+               clone.authorizationControl = authorizationControl;\r
+               clone.federationStrategy = federationStrategy;\r
+               clone.showReadme = showReadme;\r
+               clone.showRemoteBranches = false;\r
+               clone.allowForks = false;\r
+               clone.useDocs = useDocs;\r
+               clone.useTickets = useTickets;\r
+               clone.skipSizeCalculation = skipSizeCalculation;\r
+               clone.skipSummaryMetrics = skipSummaryMetrics;\r
+               return clone;\r
+       }\r
 }
\ No newline at end of file
index 8349bab6f8290e7279680474059ac83aff56fb8d..0ede878756d82884a4b838e2a7ac4e7d69928f3e 100644 (file)
@@ -20,6 +20,7 @@ import java.security.Principal;
 import java.util.HashSet;\r
 import java.util.Set;\r
 \r
+import com.gitblit.Constants.AccessRestrictionType;\r
 import com.gitblit.Constants.AuthorizationControl;\r
 import com.gitblit.utils.StringUtils;\r
 \r
@@ -42,6 +43,7 @@ public class UserModel implements Principal, Serializable, Comparable<UserModel>
        public String displayName;\r
        public String emailAddress;\r
        public boolean canAdmin;\r
+       public boolean canFork;\r
        public boolean excludeFromFederation;\r
        public final Set<String> repositories = new HashSet<String>();\r
        public final Set<TeamModel> teams = new HashSet<TeamModel>();\r
@@ -83,6 +85,33 @@ public class UserModel implements Principal, Serializable, Comparable<UserModel>
                }\r
                return false;\r
        }\r
+       \r
+       public boolean canForkRepository(RepositoryModel repository) {\r
+               if (canAdmin) {\r
+                       return true;\r
+               }\r
+               if (!canFork) {\r
+                       // user has been prohibited from forking\r
+                       return false;\r
+               }\r
+               if (!isAuthenticated) {\r
+                       // unauthenticated user model\r
+                       return false;\r
+               }\r
+               if (("~" + username).equalsIgnoreCase(repository.projectPath)) {\r
+                       // this repository is already a personal repository\r
+                       return false;\r
+               }\r
+               if (!repository.allowForks) {\r
+                       // repository prohibits forks\r
+                       return false;\r
+               }\r
+               if (repository.accessRestriction.atLeast(AccessRestrictionType.CLONE)) {\r
+                       return canAccessRepository(repository);\r
+               }\r
+               // repository is not clone-restricted\r
+               return true;\r
+       }\r
 \r
        public boolean hasRepository(String name) {\r
                return repositories.contains(name.toLowerCase());\r
index 02a9924ba40724259ad035d1fc13a734248f34b4..e51599425a0d501dc4d9cb6f5a133ff99925ef0f 100644 (file)
@@ -94,7 +94,7 @@ public class ActivityUtils {
                                        branches.add(objectId);\r
                                }\r
                                Map<ObjectId, List<RefModel>> allRefs = JGitUtils\r
-                                               .getAllRefs(repository);\r
+                                               .getAllRefs(repository, model.showRemoteBranches);\r
 \r
                                for (String branch : branches) {\r
                                        String shortName = branch;\r
index c5cd1c3589188368ba55aea357b9511f21eebd52..050b591f483e77b8e4a7737c1e106a7d0e5983b4 100644 (file)
@@ -1369,9 +1369,23 @@ public class JGitUtils {
         * @return all refs grouped by their referenced object id\r
         */\r
        public static Map<ObjectId, List<RefModel>> getAllRefs(Repository repository) {\r
+               return getAllRefs(repository, true);\r
+       }\r
+       \r
+       /**\r
+        * Returns all refs grouped by their associated object id.\r
+        * \r
+        * @param repository\r
+        * @param includeRemoteRefs\r
+        * @return all refs grouped by their referenced object id\r
+        */\r
+       public static Map<ObjectId, List<RefModel>> getAllRefs(Repository repository, boolean includeRemoteRefs) {\r
                List<RefModel> list = getRefs(repository, org.eclipse.jgit.lib.RefDatabase.ALL, true, -1);\r
                Map<ObjectId, List<RefModel>> refs = new HashMap<ObjectId, List<RefModel>>();\r
                for (RefModel ref : list) {\r
+                       if (!includeRemoteRefs && ref.getName().startsWith(Constants.R_REMOTES)) {\r
+                               continue;\r
+                       }\r
                        ObjectId objectid = ref.getReferencedObjectId();\r
                        if (!refs.containsKey(objectid)) {\r
                                refs.put(objectid, new ArrayList<RefModel>());\r
index e4407901d97b9b072e18613aa9a85d78492f6d1d..07113388a7e86f4f45225e97a7a5dbec516e9b6c 100644 (file)
@@ -367,7 +367,7 @@ public class StringUtils {
         * @return the first invalid character found or null if string is acceptable\r
         */\r
        public static Character findInvalidCharacter(String name) {\r
-               char[] validChars = { '/', '.', '_', '-' };\r
+               char[] validChars = { '/', '.', '_', '-', '~' };\r
                for (char c : name.toCharArray()) {\r
                        if (!Character.isLetterOrDigit(c)) {\r
                                boolean ok = false;\r
@@ -660,4 +660,31 @@ public class StringUtils {
                }\r
                return input;\r
        }\r
+       \r
+       /**\r
+        * Returns the first path element of a path string.  If no path separator is\r
+        * found in the path, an empty string is returned. \r
+        * \r
+        * @param path\r
+        * @return the first element in the path\r
+        */\r
+       public static String getFirstPathElement(String path) {\r
+               if (path.indexOf('/') > -1) {\r
+                       return path.substring(0, path.indexOf('/')).trim();\r
+               }\r
+               return "";\r
+       }\r
+       \r
+       /**\r
+        * Returns the last path element of a path string\r
+        * \r
+        * @param path\r
+        * @return the last element in the path\r
+        */\r
+       public static String getLastPathElement(String path) {\r
+               if (path.indexOf('/') > -1) {\r
+                       return path.substring(path.lastIndexOf('/') + 1);\r
+               }\r
+               return path;\r
+       }\r
 }
\ No newline at end of file
index 507de15d79027c097ebf2bd47ebb2c0d50679c47..b691cea41dc6c6cb9ae375bca6312ff6e70b2509 100644 (file)
@@ -34,6 +34,7 @@ import com.gitblit.wicket.pages.CommitDiffPage;
 import com.gitblit.wicket.pages.CommitPage;\r
 import com.gitblit.wicket.pages.DocsPage;\r
 import com.gitblit.wicket.pages.FederationRegistrationPage;\r
+import com.gitblit.wicket.pages.ForksPage;\r
 import com.gitblit.wicket.pages.GitSearchPage;\r
 import com.gitblit.wicket.pages.GravatarProfilePage;\r
 import com.gitblit.wicket.pages.HistoryPage;\r
@@ -53,6 +54,7 @@ import com.gitblit.wicket.pages.TagsPage;
 import com.gitblit.wicket.pages.TicketPage;\r
 import com.gitblit.wicket.pages.TicketsPage;\r
 import com.gitblit.wicket.pages.TreePage;\r
+import com.gitblit.wicket.pages.UserPage;\r
 import com.gitblit.wicket.pages.UsersPage;\r
 \r
 public class GitBlitWebApp extends WebApplication {\r
@@ -116,6 +118,8 @@ public class GitBlitWebApp extends WebApplication {
                mount("/lucene", LuceneSearchPage.class);\r
                mount("/project", ProjectPage.class, "p");\r
                mount("/projects", ProjectsPage.class);\r
+               mount("/user", UserPage.class, "user");\r
+               mount("/forks", ForksPage.class, "r");\r
        }\r
 \r
        private void mount(String location, Class<? extends WebPage> clazz, String... parameters) {\r
index c427dd3454ca628ce60cff9707434671065e8445..50c43fec8496694dfbf20d89109663a78fce3c21 100644 (file)
@@ -318,4 +318,18 @@ gb.clearCache = clear cache
 gb.projects = projects\r
 gb.project = project\r
 gb.allProjects = all projects\r
-gb.copyToClipboard = copy to clipboard
\ No newline at end of file
+gb.copyToClipboard = copy to clipboard\r
+gb.fork = fork\r
+gb.forks = forks\r
+gb.forkRepository = fork {0}?\r
+gb.repositoryForked = {0} has been forked\r
+gb.repositoryForkFailed= failed to fork {1}\r
+gb.personalRepositories = personal repositories\r
+gb.allowForks = allow forks\r
+gb.allowForksDescription = allow authorized users to fork this repository\r
+gb.forkedFrom = forked from\r
+gb.canFork = can fork\r
+gb.canForkDescription = user is permitted to fork authorized repositories\r
+gb.myFork = view my fork\r
+gb.forksProhibited = forks prohibited\r
+gb.forksProhibitedWarning = this repository forbids forks
\ No newline at end of file
index f9f90b0ff808a5e1635af87ad2e6383d37793ad3..00d9677f1f10e3caadb299014533938fd73264d4 100644 (file)
@@ -18,7 +18,6 @@ package com.gitblit.wicket.pages;
 import java.util.ArrayList;\r
 import java.util.Calendar;\r
 import java.util.Collections;\r
-import java.util.Comparator;\r
 import java.util.Date;\r
 import java.util.HashSet;\r
 import java.util.LinkedHashMap;\r
@@ -36,6 +35,7 @@ import org.apache.wicket.Application;
 import org.apache.wicket.MarkupContainer;\r
 import org.apache.wicket.PageParameters;\r
 import org.apache.wicket.RedirectToUrlException;\r
+import org.apache.wicket.RequestCycle;\r
 import org.apache.wicket.RestartResponseException;\r
 import org.apache.wicket.markup.html.CSSPackageResource;\r
 import org.apache.wicket.markup.html.WebPage;\r
@@ -63,7 +63,6 @@ import com.gitblit.models.UserModel;
 import com.gitblit.utils.StringUtils;\r
 import com.gitblit.utils.TimeUtils;\r
 import com.gitblit.wicket.GitBlitWebSession;\r
-import com.gitblit.wicket.PageRegistration.DropDownMenuItem;\r
 import com.gitblit.wicket.WicketUtils;\r
 import com.gitblit.wicket.panels.LinkPanel;\r
 \r
@@ -235,9 +234,9 @@ public abstract class BasePage extends WebPage {
                return req.getServerName();\r
        }\r
        \r
-       protected String getRepositoryUrl(RepositoryModel repository) {\r
+       public static String getRepositoryUrl(RepositoryModel repository) {\r
                StringBuilder sb = new StringBuilder();\r
-               sb.append(WicketUtils.getGitblitURL(getRequestCycle().getRequest()));\r
+               sb.append(WicketUtils.getGitblitURL(RequestCycle.get().getRequest()));\r
                sb.append(Constants.GIT_PATH);\r
                sb.append(repository.name);\r
                \r
@@ -252,7 +251,7 @@ public abstract class BasePage extends WebPage {
        \r
        protected List<ProjectModel> getProjectModels() {\r
                final UserModel user = GitBlitWebSession.get().getUser();\r
-               List<ProjectModel> projects = GitBlit.self().getProjectModels(user);\r
+               List<ProjectModel> projects = GitBlit.self().getProjectModels(user, true);\r
                return projects;\r
        }\r
        \r
index 2bb5776c78cc6dbb6cd5ffb443cf65ebda0b183e..6809c2d8c28706955e4e0c62432f6eed2fdc0a3e 100644 (file)
                                        </wicket:container>\r
                                </td></tr>\r
                                <tr><th colspan="2"><hr/></th></tr>\r
+                               <tr><th><wicket:message key="gb.allowForks"></wicket:message></th><td class="edit"><label class="checkbox"><input type="checkbox" wicket:id="allowForks" tabindex="17" /> &nbsp;<span class="help-inline"><wicket:message key="gb.allowForksDescription"></wicket:message></span></label></td></tr>\r
+                               <tr><th colspan="2"><hr/></th></tr>\r
                                <tr><th style="vertical-align: top;"><wicket:message key="gb.permittedUsers"></wicket:message></th><td style="padding:2px;"><span wicket:id="users"></span></td></tr>\r
                                <tr><th style="vertical-align: top;"><wicket:message key="gb.permittedTeams"></wicket:message></th><td style="padding:2px;"><span wicket:id="teams"></span></td></tr>\r
                                <tr><td colspan="2"><h3><wicket:message key="gb.federation"></wicket:message> &nbsp;<small><wicket:message key="gb.federationRepositoryDescription"></wicket:message></small></h3></td></tr>    \r
-                               <tr><th><wicket:message key="gb.federationStrategy"></wicket:message></th><td class="edit"><select class="span4" wicket:id="federationStrategy" tabindex="17" /></td></tr>\r
+                               <tr><th><wicket:message key="gb.federationStrategy"></wicket:message></th><td class="edit"><select class="span4" wicket:id="federationStrategy" tabindex="18" /></td></tr>\r
                                <tr><th style="vertical-align: top;"><wicket:message key="gb.federationSets"></wicket:message></th><td style="padding:2px;"><span wicket:id="federationSets"></span></td></tr>\r
                                <tr><td colspan="2"><h3><wicket:message key="gb.search"></wicket:message> &nbsp;<small><wicket:message key="gb.indexedBranchesDescription"></wicket:message></small></h3></td></tr>     \r
                                <tr><th style="vertical-align: top;"><wicket:message key="gb.indexedBranches"></wicket:message></th><td style="padding:2px;"><span wicket:id="indexedBranches"></span></td></tr>\r
index 505cb5486d8be1e5d6ad9c3734645e65487d4829..214d0f0d5a1be781f2abbd28420d381af35c76f7 100644 (file)
@@ -343,6 +343,7 @@ public class EditRepositoryPage extends RootSubPage {
                form.add(new TextField<String>("description"));\r
                form.add(new DropDownChoice<String>("owner", GitBlit.self().getAllUsernames())\r
                                .setEnabled(GitBlitWebSession.get().canAdmin()));\r
+               form.add(new CheckBox("allowForks"));\r
                form.add(new DropDownChoice<AccessRestrictionType>("accessRestriction", Arrays\r
                                .asList(AccessRestrictionType.values()), new AccessRestrictionRenderer()));\r
                form.add(new CheckBox("isFrozen"));\r
index 1c076bd6a24b22144db7e28bc70fe819769ddbdf..685eb64f81a8bc80b5afd7fdbf32859dd1c5ca9f 100644 (file)
                                <tr><th><wicket:message key="gb.displayName"></wicket:message></th><td class="edit"><input type="text" wicket:id="displayName" size="30" tabindex="4" /></td></tr>\r
                                <tr><th><wicket:message key="gb.emailAddress"></wicket:message></th><td class="edit"><input type="text" wicket:id="emailAddress" size="30" tabindex="5" /></td></tr>\r
                                <tr><th><wicket:message key="gb.canAdmin"></wicket:message></th><td class="edit"><label class="checkbox"><input type="checkbox" wicket:id="canAdmin" tabindex="6" /> &nbsp;<span class="help-inline"><wicket:message key="gb.canAdminDescription"></wicket:message></span></label></td></tr>                            \r
-                               <tr><th><wicket:message key="gb.excludeFromFederation"></wicket:message></th><td class="edit"><label class="checkbox"><input type="checkbox" wicket:id="excludeFromFederation" tabindex="7" /> &nbsp;<span class="help-inline"><wicket:message key="gb.excludeFromFederationDescription"></wicket:message></span></label></td></tr>                             \r
+                               <tr><th><wicket:message key="gb.canFork"></wicket:message></th><td class="edit"><label class="checkbox"><input type="checkbox" wicket:id="canFork" tabindex="7" /> &nbsp;<span class="help-inline"><wicket:message key="gb.canForkDescription"></wicket:message></span></label></td></tr>                               \r
+                               <tr><th><wicket:message key="gb.excludeFromFederation"></wicket:message></th><td class="edit"><label class="checkbox"><input type="checkbox" wicket:id="excludeFromFederation" tabindex="8" /> &nbsp;<span class="help-inline"><wicket:message key="gb.excludeFromFederationDescription"></wicket:message></span></label></td></tr>                             \r
                                <tr><td colspan="2" style="padding-top:15px;"><h3><wicket:message key="gb.accessPermissions"></wicket:message> &nbsp;<small><wicket:message key="gb.accessPermissionsForUserDescription"></wicket:message></small></h3></td></tr>       \r
                                <tr><th style="vertical-align: top;"><wicket:message key="gb.teamMemberships"></wicket:message></th><td style="padding:2px;"><span wicket:id="teams"></span></td></tr>\r
                                <tr><td colspan="2"><hr></hr></td></tr>\r
                                <tr><th style="vertical-align: top;"><wicket:message key="gb.restrictedRepositories"></wicket:message></th><td style="padding:2px;"><span wicket:id="repositories"></span></td></tr>\r
-                               <tr><td colspan='2'><div class="form-actions"><input class="btn btn-primary" type="submit" value="Save" wicket:message="value:gb.save" wicket:id="save" tabindex="8" /> &nbsp; <input class="btn" type="submit" value="Cancel" wicket:message="value:gb.cancel" wicket:id="cancel" tabindex="9" /></div></td></tr>\r
+                               <tr><td colspan='2'><div class="form-actions"><input class="btn btn-primary" type="submit" value="Save" wicket:message="value:gb.save" wicket:id="save" tabindex="9" /> &nbsp; <input class="btn" type="submit" value="Cancel" wicket:message="value:gb.cancel" wicket:id="cancel" tabindex="10" /></div></td></tr>\r
                        </tbody>\r
                </table>\r
        </form> \r
index cfe7c35da86232b81c1a541338c8fbdc190de7ff..31f91c1cb3da8a01c22824bf8e8e5576050c1dd4 100644 (file)
@@ -231,6 +231,7 @@ public class EditUserPage extends RootSubPage {
                form.add(new TextField<String>("displayName").setEnabled(editDisplayName));\r
                form.add(new TextField<String>("emailAddress").setEnabled(editEmailAddress));\r
                form.add(new CheckBox("canAdmin"));\r
+               form.add(new CheckBox("canFork"));\r
                form.add(new CheckBox("excludeFromFederation"));\r
                form.add(repositories);\r
                form.add(teams.setEnabled(editTeams));\r
diff --git a/src/com/gitblit/wicket/pages/ForksPage.html b/src/com/gitblit/wicket/pages/ForksPage.html
new file mode 100644 (file)
index 0000000..68f8489
--- /dev/null
@@ -0,0 +1,24 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">\r
+<html xmlns="http://www.w3.org/1999/xhtml"  \r
+      xmlns:wicket="http://wicket.apache.org/dtds.data/wicket-xhtml1.3-strict.dtd"  \r
+      xml:lang="en"  \r
+      lang="en"> \r
+\r
+<body>\r
+<wicket:extend>\r
+\r
+       <div class="forkSource">\r
+               <b><span class="repositorySwatch" wicket:id="forkSourceSwatch"></span></b>\r
+               <span wicket:id="forkSourceAvatar" style="vertical-align: baseline;"></span>\r
+               <span wicket:id="forkSourceProject">[a project]</span> / <span wicket:id="forkSource">[a fork]</span> \r
+       </div>\r
+       \r
+       <div wicket:id="fork">\r
+               <div class="forkEntry">\r
+                       <span wicket:id="anAvatar" style="vertical-align: baseline;"></span>\r
+                       <span wicket:id="aProject">[a project]</span> / <span wicket:id="aFork">[a fork]</span>\r
+               </div>\r
+       </div>\r
+</wicket:extend>       \r
+</body>\r
+</html>
\ No newline at end of file
diff --git a/src/com/gitblit/wicket/pages/ForksPage.java b/src/com/gitblit/wicket/pages/ForksPage.java
new file mode 100644 (file)
index 0000000..7b8235b
--- /dev/null
@@ -0,0 +1,133 @@
+/*\r
+ * Copyright 2012 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.pages;\r
+\r
+import java.util.ArrayList;\r
+import java.util.List;\r
+\r
+import org.apache.wicket.Component;\r
+import org.apache.wicket.PageParameters;\r
+import org.apache.wicket.markup.html.basic.Label;\r
+import org.apache.wicket.markup.repeater.Item;\r
+import org.apache.wicket.markup.repeater.data.DataView;\r
+import org.apache.wicket.markup.repeater.data.ListDataProvider;\r
+import org.eclipse.jgit.lib.PersonIdent;\r
+\r
+import com.gitblit.GitBlit;\r
+import com.gitblit.Keys;\r
+import com.gitblit.models.RepositoryModel;\r
+import com.gitblit.models.UserModel;\r
+import com.gitblit.utils.ArrayUtils;\r
+import com.gitblit.utils.StringUtils;\r
+import com.gitblit.wicket.GitBlitWebSession;\r
+import com.gitblit.wicket.WicketUtils;\r
+import com.gitblit.wicket.panels.GravatarImage;\r
+import com.gitblit.wicket.panels.LinkPanel;\r
+\r
+public class ForksPage extends RepositoryPage {\r
+\r
+       public ForksPage(PageParameters params) {\r
+               super(params);\r
+               \r
+               RepositoryModel model = getRepositoryModel();\r
+               RepositoryModel origin;\r
+               List<String> list;\r
+               if (ArrayUtils.isEmpty(model.forks)) {\r
+                       // origin repository has forks\r
+                       origin = GitBlit.self().getRepositoryModel(model.originRepository);\r
+                       list = new ArrayList<String>(origin.forks);\r
+               } else {\r
+                       // this repository has forks\r
+                       origin = model;\r
+                       list = new ArrayList<String>(model.forks);\r
+               }\r
+               \r
+               if (origin.isPersonalRepository()) {\r
+                       // personal repository\r
+                       UserModel user = GitBlit.self().getUserModel(origin.projectPath.substring(1));\r
+                       PersonIdent ident = new PersonIdent(user.getDisplayName(), user.emailAddress);\r
+                       add(new GravatarImage("forkSourceAvatar", ident, 20));\r
+                       add(new Label("forkSourceSwatch").setVisible(false));\r
+                       add(new LinkPanel("forkSourceProject", null, user.getDisplayName(), UserPage.class, WicketUtils.newUsernameParameter(user.username)));\r
+               } else {\r
+                       // standard repository\r
+                       add(new GravatarImage("forkSourceAvatar", new PersonIdent("", ""), 20).setVisible(false));\r
+                       Component swatch;\r
+                       if (origin.isBare){\r
+                               swatch = new Label("forkSourceSwatch", "&nbsp;").setEscapeModelStrings(false);\r
+                       } else {\r
+                               swatch = new Label("forkSourceSwatch", "!");\r
+                               WicketUtils.setHtmlTooltip(swatch, getString("gb.workingCopyWarning"));\r
+                       }\r
+                       WicketUtils.setCssBackground(swatch, origin.toString());\r
+                       add(swatch);\r
+                       final boolean showSwatch = GitBlit.getBoolean(Keys.web.repositoryListSwatches, true);\r
+                       swatch.setVisible(showSwatch);\r
+                       \r
+                       String projectName = origin.projectPath;\r
+                       if (StringUtils.isEmpty(projectName)) {\r
+                               projectName = GitBlit.getString(Keys.web.repositoryRootGroupName, "main");\r
+                       }\r
+                       add(new LinkPanel("forkSourceProject", null, projectName, ProjectPage.class, WicketUtils.newProjectParameter(origin.projectPath)));\r
+               }\r
+               \r
+               String source = StringUtils.getLastPathElement(origin.name);\r
+               add(new LinkPanel("forkSource", null, StringUtils.stripDotGit(source), SummaryPage.class, WicketUtils.newRepositoryParameter(origin.name)));\r
+\r
+               // only display user-accessible forks\r
+               UserModel user = GitBlitWebSession.get().getUser();\r
+               List<RepositoryModel> forks = new ArrayList<RepositoryModel>();\r
+               for (String aFork : list) {\r
+                       RepositoryModel fork = GitBlit.self().getRepositoryModel(user, aFork);\r
+                       if (fork != null) {\r
+                               forks.add(fork);\r
+                       }\r
+               }\r
+               \r
+               ListDataProvider<RepositoryModel> forksDp = new ListDataProvider<RepositoryModel>(forks);\r
+               DataView<RepositoryModel> forksList = new DataView<RepositoryModel>("fork", forksDp) {\r
+                       private static final long serialVersionUID = 1L;\r
+\r
+                       public void populateItem(final Item<RepositoryModel> item) {\r
+                               RepositoryModel fork = item.getModelObject();\r
+                               \r
+                               if (fork.isPersonalRepository()) {\r
+                                       UserModel user = GitBlit.self().getUserModel(fork.projectPath.substring(1));\r
+                                       PersonIdent ident = new PersonIdent(user.getDisplayName(), user.emailAddress);\r
+                                       item.add(new GravatarImage("anAvatar", ident, 20));\r
+                                       item.add(new LinkPanel("aProject", null, user.getDisplayName(), UserPage.class, WicketUtils.newUsernameParameter(user.username)));\r
+                               } else {\r
+                                       PersonIdent ident = new PersonIdent(fork.name, fork.name);\r
+                                       item.add(new GravatarImage("anAvatar", ident, 20));\r
+                                       item.add(new LinkPanel("aProject", null, fork.projectPath, ProjectPage.class, WicketUtils.newProjectParameter(fork.projectPath)));\r
+                               }\r
+                               \r
+                               String repo = StringUtils.getLastPathElement(fork.name);\r
+                               item.add(new LinkPanel("aFork", null, StringUtils.stripDotGit(repo), SummaryPage.class, WicketUtils.newRepositoryParameter(fork.name)));\r
+                               \r
+                               WicketUtils.setCssStyle(item, "margin-left:25px;");\r
+                       }\r
+               };\r
+               \r
+               add(forksList);\r
+\r
+       }\r
+\r
+       @Override\r
+       protected String getPageName() {\r
+               return getString("gb.forks");\r
+       }\r
+}\r
index ca813ac4ccf80f573c1554e5b21a988b6ddcdaa1..6b0714f0bf726482b67b8d4bf42a97e7e0c48d39 100644 (file)
@@ -36,7 +36,7 @@ public class GitSearchPage extends RepositoryPage {
                int nextPage = pageNumber + 1;\r
 \r
                SearchPanel search = new SearchPanel("searchPanel", repositoryName, objectId, value,\r
-                               searchType, getRepository(), -1, pageNumber - 1);\r
+                               searchType, getRepository(), -1, pageNumber - 1, getRepositoryModel().showRemoteBranches);\r
                boolean hasMore = search.hasMore();\r
                add(search);\r
 \r
index 122eeb8afebebf0e8d18058a7f0ebc0c2b8692e2..563202e66a7a9f775bbf9eab93c4d835f6c52ba0 100644 (file)
@@ -32,7 +32,7 @@ public class HistoryPage extends RepositoryPage {
                int nextPage = pageNumber + 1;\r
 \r
                HistoryPanel history = new HistoryPanel("historyPanel", repositoryName, objectId, path,\r
-                               getRepository(), -1, pageNumber - 1);\r
+                               getRepository(), -1, pageNumber - 1, getRepositoryModel().showRemoteBranches);\r
                boolean hasMore = history.hasMore();\r
                add(history);\r
 \r
index d3dc3a9e66a9edb1a51198d2c439c50e71e4a50c..ee8ddfef04195251136a6cd3820f5ce12d211f4b 100644 (file)
@@ -37,7 +37,7 @@ public class LogPage extends RepositoryPage {
                        refid = getRepositoryModel().HEAD;\r
                }\r
                LogPanel logPanel = new LogPanel("logPanel", repositoryName, refid, getRepository(), -1,\r
-                               pageNumber - 1);\r
+                               pageNumber - 1, getRepositoryModel().showRemoteBranches);\r
                boolean hasMore = logPanel.hasMore();\r
                add(logPanel);\r
 \r
index db10329b056c4324cd1d8d6a2805cbbb9fe2eda4..3e73ba52a26d2623e2a8fa2ee0a45159ab883ba1 100644 (file)
@@ -7,29 +7,6 @@
 <body>\r
 <wicket:extend>\r
 \r
-       <wicket:fragment wicket:id="repositoryAdminLinks">\r
-               <span class="link">\r
-                       <a wicket:id="tree"><wicket:message key="gb.tree"></wicket:message></a>\r
-                       | <a wicket:id="log"><wicket:message key="gb.log"></wicket:message></a>\r
-                       | <a wicket:id="editRepository"><wicket:message key="gb.edit">[edit]</wicket:message></a>\r
-                       | <a wicket:id="deleteRepository"><wicket:message key="gb.delete">[delete]</wicket:message></a>\r
-               </span>\r
-       </wicket:fragment>\r
-\r
-       <wicket:fragment wicket:id="repositoryOwnerLinks">\r
-               <span class="link">\r
-                       <a wicket:id="tree"><wicket:message key="gb.tree"></wicket:message></a>\r
-                       | <a wicket:id="log"><wicket:message key="gb.log"></wicket:message></a>\r
-                       | <a wicket:id="editRepository"><wicket:message key="gb.edit">[edit]</wicket:message></a>\r
-               </span>\r
-       </wicket:fragment>\r
-\r
-       <wicket:fragment wicket:id="repositoryUserLinks">\r
-               <span class="link">\r
-                       <a wicket:id="tree"><wicket:message key="gb.tree"></wicket:message></a>\r
-                       | <a wicket:id="log"><wicket:message key="gb.log"></wicket:message></a>\r
-               </span>\r
-       </wicket:fragment>\r
 \r
        <div class="row">\r
                <div class="span12">\r
                                                <div class="markdown" wicket:id="repositoriesMessage">[repositories message]</div>\r
                                        </div>\r
                                </div>\r
-                               \r
-                               <!-- repositories -->\r
                                <div class="row">\r
-                                       <div class="span6" style="border-top:1px solid #eee;" wicket:id="repository">\r
-                                               <div style="padding-top:15px;padding-bottom:15px;margin-right:15px;">\r
-                                                       <div class="pull-right" style="text-align:right;padding-right:15px;">\r
-                                                               <span wicket:id="repositoryLinks"></span>\r
-\r
-                                                               <div>\r
-                                                               <img class="inlineIcon" wicket:id="frozenIcon" />\r
-                                                               <img class="inlineIcon" wicket:id="federatedIcon" />\r
-                                                               \r
-                                                               <a style="text-decoration: none;" wicket:id="tickets" wicket:message="title:gb.tickets">\r
-                                                                               <img style="border:0px;vertical-align:middle;" src="bug_16x16.png"></img>\r
-                                                                       </a>\r
-                                                                       <a style="text-decoration: none;" wicket:id="docs" wicket:message="title:gb.docs">\r
-                                                                               <img style="border:0px;vertical-align:middle;" src="book_16x16.png"></img>\r
-                                                                       </a>\r
-                                                                       <a style="text-decoration: none;" wicket:id="syndication" wicket:message="title:gb.feed">\r
-                                                                               <img style="border:0px;vertical-align:middle;" src="feed_16x16.png"></img>\r
-                                                                       </a>\r
-                                                               </div>\r
-                                                               <span style="color: #999;font-style:italic;font-size:0.8em;" wicket:id="repositoryOwner">[owner]</span>\r
-                                                       </div>  \r
-                       \r
-                                                       <h3><span class="repositorySwatch" wicket:id="repositorySwatch"></span>\r
-                                                               <span style="padding-left:3px;" wicket:id="repositoryName">[repository name]</span>\r
-                                                               <img class="inlineIcon" wicket:id="accessRestrictionIcon" />\r
-                                                       </h3>\r
-                       \r
-                                                       <div style="padding-left:20px;">\r
-\r
-                                                       <div style="padding-bottom:10px" wicket:id="repositoryDescription">[repository description]</div>\r
-\r
-                                                       <div style="color: #999;">\r
-                                                               <wicket:message key="gb.lastChange">[last change]</wicket:message> <span wicket:id="repositoryLastChange">[last change]</span>,\r
-                                                               <span style="font-size:0.8em;" wicket:id="repositorySize">[repository size]</span>\r
-                                                       </div>\r
-        \r
-                                                               <div class="hidden-phone hidden-tablet" wicket:id="repositoryCloneUrl">[repository clone url]</div>\r
-                                                       </div>\r
-                                               </div>\r
-                                       </div>          \r
-                               </div>\r
+                                       <div class="span6" style="border-bottom:1px solid #eee;" wicket:id="repositoryList">\r
+                                               <span wicket:id="repository"></span>\r
+                                       </div>\r
+                               </div>                          \r
                        </div>\r
                        \r
                        <!-- activity tab -->\r
index 808cc065d4b19af244d482b3ee6a67cb4465e7d0..3679a6f19fa078a3c526d3d9e48293a375a26eb9 100644 (file)
@@ -34,10 +34,7 @@ import org.apache.wicket.PageParameters;
 import org.apache.wicket.RedirectException;\r
 import org.apache.wicket.behavior.HeaderContributor;\r
 import org.apache.wicket.markup.html.basic.Label;\r
-import org.apache.wicket.markup.html.link.BookmarkablePageLink;\r
 import org.apache.wicket.markup.html.link.ExternalLink;\r
-import org.apache.wicket.markup.html.link.Link;\r
-import org.apache.wicket.markup.html.panel.Fragment;\r
 import org.apache.wicket.markup.repeater.Item;\r
 import org.apache.wicket.markup.repeater.data.DataView;\r
 import org.apache.wicket.markup.repeater.data.ListDataProvider;\r
@@ -52,7 +49,6 @@ import com.gitblit.models.ProjectModel;
 import com.gitblit.models.RepositoryModel;\r
 import com.gitblit.models.UserModel;\r
 import com.gitblit.utils.ActivityUtils;\r
-import com.gitblit.utils.ArrayUtils;\r
 import com.gitblit.utils.MarkdownUtils;\r
 import com.gitblit.utils.StringUtils;\r
 import com.gitblit.wicket.GitBlitWebApp;\r
@@ -66,9 +62,7 @@ import com.gitblit.wicket.charting.GoogleCharts;
 import com.gitblit.wicket.charting.GoogleLineChart;\r
 import com.gitblit.wicket.charting.GooglePieChart;\r
 import com.gitblit.wicket.panels.ActivityPanel;\r
-import com.gitblit.wicket.panels.BasePanel.JavascriptEventConfirmation;\r
-import com.gitblit.wicket.panels.LinkPanel;\r
-import com.gitblit.wicket.panels.RepositoryUrlPanel;\r
+import com.gitblit.wicket.panels.ProjectRepositoryPanel;\r
 \r
 public class ProjectPage extends RootPage {\r
        \r
@@ -148,143 +142,16 @@ public class ProjectPage extends RootPage {
                        }\r
                });\r
 \r
-               final boolean showSwatch = GitBlit.getBoolean(Keys.web.repositoryListSwatches, true);\r
-               final boolean gitServlet = GitBlit.getBoolean(Keys.git.enableGitServlet, true);\r
-               final boolean showSize = GitBlit.getBoolean(Keys.web.showRepositorySizes, true);\r
-               \r
                final ListDataProvider<RepositoryModel> dp = new ListDataProvider<RepositoryModel>(repositories);\r
-               DataView<RepositoryModel> dataView = new DataView<RepositoryModel>("repository", dp) {\r
+               DataView<RepositoryModel> dataView = new DataView<RepositoryModel>("repositoryList", dp) {\r
                        private static final long serialVersionUID = 1L;\r
 \r
                        public void populateItem(final Item<RepositoryModel> item) {\r
                                final RepositoryModel entry = item.getModelObject();\r
-\r
-                               // repository swatch\r
-                               Component swatch;\r
-                               if (entry.isBare){\r
-                                       swatch = new Label("repositorySwatch", "&nbsp;").setEscapeModelStrings(false);\r
-                               } else {\r
-                                       swatch = new Label("repositorySwatch", "!");\r
-                                       WicketUtils.setHtmlTooltip(swatch, getString("gb.workingCopyWarning"));\r
-                               }\r
-                               WicketUtils.setCssBackground(swatch, entry.toString());\r
-                               item.add(swatch);\r
-                               swatch.setVisible(showSwatch);\r
-                               \r
-                               PageParameters pp = WicketUtils.newRepositoryParameter(entry.name);\r
-                               item.add(new LinkPanel("repositoryName", "list", StringUtils.getRelativePath(projectPath, StringUtils.stripDotGit(entry.name)), SummaryPage.class, pp));\r
-                               item.add(new Label("repositoryDescription", entry.description).setVisible(!StringUtils.isEmpty(entry.description)));\r
-                               \r
-                               item.add(new BookmarkablePageLink<Void>("tickets", TicketsPage.class, pp).setVisible(entry.useTickets));\r
-                               item.add(new BookmarkablePageLink<Void>("docs", DocsPage.class, pp).setVisible(entry.useDocs));\r
-\r
-                               if (entry.isFrozen) {\r
-                                       item.add(WicketUtils.newImage("frozenIcon", "cold_16x16.png",\r
-                                                       getString("gb.isFrozen")));\r
-                               } else {\r
-                                       item.add(WicketUtils.newClearPixel("frozenIcon").setVisible(false));\r
-                               }\r
-\r
-                               if (entry.isFederated) {\r
-                                       item.add(WicketUtils.newImage("federatedIcon", "federated_16x16.png",\r
-                                                       getString("gb.isFederated")));\r
-                               } else {\r
-                                       item.add(WicketUtils.newClearPixel("federatedIcon").setVisible(false));\r
-                               }\r
-                               switch (entry.accessRestriction) {\r
-                               case NONE:\r
-                                       item.add(WicketUtils.newBlankImage("accessRestrictionIcon").setVisible(false));\r
-                                       break;\r
-                               case PUSH:\r
-                                       item.add(WicketUtils.newImage("accessRestrictionIcon", "lock_go_16x16.png",\r
-                                                       getAccessRestrictions().get(entry.accessRestriction)));\r
-                                       break;\r
-                               case CLONE:\r
-                                       item.add(WicketUtils.newImage("accessRestrictionIcon", "lock_pull_16x16.png",\r
-                                                       getAccessRestrictions().get(entry.accessRestriction)));\r
-                                       break;\r
-                               case VIEW:\r
-                                       item.add(WicketUtils.newImage("accessRestrictionIcon", "shield_16x16.png",\r
-                                                       getAccessRestrictions().get(entry.accessRestriction)));\r
-                                       break;\r
-                               default:\r
-                                       item.add(WicketUtils.newBlankImage("accessRestrictionIcon"));\r
-                               }\r
-\r
-                               item.add(new Label("repositoryOwner", StringUtils.isEmpty(entry.owner) ? "" : (entry.owner + " (" + getString("gb.owner") + ")")));\r
-                               \r
-                               \r
-                               UserModel user = GitBlitWebSession.get().getUser();\r
-                               Fragment repositoryLinks;                               \r
-                               boolean showOwner = user != null && user.username.equalsIgnoreCase(entry.owner);\r
-                               if (showAdmin || showOwner) {\r
-                                       repositoryLinks = new Fragment("repositoryLinks",\r
-                                                       showAdmin ? "repositoryAdminLinks" : "repositoryOwnerLinks", this);\r
-                                       repositoryLinks.add(new BookmarkablePageLink<Void>("editRepository",\r
-                                                       EditRepositoryPage.class, WicketUtils\r
-                                                                       .newRepositoryParameter(entry.name)));\r
-                                       if (showAdmin) {\r
-                                               Link<Void> deleteLink = new Link<Void>("deleteRepository") {\r
-\r
-                                                       private static final long serialVersionUID = 1L;\r
-\r
-                                                       @Override\r
-                                                       public void onClick() {\r
-                                                               if (GitBlit.self().deleteRepositoryModel(entry)) {\r
-                                                                       info(MessageFormat.format(getString("gb.repositoryDeleted"), entry));\r
-                                                                       // TODO dp.remove(entry);\r
-                                                               } else {\r
-                                                                       error(MessageFormat.format(getString("gb.repositoryDeleteFailed"), entry));\r
-                                                               }\r
-                                                       }\r
-                                               };\r
-                                               deleteLink.add(new JavascriptEventConfirmation("onclick", MessageFormat.format(\r
-                                                               getString("gb.deleteRepository"), entry)));\r
-                                               repositoryLinks.add(deleteLink);\r
-                                       }\r
-                               } else {\r
-                                       repositoryLinks = new Fragment("repositoryLinks", "repositoryUserLinks", this);\r
-                               }\r
                                \r
-                               repositoryLinks.add(new BookmarkablePageLink<Void>("tree", TreePage.class,\r
-                                               WicketUtils.newRepositoryParameter(entry.name)).setEnabled(entry.hasCommits));\r
-\r
-                               repositoryLinks.add(new BookmarkablePageLink<Void>("log", LogPage.class,\r
-                                               WicketUtils.newRepositoryParameter(entry.name)).setEnabled(entry.hasCommits));\r
-\r
-                               item.add(repositoryLinks);\r
-                               \r
-                               String lastChange;\r
-                               if (entry.lastChange.getTime() == 0) {\r
-                                       lastChange = "--";\r
-                               } else {\r
-                                       lastChange = getTimeUtils().timeAgo(entry.lastChange);\r
-                               }\r
-                               Label lastChangeLabel = new Label("repositoryLastChange", lastChange);\r
-                               item.add(lastChangeLabel);\r
-                               WicketUtils.setCssClass(lastChangeLabel, getTimeUtils().timeAgoCss(entry.lastChange));\r
-                               \r
-                               if (entry.hasCommits) {\r
-                                       // Existing repository\r
-                                       item.add(new Label("repositorySize", entry.size).setVisible(showSize));\r
-                               } else {\r
-                                       // New repository\r
-                                       item.add(new Label("repositorySize", getString("gb.empty"))\r
-                                                       .setEscapeModelStrings(false));\r
-                               }\r
-                               \r
-                               item.add(new ExternalLink("syndication", SyndicationServlet.asLink("",\r
-                                               entry.name, null, 0)));\r
-                               \r
-                               List<String> repositoryUrls = new ArrayList<String>();\r
-                               if (gitServlet) {\r
-                                       // add the Gitblit repository url\r
-                                       repositoryUrls.add(getRepositoryUrl(entry));\r
-                               }\r
-                               repositoryUrls.addAll(GitBlit.self().getOtherCloneUrls(entry.name));\r
-                               \r
-                               String primaryUrl = ArrayUtils.isEmpty(repositoryUrls) ? "" : repositoryUrls.remove(0);\r
-                               item.add(new RepositoryUrlPanel("repositoryCloneUrl", primaryUrl));\r
+                               ProjectRepositoryPanel row = new ProjectRepositoryPanel("repository", \r
+                                               getLocalizer(), this, showAdmin, entry, getAccessRestrictions());\r
+                               item.add(row);\r
                        }\r
                };\r
                add(dataView);\r
@@ -434,7 +301,7 @@ public class ProjectPage extends RootPage {
        protected List<ProjectModel> getProjectModels() {\r
                if (projectModels.isEmpty()) {\r
                        final UserModel user = GitBlitWebSession.get().getUser();\r
-                       List<ProjectModel> projects = GitBlit.self().getProjectModels(user);\r
+                       List<ProjectModel> projects = GitBlit.self().getProjectModels(user, false);\r
                        projectModels.addAll(projects);\r
                }\r
                return projectModels;\r
@@ -451,7 +318,12 @@ public class ProjectPage extends RootPage {
        \r
        protected List<DropDownMenuItem> getProjectsMenu() {\r
                List<DropDownMenuItem> menu = new ArrayList<DropDownMenuItem>();\r
-               List<ProjectModel> projects = getProjectModels();\r
+               List<ProjectModel> projects = new ArrayList<ProjectModel>();\r
+               for (ProjectModel model : getProjectModels()) {\r
+                       if (!model.isUserProject()) {\r
+                               projects.add(model);\r
+                       }\r
+               }\r
                int maxProjects = 15;\r
                boolean showAllProjects = projects.size() > maxProjects;\r
                if (showAllProjects) {\r
index f3c4416e108c8edb94fc0f78574f8d98eb916b52..7161d0f33f0ec8bde49204c1ec9207f0fe6abba5 100644 (file)
@@ -36,6 +36,7 @@ import org.eclipse.jgit.lib.Constants;
 import com.gitblit.GitBlit;\r
 import com.gitblit.Keys;\r
 import com.gitblit.models.ProjectModel;\r
+import com.gitblit.models.UserModel;\r
 import com.gitblit.utils.MarkdownUtils;\r
 import com.gitblit.utils.StringUtils;\r
 import com.gitblit.wicket.GitBlitWebSession;\r
@@ -63,6 +64,13 @@ public class ProjectsPage extends RootPage {
        protected boolean reusePageParameters() {\r
                return true;\r
        }\r
+       \r
+       @Override\r
+       protected List<ProjectModel> getProjectModels() {\r
+               final UserModel user = GitBlitWebSession.get().getUser();\r
+               List<ProjectModel> projects = GitBlit.self().getProjectModels(user, false);\r
+               return projects;\r
+       }\r
 \r
        private void setup(PageParameters params) {\r
                setupPage("", "");\r
index de64fce05f48556616266dd5f1cf12d363dfc2fc..4b53dbac251849067364b258d2ac422e083c0a32 100644 (file)
                        <!-- page header -->\r
                        <div class="pageTitle">\r
                                <div class="row">\r
-                                       <div wicket:id="workingCopy"></div>\r
-                                       <div class="span9">\r
-                                               <h2><span wicket:id="repositoryName">[repository name]</span> <small><span wicket:id="pageName">[page name]</span></small></h2>\r
+                                       <div class="controls">\r
+                                               <span wicket:id="workingCopyIndicator"></span>\r
+                                               <span wicket:id="forksProhibitedIndicator"></span>\r
+                                               <div class="hidden-phone btn-group pull-right">\r
+                                                       <!-- future spot for other repo buttons -->\r
+                                                       <a class="btn" wicket:id="myForkLink"><i class="icon-random"></i> <wicket:message key="gb.myFork"></wicket:message></a>\r
+                                                       <a class="btn" wicket:id="forkLink"><i class="icon-random"></i> <wicket:message key="gb.fork"></wicket:message></a>\r
+                                               </div>\r
+                                       </div>\r
+                                       <div class="span7">\r
+                                               <div><span class="project" wicket:id="projectTitle">[project title]</span>/<span class="repository" wicket:id="repositoryName">[repository name]</span> <span wicket:id="pageName">[page name]</span></div>\r
+                                               <span wicket:id="originRepository">[origin repository]</span>\r
                                        </div>\r
                                </div>\r
                        </div>\r
                        <wicket:child />\r
                </div>\r
                \r
+               <wicket:fragment wicket:id="originFragment">\r
+                       <p class="originRepository"><wicket:message key="gb.forkedFrom">[forked from]</wicket:message> <span wicket:id="originRepository">[origin repository]</span></p>\r
+               </wicket:fragment>\r
+               \r
                <wicket:fragment wicket:id="workingCopyFragment">\r
-                       <p class="pull-right" style="padding-top:5px;">\r
-                               <span class="alert alert-info" style="padding: 8px 14px 8px 14px;vertical-align: middle;"><i class="icon-exclamation-sign" style="vertical-align: middle;"></i>&nbsp;<span class="hidden-phone" wicket:id="workingCopy" style="font-weight:bold;">[working copy]</span></span>\r
-                       </p>\r
+                       <div class="pull-right" style="padding-top:0px;margin-bottom:0px;padding-left:5px">\r
+                               <span class="alert alert-info" style="padding: 6px 14px 6px 14px;vertical-align: middle;"><i class="icon-exclamation-sign"></i>&nbsp;<span class="hidden-phone" wicket:id="workingCopy" style="font-weight:bold;">[working copy]</span></span>\r
+                       </div>\r
                </wicket:fragment>\r
+\r
+               <wicket:fragment wicket:id="forksProhibitedFragment">\r
+                       <div class="pull-right" style="padding-top:0px;margin-bottom:0px;padding-left:5px">\r
+                               <span class="alert alert-error" style="padding: 6px 14px 6px 14px;vertical-align: middle;"><i class="icon-ban-circle"></i>&nbsp;<span class="hidden-phone" wicket:id="forksProhibited" style="font-weight:bold;">[forks prohibited]</span></span>\r
+                       </div>\r
+               </wicket:fragment>\r
+               \r
        </wicket:extend>\r
 </body>\r
 </html>
\ No newline at end of file
index eb8536c332eb57026b6a941048d8d9eefec13859..a85d21e189dd665bc3e4f55b87d8608d71b25d99 100644 (file)
@@ -28,10 +28,12 @@ import java.util.Set;
 \r
 import org.apache.wicket.Component;\r
 import org.apache.wicket.PageParameters;\r
+import org.apache.wicket.RedirectException;\r
 import org.apache.wicket.markup.html.basic.Label;\r
 import org.apache.wicket.markup.html.form.DropDownChoice;\r
 import org.apache.wicket.markup.html.form.TextField;\r
 import org.apache.wicket.markup.html.link.ExternalLink;\r
+import org.apache.wicket.markup.html.link.Link;\r
 import org.apache.wicket.markup.html.panel.Fragment;\r
 import org.apache.wicket.model.IModel;\r
 import org.apache.wicket.model.Model;\r
@@ -47,8 +49,10 @@ import com.gitblit.GitBlit;
 import com.gitblit.Keys;\r
 import com.gitblit.PagesServlet;\r
 import com.gitblit.SyndicationServlet;\r
+import com.gitblit.models.ProjectModel;\r
 import com.gitblit.models.RepositoryModel;\r
 import com.gitblit.models.SubmoduleModel;\r
+import com.gitblit.models.UserModel;\r
 import com.gitblit.utils.ArrayUtils;\r
 import com.gitblit.utils.JGitUtils;\r
 import com.gitblit.utils.StringUtils;\r
@@ -58,6 +62,7 @@ import com.gitblit.wicket.PageRegistration;
 import com.gitblit.wicket.PageRegistration.OtherPageLink;\r
 import com.gitblit.wicket.SessionlessForm;\r
 import com.gitblit.wicket.WicketUtils;\r
+import com.gitblit.wicket.panels.BasePanel.JavascriptEventConfirmation;\r
 import com.gitblit.wicket.panels.LinkPanel;\r
 import com.gitblit.wicket.panels.NavigationPanel;\r
 import com.gitblit.wicket.panels.RefsPanel;\r
@@ -81,10 +86,11 @@ public abstract class RepositoryPage extends BasePage {
        public RepositoryPage(PageParameters params) {\r
                super(params);\r
                repositoryName = WicketUtils.getRepositoryName(params);\r
-               if (repositoryName.indexOf('/') > -1) {\r
-                       projectName = repositoryName.substring(0, repositoryName.indexOf('/'));\r
-               } else {\r
+               String root =StringUtils.getFirstPathElement(repositoryName);\r
+               if (StringUtils.isEmpty(root)) {\r
                        projectName = GitBlit.getString(Keys.web.repositoryRootGroupName, "main");\r
+               } else {\r
+                       projectName = root;\r
                }\r
                objectId = WicketUtils.getObject(params);\r
                \r
@@ -125,7 +131,6 @@ public abstract class RepositoryPage extends BasePage {
 \r
                // standard links\r
                pages.put("repositories", new PageRegistration("gb.repositories", RepositoriesPage.class));\r
-               pages.put("project", new PageRegistration("gb.project", ProjectPage.class, WicketUtils.newProjectParameter(projectName)));\r
                pages.put("summary", new PageRegistration("gb.summary", SummaryPage.class, params));\r
                pages.put("log", new PageRegistration("gb.log", LogPage.class, params));\r
                pages.put("branches", new PageRegistration("gb.branches", BranchesPage.class, params));\r
@@ -136,6 +141,17 @@ public abstract class RepositoryPage extends BasePage {
                Repository r = getRepository();\r
                RepositoryModel model = getRepositoryModel();\r
 \r
+               // forks list button\r
+               if (StringUtils.isEmpty(model.originRepository)) {\r
+                       if (!ArrayUtils.isEmpty(model.forks)) {\r
+                               // this origin repository has forks\r
+                               pages.put("forks", new PageRegistration("gb.forks", ForksPage.class, params));\r
+                       }\r
+               } else {\r
+                       // this is a fork of another repository\r
+                       pages.put("forks", new PageRegistration("gb.forks", ForksPage.class, params));\r
+               }\r
+               \r
                // per-repository extra page links\r
                if (model.useTickets && TicgitUtils.getTicketsBranch(r) != null) {\r
                        pages.put("tickets", new PageRegistration("gb.tickets", TicketsPage.class, params));\r
@@ -168,19 +184,100 @@ public abstract class RepositoryPage extends BasePage {
 \r
        @Override\r
        protected void setupPage(String repositoryName, String pageName) {\r
-               add(new LinkPanel("repositoryName", null, StringUtils.stripDotGit(repositoryName),\r
-                               SummaryPage.class, WicketUtils.newRepositoryParameter(repositoryName)));\r
+               String projectName = StringUtils.getFirstPathElement(repositoryName);\r
+               ProjectModel project = GitBlit.self().getProjectModel(projectName);\r
+               if (project.isUserProject()) {\r
+                       // user-as-project\r
+                       add(new LinkPanel("projectTitle", null, project.getDisplayName(),\r
+                                       UserPage.class, WicketUtils.newUsernameParameter(project.name.substring(1))));\r
+               } else {\r
+                       // project\r
+                       add(new LinkPanel("projectTitle", null, project.name,\r
+                                       ProjectPage.class, WicketUtils.newProjectParameter(project.name)));\r
+               }\r
+               \r
+               String name = StringUtils.stripDotGit(repositoryName);\r
+               if (!StringUtils.isEmpty(projectName) && name.startsWith(projectName)) {\r
+                       name = name.substring(projectName.length() + 1);\r
+               }\r
+               add(new LinkPanel("repositoryName", null, name, SummaryPage.class,\r
+                               WicketUtils.newRepositoryParameter(repositoryName)));\r
                add(new Label("pageName", pageName).setRenderBodyOnly(true));\r
+               \r
+               // indicate origin repository\r
+               RepositoryModel model = getRepositoryModel();\r
+               if (StringUtils.isEmpty(model.originRepository)) {\r
+                       add(new Label("originRepository").setVisible(false));\r
+               } else {\r
+                       Fragment forkFrag = new Fragment("originRepository", "originFragment", this);\r
+                       forkFrag.add(new LinkPanel("originRepository", null, StringUtils.stripDotGit(model.originRepository), \r
+                                       SummaryPage.class, WicketUtils.newRepositoryParameter(model.originRepository)));\r
+                       add(forkFrag);\r
+               }\r
+               \r
                if (getRepositoryModel().isBare) {\r
-                       add(new Label("workingCopy").setVisible(false));\r
+                       add(new Label("workingCopyIndicator").setVisible(false));\r
                } else {\r
-                       Fragment fragment = new Fragment("workingCopy", "workingCopyFragment", this);\r
+                       Fragment wc = new Fragment("workingCopyIndicator", "workingCopyFragment", this);\r
                        Label lbl = new Label("workingCopy", getString("gb.workingCopy"));\r
                        WicketUtils.setHtmlTooltip(lbl,  getString("gb.workingCopyWarning"));\r
-                       fragment.add(lbl);\r
-                       add(fragment);\r
+                       wc.add(lbl);\r
+                       add(wc);\r
                }\r
-\r
+               \r
+               if (getRepositoryModel().allowForks) {\r
+                       add(new Label("forksProhibitedIndicator").setVisible(false));\r
+               } else {\r
+                       Fragment wc = new Fragment("forksProhibitedIndicator", "forksProhibitedFragment", this);\r
+                       Label lbl = new Label("forksProhibited", getString("gb.forksProhibited"));\r
+                       WicketUtils.setHtmlTooltip(lbl,  getString("gb.forksProhibitedWarning"));\r
+                       wc.add(lbl);\r
+                       add(wc);\r
+               }\r
+               \r
+               UserModel user = GitBlitWebSession.get().getUser();\r
+               \r
+               // fork button\r
+               if (user != null) {                     \r
+                       final String clonedRepo = MessageFormat.format("~{0}/{1}.git", user.username, StringUtils.stripDotGit(StringUtils.getLastPathElement(model.name)));\r
+                       boolean hasClone = GitBlit.self().hasRepository(clonedRepo) && !getRepositoryModel().name.equals(clonedRepo);\r
+                       if (user.canForkRepository(model) && !hasClone) {\r
+                               Link<Void> forkLink = new Link<Void>("forkLink") {\r
+\r
+                                       private static final long serialVersionUID = 1L;\r
+\r
+                                       @Override\r
+                                       public void onClick() {\r
+                                               RepositoryModel model = getRepositoryModel();\r
+                                               if (GitBlit.self().fork(model, GitBlitWebSession.get().getUser())) {\r
+                                                       throw new RedirectException(SummaryPage.class, WicketUtils.newRepositoryParameter(clonedRepo));\r
+                                               } else {\r
+                                                       error(MessageFormat.format(getString("gb.repositoryForkFailed"), model));\r
+                                               }\r
+                                       }\r
+                               };\r
+                               forkLink.add(new JavascriptEventConfirmation("onclick", MessageFormat.format(\r
+                                               getString("gb.forkRepository"), getRepositoryModel())));\r
+                               add(forkLink);\r
+                       } else {\r
+                               // user not allowed to fork or fork already exists or repo forbids forking\r
+                               add(new ExternalLink("forkLink", "").setVisible(false));\r
+                       }\r
+                       \r
+                       if (hasClone) {\r
+                               // user has clone\r
+                               String url = getRequestCycle().urlFor(SummaryPage.class, WicketUtils.newRepositoryParameter(clonedRepo)).toString();\r
+                               add(new ExternalLink("myForkLink", url));\r
+                       } else {\r
+                               // user does not have clone\r
+                               add(new ExternalLink("myForkLink", "").setVisible(false));\r
+                       }\r
+               } else {\r
+                       // server prohibits forking\r
+                       add(new ExternalLink("forkLink", "").setVisible(false));\r
+                       add(new ExternalLink("myForkLink", "").setVisible(false));\r
+               }\r
+               \r
                super.setupPage(repositoryName, pageName);\r
        }\r
 \r
@@ -312,7 +409,7 @@ public abstract class RepositoryPage extends BasePage {
        }\r
 \r
        protected void addRefs(Repository r, RevCommit c) {\r
-               add(new RefsPanel("refsPanel", repositoryName, c, JGitUtils.getAllRefs(r)));\r
+               add(new RefsPanel("refsPanel", repositoryName, c, JGitUtils.getAllRefs(r, getRepositoryModel().showRemoteBranches)));\r
        }\r
 \r
        protected void addFullText(String wicketId, String text, boolean substituteRegex) {\r
index 485836850547e504aa765fbfe38bdb546b171475..1e6f130c9ed2e69b334a1d33ccdb3a34f4a97adc 100644 (file)
@@ -184,6 +184,9 @@ public abstract class RootPage extends BasePage {
                                // remove named repository parameter\r
                                params.remove("r");\r
 \r
+                               // remove named user parameter\r
+                               params.remove("user");\r
+\r
                                // remove days back parameter if it is the default value\r
                                if (params.containsKey("db")\r
                                                && params.getInt("db") == GitBlit.getInteger(Keys.web.activityDuration, 14)) {\r
@@ -327,6 +330,12 @@ public abstract class RootPage extends BasePage {
 \r
                boolean hasParameter = false;\r
                String projectName = WicketUtils.getProjectName(params);\r
+               String userName = WicketUtils.getUsername(params);\r
+               if (StringUtils.isEmpty(projectName)) {\r
+                       if (!StringUtils.isEmpty(userName)) {\r
+                               projectName = "~" + userName;\r
+                       }\r
+               }\r
                String repositoryName = WicketUtils.getRepositoryName(params);\r
                String set = WicketUtils.getSet(params);\r
                String regex = WicketUtils.getRegEx(params);\r
index 39c7269dcca0f20a99a41b3fd99074e2484a51c7..22b1c1ab4a08c8c509c06bba91e89999c51e6cc3 100644 (file)
@@ -130,7 +130,7 @@ public class SummaryPage extends RepositoryPage {
                add(new Label("otherUrls", StringUtils.flattenStrings(repositoryUrls, "<br/>"))\r
                .setEscapeModelStrings(false));\r
 \r
-               add(new LogPanel("commitsPanel", repositoryName, getRepositoryModel().HEAD, r, numberCommits, 0));\r
+               add(new LogPanel("commitsPanel", repositoryName, getRepositoryModel().HEAD, r, numberCommits, 0, getRepositoryModel().showRemoteBranches));\r
                add(new TagsPanel("tagsPanel", repositoryName, r, numberRefs).hideIfEmpty());\r
                add(new BranchesPanel("branchesPanel", getRepositoryModel(), r, numberRefs, false).hideIfEmpty());\r
 \r
diff --git a/src/com/gitblit/wicket/pages/UserPage.html b/src/com/gitblit/wicket/pages/UserPage.html
new file mode 100644 (file)
index 0000000..5886a3a
--- /dev/null
@@ -0,0 +1,44 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">\r
+<html xmlns="http://www.w3.org/1999/xhtml"  \r
+      xmlns:wicket="http://wicket.apache.org/dtds.data/wicket-xhtml1.3-strict.dtd"  \r
+      xml:lang="en"  \r
+      lang="en"> \r
+\r
+<body>\r
+<wicket:extend>\r
+\r
+       <div class="row">\r
+               <div class="span4">\r
+                       <div wicket:id="gravatar"></div>\r
+                       <div style="text-align: left;">\r
+                               <h2><span wicket:id="userDisplayName"></span></h2>\r
+                               <div><i class="icon-user"></i> <span wicket:id="userUsername"></span></div>\r
+                               <div><i class="icon-envelope"></i><span wicket:id="userEmail"></span></div>\r
+                       </div>\r
+               </div>\r
+               \r
+               <div class="span8">\r
+                       <div class="tabbable">\r
+                               <!-- tab titles -->\r
+                               <ul class="nav nav-tabs">\r
+                                       <li class="active"><a href="#repositories" data-toggle="tab"><wicket:message key="gb.repositories"></wicket:message></a></li>\r
+                               </ul>\r
+       \r
+                               <!-- tab content -->\r
+                               <div class="tab-content">\r
+\r
+                                       <!-- repositories tab -->\r
+                                       <div class="tab-pane active" id="repositories">\r
+                                               <table width="100%">\r
+                                                       <tbody>\r
+                                                               <tr wicket:id="repositoryList"><td style="border-bottom:1px solid #eee;"><span wicket:id="repository"></span></td></tr>\r
+                                                       </tbody>\r
+                                               </table>\r
+                                       </div>\r
+                               </div>\r
+                       </div>\r
+               </div>\r
+       </div>\r
+</wicket:extend>\r
+</body>\r
+</html>
\ No newline at end of file
diff --git a/src/com/gitblit/wicket/pages/UserPage.java b/src/com/gitblit/wicket/pages/UserPage.java
new file mode 100644 (file)
index 0000000..cabefb4
--- /dev/null
@@ -0,0 +1,146 @@
+/*\r
+ * Copyright 2012 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.pages;\r
+\r
+import java.util.ArrayList;\r
+import java.util.Collections;\r
+import java.util.Comparator;\r
+import java.util.List;\r
+\r
+import org.apache.wicket.PageParameters;\r
+import org.apache.wicket.RedirectException;\r
+import org.apache.wicket.markup.html.basic.Label;\r
+import org.apache.wicket.markup.repeater.Item;\r
+import org.apache.wicket.markup.repeater.data.DataView;\r
+import org.apache.wicket.markup.repeater.data.ListDataProvider;\r
+import org.eclipse.jgit.lib.PersonIdent;\r
+\r
+import com.gitblit.GitBlit;\r
+import com.gitblit.Keys;\r
+import com.gitblit.models.ProjectModel;\r
+import com.gitblit.models.RepositoryModel;\r
+import com.gitblit.models.UserModel;\r
+import com.gitblit.utils.StringUtils;\r
+import com.gitblit.wicket.GitBlitWebApp;\r
+import com.gitblit.wicket.GitBlitWebSession;\r
+import com.gitblit.wicket.PageRegistration;\r
+import com.gitblit.wicket.PageRegistration.DropDownMenuItem;\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
+import com.gitblit.wicket.panels.ProjectRepositoryPanel;\r
+\r
+public class UserPage extends RootPage {\r
+       \r
+       List<ProjectModel> projectModels = new ArrayList<ProjectModel>();\r
+\r
+       public UserPage() {\r
+               super();\r
+               throw new RedirectException(GitBlitWebApp.get().getHomePage());\r
+       }\r
+\r
+       public UserPage(PageParameters params) {\r
+               super(params);\r
+               setup(params);\r
+       }\r
+\r
+       @Override\r
+       protected boolean reusePageParameters() {\r
+               return true;\r
+       }\r
+\r
+       private void setup(PageParameters params) {\r
+               setupPage("", "");\r
+               // check to see if we should display a login message\r
+               boolean authenticateView = GitBlit.getBoolean(Keys.web.authenticateViewPages, true);\r
+               if (authenticateView && !GitBlitWebSession.get().isLoggedIn()) {\r
+                       authenticationError("Please login");\r
+                       return;\r
+               }\r
+\r
+               String userName = WicketUtils.getUsername(params);\r
+               if (StringUtils.isEmpty(userName)) {\r
+                       throw new RedirectException(GitBlitWebApp.get().getHomePage());\r
+               }\r
+\r
+               UserModel user = GitBlit.self().getUserModel(userName);\r
+               if (user == null) {\r
+                       // construct a temporary user model\r
+                       user = new UserModel(userName);\r
+               }\r
+               \r
+               String projectName = "~" + userName;\r
+               \r
+               ProjectModel project = GitBlit.self().getProjectModel(projectName);\r
+               if (project == null) {\r
+                       throw new RedirectException(GitBlitWebApp.get().getHomePage());\r
+               }\r
+               \r
+               add(new Label("userDisplayName", user.getDisplayName()));\r
+               add(new Label("userUsername", user.username));\r
+               LinkPanel email = new LinkPanel("userEmail", null, user.emailAddress, "mailto:#");\r
+               email.setRenderBodyOnly(true);\r
+               add(email.setVisible(GitBlit.getBoolean(Keys.web.showEmailAddresses, true) && !StringUtils.isEmpty(user.emailAddress)));\r
+               \r
+               PersonIdent person = new PersonIdent(user.getDisplayName(), user.emailAddress);\r
+               add(new GravatarImage("gravatar", person, 210));\r
+               \r
+               List<RepositoryModel> repositories = getRepositories(params);\r
+               \r
+               Collections.sort(repositories, new Comparator<RepositoryModel>() {\r
+                       @Override\r
+                       public int compare(RepositoryModel o1, RepositoryModel o2) {\r
+                               // reverse-chronological sort\r
+                               return o2.lastChange.compareTo(o1.lastChange);\r
+                       }\r
+               });\r
+\r
+               final ListDataProvider<RepositoryModel> dp = new ListDataProvider<RepositoryModel>(repositories);\r
+               DataView<RepositoryModel> dataView = new DataView<RepositoryModel>("repositoryList", dp) {\r
+                       private static final long serialVersionUID = 1L;\r
+\r
+                       public void populateItem(final Item<RepositoryModel> item) {\r
+                               final RepositoryModel entry = item.getModelObject();\r
+                               \r
+                               ProjectRepositoryPanel row = new ProjectRepositoryPanel("repository", \r
+                                               getLocalizer(), this, showAdmin, entry, getAccessRestrictions());\r
+                               item.add(row);\r
+                       }\r
+               };\r
+               add(dataView);\r
+       }\r
+\r
+       @Override\r
+       protected void addDropDownMenus(List<PageRegistration> pages) {\r
+               PageParameters params = getPageParameters();\r
+\r
+               DropDownMenuRegistration menu = new DropDownMenuRegistration("gb.filters",\r
+                               UserPage.class);\r
+               // preserve time filter option on repository choices\r
+               menu.menuItems.addAll(getRepositoryFilterItems(params));\r
+\r
+               // preserve repository filter option on time choices\r
+               menu.menuItems.addAll(getTimeFilterItems(params));\r
+\r
+               if (menu.menuItems.size() > 0) {\r
+                       // Reset Filter\r
+                       menu.menuItems.add(new DropDownMenuItem(getString("gb.reset"), null, null));\r
+               }\r
+\r
+               pages.add(menu);\r
+       }\r
+}\r
index b1c7b65c5c046ae6912b30849200a9649acf9db3..f26a90276205ae8029a7827ab78a02863eeb83cd 100644 (file)
@@ -48,7 +48,7 @@ public class GravatarImage extends Panel {
        public GravatarImage(String id, PersonIdent person, int width) {\r
                super(id);\r
 \r
-               String email = person.getEmailAddress().toLowerCase();\r
+               String email = person.getEmailAddress() == null ? person.getName().toLowerCase() : person.getEmailAddress().toLowerCase();\r
                String hash = StringUtils.getMD5(email);\r
                Link<Void> link = new BookmarkablePageLink<Void>("link", GravatarProfilePage.class,\r
                                WicketUtils.newObjectParameter(hash));\r
index befd7011063c257db0ffdf8fc45312b9e031e2ae..14aed9121781755e453537a14de820d70e2c893e 100644 (file)
@@ -54,7 +54,7 @@ public class HistoryPanel extends BasePanel {
        private boolean hasMore;\r
 \r
        public HistoryPanel(String wicketId, final String repositoryName, final String objectId,\r
-                       final String path, Repository r, int limit, int pageOffset) {\r
+                       final String path, Repository r, int limit, int pageOffset, boolean showRemoteRefs) {\r
                super(wicketId);\r
                boolean pageResults = limit <= 0;\r
                int itemsPerPage = GitBlit.getInteger(Keys.web.itemsPerPage, 50);\r
@@ -74,7 +74,7 @@ public class HistoryPanel extends BasePanel {
                }\r
                final boolean isTree = matchingPath == null ? true : matchingPath.isTree();\r
 \r
-               final Map<ObjectId, List<RefModel>> allRefs = JGitUtils.getAllRefs(r);\r
+               final Map<ObjectId, List<RefModel>> allRefs = JGitUtils.getAllRefs(r, showRemoteRefs);\r
                List<RevCommit> commits;\r
                if (pageResults) {\r
                        // Paging result set\r
index f441ba57aaf611448dc2532e4db3f44c27748677..a31c3dfaa1f532c91352c04f2421c447b4da4a95 100644 (file)
@@ -49,7 +49,7 @@ public class LogPanel extends BasePanel {
        private boolean hasMore;\r
 \r
        public LogPanel(String wicketId, final String repositoryName, final String objectId,\r
-                       Repository r, int limit, int pageOffset) {\r
+                       Repository r, int limit, int pageOffset, boolean showRemoteRefs) {\r
                super(wicketId);\r
                boolean pageResults = limit <= 0;\r
                int itemsPerPage = GitBlit.getInteger(Keys.web.itemsPerPage, 50);\r
@@ -57,7 +57,7 @@ public class LogPanel extends BasePanel {
                        itemsPerPage = 50;\r
                }\r
 \r
-               final Map<ObjectId, List<RefModel>> allRefs = JGitUtils.getAllRefs(r);\r
+               final Map<ObjectId, List<RefModel>> allRefs = JGitUtils.getAllRefs(r, showRemoteRefs);\r
                List<RevCommit> commits;\r
                if (pageResults) {\r
                        // Paging result set\r
diff --git a/src/com/gitblit/wicket/panels/ProjectRepositoryPanel.html b/src/com/gitblit/wicket/panels/ProjectRepositoryPanel.html
new file mode 100644 (file)
index 0000000..4678153
--- /dev/null
@@ -0,0 +1,79 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">\r
+<html xmlns="http://www.w3.org/1999/xhtml"  \r
+      xmlns:wicket="http://wicket.apache.org/dtds.data/wicket-xhtml1.3-strict.dtd"  \r
+      xml:lang="en"  \r
+      lang="en"> \r
+\r
+<wicket:panel>\r
+       <wicket:fragment wicket:id="repositoryAdminLinks">\r
+               <span class="link">\r
+                       <a wicket:id="tree"><wicket:message key="gb.tree"></wicket:message></a>\r
+                       | <a wicket:id="log"><wicket:message key="gb.log"></wicket:message></a>\r
+                       | <a wicket:id="editRepository"><wicket:message key="gb.edit">[edit]</wicket:message></a>\r
+                       | <a wicket:id="deleteRepository"><wicket:message key="gb.delete">[delete]</wicket:message></a>\r
+               </span>\r
+       </wicket:fragment>\r
+\r
+       <wicket:fragment wicket:id="repositoryOwnerLinks">\r
+               <span class="link">\r
+                       <a wicket:id="tree"><wicket:message key="gb.tree"></wicket:message></a>\r
+                       | <a wicket:id="log"><wicket:message key="gb.log"></wicket:message></a>\r
+                       | <a wicket:id="editRepository"><wicket:message key="gb.edit">[edit]</wicket:message></a>\r
+               </span>\r
+       </wicket:fragment>\r
+\r
+       <wicket:fragment wicket:id="repositoryUserLinks">\r
+               <span class="link">\r
+                       <a wicket:id="tree"><wicket:message key="gb.tree"></wicket:message></a>\r
+                       | <a wicket:id="log"><wicket:message key="gb.log"></wicket:message></a>\r
+               </span>\r
+       </wicket:fragment>\r
+\r
+       <wicket:fragment wicket:id="originFragment">\r
+               <p class="originRepository" style="margin-left:20px;" ><wicket:message key="gb.forkedFrom">[forked from]</wicket:message> <span wicket:id="originRepository">[origin repository]</span></p>\r
+       </wicket:fragment>\r
+\r
+       <div>\r
+               <div style="padding-top:15px;padding-bottom:15px;margin-right:15px;">\r
+                       <div class="pull-right" style="text-align:right;padding-right:15px;">\r
+                               <span wicket:id="repositoryLinks"></span>\r
+                               <div>\r
+                                       <img class="inlineIcon" wicket:id="frozenIcon" />\r
+                                       <img class="inlineIcon" wicket:id="federatedIcon" />\r
+                                                       \r
+                                       <a style="text-decoration: none;" wicket:id="tickets" wicket:message="title:gb.tickets">\r
+                                               <img style="border:0px;vertical-align:middle;" src="bug_16x16.png"></img>\r
+                                       </a>\r
+                                       <a style="text-decoration: none;" wicket:id="docs" wicket:message="title:gb.docs">\r
+                                               <img style="border:0px;vertical-align:middle;" src="book_16x16.png"></img>\r
+                                       </a>\r
+                                       <a style="text-decoration: none;" wicket:id="syndication" wicket:message="title:gb.feed">\r
+                                               <img style="border:0px;vertical-align:middle;" src="feed_16x16.png"></img>\r
+                                       </a>\r
+                               </div>\r
+                               <span style="color: #999;font-style:italic;font-size:0.8em;" wicket:id="repositoryOwner">[owner]</span>\r
+                       </div>  \r
+                       \r
+                       <div class="pageTitle" style="border:0px;">\r
+                               <div>\r
+                                       <span class="repositorySwatch" wicket:id="repositorySwatch"></span>\r
+                                       <span class="repository" style="padding-left:3px;color:black;" wicket:id="repositoryName">[repository name]</span>\r
+                                       <img class="inlineIcon" style="vertical-align:baseline" wicket:id="accessRestrictionIcon" />\r
+                               </div>\r
+                               <span wicket:id="originRepository">[origin repository]</span>\r
+                       </div>\r
+                       \r
+                       <div style="padding-left:20px;">\r
+                               <div style="padding-bottom:10px" wicket:id="repositoryDescription">[repository description]</div>\r
+\r
+                       <div style="color: #999;">\r
+                                       <wicket:message key="gb.lastChange">[last change]</wicket:message> <span wicket:id="repositoryLastChange">[last change]</span>,\r
+                                       <span style="font-size:0.8em;" wicket:id="repositorySize">[repository size]</span>\r
+                               </div>\r
+        \r
+                               <div class="hidden-phone hidden-tablet" wicket:id="repositoryCloneUrl">[repository clone url]</div>\r
+                       </div>\r
+               </div>\r
+       </div>\r
+</wicket:panel>\r
+</html>
\ No newline at end of file
diff --git a/src/com/gitblit/wicket/panels/ProjectRepositoryPanel.java b/src/com/gitblit/wicket/panels/ProjectRepositoryPanel.java
new file mode 100644 (file)
index 0000000..f7deaf1
--- /dev/null
@@ -0,0 +1,199 @@
+/*\r
+ * Copyright 2012 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.panels;\r
+\r
+import java.text.MessageFormat;\r
+import java.util.ArrayList;\r
+import java.util.List;\r
+import java.util.Map;\r
+\r
+import org.apache.wicket.Component;\r
+import org.apache.wicket.Localizer;\r
+import org.apache.wicket.PageParameters;\r
+import org.apache.wicket.markup.html.basic.Label;\r
+import org.apache.wicket.markup.html.link.BookmarkablePageLink;\r
+import org.apache.wicket.markup.html.link.ExternalLink;\r
+import org.apache.wicket.markup.html.link.Link;\r
+import org.apache.wicket.markup.html.panel.Fragment;\r
+\r
+import com.gitblit.Constants.AccessRestrictionType;\r
+import com.gitblit.GitBlit;\r
+import com.gitblit.Keys;\r
+import com.gitblit.SyndicationServlet;\r
+import com.gitblit.models.RepositoryModel;\r
+import com.gitblit.models.UserModel;\r
+import com.gitblit.utils.ArrayUtils;\r
+import com.gitblit.utils.StringUtils;\r
+import com.gitblit.wicket.GitBlitWebSession;\r
+import com.gitblit.wicket.WicketUtils;\r
+import com.gitblit.wicket.pages.BasePage;\r
+import com.gitblit.wicket.pages.DocsPage;\r
+import com.gitblit.wicket.pages.EditRepositoryPage;\r
+import com.gitblit.wicket.pages.LogPage;\r
+import com.gitblit.wicket.pages.SummaryPage;\r
+import com.gitblit.wicket.pages.TicketsPage;\r
+import com.gitblit.wicket.pages.TreePage;\r
+\r
+public class ProjectRepositoryPanel extends BasePanel {\r
+\r
+       private static final long serialVersionUID = 1L;\r
+\r
+       public ProjectRepositoryPanel(String wicketId, Localizer localizer, Component owner,\r
+                       final boolean isAdmin, final RepositoryModel entry,\r
+                       final Map<AccessRestrictionType, String> accessRestrictions) {\r
+               super(wicketId);\r
+\r
+               final boolean showSwatch = GitBlit.getBoolean(Keys.web.repositoryListSwatches, true);\r
+               final boolean gitServlet = GitBlit.getBoolean(Keys.git.enableGitServlet, true);\r
+               final boolean showSize = GitBlit.getBoolean(Keys.web.showRepositorySizes, true);\r
+\r
+               // repository swatch\r
+               Component swatch;\r
+               if (entry.isBare) {\r
+                       swatch = new Label("repositorySwatch", "&nbsp;").setEscapeModelStrings(false);\r
+               } else {\r
+                       swatch = new Label("repositorySwatch", "!");\r
+                       WicketUtils.setHtmlTooltip(swatch, localizer.getString("gb.workingCopyWarning", owner));\r
+               }\r
+               WicketUtils.setCssBackground(swatch, entry.toString());\r
+               add(swatch);\r
+               swatch.setVisible(showSwatch);\r
+\r
+               PageParameters pp = WicketUtils.newRepositoryParameter(entry.name);\r
+               add(new LinkPanel("repositoryName", "list", StringUtils.getRelativePath(entry.projectPath,\r
+                               StringUtils.stripDotGit(entry.name)), SummaryPage.class, pp));\r
+               add(new Label("repositoryDescription", entry.description).setVisible(!StringUtils\r
+                               .isEmpty(entry.description)));\r
+\r
+               if (StringUtils.isEmpty(entry.originRepository)) {\r
+                       add(new Label("originRepository").setVisible(false));\r
+               } else {\r
+                       Fragment forkFrag = new Fragment("originRepository", "originFragment", this);\r
+                       forkFrag.add(new LinkPanel("originRepository", null, StringUtils.stripDotGit(entry.originRepository), \r
+                                       SummaryPage.class, WicketUtils.newRepositoryParameter(entry.originRepository)));\r
+                       add(forkFrag);\r
+               }\r
+\r
+               add(new BookmarkablePageLink<Void>("tickets", TicketsPage.class, pp).setVisible(entry.useTickets));\r
+               add(new BookmarkablePageLink<Void>("docs", DocsPage.class, pp).setVisible(entry.useDocs));\r
+\r
+               if (entry.isFrozen) {\r
+                       add(WicketUtils.newImage("frozenIcon", "cold_16x16.png", localizer.getString("gb.isFrozen", owner)));\r
+               } else {\r
+                       add(WicketUtils.newClearPixel("frozenIcon").setVisible(false));\r
+               }\r
+\r
+               if (entry.isFederated) {\r
+                       add(WicketUtils.newImage("federatedIcon", "federated_16x16.png", localizer.getString("gb.isFederated", owner)));\r
+               } else {\r
+                       add(WicketUtils.newClearPixel("federatedIcon").setVisible(false));\r
+               }\r
+               switch (entry.accessRestriction) {\r
+               case NONE:\r
+                       add(WicketUtils.newBlankImage("accessRestrictionIcon").setVisible(false));\r
+                       break;\r
+               case PUSH:\r
+                       add(WicketUtils.newImage("accessRestrictionIcon", "lock_go_16x16.png",\r
+                                       accessRestrictions.get(entry.accessRestriction)));\r
+                       break;\r
+               case CLONE:\r
+                       add(WicketUtils.newImage("accessRestrictionIcon", "lock_pull_16x16.png",\r
+                                       accessRestrictions.get(entry.accessRestriction)));\r
+                       break;\r
+               case VIEW:\r
+                       add(WicketUtils.newImage("accessRestrictionIcon", "shield_16x16.png",\r
+                                       accessRestrictions.get(entry.accessRestriction)));\r
+                       break;\r
+               default:\r
+                       add(WicketUtils.newBlankImage("accessRestrictionIcon"));\r
+               }\r
+\r
+               add(new Label("repositoryOwner", StringUtils.isEmpty(entry.owner) ? "" : (entry.owner + " ("\r
+                               + localizer.getString("gb.owner", owner) + ")")));\r
+\r
+               UserModel user = GitBlitWebSession.get().getUser();\r
+               Fragment repositoryLinks;\r
+               boolean showOwner = user != null && user.username.equalsIgnoreCase(entry.owner);\r
+               // owner of personal repository gets admin powers\r
+               boolean showAdmin = isAdmin || entry.isUsersPersonalRepository(user.username);\r
+\r
+               if (showAdmin || showOwner) {\r
+                       repositoryLinks = new Fragment("repositoryLinks", showAdmin ? "repositoryAdminLinks"\r
+                                       : "repositoryOwnerLinks", this);\r
+                       repositoryLinks.add(new BookmarkablePageLink<Void>("editRepository", EditRepositoryPage.class,\r
+                                       WicketUtils.newRepositoryParameter(entry.name)));\r
+                       if (showAdmin) {\r
+                               Link<Void> deleteLink = new Link<Void>("deleteRepository") {\r
+\r
+                                       private static final long serialVersionUID = 1L;\r
+\r
+                                       @Override\r
+                                       public void onClick() {\r
+                                               if (GitBlit.self().deleteRepositoryModel(entry)) {\r
+                                                       info(MessageFormat.format(getString("gb.repositoryDeleted"), entry));\r
+                                                       // TODO dp.remove(entry);\r
+                                               } else {\r
+                                                       error(MessageFormat.format(getString("gb.repositoryDeleteFailed"), entry));\r
+                                               }\r
+                                       }\r
+                               };\r
+                               deleteLink.add(new JavascriptEventConfirmation("onclick", MessageFormat.format(\r
+                                               localizer.getString("gb.deleteRepository", owner), entry)));\r
+                               repositoryLinks.add(deleteLink);\r
+                       }\r
+               } else {\r
+                       repositoryLinks = new Fragment("repositoryLinks", "repositoryUserLinks", this);\r
+               }\r
+\r
+               repositoryLinks.add(new BookmarkablePageLink<Void>("tree", TreePage.class, WicketUtils\r
+                               .newRepositoryParameter(entry.name)).setEnabled(entry.hasCommits));\r
+\r
+               repositoryLinks.add(new BookmarkablePageLink<Void>("log", LogPage.class, WicketUtils\r
+                               .newRepositoryParameter(entry.name)).setEnabled(entry.hasCommits));\r
+\r
+               add(repositoryLinks);\r
+\r
+               String lastChange;\r
+               if (entry.lastChange.getTime() == 0) {\r
+                       lastChange = "--";\r
+               } else {\r
+                       lastChange = getTimeUtils().timeAgo(entry.lastChange);\r
+               }\r
+               Label lastChangeLabel = new Label("repositoryLastChange", lastChange);\r
+               add(lastChangeLabel);\r
+               WicketUtils.setCssClass(lastChangeLabel, getTimeUtils().timeAgoCss(entry.lastChange));\r
+\r
+               if (entry.hasCommits) {\r
+                       // Existing repository\r
+                       add(new Label("repositorySize", entry.size).setVisible(showSize));\r
+               } else {\r
+                       // New repository\r
+                       add(new Label("repositorySize", localizer.getString("gb.empty", owner)).setEscapeModelStrings(false));\r
+               }\r
+\r
+               add(new ExternalLink("syndication", SyndicationServlet.asLink("", entry.name, null, 0)));\r
+\r
+               List<String> repositoryUrls = new ArrayList<String>();\r
+               if (gitServlet) {\r
+                       // add the Gitblit repository url\r
+                       repositoryUrls.add(BasePage.getRepositoryUrl(entry));\r
+               }\r
+               repositoryUrls.addAll(GitBlit.self().getOtherCloneUrls(entry.name));\r
+\r
+               String primaryUrl = ArrayUtils.isEmpty(repositoryUrls) ? "" : repositoryUrls.remove(0);\r
+               add(new RepositoryUrlPanel("repositoryCloneUrl", primaryUrl));\r
+       }\r
+}\r
index 5da43e00ede82053b41b4ae5069ef5fe437f136a..6c19fc51585b15255c31f8bc059475d9562864ad 100644 (file)
@@ -72,7 +72,7 @@
        \r
        <wicket:fragment wicket:id="groupRepositoryRow">\r
         <td colspan="1"><span wicket:id="groupName">[group name]</span></td>\r
-        <td colspan="6"><span class="hidden-phone" style="font-weight:normal;color:#666;" wicket:id="groupDescription">[description]</span></td>\r
+        <td colspan="6" style="padding: 2px;"><span class="hidden-phone" style="font-weight:normal;color:#666;" wicket:id="groupDescription">[description]</span></td>\r
        </wicket:fragment>\r
                \r
        <wicket:fragment wicket:id="repositoryRow">\r
index a113e00686125ccde0459b431ba4d39a21ccd69d..0855780b4099103ab35d702c1e11bc8f6a629dbd 100644 (file)
@@ -58,6 +58,7 @@ import com.gitblit.wicket.pages.EmptyRepositoryPage;
 import com.gitblit.wicket.pages.ProjectPage;\r
 import com.gitblit.wicket.pages.RepositoriesPage;\r
 import com.gitblit.wicket.pages.SummaryPage;\r
+import com.gitblit.wicket.pages.UserPage;\r
 \r
 public class RepositoriesPanel extends BasePanel {\r
 \r
@@ -116,7 +117,7 @@ public class RepositoriesPanel extends BasePanel {
                        }\r
                                                \r
                        Map<String, ProjectModel> projects = new HashMap<String, ProjectModel>();\r
-                       for (ProjectModel project : GitBlit.self().getProjectModels(user)) {\r
+                       for (ProjectModel project : GitBlit.self().getProjectModels(user, true)) {\r
                                projects.put(project.name, project);\r
                        }\r
                        List<RepositoryModel> groupedModels = new ArrayList<RepositoryModel>();\r
@@ -138,7 +139,7 @@ public class RepositoriesPanel extends BasePanel {
 \r
                final String baseUrl = WicketUtils.getGitblitURL(getRequest());\r
                final boolean showSwatch = GitBlit.getBoolean(Keys.web.repositoryListSwatches, true);\r
-\r
+               \r
                DataView<RepositoryModel> dataView = new DataView<RepositoryModel>("row", dp) {\r
                        private static final long serialVersionUID = 1L;\r
                        int counter;\r
@@ -156,8 +157,19 @@ public class RepositoriesPanel extends BasePanel {
                                        currGroupName = entry.name;\r
                                        Fragment row = new Fragment("rowContent", "groupRepositoryRow", this);\r
                                        item.add(row);\r
-                                       row.add(new LinkPanel("groupName", null, entry.toString(), ProjectPage.class, WicketUtils.newProjectParameter(entry.name)));\r
-                                       row.add(new Label("groupDescription", entry.description == null ? "":entry.description));\r
+                                       \r
+                                       String name = entry.toString();\r
+                                       if (name.charAt(0) == '~') {\r
+                                               // user page\r
+                                               String username = name.substring(1);\r
+                                               UserModel user = GitBlit.self().getUserModel(username);\r
+                                               row.add(new LinkPanel("groupName", null, user == null ? username : user.getDisplayName(), UserPage.class, WicketUtils.newUsernameParameter(username)));\r
+                                               row.add(new Label("groupDescription", getString("gb.personalRepositories")));\r
+                                       } else {\r
+                                               // project page\r
+                                               row.add(new LinkPanel("groupName", null, name, ProjectPage.class, WicketUtils.newProjectParameter(name)));\r
+                                               row.add(new Label("groupDescription", entry.description == null ? "":entry.description));\r
+                                       }\r
                                        WicketUtils.setCssClass(item, "group");\r
                                        // reset counter so that first row is light background\r
                                        counter = 0;\r
@@ -272,7 +284,8 @@ public class RepositoriesPanel extends BasePanel {
                                WicketUtils.setCssClass(lastChangeLabel, getTimeUtils().timeAgoCss(entry.lastChange));\r
 \r
                                boolean showOwner = user != null && user.username.equalsIgnoreCase(entry.owner);\r
-                               if (showAdmin) {\r
+                               boolean myPersonalRepository = showOwner && entry.isUsersPersonalRepository(user.username);\r
+                               if (showAdmin || myPersonalRepository) {\r
                                        Fragment repositoryLinks = new Fragment("repositoryLinks",\r
                                                        "repositoryAdminLinks", this);\r
                                        repositoryLinks.add(new BookmarkablePageLink<Void>("editRepository",\r
index eab3aea1edcaf8834ef60b512192370e256c7c4b..9d38ab09a9bbba380aa89d8d3c6b313b3a117cdd 100644 (file)
@@ -47,7 +47,8 @@ public class SearchPanel extends BasePanel {
        private boolean hasMore;\r
 \r
        public SearchPanel(String wicketId, final String repositoryName, final String objectId,\r
-                       final String value, Constants.SearchType searchType, Repository r, int limit, int pageOffset) {\r
+                       final String value, Constants.SearchType searchType, Repository r, int limit, int pageOffset,\r
+                       boolean showRemoteRefs) {\r
                super(wicketId);\r
                boolean pageResults = limit <= 0;\r
                int itemsPerPage = GitBlit.getInteger(Keys.web.itemsPerPage, 50);\r
@@ -57,7 +58,7 @@ public class SearchPanel extends BasePanel {
 \r
                RevCommit commit = JGitUtils.getCommit(r, objectId);\r
 \r
-               final Map<ObjectId, List<RefModel>> allRefs = JGitUtils.getAllRefs(r);\r
+               final Map<ObjectId, List<RefModel>> allRefs = JGitUtils.getAllRefs(r, showRemoteRefs);\r
                List<RevCommit> commits;\r
                if (pageResults) {\r
                        // Paging result set\r