rawDiffOptions) {
+ this.rawDiffOptions = rawDiffOptions;
+ }
+
+ public void setDiffOptions(SVNDiffOptions diffOptions) {
+ this.diffOptions = diffOptions;
+ }
+
+ public void setDiffDeleted(boolean diffDeleted) {
+ this.diffDeleted = diffDeleted;
+ }
+
+ public void setDiffAdded(boolean diffAdded) {
+ this.diffAdded = diffAdded;
+ }
+
+ public void setBasePath(File absoluteFile) {
+ setBaseTarget(SvnTarget.fromFile(absoluteFile));
+ }
+
+ public void setFallbackToAbsolutePath(boolean fallbackToAbsolutePath) {
+ this.fallbackToAbsolutePath = fallbackToAbsolutePath;
+ }
+
+ public void setOptions(ISVNOptions options) {
+ this.options = options;
+ }
+
+ public ISVNOptions getOptions() {
+ return options;
+ }
+
+ private class EmptyDetectionOutputStream extends OutputStream {
+
+ private final OutputStream outputStream;
+ private boolean somethingWritten;
+
+ public EmptyDetectionOutputStream(OutputStream outputStream) {
+ this.outputStream = outputStream;
+ this.somethingWritten = false;
+ }
+
+ public boolean isSomethingWritten() {
+ return somethingWritten;
+ }
+
+ @Override
+ public void write(int c) throws IOException {
+ somethingWritten = true;
+ outputStream.write(c);
+ }
+
+ @Override
+ public void write(byte[] bytes) throws IOException {
+ somethingWritten = bytes.length > 0;
+ outputStream.write(bytes);
+ }
+
+ @Override
+ public void write(byte[] bytes, int offset, int length) throws IOException {
+ somethingWritten = length > 0;
+ outputStream.write(bytes, offset, length);
+ }
+
+ @Override
+ public void flush() throws IOException {
+ outputStream.flush();
+ }
+
+ @Override
+ public void close() throws IOException {
+ outputStream.close();
+ }
+ }
+}
diff --git a/scm-plugins/scm-svn-plugin/src/main/java/sonia/scm/repository/spi/SvnDiffCommand.java b/scm-plugins/scm-svn-plugin/src/main/java/sonia/scm/repository/spi/SvnDiffCommand.java
index 8418fd63b8..4aaa12e28f 100644
--- a/scm-plugins/scm-svn-plugin/src/main/java/sonia/scm/repository/spi/SvnDiffCommand.java
+++ b/scm-plugins/scm-svn-plugin/src/main/java/sonia/scm/repository/spi/SvnDiffCommand.java
@@ -1,19 +1,19 @@
-/**
+/*
* 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.
+ * 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.
+ * 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.
- *
+ * 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
@@ -24,13 +24,11 @@
* 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.spi;
//~--- non-JDK imports --------------------------------------------------------
@@ -41,8 +39,7 @@ import org.slf4j.LoggerFactory;
import org.tmatesoft.svn.core.SVNDepth;
import org.tmatesoft.svn.core.SVNException;
import org.tmatesoft.svn.core.SVNURL;
-import org.tmatesoft.svn.core.wc.DefaultSVNDiffGenerator;
-import org.tmatesoft.svn.core.wc.ISVNDiffGenerator;
+import org.tmatesoft.svn.core.internal.wc2.ng.SvnNewDiffGenerator;
import org.tmatesoft.svn.core.wc.SVNClientManager;
import org.tmatesoft.svn.core.wc.SVNDiffClient;
import org.tmatesoft.svn.core.wc.SVNRevision;
@@ -60,8 +57,7 @@ import java.io.OutputStream;
*
* @author Sebastian Sdorra
*/
-public class SvnDiffCommand extends AbstractSvnCommand implements DiffCommand
-{
+public class SvnDiffCommand extends AbstractSvnCommand implements DiffCommand {
/**
* the logger for SvnDiffCommand
@@ -69,46 +65,26 @@ public class SvnDiffCommand extends AbstractSvnCommand implements DiffCommand
private static final Logger logger =
LoggerFactory.getLogger(SvnDiffCommand.class);
- public SvnDiffCommand(SvnContext context, Repository repository)
- {
+ public SvnDiffCommand(SvnContext context, Repository repository) {
super(context, repository);
}
@Override
public void getDiffResult(DiffCommandRequest request, OutputStream output) {
- if (logger.isDebugEnabled())
- {
- logger.debug("create diff for {}", request);
- }
-
+ logger.debug("create diff for {}", request);
Preconditions.checkNotNull(request, "request is required");
Preconditions.checkNotNull(output, "outputstream is required");
String path = request.getPath();
SVNClientManager clientManager = null;
-
- try
- {
+ try {
SVNURL svnurl = context.createUrl();
-
- if (Util.isNotEmpty(path))
- {
+ if (Util.isNotEmpty(path)) {
svnurl = svnurl.appendPath(path, true);
}
-
clientManager = SVNClientManager.newInstance();
-
SVNDiffClient diffClient = clientManager.getDiffClient();
- ISVNDiffGenerator diffGenerator = diffClient.getDiffGenerator();
-
- if (diffGenerator == null)
- {
- diffGenerator = new DefaultSVNDiffGenerator();
- }
-
- diffGenerator.setDiffAdded(true);
- diffGenerator.setDiffDeleted(true);
- diffClient.setDiffGenerator(diffGenerator);
+ diffClient.setDiffGenerator(new SvnNewDiffGenerator(new SCMSvnDiffGenerator()));
long currentRev = SvnUtil.getRevisionNumber(request.getRevision(), repository);
@@ -117,13 +93,9 @@ public class SvnDiffCommand extends AbstractSvnCommand implements DiffCommand
diffClient.doDiff(svnurl, SVNRevision.HEAD,
SVNRevision.create(currentRev - 1), SVNRevision.create(currentRev),
SVNDepth.INFINITY, false, output);
- }
- catch (SVNException ex)
- {
+ } catch (SVNException ex) {
throw new InternalRepositoryException(repository, "could not create diff", ex);
- }
- finally
- {
+ } finally {
SvnUtil.dispose(clientManager);
}
}
diff --git a/scm-plugins/scm-svn-plugin/yarn.lock b/scm-plugins/scm-svn-plugin/yarn.lock
index c3e8cc476f..977d642f6f 100644
--- a/scm-plugins/scm-svn-plugin/yarn.lock
+++ b/scm-plugins/scm-svn-plugin/yarn.lock
@@ -641,9 +641,9 @@
version "0.0.2"
resolved "https://registry.yarnpkg.com/@scm-manager/eslint-config/-/eslint-config-0.0.2.tgz#94cc8c3fb4f51f870b235893dc134fc6c423ae85"
-"@scm-manager/ui-bundler@^0.0.17":
- version "0.0.17"
- resolved "https://registry.yarnpkg.com/@scm-manager/ui-bundler/-/ui-bundler-0.0.17.tgz#949b90ca57e4268be28fcf4975bd9622f60278bb"
+"@scm-manager/ui-bundler@^0.0.19":
+ version "0.0.19"
+ resolved "https://registry.yarnpkg.com/@scm-manager/ui-bundler/-/ui-bundler-0.0.19.tgz#646ab1fa1e5389fad614542215c60678fb9816ae"
dependencies:
"@babel/core" "^7.0.0"
"@babel/plugin-proposal-class-properties" "^7.0.0"
diff --git a/scm-ui-components/packages/ui-components/flow-typed/npm/react-i18next_v7.x.x.js b/scm-ui-components/packages/ui-components/flow-typed/npm/react-i18next_v7.x.x.js
new file mode 100644
index 0000000000..93d27674d7
--- /dev/null
+++ b/scm-ui-components/packages/ui-components/flow-typed/npm/react-i18next_v7.x.x.js
@@ -0,0 +1,95 @@
+// flow-typed signature: 65d42f62f8de603dcc631ea5a6b00580
+// flow-typed version: f3f13164e0/react-i18next_v7.x.x/flow_>=v0.64.x
+
+declare module "react-i18next" {
+ declare type TFunction = (key?: ?string, data?: ?Object) => string;
+
+ declare type TranslatorProps = {|
+ t: TFunction,
+ i18nLoadedAt: Date,
+ i18n: Object
+ |};
+
+ declare type TranslatorPropsVoid = {
+ t: TFunction | void,
+ i18nLoadedAt: Date | void,
+ i18n: Object | void
+ };
+
+ declare type Translator> = (
+ WrappedComponent: Component
+ ) => React$ComponentType<
+ $Diff, TranslatorPropsVoid>
+ >;
+
+ declare type TranslateOptions = $Shape<{
+ wait: boolean,
+ nsMode: "default" | "fallback",
+ bindi18n: false | string,
+ bindStore: false | string,
+ withRef: boolean,
+ translateFuncName: string,
+ i18n: Object,
+ usePureComponent: boolean
+ }>;
+
+ declare function translate>(
+ namespaces?: | string
+ | Array
+ | (($Diff) => string | Array),
+ options?: TranslateOptions
+ ): Translator;
+
+ declare type I18nProps = {
+ i18n?: Object,
+ ns?: string | Array,
+ children: (t: TFunction, { i18n: Object, t: TFunction }) => React$Node,
+ initialI18nStore?: Object,
+ initialLanguage?: string
+ };
+ declare var I18n: React$ComponentType;
+
+ declare type InterpolateProps = {
+ className?: string,
+ dangerouslySetInnerHTMLPartElement?: string,
+ i18n?: Object,
+ i18nKey?: string,
+ options?: Object,
+ parent?: string,
+ style?: Object,
+ t?: TFunction,
+ useDangerouslySetInnerHTML?: boolean
+ };
+ declare var Interpolate: React$ComponentType;
+
+ declare type TransProps = {
+ count?: number,
+ parent?: string,
+ i18n?: Object,
+ i18nKey?: string,
+ t?: TFunction
+ };
+ declare var Trans: React$ComponentType;
+
+ declare type ProviderProps = { i18n: Object, children: React$Element<*> };
+ declare var I18nextProvider: React$ComponentType;
+
+ declare type NamespacesProps = {
+ components: Array>,
+ i18n: { loadNamespaces: Function }
+ };
+ declare function loadNamespaces(props: NamespacesProps): Promise;
+
+ declare var reactI18nextModule: {
+ type: "3rdParty",
+ init: (instance: Object) => void
+ };
+
+ declare function setDefaults(options: TranslateOptions): void;
+
+ declare function getDefaults(): TranslateOptions;
+
+ declare function getI18n(): Object;
+
+ declare function setI18n(instance: Object): void;
+}
diff --git a/scm-ui-components/packages/ui-components/package.json b/scm-ui-components/packages/ui-components/package.json
index 096a8636b3..3d52d16173 100644
--- a/scm-ui-components/packages/ui-components/package.json
+++ b/scm-ui-components/packages/ui-components/package.json
@@ -12,7 +12,7 @@
"eslint-fix": "eslint src --fix"
},
"devDependencies": {
- "@scm-manager/ui-bundler": "^0.0.17",
+ "@scm-manager/ui-bundler": "^0.0.19",
"create-index": "^2.3.0",
"enzyme": "^3.5.0",
"enzyme-adapter-react-16": "^1.3.1",
diff --git a/scm-ui-components/packages/ui-components/src/Image.js b/scm-ui-components/packages/ui-components/src/Image.js
index 5cb7fd6aa9..f5760277e4 100644
--- a/scm-ui-components/packages/ui-components/src/Image.js
+++ b/scm-ui-components/packages/ui-components/src/Image.js
@@ -1,6 +1,6 @@
//@flow
import React from "react";
-import { withContextPath } from "./urls";
+import {withContextPath} from "./urls";
type Props = {
src: string,
diff --git a/scm-ui-components/packages/ui-components/src/apiclient.js b/scm-ui-components/packages/ui-components/src/apiclient.js
index 0b57abeada..bd19dcdf14 100644
--- a/scm-ui-components/packages/ui-components/src/apiclient.js
+++ b/scm-ui-components/packages/ui-components/src/apiclient.js
@@ -48,6 +48,14 @@ class ApiClient {
return this.httpRequestWithJSONBody("PUT", url, contentType, payload);
}
+ head(url: string) {
+ let options: RequestOptions = {
+ method: "HEAD"
+ };
+ options = Object.assign(options, fetchOptions);
+ return fetch(createUrl(url), options).then(handleStatusCode);
+ }
+
delete(url: string): Promise {
let options: RequestOptions = {
method: "DELETE"
diff --git a/scm-ui-components/packages/ui-components/src/buttons/DownloadButton.js b/scm-ui-components/packages/ui-components/src/buttons/DownloadButton.js
new file mode 100644
index 0000000000..3a99dc876b
--- /dev/null
+++ b/scm-ui-components/packages/ui-components/src/buttons/DownloadButton.js
@@ -0,0 +1,25 @@
+//@flow
+import React from "react";
+import Button, { type ButtonProps } from "./Button";
+import type {File} from "@scm-manager/ui-types";
+
+type Props = {
+ displayName: string,
+ url: string
+};
+
+class DownloadButton extends React.Component {
+ render() {
+ const {displayName, url} = this.props;
+ return (
+
+
+
+
+ {displayName}
+
+ );
+ }
+}
+
+export default DownloadButton;
diff --git a/scm-ui-components/packages/ui-components/src/buttons/index.js b/scm-ui-components/packages/ui-components/src/buttons/index.js
index d7da320fc2..2e166e1d93 100644
--- a/scm-ui-components/packages/ui-components/src/buttons/index.js
+++ b/scm-ui-components/packages/ui-components/src/buttons/index.js
@@ -7,4 +7,4 @@ export { default as DeleteButton } from "./DeleteButton.js";
export { default as EditButton } from "./EditButton.js";
export { default as RemoveEntryOfTableButton } from "./RemoveEntryOfTableButton.js";
export { default as SubmitButton } from "./SubmitButton.js";
-
+export {default as DownloadButton} from "./DownloadButton.js";
diff --git a/scm-ui-components/packages/ui-components/src/navigation/NavLink.js b/scm-ui-components/packages/ui-components/src/navigation/NavLink.js
index 20a7f2469f..9a7c72adb1 100644
--- a/scm-ui-components/packages/ui-components/src/navigation/NavLink.js
+++ b/scm-ui-components/packages/ui-components/src/navigation/NavLink.js
@@ -1,6 +1,6 @@
//@flow
import * as React from "react";
-import { Route, Link } from "react-router-dom";
+import {Link, Route} from "react-router-dom";
// TODO mostly copy of PrimaryNavigationLink
diff --git a/scm-ui-components/packages/ui-components/src/urls.test.js b/scm-ui-components/packages/ui-components/src/urls.test.js
index e1d88bfe55..60f27510b8 100644
--- a/scm-ui-components/packages/ui-components/src/urls.test.js
+++ b/scm-ui-components/packages/ui-components/src/urls.test.js
@@ -1,5 +1,5 @@
// @flow
-import { concat, getPageFromMatch, withEndingSlash } from "./urls";
+import {concat, getPageFromMatch, withEndingSlash} from "./urls";
describe("tests for withEndingSlash", () => {
diff --git a/scm-ui-components/packages/ui-components/yarn.lock b/scm-ui-components/packages/ui-components/yarn.lock
index b4b0cc32a8..0200fdd1bf 100644
--- a/scm-ui-components/packages/ui-components/yarn.lock
+++ b/scm-ui-components/packages/ui-components/yarn.lock
@@ -641,9 +641,9 @@
version "0.0.2"
resolved "https://registry.yarnpkg.com/@scm-manager/eslint-config/-/eslint-config-0.0.2.tgz#94cc8c3fb4f51f870b235893dc134fc6c423ae85"
-"@scm-manager/ui-bundler@^0.0.17":
- version "0.0.17"
- resolved "https://registry.yarnpkg.com/@scm-manager/ui-bundler/-/ui-bundler-0.0.17.tgz#949b90ca57e4268be28fcf4975bd9622f60278bb"
+"@scm-manager/ui-bundler@^0.0.19":
+ version "0.0.19"
+ resolved "https://registry.yarnpkg.com/@scm-manager/ui-bundler/-/ui-bundler-0.0.19.tgz#646ab1fa1e5389fad614542215c60678fb9816ae"
dependencies:
"@babel/core" "^7.0.0"
"@babel/plugin-proposal-class-properties" "^7.0.0"
diff --git a/scm-ui-components/packages/ui-types/package.json b/scm-ui-components/packages/ui-types/package.json
index 0701870562..122b1ba504 100644
--- a/scm-ui-components/packages/ui-types/package.json
+++ b/scm-ui-components/packages/ui-types/package.json
@@ -14,7 +14,7 @@
"check": "flow check"
},
"devDependencies": {
- "@scm-manager/ui-bundler": "^0.0.17"
+ "@scm-manager/ui-bundler": "^0.0.19"
},
"browserify": {
"transform": [
diff --git a/scm-ui-components/packages/ui-types/src/RepositoryPermissions.js b/scm-ui-components/packages/ui-types/src/RepositoryPermissions.js
index d86e499378..4352c21da6 100644
--- a/scm-ui-components/packages/ui-types/src/RepositoryPermissions.js
+++ b/scm-ui-components/packages/ui-types/src/RepositoryPermissions.js
@@ -1,5 +1,5 @@
//@flow
-import type { Links } from "./hal";
+import type {Links} from "./hal";
export type Permission = PermissionCreateEntry & {
_links: Links
diff --git a/scm-ui-components/packages/ui-types/yarn.lock b/scm-ui-components/packages/ui-types/yarn.lock
index 58b579d6ae..4f7257e8bb 100644
--- a/scm-ui-components/packages/ui-types/yarn.lock
+++ b/scm-ui-components/packages/ui-types/yarn.lock
@@ -707,9 +707,9 @@
version "0.0.2"
resolved "https://registry.yarnpkg.com/@scm-manager/eslint-config/-/eslint-config-0.0.2.tgz#94cc8c3fb4f51f870b235893dc134fc6c423ae85"
-"@scm-manager/ui-bundler@^0.0.17":
- version "0.0.17"
- resolved "https://registry.yarnpkg.com/@scm-manager/ui-bundler/-/ui-bundler-0.0.17.tgz#949b90ca57e4268be28fcf4975bd9622f60278bb"
+"@scm-manager/ui-bundler@^0.0.19":
+ version "0.0.19"
+ resolved "https://registry.yarnpkg.com/@scm-manager/ui-bundler/-/ui-bundler-0.0.19.tgz#646ab1fa1e5389fad614542215c60678fb9816ae"
dependencies:
"@babel/core" "^7.0.0"
"@babel/plugin-proposal-class-properties" "^7.0.0"
diff --git a/scm-ui/package.json b/scm-ui/package.json
index de42ff2a36..69ca90a5af 100644
--- a/scm-ui/package.json
+++ b/scm-ui/package.json
@@ -10,19 +10,24 @@
"bulma": "^0.7.1",
"bulma-tooltip": "^2.0.2",
"classnames": "^2.2.5",
+ "diff2html": "^2.4.0",
"font-awesome": "^4.7.0",
"history": "^4.7.2",
"i18next": "^11.4.0",
"i18next-browser-languagedetector": "^2.2.2",
"i18next-fetch-backend": "^0.1.0",
"moment": "^2.22.2",
- "react": "^16.5.2",
- "react-dom": "^16.5.2",
+ "node-sass": "^4.9.3",
+ "postcss-easy-import": "^3.0.0",
+ "react": "^16.4.2",
+ "react-diff-view": "^1.7.0",
+ "react-dom": "^16.4.2",
"react-i18next": "^7.9.0",
"react-jss": "^8.6.0",
"react-redux": "^5.0.7",
"react-router-dom": "^4.3.1",
"react-router-redux": "^5.0.0-alpha.9",
+ "react-syntax-highlighter": "^9.0.1",
"redux": "^4.0.0",
"redux-devtools-extension": "^2.13.5",
"redux-logger": "^3.0.6",
@@ -43,7 +48,7 @@
"pre-commit": "jest && flow && eslint src"
},
"devDependencies": {
- "@scm-manager/ui-bundler": "^0.0.17",
+ "@scm-manager/ui-bundler": "^0.0.19",
"copyfiles": "^2.0.0",
"enzyme": "^3.3.0",
"enzyme-adapter-react-16": "^1.1.1",
@@ -53,6 +58,7 @@
"node-sass": "^4.9.3",
"node-sass-chokidar": "^1.3.0",
"npm-run-all": "^4.1.3",
+ "postcss-easy-import": "^3.0.0",
"prettier": "^1.13.7",
"react-router-enzyme-context": "^1.2.0",
"react-test-renderer": "^16.4.1",
diff --git a/scm-ui/public/index.mustache b/scm-ui/public/index.mustache
index 802be2ca97..62a40d8e93 100644
--- a/scm-ui/public/index.mustache
+++ b/scm-ui/public/index.mustache
@@ -36,5 +36,9 @@
+
+ {{#liveReloadURL}}
+
+ {{/liveReloadURL}}