- Use standard ServletRequestWrapper instead of custom wrapper (issue 224) | - Use standard ServletRequestWrapper instead of custom wrapper (issue 224) | ||||
changes: | 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) | - Improve Gerrit change ref decoration in the refs panel (issue 206) | ||||
- Disable Gson's pretty printing which has a huge performance gain | - Disable Gson's pretty printing which has a huge performance gain | ||||
- Properly set application/json content-type on api calls | - Properly set application/json content-type on api calls | ||||
- Updated Japanese translation | - Updated Japanese translation | ||||
additions: | 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 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. | - 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 | - Option to automatically tag branch tips on each push with an incremental revision number | ||||
- { name: 'git.daemonBindInterface', defaultValue: 'localhost' } | - { name: 'git.daemonBindInterface', defaultValue: 'localhost' } | ||||
- { name: 'git.daemonPort', defaultValue: 0 } | - { name: 'git.daemonPort', defaultValue: 0 } | ||||
- { name: 'git.defaultIncrementalPushTagPrefix', defaultValue: 'r' } | - { name: 'git.defaultIncrementalPushTagPrefix', defaultValue: 'r' } | ||||
- { name: 'web.allowAppCloneLinks', defaultValue: true } | |||||
- { name: 'web.forceDefaultLocale', defaultValue: ' ' } | - { name: 'web.forceDefaultLocale', defaultValue: ' ' } | ||||
- { name: 'server.nioThreadPoolSize', defaultValue: 50 } | |||||
- { name: 'server.nioThreadPoolSize', defaultValue: 50 } | |||||
} | } | ||||
# | # |
# SINCE 0.5.0 | # SINCE 0.5.0 | ||||
web.otherUrls = | 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. | # Choose how to present the repositories list. | ||||
# grouped = group nested/subfolder repositories together (no sorting) | # grouped = group nested/subfolder repositories together (no sorting) | ||||
# flat = flat list of repositories (sorting allowed) | # flat = flat list of repositories (sorting allowed) | ||||
# RESTART REQUIRED | # RESTART REQUIRED | ||||
server.useNio = true | 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 | # 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. | # path if running Gitblit behind a proxy layer such as mod_proxy. | ||||
# | # |
// conditionally configure the http connector | // conditionally configure the http connector | ||||
if (params.port > 0) { | 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); | String bindInterface = settings.getString(Keys.server.httpBindInterface, null); | ||||
if (!StringUtils.isEmpty(bindInterface)) { | if (!StringUtils.isEmpty(bindInterface)) { | ||||
logger.warn(MessageFormat.format("Binding connector on port {0,number,0} to {1}", | logger.warn(MessageFormat.format("Binding connector on port {0,number,0} to {1}", | ||||
if (serverKeyStore.exists()) { | if (serverKeyStore.exists()) { | ||||
Connector secureConnector = createSSLConnector(params.alias, serverKeyStore, serverTrustStore, params.storePassword, | 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); | String bindInterface = settings.getString(Keys.server.httpsBindInterface, null); | ||||
if (!StringUtils.isEmpty(bindInterface)) { | if (!StringUtils.isEmpty(bindInterface)) { | ||||
logger.warn(MessageFormat.format( | logger.warn(MessageFormat.format( | ||||
* | * | ||||
* @param useNIO | * @param useNIO | ||||
* @param port | * @param port | ||||
* @param maxThreads | |||||
* @param maxThreads | |||||
* @return an http connector | * @return an http connector | ||||
*/ | */ | ||||
private Connector createConnector(boolean useNIO, int port, int maxThreads) { | |||||
private Connector createConnector(boolean useNIO, int port, int maxThreads) { | |||||
Connector connector; | Connector connector; | ||||
if (useNIO) { | if (useNIO) { | ||||
logger.info("Setting up NIO SelectChannelConnector on port " + port); | logger.info("Setting up NIO SelectChannelConnector on port " + port); | ||||
SelectChannelConnector nioconn = new SelectChannelConnector(); | SelectChannelConnector nioconn = new SelectChannelConnector(); | ||||
nioconn.setSoLingerTime(-1); | nioconn.setSoLingerTime(-1); | ||||
nioconn.setThreadPool(new QueuedThreadPool(maxThreads)); | |||||
nioconn.setThreadPool(new QueuedThreadPool(maxThreads)); | |||||
connector = nioconn; | connector = nioconn; | ||||
} else { | } else { | ||||
logger.info("Setting up SocketConnector on port " + port); | logger.info("Setting up SocketConnector on port " + port); | ||||
* @param storePassword | * @param storePassword | ||||
* @param caRevocationList | * @param caRevocationList | ||||
* @param useNIO | * @param useNIO | ||||
* @param nioThreadPoolSize | |||||
* @param nioThreadPoolSize | |||||
* @param port | * @param port | ||||
* @param requireClientCertificates | * @param requireClientCertificates | ||||
* @return an https connector | * @return an https connector | ||||
*/ | */ | ||||
private Connector createSSLConnector(String certAlias, File keyStore, File clientTrustStore, | 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) { | boolean requireClientCertificates) { | ||||
GitblitSslContextFactory factory = new GitblitSslContextFactory(certAlias, | GitblitSslContextFactory factory = new GitblitSslContextFactory(certAlias, | ||||
keyStore, clientTrustStore, storePassword, caRevocationList); | keyStore, clientTrustStore, storePassword, caRevocationList); | ||||
} else { | } else { | ||||
factory.setWantClientAuth(true); | factory.setWantClientAuth(true); | ||||
} | } | ||||
ssl.setThreadPool(new QueuedThreadPool(nioThreadPoolSize)); | |||||
ssl.setThreadPool(new QueuedThreadPool(nioThreadPoolSize)); | |||||
connector = ssl; | connector = ssl; | ||||
} else { | } else { | ||||
logger.info("Setting up NIO SslSocketConnector on port " + port); | logger.info("Setting up NIO SslSocketConnector on port " + port); |
gb.externalPermissions = {0} access permissions for {1} are externally maintained | gb.externalPermissions = {0} access permissions for {1} are externally maintained | ||||
gb.viewAccess = You do not have Gitblit read or write access | gb.viewAccess = You do not have Gitblit read or write access | ||||
gb.yourProtocolPermissionIs = Your {0} access permission for {1} is {2} | 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 |
import com.gitblit.Constants.FederationStrategy; | import com.gitblit.Constants.FederationStrategy; | ||||
import com.gitblit.GitBlit; | import com.gitblit.GitBlit; | ||||
import com.gitblit.Keys; | import com.gitblit.Keys; | ||||
import com.gitblit.SparkleShareInviteServlet; | |||||
import com.gitblit.models.ProjectModel; | import com.gitblit.models.ProjectModel; | ||||
import com.gitblit.models.RepositoryModel; | import com.gitblit.models.RepositoryModel; | ||||
import com.gitblit.models.TeamModel; | import com.gitblit.models.TeamModel; | ||||
} | } | ||||
} | } | ||||
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() { | protected List<ProjectModel> getProjectModels() { | ||||
final UserModel user = GitBlitWebSession.get().getUser(); | final UserModel user = GitBlitWebSession.get().getUser(); | ||||
List<ProjectModel> projects = GitBlit.self().getProjectModels(user, true); | List<ProjectModel> projects = GitBlit.self().getProjectModels(user, true); |
package com.gitblit.wicket.pages; | package com.gitblit.wicket.pages; | ||||
import java.text.MessageFormat; | import java.text.MessageFormat; | ||||
import java.util.ArrayList; | |||||
import java.util.List; | |||||
import org.apache.wicket.PageParameters; | import org.apache.wicket.PageParameters; | ||||
import org.apache.wicket.markup.html.basic.Label; | import org.apache.wicket.markup.html.basic.Label; | ||||
import com.gitblit.Constants.AccessPermission; | |||||
import com.gitblit.GitBlit; | import com.gitblit.GitBlit; | ||||
import com.gitblit.Keys; | |||||
import com.gitblit.models.RepositoryModel; | import com.gitblit.models.RepositoryModel; | ||||
import com.gitblit.models.UserModel; | import com.gitblit.models.UserModel; | ||||
import com.gitblit.utils.ArrayUtils; | |||||
import com.gitblit.wicket.GitBlitWebSession; | import com.gitblit.wicket.GitBlitWebSession; | ||||
import com.gitblit.wicket.GitblitRedirectException; | import com.gitblit.wicket.GitblitRedirectException; | ||||
import com.gitblit.wicket.WicketUtils; | import com.gitblit.wicket.WicketUtils; | ||||
import com.gitblit.wicket.panels.DetailedRepositoryUrlPanel; | |||||
import com.gitblit.wicket.panels.RepositoryUrlPanel; | |||||
public class EmptyRepositoryPage extends RootPage { | public class EmptyRepositoryPage extends RootPage { | ||||
setupPage(repositoryName, getString("gb.emptyRepository")); | 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(); | UserModel user = GitBlitWebSession.get().getUser(); | ||||
if (user == null) { | if (user == null) { | ||||
user = UserModel.ANONYMOUS; | 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 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))); | add(new Label("remoteSyntax", MessageFormat.format("git remote add gitblit {0}\ngit push gitblit master", primaryUrl))); | ||||
} | } | ||||
} | } |
<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><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> | <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;"> | <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="otherUrls" > | ||||
<div wicket:id="otherUrl" style="padding-top:10px"></div> | <div wicket:id="otherUrl" style="padding-top:10px"></div> | ||||
</div> | </div> | ||||
<div wicket:id="repositorySparkleShareInviteUrl">[repository sparkleshare invite url]</div> | |||||
</td> | </td> | ||||
</tr> | </tr> | ||||
</table> | </table> |
import org.wicketstuff.googlecharts.MarkerType; | import org.wicketstuff.googlecharts.MarkerType; | ||||
import org.wicketstuff.googlecharts.ShapeMarker; | import org.wicketstuff.googlecharts.ShapeMarker; | ||||
import com.gitblit.Constants.AccessPermission; | |||||
import com.gitblit.Constants.AccessRestrictionType; | import com.gitblit.Constants.AccessRestrictionType; | ||||
import com.gitblit.GitBlit; | import com.gitblit.GitBlit; | ||||
import com.gitblit.Keys; | import com.gitblit.Keys; | ||||
import com.gitblit.models.PathModel; | import com.gitblit.models.PathModel; | ||||
import com.gitblit.models.RepositoryModel; | import com.gitblit.models.RepositoryModel; | ||||
import com.gitblit.models.UserModel; | import com.gitblit.models.UserModel; | ||||
import com.gitblit.utils.ArrayUtils; | |||||
import com.gitblit.utils.JGitUtils; | import com.gitblit.utils.JGitUtils; | ||||
import com.gitblit.utils.MarkdownUtils; | import com.gitblit.utils.MarkdownUtils; | ||||
import com.gitblit.utils.StringUtils; | import com.gitblit.utils.StringUtils; | ||||
import com.gitblit.wicket.panels.DetailedRepositoryUrlPanel; | import com.gitblit.wicket.panels.DetailedRepositoryUrlPanel; | ||||
import com.gitblit.wicket.panels.LinkPanel; | import com.gitblit.wicket.panels.LinkPanel; | ||||
import com.gitblit.wicket.panels.LogPanel; | import com.gitblit.wicket.panels.LogPanel; | ||||
import com.gitblit.wicket.panels.SparkleShareInvitePanel; | |||||
import com.gitblit.wicket.panels.RepositoryUrlPanel; | |||||
import com.gitblit.wicket.panels.TagsPanel; | import com.gitblit.wicket.panels.TagsPanel; | ||||
public class SummaryPage extends RepositoryPage { | public class SummaryPage extends RepositoryPage { | ||||
add(new BookmarkablePageLink<Void>("metrics", MetricsPage.class, | add(new BookmarkablePageLink<Void>("metrics", MetricsPage.class, | ||||
WicketUtils.newRepositoryParameter(repositoryName))); | WicketUtils.newRepositoryParameter(repositoryName))); | ||||
List<String> repositoryUrls = new ArrayList<String>(); | |||||
AccessPermission accessPermission = null; | |||||
if (GitBlit.getBoolean(Keys.git.enableGitServlet, true)) { | if (GitBlit.getBoolean(Keys.git.enableGitServlet, true)) { | ||||
accessPermission = user.getRepositoryPermission(model).permission; | |||||
AccessRestrictionType accessRestriction = getRepositoryModel().accessRestriction; | AccessRestrictionType accessRestriction = getRepositoryModel().accessRestriction; | ||||
switch (accessRestriction) { | switch (accessRestriction) { | ||||
case NONE: | case NONE: | ||||
default: | default: | ||||
add(WicketUtils.newClearPixel("accessRestrictionIcon").setVisible(false)); | add(WicketUtils.newClearPixel("accessRestrictionIcon").setVisible(false)); | ||||
} | } | ||||
// add the Gitblit repository url | |||||
repositoryUrls.add(getRepositoryUrl(getRepositoryModel())); | |||||
} else { | } else { | ||||
add(WicketUtils.newClearPixel("accessRestrictionIcon").setVisible(false)); | 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) { | DataView<String> otherUrlsView = new DataView<String>("otherUrls", urls) { | ||||
private static final long serialVersionUID = 1L; | private static final long serialVersionUID = 1L; | ||||
import org.apache.wicket.Component; | import org.apache.wicket.Component; | ||||
import org.apache.wicket.markup.html.panel.Panel; | import org.apache.wicket.markup.html.panel.Panel; | ||||
import org.apache.wicket.model.Model; | import org.apache.wicket.model.Model; | ||||
import org.apache.wicket.protocol.http.request.WebClientInfo; | |||||
import com.gitblit.Constants; | import com.gitblit.Constants; | ||||
import com.gitblit.GitBlit; | import com.gitblit.GitBlit; | ||||
} | } | ||||
return timeUtils; | 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) { | protected void setPersonSearchTooltip(Component component, String value, Constants.SearchType searchType) { | ||||
if (searchType.equals(Constants.SearchType.AUTHOR)) { | if (searchType.equals(Constants.SearchType.AUTHOR)) { |
package com.gitblit.wicket.panels; | package com.gitblit.wicket.panels; | ||||
import java.text.MessageFormat; | import java.text.MessageFormat; | ||||
import java.util.ArrayList; | |||||
import java.util.List; | |||||
import java.util.Map; | import java.util.Map; | ||||
import org.apache.wicket.Component; | import org.apache.wicket.Component; | ||||
import org.apache.wicket.markup.html.link.Link; | import org.apache.wicket.markup.html.link.Link; | ||||
import org.apache.wicket.markup.html.panel.Fragment; | import org.apache.wicket.markup.html.panel.Fragment; | ||||
import com.gitblit.Constants.AccessPermission; | |||||
import com.gitblit.Constants.AccessRestrictionType; | import com.gitblit.Constants.AccessRestrictionType; | ||||
import com.gitblit.GitBlit; | import com.gitblit.GitBlit; | ||||
import com.gitblit.Keys; | import com.gitblit.Keys; | ||||
import com.gitblit.utils.StringUtils; | import com.gitblit.utils.StringUtils; | ||||
import com.gitblit.wicket.GitBlitWebSession; | import com.gitblit.wicket.GitBlitWebSession; | ||||
import com.gitblit.wicket.WicketUtils; | import com.gitblit.wicket.WicketUtils; | ||||
import com.gitblit.wicket.pages.BasePage; | |||||
import com.gitblit.wicket.pages.DocsPage; | import com.gitblit.wicket.pages.DocsPage; | ||||
import com.gitblit.wicket.pages.EditRepositoryPage; | import com.gitblit.wicket.pages.EditRepositoryPage; | ||||
import com.gitblit.wicket.pages.LogPage; | import com.gitblit.wicket.pages.LogPage; | ||||
super(wicketId); | super(wicketId); | ||||
final boolean showSwatch = GitBlit.getBoolean(Keys.web.repositoryListSwatches, true); | 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); | final boolean showSize = GitBlit.getBoolean(Keys.web.showRepositorySizes, true); | ||||
// repository swatch | // repository swatch | ||||
add(new ExternalLink("syndication", SyndicationServlet.asLink("", entry.name, null, 0))); | 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)); | |||||
} | } | ||||
} | } |
lang="en"> | lang="en"> | ||||
<wicket:panel> | <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> | </wicket:panel> | ||||
</html> | </html> |
*/ | */ | ||||
package com.gitblit.wicket.panels; | package com.gitblit.wicket.panels; | ||||
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.html.basic.Label; | ||||
import org.apache.wicket.markup.html.image.ContextImage; | |||||
import org.apache.wicket.markup.html.panel.Fragment; | |||||
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.GitBlit; | ||||
import com.gitblit.Keys; | 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.utils.StringUtils; | ||||
import com.gitblit.wicket.GitBlitWebSession; | |||||
import com.gitblit.wicket.WicketUtils; | 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 { | public class RepositoryUrlPanel extends BasePanel { | ||||
private static final long serialVersionUID = 1L; | 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); | 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 { | } 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; | |||||
} | } | ||||
} | } | ||||
} | } |
<!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> |
/* | |||||
* 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); | |||||
} | |||||
} |