James Moger
2013-05-03 8c92559a483cf0b01b33d926421ec17605b5ff75
Partially merged url panel with optional support for app clone urls
2 files deleted
12 files modified
491 ■■■■■ changed files
releases.moxie 7 ●●●● patch | view | raw | blame | history
src/main/distrib/data/gitblit.properties 19 ●●●●● patch | view | raw | blame | history
src/main/java/com/gitblit/GitBlitServer.java 16 ●●●● patch | view | raw | blame | history
src/main/java/com/gitblit/wicket/GitBlitWebApp.properties 5 ●●●● patch | view | raw | blame | history
src/main/java/com/gitblit/wicket/pages/BasePage.java 27 ●●●●● patch | view | raw | blame | history
src/main/java/com/gitblit/wicket/pages/EmptyRepositoryPage.java 22 ●●●● patch | view | raw | blame | history
src/main/java/com/gitblit/wicket/pages/SummaryPage.html 4 ●●● patch | view | raw | blame | history
src/main/java/com/gitblit/wicket/pages/SummaryPage.java 34 ●●●● patch | view | raw | blame | history
src/main/java/com/gitblit/wicket/panels/BasePanel.java 14 ●●●●● patch | view | raw | blame | history
src/main/java/com/gitblit/wicket/panels/ProjectRepositoryPanel.java 16 ●●●●● patch | view | raw | blame | history
src/main/java/com/gitblit/wicket/panels/RepositoryUrlPanel.html 30 ●●●●● patch | view | raw | blame | history
src/main/java/com/gitblit/wicket/panels/RepositoryUrlPanel.java 227 ●●●●● patch | view | raw | blame | history
src/main/java/com/gitblit/wicket/panels/SparkleShareInvitePanel.html 18 ●●●●● patch | view | raw | blame | history
src/main/java/com/gitblit/wicket/panels/SparkleShareInvitePanel.java 52 ●●●●● patch | view | raw | blame | history
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 }
}
#
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.
#
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);
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
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
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() {
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)));
    }
}
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>&nbsp;<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>
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;
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)) {
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));
    }
}
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>
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;
        }
    }
}
src/main/java/com/gitblit/wicket/panels/SparkleShareInvitePanel.html
File was deleted
src/main/java/com/gitblit/wicket/panels/SparkleShareInvitePanel.java
File was deleted