From b7502e5c0666154c7378469fd769b3aeb242e949 Mon Sep 17 00:00:00 2001
From: James Moger <james.moger@gitblit.com>
Date: Thu, 02 May 2013 21:44:18 -0400
Subject: [PATCH] Added SparkleShare invite url panel

---
 src/main/java/com/gitblit/wicket/pages/BasePage.java                 |   29 ++++
 src/main/java/com/gitblit/SparkleShareInviteServlet.java             |  210 +++++++++++++++++++++++++++++++++++
 src/main/java/com/gitblit/wicket/pages/SummaryPage.java              |   10 +
 src/main/java/com/gitblit/wicket/panels/SparkleShareInvitePanel.html |   18 +++
 src/main/java/WEB-INF/web.xml                                        |   18 ++
 src/main/java/com/gitblit/Constants.java                             |    4 
 src/main/java/com/gitblit/wicket/GitBlitWebApp.properties            |    5 
 src/main/java/com/gitblit/wicket/pages/SummaryPage.html              |    1 
 src/main/java/com/gitblit/wicket/panels/SparkleShareInvitePanel.java |   52 ++++++++
 9 files changed, 342 insertions(+), 5 deletions(-)

diff --git a/src/main/java/WEB-INF/web.xml b/src/main/java/WEB-INF/web.xml
index 5ee5ed2..573305c 100644
--- a/src/main/java/WEB-INF/web.xml
+++ b/src/main/java/WEB-INF/web.xml
@@ -53,7 +53,21 @@
 		<servlet-name>GitServlet</servlet-name>		
 		<url-pattern>/git/*</url-pattern>
 	</servlet-mapping>
+
 	
+	<!-- SparkleShare Invite Servlet
+		 <url-pattern> MUST match: 
+			* com.gitblit.Constants.SPARKLESHARE_INVITE_PATH
+			* Wicket Filter ignorePaths parameter -->
+	<servlet>
+		<servlet-name>SparkleShareInviteServlet</servlet-name>
+		<servlet-class>com.gitblit.SparkleShareInviteServlet</servlet-class>
+	</servlet>
+	<servlet-mapping>
+		<servlet-name>SparkleShareInviteServlet</servlet-name>		
+		<url-pattern>/sparkleshare/*</url-pattern>
+	</servlet-mapping>
+
 	
 	<!-- Syndication Servlet
 		 <url-pattern> MUST match: 
@@ -244,6 +258,8 @@
              	* GitFilter <url-pattern>
              	* GitServlet <url-pattern>
              	* com.gitblit.Constants.GIT_PATH
+             	* SparkleshareInviteServlet <url-pattern>
+             	* com.gitblit.Constants.SPARKLESHARE_INVITE_PATH
              	* Zipfilter <url-pattern>
              	* ZipServlet <url-pattern>
              	* com.gitblit.Constants.ZIP_PATH
@@ -253,7 +269,7 @@
              	* PagesFilter <url-pattern>
              	* PagesServlet <url-pattern>
              	* com.gitblit.Constants.PAGES_PATH -->
-            <param-value>git/,feed/,zip/,federation/,rpc/,pages/,robots.txt</param-value>
+            <param-value>git/,feed/,zip/,federation/,rpc/,pages/,robots.txt,sparkleshare/</param-value>
         </init-param>
     </filter>
     <filter-mapping>
diff --git a/src/main/java/com/gitblit/Constants.java b/src/main/java/com/gitblit/Constants.java
index 5bd5b60..f037346 100644
--- a/src/main/java/com/gitblit/Constants.java
+++ b/src/main/java/com/gitblit/Constants.java
@@ -56,7 +56,9 @@
 
 	public static final String RPC_PATH = "/rpc/";
 	
-	public static final String PAGES= "/pages/";
+	public static final String PAGES = "/pages/";
+	
+	public static final String SPARKLESHARE_INVITE_PATH = "/sparkleshare/";
 
 	public static final String BORDER = "***********************************************************";
 
diff --git a/src/main/java/com/gitblit/SparkleShareInviteServlet.java b/src/main/java/com/gitblit/SparkleShareInviteServlet.java
new file mode 100644
index 0000000..3cabb41
--- /dev/null
+++ b/src/main/java/com/gitblit/SparkleShareInviteServlet.java
@@ -0,0 +1,210 @@
+/*
+ * Copyright 2013 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;
+
+import java.io.IOException;
+import java.text.MessageFormat;
+import java.util.List;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import com.gitblit.Constants.AccessRestrictionType;
+import com.gitblit.models.RepositoryModel;
+import com.gitblit.models.UserModel;
+import com.gitblit.utils.StringUtils;
+
+/**
+ * Handles requests for Sparkleshare Invites
+ * 
+ * @author James Moger
+ * 
+ */
+public class SparkleShareInviteServlet extends HttpServlet {
+
+	private static final long serialVersionUID = 1L;
+
+	public SparkleShareInviteServlet() {
+		super();
+	}
+	
+	/**
+	 * Returns an Sparkleshare invite url to this servlet for the repository.
+	 * https://github.com/hbons/SparkleShare/wiki/Invites
+	 * 
+	 * @param baseURL
+	 * @param repository
+	 * @param username
+	 * @return an url
+	 */
+	public static String asLink(String baseURL, String repository, String username) {
+		if (baseURL.length() > 0 && baseURL.charAt(baseURL.length() - 1) == '/') {
+			baseURL = baseURL.substring(0, baseURL.length() - 1);
+		}
+		String url = baseURL + Constants.SPARKLESHARE_INVITE_PATH
+				+ ((StringUtils.isEmpty(username) ? "" : (username + "@")))
+				+ repository + ".xml";
+		url = url.replace("https://", "sparkleshare://");
+		url = url.replace("http://", "sparkleshare-unsafe://");
+		return url;
+	}
+	
+	@Override
+	protected void doPost(HttpServletRequest request, HttpServletResponse response)
+			throws ServletException, java.io.IOException {
+		processRequest(request, response);
+	}
+
+	@Override
+	protected void doGet(HttpServletRequest request, HttpServletResponse response)
+			throws ServletException, IOException {
+		processRequest(request, response);
+	}
+
+	protected void processRequest(javax.servlet.http.HttpServletRequest request,
+			javax.servlet.http.HttpServletResponse response) throws javax.servlet.ServletException,
+			java.io.IOException {		
+		
+		// extract repo name from request
+		String path = request.getPathInfo();
+		if (path != null && path.length() > 1) {
+			if (path.charAt(0) == '/') {
+				path = path.substring(1);
+			}
+		}
+		// trim trailing .xml
+		if (path.endsWith(".xml")) {
+			path = path.substring(0, path.length() - 4);
+		}
+		
+		String username = null;
+		int fetch = path.indexOf('@');
+		if (fetch > -1) {
+			username = path.substring(0, fetch);
+			path = path.substring(fetch + 1);
+		}
+		UserModel user;
+		if (StringUtils.isEmpty(username)) {
+			user = GitBlit.self().authenticate(request);
+		} else {
+			user = GitBlit.self().getUserModel(username);
+		}
+		if (user == null) {
+			user = UserModel.ANONYMOUS;
+			username = "";
+		}
+		
+		// ensure that the requested repository exists and is sparkleshared
+		RepositoryModel model = GitBlit.self().getRepositoryModel(path);
+		if (model == null) {
+			response.setStatus(HttpServletResponse.SC_NOT_FOUND);
+			response.getWriter().append(MessageFormat.format("Repository \"{0}\" not found!", path));
+			return;
+		} else if (!model.isSparkleshared()) {
+			response.setStatus(HttpServletResponse.SC_FORBIDDEN);
+			response.getWriter().append(MessageFormat.format("Repository \"{0}\" is not sparkleshared!", path));
+			return;
+		}
+		
+		if (GitBlit.getBoolean(Keys.git.enableGitServlet, true)
+				|| GitBlit.getInteger(Keys.git.daemonPort, 0) > 0) {
+			// Gitblit as server
+			// determine username for repository url
+			if (model.accessRestriction.exceeds(AccessRestrictionType.NONE)) {
+				if (!user.canRewindRef(model)) {
+					response.setStatus(HttpServletResponse.SC_FORBIDDEN);
+					response.getWriter().append(MessageFormat.format("\"{0}\" does not have RW+ permissions for {1}!", user.username, path));
+					return;
+				}
+			}
+			
+			if (model.accessRestriction.exceeds(AccessRestrictionType.NONE)) {
+				username = user.username + "@";
+			} else {
+				username = "";
+			}
+
+			String serverPort = "";
+			if (request.getScheme().equals("https")) {
+				if (request.getServerPort() != 443) {
+					serverPort = ":" + request.getServerPort();
+				}
+			} else if (request.getScheme().equals("http")) {
+				if (request.getServerPort() != 80) {
+					serverPort = ":" + request.getServerPort();
+				}
+			}
+
+			// assume http/https serving
+			String scheme = request.getScheme();
+			String servletPath = Constants.GIT_PATH;
+
+			// try to switch to git://, if git servlet disabled and repo has no restrictions
+			if (!GitBlit.getBoolean(Keys.git.enableGitServlet, true)
+					&& (GitBlit.getInteger(Keys.git.daemonPort, 0) > 0)
+					&& AccessRestrictionType.NONE == model.accessRestriction) {
+				scheme = "git";
+				servletPath = "/";
+				serverPort = GitBlit.getString(Keys.git.daemonPort, "");
+			}
+
+			// construct Sparkleshare invite
+			StringBuilder sb = new StringBuilder();		
+			sb.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
+			sb.append("<sparkleshare><invite>\n");
+			sb.append(MessageFormat.format("<address>{0}://{1}{2}{3}{4}</address>\n", scheme, username, request.getServerName(), serverPort, request.getContextPath()));
+			sb.append(MessageFormat.format("<remote_path>{0}{1}</remote_path>\n", servletPath, model.name));
+			if (GitBlit.getInteger(Keys.fanout.port, 0) > 0) {
+				// Gitblit is running it's own fanout service for pubsub notifications
+				sb.append(MessageFormat.format("<announcements_url>tcp://{0}:{1}</announcements_url>\n", request.getServerName(), GitBlit.getString(Keys.fanout.port, "")));
+			}
+			sb.append("</invite></sparkleshare>\n");
+
+			// write invite to client
+			response.setContentType("application/xml");
+			response.setContentLength(sb.length());
+			response.getWriter().append(sb.toString());
+		} else {
+			// Gitblit as viewer, repository access handled externally so
+			// assume RW+ permission
+			List<String> others = GitBlit.getStrings(Keys.web.otherUrls);
+			if (others.size() == 0) {
+				return;
+			}
+			
+			String address = MessageFormat.format(others.get(0), "", username);
+			
+			StringBuilder sb = new StringBuilder();		
+			sb.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
+			sb.append("<sparkleshare><invite>\n");
+			
+			sb.append(MessageFormat.format("<address>{0}</address>\n", address));
+			sb.append(MessageFormat.format("<remote_path>{0}</remote_path>\n", model.name));
+			if (GitBlit.getInteger(Keys.fanout.port, 0) > 0) {
+				// Gitblit is running it's own fanout service for pubsub notifications
+				sb.append(MessageFormat.format("<announcements_url>tcp://{0}:{1}</announcements_url>\n", request.getServerName(), GitBlit.getString(Keys.fanout.port, "")));
+			}
+			sb.append("</invite></sparkleshare>\n");
+
+			// write invite to client
+			response.setContentType("application/xml");
+			response.setContentLength(sb.length());
+			response.getWriter().append(sb.toString());
+		}
+	}
+}
diff --git a/src/main/java/com/gitblit/wicket/GitBlitWebApp.properties b/src/main/java/com/gitblit/wicket/GitBlitWebApp.properties
index aa5a415..049cc40 100644
--- a/src/main/java/com/gitblit/wicket/GitBlitWebApp.properties
+++ b/src/main/java/com/gitblit/wicket/GitBlitWebApp.properties
@@ -445,10 +445,11 @@
 gb.owners = owners
 gb.sessionEnded = Session has been closed
 gb.closeBrowser = Please close the browser to properly end the session.
