diff --git a/CHANGELOG.md b/CHANGELOG.md index 16e999d1e9..b51e448ef6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,11 +6,22 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Unreleased ### Added -- Add generic popover component to ui-components +- Sign PR merges and commits performed through ui with generated private key ([#1285](https://github.com/scm-manager/scm-manager/pull/1285)) +- Add generic popover component to ui-components ([#1285](https://github.com/scm-manager/scm-manager/pull/1285)) - Show changeset signatures in ui and add public keys ([#1273](https://github.com/scm-manager/scm-manager/pull/1273)) -## [2.3.0] - 2020-07-23 +## [2.3.1] - 2020-08-04 +### Added +- New api to resolve SCM-Manager root url ([#1276](https://github.com/scm-manager/scm-manager/pull/1276)) +### Changed +- Help tooltips are now mutliline by default ([#1271](https://github.com/scm-manager/scm-manager/pull/1271)) + +### Fixed +- Fixed unnecessary horizontal scrollbar in modal dialogs ([#1271](https://github.com/scm-manager/scm-manager/pull/1271)) +- Avoid stacktrace logging when protocol url is accessed outside of request scope ([#1276](https://github.com/scm-manager/scm-manager/pull/1276)) + +## [2.3.0] - 2020-07-23 ### Added - Add branch link provider to access branch links in plugins ([#1243](https://github.com/scm-manager/scm-manager/pull/1243)) - Add key value input field component ([#1246](https://github.com/scm-manager/scm-manager/pull/1246)) @@ -250,3 +261,4 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 [2.1.1]: https://www.scm-manager.org/download/2.1.1 [2.2.0]: https://www.scm-manager.org/download/2.2.0 [2.3.0]: https://www.scm-manager.org/download/2.3.0 +[2.3.1]: https://www.scm-manager.org/download/2.3.1 diff --git a/docs/en/administration/logging.md b/docs/en/administration/logging.md index 412ecbac1d..308ad5b744 100644 --- a/docs/en/administration/logging.md +++ b/docs/en/administration/logging.md @@ -25,7 +25,7 @@ The location of the file depends also on the type of installation. | Type of Installation | Path | |----------------------|---------| -| Docker | /opt/scm-server/conf/logging.xml | +| Docker | /etc/scm/logging.xml | | RPM | /etc/scm/logging.xml | | DEB | /etc/scm/logging.xml | | Unix | $EXTRACT_PATH/scm-server/conf/logging.xml | diff --git a/docs/en/faq.md b/docs/en/faq.md index ef64b4d9db..64a8957395 100644 --- a/docs/en/faq.md +++ b/docs/en/faq.md @@ -40,3 +40,7 @@ After changing the configuration, SCM-Manager must be restarted. ### How do I install plugins? Find the plugin you like to install at [plugins](/plugins#categories) and follow the installation instructions on the install page of the plugin. + +### How can I import my existing (git|mercurial|subversion) repository + +Please have a look on [these](../import/) detailed instructions. diff --git a/docs/en/import.md b/docs/en/import.md new file mode 100644 index 0000000000..fd4d9e6d99 --- /dev/null +++ b/docs/en/import.md @@ -0,0 +1,55 @@ +--- +title: Import existing repositories +subtitle: How to import existing repositories into SCM-Manager +displayToc: true +--- + +## Git + +First you have to clone the old repository with the `mirror` option. +This option ensures that all branches and tags are fetched from the remote repository. +Assuming that your remote repository is accessible under the url `https://hgttg.com/r/git/heart-of-gold`, the clone command should look like this: + +```bash +git clone --mirror https://hgttg.com/r/git/heart-of-gold +``` + +Than you have to create your new repository via the SCM-Manager web interface and copy the url. +In this example we assume that the new repository is available at `https://hitchhiker.com/scm/repo/hgttg/heart-of-gold`. After the new repository is created, we can configure our local repository for the new location and push all refs. + +```bash +cd heart-of-gold +git remote set-url origin https://hitchhiker.com/scm/repo/hgttg/heart-of-gold +git push --mirror +``` + +## Mercurial + +To import an existing mercurial repository, we have to create a new repository over the SCM-Manager web interface, clone it, pull from the old repository and push to the new repository. +In this example we assume that the old repository is `https://hgttg.com/r/hg/heart-of-gold` and the newly created is located at `https://hitchhiker.com/scm/repo/hgttg/heart-of-gold`: + +```bash +hg clone https://hitchhiker.com/scm/repo/hgttg/heart-of-gold +cd heart-of-gold +hg pull https://hgttg.com/r/hg/heart-of-gold +hg push +``` + +## Subversion + +Subversion is not as easy as mercurial or git. +For subversion we have to locate the old repository on the filesystem and create a dump with the `svnadmin` tool. + +```bash +svnadmin dump /path/to/repo > oldrepo.dump +``` + +Now we have to create a new repository via the SCM-Manager web interface. +After the repository is created, we have to find its location on the filesystem. +This could be done by finding the directory with the newest timestamp in your scm home directory under `repositories`. +You can check whether you have found the correct directory by having a look at the file `metadata.xml`. Here you should find the namespace and the name of the repository created. +Now its time to import the dump from the old repository: + +```bash +svnadmin load /path/to/scm-home/repositories/id/data < oldrepo.dump +``` diff --git a/docs/en/navigation.yml b/docs/en/navigation.yml index e696c98bc6..9c30b03156 100644 --- a/docs/en/navigation.yml +++ b/docs/en/navigation.yml @@ -2,6 +2,7 @@ entries: - /installation/ - /migrate-scm-manager-from-v1/ + - /import/ - /faq/ - /known-issues/ diff --git a/package.json b/package.json index 35682ca3de..61331899bc 100644 --- a/package.json +++ b/package.json @@ -14,9 +14,12 @@ "deploy": "ui-scripts publish", "set-version": "ui-scripts version" }, + "dependencies": {}, "devDependencies": { "babel-plugin-reflow": "^0.2.7", - "lerna": "^3.17.0" + "husky": "^4.2.5", + "lerna": "^3.17.0", + "lint-staged": "^10.2.11" }, "resolutions": { "babel-core": "7.0.0-bridge.0", @@ -32,5 +35,12 @@ "preset": "@scm-manager/jest-preset" }, "prettier": "@scm-manager/prettier-config", - "dependencies": {} + "husky": { + "hooks": { + "pre-commit": "lint-staged --verbose" + } + }, + "lint-staged": { + "*.{js,jsx,ts,tsx}": "eslint" + } } diff --git a/scm-core/src/main/java/sonia/scm/RootURL.java b/scm-core/src/main/java/sonia/scm/RootURL.java new file mode 100644 index 0000000000..0187b51fb1 --- /dev/null +++ b/scm-core/src/main/java/sonia/scm/RootURL.java @@ -0,0 +1,53 @@ +/* + * MIT License + * + * Copyright (c) 2020-present Cloudogu GmbH and Contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package sonia.scm; + +import java.net.URL; + +/** + * RootURL is able to return the root url of the SCM-Manager instance, + * regardless of the scope (web request, async hook, ssh command, etc). + * + * @since 2.3.1 + */ +public interface RootURL { + + /** + * Returns the root url of the SCM-Manager instance. + * + * @return root url + */ + URL get(); + + /** + * Returns the root url of the SCM-Manager instance as string. + * + * @return root url as string + */ + default String getAsString() { + return get().toExternalForm(); + } + +} diff --git a/scm-core/src/main/java/sonia/scm/repository/spi/InitializingHttpScmProtocolWrapper.java b/scm-core/src/main/java/sonia/scm/repository/spi/InitializingHttpScmProtocolWrapper.java index 063c7de408..c6a656f276 100644 --- a/scm-core/src/main/java/sonia/scm/repository/spi/InitializingHttpScmProtocolWrapper.java +++ b/scm-core/src/main/java/sonia/scm/repository/spi/InitializingHttpScmProtocolWrapper.java @@ -21,10 +21,11 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ - + package sonia.scm.repository.spi; import lombok.extern.slf4j.Slf4j; +import sonia.scm.RootURL; import sonia.scm.api.v2.resources.ScmPathInfoStore; import sonia.scm.config.ScmConfiguration; import sonia.scm.repository.Repository; @@ -37,6 +38,7 @@ import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.util.Optional; +import java.util.function.Supplier; import static java.util.Optional.empty; import static java.util.Optional.of; @@ -45,16 +47,36 @@ import static java.util.Optional.of; public abstract class InitializingHttpScmProtocolWrapper implements ScmProtocolProvider { private final Provider delegateProvider; - private final Provider pathInfoStore; - private final ScmConfiguration scmConfiguration; + private final Supplier basePathSupplier; private volatile boolean isInitialized = false; - + /** + * Constructs a new {@link InitializingHttpScmProtocolWrapper}. + * + * @param delegateProvider injection provider for the servlet delegate + * @param pathInfoStore url info store + * @param scmConfiguration scm-manager main configuration + * + * @deprecated use {@link InitializingHttpScmProtocolWrapper(Provider, RootURL)} instead. + */ + @Deprecated protected InitializingHttpScmProtocolWrapper(Provider delegateProvider, Provider pathInfoStore, ScmConfiguration scmConfiguration) { this.delegateProvider = delegateProvider; - this.pathInfoStore = pathInfoStore; - this.scmConfiguration = scmConfiguration; + this.basePathSupplier = new LegacySupplier(pathInfoStore, scmConfiguration); + } + + /** + * Constructs a new {@link InitializingHttpScmProtocolWrapper}. + * + * @param delegateProvider injection provider for the servlet delegate + * @param rootURL root url + * + * @since 2.3.1 + */ + public InitializingHttpScmProtocolWrapper(Provider delegateProvider, RootURL rootURL) { + this.delegateProvider = delegateProvider; + this.basePathSupplier = rootURL::getAsString; } protected void initializeServlet(ServletConfig config, ScmProviderHttpServlet httpServlet) throws ServletException { @@ -64,30 +86,45 @@ public abstract class InitializingHttpScmProtocolWrapper implements ScmProtocolP @Override public HttpScmProtocol get(Repository repository) { if (!repository.getType().equals(getType())) { - throw new IllegalArgumentException(String.format("cannot handle repository with type %s with protocol for type %s", repository.getType(), getType())); + throw new IllegalArgumentException( + String.format("cannot handle repository with type %s with protocol for type %s", repository.getType(), getType()) + ); } - return new ProtocolWrapper(repository, computeBasePath()); + return new ProtocolWrapper(repository, basePathSupplier.get()); } - private String computeBasePath() { - return getPathFromScmPathInfoIfAvailable().orElse(getPathFromConfiguration()); - } + private static class LegacySupplier implements Supplier { - private Optional getPathFromScmPathInfoIfAvailable() { - try { - ScmPathInfoStore scmPathInfoStore = pathInfoStore.get(); - if (scmPathInfoStore != null && scmPathInfoStore.get() != null) { - return of(scmPathInfoStore.get().getRootUri().toASCIIString()); + private final Provider pathInfoStore; + private final ScmConfiguration scmConfiguration; + + private LegacySupplier(Provider pathInfoStore, ScmConfiguration scmConfiguration) { + this.pathInfoStore = pathInfoStore; + this.scmConfiguration = scmConfiguration; + } + + @Override + public String get() { + return getPathFromScmPathInfoIfAvailable().orElse(getPathFromConfiguration()); + } + + private Optional getPathFromScmPathInfoIfAvailable() { + try { + ScmPathInfoStore scmPathInfoStore = pathInfoStore.get(); + if (scmPathInfoStore != null && scmPathInfoStore.get() != null) { + return of(scmPathInfoStore.get().getRootUri().toASCIIString()); + } + } catch (Exception e) { + log.debug("could not get ScmPathInfoStore from context", e); } - } catch (Exception e) { - log.debug("could not get ScmPathInfoStore from context", e); + return empty(); + } + + private String getPathFromConfiguration() { + log.debug("using base path from configuration: {}", scmConfiguration.getBaseUrl()); + return scmConfiguration.getBaseUrl(); } - return empty(); - } - private String getPathFromConfiguration() { - log.debug("using base path from configuration: {}", scmConfiguration.getBaseUrl()); - return scmConfiguration.getBaseUrl(); } private class ProtocolWrapper extends HttpScmProtocol { 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 88adc2a24c..7056911a20 100644 --- a/scm-core/src/main/java/sonia/scm/util/HttpUtil.java +++ b/scm-core/src/main/java/sonia/scm/util/HttpUtil.java @@ -21,7 +21,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ - + package sonia.scm.util; //~--- non-JDK imports -------------------------------------------------------- @@ -925,11 +925,16 @@ public final class HttpUtil @VisibleForTesting static String createForwardedBaseUrl(HttpServletRequest request) { - String proto = getHeader(request, HEADER_X_FORWARDED_PROTO, - request.getScheme()); + String fhost = getHeader(request, HEADER_X_FORWARDED_HOST, null); + if (fhost == null) { + throw new IllegalStateException( + String.format("request has no %s header and does not look like it is forwarded", HEADER_X_FORWARDED_HOST) + ); + } + + String proto = getHeader(request, HEADER_X_FORWARDED_PROTO, request.getScheme()); String host; - String fhost = getHeader(request, HEADER_X_FORWARDED_HOST, - request.getScheme()); + String port = request.getHeader(HEADER_X_FORWARDED_PORT); int s = fhost.indexOf(SEPARATOR_PORT); diff --git a/scm-core/src/test/java/sonia/scm/repository/spi/InitializingHttpScmProtocolWrapperTest.java b/scm-core/src/test/java/sonia/scm/repository/spi/InitializingHttpScmProtocolWrapperTest.java index 8c1a0c8057..f450eb380a 100644 --- a/scm-core/src/test/java/sonia/scm/repository/spi/InitializingHttpScmProtocolWrapperTest.java +++ b/scm-core/src/test/java/sonia/scm/repository/spi/InitializingHttpScmProtocolWrapperTest.java @@ -21,15 +21,19 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ - + package sonia.scm.repository.spi; import com.google.inject.ProvisionException; import com.google.inject.util.Providers; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; import org.mockito.stubbing.OngoingStubbing; +import sonia.scm.RootURL; import sonia.scm.api.v2.resources.ScmPathInfo; import sonia.scm.api.v2.resources.ScmPathInfoStore; import sonia.scm.config.ScmConfiguration; @@ -44,101 +48,135 @@ import java.io.IOException; import java.net.URI; import static org.junit.Assert.assertEquals; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; -import static org.mockito.MockitoAnnotations.initMocks; +import static org.junit.Assert.assertThrows; +import static org.mockito.Mockito.*; -public class InitializingHttpScmProtocolWrapperTest { +@ExtendWith(MockitoExtension.class) +class InitializingHttpScmProtocolWrapperTest { private static final Repository REPOSITORY = new Repository("", "git", "space", "name"); @Mock private ScmProviderHttpServlet delegateServlet; - @Mock - private ScmPathInfoStore pathInfoStore; - @Mock - private ScmConfiguration scmConfiguration; - private Provider pathInfoStoreProvider; - - @Mock - private HttpServletRequest request; - @Mock - private HttpServletResponse response; - @Mock - private ServletConfig servletConfig; - private InitializingHttpScmProtocolWrapper wrapper; - @Before - public void init() { - initMocks(this); - pathInfoStoreProvider = mock(Provider.class); - when(pathInfoStoreProvider.get()).thenReturn(pathInfoStore); - wrapper = new InitializingHttpScmProtocolWrapper(Providers.of(this.delegateServlet), pathInfoStoreProvider, scmConfiguration) { - @Override - public String getType() { - return "git"; - } - }; - when(scmConfiguration.getBaseUrl()).thenReturn("http://example.com/scm"); + @Nested + class WithRootURL { + + @Mock + private RootURL rootURL; + + @BeforeEach + void init() { + wrapper = new InitializingHttpScmProtocolWrapper(Providers.of(delegateServlet), rootURL) { + @Override + public String getType() { + return "git"; + } + }; + when(rootURL.getAsString()).thenReturn("https://hitchhiker.com/scm"); + } + + @Test + void shouldReturnUrlFromRootURL() { + HttpScmProtocol httpScmProtocol = wrapper.get(REPOSITORY); + + assertEquals("https://hitchhiker.com/scm/repo/space/name", httpScmProtocol.getUrl()); + } + } - @Test - public void shouldUsePathFromPathInfo() { - mockSetPathInfo(); + @Nested + class WithPathInfoStore { - HttpScmProtocol httpScmProtocol = wrapper.get(REPOSITORY); + @Mock + private ScmPathInfoStore pathInfoStore; + @Mock + private ScmConfiguration scmConfiguration; - assertEquals("http://example.com/scm/repo/space/name", httpScmProtocol.getUrl()); - } + private Provider pathInfoStoreProvider; - @Test - public void shouldUseConfigurationWhenPathInfoNotSet() { - HttpScmProtocol httpScmProtocol = wrapper.get(REPOSITORY); + @Mock + private HttpServletRequest request; + @Mock + private HttpServletResponse response; + @Mock + private ServletConfig servletConfig; - assertEquals("http://example.com/scm/repo/space/name", httpScmProtocol.getUrl()); - } + @BeforeEach + void init() { + pathInfoStoreProvider = mock(Provider.class); + lenient().when(pathInfoStoreProvider.get()).thenReturn(pathInfoStore); - @Test - public void shouldUseConfigurationWhenNotInRequestScope() { - when(pathInfoStoreProvider.get()).thenThrow(new ProvisionException("test")); + wrapper = new InitializingHttpScmProtocolWrapper(Providers.of(delegateServlet), pathInfoStoreProvider, scmConfiguration) { + @Override + public String getType() { + return "git"; + } + }; + lenient().when(scmConfiguration.getBaseUrl()).thenReturn("http://example.com/scm"); + } - HttpScmProtocol httpScmProtocol = wrapper.get(REPOSITORY); + @Test + void shouldUsePathFromPathInfo() { + mockSetPathInfo(); - assertEquals("http://example.com/scm/repo/space/name", httpScmProtocol.getUrl()); - } + HttpScmProtocol httpScmProtocol = wrapper.get(REPOSITORY); - @Test - public void shouldInitializeAndDelegateRequestThroughFilter() throws ServletException, IOException { - HttpScmProtocol httpScmProtocol = wrapper.get(REPOSITORY); + assertEquals("http://example.com/scm/repo/space/name", httpScmProtocol.getUrl()); + } - httpScmProtocol.serve(request, response, servletConfig); + @Test + void shouldUseConfigurationWhenPathInfoNotSet() { + HttpScmProtocol httpScmProtocol = wrapper.get(REPOSITORY); - verify(delegateServlet).init(servletConfig); - verify(delegateServlet).service(request, response, REPOSITORY); - } + assertEquals("http://example.com/scm/repo/space/name", httpScmProtocol.getUrl()); + } - @Test - public void shouldInitializeOnlyOnce() throws ServletException, IOException { - HttpScmProtocol httpScmProtocol = wrapper.get(REPOSITORY); + @Test + void shouldUseConfigurationWhenNotInRequestScope() { + when(pathInfoStoreProvider.get()).thenThrow(new ProvisionException("test")); - httpScmProtocol.serve(request, response, servletConfig); - httpScmProtocol.serve(request, response, servletConfig); + HttpScmProtocol httpScmProtocol = wrapper.get(REPOSITORY); - verify(delegateServlet, times(1)).init(servletConfig); - verify(delegateServlet, times(2)).service(request, response, REPOSITORY); - } + assertEquals("http://example.com/scm/repo/space/name", httpScmProtocol.getUrl()); + } - @Test(expected = IllegalArgumentException.class) - public void shouldFailForIllegalScmType() { - HttpScmProtocol httpScmProtocol = wrapper.get(new Repository("", "other", "space", "name")); - } + @Test + void shouldInitializeAndDelegateRequestThroughFilter() throws ServletException, IOException { + HttpScmProtocol httpScmProtocol = wrapper.get(REPOSITORY); + + httpScmProtocol.serve(request, response, servletConfig); + + verify(delegateServlet).init(servletConfig); + verify(delegateServlet).service(request, response, REPOSITORY); + } + + @Test + void shouldInitializeOnlyOnce() throws ServletException, IOException { + HttpScmProtocol httpScmProtocol = wrapper.get(REPOSITORY); + + httpScmProtocol.serve(request, response, servletConfig); + httpScmProtocol.serve(request, response, servletConfig); + + verify(delegateServlet, times(1)).init(servletConfig); + verify(delegateServlet, times(2)).service(request, response, REPOSITORY); + } + + @Test + void shouldFailForIllegalScmType() { + Repository repository = new Repository("", "other", "space", "name"); + assertThrows( + IllegalArgumentException.class, + () -> wrapper.get(repository) + ); + } + + private OngoingStubbing mockSetPathInfo() { + return when(pathInfoStore.get()).thenReturn(() -> URI.create("http://example.com/scm/api/")); + } - private OngoingStubbing mockSetPathInfo() { - return when(pathInfoStore.get()).thenReturn(() -> URI.create("http://example.com/scm/api/")); } } diff --git a/scm-core/src/test/java/sonia/scm/util/HttpUtilTest.java b/scm-core/src/test/java/sonia/scm/util/HttpUtilTest.java index 162033c7ca..40aa184145 100644 --- a/scm-core/src/test/java/sonia/scm/util/HttpUtilTest.java +++ b/scm-core/src/test/java/sonia/scm/util/HttpUtilTest.java @@ -21,7 +21,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ - + package sonia.scm.util; //~--- non-JDK imports -------------------------------------------------------- @@ -234,6 +234,12 @@ public class HttpUtilTest HttpUtil.createForwardedBaseUrl(request)); } + @Test(expected = IllegalStateException.class) + public void shouldTrowIllegalStateExceptionWithoutForwardedHostHeader() { + HttpServletRequest request = mock(HttpServletRequest.class); + HttpUtil.createForwardedBaseUrl(request); + } + /** * Method description * diff --git a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/web/GitScmProtocolProviderWrapper.java b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/web/GitScmProtocolProviderWrapper.java index 87adbd65fe..318282d7bb 100644 --- a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/web/GitScmProtocolProviderWrapper.java +++ b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/web/GitScmProtocolProviderWrapper.java @@ -21,25 +21,23 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ - + package sonia.scm.web; -import sonia.scm.api.v2.resources.ScmPathInfoStore; -import sonia.scm.config.ScmConfiguration; +import sonia.scm.RootURL; import sonia.scm.plugin.Extension; import sonia.scm.repository.GitRepositoryHandler; import sonia.scm.repository.spi.InitializingHttpScmProtocolWrapper; import javax.inject.Inject; -import javax.inject.Provider; import javax.inject.Singleton; @Singleton @Extension public class GitScmProtocolProviderWrapper extends InitializingHttpScmProtocolWrapper { @Inject - public GitScmProtocolProviderWrapper(ScmGitServletProvider servletProvider, Provider uriInfoStore, ScmConfiguration scmConfiguration) { - super(servletProvider, uriInfoStore, scmConfiguration); + public GitScmProtocolProviderWrapper(ScmGitServletProvider servletProvider, RootURL rootURL) { + super(servletProvider, rootURL); } @Override diff --git a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/web/HgScmProtocolProviderWrapper.java b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/web/HgScmProtocolProviderWrapper.java index 28f80952e2..12b85a1746 100644 --- a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/web/HgScmProtocolProviderWrapper.java +++ b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/web/HgScmProtocolProviderWrapper.java @@ -21,25 +21,24 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ - + package sonia.scm.web; -import sonia.scm.api.v2.resources.ScmPathInfoStore; -import sonia.scm.config.ScmConfiguration; +import sonia.scm.RootURL; import sonia.scm.plugin.Extension; import sonia.scm.repository.HgRepositoryHandler; import sonia.scm.repository.spi.InitializingHttpScmProtocolWrapper; import javax.inject.Inject; -import javax.inject.Provider; import javax.inject.Singleton; @Singleton @Extension public class HgScmProtocolProviderWrapper extends InitializingHttpScmProtocolWrapper { + @Inject - public HgScmProtocolProviderWrapper(HgCGIServletProvider servletProvider, Provider uriInfoStore, ScmConfiguration scmConfiguration) { - super(servletProvider, uriInfoStore, scmConfiguration); + public HgScmProtocolProviderWrapper(HgCGIServletProvider servletProvider, RootURL rootURL) { + super(servletProvider, rootURL); } @Override diff --git a/scm-plugins/scm-svn-plugin/src/main/java/sonia/scm/web/SvnScmProtocolProviderWrapper.java b/scm-plugins/scm-svn-plugin/src/main/java/sonia/scm/web/SvnScmProtocolProviderWrapper.java index a8e8857066..fbe71d4bb2 100644 --- a/scm-plugins/scm-svn-plugin/src/main/java/sonia/scm/web/SvnScmProtocolProviderWrapper.java +++ b/scm-plugins/scm-svn-plugin/src/main/java/sonia/scm/web/SvnScmProtocolProviderWrapper.java @@ -21,18 +21,16 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ - + package sonia.scm.web; -import sonia.scm.api.v2.resources.ScmPathInfoStore; -import sonia.scm.config.ScmConfiguration; +import sonia.scm.RootURL; import sonia.scm.plugin.Extension; import sonia.scm.repository.SvnRepositoryHandler; import sonia.scm.repository.spi.InitializingHttpScmProtocolWrapper; import sonia.scm.repository.spi.ScmProviderHttpServlet; import javax.inject.Inject; -import javax.inject.Provider; import javax.inject.Singleton; import javax.servlet.ServletConfig; import javax.servlet.ServletContext; @@ -45,19 +43,18 @@ public class SvnScmProtocolProviderWrapper extends InitializingHttpScmProtocolWr public static final String PARAMETER_SVN_PARENTPATH = "SVNParentPath"; + @Inject + public SvnScmProtocolProviderWrapper(SvnDAVServletProvider servletProvider, RootURL rootURL) { + super(servletProvider, rootURL); + } + @Override public String getType() { return SvnRepositoryHandler.TYPE_NAME; } - @Inject - public SvnScmProtocolProviderWrapper(SvnDAVServletProvider servletProvider, Provider uriInfoStore, ScmConfiguration scmConfiguration) { - super(servletProvider, uriInfoStore, scmConfiguration); - } - @Override protected void initializeServlet(ServletConfig config, ScmProviderHttpServlet httpServlet) throws ServletException { - super.initializeServlet(new SvnConfigEnhancer(config), httpServlet); } diff --git a/scm-ui/ui-components/src/Help.stories.tsx b/scm-ui/ui-components/src/Help.stories.tsx new file mode 100644 index 0000000000..6be7d02654 --- /dev/null +++ b/scm-ui/ui-components/src/Help.stories.tsx @@ -0,0 +1,58 @@ +/* + * MIT License + * + * Copyright (c) 2020-present Cloudogu GmbH and Contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +import styled from "styled-components"; +import * as React from "react"; +import { storiesOf } from "@storybook/react"; +import Help from "./Help"; + +const Wrapper = styled.div` + margin: 5rem; +`; + +const Spacing = styled.div` + margin-top: 1rem; +`; + +const longContent = + "Cleverness nuclear genuine static irresponsibility invited President Zaphod\n" + + "Beeblebrox hyperspace ship. Another custard through computer-generated universe\n" + + "shapes field strong disaster parties Russell’s ancestors infinite colour\n" + + "imaginative generator sweep."; + +storiesOf("Help", module) + .addDecorator(storyFn => {storyFn()}) + .add("Default", () => ) + .add("Multiline", () => ( + <> + + + + + + + + + + )); diff --git a/scm-ui/ui-components/src/Help.tsx b/scm-ui/ui-components/src/Help.tsx index 3bb250f3f0..f49b49f1ca 100644 --- a/scm-ui/ui-components/src/Help.tsx +++ b/scm-ui/ui-components/src/Help.tsx @@ -21,7 +21,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ -import React from "react"; +import React, { FC } from "react"; import classNames from "classnames"; import styled from "styled-components"; import Tooltip from "./Tooltip"; @@ -29,6 +29,7 @@ import HelpIcon from "./HelpIcon"; type Props = { message: string; + multiline?: boolean; className?: string; }; @@ -37,13 +38,17 @@ const HelpTooltip = styled(Tooltip)` padding-left: 3px; `; -export default class Help extends React.Component { - render() { - const { message, className } = this.props; - return ( - - - - ); - } -} +const Help: FC = ({ message, multiline, className }) => ( + + + +); + +Help.defaultProps = { + multiline: true +}; + +export default Help; diff --git a/scm-ui/ui-components/src/__snapshots__/storyshots.test.ts.snap b/scm-ui/ui-components/src/__snapshots__/storyshots.test.ts.snap index f23f781046..e496e41fc1 100644 --- a/scm-ui/ui-components/src/__snapshots__/storyshots.test.ts.snap +++ b/scm-ui/ui-components/src/__snapshots__/storyshots.test.ts.snap @@ -38956,6 +38956,65 @@ exports[`Storyshots Forms|Checkbox Disabled 1`] = ` `; +exports[`Storyshots Forms|Checkbox With HelpText 1`] = ` +
+
+
+ +
+
+
+
+ +
+
+
+`; + exports[`Storyshots Forms|DropDown Default 1`] = `
`; +exports[`Storyshots Forms|Radio With HelpText 1`] = ` +
+ + +
+`; + exports[`Storyshots Forms|Textarea OnCancel 1`] = `
`; +exports[`Storyshots Help Default 1`] = ` +
+ + + +
+`; + +exports[`Storyshots Help Multiline 1`] = ` +
+
+ + + + +
+
+ + + + +
+
+`; + exports[`Storyshots Icon Colors 1`] = `
`; +exports[`Storyshots Modal|Modal Closeable 1`] = ` +
+
+
+
+

