From eb1405f736f2f98e14215774dd53eea9b9a77017 Mon Sep 17 00:00:00 2001 From: James Moger Date: Mon, 1 Oct 2012 20:45:19 -0400 Subject: [PATCH] Show fork links according to user permissions. Improve fork detection. --- src/com/gitblit/GitBlit.java | 76 ++++++++++++ src/com/gitblit/models/UserModel.java | 10 ++ src/com/gitblit/wicket/pages/ForksPage.java | 31 +++-- .../gitblit/wicket/pages/RepositoryPage.java | 110 ++++++++++-------- 4 files changed, 168 insertions(+), 59 deletions(-) diff --git a/src/com/gitblit/GitBlit.java b/src/com/gitblit/GitBlit.java index 699bbacb..f86c66a3 100644 --- a/src/com/gitblit/GitBlit.java +++ b/src/com/gitblit/GitBlit.java @@ -32,6 +32,7 @@ import java.util.Collection; import java.util.Collections; import java.util.Date; import java.util.HashMap; +import java.util.HashSet; import java.util.LinkedHashMap; import java.util.LinkedHashSet; import java.util.List; @@ -1325,6 +1326,81 @@ public class GitBlit implements ServletContextListener { r.close(); return true; } + + /** + * Determines if the specified user has a fork of the specified origin + * repository. + * + * @param username + * @param origin + * @return true the if the user has a fork + */ + public boolean hasFork(String username, String origin) { + return getFork(username, origin) != null; + } + + /** + * Gets the name of a user's fork of the specified origin + * repository. + * + * @param username + * @param origin + * @return the name of the user's fork, null otherwise + */ + public String getFork(String username, String origin) { + String userProject = "~" + username.toLowerCase(); + if (settings.getBoolean(Keys.git.cacheRepositoryList, true)) { + String userPath = userProject + "/"; + + // collect all origin nodes in fork network + Set roots = new HashSet(); + roots.add(origin); + RepositoryModel originModel = repositoryListCache.get(origin); + while (originModel != null) { + if (!ArrayUtils.isEmpty(originModel.forks)) { + for (String fork : originModel.forks) { + if (!fork.startsWith(userPath)) { + roots.add(fork); + } + } + } + + if (originModel.originRepository != null) { + roots.add(originModel.originRepository); + originModel = repositoryListCache.get(originModel.originRepository); + } else { + // break + originModel = null; + } + } + + for (String repository : repositoryListCache.keySet()) { + if (repository.toLowerCase().startsWith(userPath)) { + RepositoryModel model = repositoryListCache.get(repository); + if (!StringUtils.isEmpty(model.originRepository)) { + if (roots.contains(model.originRepository)) { + // user has a fork in this graph + return model.name; + } + } + } + } + } else { + // not caching + ProjectModel project = getProjectModel(userProject); + for (String repository : project.repositories) { + if (repository.toLowerCase().startsWith(userProject)) { + RepositoryModel model = repositoryListCache.get(repository); + if (model.originRepository.equalsIgnoreCase(origin)) { + // user has a fork + return model.name; + } + } + } + } + // user does not have a fork + return null; + } /** * Returns the size in bytes of the repository. Gitblit caches the diff --git a/src/com/gitblit/models/UserModel.java b/src/com/gitblit/models/UserModel.java index 0ede8787..25787a15 100644 --- a/src/com/gitblit/models/UserModel.java +++ b/src/com/gitblit/models/UserModel.java @@ -86,6 +86,16 @@ public class UserModel implements Principal, Serializable, Comparable return false; } + public boolean canViewRepository(RepositoryModel repository) { + if (canAdmin) { + return true; + } + if (repository.accessRestriction.atLeast(AccessRestrictionType.VIEW)) { + return canAccessRepository(repository); + } + return true; + } + public boolean canForkRepository(RepositoryModel repository) { if (canAdmin) { return true; diff --git a/src/com/gitblit/wicket/pages/ForksPage.java b/src/com/gitblit/wicket/pages/ForksPage.java index 7b8235b0..54c2c82a 100644 --- a/src/com/gitblit/wicket/pages/ForksPage.java +++ b/src/com/gitblit/wicket/pages/ForksPage.java @@ -42,26 +42,32 @@ public class ForksPage extends RepositoryPage { public ForksPage(PageParameters params) { super(params); + UserModel user = GitBlitWebSession.get().getUser(); RepositoryModel model = getRepositoryModel(); - RepositoryModel origin; + RepositoryModel origin = model; List list; if (ArrayUtils.isEmpty(model.forks)) { - // origin repository has forks - origin = GitBlit.self().getRepositoryModel(model.originRepository); - list = new ArrayList(origin.forks); + if (!StringUtils.isEmpty(model.originRepository)) { + // try origin repository + origin = GitBlit.self().getRepositoryModel(model.originRepository); + } + if (origin == null || origin.forks == null) { + list = new ArrayList(); + } else { + list = new ArrayList(origin.forks); + } } else { // this repository has forks - origin = model; list = new ArrayList(model.forks); } if (origin.isPersonalRepository()) { // personal repository - UserModel user = GitBlit.self().getUserModel(origin.projectPath.substring(1)); - PersonIdent ident = new PersonIdent(user.getDisplayName(), user.emailAddress); + UserModel originUser = GitBlit.self().getUserModel(origin.projectPath.substring(1)); + PersonIdent ident = new PersonIdent(originUser.getDisplayName(), originUser.emailAddress); add(new GravatarImage("forkSourceAvatar", ident, 20)); add(new Label("forkSourceSwatch").setVisible(false)); - add(new LinkPanel("forkSourceProject", null, user.getDisplayName(), UserPage.class, WicketUtils.newUsernameParameter(user.username))); + add(new LinkPanel("forkSourceProject", null, originUser.getDisplayName(), UserPage.class, WicketUtils.newUsernameParameter(originUser.username))); } else { // standard repository add(new GravatarImage("forkSourceAvatar", new PersonIdent("", ""), 20).setVisible(false)); @@ -85,10 +91,15 @@ public class ForksPage extends RepositoryPage { } String source = StringUtils.getLastPathElement(origin.name); - add(new LinkPanel("forkSource", null, StringUtils.stripDotGit(source), SummaryPage.class, WicketUtils.newRepositoryParameter(origin.name))); + if (user != null && user.canViewRepository(origin)) { + // user can view the origin + add(new LinkPanel("forkSource", null, StringUtils.stripDotGit(source), SummaryPage.class, WicketUtils.newRepositoryParameter(origin.name))); + } else { + // user can not view the origin + add(new Label("forkSource", StringUtils.stripDotGit(source))); + } // only display user-accessible forks - UserModel user = GitBlitWebSession.get().getUser(); List forks = new ArrayList(); for (String aFork : list) { RepositoryModel fork = GitBlit.self().getRepositoryModel(user, aFork); diff --git a/src/com/gitblit/wicket/pages/RepositoryPage.java b/src/com/gitblit/wicket/pages/RepositoryPage.java index a85d21e1..8ca2b335 100644 --- a/src/com/gitblit/wicket/pages/RepositoryPage.java +++ b/src/com/gitblit/wicket/pages/RepositoryPage.java @@ -136,22 +136,12 @@ public abstract class RepositoryPage extends BasePage { pages.put("branches", new PageRegistration("gb.branches", BranchesPage.class, params)); pages.put("tags", new PageRegistration("gb.tags", TagsPage.class, params)); pages.put("tree", new PageRegistration("gb.tree", TreePage.class, params)); + pages.put("forks", new PageRegistration("gb.forks", ForksPage.class, params)); // conditional links Repository r = getRepository(); RepositoryModel model = getRepositoryModel(); - // forks list button - if (StringUtils.isEmpty(model.originRepository)) { - if (!ArrayUtils.isEmpty(model.forks)) { - // this origin repository has forks - pages.put("forks", new PageRegistration("gb.forks", ForksPage.class, params)); - } - } else { - // this is a fork of another repository - pages.put("forks", new PageRegistration("gb.forks", ForksPage.class, params)); - } - // per-repository extra page links if (model.useTickets && TicgitUtils.getTicketsBranch(r) != null) { pages.put("tickets", new PageRegistration("gb.tickets", TicketsPage.class, params)); @@ -204,15 +194,29 @@ public abstract class RepositoryPage extends BasePage { WicketUtils.newRepositoryParameter(repositoryName))); add(new Label("pageName", pageName).setRenderBodyOnly(true)); + UserModel user = GitBlitWebSession.get().getUser(); + // indicate origin repository RepositoryModel model = getRepositoryModel(); if (StringUtils.isEmpty(model.originRepository)) { add(new Label("originRepository").setVisible(false)); } else { - Fragment forkFrag = new Fragment("originRepository", "originFragment", this); - forkFrag.add(new LinkPanel("originRepository", null, StringUtils.stripDotGit(model.originRepository), - SummaryPage.class, WicketUtils.newRepositoryParameter(model.originRepository))); - add(forkFrag); + RepositoryModel origin = GitBlit.self().getRepositoryModel(model.originRepository); + if (origin == null) { + // no origin repository + add(new Label("originRepository").setVisible(false)); + } else if (!user.canViewRepository(origin)) { + // show origin repository without link + Fragment forkFrag = new Fragment("originRepository", "originFragment", this); + forkFrag.add(new Label("originRepository", StringUtils.stripDotGit(model.originRepository))); + add(forkFrag); + } else { + // link to origin repository + Fragment forkFrag = new Fragment("originRepository", "originFragment", this); + forkFrag.add(new LinkPanel("originRepository", null, StringUtils.stripDotGit(model.originRepository), + SummaryPage.class, WicketUtils.newRepositoryParameter(model.originRepository))); + add(forkFrag); + } } if (getRepositoryModel().isBare) { @@ -224,33 +228,57 @@ public abstract class RepositoryPage extends BasePage { wc.add(lbl); add(wc); } - - if (getRepositoryModel().allowForks) { + + // fork controls + if (user == null) { + // must be logged-in to fork, hide all fork controls + add(new ExternalLink("forkLink", "").setVisible(false)); + add(new ExternalLink("myForkLink", "").setVisible(false)); add(new Label("forksProhibitedIndicator").setVisible(false)); } else { - Fragment wc = new Fragment("forksProhibitedIndicator", "forksProhibitedFragment", this); - Label lbl = new Label("forksProhibited", getString("gb.forksProhibited")); - WicketUtils.setHtmlTooltip(lbl, getString("gb.forksProhibitedWarning")); - wc.add(lbl); - add(wc); - } - - UserModel user = GitBlitWebSession.get().getUser(); - - // fork button - if (user != null) { - final String clonedRepo = MessageFormat.format("~{0}/{1}.git", user.username, StringUtils.stripDotGit(StringUtils.getLastPathElement(model.name))); - boolean hasClone = GitBlit.self().hasRepository(clonedRepo) && !getRepositoryModel().name.equals(clonedRepo); - if (user.canForkRepository(model) && !hasClone) { + String fork = GitBlit.self().getFork(user.username, model.name); + boolean hasFork = fork != null; + boolean canFork = user.canForkRepository(model); + + if (hasFork || !canFork) { + // user not allowed to fork or fork already exists or repo forbids forking + add(new ExternalLink("forkLink", "").setVisible(false)); + + if (user.canFork && !model.allowForks) { + // show forks prohibited indicator + Fragment wc = new Fragment("forksProhibitedIndicator", "forksProhibitedFragment", this); + Label lbl = new Label("forksProhibited", getString("gb.forksProhibited")); + WicketUtils.setHtmlTooltip(lbl, getString("gb.forksProhibitedWarning")); + wc.add(lbl); + add(wc); + } else { + // can not fork, no need for forks prohibited indicator + add(new Label("forksProhibitedIndicator").setVisible(false)); + } + + if (hasFork && !fork.equals(model.name)) { + // user has fork, view my fork link + String url = getRequestCycle().urlFor(SummaryPage.class, WicketUtils.newRepositoryParameter(fork)).toString(); + add(new ExternalLink("myForkLink", url)); + } else { + // no fork, hide view my fork link + add(new ExternalLink("myForkLink", "").setVisible(false)); + } + } else if (canFork) { + // can fork and we do not have one + add(new Label("forksProhibitedIndicator").setVisible(false)); + add(new ExternalLink("myForkLink", "").setVisible(false)); Link forkLink = new Link("forkLink") { private static final long serialVersionUID = 1L; @Override public void onClick() { + UserModel user = GitBlitWebSession.get().getUser(); RepositoryModel model = getRepositoryModel(); + String asFork = MessageFormat.format("~{0}/{1}.git", user.username, StringUtils.stripDotGit(StringUtils.getLastPathElement(model.name))); if (GitBlit.self().fork(model, GitBlitWebSession.get().getUser())) { - throw new RedirectException(SummaryPage.class, WicketUtils.newRepositoryParameter(clonedRepo)); + throw new RedirectException(SummaryPage.class, WicketUtils.newRepositoryParameter(asFork)); } else { error(MessageFormat.format(getString("gb.repositoryForkFailed"), model)); } @@ -259,23 +287,7 @@ public abstract class RepositoryPage extends BasePage { forkLink.add(new JavascriptEventConfirmation("onclick", MessageFormat.format( getString("gb.forkRepository"), getRepositoryModel()))); add(forkLink); - } else { - // user not allowed to fork or fork already exists or repo forbids forking - add(new ExternalLink("forkLink", "").setVisible(false)); } - - if (hasClone) { - // user has clone - String url = getRequestCycle().urlFor(SummaryPage.class, WicketUtils.newRepositoryParameter(clonedRepo)).toString(); - add(new ExternalLink("myForkLink", url)); - } else { - // user does not have clone - add(new ExternalLink("myForkLink", "").setVisible(false)); - } - } else { - // server prohibits forking - add(new ExternalLink("forkLink", "").setVisible(false)); - add(new ExternalLink("myForkLink", "").setVisible(false)); } super.setupPage(repositoryName, pageName); @@ -577,4 +589,4 @@ public abstract class RepositoryPage extends BasePage { getRequestCycle().setRequestTarget(new RedirectRequestTarget(absoluteUrl)); } } -} +} \ No newline at end of file -- 2.39.5