-gb.doesNotExistInTree = {0} does not exist in tree {1}
+gb.doesNotExistInTree = {0} does not exist in tree {1}
 gb.enableIncrementalPushTags = enable incremental push tags
 gb.useIncrementalPushTagsDescription = on push, automatically tag each branch tip with an incremental revision number
 gb.incrementalPushTagMessage = Auto-tagged [{0}] branch on push
 gb.externalPermissions = {0} access permissions for {1} are externally maintained
 gb.viewAccess = You do not have Gitblit read or write access
-gb.yourProtocolPermissionIs = Your {0} access permission for {1} is {2}
\ No newline at end of file
+gb.yourProtocolPermissionIs = Your {0} access permission for {1} is {2}
+gb.sparkleshareInvite = SparkleShare invite
\ No newline at end of file
diff --git a/src/main/java/com/gitblit/wicket/pages/BasePage.java b/src/main/java/com/gitblit/wicket/pages/BasePage.java
index b2dcce3..b3b0767 100644
--- a/src/main/java/com/gitblit/wicket/pages/BasePage.java
+++ b/src/main/java/com/gitblit/wicket/pages/BasePage.java
@@ -57,6 +57,7 @@
 import com.gitblit.Constants.FederationStrategy;
 import com.gitblit.GitBlit;
 import com.gitblit.Keys;
