From 8c92559a483cf0b01b33d926421ec17605b5ff75 Mon Sep 17 00:00:00 2001 From: James Moger <james.moger@gitblit.com> Date: Fri, 03 May 2013 19:27:25 -0400 Subject: [PATCH] Partially merged url panel with optional support for app clone urls --- src/main/java/com/gitblit/wicket/panels/BasePanel.java | 14 + src/main/java/com/gitblit/wicket/panels/RepositoryUrlPanel.java | 227 ++++++++++++++++++++++++++-- src/main/distrib/data/gitblit.properties | 19 + src/main/java/com/gitblit/wicket/GitBlitWebApp.properties | 5 src/main/java/com/gitblit/wicket/pages/EmptyRepositoryPage.java | 22 -- src/main/java/com/gitblit/wicket/panels/ProjectRepositoryPanel.java | 16 - src/main/java/com/gitblit/wicket/pages/BasePage.java | 27 --- src/main/java/com/gitblit/GitBlitServer.java | 16 +- /dev/null | 52 ------ src/main/java/com/gitblit/wicket/pages/SummaryPage.java | 34 --- releases.moxie | 7 src/main/java/com/gitblit/wicket/panels/RepositoryUrlPanel.html | 30 +-- src/main/java/com/gitblit/wicket/pages/SummaryPage.html | 4 13 files changed, 272 insertions(+), 201 deletions(-) diff --git a/releases.moxie b/releases.moxie index 7c0ec8f..53aef4b 100644 --- a/releases.moxie +++ b/releases.moxie @@ -26,6 +26,7 @@ - Use standard ServletRequestWrapper instead of custom wrapper (issue 224) changes: + - Improved the repository url display. This display now indicates your repository access permission, per-protocol. - Improve Gerrit change ref decoration in the refs panel (issue 206) - Disable Gson's pretty printing which has a huge performance gain - Properly set application/json content-type on api calls @@ -33,7 +34,8 @@ - Updated Japanese translation additions: - - Added GO http/https connector thread pool size setting + - Added 3rd-party app clone links for SourceTree and SparkleShare + - Added GO http/https connector thread pool size setting - Added a server setting to force a particular translation/Locale for all sessions - Added smart Git Daemon serving. If enabled, git:// access will be offered for any repository which permits anonymous access. If the repository permits anonymous cloning, anonymous git:// clone will be permitted while anonmymous git:// pushes will be rejected. - Option to automatically tag branch tips on each push with an incremental revision number @@ -81,8 +83,9 @@ - { name: 'git.daemonBindInterface', defaultValue: 'localhost' } - { name: 'git.daemonPort', defaultValue: 0 } - { name: 'git.defaultIncrementalPushTagPrefix', defaultValue: 'r' } + - { name: 'web.allowAppCloneLinks', defaultValue: true } - { name: 'web.forceDefaultLocale', defaultValue: ' ' } - - { name: 'server.nioThreadPoolSize', defaultValue: 50 } + - { name: 'server.nioThreadPoolSize', defaultValue: 50 } } # diff --git a/src/main/distrib/data/gitblit.properties b/src/main/distrib/data/gitblit.properties index 43185f5..bfdc3e8 100644 --- a/src/main/distrib/data/gitblit.properties +++ b/src/main/distrib/data/gitblit.properties @@ -766,6 +766,11 @@ # SINCE 0.5.0 web.otherUrls = +# Should app-specific clone links be displayed for SourceTree, SparkleShare, etc? +# +# SINCE 1.3.0 +web.allowAppCloneLinks = true + # Choose how to present the repositories list. # grouped = group nested/subfolder repositories together (no sorting) # flat = flat list of repositories (sorting allowed) @@ -1250,13 +1255,13 @@ # RESTART REQUIRED server.useNio = true -# If using Jetty NIO connectors, specify the maximum number of concurrent -# http/https worker threads to allow. -# -# SINCE 1.3.0 -# RESTART REQUIRED -server.nioThreadPoolSize = 50 - +# If using Jetty NIO connectors, specify the maximum number of concurrent +# http/https worker threads to allow. +# +# SINCE 1.3.0 +# RESTART REQUIRED +server.nioThreadPoolSize = 50 + # Context path for the GO application. You might want to change the context # path if running Gitblit behind a proxy layer such as mod_proxy. # diff --git a/src/main/java/com/gitblit/GitBlitServer.java b/src/main/java/com/gitblit/GitBlitServer.java index a41b8a2..2c558be 100644 --- a/src/main/java/com/gitblit/GitBlitServer.java +++ b/src/main/java/com/gitblit/GitBlitServer.java @@ -203,7 +203,7 @@ // conditionally configure the http connector if (params.port > 0) { - Connector httpConnector = createConnector(params.useNIO, settings.getInteger(Keys.server.nioThreadPoolSize, 50), params.port); + Connector httpConnector = createConnector(params.useNIO, settings.getInteger(Keys.server.nioThreadPoolSize, 50), params.port); String bindInterface = settings.getString(Keys.server.httpBindInterface, null); if (!StringUtils.isEmpty(bindInterface)) { logger.warn(MessageFormat.format("Binding connector on port {0,number,0} to {1}", @@ -262,7 +262,7 @@ if (serverKeyStore.exists()) { Connector secureConnector = createSSLConnector(params.alias, serverKeyStore, serverTrustStore, params.storePassword, - caRevocationList, params.useNIO, settings.getInteger(Keys.server.nioThreadPoolSize, 50), params.securePort, params.requireClientCertificates); + caRevocationList, params.useNIO, settings.getInteger(Keys.server.nioThreadPoolSize, 50), params.securePort, params.requireClientCertificates); String bindInterface = settings.getString(Keys.server.httpsBindInterface, null); if (!StringUtils.isEmpty(bindInterface)) { logger.warn(MessageFormat.format( @@ -410,16 +410,16 @@ * * @param useNIO * @param port - * @param maxThreads + * @param maxThreads * @return an http connector */ - private Connector createConnector(boolean useNIO, int port, int maxThreads) { + private Connector createConnector(boolean useNIO, int port, int maxThreads) { Connector connector; if (useNIO) { logger.info("Setting up NIO SelectChannelConnector on port " + port); SelectChannelConnector nioconn = new SelectChannelConnector(); nioconn.setSoLingerTime(-1); - nioconn.setThreadPool(new QueuedThreadPool(maxThreads)); + nioconn.setThreadPool(new QueuedThreadPool(maxThreads)); connector = nioconn; } else { logger.info("Setting up SocketConnector on port " + port); @@ -444,13 +444,13 @@ * @param storePassword * @param caRevocationList * @param useNIO - * @param nioThreadPoolSize + * @param nioThreadPoolSize * @param port * @param requireClientCertificates * @return an https connector */ private Connector createSSLConnector(String certAlias, File keyStore, File clientTrustStore, - String storePassword, File caRevocationList, boolean useNIO, int nioThreadPoolSize, int port, + String storePassword, File caRevocationList, boolean useNIO, int nioThreadPoolSize, int port, boolean requireClientCertificates) { GitblitSslContextFactory factory = new GitblitSslContextFactory(certAlias, keyStore, clientTrustStore, storePassword, caRevocationList); @@ -464,7 +464,7 @@ } else { factory.setWantClientAuth(true); } - ssl.setThreadPool(new QueuedThreadPool(nioThreadPoolSize)); + ssl.setThreadPool(new QueuedThreadPool(nioThreadPoolSize)); connector = ssl; } else { logger.info("Setting up NIO SslSocketConnector on port " + port); diff --git a/src/main/java/com/gitblit/wicket/GitBlitWebApp.properties b/src/main/java/com/gitblit/wicket/GitBlitWebApp.properties index 049cc40..4752e99 100644 --- a/src/main/java/com/gitblit/wicket/GitBlitWebApp.properties +++ b/src/main/java/com/gitblit/wicket/GitBlitWebApp.properties @@ -452,4 +452,7 @@ 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} -gb.sparkleshareInvite = SparkleShare invite \ No newline at end of file +gb.cloneWithSparkleShare = clone with SparkleShare\u2122 +gb.cloneWithSourceTree = clone with SourceTree\u2122 +gb.cloneWithGitHub = clone with GitHub\u2122 for {0} +gb.cloneWithSmartGit = clone with SmartGit\u2122 \ 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 b3b0767..19fa749 100644 --- a/src/main/java/com/gitblit/wicket/pages/BasePage.java +++ b/src/main/java/com/gitblit/wicket/pages/BasePage.java @@ -57,7 +57,6 @@ 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,32 +310,6 @@ // git daemon is not running 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() { diff --git a/src/main/java/com/gitblit/wicket/pages/EmptyRepositoryPage.java b/src/main/java/com/gitblit/wicket/pages/EmptyRepositoryPage.java index a2264c4..f7e8848 100644 --- a/src/main/java/com/gitblit/wicket/pages/EmptyRepositoryPage.java +++ b/src/main/java/com/gitblit/wicket/pages/EmptyRepositoryPage.java @@ -16,22 +16,17 @@ package com.gitblit.wicket.pages; import java.text.MessageFormat; -import java.util.ArrayList; -import java.util.List; import org.apache.wicket.PageParameters; import org.apache.wicket.markup.html.basic.Label; -import com.gitblit.Constants.AccessPermission; import com.gitblit.GitBlit; -import com.gitblit.Keys; import com.gitblit.models.RepositoryModel; import com.gitblit.models.UserModel; -import com.gitblit.utils.ArrayUtils; import com.gitblit.wicket.GitBlitWebSession; import com.gitblit.wicket.GitblitRedirectException; import com.gitblit.wicket.WicketUtils; -import com.gitblit.wicket.panels.DetailedRepositoryUrlPanel; +import com.gitblit.wicket.panels.RepositoryUrlPanel; public class EmptyRepositoryPage extends RootPage { @@ -53,24 +48,17 @@ setupPage(repositoryName, getString("gb.emptyRepository")); - List<String> repositoryUrls = new ArrayList<String>(); - - if (GitBlit.getBoolean(Keys.git.enableGitServlet, true)) { - // add the Gitblit repository url - repositoryUrls.add(getRepositoryUrl(repository)); - } UserModel user = GitBlitWebSession.get().getUser(); if (user == null) { user = UserModel.ANONYMOUS; } - repositoryUrls.addAll(GitBlit.self().getOtherCloneUrls(repositoryName, UserModel.ANONYMOUS.equals(user) ? "" : user.username)); - String primaryUrl = ArrayUtils.isEmpty(repositoryUrls) ? "" : repositoryUrls.get(0); - AccessPermission accessPermission = user.getRepositoryPermission(repository).permission; + RepositoryUrlPanel urlPanel = new RepositoryUrlPanel("pushurl", false, user, repository, getLocalizer(), this); + String primaryUrl = urlPanel.getPrimaryUrl(); add(new Label("repository", repositoryName)); - add(new DetailedRepositoryUrlPanel("pushurl", getLocalizer(), this, repository.name, primaryUrl, accessPermission)); - add(new Label("cloneSyntax", MessageFormat.format("git clone {0}", repositoryUrls.get(0)))); + add(urlPanel); + add(new Label("cloneSyntax", MessageFormat.format("git clone {0}", primaryUrl))); add(new Label("remoteSyntax", MessageFormat.format("git remote add gitblit {0}\ngit push gitblit master", primaryUrl))); } } diff --git a/src/main/java/com/gitblit/wicket/pages/SummaryPage.html b/src/main/java/com/gitblit/wicket/pages/SummaryPage.html index c9bce40..a20ad2f 100644 --- a/src/main/java/com/gitblit/wicket/pages/SummaryPage.html +++ b/src/main/java/com/gitblit/wicket/pages/SummaryPage.html @@ -21,12 +21,10 @@ <tr><th><wicket:message key="gb.stats">[stats]</wicket:message></th><td><span wicket:id="branchStats">[branch stats]</span> <span class="link"><a wicket:id="metrics"><wicket:message key="gb.metrics">[metrics]</wicket:message></a></span></td></tr> <tr><th style="vertical-align:top;padding-top:4px;"><wicket:message key="gb.repositoryUrl">[URL]</wicket:message> <img style="vertical-align: top;padding-left:3px;" wicket:id="accessRestrictionIcon" /></th> <td style="padding-top:4px;"> - <div wicket:id="repositoryPrimaryUrl">[repository primary url]</div> - <div wicket:id="repositoryGitDaemonUrl">[repository git daemon url]</div> + <div wicket:id="repositoryUrlPanel">[repository url panel]</div> <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 f092a38..7588a93 100644 --- a/src/main/java/com/gitblit/wicket/pages/SummaryPage.java +++ b/src/main/java/com/gitblit/wicket/pages/SummaryPage.java @@ -42,7 +42,6 @@ import org.wicketstuff.googlecharts.MarkerType; import org.wicketstuff.googlecharts.ShapeMarker; -import com.gitblit.Constants.AccessPermission; import com.gitblit.Constants.AccessRestrictionType; import com.gitblit.GitBlit; import com.gitblit.Keys; @@ -50,7 +49,6 @@ import com.gitblit.models.PathModel; import com.gitblit.models.RepositoryModel; import com.gitblit.models.UserModel; -import com.gitblit.utils.ArrayUtils; import com.gitblit.utils.JGitUtils; import com.gitblit.utils.MarkdownUtils; import com.gitblit.utils.StringUtils; @@ -60,7 +58,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.RepositoryUrlPanel; import com.gitblit.wicket.panels.TagsPanel; public class SummaryPage extends RepositoryPage { @@ -127,11 +125,7 @@ add(new BookmarkablePageLink<Void>("metrics", MetricsPage.class, WicketUtils.newRepositoryParameter(repositoryName))); - List<String> repositoryUrls = new ArrayList<String>(); - - AccessPermission accessPermission = null; if (GitBlit.getBoolean(Keys.git.enableGitServlet, true)) { - accessPermission = user.getRepositoryPermission(model).permission; AccessRestrictionType accessRestriction = getRepositoryModel().accessRestriction; switch (accessRestriction) { case NONE: @@ -152,32 +146,14 @@ default: add(WicketUtils.newClearPixel("accessRestrictionIcon").setVisible(false)); } - // add the Gitblit repository url - repositoryUrls.add(getRepositoryUrl(getRepositoryModel())); } else { add(WicketUtils.newClearPixel("accessRestrictionIcon").setVisible(false)); } - repositoryUrls.addAll(GitBlit.self().getOtherCloneUrls(repositoryName, UserModel.ANONYMOUS.equals(user) ? "" : user.username)); - String primaryUrl = ArrayUtils.isEmpty(repositoryUrls) ? "" : repositoryUrls.remove(0); - add(new DetailedRepositoryUrlPanel("repositoryPrimaryUrl", getLocalizer(), this, model.name, primaryUrl, accessPermission)); - - Component gitDaemonUrlPanel = createGitDaemonUrlPanel("repositoryGitDaemonUrl", user, model); - if (!StringUtils.isEmpty(primaryUrl) && gitDaemonUrlPanel instanceof DetailedRepositoryUrlPanel) { - WicketUtils.setCssStyle(gitDaemonUrlPanel, "padding-top: 10px"); - } - 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); + add(new RepositoryUrlPanel("repositoryUrlPanel", false, user, model, getLocalizer(), this)); + + List<String> otherUrls = GitBlit.self().getOtherCloneUrls(repositoryName, UserModel.ANONYMOUS.equals(user) ? "" : user.username); + ListDataProvider<String> urls = new ListDataProvider<String>(otherUrls); DataView<String> otherUrlsView = new DataView<String>("otherUrls", urls) { private static final long serialVersionUID = 1L; diff --git a/src/main/java/com/gitblit/wicket/panels/BasePanel.java b/src/main/java/com/gitblit/wicket/panels/BasePanel.java index ec87917..e241a43 100644 --- a/src/main/java/com/gitblit/wicket/panels/BasePanel.java +++ b/src/main/java/com/gitblit/wicket/panels/BasePanel.java @@ -22,6 +22,7 @@ import org.apache.wicket.Component; import org.apache.wicket.markup.html.panel.Panel; import org.apache.wicket.model.Model; +import org.apache.wicket.protocol.http.request.WebClientInfo; import com.gitblit.Constants; import com.gitblit.GitBlit; @@ -57,6 +58,19 @@ } return timeUtils; } + + protected boolean isWindows() { + return isPlatform("windows"); + } + + protected boolean isMac() { + return isPlatform("macintosh"); + } + + protected boolean isPlatform(String platform) { + String ua = ((WebClientInfo) GitBlitWebSession.get().getClientInfo()).getUserAgent(); + return ua.toLowerCase().contains(platform); + } protected void setPersonSearchTooltip(Component component, String value, Constants.SearchType searchType) { if (searchType.equals(Constants.SearchType.AUTHOR)) { diff --git a/src/main/java/com/gitblit/wicket/panels/ProjectRepositoryPanel.java b/src/main/java/com/gitblit/wicket/panels/ProjectRepositoryPanel.java index 822a9b2..7cce74f 100644 --- a/src/main/java/com/gitblit/wicket/panels/ProjectRepositoryPanel.java +++ b/src/main/java/com/gitblit/wicket/panels/ProjectRepositoryPanel.java @@ -16,8 +16,6 @@ package com.gitblit.wicket.panels; import java.text.MessageFormat; -import java.util.ArrayList; -import java.util.List; import java.util.Map; import org.apache.wicket.Component; @@ -29,7 +27,6 @@ import org.apache.wicket.markup.html.link.Link; import org.apache.wicket.markup.html.panel.Fragment; -import com.gitblit.Constants.AccessPermission; import com.gitblit.Constants.AccessRestrictionType; import com.gitblit.GitBlit; import com.gitblit.Keys; @@ -40,7 +37,6 @@ import com.gitblit.utils.StringUtils; import com.gitblit.wicket.GitBlitWebSession; import com.gitblit.wicket.WicketUtils; -import com.gitblit.wicket.pages.BasePage; import com.gitblit.wicket.pages.DocsPage; import com.gitblit.wicket.pages.EditRepositoryPage; import com.gitblit.wicket.pages.LogPage; @@ -58,7 +54,6 @@ super(wicketId); final boolean showSwatch = GitBlit.getBoolean(Keys.web.repositoryListSwatches, true); - final boolean gitServlet = GitBlit.getBoolean(Keys.git.enableGitServlet, true); final boolean showSize = GitBlit.getBoolean(Keys.web.showRepositorySizes, true); // repository swatch @@ -217,15 +212,6 @@ add(new ExternalLink("syndication", SyndicationServlet.asLink("", entry.name, null, 0))); - List<String> repositoryUrls = new ArrayList<String>(); - if (gitServlet) { - // add the Gitblit repository url - repositoryUrls.add(BasePage.getRepositoryUrl(entry)); - } - repositoryUrls.addAll(GitBlit.self().getOtherCloneUrls(entry.name, UserModel.ANONYMOUS.equals(user) ? "" : user.username)); - - AccessPermission ap = user.getRepositoryPermission(entry).permission; - String primaryUrl = ArrayUtils.isEmpty(repositoryUrls) ? "" : repositoryUrls.remove(0); - add(new DetailedRepositoryUrlPanel("repositoryPrimaryUrl",localizer, parent, entry.name, primaryUrl, ap)); + add(new RepositoryUrlPanel("repositoryPrimaryUrl", true, user, entry, localizer, parent)); } } diff --git a/src/main/java/com/gitblit/wicket/panels/RepositoryUrlPanel.html b/src/main/java/com/gitblit/wicket/panels/RepositoryUrlPanel.html index d7c76f1..c32d9d8 100644 --- a/src/main/java/com/gitblit/wicket/panels/RepositoryUrlPanel.html +++ b/src/main/java/com/gitblit/wicket/panels/RepositoryUrlPanel.html @@ -5,26 +5,14 @@ lang="en"> <wicket:panel> - <span wicket:id="repositoryUrl" style="color: blue;">[repository url]</span><span class="hidden-phone hidden-tablet" wicket:id="copyFunction"></span> - - <!-- Plain JavaScript manual copy & paste --> - <wicket:fragment wicket:id="jsPanel"> - <span style="vertical-align:baseline;"> - <img wicket:id="copyIcon" wicket:message="title:gb.copyToClipboard"></img> - </span> - </wicket:fragment> - - <!-- flash-based button-press copy & paste --> - <wicket:fragment wicket:id="clippyPanel"> - <object wicket:message="title:gb.copyToClipboard" style="vertical-align:middle;" - wicket:id="clippy" - width="14" - height="14" - bgcolor="#ffffff" - quality="high" - wmode="transparent" - scale="noscale" - allowScriptAccess="always"></object> - </wicket:fragment> + <div wicket:id="repositoryPrimaryUrl">[repository primary url]</div> + <div style="padding-top: 2px;"> + <span class="link" wicket:id="appCloneLink"> + <span wicket:id="icon"></span> + <span wicket:id="link"></span> + <span wicket:id="separator" style="padding: 0px 5px 0px 5px;"></span> + </span> + </div> + <div wicket:id="repositoryGitDaemonUrl">[repository git daemon url]</div> </wicket:panel> </html> \ No newline at end of file diff --git a/src/main/java/com/gitblit/wicket/panels/RepositoryUrlPanel.java b/src/main/java/com/gitblit/wicket/panels/RepositoryUrlPanel.java index 58df028..3afb7b1 100644 --- a/src/main/java/com/gitblit/wicket/panels/RepositoryUrlPanel.java +++ b/src/main/java/com/gitblit/wicket/panels/RepositoryUrlPanel.java @@ -15,37 +15,226 @@ */ package com.gitblit.wicket.panels; -import org.apache.wicket.markup.html.basic.Label; -import org.apache.wicket.markup.html.image.ContextImage; -import org.apache.wicket.markup.html.panel.Fragment; +import java.io.Serializable; +import java.text.MessageFormat; +import java.util.ArrayList; +import java.util.List; +import org.apache.wicket.Component; +import org.apache.wicket.Localizer; +import org.apache.wicket.RequestCycle; +import org.apache.wicket.markup.html.basic.Label; +import org.apache.wicket.markup.repeater.Item; +import org.apache.wicket.markup.repeater.data.DataView; +import org.apache.wicket.markup.repeater.data.ListDataProvider; +import org.apache.wicket.protocol.http.WebRequest; + +import com.gitblit.Constants; +import com.gitblit.Constants.AccessPermission; +import com.gitblit.Constants.AccessRestrictionType; import com.gitblit.GitBlit; import com.gitblit.Keys; +import com.gitblit.SparkleShareInviteServlet; +import com.gitblit.models.RepositoryModel; +import com.gitblit.models.UserModel; import com.gitblit.utils.StringUtils; +import com.gitblit.wicket.GitBlitWebSession; import com.gitblit.wicket.WicketUtils; +/** + * Smart repository url panel which can display multiple Gitblit repository urls + * and also supports 3rd party app clone links. + * + * @author James Moger + * + */ public class RepositoryUrlPanel extends BasePanel { private static final long serialVersionUID = 1L; + + private final String primaryUrl; - public RepositoryUrlPanel(String wicketId, String url) { + public RepositoryUrlPanel(String wicketId, boolean onlyPrimary, UserModel user, + RepositoryModel repository, Localizer localizer, Component owner) { super(wicketId); - add(new Label("repositoryUrl", url)); - if (GitBlit.getBoolean(Keys.web.allowFlashCopyToClipboard, true)) { - // clippy: flash-based copy & paste - Fragment fragment = new Fragment("copyFunction", "clippyPanel", this); - String baseUrl = WicketUtils.getGitblitURL(getRequest()); - ShockWaveComponent clippy = new ShockWaveComponent("clippy", baseUrl + "/clippy.swf"); - clippy.setValue("flashVars", "text=" + StringUtils.encodeURL(url)); - fragment.add(clippy); - add(fragment); + if (user == null) { + user = UserModel.ANONYMOUS; + } + List<String> repositoryUrls = new ArrayList<String>(); + + AccessPermission accessPermission = null; + if (GitBlit.getBoolean(Keys.git.enableGitServlet, true)) { + accessPermission = user.getRepositoryPermission(repository).permission; + repositoryUrls.add(getRepositoryUrl(repository)); + } + repositoryUrls.addAll(GitBlit.self().getOtherCloneUrls(repository.name, UserModel.ANONYMOUS.equals(user) ? "" : user.username)); + + primaryUrl = repositoryUrls.size() == 0 ? "" : repositoryUrls.remove(0); + + add(new DetailedRepositoryUrlPanel("repositoryPrimaryUrl", localizer, owner, repository.name, primaryUrl, accessPermission)); + + if (!onlyPrimary) { + Component gitDaemonUrlPanel = createGitDaemonUrlPanel("repositoryGitDaemonUrl", user, repository); + if (!StringUtils.isEmpty(primaryUrl) && gitDaemonUrlPanel instanceof DetailedRepositoryUrlPanel) { + WicketUtils.setCssStyle(gitDaemonUrlPanel, "padding-top: 10px"); + } + add(gitDaemonUrlPanel); } else { - // javascript: manual copy & paste with modal browser prompt dialog - Fragment fragment = new Fragment("copyFunction", "jsPanel", this); - ContextImage img = WicketUtils.newImage("copyIcon", "clippy.png"); - img.add(new JavascriptTextPrompt("onclick", "Copy to Clipboard (Ctrl+C, Enter)", url)); - fragment.add(img); - add(fragment); + add(new Label("repositoryGitDaemonUrl").setVisible(false)); + } + + final List<AppCloneLink> cloneLinks = new ArrayList<AppCloneLink>(); + if (user.canClone(repository) && GitBlit.getBoolean(Keys.web.allowAppCloneLinks, true)) { + // universal app clone urls +// cloneLinks.add(new AppCloneLink(localizer.getString("gb.cloneWithSmartGit", owner), +// MessageFormat.format("smartgit://cloneRepo/{0}", primaryUrl), +// "Syntevo SmartGit\u2122")); + + if (isWindows()) { + // Windows client app clone urls + cloneLinks.add(new AppCloneLink(localizer.getString("gb.cloneWithSourceTree", owner), + MessageFormat.format("sourcetree://cloneRepo/{0}", primaryUrl), + "Atlassian SourceTree\u2122")); +// cloneLinks.add(new AppCloneLink( +// MessageFormat.format(localizer.getString("gb.cloneWithGitHub", owner), "Windows"), +// MessageFormat.format("github-windows://openRepo/{0}", primaryUrl))); + } else if (isMac()) { + // Mac client app clone urls + cloneLinks.add(new AppCloneLink(localizer.getString("gb.cloneWithSourceTree", owner), + MessageFormat.format("sourcetree://cloneRepo/{0}", primaryUrl), + "Atlassian SourceTree\u2122")); +// cloneLinks.add(new AppCloneLink( +// MessageFormat.format(localizer.getString("gb.cloneWithGitHub", owner), "Mac"), +// MessageFormat.format("github-mac://openRepo/{0}", primaryUrl))); + } + + // sparkleshare invite url + String sparkleshareUrl = getSparkleShareInviteUrl(user, repository); + if (!StringUtils.isEmpty(sparkleshareUrl)) { + cloneLinks.add(new AppCloneLink(localizer.getString("gb.cloneWithSparkleShare", owner), + sparkleshareUrl, "SparkleShare \u2122", "icon-star")); + } + } + + // app clone links + ListDataProvider<AppCloneLink> appLinks = new ListDataProvider<AppCloneLink>(cloneLinks); + DataView<AppCloneLink> appCloneLinks = new DataView<AppCloneLink>("appCloneLink", appLinks) { + private static final long serialVersionUID = 1L; + int count; + + public void populateItem(final Item<AppCloneLink> item) { + final AppCloneLink appLink = item.getModelObject(); + item.add(new Label("icon", MessageFormat.format("<i class=\"{0}\"></i>", appLink.icon)).setEscapeModelStrings(false)); + LinkPanel linkPanel = new LinkPanel("link", null, appLink.name, appLink.url); + if (!StringUtils.isEmpty(appLink.tooltip)) { + WicketUtils.setHtmlTooltip(linkPanel, appLink.tooltip); + } + item.add(linkPanel); + item.add(new Label("separator", "|").setVisible(count < (cloneLinks.size() - 1))); + count++; + } + }; + add(appCloneLinks); + } + + public String getPrimaryUrl() { + return primaryUrl; + } + + protected String getRepositoryUrl(RepositoryModel repository) { + StringBuilder sb = new StringBuilder(); + sb.append(WicketUtils.getGitblitURL(RequestCycle.get().getRequest())); + sb.append(Constants.GIT_PATH); + sb.append(repository.name); + + // inject username into repository url if authentication is required + if (repository.accessRestriction.exceeds(AccessRestrictionType.NONE) + && GitBlitWebSession.get().isLoggedIn()) { + String username = GitBlitWebSession.get().getUsername(); + sb.insert(sb.indexOf("://") + 3, username + "@"); + } + return sb.toString(); + } + + protected Component createGitDaemonUrlPanel(String wicketId, UserModel user, RepositoryModel repository) { + int gitDaemonPort = GitBlit.getInteger(Keys.git.daemonPort, 0); + if (gitDaemonPort > 0 && user.canClone(repository)) { + String servername = ((WebRequest) getRequest()).getHttpServletRequest().getServerName(); + String gitDaemonUrl; + if (gitDaemonPort == 9418) { + // standard port + gitDaemonUrl = MessageFormat.format("git://{0}/{1}", servername, repository.name); + } else { + // non-standard port + gitDaemonUrl = MessageFormat.format("git://{0}:{1,number,0}/{2}", servername, gitDaemonPort, repository.name); + } + + AccessPermission gitDaemonPermission = user.getRepositoryPermission(repository).permission;; + if (gitDaemonPermission.atLeast(AccessPermission.CLONE)) { + if (repository.accessRestriction.atLeast(AccessRestrictionType.CLONE)) { + // can not authenticate clone via anonymous git protocol + gitDaemonPermission = AccessPermission.NONE; + } else if (repository.accessRestriction.atLeast(AccessRestrictionType.PUSH)) { + // can not authenticate push via anonymous git protocol + gitDaemonPermission = AccessPermission.CLONE; + } else { + // normal user permission + } + } + + if (AccessPermission.NONE.equals(gitDaemonPermission)) { + // repository prohibits all anonymous access + return new Label(wicketId).setVisible(false); + } else { + // repository allows some form of anonymous access + return new DetailedRepositoryUrlPanel(wicketId, getLocalizer(), this, repository.name, gitDaemonUrl, gitDaemonPermission); + } + } else { + // git daemon is not running + return new Label(wicketId).setVisible(false); + } + } + + protected String getSparkleShareInviteUrl(UserModel user, RepositoryModel repository) { + if (repository.isBare && repository.isSparkleshared()) { + 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; + } + + static class AppCloneLink implements Serializable { + + private static final long serialVersionUID = 1L; + + final String name; + final String url; + final String tooltip; + final String icon; + + public AppCloneLink(String name, String url, String tooltip) { + this(name, url, tooltip, "icon-download"); + } + + public AppCloneLink(String name, String url, String tooltip, String icon) { + this.name = name; + this.url = url; + this.tooltip = tooltip; + this.icon = icon; } } } diff --git a/src/main/java/com/gitblit/wicket/panels/SparkleShareInvitePanel.html b/src/main/java/com/gitblit/wicket/panels/SparkleShareInvitePanel.html deleted file mode 100644 index 483050c..0000000 --- a/src/main/java/com/gitblit/wicket/panels/SparkleShareInvitePanel.html +++ /dev/null @@ -1,18 +0,0 @@ -<!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 deleted file mode 100644 index 5d7aa58..0000000 --- a/src/main/java/com/gitblit/wicket/panels/SparkleShareInvitePanel.java +++ /dev/null @@ -1,52 +0,0 @@ -/* - * 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