diff --git a/scm-core/src/main/java/sonia/scm/repository/RepositoryTypePredicate.java b/scm-core/src/main/java/sonia/scm/repository/RepositoryTypePredicate.java new file mode 100644 index 0000000000..7b6eb0b302 --- /dev/null +++ b/scm-core/src/main/java/sonia/scm/repository/RepositoryTypePredicate.java @@ -0,0 +1,77 @@ +/** + * Copyright (c) 2010, Sebastian Sdorra All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. 2. Redistributions in + * binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. 3. Neither the name of SCM-Manager; + * nor the names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * http://bitbucket.org/sdorra/scm-manager + * + */ + + + +package sonia.scm.repository; + +//~--- non-JDK imports -------------------------------------------------------- + +import com.google.common.base.Predicate; + +/** + * + * @author Sebastian Sdorra + * @since 1.16 + */ +public class RepositoryTypePredicate implements Predicate +{ + + /** + * Constructs ... + * + * + * @param type + */ + public RepositoryTypePredicate(String type) + { + this.type = type; + } + + //~--- methods -------------------------------------------------------------- + + /** + * Method description + * + * + * @param repository + * + * @return + */ + @Override + public boolean apply(Repository repository) + { + return type.equalsIgnoreCase(repository.getType()); + } + + //~--- fields --------------------------------------------------------------- + + /** Field description */ + private String type; +} diff --git a/scm-core/src/main/java/sonia/scm/repository/RepositoryUtil.java b/scm-core/src/main/java/sonia/scm/repository/RepositoryUtil.java index 639fc55e25..d44e7e50b1 100644 --- a/scm-core/src/main/java/sonia/scm/repository/RepositoryUtil.java +++ b/scm-core/src/main/java/sonia/scm/repository/RepositoryUtil.java @@ -38,7 +38,9 @@ package sonia.scm.repository; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import sonia.scm.config.ScmConfiguration; import sonia.scm.io.DirectoryFileFilter; +import sonia.scm.util.HttpUtil; import sonia.scm.util.IOUtil; //~--- JDK imports ------------------------------------------------------------ @@ -50,6 +52,8 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.List; +import javax.servlet.http.HttpServletRequest; + /** * * @author Sebastian Sdorra @@ -64,6 +68,59 @@ public class RepositoryUtil //~--- methods -------------------------------------------------------------- + /** + * Method description + * + * + * @param configuration + * @param repositoryManager + * @param repository + * + * @since 1.16 + */ + public static void appendUrl(ScmConfiguration configuration, + RepositoryManager repositoryManager, + Repository repository) + { + RepositoryHandler handler = + repositoryManager.getHandler(repository.getType()); + + if (handler != null) + { + String url = handler.createResourcePath(repository); + + url = HttpUtil.getCompleteUrl(configuration, url); + repository.setUrl(url); + } + } + + /** + * Method description + * + * + * + * @param request + * @param repositoryManager + * @param repository + * + * @since 1.16 + */ + public static void appendUrl(HttpServletRequest request, + RepositoryManager repositoryManager, + Repository repository) + { + RepositoryHandler handler = + repositoryManager.getHandler(repository.getType()); + + if (handler != null) + { + String url = handler.createResourcePath(repository); + + url = HttpUtil.getCompleteUrl(request, url); + repository.setUrl(url); + } + } + /** * Method description * diff --git a/scm-core/src/main/java/sonia/scm/util/HttpUtil.java b/scm-core/src/main/java/sonia/scm/util/HttpUtil.java index 8ed7af3967..f598bcc9a9 100644 --- a/scm-core/src/main/java/sonia/scm/util/HttpUtil.java +++ b/scm-core/src/main/java/sonia/scm/util/HttpUtil.java @@ -155,7 +155,7 @@ public class HttpUtil */ public static String append(String uri, String suffix) { - if (!uri.endsWith(SEPARATOR_PATH)) + if (!uri.endsWith(SEPARATOR_PATH) && !suffix.startsWith(SEPARATOR_PATH)) { uri = uri.concat(SEPARATOR_PATH); } @@ -293,6 +293,34 @@ public class HttpUtil //~--- get methods ---------------------------------------------------------- + /** + * Returns an absolute url with context path. + * + * + * @param request http client request + * @param pathSegments + * + * @return absolute url with context path + * @since 1.16 + */ + public static String getCompleteUrl(HttpServletRequest request, + String... pathSegments) + { + String baseUrl = + request.getRequestURL().toString().replace(request.getRequestURI(), + Util.EMPTY_STRING).concat(request.getContextPath()); + + if (Util.isNotEmpty(pathSegments)) + { + for (String ps : pathSegments) + { + baseUrl = append(baseUrl, ps); + } + } + + return baseUrl; + } + /** * Return the complete url of the given path. * diff --git a/scm-core/src/main/java/sonia/scm/web/filter/PermissionFilter.java b/scm-core/src/main/java/sonia/scm/web/filter/PermissionFilter.java index 181c743232..a584c821e5 100644 --- a/scm-core/src/main/java/sonia/scm/web/filter/PermissionFilter.java +++ b/scm-core/src/main/java/sonia/scm/web/filter/PermissionFilter.java @@ -35,6 +35,7 @@ package sonia.scm.web.filter; //~--- non-JDK imports -------------------------------------------------------- +import com.google.common.base.Splitter; import com.google.inject.Provider; import org.slf4j.Logger; @@ -49,12 +50,15 @@ import sonia.scm.security.ScmSecurityException; import sonia.scm.user.User; import sonia.scm.util.AssertUtil; import sonia.scm.util.HttpUtil; +import sonia.scm.util.Util; import sonia.scm.web.security.WebSecurityContext; //~--- JDK imports ------------------------------------------------------------ import java.io.IOException; +import java.util.Iterator; + import javax.servlet.FilterChain; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; @@ -172,6 +176,17 @@ public abstract class PermissionFilter extends HttpFilter response.sendError(HttpServletResponse.SC_NOT_FOUND); } } + catch (IllegalStateException ex) + { + if (logger.isWarnEnabled()) + { + logger.warn( + "wrong request at ".concat(request.getRequestURI()).concat( + " send redirect"), ex); + } + + response.sendRedirect(getRepositoryRootHelpUrl(request)); + } catch (ScmSecurityException ex) { if (logger.isWarnEnabled()) @@ -193,6 +208,29 @@ public abstract class PermissionFilter extends HttpFilter } } + /** + * Method description + * + * + * @param request + * + * @return + */ + private String extractType(HttpServletRequest request) + { + Iterator it = Splitter.on( + HttpUtil.SEPARATOR_PATH).omitEmptyStrings().split( + request.getRequestURI()).iterator(); + String type = it.next(); + + if (Util.isNotEmpty(request.getContextPath())) + { + type = it.next(); + } + + return type; + } + /** * Method description * @@ -217,6 +255,25 @@ public abstract class PermissionFilter extends HttpFilter //~--- get methods ---------------------------------------------------------- + /** + * Method description + * + * + * @param request + * + * @return + */ + private String getRepositoryRootHelpUrl(HttpServletRequest request) + { + String type = extractType(request); + String helpUrl = HttpUtil.getCompleteUrl(request, + "/api/rest/help/repository-root/"); + + helpUrl = helpUrl.concat(type).concat(".html"); + + return helpUrl; + } + /** * Method description * diff --git a/scm-core/src/main/java/sonia/scm/web/filter/ProviderPermissionFilter.java b/scm-core/src/main/java/sonia/scm/web/filter/ProviderPermissionFilter.java index e1b233f5ab..6d9abe73b1 100644 --- a/scm-core/src/main/java/sonia/scm/web/filter/ProviderPermissionFilter.java +++ b/scm-core/src/main/java/sonia/scm/web/filter/ProviderPermissionFilter.java @@ -35,8 +35,14 @@ package sonia.scm.web.filter; //~--- non-JDK imports -------------------------------------------------------- +import com.google.common.base.Throwables; import com.google.inject.Provider; +import com.google.inject.ProvisionException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import sonia.scm.config.ScmConfiguration; import sonia.scm.repository.Repository; import sonia.scm.repository.RepositoryProvider; import sonia.scm.web.security.WebSecurityContext; @@ -44,7 +50,6 @@ import sonia.scm.web.security.WebSecurityContext; //~--- JDK imports ------------------------------------------------------------ import javax.servlet.http.HttpServletRequest; -import sonia.scm.config.ScmConfiguration; /** * @@ -54,10 +59,20 @@ import sonia.scm.config.ScmConfiguration; public abstract class ProviderPermissionFilter extends PermissionFilter { + /** + * the logger for ProviderPermissionFilter + */ + private static final Logger logger = + LoggerFactory.getLogger(ProviderPermissionFilter.class); + + //~--- constructors --------------------------------------------------------- + /** * Constructs ... * * + * + * @param configuration * @param securityContextProvider * @param repositoryProvider */ @@ -83,7 +98,24 @@ public abstract class ProviderPermissionFilter extends PermissionFilter @Override protected Repository getRepository(HttpServletRequest request) { - return repositoryProvider.get(); + Repository repository = null; + + try + { + repository = repositoryProvider.get(); + } + catch (ProvisionException ex) + { + Throwables.propagateIfInstanceOf(ex.getCause(), + IllegalStateException.class); + + if (logger.isErrorEnabled()) + { + logger.error("could not get repository from request", ex); + } + } + + return repository; } //~--- fields --------------------------------------------------------------- diff --git a/scm-webapp/src/main/java/sonia/scm/api/rest/resources/RepositoryResource.java b/scm-webapp/src/main/java/sonia/scm/api/rest/resources/RepositoryResource.java index d3491532cc..8880bc6a70 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/rest/resources/RepositoryResource.java +++ b/scm-webapp/src/main/java/sonia/scm/api/rest/resources/RepositoryResource.java @@ -65,6 +65,7 @@ import sonia.scm.repository.RepositoryHandler; import sonia.scm.repository.RepositoryIsNotArchivedException; import sonia.scm.repository.RepositoryManager; import sonia.scm.repository.RepositoryNotFoundException; +import sonia.scm.repository.RepositoryUtil; import sonia.scm.security.ScmSecurityException; import sonia.scm.util.AssertUtil; import sonia.scm.util.HttpUtil; @@ -779,7 +780,7 @@ public class RepositoryResource { for (Repository repository : repositories) { - appendUrl(repository); + RepositoryUtil.appendUrl(configuration, repositoryManager, repository); prepareRepository(repository); } @@ -797,7 +798,7 @@ public class RepositoryResource @Override protected Repository prepareForReturn(Repository repository) { - appendUrl(repository); + RepositoryUtil.appendUrl(configuration, repositoryManager, repository); prepareRepository(repository); return repository; @@ -833,26 +834,6 @@ public class RepositoryResource //~--- methods -------------------------------------------------------------- - /** - * Method description - * - * - * @param repository - */ - private void appendUrl(Repository repository) - { - RepositoryHandler handler = - repositoryManager.getHandler(repository.getType()); - - if (handler != null) - { - String url = handler.createResourcePath(repository); - - url = HttpUtil.getCompleteUrl(configuration, url); - repository.setUrl(url); - } - } - /** * Method description * diff --git a/scm-webapp/src/main/java/sonia/scm/api/rest/resources/RepositoryRootResource.java b/scm-webapp/src/main/java/sonia/scm/api/rest/resources/RepositoryRootResource.java new file mode 100644 index 0000000000..9a11e74a6f --- /dev/null +++ b/scm-webapp/src/main/java/sonia/scm/api/rest/resources/RepositoryRootResource.java @@ -0,0 +1,349 @@ +/** + * Copyright (c) 2010, Sebastian Sdorra All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. 2. Redistributions in + * binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. 3. Neither the name of SCM-Manager; + * nor the names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * http://bitbucket.org/sdorra/scm-manager + * + */ + + + +package sonia.scm.api.rest.resources; + +//~--- non-JDK imports -------------------------------------------------------- + +import com.google.common.base.Function; +import com.google.common.collect.Collections2; +import com.google.common.collect.Maps; +import com.google.common.collect.Ordering; +import com.google.inject.Inject; + +import sonia.scm.repository.Repository; +import sonia.scm.repository.RepositoryManager; +import sonia.scm.repository.RepositoryTypePredicate; +import sonia.scm.repository.RepositoryUtil; +import sonia.scm.template.TemplateHandler; +import sonia.scm.url.UrlProvider; +import sonia.scm.url.UrlProviderFactory; +import sonia.scm.util.HttpUtil; + +//~--- JDK imports ------------------------------------------------------------ + +import java.io.IOException; +import java.io.StringWriter; + +import java.util.Collection; +import java.util.Comparator; +import java.util.List; +import java.util.Map; + +import javax.servlet.http.HttpServletRequest; + +import javax.ws.rs.GET; +import javax.ws.rs.Path; +import javax.ws.rs.PathParam; +import javax.ws.rs.Produces; +import javax.ws.rs.core.Context; +import javax.ws.rs.core.MediaType; + +/** + * + * @author Sebastian Sdorra + */ +@Path("help/repository-root/{type}.html") +public class RepositoryRootResource +{ + + /** Field description */ + public static final String TEMPLATE = "/repository-root"; + + //~--- constructors --------------------------------------------------------- + + /** + * Constructs ... + * + * + * + * @param templateHandler + * @param repositoryManager + */ + @Inject + public RepositoryRootResource(TemplateHandler templateHandler, + RepositoryManager repositoryManager) + { + this.templateHandler = templateHandler; + this.repositoryManager = repositoryManager; + } + + //~--- methods -------------------------------------------------------------- + + /** + * Method description + * + * + * + * @param request + * @param type + * + * @return + * + * @throws IOException + */ + @GET + @Produces(MediaType.TEXT_HTML) + public String renderRepositoriesRoot(@Context HttpServletRequest request, + @PathParam("type") final String type) + throws IOException + { + UrlProvider uiUrlProvider = + UrlProviderFactory.createUrlProvider(HttpUtil.getCompleteUrl(request), + UrlProviderFactory.TYPE_WUI); + //J- + Collection unsortedRepositories = + Collections2.transform( + Collections2.filter( + repositoryManager.getAll(), new RepositoryTypePredicate(type)) + , new RepositoryTransformFunction(request, repositoryManager, uiUrlProvider) + ); + + List repositories = Ordering.from( + new RepositoryTemplateElementComparator() + ).sortedCopy(unsortedRepositories); + //J+ + Map environment = Maps.newHashMap(); + + environment.put("repositories", repositories); + + StringWriter writer = new StringWriter(); + + templateHandler.render(TEMPLATE, writer, environment); + + return writer.toString(); + } + + //~--- inner classes -------------------------------------------------------- + + /** + * Class description + * + * + * @version Enter version here..., 12/05/28 + * @author Enter your name here... + */ + public static class RepositoryTemplateElement + { + + /** + * Constructs ... + * + * + * @param repository + * @param uiUrlProvider + */ + public RepositoryTemplateElement(Repository repository, + UrlProvider uiUrlProvider) + { + this.repository = repository; + this.urlProvider = uiUrlProvider; + } + + //~--- get methods -------------------------------------------------------- + + /** + * Method description + * + * + * @return + */ + public String getCommitUrl() + { + return urlProvider.getRepositoryUrlProvider().getChangesetUrl( + repository.getId(), 0, 20); + } + + /** + * Method description + * + * + * @return + */ + public String getDetailUrl() + { + return urlProvider.getRepositoryUrlProvider().getDetailUrl( + repository.getId()); + } + + /** + * Method description + * + * + * @return + */ + public String getName() + { + return repository.getName(); + } + + /** + * Method description + * + * + * @return + */ + public Repository getRepository() + { + return repository; + } + + /** + * Method description + * + * + * @return + */ + public String getSourceUrl() + { + return urlProvider.getRepositoryUrlProvider().getBrowseUrl( + repository.getId(), null, null); + } + + /** + * Method description + * + * + * @return + */ + public String getUrl() + { + return repository.getUrl(); + } + + //~--- fields ------------------------------------------------------------- + + /** Field description */ + private Repository repository; + + /** Field description */ + private UrlProvider urlProvider; + } + + + /** + * Class description + * + * + * @version Enter version here..., 12/05/29 + * @author Enter your name here... + */ + private static class RepositoryTemplateElementComparator + implements Comparator + { + + /** + * Method description + * + * + * @param left + * @param right + * + * @return + */ + @Override + public int compare(RepositoryTemplateElement left, + RepositoryTemplateElement right) + { + return left.getName().compareTo(right.getName()); + } + } + + + /** + * Class description + * + * + * @version Enter version here..., 12/05/28 + * @author Enter your name here... + */ + private static class RepositoryTransformFunction + implements Function + { + + /** + * Constructs ... + * + * + * + * + * @param request + * @param repositoryManager + * @param urlProvider + */ + public RepositoryTransformFunction(HttpServletRequest request, + RepositoryManager repositoryManager, + UrlProvider urlProvider) + { + this.request = request; + this.repositoryManager = repositoryManager; + this.urlProvider = urlProvider; + } + + //~--- methods ------------------------------------------------------------ + + /** + * Method description + * + * + * @param repository + * + * @return + */ + @Override + public RepositoryTemplateElement apply(Repository repository) + { + RepositoryUtil.appendUrl(request, repositoryManager, repository); + + return new RepositoryTemplateElement(repository, urlProvider); + } + + //~--- fields ------------------------------------------------------------- + + /** Field description */ + private RepositoryManager repositoryManager; + + /** Field description */ + private HttpServletRequest request; + + /** Field description */ + private UrlProvider urlProvider; + } + + + //~--- fields --------------------------------------------------------------- + + /** Field description */ + private RepositoryManager repositoryManager; + + /** Field description */ + private TemplateHandler templateHandler; +} diff --git a/scm-webapp/src/main/resources/templates/repository-root.html b/scm-webapp/src/main/resources/templates/repository-root.html new file mode 100644 index 0000000000..b8ee88f7f9 --- /dev/null +++ b/scm-webapp/src/main/resources/templates/repository-root.html @@ -0,0 +1,102 @@ + + + + + SCM-Manager support information + + + + +

SCM-Manager Repositories

+ + + + +