+import com.gitblit.SparkleShareInviteServlet;
 import com.gitblit.models.ProjectModel;
 import com.gitblit.models.RepositoryModel;
 import com.gitblit.models.TeamModel;
@@ -311,7 +312,33 @@
 			return new Label(wicketId).setVisible(false);
 		}
 	}
-	
+
+	protected String getSparkleShareInviteUrl(RepositoryModel repository) {
+		if (repository.isBare && repository.isSparkleshared()) {
+			UserModel user = GitBlitWebSession.get().getUser();
+			if (user == null) {
+				user = UserModel.ANONYMOUS;
+			}
+			String username = null;
+			if (UserModel.ANONYMOUS != user) {
+				username = user.username;
+			}
+			if (GitBlit.getBoolean(Keys.git.enableGitServlet, true) || (GitBlit.getInteger(Keys.git.daemonPort, 0) > 0)) {
+				// Gitblit as server
+				// ensure user can rewind
+				if (user.canRewindRef(repository)) {
+					String baseURL = WicketUtils.getGitblitURL(RequestCycle.get().getRequest());
+					return SparkleShareInviteServlet.asLink(baseURL, repository.name, username);
+				}
+			} else {
+				// Gitblit as viewer, assume RW+ permission
+				String baseURL = WicketUtils.getGitblitURL(RequestCycle.get().getRequest());
+				return SparkleShareInviteServlet.asLink(baseURL, repository.name, username);
+			}
+		}
+		return null;
+	}
+
 	protected List<ProjectModel> getProjectModels() {
 		final UserModel user = GitBlitWebSession.get().getUser();
 		List<ProjectModel> projects = GitBlit.self().getProjectModels(user, true);
diff --git a/src/main/java/com/gitblit/wicket/pages/SummaryPage.html b/src/main/java/com/gitblit/wicket/pages/SummaryPage.html
index a751d1f..c9bce40 100644
--- a/src/main/java/com/gitblit/wicket/pages/SummaryPage.html
+++ b/src/main/java/com/gitblit/wicket/pages/SummaryPage.html
@@ -26,6 +26,7 @@
 				    	<div wicket:id="otherUrls" >
 				    		<div wicket:id="otherUrl" style="padding-top:10px"></div>
 				    	</div>
+				    	<div wicket:id="repositorySparkleShareInviteUrl">[repository sparkleshare invite url]</div>
 				    </td>
 				</tr>
 			</table>
diff --git a/src/main/java/com/gitblit/wicket/pages/SummaryPage.java b/src/main/java/com/gitblit/wicket/pages/SummaryPage.java
index 863974b..f092a38 100644
--- a/src/main/java/com/gitblit/wicket/pages/SummaryPage.java
+++ b/src/main/java/com/gitblit/wicket/pages/SummaryPage.java
@@ -60,6 +60,7 @@
 import com.gitblit.wicket.panels.DetailedRepositoryUrlPanel;
 import com.gitblit.wicket.panels.LinkPanel;
 import com.gitblit.wicket.panels.LogPanel;
+import com.gitblit.wicket.panels.SparkleShareInvitePanel;
 import com.gitblit.wicket.panels.TagsPanel;
 
 public class SummaryPage extends RepositoryPage {
@@ -167,6 +168,15 @@
 		}
 		add(gitDaemonUrlPanel);
 		
+		String sparkleshareUrl = getSparkleShareInviteUrl(model);
+		if (StringUtils.isEmpty(sparkleshareUrl)) {
+			add(new Label("repositorySparkleShareInviteUrl").setVisible(false));
+		} else {
+			Component sparklesharePanel = new SparkleShareInvitePanel("repositorySparkleShareInviteUrl", getLocalizer(), this, sparkleshareUrl, accessPermission);
+			WicketUtils.setCssStyle(sparklesharePanel, "padding-top: 10px;");
+			add(sparklesharePanel);
+		}
+
 		ListDataProvider<String> urls = new ListDataProvider<String>(repositoryUrls);
 		DataView<String> otherUrlsView = new DataView<String>("otherUrls", urls) {
 			private static final long serialVersionUID = 1L;
diff --git a/src/main/java/com/gitblit/wicket/panels/SparkleShareInvitePanel.html b/src/main/java/com/gitblit/wicket/panels/SparkleShareInvitePanel.html
new file mode 100644
index 0000000..483050c
--- /dev/null
+++ b/src/main/java/com/gitblit/wicket/panels/SparkleShareInvitePanel.html
@@ -0,0 +1,18 @@
+<!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"> 
+
+<wicket:panel>
+   	<span class="repositoryUrlContainer">
+		<span class="repositoryUrlEndCap">
+			<img wicket:id="sparkleshareIcon"></img>
+		</span>
+		<span class="repositoryUrl">
+			<a wicket:id="inviteUrl"><wicket:message key="gb.sparkleshareInvite"></wicket:message></a>
+		</span>
+		<span class="hidden-phone hidden-tablet repositoryUrlEndCap" wicket:id="accessPermission">[access permission]</span>
+	</span>
+</wicket:panel>
+</html>
\ No newline at end of file
diff --git a/src/main/java/com/gitblit/wicket/panels/SparkleShareInvitePanel.java b/src/main/java/com/gitblit/wicket/panels/SparkleShareInvitePanel.java
new file mode 100644
index 0000000..5d7aa58
--- /dev/null
+++ b/src/main/java/com/gitblit/wicket/panels/SparkleShareInvitePanel.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2013 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.panels;
+
+import org.apache.wicket.Component;
+import org.apache.wicket.Localizer;
+import org.apache.wicket.markup.html.basic.Label;
+import org.apache.wicket.markup.html.image.ContextImage;
+import org.apache.wicket.markup.html.link.ExternalLink;
+
+import com.gitblit.Constants.AccessPermission;
+import com.gitblit.wicket.WicketUtils;
+
+public class SparkleShareInvitePanel extends BasePanel {
+
+	private static final long serialVersionUID = 1L;
+
+	public SparkleShareInvitePanel(String wicketId, Localizer localizer, Component parent, String url, AccessPermission ap) {
+		super(wicketId);
+		ContextImage star = WicketUtils.newImage("sparkleshareIcon", "star_16x16.png");
+		add(star);
+		add(new ExternalLink("inviteUrl", url));
+		String note = localizer.getString("gb.externalAccess", parent);
+		String permission = "";
+		if (ap != null) {
+			permission = ap.toString();
+			if (ap.atLeast(AccessPermission.PUSH)) {
+				note = localizer.getString("gb.readWriteAccess", parent);
+			} else if (ap.atLeast(AccessPermission.CLONE)) {
+				note = localizer.getString("gb.readOnlyAccess", parent);
+			} else {
+				note = localizer.getString("gb.viewAccess", parent);
+			}
+		}
+		Label label = new Label("accessPermission", permission);
+		WicketUtils.setHtmlTooltip(label, note);
+		add(label);
+	}
+}

--
Gitblit v1.9.1