mirror of
https://github.com/scm-manager/scm-manager.git
synced 2026-01-27 17:59:09 +01:00
Merge with default
This commit is contained in:
2
Jenkinsfile
vendored
2
Jenkinsfile
vendored
@@ -7,7 +7,7 @@ import com.cloudogu.ces.cesbuildlib.*
|
||||
node('docker') {
|
||||
|
||||
// Change this as when we go back to default - necessary for proper SonarQube analysis
|
||||
mainBranch = '2.0.0-m3'
|
||||
mainBranch = 'default'
|
||||
|
||||
properties([
|
||||
// Keep only the last 10 build to preserve space
|
||||
|
||||
@@ -11,7 +11,8 @@
|
||||
"test": "lerna run --scope '@scm-manager/ui-*' test",
|
||||
"typecheck": "lerna run --scope '@scm-manager/ui-*' typecheck",
|
||||
"serve": "webpack-dev-server --mode=development --config=scm-ui/ui-scripts/src/webpack.config.js",
|
||||
"deploy": "ui-scripts publish"
|
||||
"deploy": "ui-scripts publish",
|
||||
"set-version": "ui-scripts version"
|
||||
},
|
||||
"devDependencies": {
|
||||
"babel-plugin-reflow": "^0.2.7",
|
||||
|
||||
@@ -99,8 +99,9 @@ import javax.xml.transform.TransformerFactory;
|
||||
import javax.xml.transform.dom.DOMSource;
|
||||
import javax.xml.transform.stream.StreamResult;
|
||||
|
||||
import static javax.lang.model.util.ElementFilter.methodsIn;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Sebastian Sdorra
|
||||
*/
|
||||
@SupportedAnnotationTypes("*")
|
||||
@@ -372,6 +373,18 @@ public final class ScmAnnotationProcessor extends AbstractProcessor {
|
||||
attributes.put(entry.getKey().getSimpleName().toString(),
|
||||
getValue(entry.getValue()));
|
||||
}
|
||||
|
||||
// add default values
|
||||
for (ExecutableElement meth : methodsIn(annotationMirror.getAnnotationType().asElement().getEnclosedElements())) {
|
||||
String attribute = meth.getSimpleName().toString();
|
||||
AnnotationValue defaultValue = meth.getDefaultValue();
|
||||
if (defaultValue != null && !attributes.containsKey(attribute)) {
|
||||
String value = getValue(defaultValue);
|
||||
if (value != null && !value.isEmpty()) {
|
||||
attributes.put(attribute, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -8,8 +8,7 @@
|
||||
<artifactId>scm</artifactId>
|
||||
<version>2.0.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<groupId>sonia.scm</groupId>
|
||||
|
||||
<artifactId>scm-annotations</artifactId>
|
||||
<version>2.0.0-SNAPSHOT</version>
|
||||
<name>scm-annotations</name>
|
||||
|
||||
@@ -134,7 +134,7 @@ public final class IterableQueue<T> implements Iterable<T>
|
||||
else
|
||||
{
|
||||
logger.trace("create queue iterator");
|
||||
iterator = new QueueIterator<T>(this);
|
||||
iterator = new QueueIterator<>(this);
|
||||
}
|
||||
|
||||
return iterator;
|
||||
|
||||
@@ -63,7 +63,7 @@ public class LimitedSortedSet<E> extends ForwardingSortedSet<E>
|
||||
*/
|
||||
public LimitedSortedSet(int maxSize)
|
||||
{
|
||||
this.sortedSet = new TreeSet<E>();
|
||||
this.sortedSet = new TreeSet<>();
|
||||
this.maxSize = maxSize;
|
||||
}
|
||||
|
||||
|
||||
@@ -183,5 +183,5 @@ public abstract class AbstractResourceProcessor implements ResourceProcessor
|
||||
//~--- fields ---------------------------------------------------------------
|
||||
|
||||
/** Field description */
|
||||
private Map<String, String> variableMap = new HashMap<String, String>();
|
||||
private Map<String, String> variableMap = new HashMap<>();
|
||||
}
|
||||
|
||||
@@ -52,7 +52,7 @@ public class INIConfiguration
|
||||
*/
|
||||
public INIConfiguration()
|
||||
{
|
||||
this.sectionMap = new LinkedHashMap<String, INISection>();
|
||||
this.sectionMap = new LinkedHashMap<>();
|
||||
}
|
||||
|
||||
//~--- methods --------------------------------------------------------------
|
||||
|
||||
@@ -55,7 +55,7 @@ public class INISection
|
||||
public INISection(String name)
|
||||
{
|
||||
this.name = name;
|
||||
this.parameters = new LinkedHashMap<String, String>();
|
||||
this.parameters = new LinkedHashMap<>();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -46,7 +46,7 @@ public final class Base32 extends AbstractBase
|
||||
{
|
||||
|
||||
/** base value */
|
||||
private static final BigInteger BASE = BigInteger.valueOf(32l);
|
||||
private static final BigInteger BASE = BigInteger.valueOf(32L);
|
||||
|
||||
/** char table */
|
||||
private static final String CHARS = "0123456789bcdefghjkmnpqrstuvwxyz";
|
||||
|
||||
@@ -46,7 +46,7 @@ public final class Base62 extends AbstractBase
|
||||
{
|
||||
|
||||
/** base value */
|
||||
private static final BigInteger BASE = BigInteger.valueOf(62l);
|
||||
private static final BigInteger BASE = BigInteger.valueOf(62L);
|
||||
|
||||
/** char table */
|
||||
private static final String CHARS =
|
||||
|
||||
@@ -79,7 +79,7 @@ public final class LinkTextParser
|
||||
public static String parseText(String content)
|
||||
{
|
||||
Matcher m = REGEX_URL.matcher(content);
|
||||
List<Token> tokens = new ArrayList<Token>();
|
||||
List<Token> tokens = new ArrayList<>();
|
||||
int position = 0;
|
||||
String tokenContent = null;
|
||||
|
||||
|
||||
@@ -119,7 +119,7 @@ public final class ServiceUtil
|
||||
*/
|
||||
public static <T> List<T> getServices(Class<T> type)
|
||||
{
|
||||
List<T> result = new ArrayList<T>();
|
||||
List<T> result = new ArrayList<>();
|
||||
|
||||
try
|
||||
{
|
||||
|
||||
@@ -38,23 +38,12 @@ package sonia.scm.util;
|
||||
import com.google.common.base.Strings;
|
||||
import com.google.common.collect.Multimap;
|
||||
|
||||
//~--- JDK imports ------------------------------------------------------------
|
||||
|
||||
import java.math.BigInteger;
|
||||
|
||||
import java.text.ParseException;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.*;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.Date;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.TimeZone;
|
||||
//~--- JDK imports ------------------------------------------------------------
|
||||
|
||||
/**
|
||||
*
|
||||
@@ -273,12 +262,12 @@ public final class Util
|
||||
Comparator<T> comparator, CollectionAppender<T> appender, int start,
|
||||
int limit)
|
||||
{
|
||||
List<T> result = new ArrayList<T>();
|
||||
List<T> valueList = new ArrayList<T>(values);
|
||||
List<T> result = new ArrayList<>();
|
||||
List<T> valueList = new ArrayList<>(values);
|
||||
|
||||
if (comparator != null)
|
||||
{
|
||||
Collections.sort(valueList, comparator);
|
||||
valueList.sort(comparator);
|
||||
}
|
||||
|
||||
int length = valueList.size();
|
||||
@@ -506,12 +495,10 @@ public final class Util
|
||||
{
|
||||
StringBuilder buffer = new StringBuilder();
|
||||
|
||||
for (int i = 0; i < byteValue.length; i++)
|
||||
{
|
||||
int x = byteValue[i] & 0xff;
|
||||
for (final byte aByteValue : byteValue) {
|
||||
int x = aByteValue & 0xff;
|
||||
|
||||
if (x < 16)
|
||||
{
|
||||
if (x < 16) {
|
||||
buffer.append('0');
|
||||
}
|
||||
|
||||
|
||||
@@ -66,7 +66,7 @@ public class EnvList
|
||||
*/
|
||||
public EnvList()
|
||||
{
|
||||
envMap = new HashMap<String, String>();
|
||||
envMap = new HashMap<>();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -77,7 +77,7 @@ public class EnvList
|
||||
*/
|
||||
public EnvList(EnvList l)
|
||||
{
|
||||
envMap = new HashMap<String, String>(l.envMap);
|
||||
envMap = new HashMap<>(l.envMap);
|
||||
}
|
||||
|
||||
//~--- methods --------------------------------------------------------------
|
||||
|
||||
@@ -470,13 +470,13 @@ public class BufferedHttpServletResponse extends HttpServletResponseWrapper
|
||||
private ByteArrayPrintWriter pw = null;
|
||||
|
||||
/** Field description */
|
||||
private Set<Cookie> cookies = new HashSet<Cookie>();
|
||||
private Set<Cookie> cookies = new HashSet<>();
|
||||
|
||||
/** Field description */
|
||||
private int statusCode = HttpServletResponse.SC_OK;
|
||||
|
||||
/** Field description */
|
||||
private Map<String, String> headers = new LinkedHashMap<String, String>();
|
||||
private Map<String, String> headers = new LinkedHashMap<>();
|
||||
|
||||
/** Field description */
|
||||
private String statusMessage;
|
||||
|
||||
@@ -40,28 +40,20 @@ import com.google.common.io.ByteStreams;
|
||||
import com.google.common.io.Closer;
|
||||
import com.google.inject.Inject;
|
||||
import com.google.inject.Singleton;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
//~--- JDK imports ------------------------------------------------------------
|
||||
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.BufferedOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
|
||||
import javax.servlet.http.HttpServlet;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.io.*;
|
||||
import java.net.HttpURLConnection;
|
||||
|
||||
import java.util.Enumeration;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
import javax.servlet.http.HttpServlet;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
//~--- JDK imports ------------------------------------------------------------
|
||||
|
||||
/**
|
||||
*
|
||||
@@ -175,10 +167,8 @@ public class ProxyServlet extends HttpServlet
|
||||
private void copyContent(HttpURLConnection con, HttpServletResponse response)
|
||||
throws IOException
|
||||
{
|
||||
Closer closer = Closer.create();
|
||||
|
||||
try
|
||||
{
|
||||
try (Closer closer = Closer.create()) {
|
||||
InputStream webToProxyBuf =
|
||||
closer.register(new BufferedInputStream(con.getInputStream()));
|
||||
OutputStream proxyToClientBuf =
|
||||
@@ -188,10 +178,6 @@ public class ProxyServlet extends HttpServlet
|
||||
|
||||
logger.trace("copied {} bytes for proxy", bytes);
|
||||
}
|
||||
finally
|
||||
{
|
||||
closer.close();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -102,7 +102,7 @@ public class XmlMapStringAdapter
|
||||
public Map<String, String> unmarshal(XmlMapStringElement[] elements)
|
||||
throws Exception
|
||||
{
|
||||
Map<String, String> map = new HashMap<String, String>();
|
||||
Map<String, String> map = new HashMap<>();
|
||||
|
||||
if (elements != null)
|
||||
{
|
||||
|
||||
@@ -90,7 +90,7 @@ public class XmlSetStringAdapter extends XmlAdapter<String, Set<String>>
|
||||
@Override
|
||||
public Set<String> unmarshal(String rawString) throws Exception
|
||||
{
|
||||
Set<String> tokens = new HashSet<String>();
|
||||
Set<String> tokens = new HashSet<>();
|
||||
|
||||
for (String token : rawString.split(","))
|
||||
{
|
||||
|
||||
@@ -63,7 +63,7 @@ public class IterableQueueTest
|
||||
@Test(expected = IllegalStateException.class)
|
||||
public void testDuplicatedEndReached()
|
||||
{
|
||||
IterableQueue<String> queue = new IterableQueue<String>();
|
||||
IterableQueue<String> queue = new IterableQueue<>();
|
||||
|
||||
queue.endReached();
|
||||
queue.endReached();
|
||||
@@ -76,7 +76,7 @@ public class IterableQueueTest
|
||||
@Test
|
||||
public void testIterator()
|
||||
{
|
||||
IterableQueue<String> queue = new IterableQueue<String>();
|
||||
IterableQueue<String> queue = new IterableQueue<>();
|
||||
|
||||
assertEquals(QueueIterator.class, queue.iterator().getClass());
|
||||
queue.endReached();
|
||||
@@ -120,7 +120,7 @@ public class IterableQueueTest
|
||||
@Test(expected = IllegalStateException.class)
|
||||
public void testPushEndReached()
|
||||
{
|
||||
IterableQueue<String> queue = new IterableQueue<String>();
|
||||
IterableQueue<String> queue = new IterableQueue<>();
|
||||
|
||||
queue.push("a");
|
||||
queue.endReached();
|
||||
@@ -134,7 +134,7 @@ public class IterableQueueTest
|
||||
@Test
|
||||
public void testSingleConsumer()
|
||||
{
|
||||
final IterableQueue<Integer> queue = new IterableQueue<Integer>();
|
||||
final IterableQueue<Integer> queue = new IterableQueue<>();
|
||||
|
||||
new Thread(new IntegerProducer(queue, false, 100)).start();
|
||||
assertResult(Lists.newArrayList(queue), 100);
|
||||
@@ -176,12 +176,12 @@ public class IterableQueueTest
|
||||
ExecutorService executor = Executors.newFixedThreadPool(threads);
|
||||
List<Future<List<Integer>>> futures = Lists.newArrayList();
|
||||
|
||||
final IterableQueue<Integer> queue = new IterableQueue<Integer>();
|
||||
final IterableQueue<Integer> queue = new IterableQueue<>();
|
||||
|
||||
for (int i = 0; i < consumer; i++)
|
||||
{
|
||||
Future<List<Integer>> future =
|
||||
executor.submit(new CallableQueueCollector<Integer>(queue));
|
||||
executor.submit(new CallableQueueCollector<>(queue));
|
||||
|
||||
futures.add(future);
|
||||
}
|
||||
|
||||
@@ -71,8 +71,8 @@ public class ScmModuleTest
|
||||
assertThat(
|
||||
module.getExtensions(),
|
||||
containsInAnyOrder(
|
||||
(Class<?>) String.class,
|
||||
(Class<?>) Integer.class
|
||||
String.class,
|
||||
Integer.class
|
||||
)
|
||||
);
|
||||
assertThat(
|
||||
@@ -86,8 +86,8 @@ public class ScmModuleTest
|
||||
assertThat(
|
||||
module.getEvents(),
|
||||
containsInAnyOrder(
|
||||
(Class<?>) String.class,
|
||||
(Class<?>) Boolean.class
|
||||
String.class,
|
||||
Boolean.class
|
||||
)
|
||||
);
|
||||
assertThat(
|
||||
@@ -100,15 +100,15 @@ public class ScmModuleTest
|
||||
assertThat(
|
||||
module.getRestProviders(),
|
||||
containsInAnyOrder(
|
||||
(Class<?>) Integer.class,
|
||||
(Class<?>) Long.class
|
||||
Integer.class,
|
||||
Long.class
|
||||
)
|
||||
);
|
||||
assertThat(
|
||||
module.getRestResources(),
|
||||
containsInAnyOrder(
|
||||
(Class<?>) Float.class,
|
||||
(Class<?>) Double.class
|
||||
Float.class,
|
||||
Double.class
|
||||
)
|
||||
);
|
||||
//J+
|
||||
|
||||
@@ -128,7 +128,7 @@ public class TemplateEngineFactoryTest
|
||||
assertTrue(engines.contains(engine1));
|
||||
assertTrue(engines.contains(engine2));
|
||||
|
||||
Set<TemplateEngine> ce = new HashSet<TemplateEngine>();
|
||||
Set<TemplateEngine> ce = new HashSet<>();
|
||||
|
||||
ce.add(engine1);
|
||||
factory = new TemplateEngineFactory(ce, engine2);
|
||||
|
||||
@@ -56,7 +56,7 @@ public class UrlBuilderTest
|
||||
UrlBuilder builder = new UrlBuilder("http://www.short.de");
|
||||
|
||||
builder.appendParameter("i", 123).appendParameter("s", "abc");
|
||||
builder.appendParameter("b", true).appendParameter("l", 321l);
|
||||
builder.appendParameter("b", true).appendParameter("l", 321L);
|
||||
assertEquals("http://www.short.de?i=123&s=abc&b=true&l=321", builder.toString());
|
||||
builder.appendParameter("c", "a b");
|
||||
assertEquals("http://www.short.de?i=123&s=abc&b=true&l=321&c=a%20b", builder.toString());
|
||||
|
||||
@@ -199,7 +199,7 @@ public class XmlGroupDatabase implements XmlDatabase<Group>
|
||||
/** Field description */
|
||||
@XmlJavaTypeAdapter(XmlGroupMapAdapter.class)
|
||||
@XmlElement(name = "groups")
|
||||
private Map<String, Group> groupMap = new LinkedHashMap<String, Group>();
|
||||
private Map<String, Group> groupMap = new LinkedHashMap<>();
|
||||
|
||||
/** Field description */
|
||||
private Long lastModified;
|
||||
|
||||
@@ -72,7 +72,7 @@ public class XmlGroupList implements Iterable<Group>
|
||||
*/
|
||||
public XmlGroupList(Map<String, Group> groupMap)
|
||||
{
|
||||
this.groups = new LinkedList<Group>(groupMap.values());
|
||||
this.groups = new LinkedList<>(groupMap.values());
|
||||
}
|
||||
|
||||
//~--- methods --------------------------------------------------------------
|
||||
|
||||
@@ -81,7 +81,7 @@ public class XmlGroupMapAdapter
|
||||
@Override
|
||||
public Map<String, Group> unmarshal(XmlGroupList groups) throws Exception
|
||||
{
|
||||
Map<String, Group> groupMap = new LinkedHashMap<String, Group>();
|
||||
Map<String, Group> groupMap = new LinkedHashMap<>();
|
||||
|
||||
for (Group group : groups)
|
||||
{
|
||||
|
||||
@@ -202,5 +202,5 @@ public class XmlUserDatabase implements XmlDatabase<User>
|
||||
/** Field description */
|
||||
@XmlJavaTypeAdapter(XmlUserMapAdapter.class)
|
||||
@XmlElement(name = "users")
|
||||
private Map<String, User> userMap = new LinkedHashMap<String, User>();
|
||||
private Map<String, User> userMap = new LinkedHashMap<>();
|
||||
}
|
||||
|
||||
@@ -72,7 +72,7 @@ public class XmlUserList implements Iterable<User>
|
||||
*/
|
||||
public XmlUserList(Map<String, User> userMap)
|
||||
{
|
||||
this.users = new LinkedList<User>(userMap.values());
|
||||
this.users = new LinkedList<>(userMap.values());
|
||||
}
|
||||
|
||||
//~--- methods --------------------------------------------------------------
|
||||
|
||||
@@ -81,7 +81,7 @@ public class XmlUserMapAdapter
|
||||
@Override
|
||||
public Map<String, User> unmarshal(XmlUserList users) throws Exception
|
||||
{
|
||||
Map<String, User> userMap = new LinkedHashMap<String, User>();
|
||||
Map<String, User> userMap = new LinkedHashMap<>();
|
||||
|
||||
for (User user : users)
|
||||
{
|
||||
|
||||
@@ -52,7 +52,9 @@ public final class XmlStreams {
|
||||
}
|
||||
|
||||
private static XMLStreamReader createReader(Reader reader) throws XMLStreamException {
|
||||
return XMLInputFactory.newInstance().createXMLStreamReader(reader);
|
||||
XMLInputFactory factory = XMLInputFactory.newInstance();
|
||||
factory.setProperty(XMLInputFactory.IS_SUPPORTING_EXTERNAL_ENTITIES, Boolean.FALSE);
|
||||
return factory.createXMLStreamReader(reader);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -14,6 +14,12 @@ const Switcher = styled(ButtonAddons)`
|
||||
right: 0;
|
||||
`;
|
||||
|
||||
const SmallButton = styled(Button).attrs(props => ({
|
||||
className: "is-small"
|
||||
}))`
|
||||
height: inherit;
|
||||
`;
|
||||
|
||||
type Props = {
|
||||
repository: Repository;
|
||||
};
|
||||
@@ -62,9 +68,9 @@ export default class ProtocolInformation extends React.Component<Props, State> {
|
||||
}
|
||||
|
||||
return (
|
||||
<Button color={color} action={() => this.selectProtocol(protocol)}>
|
||||
<SmallButton color={color} action={() => this.selectProtocol(protocol)}>
|
||||
{name.toUpperCase()}
|
||||
</Button>
|
||||
</SmallButton>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -145,7 +145,7 @@ public class HgPackageReader
|
||||
*/
|
||||
private void filterPackage(HgPackages packages)
|
||||
{
|
||||
List<HgPackage> pkgList = new ArrayList<HgPackage>();
|
||||
List<HgPackage> pkgList = new ArrayList<>();
|
||||
|
||||
for (HgPackage pkg : packages)
|
||||
{
|
||||
@@ -228,7 +228,7 @@ public class HgPackageReader
|
||||
if (packages == null)
|
||||
{
|
||||
packages = new HgPackages();
|
||||
packages.setPackages(new ArrayList<HgPackage>());
|
||||
packages.setPackages(new ArrayList<>());
|
||||
}
|
||||
|
||||
return packages;
|
||||
|
||||
@@ -211,15 +211,7 @@ public class SVNKitLogger extends SVNDebugLogAdapter
|
||||
*/
|
||||
private Logger getLogger(SVNLogType type)
|
||||
{
|
||||
Logger logger = loggerMap.get(type);
|
||||
|
||||
if (logger == null)
|
||||
{
|
||||
logger = LoggerFactory.getLogger(parseName(type.getName()));
|
||||
loggerMap.put(type, logger);
|
||||
}
|
||||
|
||||
return logger;
|
||||
return loggerMap.computeIfAbsent(type, t -> LoggerFactory.getLogger(parseName(t.getName())));
|
||||
}
|
||||
|
||||
//~--- fields ---------------------------------------------------------------
|
||||
|
||||
@@ -81,15 +81,7 @@ public class MapCacheManager
|
||||
@Override
|
||||
public synchronized <K, V> MapCache<K, V> getCache(String name)
|
||||
{
|
||||
MapCache<K, V> cache = cacheMap.get(name);
|
||||
|
||||
if (cache == null)
|
||||
{
|
||||
cache = new MapCache<K, V>();
|
||||
cacheMap.put(name, cache);
|
||||
}
|
||||
|
||||
return cache;
|
||||
return (MapCache<K, V>) cacheMap.computeIfAbsent(name, k -> new MapCache<K, V>());
|
||||
}
|
||||
|
||||
//~--- fields ---------------------------------------------------------------
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import React from "react";
|
||||
import classNames from "classnames";
|
||||
import { Async, AsyncCreatable } from "react-select";
|
||||
import { SelectValue } from "@scm-manager/ui-types";
|
||||
import LabelWithHelpIcon from "./forms/LabelWithHelpIcon";
|
||||
@@ -14,6 +15,7 @@ type Props = {
|
||||
loadingMessage: string;
|
||||
noOptionsMessage: string;
|
||||
creatable?: boolean;
|
||||
className?: string;
|
||||
};
|
||||
|
||||
type State = {};
|
||||
@@ -53,10 +55,11 @@ class Autocomplete extends React.Component<Props, State> {
|
||||
loadingMessage,
|
||||
noOptionsMessage,
|
||||
loadSuggestions,
|
||||
creatable
|
||||
creatable,
|
||||
className
|
||||
} = this.props;
|
||||
return (
|
||||
<div className="field">
|
||||
<div className={classNames("field", className)}>
|
||||
<LabelWithHelpIcon label={label} helpText={helpText} />
|
||||
<div className="control">
|
||||
{creatable ? (
|
||||
|
||||
@@ -336,7 +336,7 @@ exports[`Storyshots DateFromNow Default 1`] = `
|
||||
|
||||
exports[`Storyshots Forms|Checkbox Default 1`] = `
|
||||
<div
|
||||
className="sc-gipzik xsalO"
|
||||
className="sc-fBuWsC ldmpJA"
|
||||
>
|
||||
<div
|
||||
className="field"
|
||||
@@ -381,7 +381,7 @@ exports[`Storyshots Forms|Checkbox Default 1`] = `
|
||||
|
||||
exports[`Storyshots Forms|Checkbox Disabled 1`] = `
|
||||
<div
|
||||
className="sc-gipzik xsalO"
|
||||
className="sc-fBuWsC ldmpJA"
|
||||
>
|
||||
<div
|
||||
className="field"
|
||||
@@ -409,7 +409,7 @@ exports[`Storyshots Forms|Checkbox Disabled 1`] = `
|
||||
|
||||
exports[`Storyshots Forms|Radio Default 1`] = `
|
||||
<div
|
||||
className="sc-csuQGl fFFkRK"
|
||||
className="sc-fMiknA keSQNk"
|
||||
>
|
||||
<label
|
||||
className="radio"
|
||||
@@ -438,7 +438,7 @@ exports[`Storyshots Forms|Radio Default 1`] = `
|
||||
|
||||
exports[`Storyshots Forms|Radio Disabled 1`] = `
|
||||
<div
|
||||
className="sc-csuQGl fFFkRK"
|
||||
className="sc-fMiknA keSQNk"
|
||||
>
|
||||
<label
|
||||
className="radio"
|
||||
@@ -2311,3 +2311,173 @@ PORT_NUMBER =
|
||||
</pre>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`Storyshots Table|Table Default 1`] = `
|
||||
<table
|
||||
className="sc-jhAzac hmXDXQ table content is-hoverable"
|
||||
>
|
||||
<thead>
|
||||
<tr>
|
||||
<th
|
||||
onMouseEnter={[Function]}
|
||||
onMouseLeave={[Function]}
|
||||
>
|
||||
First Name
|
||||
</th>
|
||||
<th
|
||||
className="has-cursor-pointer"
|
||||
onClick={[Function]}
|
||||
onMouseEnter={[Function]}
|
||||
onMouseLeave={[Function]}
|
||||
>
|
||||
Last Name
|
||||
<i
|
||||
className="fas fa-sort-amount-down has-text-grey-light sc-hzDkRC escBde"
|
||||
/>
|
||||
</th>
|
||||
<th
|
||||
onMouseEnter={[Function]}
|
||||
onMouseLeave={[Function]}
|
||||
>
|
||||
E-Mail
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>
|
||||
<h4>
|
||||
Tricia
|
||||
</h4>
|
||||
</td>
|
||||
<td>
|
||||
<b
|
||||
style={
|
||||
Object {
|
||||
"color": "red",
|
||||
}
|
||||
}
|
||||
>
|
||||
McMillan
|
||||
</b>
|
||||
</td>
|
||||
<td>
|
||||
<a>
|
||||
tricia@hitchhiker.com
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<h4>
|
||||
Arthur
|
||||
</h4>
|
||||
</td>
|
||||
<td>
|
||||
<b
|
||||
style={
|
||||
Object {
|
||||
"color": "red",
|
||||
}
|
||||
}
|
||||
>
|
||||
Dent
|
||||
</b>
|
||||
</td>
|
||||
<td>
|
||||
<a>
|
||||
arthur@hitchhiker.com
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
`;
|
||||
|
||||
exports[`Storyshots Table|Table Empty 1`] = `
|
||||
<div
|
||||
className="notification is-info"
|
||||
>
|
||||
|
||||
No data found.
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`Storyshots Table|Table TextColumn 1`] = `
|
||||
<table
|
||||
className="sc-jhAzac hmXDXQ table content is-hoverable"
|
||||
>
|
||||
<thead>
|
||||
<tr>
|
||||
<th
|
||||
className="has-cursor-pointer"
|
||||
onClick={[Function]}
|
||||
onMouseEnter={[Function]}
|
||||
onMouseLeave={[Function]}
|
||||
>
|
||||
Id
|
||||
<i
|
||||
className="fas fa-sort-alpha-down has-text-grey-light sc-hzDkRC escBde"
|
||||
/>
|
||||
</th>
|
||||
<th
|
||||
className="has-cursor-pointer"
|
||||
onClick={[Function]}
|
||||
onMouseEnter={[Function]}
|
||||
onMouseLeave={[Function]}
|
||||
>
|
||||
Name
|
||||
<i
|
||||
className="fas fa-sort-alpha-down has-text-grey-light sc-hzDkRC escBde"
|
||||
/>
|
||||
</th>
|
||||
<th
|
||||
className="has-cursor-pointer"
|
||||
onClick={[Function]}
|
||||
onMouseEnter={[Function]}
|
||||
onMouseLeave={[Function]}
|
||||
>
|
||||
Description
|
||||
<i
|
||||
className="fas fa-sort-alpha-down has-text-grey-light sc-hzDkRC escBde"
|
||||
/>
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>
|
||||
21
|
||||
</td>
|
||||
<td>
|
||||
Pommes
|
||||
</td>
|
||||
<td>
|
||||
Fried potato sticks
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
42
|
||||
</td>
|
||||
<td>
|
||||
Quarter-Pounder
|
||||
</td>
|
||||
<td>
|
||||
Big burger
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
-84
|
||||
</td>
|
||||
<td>
|
||||
Icecream
|
||||
</td>
|
||||
<td>
|
||||
Cold dessert
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
`;
|
||||
|
||||
@@ -2,8 +2,8 @@ import React from "react";
|
||||
|
||||
type Props = {
|
||||
displayName: string;
|
||||
url: string;
|
||||
disabled: boolean;
|
||||
url?: string;
|
||||
disabled?: boolean;
|
||||
onClick?: () => void;
|
||||
};
|
||||
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
import React from "react";
|
||||
import React, { FormEvent } from "react";
|
||||
import { WithTranslation, withTranslation } from "react-i18next";
|
||||
import { Links, Link } from "@scm-manager/ui-types";
|
||||
import { apiClient, SubmitButton, Loading, ErrorNotification } from "../";
|
||||
import { FormEvent } from "react";
|
||||
import { apiClient, Level, SubmitButton, Loading, ErrorNotification } from "../";
|
||||
|
||||
type RenderProps = {
|
||||
readOnly: boolean;
|
||||
@@ -179,7 +178,9 @@ class Configuration extends React.Component<Props, State> {
|
||||
<form onSubmit={this.modifyConfiguration}>
|
||||
{this.props.render(renderProps)}
|
||||
<hr />
|
||||
<SubmitButton label={t("config.form.submit")} disabled={!valid || readOnly} loading={modifying} />
|
||||
<Level
|
||||
right={<SubmitButton label={t("config.form.submit")} disabled={!valid || readOnly} loading={modifying} />}
|
||||
/>
|
||||
</form>
|
||||
</>
|
||||
);
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import React, { MouseEvent } from "react";
|
||||
|
||||
import { AddButton } from "../buttons";
|
||||
import styled from "styled-components";
|
||||
import Level from "../layout/Level";
|
||||
import InputField from "./InputField";
|
||||
import AddButton from "../buttons/AddButton";
|
||||
|
||||
type Props = {
|
||||
addEntry: (p: string) => void;
|
||||
@@ -17,6 +18,22 @@ type State = {
|
||||
entryToAdd: string;
|
||||
};
|
||||
|
||||
const StyledLevel = styled(Level)`
|
||||
align-items: stretch;
|
||||
margin-bottom: 1rem !important; // same margin as field
|
||||
`;
|
||||
|
||||
const StyledInputField = styled(InputField)`
|
||||
width: 100%;
|
||||
margin-right: 1.5rem;
|
||||
`;
|
||||
|
||||
const StyledField = styled.div.attrs(props => ({
|
||||
className: "field"
|
||||
}))`
|
||||
align-self: flex-end;
|
||||
`;
|
||||
|
||||
class AddEntryToTableField extends React.Component<Props, State> {
|
||||
constructor(props: Props) {
|
||||
super(props);
|
||||
@@ -37,23 +54,29 @@ class AddEntryToTableField extends React.Component<Props, State> {
|
||||
render() {
|
||||
const { disabled, buttonLabel, fieldLabel, errorMessage, helpText } = this.props;
|
||||
return (
|
||||
<>
|
||||
<InputField
|
||||
label={fieldLabel}
|
||||
errorMessage={errorMessage}
|
||||
onChange={this.handleAddEntryChange}
|
||||
validationError={!this.isValid()}
|
||||
value={this.state.entryToAdd}
|
||||
onReturnPressed={this.appendEntry}
|
||||
disabled={disabled}
|
||||
helpText={helpText}
|
||||
/>
|
||||
<AddButton
|
||||
label={buttonLabel}
|
||||
action={this.addButtonClicked}
|
||||
disabled={disabled || this.state.entryToAdd === "" || !this.isValid()}
|
||||
/>
|
||||
</>
|
||||
<StyledLevel
|
||||
children={
|
||||
<StyledInputField
|
||||
label={fieldLabel}
|
||||
errorMessage={errorMessage}
|
||||
onChange={this.handleAddEntryChange}
|
||||
validationError={!this.isValid()}
|
||||
value={this.state.entryToAdd}
|
||||
onReturnPressed={this.appendEntry}
|
||||
disabled={disabled}
|
||||
helpText={helpText}
|
||||
/>
|
||||
}
|
||||
right={
|
||||
<StyledField>
|
||||
<AddButton
|
||||
label={buttonLabel}
|
||||
action={this.addButtonClicked}
|
||||
disabled={disabled || this.state.entryToAdd === "" || !this.isValid()}
|
||||
/>
|
||||
</StyledField>
|
||||
}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import React, { MouseEvent } from "react";
|
||||
|
||||
import { AutocompleteObject, SelectValue } from "@scm-manager/ui-types";
|
||||
import styled from "styled-components";
|
||||
import { SelectValue } from "@scm-manager/ui-types";
|
||||
import Level from "../layout/Level";
|
||||
import Autocomplete from "../Autocomplete";
|
||||
import AddButton from "../buttons/AddButton";
|
||||
|
||||
@@ -20,6 +21,11 @@ type State = {
|
||||
selectedValue?: SelectValue;
|
||||
};
|
||||
|
||||
const StyledAutocomplete = styled(Autocomplete)`
|
||||
width: 100%;
|
||||
margin-right: 1.5rem;
|
||||
`;
|
||||
|
||||
class AutocompleteAddEntryToTableField extends React.Component<Props, State> {
|
||||
constructor(props: Props) {
|
||||
super(props);
|
||||
@@ -41,21 +47,26 @@ class AutocompleteAddEntryToTableField extends React.Component<Props, State> {
|
||||
|
||||
const { selectedValue } = this.state;
|
||||
return (
|
||||
<div className="field">
|
||||
<Autocomplete
|
||||
label={fieldLabel}
|
||||
loadSuggestions={loadSuggestions}
|
||||
valueSelected={this.handleAddEntryChange}
|
||||
helpText={helpText}
|
||||
value={selectedValue}
|
||||
placeholder={placeholder}
|
||||
loadingMessage={loadingMessage}
|
||||
noOptionsMessage={noOptionsMessage}
|
||||
creatable={true}
|
||||
/>
|
||||
|
||||
<AddButton label={buttonLabel} action={this.addButtonClicked} disabled={disabled} />
|
||||
</div>
|
||||
<Level
|
||||
children={
|
||||
<StyledAutocomplete
|
||||
label={fieldLabel}
|
||||
loadSuggestions={loadSuggestions}
|
||||
valueSelected={this.handleAddEntryChange}
|
||||
helpText={helpText}
|
||||
value={selectedValue}
|
||||
placeholder={placeholder}
|
||||
loadingMessage={loadingMessage}
|
||||
noOptionsMessage={noOptionsMessage}
|
||||
creatable={true}
|
||||
/>
|
||||
}
|
||||
right={
|
||||
<div className="field">
|
||||
<AddButton label={buttonLabel} action={this.addButtonClicked} disabled={disabled} />
|
||||
</div>
|
||||
}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -15,6 +15,7 @@ type Props = {
|
||||
errorMessage?: string;
|
||||
disabled?: boolean;
|
||||
helpText?: string;
|
||||
className?: string;
|
||||
};
|
||||
|
||||
class InputField extends React.Component<Props> {
|
||||
@@ -47,11 +48,21 @@ class InputField extends React.Component<Props> {
|
||||
};
|
||||
|
||||
render() {
|
||||
const { type, placeholder, value, validationError, errorMessage, disabled, label, helpText } = this.props;
|
||||
const {
|
||||
type,
|
||||
placeholder,
|
||||
value,
|
||||
validationError,
|
||||
errorMessage,
|
||||
disabled,
|
||||
label,
|
||||
helpText,
|
||||
className
|
||||
} = this.props;
|
||||
const errorView = validationError ? "is-danger" : "";
|
||||
const helper = validationError ? <p className="help is-danger">{errorMessage}</p> : "";
|
||||
return (
|
||||
<div className="field">
|
||||
<div className={classNames("field", className)}>
|
||||
<LabelWithHelpIcon label={label} helpText={helpText} />
|
||||
<div className="control">
|
||||
<input
|
||||
|
||||
@@ -63,6 +63,7 @@ export * from "./layout";
|
||||
export * from "./modals";
|
||||
export * from "./navigation";
|
||||
export * from "./repos";
|
||||
export * from "./table";
|
||||
|
||||
export {
|
||||
File,
|
||||
|
||||
@@ -4,15 +4,22 @@ import classNames from "classnames";
|
||||
type Props = {
|
||||
className?: string;
|
||||
left?: ReactNode;
|
||||
children?: ReactNode;
|
||||
right?: ReactNode;
|
||||
};
|
||||
|
||||
export default class Level extends React.Component<Props> {
|
||||
render() {
|
||||
const { className, left, right } = this.props;
|
||||
const { className, left, children, right } = this.props;
|
||||
let child = null;
|
||||
if (children) {
|
||||
child = <div className="level-item">{children}</div>;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={classNames("level", className)}>
|
||||
<div className="level-left">{left}</div>
|
||||
{child}
|
||||
<div className="level-right">{right}</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
18
scm-ui/ui-components/src/table/Column.tsx
Normal file
18
scm-ui/ui-components/src/table/Column.tsx
Normal file
@@ -0,0 +1,18 @@
|
||||
import React, { FC, ReactNode } from "react";
|
||||
import { ColumnProps } from "./types";
|
||||
|
||||
type Props = ColumnProps & {
|
||||
children: (row: any, columnIndex: number) => ReactNode;
|
||||
};
|
||||
|
||||
const Column: FC<Props> = ({ row, columnIndex, children }) => {
|
||||
if (row === undefined) {
|
||||
throw new Error("missing row, use column only as child of Table");
|
||||
}
|
||||
if (columnIndex === undefined) {
|
||||
throw new Error("missing row, use column only as child of Table");
|
||||
}
|
||||
return <>{children(row, columnIndex)}</>;
|
||||
};
|
||||
|
||||
export default Column;
|
||||
19
scm-ui/ui-components/src/table/SortIcon.tsx
Normal file
19
scm-ui/ui-components/src/table/SortIcon.tsx
Normal file
@@ -0,0 +1,19 @@
|
||||
import React, { FC } from "react";
|
||||
import styled from "styled-components";
|
||||
import Icon from "../Icon";
|
||||
|
||||
type Props = {
|
||||
name: string;
|
||||
isVisible: boolean;
|
||||
};
|
||||
|
||||
const IconWithMarginLeft = styled(Icon)`
|
||||
visibility: ${(props: Props) => (props.isVisible ? "visible" : "hidden")};
|
||||
margin-left: 0.25em;
|
||||
`;
|
||||
|
||||
const SortIcon: FC<Props> = (props: Props) => {
|
||||
return <IconWithMarginLeft name={props.name} isVisible={props.isVisible} />;
|
||||
};
|
||||
|
||||
export default SortIcon;
|
||||
53
scm-ui/ui-components/src/table/Table.stories.tsx
Normal file
53
scm-ui/ui-components/src/table/Table.stories.tsx
Normal file
@@ -0,0 +1,53 @@
|
||||
import React from "react";
|
||||
import { storiesOf } from "@storybook/react";
|
||||
import Table from "./Table";
|
||||
import Column from "./Column";
|
||||
import TextColumn from "./TextColumn";
|
||||
|
||||
storiesOf("Table|Table", module)
|
||||
.add("Default", () => (
|
||||
<Table
|
||||
data={[
|
||||
{ firstname: "Tricia", lastname: "McMillan", email: "tricia@hitchhiker.com" },
|
||||
{ firstname: "Arthur", lastname: "Dent", email: "arthur@hitchhiker.com" }
|
||||
]}
|
||||
>
|
||||
<Column header={"First Name"}>{(row: any) => <h4>{row.firstname}</h4>}</Column>
|
||||
<Column
|
||||
header={"Last Name"}
|
||||
createComparator={() => {
|
||||
return (a: any, b: any) => {
|
||||
if (a.lastname > b.lastname) {
|
||||
return -1;
|
||||
} else if (a.lastname < b.lastname) {
|
||||
return 1;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
}}
|
||||
>
|
||||
{(row: any) => <b style={{ color: "red" }}>{row.lastname}</b>}
|
||||
</Column>
|
||||
<Column header={"E-Mail"}>{(row: any) => <a>{row.email}</a>}</Column>
|
||||
</Table>
|
||||
))
|
||||
.add("TextColumn", () => (
|
||||
<Table
|
||||
data={[
|
||||
{ id: "21", title: "Pommes", desc: "Fried potato sticks" },
|
||||
{ id: "42", title: "Quarter-Pounder", desc: "Big burger" },
|
||||
{ id: "-84", title: "Icecream", desc: "Cold dessert" }
|
||||
]}
|
||||
>
|
||||
<TextColumn header="Id" dataKey="id" />
|
||||
<TextColumn header="Name" dataKey="title" />
|
||||
<TextColumn header="Description" dataKey="desc" />
|
||||
</Table>
|
||||
))
|
||||
.add("Empty", () => (
|
||||
<Table data={[]} emptyMessage="No data found.">
|
||||
<TextColumn header="Id" dataKey="id" />
|
||||
<TextColumn header="Name" dataKey="name" />
|
||||
</Table>
|
||||
));
|
||||
120
scm-ui/ui-components/src/table/Table.tsx
Normal file
120
scm-ui/ui-components/src/table/Table.tsx
Normal file
@@ -0,0 +1,120 @@
|
||||
import React, { FC, ReactElement, useEffect, useState } from "react";
|
||||
import styled from "styled-components";
|
||||
import { Comparator } from "./types";
|
||||
import SortIcon from "./SortIcon";
|
||||
import Notification from "../Notification";
|
||||
|
||||
const StyledTable = styled.table.attrs(() => ({
|
||||
className: "table content is-hoverable"
|
||||
}))``;
|
||||
|
||||
type Props = {
|
||||
data: any[];
|
||||
sortable?: boolean;
|
||||
emptyMessage?: string;
|
||||
children: Array<ReactElement>;
|
||||
};
|
||||
|
||||
const Table: FC<Props> = ({ data, sortable, children, emptyMessage }) => {
|
||||
const [tableData, setTableData] = useState(data);
|
||||
useEffect(() => {
|
||||
setTableData(data);
|
||||
}, [data]);
|
||||
const [ascending, setAscending] = useState(false);
|
||||
const [lastSortBy, setlastSortBy] = useState<number | undefined>();
|
||||
const [hoveredColumnIndex, setHoveredColumnIndex] = useState<number | undefined>();
|
||||
|
||||
const isSortable = (child: ReactElement) => {
|
||||
return sortable && child.props.createComparator;
|
||||
};
|
||||
|
||||
const sortFunctions: Comparator | undefined[] = [];
|
||||
React.Children.forEach(children, (child, index) => {
|
||||
if (child && isSortable(child)) {
|
||||
sortFunctions.push(child.props.createComparator(child.props, index));
|
||||
} else {
|
||||
sortFunctions.push(undefined);
|
||||
}
|
||||
});
|
||||
|
||||
const mapDataToColumns = (row: any) => {
|
||||
return (
|
||||
<tr>
|
||||
{React.Children.map(children, (child, columnIndex) => {
|
||||
return <td>{React.cloneElement(child, { ...child.props, columnIndex, row })}</td>;
|
||||
})}
|
||||
</tr>
|
||||
);
|
||||
};
|
||||
|
||||
const sortDescending = (sortAscending: (a: any, b: any) => number) => {
|
||||
return (a: any, b: any) => {
|
||||
return sortAscending(a, b) * -1;
|
||||
};
|
||||
};
|
||||
|
||||
const tableSort = (index: number) => {
|
||||
const sortFn = sortFunctions[index];
|
||||
if (!sortFn) {
|
||||
throw new Error(`column with index ${index} is not sortable`);
|
||||
}
|
||||
const sortableData = [...tableData];
|
||||
let sortOrder = ascending;
|
||||
if (lastSortBy !== index) {
|
||||
setAscending(true);
|
||||
sortOrder = true;
|
||||
}
|
||||
const sortFunction = sortOrder ? sortFn : sortDescending(sortFn);
|
||||
sortableData.sort(sortFunction);
|
||||
setTableData(sortableData);
|
||||
setAscending(!sortOrder);
|
||||
setlastSortBy(index);
|
||||
};
|
||||
|
||||
const shouldShowIcon = (index: number) => {
|
||||
return index === lastSortBy || index === hoveredColumnIndex;
|
||||
};
|
||||
|
||||
if (!tableData || tableData.length <= 0) {
|
||||
if (emptyMessage) {
|
||||
return <Notification type="info">{emptyMessage}</Notification>;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<StyledTable>
|
||||
<thead>
|
||||
<tr>
|
||||
{React.Children.map(children, (child, index) => (
|
||||
<th
|
||||
className={isSortable(child) && "has-cursor-pointer"}
|
||||
onClick={isSortable(child) ? () => tableSort(index) : undefined}
|
||||
onMouseEnter={() => setHoveredColumnIndex(index)}
|
||||
onMouseLeave={() => setHoveredColumnIndex(undefined)}
|
||||
>
|
||||
{child.props.header}
|
||||
{isSortable(child) && renderSortIcon(child, ascending, shouldShowIcon(index))}
|
||||
</th>
|
||||
))}
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>{tableData.map(mapDataToColumns)}</tbody>
|
||||
</StyledTable>
|
||||
);
|
||||
};
|
||||
|
||||
Table.defaultProps = {
|
||||
sortable: true
|
||||
};
|
||||
|
||||
const renderSortIcon = (child: ReactElement, ascending: boolean, showIcon: boolean) => {
|
||||
if (child.props.ascendingIcon && child.props.descendingIcon) {
|
||||
return <SortIcon name={ascending ? child.props.ascendingIcon : child.props.descendingIcon} isVisible={showIcon} />;
|
||||
} else {
|
||||
return <SortIcon name={ascending ? "sort-amount-down-alt" : "sort-amount-down"} isVisible={showIcon} />;
|
||||
}
|
||||
};
|
||||
|
||||
export default Table;
|
||||
28
scm-ui/ui-components/src/table/TextColumn.tsx
Normal file
28
scm-ui/ui-components/src/table/TextColumn.tsx
Normal file
@@ -0,0 +1,28 @@
|
||||
import React, { FC } from "react";
|
||||
import { ColumnProps } from "./types";
|
||||
|
||||
type Props = ColumnProps & {
|
||||
dataKey: string;
|
||||
};
|
||||
|
||||
const TextColumn: FC<Props> = ({ row, dataKey }) => {
|
||||
return row[dataKey];
|
||||
};
|
||||
|
||||
TextColumn.defaultProps = {
|
||||
createComparator: (props: Props) => {
|
||||
return (a: any, b: any) => {
|
||||
if (a[props.dataKey] < b[props.dataKey]) {
|
||||
return -1;
|
||||
} else if (a[props.dataKey] > b[props.dataKey]) {
|
||||
return 1;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
},
|
||||
ascendingIcon: "sort-alpha-down-alt",
|
||||
descendingIcon: "sort-alpha-down"
|
||||
};
|
||||
|
||||
export default TextColumn;
|
||||
4
scm-ui/ui-components/src/table/index.ts
Normal file
4
scm-ui/ui-components/src/table/index.ts
Normal file
@@ -0,0 +1,4 @@
|
||||
export { default as Table } from "./Table";
|
||||
export { default as Column } from "./Column";
|
||||
export { default as TextColumn } from "./TextColumn";
|
||||
export { default as SortIcon } from "./SortIcon";
|
||||
12
scm-ui/ui-components/src/table/types.ts
Normal file
12
scm-ui/ui-components/src/table/types.ts
Normal file
@@ -0,0 +1,12 @@
|
||||
import { ReactNode } from "react";
|
||||
|
||||
export type Comparator = (a: any, b: any) => number;
|
||||
|
||||
export type ColumnProps = {
|
||||
header: ReactNode;
|
||||
row?: any;
|
||||
columnIndex?: number;
|
||||
createComparator?: (props: any, columnIndex: number) => Comparator;
|
||||
ascendingIcon?: string;
|
||||
descendingIcon?: string;
|
||||
};
|
||||
@@ -1,7 +1,7 @@
|
||||
#!/usr/bin/env node
|
||||
const { spawnSync } = require("child_process");
|
||||
|
||||
const commands = ["plugin", "plugin-watch", "publish"];
|
||||
const commands = ["plugin", "plugin-watch", "publish", "version"];
|
||||
|
||||
const args = process.argv.slice(2);
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ const versions = require("../versions");
|
||||
const args = process.argv.slice(2);
|
||||
|
||||
if (args.length < 1) {
|
||||
console.log("usage ui-scripts publish version");
|
||||
console.log("usage ui-scripts publish <version>");
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
|
||||
12
scm-ui/ui-scripts/src/commands/version.js
Normal file
12
scm-ui/ui-scripts/src/commands/version.js
Normal file
@@ -0,0 +1,12 @@
|
||||
const lerna = require("../lerna");
|
||||
|
||||
const args = process.argv.slice(2);
|
||||
|
||||
if (args.length < 1) {
|
||||
console.log("usage ui-scripts version <new-version>");
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const version = args[0];
|
||||
|
||||
lerna.version(version);
|
||||
@@ -622,6 +622,10 @@ form .field:not(.is-grouped) {
|
||||
}
|
||||
}
|
||||
|
||||
.help {
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
// label with help-icon compensation
|
||||
.label-icon-spacing {
|
||||
margin-top: 30px;
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
"settingsNavLink": "Einstellungen",
|
||||
"generalNavLink": "Generell"
|
||||
},
|
||||
"info": {
|
||||
"info": {
|
||||
"currentAppVersion": "Aktuelle Software-Versionsnummer",
|
||||
"communityTitle": "Community Support",
|
||||
"communityIconAlt": "Community Support Icon",
|
||||
@@ -91,8 +91,7 @@
|
||||
"submit": "Speichern"
|
||||
},
|
||||
"delete": {
|
||||
"button": "Löschen",
|
||||
"subtitle": "Berechtigungsrolle löschen",
|
||||
"button": "Berechtigungsrolle löschen",
|
||||
"confirmAlert": {
|
||||
"title": "Berechtigungsrolle löschen?",
|
||||
"message": "Wollen Sie diese Rolle wirklich löschen? Alle Benutzer mit dieser Rolle verlieren die entsprechenden Berechtigungen.",
|
||||
|
||||
@@ -60,8 +60,7 @@
|
||||
}
|
||||
},
|
||||
"deleteGroup": {
|
||||
"subtitle": "Gruppe löschen",
|
||||
"button": "Löschen",
|
||||
"button": "Gruppe löschen",
|
||||
"confirmAlert": {
|
||||
"title": "Gruppe löschen",
|
||||
"message": "Soll die Gruppe wirklich gelöscht werden?",
|
||||
|
||||
@@ -114,7 +114,7 @@
|
||||
"size": "Größe"
|
||||
},
|
||||
"noSources": "Keine Sources in diesem Branch gefunden.",
|
||||
"extension" : {
|
||||
"extension": {
|
||||
"notBound": "Keine Erweiterung angebunden."
|
||||
}
|
||||
},
|
||||
@@ -166,8 +166,7 @@
|
||||
}
|
||||
},
|
||||
"deleteRepo": {
|
||||
"subtitle": "Repository löschen",
|
||||
"button": "Löschen",
|
||||
"button": "Repository löschen",
|
||||
"confirmAlert": {
|
||||
"title": "Repository löschen",
|
||||
"message": "Soll das Repository wirklich gelöscht werden?",
|
||||
|
||||
@@ -45,8 +45,7 @@
|
||||
"subtitle": "Erstellen eines neuen Benutzers"
|
||||
},
|
||||
"deleteUser": {
|
||||
"subtitle": "Benutzer löschen",
|
||||
"button": "Löschen",
|
||||
"button": "Benutzer löschen",
|
||||
"confirmAlert": {
|
||||
"title": "Benutzer löschen",
|
||||
"message": "Soll der Benutzer wirklich gelöscht werden?",
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
"communityTitle": "Community Support",
|
||||
"communityIconAlt": "Community Support Icon",
|
||||
"communityInfo": "Contact the SCM-Manager support team for questions about SCM-Manager, to report bugs or to request features through the official channels.",
|
||||
"communityButton": "Contact our team",
|
||||
"communityButton": "Contact our Team",
|
||||
"enterpriseTitle": "Enterprise Support",
|
||||
"enterpriseIconAlt": "Enterprise Support Icon",
|
||||
"enterpriseInfo": "You require support with the integration of SCM-Manager into your processes, with the customization of the tool or simply a service level agreement (SLA)?",
|
||||
@@ -76,8 +76,8 @@
|
||||
"createSubtitle": "Create Permission Role",
|
||||
"editSubtitle": "Edit Permission Role",
|
||||
"overview": {
|
||||
"title": "Overview of all permission roles",
|
||||
"noPermissionRoles": "No permission roles found.",
|
||||
"title": "Overview of all Permission Roles",
|
||||
"noPermissionRoles": "No Permission Roles found.",
|
||||
"createButton": "Create Permission Role"
|
||||
},
|
||||
"editButton": "Edit",
|
||||
@@ -91,10 +91,9 @@
|
||||
"submit": "Save"
|
||||
},
|
||||
"delete": {
|
||||
"button": "Delete",
|
||||
"subtitle": "Delete permission role",
|
||||
"button": "Delete Permission Role",
|
||||
"confirmAlert": {
|
||||
"title": "Delete permission role",
|
||||
"title": "Delete Permission Role",
|
||||
"message": "Do you really want to delete this permission role? All users will lose their corresponding permissions.",
|
||||
"submit": "Yes",
|
||||
"cancel": "No"
|
||||
|
||||
@@ -60,8 +60,7 @@
|
||||
}
|
||||
},
|
||||
"deleteGroup": {
|
||||
"subtitle": "Delete Group",
|
||||
"button": "Delete",
|
||||
"button": "Delete Group",
|
||||
"confirmAlert": {
|
||||
"title": "Delete Group",
|
||||
"message": "Do you really want to delete the group?",
|
||||
|
||||
@@ -114,7 +114,7 @@
|
||||
"size": "Size"
|
||||
},
|
||||
"noSources": "No sources found for this branch.",
|
||||
"extension" : {
|
||||
"extension": {
|
||||
"notBound": "No extension bound."
|
||||
}
|
||||
},
|
||||
@@ -166,8 +166,7 @@
|
||||
}
|
||||
},
|
||||
"deleteRepo": {
|
||||
"subtitle": "Delete Repository",
|
||||
"button": "Delete",
|
||||
"button": "Delete Repository",
|
||||
"confirmAlert": {
|
||||
"title": "Delete repository",
|
||||
"message": "Do you really want to delete the repository?",
|
||||
@@ -178,7 +177,7 @@
|
||||
"diff": {
|
||||
"changes": {
|
||||
"add": "added",
|
||||
"delete": "deleted",
|
||||
"delete": "deleted",
|
||||
"modify": "modified",
|
||||
"rename": "renamed",
|
||||
"copy": "copied"
|
||||
|
||||
@@ -45,17 +45,16 @@
|
||||
"subtitle": "Create a new user"
|
||||
},
|
||||
"deleteUser": {
|
||||
"subtitle": "Delete User",
|
||||
"button": "Delete",
|
||||
"button": "Delete User",
|
||||
"confirmAlert": {
|
||||
"title": "Delete user",
|
||||
"title": "Delete User",
|
||||
"message": "Do you really want to delete the user?",
|
||||
"submit": "Yes",
|
||||
"cancel": "No"
|
||||
}
|
||||
},
|
||||
"singleUserPassword": {
|
||||
"button": "Set password",
|
||||
"button": "Set Password",
|
||||
"setPasswordSuccessful": "Password successfully set"
|
||||
},
|
||||
"userForm": {
|
||||
|
||||
@@ -86,8 +86,7 @@
|
||||
"submit": "Guardar"
|
||||
},
|
||||
"delete": {
|
||||
"button": "Borrar",
|
||||
"subtitle": "Eliminar el rol",
|
||||
"button": "Eliminar el rol",
|
||||
"confirmAlert": {
|
||||
"title": "Eliminar el rol",
|
||||
"message": "¿Realmente desea borrar el rol? Todos los usuarios de este rol perderń sus permisos.",
|
||||
|
||||
@@ -60,8 +60,7 @@
|
||||
}
|
||||
},
|
||||
"deleteGroup": {
|
||||
"subtitle": "Borrar grupo",
|
||||
"button": "Borrar",
|
||||
"button": "Borrar grupo",
|
||||
"confirmAlert": {
|
||||
"title": "Borrar grupo",
|
||||
"message": "¿Realmente desea borrar el grupo?",
|
||||
|
||||
@@ -114,7 +114,7 @@
|
||||
"size": "tamaño"
|
||||
},
|
||||
"noSources": "No se han encontrado fuentes para esta rama.",
|
||||
"extension" : {
|
||||
"extension": {
|
||||
"notBound": "Sin extensión conectada."
|
||||
}
|
||||
},
|
||||
@@ -166,8 +166,7 @@
|
||||
}
|
||||
},
|
||||
"deleteRepo": {
|
||||
"subtitle": "Borrar repositorio",
|
||||
"button": "Borrar",
|
||||
"button": "Borrar repositorio",
|
||||
"confirmAlert": {
|
||||
"title": "Borrar repositorio",
|
||||
"message": "¿Realmente desea borrar el repositorio?",
|
||||
@@ -178,7 +177,7 @@
|
||||
"diff": {
|
||||
"changes": {
|
||||
"add": "añadido",
|
||||
"delete": "borrado",
|
||||
"delete": "borrado",
|
||||
"modify": "modificado",
|
||||
"rename": "renombrado",
|
||||
"copy": "copiado"
|
||||
|
||||
@@ -45,8 +45,7 @@
|
||||
"subtitle": "Crear un nuevo usuario"
|
||||
},
|
||||
"deleteUser": {
|
||||
"subtitle": "Borrar usuario",
|
||||
"button": "Borrar",
|
||||
"button": "Borrar usuario",
|
||||
"confirmAlert": {
|
||||
"title": "Borrar usuario",
|
||||
"message": "¿Realmente desea borrar el usuario?",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import React from "react";
|
||||
import { WithTranslation, withTranslation } from "react-i18next";
|
||||
import { NamespaceStrategies, Config } from "@scm-manager/ui-types";
|
||||
import { SubmitButton, Notification } from "@scm-manager/ui-components";
|
||||
import { Level, SubmitButton, Notification } from "@scm-manager/ui-components";
|
||||
import ProxySettings from "./ProxySettings";
|
||||
import GeneralSettings from "./GeneralSettings";
|
||||
import BaseUrlSettings from "./BaseUrlSettings";
|
||||
@@ -151,10 +151,14 @@ class ConfigForm extends React.Component<Props, State> {
|
||||
hasUpdatePermission={configUpdatePermission}
|
||||
/>
|
||||
<hr />
|
||||
<SubmitButton
|
||||
loading={loading}
|
||||
label={t("config.form.submit")}
|
||||
disabled={!configUpdatePermission || this.hasError() || !this.state.changed}
|
||||
<Level
|
||||
right={
|
||||
<SubmitButton
|
||||
loading={loading}
|
||||
label={t("config.form.submit")}
|
||||
disabled={!configUpdatePermission || this.hasError() || !this.state.changed}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
</form>
|
||||
);
|
||||
|
||||
@@ -13,15 +13,13 @@ class AvailableVerbs extends React.Component<Props> {
|
||||
let verbs = null;
|
||||
if (role.verbs.length > 0) {
|
||||
verbs = (
|
||||
<tr>
|
||||
<td className="is-paddingless">
|
||||
<ul>
|
||||
{role.verbs.map(verb => {
|
||||
return <li>{t("verbs.repository." + verb + ".displayName")}</li>;
|
||||
})}
|
||||
</ul>
|
||||
</td>
|
||||
</tr>
|
||||
<td className="is-paddingless">
|
||||
<ul>
|
||||
{role.verbs.map((verb, key) => {
|
||||
return <li key={key}>{t("verbs.repository." + verb + ".displayName")}</li>;
|
||||
})}
|
||||
</ul>
|
||||
</td>
|
||||
);
|
||||
}
|
||||
return verbs;
|
||||
|
||||
@@ -2,7 +2,7 @@ import React from "react";
|
||||
import { WithTranslation, withTranslation } from "react-i18next";
|
||||
import { ExtensionPoint } from "@scm-manager/ui-extensions";
|
||||
import { RepositoryRole } from "@scm-manager/ui-types";
|
||||
import { Button } from "@scm-manager/ui-components";
|
||||
import { Level, Button } from "@scm-manager/ui-components";
|
||||
import PermissionRoleDetailsTable from "./PermissionRoleDetailsTable";
|
||||
|
||||
type Props = WithTranslation & {
|
||||
@@ -14,7 +14,12 @@ class PermissionRoleDetails extends React.Component<Props> {
|
||||
renderEditButton() {
|
||||
const { t, url } = this.props;
|
||||
if (!!this.props.role._links.update) {
|
||||
return <Button label={t("repositoryRole.editButton")} link={`${url}/edit`} color="primary" />;
|
||||
return (
|
||||
<>
|
||||
<hr />
|
||||
<Level right={<Button label={t("repositoryRole.editButton")} link={`${url}/edit`} color="primary" />} />
|
||||
</>
|
||||
);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
@@ -25,7 +30,6 @@ class PermissionRoleDetails extends React.Component<Props> {
|
||||
return (
|
||||
<>
|
||||
<PermissionRoleDetailsTable role={role} />
|
||||
<hr />
|
||||
{this.renderEditButton()}
|
||||
<ExtensionPoint
|
||||
name="repositoryRole.role-details.information"
|
||||
|
||||
@@ -4,7 +4,7 @@ import { withRouter } from "react-router-dom";
|
||||
import { WithTranslation, withTranslation } from "react-i18next";
|
||||
import { History } from "history";
|
||||
import { RepositoryRole } from "@scm-manager/ui-types";
|
||||
import { Subtitle, DeleteButton, confirmAlert, ErrorNotification } from "@scm-manager/ui-components";
|
||||
import { Level, DeleteButton, confirmAlert, ErrorNotification } from "@scm-manager/ui-components";
|
||||
import { deleteRole, getDeleteRoleFailure, isDeleteRolePending } from "../modules/roles";
|
||||
|
||||
type Props = WithTranslation & {
|
||||
@@ -64,13 +64,9 @@ class DeleteRepositoryRole extends React.Component<Props> {
|
||||
|
||||
return (
|
||||
<>
|
||||
<Subtitle subtitle={t("repositoryRole.delete.subtitle")} />
|
||||
<div className="columns">
|
||||
<div className="column">
|
||||
<ErrorNotification error={error} />
|
||||
<DeleteButton label={t("repositoryRole.delete.button")} action={action} loading={loading} />
|
||||
</div>
|
||||
</div>
|
||||
<hr />
|
||||
<ErrorNotification error={error} />
|
||||
<Level right={<DeleteButton label={t("repositoryRole.delete.button")} action={action} loading={loading} />} />
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -44,7 +44,6 @@ class EditRepositoryRole extends React.Component<Props> {
|
||||
<>
|
||||
<Subtitle subtitle={t("repositoryRole.editSubtitle")} />
|
||||
<RepositoryRoleForm role={this.props.role} submitForm={role => this.updateRepositoryRole(role)} />
|
||||
<hr />
|
||||
<DeleteRepositoryRole role={this.props.role} />
|
||||
</>
|
||||
);
|
||||
|
||||
@@ -2,10 +2,10 @@ import React from "react";
|
||||
import { connect } from "react-redux";
|
||||
import { WithTranslation, withTranslation } from "react-i18next";
|
||||
import { RepositoryRole } from "@scm-manager/ui-types";
|
||||
import { InputField, SubmitButton } from "@scm-manager/ui-components";
|
||||
import { InputField, Level, SubmitButton } from "@scm-manager/ui-components";
|
||||
import { getRepositoryRolesLink, getRepositoryVerbsLink } from "../../../modules/indexResource";
|
||||
import { fetchAvailableVerbs, getFetchVerbsFailure, getVerbsFromState, isFetchVerbsPending } from "../modules/roles";
|
||||
import PermissionCheckbox from "../../../repos/permissions/components/PermissionCheckbox";
|
||||
import PermissionsWrapper from "../../../permissions/components/PermissionsWrapper";
|
||||
|
||||
type Props = WithTranslation & {
|
||||
role?: RepositoryRole;
|
||||
@@ -89,16 +89,9 @@ class RepositoryRoleForm extends React.Component<Props, State> {
|
||||
const { loading, availableVerbs, t } = this.props;
|
||||
const { role } = this.state;
|
||||
|
||||
const verbSelectBoxes = !availableVerbs
|
||||
? null
|
||||
: availableVerbs.map(verb => (
|
||||
<PermissionCheckbox
|
||||
key={verb}
|
||||
name={verb}
|
||||
checked={role.verbs.includes(verb)}
|
||||
onChange={this.handleVerbChange}
|
||||
/>
|
||||
));
|
||||
const selectedVerbs = availableVerbs
|
||||
? availableVerbs.reduce((obj, verb) => ({ ...obj, [verb]: role.verbs.includes(verb) }), {})
|
||||
: {};
|
||||
|
||||
return (
|
||||
<form onSubmit={this.submit}>
|
||||
@@ -111,16 +104,23 @@ class RepositoryRoleForm extends React.Component<Props, State> {
|
||||
/>
|
||||
<div className="field">
|
||||
<label className="label">{t("repositoryRole.form.permissions")}</label>
|
||||
{verbSelectBoxes}
|
||||
<PermissionsWrapper
|
||||
permissions={selectedVerbs}
|
||||
onChange={this.handleVerbChange}
|
||||
disabled={false}
|
||||
role={true}
|
||||
/>
|
||||
</div>
|
||||
<hr />
|
||||
<SubmitButton loading={loading} label={t("repositoryRole.form.submit")} disabled={!this.isValid()} />
|
||||
<Level
|
||||
right={<SubmitButton loading={loading} label={t("repositoryRole.form.submit")} disabled={!this.isValid()} />}
|
||||
/>
|
||||
</form>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const mapStateToProps = (state) => {
|
||||
const mapStateToProps = state => {
|
||||
const loading = isFetchVerbsPending(state);
|
||||
const error = getFetchVerbsFailure(state);
|
||||
const verbsLink = getRepositoryVerbsLink(state);
|
||||
|
||||
@@ -4,6 +4,7 @@ import {
|
||||
InputField,
|
||||
Notification,
|
||||
PasswordConfirmation,
|
||||
Level,
|
||||
SubmitButton
|
||||
} from "@scm-manager/ui-components";
|
||||
import { WithTranslation, withTranslation } from "react-i18next";
|
||||
@@ -124,11 +125,7 @@ class ChangeUserPassword extends React.Component<Props, State> {
|
||||
passwordChanged={this.passwordChanged}
|
||||
key={this.state.passwordChanged ? "changed" : "unchanged"}
|
||||
/>
|
||||
<div className="columns">
|
||||
<div className="column">
|
||||
<SubmitButton disabled={!this.isValid()} loading={loading} label={t("password.submit")} />
|
||||
</div>
|
||||
</div>
|
||||
<Level right={<SubmitButton disabled={!this.isValid()} loading={loading} label={t("password.submit")} />} />
|
||||
</form>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -8,9 +8,9 @@ import {
|
||||
InputField,
|
||||
SubmitButton,
|
||||
Textarea,
|
||||
Level,
|
||||
Checkbox
|
||||
} from "@scm-manager/ui-components";
|
||||
|
||||
import * as validator from "./groupValidation";
|
||||
|
||||
type Props = WithTranslation & {
|
||||
@@ -154,7 +154,7 @@ class GroupForm extends React.Component<Props, State> {
|
||||
/>
|
||||
{this.renderExternalField(group)}
|
||||
{this.renderMemberfields(group)}
|
||||
<SubmitButton disabled={!this.isValid()} label={t("groupForm.submit")} loading={loading} />
|
||||
<Level right={<SubmitButton disabled={!this.isValid()} label={t("groupForm.submit")} loading={loading} />} />
|
||||
</form>
|
||||
</>
|
||||
);
|
||||
|
||||
@@ -17,9 +17,9 @@ class GroupRow extends React.Component<Props> {
|
||||
const { group, t } = this.props;
|
||||
const to = `/group/${group.name}`;
|
||||
const iconType = group.external ? (
|
||||
<Icon title={t("group.external")} name="sign-out-alt fa-rotate-270" />
|
||||
<Icon title={t("group.external")} name="globe-americas" />
|
||||
) : (
|
||||
<Icon title={t("group.internal")} name="sign-in-alt fa-rotate-90" />
|
||||
<Icon title={t("group.internal")} name="home" />
|
||||
);
|
||||
|
||||
return (
|
||||
|
||||
@@ -4,7 +4,7 @@ import { withRouter } from "react-router-dom";
|
||||
import { WithTranslation, withTranslation } from "react-i18next";
|
||||
import { History } from "history";
|
||||
import { Group } from "@scm-manager/ui-types";
|
||||
import { Subtitle, DeleteButton, confirmAlert, ErrorNotification } from "@scm-manager/ui-components";
|
||||
import { Level, DeleteButton, confirmAlert, ErrorNotification } from "@scm-manager/ui-components";
|
||||
import { deleteGroup, getDeleteGroupFailure, isDeleteGroupPending } from "../modules/groups";
|
||||
|
||||
type Props = WithTranslation & {
|
||||
@@ -64,13 +64,9 @@ export class DeleteGroup extends React.Component<Props> {
|
||||
|
||||
return (
|
||||
<>
|
||||
<Subtitle subtitle={t("deleteGroup.subtitle")} />
|
||||
<hr />
|
||||
<ErrorNotification error={error} />
|
||||
<div className="columns">
|
||||
<div className="column">
|
||||
<DeleteButton label={t("deleteGroup.button")} action={action} loading={loading} />
|
||||
</div>
|
||||
</div>
|
||||
<Level right={<DeleteButton label={t("deleteGroup.button")} action={action} loading={loading} />} />
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -63,7 +63,6 @@ class EditGroup extends React.Component<Props> {
|
||||
loading={loading}
|
||||
loadUserSuggestions={this.loadUserAutocompletion}
|
||||
/>
|
||||
<hr />
|
||||
<DeleteGroup group={group} />
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -3,24 +3,34 @@ import { WithTranslation, withTranslation } from "react-i18next";
|
||||
import { Checkbox } from "@scm-manager/ui-components";
|
||||
|
||||
type Props = WithTranslation & {
|
||||
permission: string;
|
||||
name: string;
|
||||
checked: boolean;
|
||||
onChange: (value: boolean, name: string) => void;
|
||||
onChange?: (value: boolean, name?: string) => void;
|
||||
disabled: boolean;
|
||||
role?: boolean;
|
||||
};
|
||||
|
||||
class PermissionCheckbox extends React.Component<Props> {
|
||||
render() {
|
||||
const { t, permission, checked, onChange, disabled } = this.props;
|
||||
const key = permission.split(":").join(".");
|
||||
const { name, checked, onChange, disabled, role, t } = this.props;
|
||||
|
||||
const key = name.split(":").join(".");
|
||||
const label = role
|
||||
? t("verbs.repository." + name + ".displayName")
|
||||
: this.translateOrDefault("permissions." + key + ".displayName", key);
|
||||
const helpText = role
|
||||
? t("verbs.repository." + name + ".description")
|
||||
: this.translateOrDefault("permissions." + key + ".description", t("permissions.unknown"));
|
||||
|
||||
return (
|
||||
<Checkbox
|
||||
name={permission}
|
||||
label={this.translateOrDefault("permissions." + key + ".displayName", key)}
|
||||
key={name}
|
||||
name={name}
|
||||
label={label}
|
||||
helpText={helpText}
|
||||
checked={checked}
|
||||
onChange={onChange}
|
||||
disabled={disabled}
|
||||
helpText={this.translateOrDefault("permissions." + key + ".description", t("permissions.unknown"))}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
import React from "react";
|
||||
import classNames from "classnames";
|
||||
import styled from "styled-components";
|
||||
import PermissionCheckbox from "./PermissionCheckbox";
|
||||
import { Loading } from "@scm-manager/ui-components";
|
||||
|
||||
type Props = {
|
||||
permissions: {
|
||||
[key: string]: boolean;
|
||||
};
|
||||
onChange: (value: boolean, name: string) => void;
|
||||
disabled: boolean;
|
||||
role?: boolean;
|
||||
};
|
||||
|
||||
const StyledWrapper = styled.div`
|
||||
padding-bottom: 0;
|
||||
|
||||
& .field .control {
|
||||
width: 100%;
|
||||
word-wrap: break-word;
|
||||
}
|
||||
`;
|
||||
|
||||
export default class PermissionsWrapper extends React.Component<Props> {
|
||||
render() {
|
||||
const { permissions, onChange, disabled, role } = this.props;
|
||||
|
||||
if (!permissions) {
|
||||
return <Loading />;
|
||||
}
|
||||
|
||||
const permissionArray = Object.keys(permissions);
|
||||
return (
|
||||
<div className="columns">
|
||||
<StyledWrapper className={classNames("column", "is-half")}>
|
||||
{permissionArray.slice(0, permissionArray.length / 2 + 1).map(p => (
|
||||
<PermissionCheckbox
|
||||
key={p}
|
||||
name={p}
|
||||
checked={permissions[p]}
|
||||
onChange={onChange}
|
||||
disabled={disabled}
|
||||
role={role}
|
||||
/>
|
||||
))}
|
||||
</StyledWrapper>
|
||||
<StyledWrapper className={classNames("column", "is-half")}>
|
||||
{permissionArray.slice(permissionArray.length / 2 + 1, permissionArray.length).map(p => (
|
||||
<PermissionCheckbox
|
||||
key={p}
|
||||
name={p}
|
||||
checked={permissions[p]}
|
||||
onChange={onChange}
|
||||
disabled={disabled}
|
||||
role={role}
|
||||
/>
|
||||
))}
|
||||
</StyledWrapper>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,13 +1,11 @@
|
||||
import React from "react";
|
||||
import { connect } from "react-redux";
|
||||
import { WithTranslation, withTranslation } from "react-i18next";
|
||||
import classNames from "classnames";
|
||||
import styled from "styled-components";
|
||||
import { Link } from "@scm-manager/ui-types";
|
||||
import { Notification, ErrorNotification, SubmitButton } from "@scm-manager/ui-components";
|
||||
import { Notification, ErrorNotification, SubmitButton, Level } from "@scm-manager/ui-components";
|
||||
import { getLink } from "../../modules/indexResource";
|
||||
import { loadPermissionsForEntity, setPermissions } from "./handlePermissions";
|
||||
import PermissionCheckbox from "./PermissionCheckbox";
|
||||
import PermissionsWrapper from "./PermissionsWrapper";
|
||||
|
||||
type Props = WithTranslation & {
|
||||
availablePermissionLink: string;
|
||||
@@ -25,15 +23,6 @@ type State = {
|
||||
overwritePermissionsLink?: Link;
|
||||
};
|
||||
|
||||
const PermissionsWrapper = styled.div`
|
||||
padding-bottom: 0;
|
||||
|
||||
& .field .control {
|
||||
width: 100%;
|
||||
word-wrap: break-word;
|
||||
}
|
||||
`;
|
||||
|
||||
class SetPermissions extends React.Component<Props, State> {
|
||||
constructor(props: Props) {
|
||||
super(props);
|
||||
@@ -43,7 +32,6 @@ class SetPermissions extends React.Component<Props, State> {
|
||||
loading: true,
|
||||
permissionsChanged: false,
|
||||
permissionsSubmitted: false,
|
||||
modifiable: false,
|
||||
overwritePermissionsLink: undefined
|
||||
};
|
||||
}
|
||||
@@ -125,39 +113,23 @@ class SetPermissions extends React.Component<Props, State> {
|
||||
<form onSubmit={this.submit}>
|
||||
{message}
|
||||
{this.renderPermissions()}
|
||||
<SubmitButton disabled={!this.state.permissionsChanged} loading={loading} label={t("setPermissions.button")} />
|
||||
<Level
|
||||
right={
|
||||
<SubmitButton
|
||||
disabled={!this.state.permissionsChanged}
|
||||
loading={loading}
|
||||
label={t("setPermissions.button")}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
</form>
|
||||
);
|
||||
}
|
||||
|
||||
renderPermissions = () => {
|
||||
const { overwritePermissionsLink, permissions } = this.state;
|
||||
const permissionArray = Object.keys(permissions);
|
||||
return (
|
||||
<div className="columns">
|
||||
<PermissionsWrapper className={classNames("column", "is-half")}>
|
||||
{permissionArray.slice(0, permissionArray.length / 2 + 1).map(p => (
|
||||
<PermissionCheckbox
|
||||
key={p}
|
||||
permission={p}
|
||||
checked={permissions[p]}
|
||||
onChange={this.valueChanged}
|
||||
disabled={!overwritePermissionsLink}
|
||||
/>
|
||||
))}
|
||||
</PermissionsWrapper>
|
||||
<PermissionsWrapper className={classNames("column", "is-half")}>
|
||||
{permissionArray.slice(permissionArray.length / 2 + 1, permissionArray.length).map(p => (
|
||||
<PermissionCheckbox
|
||||
key={p}
|
||||
permission={p}
|
||||
checked={permissions[p]}
|
||||
onChange={this.valueChanged}
|
||||
disabled={!overwritePermissionsLink}
|
||||
/>
|
||||
))}
|
||||
</PermissionsWrapper>
|
||||
</div>
|
||||
<PermissionsWrapper permissions={permissions} onChange={this.valueChanged} disabled={!overwritePermissionsLink} />
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@ import React from "react";
|
||||
import { WithTranslation, withTranslation } from "react-i18next";
|
||||
import { ExtensionPoint } from "@scm-manager/ui-extensions";
|
||||
import { Repository, RepositoryType } from "@scm-manager/ui-types";
|
||||
import { Subtitle, InputField, Select, SubmitButton, Textarea } from "@scm-manager/ui-components";
|
||||
import { Subtitle, InputField, Select, Textarea, Level, SubmitButton } from "@scm-manager/ui-components";
|
||||
import * as validator from "./repositoryValidation";
|
||||
|
||||
type Props = WithTranslation & {
|
||||
@@ -52,9 +52,8 @@ class RepositoryForm extends React.Component<Props, State> {
|
||||
}
|
||||
}
|
||||
|
||||
isFalsy(value) {
|
||||
isFalsy(value: string) {
|
||||
return !value;
|
||||
|
||||
}
|
||||
|
||||
isValid = () => {
|
||||
@@ -91,7 +90,7 @@ class RepositoryForm extends React.Component<Props, State> {
|
||||
const disabled = !this.isModifiable() && !this.isCreateMode();
|
||||
|
||||
const submitButton = disabled ? null : (
|
||||
<SubmitButton disabled={!this.isValid()} loading={loading} label={t("repositoryForm.submit")} />
|
||||
<Level right={<SubmitButton disabled={!this.isValid()} loading={loading} label={t("repositoryForm.submit")} />} />
|
||||
);
|
||||
|
||||
let subtitle = null;
|
||||
|
||||
@@ -4,7 +4,7 @@ import { withRouter } from "react-router-dom";
|
||||
import { WithTranslation, withTranslation } from "react-i18next";
|
||||
import { History } from "history";
|
||||
import { Repository } from "@scm-manager/ui-types";
|
||||
import { Subtitle, DeleteButton, confirmAlert, ErrorNotification } from "@scm-manager/ui-components";
|
||||
import { Level, DeleteButton, confirmAlert, ErrorNotification } from "@scm-manager/ui-components";
|
||||
import { deleteRepo, getDeleteRepoFailure, isDeleteRepoPending } from "../modules/repos";
|
||||
|
||||
type Props = WithTranslation & {
|
||||
@@ -65,13 +65,8 @@ class DeleteRepo extends React.Component<Props> {
|
||||
return (
|
||||
<>
|
||||
<hr />
|
||||
<Subtitle subtitle={t("deleteRepo.subtitle")} />
|
||||
<ErrorNotification error={error} />
|
||||
<div className="columns">
|
||||
<div className="column">
|
||||
<DeleteButton label={t("deleteRepo.button")} action={action} loading={loading} />
|
||||
</div>
|
||||
</div>
|
||||
<Level right={<DeleteButton label={t("deleteRepo.button")} action={action} loading={loading} />} />
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,29 +0,0 @@
|
||||
import React from "react";
|
||||
import { WithTranslation, withTranslation } from "react-i18next";
|
||||
import { Checkbox } from "@scm-manager/ui-components";
|
||||
|
||||
type Props = WithTranslation & {
|
||||
disabled: boolean;
|
||||
name: string;
|
||||
checked: boolean;
|
||||
onChange?: (value: boolean, name?: string) => void;
|
||||
};
|
||||
|
||||
class PermissionCheckbox extends React.Component<Props> {
|
||||
render() {
|
||||
const { t } = this.props;
|
||||
return (
|
||||
<Checkbox
|
||||
key={this.props.name}
|
||||
name={this.props.name}
|
||||
helpText={t("verbs.repository." + this.props.name + ".description")}
|
||||
label={t("verbs.repository." + this.props.name + ".displayName")}
|
||||
checked={this.props.checked}
|
||||
onChange={this.props.onChange}
|
||||
disabled={this.props.disabled}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default withTranslation("plugins")(PermissionCheckbox);
|
||||
@@ -1,7 +1,7 @@
|
||||
import React from "react";
|
||||
import { WithTranslation, withTranslation } from "react-i18next";
|
||||
import { ButtonGroup, Button, SubmitButton, Modal } from "@scm-manager/ui-components";
|
||||
import PermissionCheckbox from "../components/PermissionCheckbox";
|
||||
import PermissionCheckbox from "../../../permissions/components/PermissionCheckbox";
|
||||
|
||||
type Props = WithTranslation & {
|
||||
readOnly: boolean;
|
||||
@@ -33,7 +33,14 @@ class AdvancedPermissionsDialog extends React.Component<Props, State> {
|
||||
const { verbs } = this.state;
|
||||
|
||||
const verbSelectBoxes = Object.entries(verbs).map(e => (
|
||||
<PermissionCheckbox key={e[0]} disabled={readOnly} name={e[0]} checked={e[1]} onChange={this.handleChange} />
|
||||
<PermissionCheckbox
|
||||
key={e[0]}
|
||||
name={e[0]}
|
||||
checked={e[1]}
|
||||
onChange={this.handleChange}
|
||||
disabled={readOnly}
|
||||
role={true}
|
||||
/>
|
||||
));
|
||||
|
||||
const submitButton = !readOnly ? <SubmitButton label={t("permission.advanced.dialog.submit")} /> : null;
|
||||
|
||||
@@ -6,6 +6,7 @@ import {
|
||||
GroupAutocomplete,
|
||||
LabelWithHelpIcon,
|
||||
Radio,
|
||||
Level,
|
||||
SubmitButton,
|
||||
Subtitle,
|
||||
UserAutocomplete
|
||||
@@ -141,8 +142,8 @@ class CreatePermissionForm extends React.Component<Props, State> {
|
||||
</div>
|
||||
</div>
|
||||
<div className="columns">
|
||||
<div className="column is-three-fifths">{this.renderAutocompletionField()}</div>
|
||||
<div className="column is-two-fifths">
|
||||
<div className="column is-half">{this.renderAutocompletionField()}</div>
|
||||
<div className="column is-half">
|
||||
<div className="columns">
|
||||
<div className="column is-narrow">
|
||||
<RoleSelector
|
||||
@@ -163,15 +164,15 @@ class CreatePermissionForm extends React.Component<Props, State> {
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="columns">
|
||||
<div className="column">
|
||||
<Level
|
||||
right={
|
||||
<SubmitButton
|
||||
label={t("permission.add-permission.submit-button")}
|
||||
loading={loading}
|
||||
disabled={!this.state.valid || this.state.name === ""}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
/>
|
||||
</form>
|
||||
</>
|
||||
);
|
||||
|
||||
@@ -18,7 +18,7 @@ class FileButtonAddons extends React.Component<Props> {
|
||||
};
|
||||
|
||||
color = (selected: boolean) => {
|
||||
return selected ? "link is-selected" : null;
|
||||
return selected ? "link is-selected" : "";
|
||||
};
|
||||
|
||||
render() {
|
||||
@@ -27,14 +27,14 @@ class FileButtonAddons extends React.Component<Props> {
|
||||
return (
|
||||
<ButtonAddons className={className}>
|
||||
<div title={t("sources.content.sourcesButton")}>
|
||||
<Button action={this.showSources} className="reduced" color={this.color(!historyIsSelected)}>
|
||||
<Button action={this.showSources} color={this.color(!historyIsSelected)}>
|
||||
<span className="icon">
|
||||
<i className="fas fa-code" />
|
||||
</span>
|
||||
</Button>
|
||||
</div>
|
||||
<div title={t("sources.content.historyButton")}>
|
||||
<Button action={this.showHistory} className="reduced" color={this.color(historyIsSelected)}>
|
||||
<Button action={this.showHistory} color={this.color(historyIsSelected)}>
|
||||
<span className="icon">
|
||||
<i className="fas fa-history" />
|
||||
</span>
|
||||
|
||||
@@ -7,24 +7,25 @@ import { fetchSources, getFetchSourcesFailure, getSources, isFetchSourcesPending
|
||||
import { connect } from "react-redux";
|
||||
import { Loading, ErrorNotification } from "@scm-manager/ui-components";
|
||||
import Notification from "@scm-manager/ui-components/src/Notification";
|
||||
import {WithTranslation, withTranslation} from "react-i18next";
|
||||
import { WithTranslation, withTranslation } from "react-i18next";
|
||||
|
||||
type Props = WithTranslation & RouteComponentProps & {
|
||||
repository: Repository;
|
||||
type Props = WithTranslation &
|
||||
RouteComponentProps & {
|
||||
repository: Repository;
|
||||
|
||||
// url params
|
||||
extension: string;
|
||||
revision?: string;
|
||||
path?: string;
|
||||
// url params
|
||||
extension: string;
|
||||
revision?: string;
|
||||
path?: string;
|
||||
|
||||
// redux state
|
||||
loading: boolean;
|
||||
error?: Error | null;
|
||||
sources?: File | null;
|
||||
// redux state
|
||||
loading: boolean;
|
||||
error?: Error | null;
|
||||
sources?: File | null;
|
||||
|
||||
// dispatch props
|
||||
fetchSources: (repository: Repository, revision: string, path: string) => void;
|
||||
};
|
||||
// dispatch props
|
||||
fetchSources: (repository: Repository, revision: string, path: string) => void;
|
||||
};
|
||||
|
||||
const extensionPointName = "repos.sources.extensions";
|
||||
|
||||
@@ -32,7 +33,7 @@ class SourceExtensions extends React.Component<Props> {
|
||||
componentDidMount() {
|
||||
const { fetchSources, repository, revision, path } = this.props;
|
||||
// TODO get typing right
|
||||
fetchSources(repository,revision || "", path || "");
|
||||
fetchSources(repository, revision || "", path || "");
|
||||
}
|
||||
|
||||
render() {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import React from "react";
|
||||
import { WithTranslation, withTranslation } from "react-i18next";
|
||||
import { User } from "@scm-manager/ui-types";
|
||||
import { SubmitButton, Notification, ErrorNotification, PasswordConfirmation } from "@scm-manager/ui-components";
|
||||
import { Level, SubmitButton, Notification, ErrorNotification, PasswordConfirmation } from "@scm-manager/ui-components";
|
||||
import { setPassword } from "./setPassword";
|
||||
|
||||
type Props = WithTranslation & {
|
||||
@@ -98,15 +98,15 @@ class SetUserPassword extends React.Component<Props, State> {
|
||||
passwordChanged={this.passwordChanged}
|
||||
key={this.state.passwordChanged ? "changed" : "unchanged"}
|
||||
/>
|
||||
<div className="columns">
|
||||
<div className="column">
|
||||
<Level
|
||||
right={
|
||||
<SubmitButton
|
||||
disabled={!this.state.passwordValid}
|
||||
loading={loading}
|
||||
label={t("singleUserPassword.button")}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
/>
|
||||
</form>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ import {
|
||||
Checkbox,
|
||||
InputField,
|
||||
PasswordConfirmation,
|
||||
Level,
|
||||
SubmitButton,
|
||||
validation as validator
|
||||
} from "@scm-manager/ui-components";
|
||||
@@ -166,11 +167,7 @@ class UserForm extends React.Component<Props, State> {
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="columns">
|
||||
<div className="column">
|
||||
<SubmitButton disabled={!this.isValid()} loading={loading} label={t("userForm.button")} />
|
||||
</div>
|
||||
</div>
|
||||
<Level right={<SubmitButton disabled={!this.isValid()} loading={loading} label={t("userForm.button")} />} />
|
||||
</form>
|
||||
</>
|
||||
);
|
||||
|
||||
@@ -4,7 +4,7 @@ import { withRouter } from "react-router-dom";
|
||||
import { WithTranslation, withTranslation } from "react-i18next";
|
||||
import { History } from "history";
|
||||
import { User } from "@scm-manager/ui-types";
|
||||
import { Subtitle, DeleteButton, confirmAlert, ErrorNotification } from "@scm-manager/ui-components";
|
||||
import { Level, DeleteButton, confirmAlert, ErrorNotification } from "@scm-manager/ui-components";
|
||||
import { deleteUser, getDeleteUserFailure, isDeleteUserPending } from "../modules/users";
|
||||
|
||||
type Props = WithTranslation & {
|
||||
@@ -64,13 +64,9 @@ class DeleteUser extends React.Component<Props> {
|
||||
|
||||
return (
|
||||
<>
|
||||
<Subtitle subtitle={t("deleteUser.subtitle")} />
|
||||
<hr />
|
||||
<ErrorNotification error={error} />
|
||||
<div className="columns">
|
||||
<div className="column">
|
||||
<DeleteButton label={t("deleteUser.button")} action={action} loading={loading} />
|
||||
</div>
|
||||
</div>
|
||||
<Level right={<DeleteButton label={t("deleteUser.button")} action={action} loading={loading} />} />
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -41,7 +41,6 @@ class EditUser extends React.Component<Props> {
|
||||
<div>
|
||||
<ErrorNotification error={error} />
|
||||
<UserForm submitForm={user => this.modifyUser(user)} user={user} loading={loading} />
|
||||
<hr />
|
||||
<DeleteUser user={user} />
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -88,14 +88,16 @@ public class BootstrapContextListener extends GuiceServletContextListener {
|
||||
protected Injector getInjector() {
|
||||
Throwable startupError = SCMContext.getContext().getStartupError();
|
||||
if (startupError != null) {
|
||||
LOG.error("received unrecoverable error during startup", startupError);
|
||||
return createStageOneInjector(SingleView.error(startupError));
|
||||
} else if (Versions.isTooOld()) {
|
||||
LOG.error("Existing version is too old and cannot be migrated to new version. Please update to version {} first", Versions.MIN_VERSION);
|
||||
LOG.error("existing version is too old and cannot be migrated to new version. Please update to version {} first", Versions.MIN_VERSION);
|
||||
return createStageOneInjector(SingleView.view("/templates/too-old.mustache", HttpServletResponse.SC_CONFLICT));
|
||||
} else {
|
||||
try {
|
||||
return createStageTwoInjector();
|
||||
} catch (Exception ex) {
|
||||
LOG.error("failed to create stage two injector", ex);
|
||||
return createStageOneInjector(SingleView.error(ex));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -38,39 +38,27 @@ import com.google.common.base.Strings;
|
||||
import com.google.common.collect.Multimap;
|
||||
import com.google.common.io.Closeables;
|
||||
import com.google.inject.Inject;
|
||||
|
||||
import org.apache.shiro.codec.Base64;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import sonia.scm.config.ScmConfiguration;
|
||||
import sonia.scm.net.Proxies;
|
||||
import sonia.scm.net.TrustAllHostnameVerifier;
|
||||
import sonia.scm.net.TrustAllTrustManager;
|
||||
import sonia.scm.util.HttpUtil;
|
||||
|
||||
//~--- JDK imports ------------------------------------------------------------
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.ProtocolException;
|
||||
import java.net.Proxy;
|
||||
import java.net.SocketAddress;
|
||||
import java.net.URL;
|
||||
|
||||
import java.security.KeyManagementException;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
|
||||
import java.util.Set;
|
||||
import javax.inject.Provider;
|
||||
|
||||
import javax.net.ssl.HttpsURLConnection;
|
||||
import javax.net.ssl.SSLContext;
|
||||
import javax.net.ssl.TrustManager;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.net.*;
|
||||
import java.security.KeyManagementException;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.util.Set;
|
||||
|
||||
//~--- JDK imports ------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Default implementation of the {@link AdvancedHttpClient}. The default
|
||||
@@ -324,11 +312,7 @@ public class DefaultAdvancedHttpClient extends AdvancedHttpClient
|
||||
sc.init(null, trustAllCerts, new java.security.SecureRandom());
|
||||
connection.setSSLSocketFactory(sc.getSocketFactory());
|
||||
}
|
||||
catch (KeyManagementException ex)
|
||||
{
|
||||
logger.error("could not disable certificate validation", ex);
|
||||
}
|
||||
catch (NoSuchAlgorithmException ex)
|
||||
catch (KeyManagementException | NoSuchAlgorithmException ex)
|
||||
{
|
||||
logger.error("could not disable certificate validation", ex);
|
||||
}
|
||||
|
||||
@@ -34,20 +34,17 @@ package sonia.scm.net.ahc;
|
||||
//~--- non-JDK imports --------------------------------------------------------
|
||||
|
||||
import com.google.common.io.ByteSource;
|
||||
|
||||
import sonia.scm.plugin.Extension;
|
||||
import sonia.scm.util.IOUtil;
|
||||
|
||||
//~--- JDK imports ------------------------------------------------------------
|
||||
|
||||
import javax.ws.rs.core.MediaType;
|
||||
import javax.xml.bind.DataBindingException;
|
||||
import javax.xml.bind.JAXB;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
import javax.ws.rs.core.MediaType;
|
||||
|
||||
import javax.xml.bind.DataBindingException;
|
||||
import javax.xml.bind.JAXB;
|
||||
//~--- JDK imports ------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* {@link ContentTransformer} for xml. The {@link XmlContentTransformer} uses
|
||||
@@ -96,15 +93,10 @@ public class XmlContentTransformer implements ContentTransformer
|
||||
stream = content.openBufferedStream();
|
||||
object = JAXB.unmarshal(stream, type);
|
||||
}
|
||||
catch (IOException ex)
|
||||
catch (IOException | DataBindingException ex)
|
||||
{
|
||||
throw new ContentTransformerException("could not unmarshall content", ex);
|
||||
}
|
||||
catch (DataBindingException ex)
|
||||
{
|
||||
throw new ContentTransformerException("could not unmarshall content", ex);
|
||||
}
|
||||
finally
|
||||
} finally
|
||||
{
|
||||
IOUtil.close(stream);
|
||||
}
|
||||
|
||||
@@ -248,6 +248,7 @@ public class DefaultPluginManager implements PluginManager {
|
||||
try {
|
||||
Thread.sleep(200);
|
||||
} catch (InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
eventBus.post(new RestartEvent(PluginManager.class, cause));
|
||||
}).start();
|
||||
|
||||
@@ -74,7 +74,7 @@ public class MultiParentClassLoader extends ClassLoader
|
||||
public MultiParentClassLoader(Collection<? extends ClassLoader> parents)
|
||||
{
|
||||
super(null);
|
||||
this.parents = new CopyOnWriteArrayList<ClassLoader>(parents);
|
||||
this.parents = new CopyOnWriteArrayList<>(parents);
|
||||
}
|
||||
|
||||
//~--- get methods ----------------------------------------------------------
|
||||
|
||||
@@ -53,7 +53,7 @@ public final class PluginCenterDto implements Serializable {
|
||||
private String category;
|
||||
private String author;
|
||||
private String avatarUrl;
|
||||
private String sha256;
|
||||
private String sha256sum;
|
||||
|
||||
@XmlElement(name = "conditions")
|
||||
private Condition conditions;
|
||||
|
||||
@@ -19,7 +19,7 @@ public abstract class PluginCenterDtoMapper {
|
||||
for (PluginCenterDto.Plugin plugin : pluginCenterDto.getEmbedded().getPlugins()) {
|
||||
String url = plugin.getLinks().get("download").getHref();
|
||||
AvailablePluginDescriptor descriptor = new AvailablePluginDescriptor(
|
||||
map(plugin), map(plugin.getConditions()), plugin.getDependencies(), url, plugin.getSha256()
|
||||
map(plugin), map(plugin.getConditions()), plugin.getDependencies(), url, plugin.getSha256sum()
|
||||
);
|
||||
plugins.add(new AvailablePlugin(descriptor));
|
||||
}
|
||||
|
||||
@@ -128,15 +128,7 @@ public class HealthCheckContextListener implements ServletContextListener
|
||||
{
|
||||
|
||||
// excute health checks for all repsitories asynchronous
|
||||
SecurityUtils.getSubject().execute(new Runnable()
|
||||
{
|
||||
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
healthChecker.checkAll();
|
||||
}
|
||||
});
|
||||
SecurityUtils.getSubject().execute(healthChecker::checkAll);
|
||||
}
|
||||
|
||||
//~--- fields -------------------------------------------------------------
|
||||
|
||||
@@ -35,18 +35,14 @@ package sonia.scm.cache;
|
||||
|
||||
//~--- non-JDK imports --------------------------------------------------------
|
||||
|
||||
import com.google.common.base.Predicate;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Assume;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import sonia.scm.util.IOUtil;
|
||||
|
||||
import static org.hamcrest.Matchers.*;
|
||||
|
||||
import static org.hamcrest.Matchers.containsInAnyOrder;
|
||||
import static org.junit.Assert.*;
|
||||
import org.junit.Assume;
|
||||
|
||||
/**
|
||||
*
|
||||
@@ -166,14 +162,7 @@ public abstract class CacheTestBase
|
||||
cache.put("a-1", "test123");
|
||||
cache.put("a-2", "test123");
|
||||
|
||||
Iterable<String> previous = cache.removeAll(new Predicate<String>()
|
||||
{
|
||||
@Override
|
||||
public boolean apply(String item)
|
||||
{
|
||||
return item.startsWith("test");
|
||||
}
|
||||
});
|
||||
Iterable<String> previous = cache.removeAll(item -> item != null && item.startsWith("test"));
|
||||
|
||||
assertThat(previous, containsInAnyOrder("test123", "test456"));
|
||||
assertNull(cache.get("test-1"));
|
||||
@@ -188,8 +177,8 @@ public abstract class CacheTestBase
|
||||
// skip test if implementation does not support stats
|
||||
Assume.assumeTrue( stats != null );
|
||||
assertEquals("test", stats.getName());
|
||||
assertEquals(0l, stats.getHitCount());
|
||||
assertEquals(0l, stats.getMissCount());
|
||||
assertEquals(0L, stats.getHitCount());
|
||||
assertEquals(0L, stats.getMissCount());
|
||||
cache.put("test-1", "test123");
|
||||
cache.put("test-2", "test456");
|
||||
cache.get("test-1");
|
||||
@@ -197,11 +186,11 @@ public abstract class CacheTestBase
|
||||
cache.get("test-1");
|
||||
cache.get("test-3");
|
||||
// check that stats have not changed
|
||||
assertEquals(0l, stats.getHitCount());
|
||||
assertEquals(0l, stats.getMissCount());
|
||||
assertEquals(0L, stats.getHitCount());
|
||||
assertEquals(0L, stats.getMissCount());
|
||||
stats = cache.getStatistics();
|
||||
assertEquals(3l, stats.getHitCount());
|
||||
assertEquals(1l, stats.getMissCount());
|
||||
assertEquals(3L, stats.getHitCount());
|
||||
assertEquals(1L, stats.getMissCount());
|
||||
assertEquals(0.75d, stats.getHitRate(), 0.0d);
|
||||
assertEquals(0.25d, stats.getMissRate(), 0.0d);
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user