James Moger
2012-10-01 eb1405f736f2f98e14215774dd53eea9b9a77017
Show fork links according to user permissions. Improve fork detection.
4 files modified
227 ■■■■ changed files
src/com/gitblit/GitBlit.java 76 ●●●●● patch | view | raw | blame | history
src/com/gitblit/models/UserModel.java 10 ●●●●● patch | view | raw | blame | history
src/com/gitblit/wicket/pages/ForksPage.java 31 ●●●●● patch | view | raw | blame | history
src/com/gitblit/wicket/pages/RepositoryPage.java 110 ●●●●● patch | view | raw | blame | history
src/com/gitblit/GitBlit.java
@@ -32,6 +32,7 @@
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 @@
        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<String> roots = new HashSet<String>();
            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
src/com/gitblit/models/UserModel.java
@@ -86,6 +86,16 @@
        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;
src/com/gitblit/wicket/pages/ForksPage.java
@@ -42,26 +42,32 @@
    public ForksPage(PageParameters params) {
        super(params);
        
        UserModel user = GitBlitWebSession.get().getUser();
        RepositoryModel model = getRepositoryModel();
        RepositoryModel origin;
        RepositoryModel origin = model;
        List<String> list;
        if (ArrayUtils.isEmpty(model.forks)) {
            // origin repository has forks
            origin = GitBlit.self().getRepositoryModel(model.originRepository);
            list = new ArrayList<String>(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<String>();
            } else {
                list = new ArrayList<String>(origin.forks);
            }
        } else {
            // this repository has forks
            origin = model;
            list = new ArrayList<String>(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 @@
        }
        
        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<RepositoryModel> forks = new ArrayList<RepositoryModel>();
        for (String aFork : list) {
            RepositoryModel fork = GitBlit.self().getRepositoryModel(user, aFork);
src/com/gitblit/wicket/pages/RepositoryPage.java
@@ -136,22 +136,12 @@
        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 @@
                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 @@
            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<Void> forkLink = new Link<Void>("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 @@
                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 @@
            getRequestCycle().setRequestTarget(new RedirectRequestTarget(absoluteUrl));
        }
    }
}
}