From 1f52c8b5f123b97fc631465479bc2855c12b9ee3 Mon Sep 17 00:00:00 2001 From: James Moger Date: Tue, 2 Oct 2012 17:23:16 -0400 Subject: [PATCH] Moved the fork mechanism from a javascript link to a separate page --- resources/git-black.png | Bin 0 -> 4980 bytes src/com/gitblit/GitBlit.java | 63 ++++++----- src/com/gitblit/models/RepositoryModel.java | 3 + src/com/gitblit/wicket/GitBlitWebApp.java | 2 + .../gitblit/wicket/GitBlitWebApp.properties | 7 +- src/com/gitblit/wicket/GitBlitWebSession.java | 12 ++ src/com/gitblit/wicket/pages/ForkPage.html | 40 +++++++ src/com/gitblit/wicket/pages/ForkPage.java | 107 ++++++++++++++++++ .../gitblit/wicket/pages/RepositoryPage.java | 30 ++--- .../wicket/panels/ProjectRepositoryPanel.java | 2 - .../wicket/panels/RepositoriesPanel.java | 4 +- 11 files changed, 210 insertions(+), 60 deletions(-) create mode 100644 resources/git-black.png create mode 100644 src/com/gitblit/wicket/pages/ForkPage.html create mode 100644 src/com/gitblit/wicket/pages/ForkPage.java diff --git a/resources/git-black.png b/resources/git-black.png new file mode 100644 index 0000000000000000000000000000000000000000..8bc4c4399ad702349eab08d485c588b71f4861bb GIT binary patch literal 4980 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 literal 0 HcmV?d00001 diff --git a/src/com/gitblit/GitBlit.java b/src/com/gitblit/GitBlit.java index 51c44f66..4ada7481 100644 --- a/src/com/gitblit/GitBlit.java +++ b/src/com/gitblit/GitBlit.java @@ -2637,44 +2637,45 @@ public class GitBlit implements ServletContextListener { * * @param repository * @param user - * @return true, if successful + * @return the repository model of the fork, if successful + * @throws GitBlitException */ - public boolean fork(RepositoryModel repository, UserModel user) { + public RepositoryModel fork(RepositoryModel repository, UserModel user) throws GitBlitException { String cloneName = MessageFormat.format("~{0}/{1}.git", user.username, StringUtils.stripDotGit(StringUtils.getLastPathElement(repository.name))); String fromUrl = MessageFormat.format("file://{0}/{1}", repositoriesFolder.getAbsolutePath(), repository.name); + + // clone the repository try { - // clone the repository JGitUtils.cloneRepository(repositoriesFolder, cloneName, fromUrl, true, null); - - // create a Gitblit repository model for the clone - RepositoryModel cloneModel = repository.cloneAs(cloneName); - cloneModel.owner = user.username; - updateRepositoryModel(cloneName, cloneModel, false); - - if (AuthorizationControl.NAMED.equals(cloneModel.authorizationControl)) { - // add the owner of the source repository to the clone's access list - if (!StringUtils.isEmpty(repository.owner)) { - UserModel owner = getUserModel(repository.owner); - if (owner != null) { - owner.repositories.add(cloneName); - updateUserModel(owner.username, owner, false); - } + } catch (Exception e) { + throw new GitBlitException(e); + } + + // create a Gitblit repository model for the clone + RepositoryModel cloneModel = repository.cloneAs(cloneName); + cloneModel.owner = user.username; + updateRepositoryModel(cloneName, cloneModel, false); + + if (AuthorizationControl.NAMED.equals(cloneModel.authorizationControl)) { + // add the owner of the source repository to the clone's access list + if (!StringUtils.isEmpty(repository.owner)) { + UserModel owner = getUserModel(repository.owner); + if (owner != null) { + owner.repositories.add(cloneName); + updateUserModel(owner.username, owner, false); } - - // inherit origin's access lists - List users = getRepositoryUsers(repository); - setRepositoryUsers(cloneModel, users); - - List teams = getRepositoryTeams(repository); - setRepositoryTeams(cloneModel, teams); } - - // add this clone to the cached model - addToCachedRepositoryList(cloneModel.name, cloneModel); - return true; - } catch (Exception e) { - logger.error("failed to fork", e); + + // inherit origin's access lists + List users = getRepositoryUsers(repository); + setRepositoryUsers(cloneModel, users); + + List teams = getRepositoryTeams(repository); + setRepositoryTeams(cloneModel, teams); } - return false; + + // add this clone to the cached model + addToCachedRepositoryList(cloneModel.name, cloneModel); + return cloneModel; } } diff --git a/src/com/gitblit/models/RepositoryModel.java b/src/com/gitblit/models/RepositoryModel.java index 44aba1d8..3148b5b4 100644 --- a/src/com/gitblit/models/RepositoryModel.java +++ b/src/com/gitblit/models/RepositoryModel.java @@ -145,7 +145,10 @@ public class RepositoryModel implements Serializable, Comparable clazz, String... parameters) { diff --git a/src/com/gitblit/wicket/GitBlitWebApp.properties b/src/com/gitblit/wicket/GitBlitWebApp.properties index d30f5710..e0bb6692 100644 --- a/src/com/gitblit/wicket/GitBlitWebApp.properties +++ b/src/com/gitblit/wicket/GitBlitWebApp.properties @@ -323,7 +323,7 @@ gb.fork = fork gb.forks = forks gb.forkRepository = fork {0}? gb.repositoryForked = {0} has been forked -gb.repositoryForkFailed= failed to fork {1} +gb.repositoryForkFailed= fork has failed gb.personalRepositories = personal repositories gb.allowForks = allow forks gb.allowForksDescription = allow authorized users to fork this repository @@ -333,4 +333,7 @@ gb.canForkDescription = user is permitted to fork authorized repositories gb.myFork = view my fork gb.forksProhibited = forks prohibited gb.forksProhibitedWarning = this repository forbids forks -gb.noForks = {0} has no forks \ No newline at end of file +gb.noForks = {0} has no forks +gb.forkNotAuthorized = sorry, you are not authorized to fork {0} +gb.forkInProgress = fork in progress +gb.preparingFork = Gitblit is preparing your fork \ No newline at end of file diff --git a/src/com/gitblit/wicket/GitBlitWebSession.java b/src/com/gitblit/wicket/GitBlitWebSession.java index 7ecc05b2..0e1ae512 100644 --- a/src/com/gitblit/wicket/GitBlitWebSession.java +++ b/src/com/gitblit/wicket/GitBlitWebSession.java @@ -17,6 +17,7 @@ package com.gitblit.wicket; import java.util.Map; import java.util.TimeZone; +import java.util.concurrent.atomic.AtomicBoolean; import org.apache.wicket.Page; import org.apache.wicket.PageParameters; @@ -42,8 +43,11 @@ public final class GitBlitWebSession extends WebSession { private String requestUrl; + private AtomicBoolean isForking; + public GitBlitWebSession(Request request) { super(request); + isForking = new AtomicBoolean(); } public void invalidate() { @@ -134,6 +138,14 @@ public final class GitBlitWebSession extends WebSession { errorMessage = null; return msg; } + + public boolean isForking() { + return isForking.get(); + } + + public void isForking(boolean val) { + isForking.set(val); + } public static GitBlitWebSession get() { return (GitBlitWebSession) Session.get(); diff --git a/src/com/gitblit/wicket/pages/ForkPage.html b/src/com/gitblit/wicket/pages/ForkPage.html new file mode 100644 index 00000000..7d2d2aa5 --- /dev/null +++ b/src/com/gitblit/wicket/pages/ForkPage.html @@ -0,0 +1,40 @@ + + + + + + + + + + + + +
+
+
[fork text]
+
+
+
+
+
+
+
+
+
+
+
+
+ + +
+ + \ No newline at end of file diff --git a/src/com/gitblit/wicket/pages/ForkPage.java b/src/com/gitblit/wicket/pages/ForkPage.java new file mode 100644 index 00000000..082dab51 --- /dev/null +++ b/src/com/gitblit/wicket/pages/ForkPage.java @@ -0,0 +1,107 @@ +/* + * Copyright 2012 gitblit.com. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.gitblit.wicket.pages; + +import java.text.MessageFormat; + +import org.apache.wicket.PageParameters; +import org.apache.wicket.markup.html.basic.Label; +import org.slf4j.LoggerFactory; + +import com.gitblit.GitBlit; +import com.gitblit.models.RepositoryModel; +import com.gitblit.models.UserModel; +import com.gitblit.wicket.GitBlitWebSession; +import com.gitblit.wicket.GitblitRedirectException; +import com.gitblit.wicket.WicketUtils; + +public class ForkPage extends RepositoryPage { + + + public ForkPage(PageParameters params) { + super(params); + + setVersioned(false); + + GitBlitWebSession session = GitBlitWebSession.get(); + + RepositoryModel repository = getRepositoryModel(); + UserModel user = session.getUser(); + boolean canFork = user.canForkRepository(repository); + + if (!canFork) { + // redirect to the summary page if this repository is not empty + GitBlitWebSession.get().cacheErrorMessage( + MessageFormat.format(getString("gb.forkNotAuthorized"), repository.name)); + throw new GitblitRedirectException(SummaryPage.class, WicketUtils.newRepositoryParameter(repository.name)); + } + + String fork = GitBlit.self().getFork(user.username, repository.name); + if (fork != null) { + // redirect to user's fork + throw new GitblitRedirectException(SummaryPage.class, WicketUtils.newRepositoryParameter(fork)); + } + + add(new Label("forkText", getString("gb.preparingFork"))); + + if (!session.isForking()) { + // prepare session + session.isForking(true); + + // fork it + ForkThread forker = new ForkThread(repository, session); + forker.start(); + } + } + + @Override + protected boolean allowForkControls() { + return false; + } + + @Override + protected String getPageName() { + return "fork"; + } + + /** + * ForkThread does the work of working the repository in a background + * thread. The completion status is tracked through a session variable and + * monitored by this page. + */ + private static class ForkThread extends Thread { + + private final RepositoryModel repository; + private final GitBlitWebSession session; + + public ForkThread(RepositoryModel repository, GitBlitWebSession session) { + this.repository = repository; + this.session = session; + } + + @Override + public void run() { + UserModel user = session.getUser(); + try { + GitBlit.self().fork(repository, user); + } catch (Exception e) { + LoggerFactory.getLogger(ForkPage.class).error(MessageFormat.format("Failed to fork {0} for {1}", repository.name, user.username), e); + } finally { + session.isForking(false); + } + } + } +} diff --git a/src/com/gitblit/wicket/pages/RepositoryPage.java b/src/com/gitblit/wicket/pages/RepositoryPage.java index 8ca2b335..879c4321 100644 --- a/src/com/gitblit/wicket/pages/RepositoryPage.java +++ b/src/com/gitblit/wicket/pages/RepositoryPage.java @@ -28,12 +28,10 @@ import java.util.Set; import org.apache.wicket.Component; import org.apache.wicket.PageParameters; -import org.apache.wicket.RedirectException; import org.apache.wicket.markup.html.basic.Label; import org.apache.wicket.markup.html.form.DropDownChoice; import org.apache.wicket.markup.html.form.TextField; import org.apache.wicket.markup.html.link.ExternalLink; -import org.apache.wicket.markup.html.link.Link; import org.apache.wicket.markup.html.panel.Fragment; import org.apache.wicket.model.IModel; import org.apache.wicket.model.Model; @@ -62,7 +60,6 @@ import com.gitblit.wicket.PageRegistration; import com.gitblit.wicket.PageRegistration.OtherPageLink; import com.gitblit.wicket.SessionlessForm; import com.gitblit.wicket.WicketUtils; -import com.gitblit.wicket.panels.BasePanel.JavascriptEventConfirmation; import com.gitblit.wicket.panels.LinkPanel; import com.gitblit.wicket.panels.NavigationPanel; import com.gitblit.wicket.panels.RefsPanel; @@ -171,6 +168,10 @@ public abstract class RepositoryPage extends BasePage { } return pages; } + + protected boolean allowForkControls() { + return true; + } @Override protected void setupPage(String repositoryName, String pageName) { @@ -230,7 +231,7 @@ public abstract class RepositoryPage extends BasePage { } // fork controls - if (user == null) { + if (!allowForkControls() || user == null) { // must be logged-in to fork, hide all fork controls add(new ExternalLink("forkLink", "").setVisible(false)); add(new ExternalLink("myForkLink", "").setVisible(false)); @@ -268,25 +269,8 @@ public abstract class RepositoryPage extends BasePage { // 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(asFork)); - } else { - error(MessageFormat.format(getString("gb.repositoryForkFailed"), model)); - } - } - }; - forkLink.add(new JavascriptEventConfirmation("onclick", MessageFormat.format( - getString("gb.forkRepository"), getRepositoryModel()))); - add(forkLink); + String url = getRequestCycle().urlFor(ForkPage.class, WicketUtils.newRepositoryParameter(model.name)).toString(); + add(new ExternalLink("forkLink", url)); } } diff --git a/src/com/gitblit/wicket/panels/ProjectRepositoryPanel.java b/src/com/gitblit/wicket/panels/ProjectRepositoryPanel.java index 6f693ee0..0a6bc623 100644 --- a/src/com/gitblit/wicket/panels/ProjectRepositoryPanel.java +++ b/src/com/gitblit/wicket/panels/ProjectRepositoryPanel.java @@ -22,7 +22,6 @@ import java.util.Map; import org.apache.wicket.Component; import org.apache.wicket.Localizer; -import org.apache.wicket.Page; import org.apache.wicket.PageParameters; import org.apache.wicket.markup.html.basic.Label; import org.apache.wicket.markup.html.link.BookmarkablePageLink; @@ -153,7 +152,6 @@ public class ProjectRepositoryPanel extends BasePanel { @Override public void onClick() { if (GitBlit.self().deleteRepositoryModel(entry)) { - info(MessageFormat.format(getString("gb.repositoryDeleted"), entry)); // redirect to the owning page if (entry.isPersonalRepository()) { setResponsePage(getPage().getClass(), WicketUtils.newUsernameParameter(entry.projectPath.substring(1))); diff --git a/src/com/gitblit/wicket/panels/RepositoriesPanel.java b/src/com/gitblit/wicket/panels/RepositoriesPanel.java index 4c9ed280..1967b501 100644 --- a/src/com/gitblit/wicket/panels/RepositoriesPanel.java +++ b/src/com/gitblit/wicket/panels/RepositoriesPanel.java @@ -305,11 +305,11 @@ public class RepositoriesPanel extends BasePanel { @Override public void onClick() { if (GitBlit.self().deleteRepositoryModel(entry)) { - info(MessageFormat.format(getString("gb.repositoryDeleted"), entry)); if (dp instanceof SortableRepositoriesProvider) { + info(MessageFormat.format(getString("gb.repositoryDeleted"), entry)); ((SortableRepositoriesProvider) dp).remove(entry); } else { - ((RepositoriesProvider) dp).remove(entry); + setResponsePage(getPage().getClass(), getPage().getPageParameters()); } } else { error(MessageFormat.format(getString("gb.repositoryDeleteFailed"), entry)); -- 2.39.5