+ Hitchhiker Modal +

+
+
+

+ Mind-paralyzing change needed improbability vortex machine sorts sought same theory upending job just allows + hostess’s really oblong Infinite Improbability thing into the starship against which behavior accordance.with + Kakrafoon humanoid undergarment ship powered by GPP-guided bowl of petunias nothing was frequently away incredibly + ordinary mob. +

+
+
+
+`; + exports[`Storyshots Modal|Modal Default 1`] = `

- Mind-paralyzing change needed improbability vortex machine sorts sought same theory upending job just allows hostess’s really oblong Infinite Improbability thing into the starship against which behavior accordance.with Kakrafoon humanoid undergarment ship powered by GPP-guided bowl of petunias nothing was frequently away incredibly ordinary mob. + Mind-paralyzing change needed improbability vortex machine sorts sought same theory upending job just allows + hostess’s really oblong Infinite Improbability thing into the starship against which behavior accordance.with + Kakrafoon humanoid undergarment ship powered by GPP-guided bowl of petunias nothing was frequently away incredibly + ordinary mob. +

+ +
+
+`; + +exports[`Storyshots Modal|Modal Long content 1`] = ` +
+
+
+
+

+ Hitchhiker Modal +

+
+
+

+ Marvin +

+

+ The Paranoid Android +

