From 1f52c8b5f123b97fc631465479bc2855c12b9ee3 Mon Sep 17 00:00:00 2001
From: James Moger <james.moger@gitblit.com>
Date: Tue, 02 Oct 2012 17:23:16 -0400
Subject: [PATCH] Moved the fork mechanism from a javascript link to a separate page

---
 src/com/gitblit/GitBlit.java                              |   65 ++++++------
 src/com/gitblit/wicket/GitBlitWebApp.properties           |    7 +
 src/com/gitblit/wicket/pages/ForkPage.java                |  107 +++++++++++++++++++++
 src/com/gitblit/wicket/pages/ForkPage.html                |   40 ++++++++
 src/com/gitblit/wicket/GitBlitWebSession.java             |   12 ++
 resources/git-black.png                                   |    0 
 src/com/gitblit/models/RepositoryModel.java               |    3 
 src/com/gitblit/wicket/GitBlitWebApp.java                 |    2 
 src/com/gitblit/wicket/panels/RepositoriesPanel.java      |    4 
 src/com/gitblit/wicket/pages/RepositoryPage.java          |   30 +----
 src/com/gitblit/wicket/panels/ProjectRepositoryPanel.java |    2 
 11 files changed, 211 insertions(+), 61 deletions(-)

diff --git a/resources/git-black.png b/resources/git-black.png
new file mode 100644
index 0000000..8bc4c43
--- /dev/null
+++ b/resources/git-black.png
Binary files differ
diff --git a/src/com/gitblit/GitBlit.java b/src/com/gitblit/GitBlit.java
index 51c44f6..4ada748 100644
--- a/src/com/gitblit/GitBlit.java
+++ b/src/com/gitblit/GitBlit.java
@@ -2637,44 +2637,45 @@
 	 * 
 	 * @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);
-					}
-				}
-				
-				// inherit origin's access lists
-				List<String> users = getRepositoryUsers(repository);
-				setRepositoryUsers(cloneModel, users);
-				
-				List<String> 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);
+			throw new GitBlitException(e);
 		}
-		return false;
+
+		// 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<String> users = getRepositoryUsers(repository);
+			setRepositoryUsers(cloneModel, users);
+
+			List<String> teams = getRepositoryTeams(repository);
+			setRepositoryTeams(cloneModel, teams);
+		}
+
+		// 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 44aba1d..3148b5b 100644
--- a/src/com/gitblit/models/RepositoryModel.java
+++ b/src/com/gitblit/models/RepositoryModel.java
@@ -145,7 +145,10 @@
 	
 	public RepositoryModel cloneAs(String cloneName) {
 		RepositoryModel clone = new RepositoryModel();
+		clone.originRepository = name;
 		clone.name = cloneName;
+		clone.projectPath = StringUtils.getFirstPathElement(cloneName);
+		clone.isBare = true;
 		clone.description = description;
 		clone.accessRestriction = accessRestriction;
 		clone.authorizationControl = authorizationControl;
diff --git a/src/com/gitblit/wicket/GitBlitWebApp.java b/src/com/gitblit/wicket/GitBlitWebApp.java
index b691cea..245b1e0 100644
--- a/src/com/gitblit/wicket/GitBlitWebApp.java
+++ b/src/com/gitblit/wicket/GitBlitWebApp.java
@@ -34,6 +34,7 @@
 import com.gitblit.wicket.pages.CommitPage;
 import com.gitblit.wicket.pages.DocsPage;
 import com.gitblit.wicket.pages.FederationRegistrationPage;
+import com.gitblit.wicket.pages.ForkPage;
 import com.gitblit.wicket.pages.ForksPage;
 import com.gitblit.wicket.pages.GitSearchPage;
 import com.gitblit.wicket.pages.GravatarProfilePage;
@@ -120,6 +121,7 @@
 		mount("/projects", ProjectsPage.class);
 		mount("/user", UserPage.class, "user");
 		mount("/forks", ForksPage.class, "r");
+		mount("/fork", ForkPage.class, "r");
 	}
 
 	private void mount(String location, Class<? extends WebPage> clazz, String... parameters) {
diff --git a/src/com/gitblit/wicket/GitBlitWebApp.properties b/src/com/gitblit/wicket/GitBlitWebApp.properties
index d30f571..e0bb669 100644
--- a/src/com/gitblit/wicket/GitBlitWebApp.properties
+++ b/src/com/gitblit/wicket/GitBlitWebApp.properties
@@ -323,7 +323,7 @@
 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.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 7ecc05b..0e1ae51 100644
--- a/src/com/gitblit/wicket/GitBlitWebSession.java
+++ b/src/com/gitblit/wicket/GitBlitWebSession.java
@@ -17,6 +17,7 @@
 
 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 @@
 	
 	private String requestUrl;
 	
+	private AtomicBoolean isForking;
+	
 	public GitBlitWebSession(Request request) {
 		super(request);
+		isForking = new AtomicBoolean();
 	}
 
 	public void invalidate() {
@@ -134,6 +138,14 @@
 		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 0000000..7d2d2aa
--- /dev/null
+++ b/src/com/gitblit/wicket/pages/ForkPage.html
@@ -0,0 +1,40 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml"  
+      xmlns:wicket="http://wicket.apache.org/dtds.data/wicket-xhtml1.3-strict.dtd"  
+      xml:lang="en"  
+      lang="en"> 
+
+<body>
+<wicket:head>
+	<noscript>
+		<meta http-equiv="refresh" content="5"></meta>
+	</noscript>
+	<script type="text/javascript"">
+		function doLoad() { setTimeout( "refresh()", 5*1000 ); }
+		function refresh() { window.location.reload(); }
+	</script>
+</wicket:head>
+<wicket:extend>
+<!-- need to specify body.onload -->
+<body onload="doLoad()">
+
+	<div class="row">
+		<div class="span6 offset3">
+			<div wicket:id="forkText" class="pageTitle project" style="border:0;font-weight:bold; text-align:center;">[fork text]</div>
+		</div>
+		<div class="span4 offset4">
+			<div class="progress progress-striped active">
+  				<div class="bar" style="width: 100%;"></div>
+			</div>
+		</div>
+		<div class="span6 offset3">
+			<div style="opacity:0.2;">
+				<center><img style="padding:10px" src="git-black.png"></img></center>
+			</div>
+		</div>
+	</div>
+</body>
+
+</wicket:extend>	
+</body>
+</html>
\ 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 0000000..082dab5
--- /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 8ca2b33..879c432 100644
--- a/src/com/gitblit/wicket/pages/RepositoryPage.java
+++ b/src/com/gitblit/wicket/pages/RepositoryPage.java
@@ -28,12 +28,10 @@
 
 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.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 @@
 		}
 		return pages;
 	}
+	
+	protected boolean allowForkControls() {
+		return true;
+	}
 
 	@Override
 	protected void setupPage(String repositoryName, String pageName) {
@@ -230,7 +231,7 @@
 		}
 
 		// 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 @@
 				// 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(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 6f693ee..0a6bc62 100644
--- a/src/com/gitblit/wicket/panels/ProjectRepositoryPanel.java
+++ b/src/com/gitblit/wicket/panels/ProjectRepositoryPanel.java
@@ -22,7 +22,6 @@
 
 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 @@
 					@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 4c9ed28..1967b50 100644
--- a/src/com/gitblit/wicket/panels/RepositoriesPanel.java
+++ b/src/com/gitblit/wicket/panels/RepositoriesPanel.java
@@ -305,11 +305,11 @@
 						@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));

--
Gitblit v1.9.1