+
+
+ + The following content comes from the awesome + + + Hitchhikers Wiki + +
+
+
+ Marvin +
+
+

+ Marvin, more fully known as Marvin the Paranoid Android, is an incredibly brilliant but overwhelmingly depressed robot manufactured by the Sirius Cybernetics Corporation and unwilling servant to the crew of the Heart of Gold. +

+
+
+

+ Physical Appearance +

+

+ In the novels, Marvin is described thusly: "...though it was beautifully constructed and polished it looked somehow as if the various parts of its more or less humanoid body didn't quite fit properly. In fact, they fit perfectly well, but something in its bearing suggested that they might have fitted better." +

+

+ On the radio show, there's no physical description of Marvin, though his voice is digitally altered to sound more robotic, and any scene that focuses on him is accompanied by sounds of mechanical clanking and hissing. +

+

+ In the TV series, Marvin is built in the style of a 1950's robot similar to Robbie the Robot from Forbidden Planet or Twiki from Buck Rogers. His body is blocky and angular, with a pair of clamp-claw hands, shuffling feet and a squarish head with a dour face. +

+

+ In the movie, Marvin is a short, stout robot built of smooth, white plastic. His arms are much longer than his legs, and his head is a massive sphere with only a pair of triangle eyes for a face. His large head and simian-like proportions give Marvin a perpetual slouch, adding to his melancholy personality. At the start of the film his eyes glow, but at the end he is shot but unharmed, leaving a hole in his head and dimming his eyes. This is probably the most depressing and unacceptable manifestation of Marvin ever conceived, and thus paradoxically the most accurate. +

+
+
+
+

+ Personality +

+

+ Marvin the robot has a prototype version of the Genuine People Personality (GPP) software from SCC, allowing him sentience and the ability to feel emotions and develop a personality. He's also incredibly smart, having a "brain the size of a planet" capable of computing extremely complex mathematics, as well as solving difficult problems and operating high-tech devices. +

+

+ However, despite being so smart, Marvin is typically made to perform menial tasks and labour such as escorting people, opening doors, picking up pieces of paper, and other tasks well beneath his skills. Even extremely hard tasks, such as computing for the vast Krikkit robot army, are trivial for Marvin. All this leaves him extremely bored, frustrated, and overwhelmingly depressed. Because of this, all modern GPP-capable machines, such as Eddie the computer and the Heart of Gold's automatic doors, are programmed to be extremely cheerful and happy, much to Marvin's disgust. +

+

+ Marvin hates everyone and everything he comes into contact with, having no respect for anybody and will criticise and insult others at any opportunity, or otherwise rant and complain for hours on end about his own problems, such as the terrible pain he suffers in all the diodes down his left side. His contempt for everyone is often justified, as almost every person he comes across, even those who consider him a friend, (such as Arthur and Trillian, who treat him more kindly than Ford and Zaphod) treat Marvin as an expendable servant, even sending him to his death more than once (such as when Zaphod ordered Marvin to fight the gigantic, heavy-duty Frogstar Scout Robot Class D so he could escape). Being a robot, he still does what he's told (he won't enjoy it, nor will he let you forget it, but he'll do it anyway), though he'd much rather sulk in a corner by himself. +

+

+ Several times in the series Marvin ends up alone and isolated for extremely long periods of time, sometimes spanning millions of years, either by sheer bad luck (such as the explosion that propelled everyone but Marvin to Milliways in the far-off future) or because his unpleasantly depressing personality drives them away or, in more than one case, makes them commit suicide. In his spare time (which he has a lot of), Marvin will attempt to occupy himself by composing songs and writing poetry. Of course, none of them are particularly cheerful, or even that good. +

+
+
+
+
+`; + +exports[`Storyshots Modal|Modal With form elements 1`] = ` +
+
+
+
+

+ Hitchhiker Modal +

+
+
+
+ + +
+
+

+ Mind-paralyzing change needed improbability vortex machine sorts sought same theory upending job just allows + hostess’s really oblong Infinite Improbability thing into the starship against which behavior accordance.with + Kakrafoon humanoid undergarment ship powered by GPP-guided bowl of petunias nothing was frequently away incredibly + ordinary mob. +

+
+
+ +
+