i18n
This commit is contained in:
parent
fd7a82ec9f
commit
dd89443df2
53 changed files with 1503 additions and 316 deletions
10
build.gradle
10
build.gradle
|
@ -95,6 +95,7 @@ dependencies {
|
|||
compile "com.querydsl:querydsl-apt:$queryDslVersion:jpa"
|
||||
//vaadin
|
||||
compile 'com.vaadin:vaadin-spring-boot-starter'
|
||||
compile 'org.vaadin.spring.addons:vaadin-spring-addon-i18n:2.0.0.RELEASE'
|
||||
compile 'com.vaadin:vaadin-push'
|
||||
compile('org.vaadin.addon:jfreechartwrapper:4.0.0') {
|
||||
exclude group: 'javax.servlet', module: 'servlet-api'
|
||||
|
@ -138,6 +139,15 @@ task generateThemeClasses(type: com.faendir.acra.gradle.ThemeClassGenerator) {
|
|||
|
||||
compileJava.dependsOn(generateThemeClasses)
|
||||
|
||||
task generateMessageClasses(type: com.faendir.acra.gradle.I18nClassGenerator) {
|
||||
inputDirectory file('src/main/resources/i18n/com/faendir/acra')
|
||||
outputDirectory file("$generated/faendir/java")
|
||||
packageName 'com.faendir.acra.i18n'
|
||||
className 'Messages'
|
||||
}
|
||||
|
||||
compileJava.dependsOn(generateMessageClasses)
|
||||
|
||||
test {
|
||||
testLogging {
|
||||
events "failed"
|
||||
|
|
|
@ -21,4 +21,5 @@ repositories {
|
|||
dependencies {
|
||||
gradleApi()
|
||||
compile 'com.squareup:javapoet:1.11.1'
|
||||
compile 'com.google.guava:guava:26.0-jre'
|
||||
}
|
|
@ -0,0 +1,71 @@
|
|||
/*
|
||||
* (C) Copyright 2018 Lukas Morawietz (https://github.com/F43nd1r)
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.faendir.acra.gradle
|
||||
|
||||
import com.google.common.base.CaseFormat
|
||||
import com.squareup.javapoet.FieldSpec
|
||||
import com.squareup.javapoet.JavaFile
|
||||
import com.squareup.javapoet.TypeSpec
|
||||
import org.gradle.api.DefaultTask
|
||||
import org.gradle.api.tasks.*
|
||||
import org.gradle.plugins.ide.idea.IdeaPlugin
|
||||
|
||||
import static javax.lang.model.element.Modifier.*
|
||||
/**
|
||||
* @author lukas
|
||||
* @since 15.08.18
|
||||
*/
|
||||
@CacheableTask
|
||||
class I18nClassGenerator extends DefaultTask {
|
||||
@SkipWhenEmpty
|
||||
@InputDirectory
|
||||
File inputDirectory
|
||||
@OutputDirectory
|
||||
File outputDirectory
|
||||
String packageName
|
||||
String className
|
||||
|
||||
I18nClassGenerator() {
|
||||
project.afterEvaluate {
|
||||
project.sourceSets.main.java.srcDirs outputs.files
|
||||
project.plugins.withType(IdeaPlugin.class, { IdeaPlugin idea -> idea.model.module.generatedSourceDirs += outputs.files })
|
||||
}
|
||||
}
|
||||
|
||||
@TaskAction
|
||||
void exec() {
|
||||
Set<String> keys = new HashSet<>()
|
||||
for (File file : project.fileTree(inputDirectory)) {
|
||||
try {
|
||||
Properties properties = new Properties()
|
||||
properties.load(file.newReader())
|
||||
keys.addAll(properties.keys() as Collection<String>)
|
||||
} catch (ignored){
|
||||
}
|
||||
}
|
||||
TypeSpec.Builder classBuilder = TypeSpec.classBuilder(className).addModifiers(PUBLIC, FINAL)
|
||||
for (String key : keys) {
|
||||
classBuilder.addField(FieldSpec.builder(String, CaseFormat.LOWER_CAMEL.to(CaseFormat.UPPER_UNDERSCORE, key), PUBLIC, STATIC, FINAL).initializer('$S', key).build())
|
||||
}
|
||||
JavaFile.builder(packageName, classBuilder.build())
|
||||
.skipJavaLangImports(true)
|
||||
.indent(" ")
|
||||
.build()
|
||||
.writeTo(outputDirectory)
|
||||
}
|
||||
|
||||
}
|
26
src/main/java/com/faendir/acra/i18n/HasI18n.java
Normal file
26
src/main/java/com/faendir/acra/i18n/HasI18n.java
Normal file
|
@ -0,0 +1,26 @@
|
|||
/*
|
||||
* (C) Copyright 2018 Lukas Morawietz (https://github.com/F43nd1r)
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.faendir.acra.i18n;
|
||||
|
||||
import org.vaadin.spring.i18n.I18N;
|
||||
|
||||
/**
|
||||
* @author lukas
|
||||
* @since 16.08.18
|
||||
*/
|
||||
public interface HasI18n {
|
||||
I18N getI18n();
|
||||
}
|
53
src/main/java/com/faendir/acra/i18n/I18nAccordion.java
Normal file
53
src/main/java/com/faendir/acra/i18n/I18nAccordion.java
Normal file
|
@ -0,0 +1,53 @@
|
|||
/*
|
||||
* (C) Copyright 2018 Lukas Morawietz (https://github.com/F43nd1r)
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.faendir.acra.i18n;
|
||||
|
||||
import com.vaadin.ui.Accordion;
|
||||
import com.vaadin.ui.Component;
|
||||
import com.vaadin.ui.TabSheet;
|
||||
import org.springframework.data.util.Pair;
|
||||
import org.vaadin.spring.i18n.I18N;
|
||||
import org.vaadin.spring.i18n.support.Translatable;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @author lukas
|
||||
* @since 16.08.18
|
||||
*/
|
||||
public class I18nAccordion extends Accordion implements Translatable {
|
||||
private final I18N i18n;
|
||||
private final Map<TabSheet.Tab, Pair<String, Object[]>> tabCaptionIds;
|
||||
|
||||
public I18nAccordion(I18N i18n) {
|
||||
this.i18n = i18n;
|
||||
tabCaptionIds = new HashMap<>();
|
||||
}
|
||||
|
||||
public Tab addTab(Component c, String captionId, Object... parameters) {
|
||||
Tab tab = super.addTab(c, i18n.get(captionId, parameters));
|
||||
tabCaptionIds.put(tab, Pair.of(captionId, parameters));
|
||||
return tab;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateMessageStrings(Locale locale) {
|
||||
tabCaptionIds.forEach((tab, pair) -> tab.setCaption(i18n.get(pair.getFirst(), locale, pair.getSecond())));
|
||||
}
|
||||
}
|
55
src/main/java/com/faendir/acra/i18n/I18nButton.java
Normal file
55
src/main/java/com/faendir/acra/i18n/I18nButton.java
Normal file
|
@ -0,0 +1,55 @@
|
|||
/*
|
||||
* (C) Copyright 2018 Lukas Morawietz (https://github.com/F43nd1r)
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.faendir.acra.i18n;
|
||||
|
||||
import com.vaadin.ui.Button;
|
||||
import org.vaadin.spring.i18n.I18N;
|
||||
import org.vaadin.spring.i18n.support.Translatable;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
/**
|
||||
* @author lukas
|
||||
* @since 15.08.18
|
||||
*/
|
||||
public class I18nButton extends Button implements Translatable, HasI18n {
|
||||
private final I18N i18n;
|
||||
private final String captionId;
|
||||
private final Object[] params;
|
||||
|
||||
public I18nButton(ClickListener clickListener, I18N i18n, String captionId, Object... params) {
|
||||
this(i18n, captionId, params);
|
||||
addClickListener(clickListener);
|
||||
}
|
||||
|
||||
public I18nButton(I18N i18n, String captionId, Object... params) {
|
||||
this.i18n = i18n;
|
||||
this.captionId = captionId;
|
||||
this.params = params;
|
||||
updateMessageStrings(i18n.getLocale());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateMessageStrings(Locale locale) {
|
||||
setCaption(i18n.get(captionId, locale, params));
|
||||
}
|
||||
|
||||
@Override
|
||||
public I18N getI18n() {
|
||||
return i18n;
|
||||
}
|
||||
}
|
55
src/main/java/com/faendir/acra/i18n/I18nCheckBox.java
Normal file
55
src/main/java/com/faendir/acra/i18n/I18nCheckBox.java
Normal file
|
@ -0,0 +1,55 @@
|
|||
/*
|
||||
* (C) Copyright 2018 Lukas Morawietz (https://github.com/F43nd1r)
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.faendir.acra.i18n;
|
||||
|
||||
import com.vaadin.ui.CheckBox;
|
||||
import org.vaadin.spring.i18n.I18N;
|
||||
import org.vaadin.spring.i18n.support.Translatable;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
/**
|
||||
* @author lukas
|
||||
* @since 15.08.18
|
||||
*/
|
||||
public class I18nCheckBox extends CheckBox implements Translatable, HasI18n {
|
||||
private final I18N i18n;
|
||||
private final String captionId;
|
||||
private final Object[] params;
|
||||
|
||||
public I18nCheckBox(boolean value, I18N i18n, String captionId, Object... params) {
|
||||
this(i18n, captionId, params);
|
||||
setValue(value);
|
||||
}
|
||||
|
||||
public I18nCheckBox(I18N i18n, String captionId, Object... params) {
|
||||
this.i18n = i18n;
|
||||
this.captionId = captionId;
|
||||
this.params = params;
|
||||
updateMessageStrings(i18n.getLocale());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateMessageStrings(Locale locale) {
|
||||
setCaption(i18n.get(captionId, locale, params));
|
||||
}
|
||||
|
||||
@Override
|
||||
public I18N getI18n() {
|
||||
return i18n;
|
||||
}
|
||||
}
|
50
src/main/java/com/faendir/acra/i18n/I18nComboBox.java
Normal file
50
src/main/java/com/faendir/acra/i18n/I18nComboBox.java
Normal file
|
@ -0,0 +1,50 @@
|
|||
/*
|
||||
* (C) Copyright 2018 Lukas Morawietz (https://github.com/F43nd1r)
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.faendir.acra.i18n;
|
||||
|
||||
import com.vaadin.ui.ComboBox;
|
||||
import org.vaadin.spring.i18n.I18N;
|
||||
import org.vaadin.spring.i18n.support.Translatable;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Locale;
|
||||
|
||||
/**
|
||||
* @author lukas
|
||||
* @since 15.08.18
|
||||
*/
|
||||
public class I18nComboBox<T> extends ComboBox<T> implements Translatable {
|
||||
private final I18N i18n;
|
||||
private final String captionId;
|
||||
private final Object[] params;
|
||||
|
||||
public I18nComboBox(Collection<T> values, I18N i18n, String captionId, Object... params) {
|
||||
this(i18n, captionId, params);
|
||||
setItems(values);
|
||||
}
|
||||
|
||||
public I18nComboBox(I18N i18n, String captionId, Object... params) {
|
||||
this.i18n = i18n;
|
||||
this.captionId = captionId;
|
||||
this.params = params;
|
||||
updateMessageStrings(i18n.getLocale());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateMessageStrings(Locale locale) {
|
||||
setCaption(i18n.get(captionId, locale, params));
|
||||
}
|
||||
}
|
37
src/main/java/com/faendir/acra/i18n/I18nConfiguration.java
Normal file
37
src/main/java/com/faendir/acra/i18n/I18nConfiguration.java
Normal file
|
@ -0,0 +1,37 @@
|
|||
/*
|
||||
* (C) Copyright 2018 Lukas Morawietz (https://github.com/F43nd1r)
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.faendir.acra.i18n;
|
||||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.vaadin.spring.i18n.MessageProvider;
|
||||
import org.vaadin.spring.i18n.ResourceBundleMessageProvider;
|
||||
import org.vaadin.spring.i18n.annotation.EnableI18N;
|
||||
|
||||
/**
|
||||
* @author lukas
|
||||
* @since 15.08.18
|
||||
*/
|
||||
@EnableI18N
|
||||
@Configuration
|
||||
public class I18nConfiguration {
|
||||
|
||||
@Bean
|
||||
MessageProvider messageProvider() {
|
||||
return new ResourceBundleMessageProvider("i18n.com.faendir.acra.messages");
|
||||
}
|
||||
}
|
58
src/main/java/com/faendir/acra/i18n/I18nIntStepper.java
Normal file
58
src/main/java/com/faendir/acra/i18n/I18nIntStepper.java
Normal file
|
@ -0,0 +1,58 @@
|
|||
/*
|
||||
* (C) Copyright 2018 Lukas Morawietz (https://github.com/F43nd1r)
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.faendir.acra.i18n;
|
||||
|
||||
import org.vaadin.risto.stepper.IntStepper;
|
||||
import org.vaadin.spring.i18n.I18N;
|
||||
import org.vaadin.spring.i18n.support.Translatable;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
/**
|
||||
* @author lukas
|
||||
* @since 15.08.18
|
||||
*/
|
||||
public class I18nIntStepper extends IntStepper implements Translatable, HasI18n {
|
||||
private final I18N i18n;
|
||||
private final String captionId;
|
||||
private final Object[] params;
|
||||
|
||||
public I18nIntStepper(int value) {
|
||||
this(value, null, null);
|
||||
}
|
||||
|
||||
public I18nIntStepper(int value, I18N i18n, String captionId, Object... params) {
|
||||
this.i18n = i18n;
|
||||
this.captionId = captionId;
|
||||
this.params = params;
|
||||
setValue(value);
|
||||
if (i18n != null) {
|
||||
updateMessageStrings(i18n.getLocale());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateMessageStrings(Locale locale) {
|
||||
if (i18n != null) {
|
||||
setCaption(i18n.get(captionId, locale, params));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public I18N getI18n() {
|
||||
return i18n;
|
||||
}
|
||||
}
|
51
src/main/java/com/faendir/acra/i18n/I18nLabel.java
Normal file
51
src/main/java/com/faendir/acra/i18n/I18nLabel.java
Normal file
|
@ -0,0 +1,51 @@
|
|||
/*
|
||||
* (C) Copyright 2018 Lukas Morawietz (https://github.com/F43nd1r)
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.faendir.acra.i18n;
|
||||
|
||||
import com.vaadin.shared.ui.ContentMode;
|
||||
import com.vaadin.ui.Label;
|
||||
import org.vaadin.spring.i18n.I18N;
|
||||
import org.vaadin.spring.i18n.support.Translatable;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
/**
|
||||
* @author lukas
|
||||
* @since 15.08.18
|
||||
*/
|
||||
public class I18nLabel extends Label implements Translatable {
|
||||
private final I18N i18n;
|
||||
private final String captionId;
|
||||
private final Object[] params;
|
||||
|
||||
public I18nLabel(ContentMode contentMode, I18N i18n, String captionId, Object... params) {
|
||||
this(i18n, captionId, params);
|
||||
setContentMode(contentMode);
|
||||
}
|
||||
|
||||
public I18nLabel(I18N i18n, String captionId, Object... params) {
|
||||
this.i18n = i18n;
|
||||
this.captionId = captionId;
|
||||
this.params = params;
|
||||
updateMessageStrings(i18n.getLocale());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateMessageStrings(Locale locale) {
|
||||
setCaption(i18n.get(captionId, locale, params));
|
||||
}
|
||||
}
|
74
src/main/java/com/faendir/acra/i18n/I18nMenuBar.java
Normal file
74
src/main/java/com/faendir/acra/i18n/I18nMenuBar.java
Normal file
|
@ -0,0 +1,74 @@
|
|||
/*
|
||||
* (C) Copyright 2018 Lukas Morawietz (https://github.com/F43nd1r)
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.faendir.acra.i18n;
|
||||
|
||||
import com.vaadin.server.Resource;
|
||||
import com.vaadin.ui.MenuBar;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.vaadin.spring.i18n.I18N;
|
||||
import org.vaadin.spring.i18n.support.Translatable;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
/**
|
||||
* @author lukas
|
||||
* @since 16.08.18
|
||||
*/
|
||||
public class I18nMenuBar extends MenuBar implements Translatable {
|
||||
private final I18N i18n;
|
||||
|
||||
public I18nMenuBar(I18N i18n) {
|
||||
this.i18n = i18n;
|
||||
}
|
||||
|
||||
public I18nMenuItem addItem(Resource icon) {
|
||||
I18nMenuItem item = new I18nMenuItem(icon, null, i18n, Messages.BLANK);
|
||||
getItems().add(item);
|
||||
return item;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateMessageStrings(Locale locale) {
|
||||
getItems().stream().filter(Translatable.class::isInstance).map(Translatable.class::cast).forEach(translatable -> translatable.updateMessageStrings(locale));
|
||||
}
|
||||
|
||||
public class I18nMenuItem extends MenuItem implements Translatable {
|
||||
private final I18N i18n;
|
||||
private final String captionId;
|
||||
private final Object[] params;
|
||||
|
||||
private I18nMenuItem(@Nullable Resource icon, Command command, I18N i18n, String captionId, Object... params) {
|
||||
//noinspection ConstantConditions
|
||||
super(i18n.get(captionId, params), icon, command);
|
||||
this.i18n = i18n;
|
||||
this.captionId = captionId;
|
||||
this.params = params;
|
||||
}
|
||||
|
||||
public I18nMenuItem addItem(Command command, String captionId, Object... params) {
|
||||
I18nMenuItem item = new I18nMenuItem(null, command, i18n, captionId, params);
|
||||
getItems().add(item);
|
||||
return item;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateMessageStrings(Locale locale) {
|
||||
setText(i18n.get(captionId, params));
|
||||
getItems().stream().filter(Translatable.class::isInstance).map(Translatable.class::cast).forEach(translatable -> translatable.updateMessageStrings(locale));
|
||||
}
|
||||
}
|
||||
}
|
50
src/main/java/com/faendir/acra/i18n/I18nPanel.java
Normal file
50
src/main/java/com/faendir/acra/i18n/I18nPanel.java
Normal file
|
@ -0,0 +1,50 @@
|
|||
/*
|
||||
* (C) Copyright 2018 Lukas Morawietz (https://github.com/F43nd1r)
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.faendir.acra.i18n;
|
||||
|
||||
import com.vaadin.ui.Component;
|
||||
import com.vaadin.ui.Panel;
|
||||
import org.vaadin.spring.i18n.I18N;
|
||||
import org.vaadin.spring.i18n.support.Translatable;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
/**
|
||||
* @author lukas
|
||||
* @since 16.08.18
|
||||
*/
|
||||
public class I18nPanel extends Panel implements Translatable {
|
||||
private final I18N i18n;
|
||||
private final String captionId;
|
||||
private final Object[] params;
|
||||
|
||||
public I18nPanel(Component content, I18N i18n, String captionId, Object... params) {
|
||||
this(i18n, captionId, params);
|
||||
setContent(content);
|
||||
}
|
||||
|
||||
public I18nPanel(I18N i18n, String captionId, Object... params) {
|
||||
this.i18n = i18n;
|
||||
this.captionId = captionId;
|
||||
this.params = params;
|
||||
updateMessageStrings(i18n.getLocale());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateMessageStrings(Locale locale) {
|
||||
setCaption(i18n.get(captionId, locale, params));
|
||||
}
|
||||
}
|
50
src/main/java/com/faendir/acra/i18n/I18nPasswordField.java
Normal file
50
src/main/java/com/faendir/acra/i18n/I18nPasswordField.java
Normal file
|
@ -0,0 +1,50 @@
|
|||
/*
|
||||
* (C) Copyright 2018 Lukas Morawietz (https://github.com/F43nd1r)
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.faendir.acra.i18n;
|
||||
|
||||
import com.vaadin.ui.PasswordField;
|
||||
import org.vaadin.spring.i18n.I18N;
|
||||
import org.vaadin.spring.i18n.support.Translatable;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
/**
|
||||
* @author lukas
|
||||
* @since 16.08.18
|
||||
*/
|
||||
public class I18nPasswordField extends PasswordField implements Translatable, HasI18n {
|
||||
private final I18N i18n;
|
||||
private final String captionId;
|
||||
private final Object[] params;
|
||||
|
||||
public I18nPasswordField(I18N i18n, String captionId, Object... params) {
|
||||
this.i18n = i18n;
|
||||
this.captionId = captionId;
|
||||
this.params = params;
|
||||
updateMessageStrings(i18n.getLocale());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateMessageStrings(Locale locale) {
|
||||
setCaption(i18n.get(captionId, locale, params));
|
||||
}
|
||||
|
||||
@Override
|
||||
public I18N getI18n() {
|
||||
return i18n;
|
||||
}
|
||||
}
|
50
src/main/java/com/faendir/acra/i18n/I18nSlider.java
Normal file
50
src/main/java/com/faendir/acra/i18n/I18nSlider.java
Normal file
|
@ -0,0 +1,50 @@
|
|||
/*
|
||||
* (C) Copyright 2018 Lukas Morawietz (https://github.com/F43nd1r)
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.faendir.acra.i18n;
|
||||
|
||||
import com.vaadin.ui.Slider;
|
||||
import org.vaadin.spring.i18n.I18N;
|
||||
import org.vaadin.spring.i18n.support.Translatable;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
/**
|
||||
* @author lukas
|
||||
* @since 15.08.18
|
||||
*/
|
||||
public class I18nSlider extends Slider implements Translatable, HasI18n {
|
||||
private final I18N i18n;
|
||||
private final String captionId;
|
||||
private final Object[] params;
|
||||
|
||||
public I18nSlider(int min, int max, I18N i18n, String captionId, Object... params) {
|
||||
super(min, max);
|
||||
this.i18n = i18n;
|
||||
this.captionId = captionId;
|
||||
this.params = params;
|
||||
updateMessageStrings(i18n.getLocale());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateMessageStrings(Locale locale) {
|
||||
setCaption(i18n.get(captionId, locale, params));
|
||||
}
|
||||
|
||||
@Override
|
||||
public I18N getI18n() {
|
||||
return i18n;
|
||||
}
|
||||
}
|
45
src/main/java/com/faendir/acra/i18n/I18nTextArea.java
Normal file
45
src/main/java/com/faendir/acra/i18n/I18nTextArea.java
Normal file
|
@ -0,0 +1,45 @@
|
|||
/*
|
||||
* (C) Copyright 2018 Lukas Morawietz (https://github.com/F43nd1r)
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.faendir.acra.i18n;
|
||||
|
||||
import com.vaadin.ui.TextArea;
|
||||
import org.vaadin.spring.i18n.I18N;
|
||||
import org.vaadin.spring.i18n.support.Translatable;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
/**
|
||||
* @author lukas
|
||||
* @since 16.08.18
|
||||
*/
|
||||
public class I18nTextArea extends TextArea implements Translatable {
|
||||
private final I18N i18n;
|
||||
private final String captionId;
|
||||
private final Object[] params;
|
||||
|
||||
public I18nTextArea(I18N i18n, String captionId, Object... params) {
|
||||
this.i18n = i18n;
|
||||
this.captionId = captionId;
|
||||
this.params = params;
|
||||
updateMessageStrings(i18n.getLocale());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateMessageStrings(Locale locale) {
|
||||
setCaption(i18n.get(captionId, locale, params));
|
||||
}
|
||||
}
|
55
src/main/java/com/faendir/acra/i18n/I18nTextField.java
Normal file
55
src/main/java/com/faendir/acra/i18n/I18nTextField.java
Normal file
|
@ -0,0 +1,55 @@
|
|||
/*
|
||||
* (C) Copyright 2018 Lukas Morawietz (https://github.com/F43nd1r)
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.faendir.acra.i18n;
|
||||
|
||||
import com.vaadin.ui.TextField;
|
||||
import org.vaadin.spring.i18n.I18N;
|
||||
import org.vaadin.spring.i18n.support.Translatable;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
/**
|
||||
* @author lukas
|
||||
* @since 15.08.18
|
||||
*/
|
||||
public class I18nTextField extends TextField implements Translatable, HasI18n {
|
||||
private final I18N i18n;
|
||||
private final String captionId;
|
||||
private final Object[] params;
|
||||
|
||||
public I18nTextField(String value, I18N i18n, String captionId, Object... params) {
|
||||
this(i18n, captionId, params);
|
||||
setValue(value);
|
||||
}
|
||||
|
||||
public I18nTextField(I18N i18n, String captionId, Object... params) {
|
||||
this.i18n = i18n;
|
||||
this.captionId = captionId;
|
||||
this.params = params;
|
||||
updateMessageStrings(i18n.getLocale());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateMessageStrings(Locale locale) {
|
||||
setCaption(i18n.get(captionId, locale, params));
|
||||
}
|
||||
|
||||
@Override
|
||||
public I18N getI18n() {
|
||||
return i18n;
|
||||
}
|
||||
}
|
|
@ -15,6 +15,9 @@
|
|||
*/
|
||||
package com.faendir.acra.ui;
|
||||
|
||||
import com.faendir.acra.i18n.I18nLabel;
|
||||
import com.faendir.acra.i18n.I18nMenuBar;
|
||||
import com.faendir.acra.i18n.Messages;
|
||||
import com.faendir.acra.model.User;
|
||||
import com.faendir.acra.security.SecurityUtils;
|
||||
import com.faendir.acra.ui.navigation.NavigationManager;
|
||||
|
@ -35,10 +38,8 @@ import com.vaadin.ui.Alignment;
|
|||
import com.vaadin.ui.HorizontalLayout;
|
||||
import com.vaadin.ui.Label;
|
||||
import com.vaadin.ui.LoginForm;
|
||||
import com.vaadin.ui.MenuBar;
|
||||
import com.vaadin.ui.Notification;
|
||||
import com.vaadin.ui.Panel;
|
||||
import com.vaadin.ui.UI;
|
||||
import com.vaadin.ui.VerticalLayout;
|
||||
import com.vaadin.ui.themes.AcraTheme;
|
||||
import com.vaadin.ui.themes.DarkAcraTheme;
|
||||
|
@ -53,6 +54,8 @@ import org.springframework.security.core.Authentication;
|
|||
import org.springframework.security.core.AuthenticationException;
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
import org.springframework.web.util.UriComponentsBuilder;
|
||||
import org.vaadin.spring.i18n.I18N;
|
||||
import org.vaadin.spring.i18n.support.TranslatableUI;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
|
@ -64,17 +67,19 @@ import java.util.Optional;
|
|||
@Theme(AcraTheme.THEME_NAME)
|
||||
@Widgetset("com.faendir.acra.AppWidgetset")
|
||||
@Viewport("width=device-width, initial-scale=1")
|
||||
public class BackendUI extends UI {
|
||||
public class BackendUI extends TranslatableUI {
|
||||
private static final String DARK_THEME = "dark";
|
||||
@NonNull private final AuthenticationManager authenticationManager;
|
||||
@NonNull private final ApplicationContext applicationContext;
|
||||
@NonNull private final I18N i18n;
|
||||
@NonNull private final Panel content;
|
||||
@NonNull private final Path path;
|
||||
|
||||
@Autowired
|
||||
public BackendUI(@NonNull AuthenticationManager authenticationManager, @NonNull ApplicationContext applicationContext) {
|
||||
public BackendUI(@NonNull AuthenticationManager authenticationManager, @NonNull ApplicationContext applicationContext, @NonNull I18N i18n) {
|
||||
this.authenticationManager = authenticationManager;
|
||||
this.applicationContext = applicationContext;
|
||||
this.i18n = i18n;
|
||||
content = new Panel();
|
||||
content.setSizeFull();
|
||||
content.addStyleNames(AcraTheme.NO_PADDING, AcraTheme.NO_BACKGROUND, AcraTheme.NO_BORDER);
|
||||
|
@ -82,7 +87,7 @@ public class BackendUI extends UI {
|
|||
}
|
||||
|
||||
@Override
|
||||
protected void init(VaadinRequest request) {
|
||||
protected void initUI(VaadinRequest request) {
|
||||
if (isDarkTheme()) {
|
||||
setTheme(DarkAcraTheme.THEME_NAME);
|
||||
}
|
||||
|
@ -97,14 +102,14 @@ public class BackendUI extends UI {
|
|||
try {
|
||||
Authentication token = authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(username.toLowerCase(), password));
|
||||
if (!token.getAuthorities().contains(User.Role.USER)) {
|
||||
throw new InsufficientAuthenticationException("Missing required role");
|
||||
throw new InsufficientAuthenticationException(i18n.get(Messages.MISSING_ROLE));
|
||||
}
|
||||
VaadinService.reinitializeSession(VaadinService.getCurrentRequest());
|
||||
SecurityContextHolder.getContext().setAuthentication(token);
|
||||
showMain();
|
||||
getPushConfiguration().setPushMode(PushMode.AUTOMATIC);
|
||||
} catch (AuthenticationException ex) {
|
||||
Notification.show("Unknown username/password combination", Notification.Type.ERROR_MESSAGE);
|
||||
Notification.show(i18n.get(Messages.LOGIN_FAILED), Notification.Type.ERROR_MESSAGE);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -127,21 +132,23 @@ public class BackendUI extends UI {
|
|||
private void showMain() {
|
||||
NavigationManager navigationManager = applicationContext.getBean(NavigationManager.class);
|
||||
|
||||
MenuBar menuBar = new MenuBar();
|
||||
MenuBar.MenuItem user = menuBar.addItem("", VaadinIcons.USER, null);
|
||||
I18nMenuBar menuBar = new I18nMenuBar(i18n);
|
||||
I18nMenuBar.I18nMenuItem user = menuBar.addItem(VaadinIcons.USER);
|
||||
user.addItem(SecurityUtils.getUsername()).setEnabled(false);
|
||||
user.addSeparator();
|
||||
MenuBar.MenuItem theme = user.addItem("Dark Theme",
|
||||
e -> getPage().setLocation(UriComponentsBuilder.fromUri(getPage().getLocation()).replaceQueryParam(DARK_THEME, e.isChecked()).build().toUri()));
|
||||
I18nMenuBar.I18nMenuItem theme = user.addItem(e -> getPage().setLocation(UriComponentsBuilder.fromUri(getPage().getLocation())
|
||||
.replaceQueryParam(DARK_THEME, e.isChecked())
|
||||
.build()
|
||||
.toUri()), Messages.DARK_THEME);
|
||||
theme.setCheckable(true);
|
||||
theme.setChecked(isDarkTheme());
|
||||
user.addSeparator();
|
||||
if (SecurityUtils.hasRole(User.Role.ADMIN)) {
|
||||
user.addItem("User Manager", e -> navigationManager.cleanNavigateTo(UserManagerView.class));
|
||||
user.addItem(e -> navigationManager.cleanNavigateTo(UserManagerView.class), Messages.USER_MANAGER);
|
||||
user.addSeparator();
|
||||
}
|
||||
user.addItem("Change Password", e -> navigationManager.cleanNavigateTo(ChangePasswordView.class));
|
||||
user.addItem("Logout", e -> logout());
|
||||
user.addItem(e -> navigationManager.cleanNavigateTo(ChangePasswordView.class), Messages.CHANGE_PASSWORD);
|
||||
user.addItem(e -> logout(), Messages.LOGOUT);
|
||||
|
||||
HorizontalLayout header = new HorizontalLayout(path, menuBar);
|
||||
header.setExpandRatio(path, 1);
|
||||
|
@ -150,9 +157,7 @@ public class BackendUI extends UI {
|
|||
|
||||
HorizontalLayout footer = new HorizontalLayout();
|
||||
footer.setDefaultComponentAlignment(Alignment.MIDDLE_CENTER);
|
||||
Label footerLabel = new Label(
|
||||
"Acrarium is developed by <a href=https://github.com/F43nd1r>F43nd1r</a>." + " <a href=https://github.com/F43nd1r/acra-backend>Code</a> is licensed under"
|
||||
+ " <a href=https://github.com/F43nd1r/acra-backend/blob/master/LICENSE>Apache License v2</a>.", ContentMode.HTML);
|
||||
Label footerLabel = new I18nLabel(ContentMode.HTML, i18n, Messages.FOOTER);
|
||||
footerLabel.setWidth(100, Unit.PERCENTAGE);
|
||||
footerLabel.addStyleName(AcraTheme.CENTER_TEXT);
|
||||
footer.addComponent(footerLabel);
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
|
||||
package com.faendir.acra.ui.navigation;
|
||||
|
||||
import com.faendir.acra.i18n.Messages;
|
||||
import com.faendir.acra.ui.view.ErrorView;
|
||||
import com.faendir.acra.ui.view.base.navigation.BaseView;
|
||||
import com.faendir.acra.ui.view.base.navigation.Path;
|
||||
|
@ -28,6 +29,7 @@ import org.springframework.beans.factory.annotation.Configurable;
|
|||
import org.springframework.lang.NonNull;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.vaadin.spring.i18n.I18N;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Deque;
|
||||
|
@ -47,7 +49,7 @@ public class NavigationManager implements Serializable {
|
|||
@NonNull private final MyNavigator navigator;
|
||||
|
||||
@Autowired
|
||||
public NavigationManager(@NonNull UI ui, @NonNull Panel mainView, @NonNull Path mainPath, @NonNull MyNavigator navigator) {
|
||||
public NavigationManager(@NonNull UI ui, @NonNull Panel mainView, @NonNull Path mainPath, @NonNull MyNavigator navigator, @NonNull I18N i18n) {
|
||||
this.path = mainPath;
|
||||
this.navigator = navigator;
|
||||
this.navigator.init(ui, mainView);
|
||||
|
@ -57,7 +59,7 @@ public class NavigationManager implements Serializable {
|
|||
SingleViewProvider provider = (SingleViewProvider) hierarchy.getLast().getProvider();
|
||||
String title = provider.getTitle(provider.getParameters(hierarchy.getLast().getNavState()));
|
||||
if (hierarchy.size() > 1) {
|
||||
title += " - Acrarium";
|
||||
title += " - " + i18n.get(Messages.ACRARIUM);
|
||||
}
|
||||
ui.getPage().setTitle(title);
|
||||
});
|
||||
|
|
|
@ -16,6 +16,8 @@
|
|||
|
||||
package com.faendir.acra.ui.view;
|
||||
|
||||
import com.faendir.acra.i18n.I18nLabel;
|
||||
import com.faendir.acra.i18n.Messages;
|
||||
import com.vaadin.navigator.View;
|
||||
import com.vaadin.navigator.ViewChangeListener;
|
||||
import com.vaadin.spring.annotation.SpringComponent;
|
||||
|
@ -24,6 +26,8 @@ import com.vaadin.ui.Alignment;
|
|||
import com.vaadin.ui.Composite;
|
||||
import com.vaadin.ui.Label;
|
||||
import com.vaadin.ui.VerticalLayout;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.vaadin.spring.i18n.I18N;
|
||||
|
||||
/**
|
||||
* @author Lukas
|
||||
|
@ -32,9 +36,10 @@ import com.vaadin.ui.VerticalLayout;
|
|||
@SpringComponent
|
||||
@UIScope
|
||||
public class ErrorView extends Composite implements View {
|
||||
public ErrorView() {
|
||||
@Autowired
|
||||
public ErrorView(I18N i18n) {
|
||||
VerticalLayout layout = new VerticalLayout();
|
||||
Label label = new Label("This page does not exist or you do not have the permission to view it.");
|
||||
Label label = new I18nLabel(i18n, Messages.ERROR4XX);
|
||||
layout.addComponent(label);
|
||||
layout.setComponentAlignment(label, Alignment.MIDDLE_CENTER);
|
||||
layout.setSizeFull();
|
||||
|
|
|
@ -15,6 +15,12 @@
|
|||
*/
|
||||
package com.faendir.acra.ui.view;
|
||||
|
||||
import com.faendir.acra.i18n.I18nButton;
|
||||
import com.faendir.acra.i18n.I18nCheckBox;
|
||||
import com.faendir.acra.i18n.I18nIntStepper;
|
||||
import com.faendir.acra.i18n.I18nLabel;
|
||||
import com.faendir.acra.i18n.I18nTextField;
|
||||
import com.faendir.acra.i18n.Messages;
|
||||
import com.faendir.acra.model.QApp;
|
||||
import com.faendir.acra.model.QBug;
|
||||
import com.faendir.acra.model.QReport;
|
||||
|
@ -36,16 +42,14 @@ import com.vaadin.spring.annotation.UIScope;
|
|||
import com.vaadin.spring.annotation.ViewScope;
|
||||
import com.vaadin.ui.Alignment;
|
||||
import com.vaadin.ui.Button;
|
||||
import com.vaadin.ui.CheckBox;
|
||||
import com.vaadin.ui.Grid;
|
||||
import com.vaadin.ui.HorizontalLayout;
|
||||
import com.vaadin.ui.Label;
|
||||
import com.vaadin.ui.TextField;
|
||||
import com.vaadin.ui.VerticalLayout;
|
||||
import com.vaadin.ui.themes.AcraTheme;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.lang.NonNull;
|
||||
import org.vaadin.risto.stepper.IntStepper;
|
||||
import org.vaadin.spring.i18n.I18N;
|
||||
|
||||
/**
|
||||
* @author Lukas
|
||||
|
@ -55,50 +59,52 @@ import org.vaadin.risto.stepper.IntStepper;
|
|||
@ViewScope
|
||||
public class Overview extends BaseView {
|
||||
@NonNull private final DataService dataService;
|
||||
@NonNull private final I18N i18n;
|
||||
private MyGrid<VApp> grid;
|
||||
|
||||
@Autowired
|
||||
public Overview(@NonNull DataService dataService) {
|
||||
public Overview(@NonNull DataService dataService, @NonNull I18N i18n) {
|
||||
this.dataService = dataService;
|
||||
this.i18n = i18n;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void enter(ViewChangeListener.ViewChangeEvent event) {
|
||||
grid = new MyGrid<>("Apps", dataService.getAppProvider());
|
||||
grid = new MyGrid<>(dataService.getAppProvider(), i18n, Messages.APPS);
|
||||
grid.setResponsive(true);
|
||||
grid.setSizeToRows();
|
||||
grid.setSelectionMode(Grid.SelectionMode.NONE);
|
||||
grid.addColumn(VApp::getName, QApp.app.name, "Name");
|
||||
grid.addColumn(VApp::getBugCount, QBug.bug.countDistinct(), "Bugs");
|
||||
grid.addColumn(VApp::getReportCount, QReport.report.count(), "Reports");
|
||||
grid.addColumn(VApp::getName, QApp.app.name, Messages.NAME);
|
||||
grid.addColumn(VApp::getBugCount, QBug.bug.countDistinct(), Messages.BUGS);
|
||||
grid.addColumn(VApp::getReportCount, QReport.report.count(), Messages.REPORTS);
|
||||
grid.addOnClickNavigation(getNavigationManager(), AppView.class, e -> String.valueOf(e.getItem().getId()));
|
||||
VerticalLayout layout = new VerticalLayout();
|
||||
if (SecurityUtils.hasRole(User.Role.ADMIN)) {
|
||||
Button add = new Button("New App", e -> {
|
||||
TextField name = new TextField("Name");
|
||||
new Popup().setTitle("New App").addComponent(name).addCreateButton(popup -> {
|
||||
popup.clear().addComponent(new ConfigurationLabel(dataService.createNewApp(name.getValue()))).addCloseButton().show();
|
||||
Button add = new I18nButton(i18n, Messages.NEW_APP);
|
||||
add.addClickListener(e -> {
|
||||
TextField name = new I18nTextField(i18n, Messages.NAME);
|
||||
new Popup(i18n, Messages.NEW_APP).addComponent(name).addCreateButton(popup -> {
|
||||
popup.clear().addComponent(new ConfigurationLabel(dataService.createNewApp(name.getValue()), i18n)).addCloseButton().show();
|
||||
grid.getDataProvider().refreshAll();
|
||||
}).show();
|
||||
});
|
||||
Button importButton = new Button("Import from Acralyzer", e -> {
|
||||
TextField host = new TextField("Host", "localhost");
|
||||
IntStepper port = new IntStepper("Port");
|
||||
port.setValue(5984);
|
||||
Button importButton = new I18nButton(i18n, Messages.IMPORT_ACRALYZER);
|
||||
importButton.addClickListener(e -> {
|
||||
I18nTextField host = new I18nTextField("localhost", i18n, Messages.HOST);
|
||||
I18nIntStepper port = new I18nIntStepper(5984, i18n, Messages.PORT);
|
||||
port.setMinValue(0);
|
||||
port.setMaxValue(65535);
|
||||
CheckBox ssl = new CheckBox("Use ssl", false);
|
||||
TextField databaseName = new TextField("Database Name", "acra-myapp");
|
||||
new Popup().setTitle("Import from Acralyzer")
|
||||
.addValidatedField(ValidatedField.of(host), true)
|
||||
I18nCheckBox ssl = new I18nCheckBox(i18n, Messages.SSL);
|
||||
I18nTextField databaseName = new I18nTextField("acra-myapp", i18n, Messages.DATABASE_NAME);
|
||||
new Popup(i18n, Messages.IMPORT_ACRALYZER).addValidatedField(ValidatedField.of(host), true)
|
||||
.addValidatedField(ValidatedField.of(port), true)
|
||||
.addValidatedField(ValidatedField.of(ssl), true)
|
||||
.addValidatedField(ValidatedField.of(databaseName), true)
|
||||
.addCreateButton(popup -> {
|
||||
ImportResult importResult = dataService.importFromAcraStorage(host.getValue(), port.getValue(), ssl.getValue(), databaseName.getValue());
|
||||
popup.clear()
|
||||
.addComponent(new Label(String.format("Import successful for %d of %d reports.", importResult.getSuccessCount(), importResult.getTotalCount())))
|
||||
.addComponent(new ConfigurationLabel(importResult.getUser()))
|
||||
.addComponent(new I18nLabel(i18n, Messages.IMPORT_SUCCESS, importResult.getSuccessCount(), importResult.getTotalCount()))
|
||||
.addComponent(new ConfigurationLabel(importResult.getUser(), i18n))
|
||||
.addCloseButton()
|
||||
.show();
|
||||
grid.getDataProvider().refreshAll();
|
||||
|
@ -120,13 +126,16 @@ public class Overview extends BaseView {
|
|||
@SpringComponent
|
||||
@UIScope
|
||||
public static class Provider extends SingleViewProvider<Overview> {
|
||||
protected Provider() {
|
||||
@NonNull private final I18N i18n;
|
||||
|
||||
protected Provider(@NonNull I18N i18n) {
|
||||
super(Overview.class);
|
||||
this.i18n = i18n;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getTitle(String parameter) {
|
||||
return "Acrarium";
|
||||
return i18n.get(Messages.ACRARIUM);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -56,10 +56,9 @@ public class AppView extends ParametrizedBaseView<Pair<App, String>> {
|
|||
protected void enter(@NonNull Pair<App, String> parameter) {
|
||||
MyTabSheet<App> tabSheet = new MyTabSheet<>(parameter.getFirst(), getNavigationManager(), tabs);
|
||||
tabSheet.setSizeFull();
|
||||
tabSheet.addSelectedTabChangeListener(e -> getNavigationManager().updatePageParameters(parameter.getFirst().getId() + "/" + e.getTabSheet().getSelectedTab().getCaption()));
|
||||
tabSheet.addMiddleClickListener(e -> getNavigationManager().openInNewTab(parameter.getFirst().getId() + "/" + e.getTab().getCaption()));
|
||||
if (tabSheet.getCaptions().contains(parameter.getSecond())) tabSheet.setInitialTab(parameter.getSecond());
|
||||
else tabSheet.setFirstTabAsInitialTab();
|
||||
tabSheet.addSelectedTabChangeListener(e -> getNavigationManager().updatePageParameters(parameter.getFirst().getId() + "/" + e.getTabSheet().getSelectedTab().getId()));
|
||||
tabSheet.addMiddleClickListener(e -> getNavigationManager().openInNewTab(parameter.getFirst().getId() + "/" + e.getTab().getId()));
|
||||
tabSheet.guessInitialTab(parameter.getSecond());
|
||||
Panel panel = new Panel(tabSheet);
|
||||
panel.setSizeFull();
|
||||
panel.addStyleNames(AcraTheme.NO_BORDER, AcraTheme.NO_BACKGROUND, AcraTheme.NO_PADDING, AcraTheme.PADDING_LEFT, AcraTheme.PADDING_RIGHT);
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
*/
|
||||
package com.faendir.acra.ui.view.app.tabs;
|
||||
|
||||
import com.faendir.acra.i18n.Messages;
|
||||
import com.faendir.acra.model.App;
|
||||
import com.faendir.acra.model.Permission;
|
||||
import com.faendir.acra.ui.annotation.RequiresAppPermission;
|
||||
|
@ -24,6 +25,7 @@ import com.vaadin.spring.annotation.SpringComponent;
|
|||
import com.vaadin.spring.annotation.ViewScope;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.lang.NonNull;
|
||||
import org.vaadin.spring.i18n.I18N;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
|
@ -35,14 +37,22 @@ import java.util.List;
|
|||
@SpringComponent
|
||||
@ViewScope
|
||||
public class AdminTab extends PanelFlexTab<App> implements AppTab {
|
||||
@NonNull private final I18N i18n;
|
||||
|
||||
@Autowired
|
||||
public AdminTab(@NonNull List<AdminPanel> panels) {
|
||||
public AdminTab(@NonNull List<AdminPanel> panels, @NonNull I18N i18n) {
|
||||
super(panels);
|
||||
this.i18n = i18n;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getCaption() {
|
||||
return "Admin";
|
||||
return i18n.get(Messages.ADMIN);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getId() {
|
||||
return "admin";
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -13,9 +13,11 @@
|
|||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.faendir.acra.ui.view.app.tabs;
|
||||
|
||||
import com.faendir.acra.i18n.I18nButton;
|
||||
import com.faendir.acra.i18n.I18nCheckBox;
|
||||
import com.faendir.acra.i18n.Messages;
|
||||
import com.faendir.acra.model.App;
|
||||
import com.faendir.acra.model.Permission;
|
||||
import com.faendir.acra.model.QBug;
|
||||
|
@ -45,6 +47,7 @@ import com.vaadin.ui.renderers.ComponentRenderer;
|
|||
import com.vaadin.ui.themes.AcraTheme;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.lang.NonNull;
|
||||
import org.vaadin.spring.i18n.I18N;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
@ -58,15 +61,22 @@ import java.util.stream.Collectors;
|
|||
@ViewScope
|
||||
public class BugTab implements AppTab {
|
||||
@NonNull private final DataService dataService;
|
||||
@NonNull private final I18N i18n;
|
||||
|
||||
@Autowired
|
||||
public BugTab(@NonNull DataService dataService) {
|
||||
public BugTab(@NonNull DataService dataService, @NonNull I18N i18n) {
|
||||
this.dataService = dataService;
|
||||
this.i18n = i18n;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getCaption() {
|
||||
return "Bugs";
|
||||
return i18n.get(Messages.BUGS);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getId() {
|
||||
return "bug";
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -77,38 +87,39 @@ public class BugTab implements AppTab {
|
|||
header.addStyleName(AcraTheme.PADDING_TOP);
|
||||
layout.addComponent(header);
|
||||
layout.setComponentAlignment(header, Alignment.MIDDLE_LEFT);
|
||||
CheckBox hideSolved = new CheckBox("Hide solved", true);
|
||||
MyGrid<VBug> bugs = new MyGrid<>(null, dataService.getBugProvider(app, hideSolved::getValue));
|
||||
CheckBox hideSolved = new I18nCheckBox(true, i18n, Messages.HIDE_SOLVED);
|
||||
MyGrid<VBug> bugs = new MyGrid<>(dataService.getBugProvider(app, hideSolved::getValue));
|
||||
bugs.setSelectionMode(Grid.SelectionMode.MULTI);
|
||||
hideSolved.addValueChangeListener(e -> layout.getUI().access(() -> {
|
||||
bugs.deselectAll();
|
||||
bugs.getDataProvider().refreshAll();
|
||||
}));
|
||||
Button merge = new Button("Merge bugs", e -> {
|
||||
Button merge = new I18nButton(e -> {
|
||||
List<VBug> selectedItems = new ArrayList<>(bugs.getSelectedItems());
|
||||
if (selectedItems.size() > 1) {
|
||||
RadioButtonGroup<String> titles = new RadioButtonGroup<>("", selectedItems.stream().map(bug -> bug.getBug().getTitle()).collect(Collectors.toList()));
|
||||
titles.setSelectedItem(selectedItems.get(0).getBug().getTitle());
|
||||
new Popup().setTitle("Choose title for bug group").addComponent(titles).addCreateButton(p -> {
|
||||
new Popup(i18n, Messages.CHOOSE_BUG_GROUP_TITLE).addComponent(titles).addCreateButton(p -> {
|
||||
dataService.mergeBugs(selectedItems.stream().map(VBug::getBug).collect(Collectors.toList()), titles.getSelectedItem().orElseThrow(IllegalStateException::new));
|
||||
bugs.deselectAll();
|
||||
bugs.getDataProvider().refreshAll();
|
||||
}, true).show();
|
||||
} else {
|
||||
Notification.show("Please select at least two bugs", Notification.Type.ERROR_MESSAGE);
|
||||
Notification.show(i18n.get(Messages.ONLY_ONE_BUG_SELECTED), Notification.Type.ERROR_MESSAGE);
|
||||
}
|
||||
});
|
||||
}, i18n, Messages.MERGE_BUGS);
|
||||
header.addComponent(merge);
|
||||
header.addComponent(hideSolved);
|
||||
header.setComponentAlignment(hideSolved, Alignment.MIDDLE_RIGHT);
|
||||
bugs.addColumn(VBug::getReportCount,QReport.report.count(), "Reports");
|
||||
bugs.sort(bugs.addColumn(VBug::getLastReport, new TimeSpanRenderer(), QReport.report.date.max(), "Latest Report"), SortDirection.DESCENDING);
|
||||
bugs.addColumn(VBug::getHighestVersionCode, QReport.report.stacktrace.version.code.max(), "Latest Version");
|
||||
bugs.addColumn(VBug::getUserCount, QReport.report.installationId.countDistinct(), "Affected Users");
|
||||
bugs.addColumn(bug -> bug.getBug().getTitle(), QBug.bug.title, "Title").setExpandRatio(1).setMinimumWidthFromContent(false);
|
||||
bugs.addColumn(VBug::getReportCount, QReport.report.count(), Messages.REPORTS);
|
||||
bugs.sort(bugs.addColumn(VBug::getLastReport, new TimeSpanRenderer(), QReport.report.date.max(), Messages.LATEST_REPORT), SortDirection.DESCENDING);
|
||||
bugs.addColumn(VBug::getHighestVersionCode, QReport.report.stacktrace.version.code.max(), Messages.LATEST_VERSION);
|
||||
bugs.addColumn(VBug::getUserCount, QReport.report.installationId.countDistinct(), Messages.AFFECTED_USERS);
|
||||
bugs.addColumn(bug -> bug.getBug().getTitle(), QBug.bug.title, Messages.TITLE).setExpandRatio(1).setMinimumWidthFromContent(false);
|
||||
bugs.addOnClickNavigation(navigationManager, com.faendir.acra.ui.view.bug.BugView.class, bugItemClick -> String.valueOf(bugItemClick.getItem().getBug().getId()));
|
||||
bugs.addColumn(bug -> new MyCheckBox(bug.getBug().isSolved(), SecurityUtils.hasPermission(app, Permission.Level.EDIT), e -> dataService.setBugSolved(bug.getBug(), e.getValue())),
|
||||
new ComponentRenderer(), QBug.bug.solved, "Solved");
|
||||
bugs.addColumn(bug -> new MyCheckBox(bug.getBug().isSolved(),
|
||||
SecurityUtils.hasPermission(app, Permission.Level.EDIT),
|
||||
e -> dataService.setBugSolved(bug.getBug(), e.getValue())), new ComponentRenderer(), QBug.bug.solved, Messages.SOLVED);
|
||||
layout.addComponent(bugs);
|
||||
layout.setExpandRatio(bugs, 1);
|
||||
layout.setSizeFull();
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
|
||||
package com.faendir.acra.ui.view.app.tabs;
|
||||
|
||||
import com.faendir.acra.i18n.Messages;
|
||||
import com.faendir.acra.model.App;
|
||||
import com.faendir.acra.service.AvatarService;
|
||||
import com.faendir.acra.service.DataService;
|
||||
|
@ -26,6 +27,7 @@ import com.vaadin.spring.annotation.ViewScope;
|
|||
import com.vaadin.ui.Component;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.lang.NonNull;
|
||||
import org.vaadin.spring.i18n.I18N;
|
||||
|
||||
/**
|
||||
* @author Lukas
|
||||
|
@ -36,23 +38,30 @@ import org.springframework.lang.NonNull;
|
|||
public class ReportTab implements AppTab {
|
||||
@NonNull private final DataService dataService;
|
||||
@NonNull private final AvatarService avatarService;
|
||||
@NonNull private final I18N i18n;
|
||||
|
||||
@Autowired
|
||||
public ReportTab(@NonNull DataService dataService, @NonNull AvatarService avatarService) {
|
||||
public ReportTab(@NonNull DataService dataService, @NonNull AvatarService avatarService, @NonNull I18N i18n) {
|
||||
this.dataService = dataService;
|
||||
this.avatarService = avatarService;
|
||||
this.i18n = i18n;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Component createContent(@NonNull App app, @NonNull NavigationManager navigationManager) {
|
||||
Component content = new ReportList(app, navigationManager, avatarService, dataService::delete, dataService.getReportProvider(app));
|
||||
Component content = new ReportList(app, navigationManager, avatarService, dataService::delete, dataService.getReportProvider(app), i18n);
|
||||
content.setSizeFull();
|
||||
return content;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getCaption() {
|
||||
return ReportList.CAPTION;
|
||||
return i18n.get(Messages.REPORTS);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getId() {
|
||||
return "report";
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
|
||||
package com.faendir.acra.ui.view.app.tabs;
|
||||
|
||||
import com.faendir.acra.i18n.Messages;
|
||||
import com.faendir.acra.model.App;
|
||||
import com.faendir.acra.model.QReport;
|
||||
import com.faendir.acra.service.DataService;
|
||||
|
@ -28,6 +29,7 @@ import com.vaadin.ui.Panel;
|
|||
import com.vaadin.ui.themes.AcraTheme;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.lang.NonNull;
|
||||
import org.vaadin.spring.i18n.I18N;
|
||||
|
||||
/**
|
||||
* @author Lukas
|
||||
|
@ -37,15 +39,17 @@ import org.springframework.lang.NonNull;
|
|||
@ViewScope
|
||||
public class StatisticsTab implements AppTab {
|
||||
@NonNull private final DataService dataService;
|
||||
@NonNull private final I18N i18n;
|
||||
|
||||
@Autowired
|
||||
public StatisticsTab(@NonNull DataService dataService) {
|
||||
public StatisticsTab(@NonNull DataService dataService, @NonNull I18N i18n) {
|
||||
this.dataService = dataService;
|
||||
this.i18n = i18n;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Component createContent(@NonNull App app, @NonNull NavigationManager navigationManager) {
|
||||
Panel root = new Panel(new Statistics(QReport.report.stacktrace.bug.app.eq(app), dataService));
|
||||
Panel root = new Panel(new Statistics(QReport.report.stacktrace.bug.app.eq(app), dataService, i18n));
|
||||
root.setSizeFull();
|
||||
root.addStyleNames(AcraTheme.NO_BACKGROUND, AcraTheme.NO_BORDER);
|
||||
return root;
|
||||
|
@ -53,7 +57,12 @@ public class StatisticsTab implements AppTab {
|
|||
|
||||
@Override
|
||||
public String getCaption() {
|
||||
return "Statistics";
|
||||
return i18n.get(Messages.STATISTICS);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getId() {
|
||||
return "statistics";
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -15,6 +15,10 @@
|
|||
*/
|
||||
package com.faendir.acra.ui.view.app.tabs.panels;
|
||||
|
||||
import com.faendir.acra.i18n.I18nButton;
|
||||
import com.faendir.acra.i18n.I18nLabel;
|
||||
import com.faendir.acra.i18n.I18nSlider;
|
||||
import com.faendir.acra.i18n.Messages;
|
||||
import com.faendir.acra.model.App;
|
||||
import com.faendir.acra.model.QReport;
|
||||
import com.faendir.acra.service.DataService;
|
||||
|
@ -33,13 +37,12 @@ import com.vaadin.ui.Button;
|
|||
import com.vaadin.ui.ComboBox;
|
||||
import com.vaadin.ui.Component;
|
||||
import com.vaadin.ui.HorizontalLayout;
|
||||
import com.vaadin.ui.Label;
|
||||
import com.vaadin.ui.Slider;
|
||||
import com.vaadin.ui.VerticalLayout;
|
||||
import com.vaadin.ui.themes.AcraTheme;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.lang.NonNull;
|
||||
import org.vaadin.risto.stepper.IntStepper;
|
||||
import org.vaadin.spring.i18n.I18N;
|
||||
|
||||
/**
|
||||
* @author lukas
|
||||
|
@ -49,31 +52,29 @@ import org.vaadin.risto.stepper.IntStepper;
|
|||
@ViewScope
|
||||
public class DangerPanel implements AdminPanel {
|
||||
@NonNull private final DataService dataService;
|
||||
@NonNull private final I18N i18n;
|
||||
|
||||
@Autowired
|
||||
public DangerPanel(@NonNull DataService dataService) {
|
||||
public DangerPanel(@NonNull DataService dataService, @NonNull I18N i18n) {
|
||||
this.dataService = dataService;
|
||||
this.i18n = i18n;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Component createContent(@NonNull App app, @NonNull NavigationManager navigationManager) {
|
||||
Button configButton = new Button("Create new ACRA Configuration",
|
||||
e -> new Popup().setTitle("Confirm")
|
||||
.addComponent(new Label("Are you sure you want to create a new ACRA configuration?<br>The existing configuration will be invalidated", ContentMode.HTML))
|
||||
.addYesNoButtons(popup -> popup.clear().addComponent(new ConfigurationLabel(dataService.recreateReporterUser(app))).addCloseButton().show())
|
||||
.show());
|
||||
Button configButton = new I18nButton(e -> new Popup(i18n, Messages.CONFIRM).addComponent(new I18nLabel(ContentMode.HTML, i18n, Messages.NEW_ACRA_CONFIG_CONFIRM))
|
||||
.addYesNoButtons(popup -> popup.clear().addComponent(new ConfigurationLabel(dataService.recreateReporterUser(app), i18n)).addCloseButton().show())
|
||||
.show(), i18n, Messages.NEW_ACRA_CONFIG);
|
||||
configButton.setWidth(100, Sizeable.Unit.PERCENTAGE);
|
||||
Button matchingButton = new Button("Configure bug matching", e -> {
|
||||
Button matchingButton = new I18nButton(e -> {
|
||||
App.Configuration configuration = app.getConfiguration();
|
||||
Slider score = new Slider("Minimum score", 0, 100);
|
||||
I18nSlider score = new I18nSlider(0, 100, i18n, Messages.MIN_SCORE);
|
||||
score.setValue((double) configuration.getMinScore());
|
||||
new Popup().addValidatedField(ValidatedField.of(score), true)
|
||||
.addComponent(new Label("Are you sure you want to save this configuration? " + "All bugs will be checked for merging, which may take some time. "
|
||||
+ "Note that a higher minimum score does not unmerge already merged bugs."))
|
||||
.addYesNoButtons(p -> dataService.changeConfiguration(app,
|
||||
new App.Configuration(score.getValue().intValue())), true)
|
||||
new Popup(i18n, Messages.CONFIRM).addValidatedField(ValidatedField.of(score), true)
|
||||
.addComponent(new I18nLabel(i18n, Messages.NEW_BUG_CONFIG_CONFIRM))
|
||||
.addYesNoButtons(p -> dataService.changeConfiguration(app, new App.Configuration(score.getValue().intValue())), true)
|
||||
.show();
|
||||
});
|
||||
}, i18n, Messages.NEW_BUG_CONFIG);
|
||||
matchingButton.setWidth(100, Sizeable.Unit.PERCENTAGE);
|
||||
IntStepper age = new IntStepper();
|
||||
age.setValue(30);
|
||||
|
@ -83,7 +84,10 @@ public class DangerPanel implements AdminPanel {
|
|||
purgeAge.setDefaultComponentAlignment(Alignment.MIDDLE_CENTER);
|
||||
purgeAge.setWidth(100, Sizeable.Unit.PERCENTAGE);
|
||||
purgeAge.addStyleName(AcraTheme.NO_MARGIN);
|
||||
purgeAge.addComponents(new Button("Purge", e -> dataService.deleteReportsOlderThanDays(app, age.getValue())), new Label(" Reports older than "), age, new Label(" Days"));
|
||||
purgeAge.addComponents(new I18nButton(e -> dataService.deleteReportsOlderThanDays(app, age.getValue()), i18n, Messages.PURGE),
|
||||
new I18nLabel(i18n, Messages.REPORTS_OLDER_THAN1),
|
||||
age,
|
||||
new I18nLabel(i18n, Messages.REPORTS_OLDER_THAN2));
|
||||
purgeAge.setExpandRatio(age, 1);
|
||||
ComboBox<Integer> versionBox = new ComboBox<>(null, dataService.getFromReports(QReport.report.stacktrace.bug.app.eq(app), QReport.report.stacktrace.version.code));
|
||||
versionBox.setEmptySelectionAllowed(false);
|
||||
|
@ -92,17 +96,16 @@ public class DangerPanel implements AdminPanel {
|
|||
purgeVersion.setWidth(100, Sizeable.Unit.PERCENTAGE);
|
||||
purgeVersion.setDefaultComponentAlignment(Alignment.MIDDLE_CENTER);
|
||||
purgeVersion.addStyleName(AcraTheme.NO_MARGIN);
|
||||
purgeVersion.addComponents(new Button("Purge", e -> {
|
||||
purgeVersion.addComponents(new I18nButton(e -> {
|
||||
if (versionBox.getValue() != null) {
|
||||
dataService.deleteReportsBeforeVersion(app, versionBox.getValue());
|
||||
}
|
||||
}), new Label(" Reports before Version "), versionBox);
|
||||
}, i18n, Messages.PURGE), new I18nLabel(i18n, Messages.REPORTS_BEFORE_VERSION), versionBox);
|
||||
purgeVersion.setExpandRatio(versionBox, 1);
|
||||
Button deleteButton = new Button("Delete App",
|
||||
e -> new Popup().setTitle("Confirm").addComponent(new Label("Are you sure you want to delete this app and all its associated content?")).addYesNoButtons(popup -> {
|
||||
dataService.delete(app);
|
||||
navigationManager.navigateBack();
|
||||
}, true).show());
|
||||
Button deleteButton = new I18nButton(e -> new Popup(i18n, Messages.CONFIRM).addComponent(new I18nLabel(i18n, Messages.DELETE_APP_CONFIRM)).addYesNoButtons(popup -> {
|
||||
dataService.delete(app);
|
||||
navigationManager.navigateBack();
|
||||
}, true).show(), i18n, Messages.DELETE_APP);
|
||||
VerticalLayout layout = new VerticalLayout(configButton, matchingButton, purgeAge, purgeVersion, deleteButton);
|
||||
deleteButton.setWidth(100, Sizeable.Unit.PERCENTAGE);
|
||||
layout.setSizeFull();
|
||||
|
@ -112,7 +115,12 @@ public class DangerPanel implements AdminPanel {
|
|||
|
||||
@Override
|
||||
public String getCaption() {
|
||||
return "Danger Zone";
|
||||
return i18n.get(Messages.DANGER_ZONE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getId() {
|
||||
return "danger-zone";
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -16,6 +16,9 @@
|
|||
|
||||
package com.faendir.acra.ui.view.app.tabs.panels;
|
||||
|
||||
import com.faendir.acra.i18n.I18nButton;
|
||||
import com.faendir.acra.i18n.I18nComboBox;
|
||||
import com.faendir.acra.i18n.Messages;
|
||||
import com.faendir.acra.model.App;
|
||||
import com.faendir.acra.model.QReport;
|
||||
import com.faendir.acra.rest.RestReportInterface;
|
||||
|
@ -33,6 +36,7 @@ import com.vaadin.ui.VerticalLayout;
|
|||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.lang.NonNull;
|
||||
import org.springframework.web.util.UriComponentsBuilder;
|
||||
import org.vaadin.spring.i18n.I18N;
|
||||
|
||||
/**
|
||||
* @author lukas
|
||||
|
@ -42,22 +46,24 @@ import org.springframework.web.util.UriComponentsBuilder;
|
|||
@ViewScope
|
||||
public class ExportPanel implements AdminPanel {
|
||||
@NonNull private final DataService dataService;
|
||||
@NonNull private final I18N i18n;
|
||||
|
||||
@Autowired
|
||||
public ExportPanel(@NonNull DataService dataService) {
|
||||
public ExportPanel(@NonNull DataService dataService, @NonNull I18N i18n) {
|
||||
this.dataService = dataService;
|
||||
this.i18n = i18n;
|
||||
}
|
||||
@Override
|
||||
public Component createContent(@NonNull App app, @NonNull NavigationManager navigationManager) {
|
||||
ComboBox<String> mailBox = new ComboBox<>("By Email Address", dataService.getFromReports(QReport.report.stacktrace.bug.app.eq(app), QReport.report.userEmail));
|
||||
ComboBox<String> mailBox = new I18nComboBox<>(dataService.getFromReports(QReport.report.stacktrace.bug.app.eq(app), QReport.report.userEmail), i18n, Messages.BY_MAIL);
|
||||
mailBox.setEmptySelectionAllowed(true);
|
||||
mailBox.setSizeFull();
|
||||
ComboBox<String> idBox = new ComboBox<>("By Installation ID", dataService.getFromReports(QReport.report.stacktrace.bug.app.eq(app), QReport.report.installationId));
|
||||
ComboBox<String> idBox = new I18nComboBox<>(dataService.getFromReports(QReport.report.stacktrace.bug.app.eq(app), QReport.report.installationId), i18n, Messages.BY_ID);
|
||||
idBox.setEmptySelectionAllowed(true);
|
||||
idBox.setSizeFull();
|
||||
Button download = new Button("Download", e -> {
|
||||
Button download = new I18nButton(e -> {
|
||||
if (idBox.getValue() == null && mailBox.getValue() == null) {
|
||||
Notification.show("Nothing selected", Notification.Type.WARNING_MESSAGE);
|
||||
Notification.show(i18n.get(Messages.NOTHING_SELECTED), Notification.Type.WARNING_MESSAGE);
|
||||
} else {
|
||||
Page page = UI.getCurrent().getPage();
|
||||
page.open(UriComponentsBuilder.fromUri(page.getLocation())
|
||||
|
@ -69,14 +75,19 @@ public class ExportPanel implements AdminPanel {
|
|||
.build()
|
||||
.toUriString(), null);
|
||||
}
|
||||
});
|
||||
}, i18n, Messages.DOWNLOAD);
|
||||
download.setSizeFull();
|
||||
return new VerticalLayout(mailBox, idBox, download);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getCaption() {
|
||||
return "Export";
|
||||
return i18n.get(Messages.EXPORT);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getId() {
|
||||
return "export";
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -16,6 +16,10 @@
|
|||
|
||||
package com.faendir.acra.ui.view.app.tabs.panels;
|
||||
|
||||
import com.faendir.acra.i18n.I18nButton;
|
||||
import com.faendir.acra.i18n.I18nIntStepper;
|
||||
import com.faendir.acra.i18n.I18nLabel;
|
||||
import com.faendir.acra.i18n.Messages;
|
||||
import com.faendir.acra.model.App;
|
||||
import com.faendir.acra.model.Permission;
|
||||
import com.faendir.acra.model.ProguardMapping;
|
||||
|
@ -31,9 +35,7 @@ import com.vaadin.icons.VaadinIcons;
|
|||
import com.vaadin.shared.data.sort.SortDirection;
|
||||
import com.vaadin.spring.annotation.SpringComponent;
|
||||
import com.vaadin.spring.annotation.ViewScope;
|
||||
import com.vaadin.ui.Button;
|
||||
import com.vaadin.ui.Component;
|
||||
import com.vaadin.ui.Label;
|
||||
import com.vaadin.ui.ProgressBar;
|
||||
import com.vaadin.ui.VerticalLayout;
|
||||
import com.vaadin.ui.renderers.ButtonRenderer;
|
||||
|
@ -41,6 +43,7 @@ import com.vaadin.ui.themes.AcraTheme;
|
|||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.lang.NonNull;
|
||||
import org.vaadin.risto.stepper.IntStepper;
|
||||
import org.vaadin.spring.i18n.I18N;
|
||||
|
||||
/**
|
||||
* @author lukas
|
||||
|
@ -50,20 +53,22 @@ import org.vaadin.risto.stepper.IntStepper;
|
|||
@ViewScope
|
||||
public class ProguardPanel implements AdminPanel{
|
||||
@NonNull private final DataService dataService;
|
||||
@NonNull private final I18N i18n;
|
||||
|
||||
@Autowired
|
||||
public ProguardPanel(@NonNull DataService dataService) {
|
||||
public ProguardPanel(@NonNull DataService dataService, @NonNull I18N i18n) {
|
||||
this.dataService = dataService;
|
||||
this.i18n = i18n;
|
||||
}
|
||||
@Override
|
||||
public Component createContent(@NonNull App app, @NonNull NavigationManager navigationManager) {
|
||||
VerticalLayout layout = new VerticalLayout();
|
||||
MyGrid<ProguardMapping> grid = new MyGrid<>(null, dataService.getMappingProvider(app));
|
||||
MyGrid<ProguardMapping> grid = new MyGrid<>(dataService.getMappingProvider(app));
|
||||
grid.setSizeToRows();
|
||||
grid.sort(grid.addColumn(ProguardMapping::getVersionCode, QProguardMapping.proguardMapping.versionCode, "Version"), SortDirection.ASCENDING);
|
||||
grid.sort(grid.addColumn(ProguardMapping::getVersionCode, QProguardMapping.proguardMapping.versionCode, Messages.VERSION), SortDirection.ASCENDING);
|
||||
if (SecurityUtils.hasPermission(app, Permission.Level.EDIT)) {
|
||||
ButtonRenderer<ProguardMapping> renderer = new ButtonRenderer<>(e -> new Popup().setTitle("Confirm")
|
||||
.addComponent(new Label("Are you sure you want to delete the mapping for version " + e.getItem().getVersionCode() + "?"))
|
||||
ButtonRenderer<ProguardMapping> renderer = new ButtonRenderer<>(e -> new Popup(i18n, Messages.CONFIRM)
|
||||
.addComponent(new I18nLabel(i18n, Messages.DELETE_MAPPING_CONFIRM, e.getItem().getVersionCode()))
|
||||
.addYesNoButtons(p -> {
|
||||
dataService.delete(e.getItem());
|
||||
grid.getDataProvider().refreshAll();
|
||||
|
@ -76,30 +81,34 @@ public class ProguardPanel implements AdminPanel{
|
|||
layout.addComponent(grid);
|
||||
layout.addStyleName(AcraTheme.NO_PADDING);
|
||||
if (SecurityUtils.hasPermission(app, Permission.Level.EDIT)) {
|
||||
layout.addComponent(new Button("Add File", e -> {
|
||||
IntStepper version = new IntStepper("Version code");
|
||||
version.setValue(dataService.getMaximumMappingVersion(app).map(i -> i + 1).orElse(1));
|
||||
InMemoryUpload upload = new InMemoryUpload("Mapping file:");
|
||||
layout.addComponent(new I18nButton(e -> {
|
||||
IntStepper version = new I18nIntStepper(dataService.getMaximumMappingVersion(app).map(i -> i + 1).orElse(1), i18n, Messages.VERSION_CODE);
|
||||
InMemoryUpload upload = new InMemoryUpload(i18n, Messages.MAPPING_FILE);
|
||||
ProgressBar progressBar = new ProgressBar();
|
||||
upload.addProgressListener((readBytes, contentLength) -> layout.getUI().access(() -> progressBar.setValue((float) readBytes / contentLength)));
|
||||
new Popup().setTitle("New Mapping Configuration")
|
||||
new Popup(i18n, Messages.NEW_MAPPING)
|
||||
.addComponent(version)
|
||||
.addValidatedField(ValidatedField.of(upload, () -> upload, consumer -> upload.addFinishedListener(event -> consumer.accept(upload)))
|
||||
.addValidator(InMemoryUpload::isUploaded, "Upload failed"))
|
||||
.addValidator(InMemoryUpload::isUploaded, Messages.ERROR_UPLOAD))
|
||||
.addComponent(progressBar)
|
||||
.addCreateButton(popup -> {
|
||||
dataService.store(new ProguardMapping(app, version.getValue(), upload.getUploadedString()));
|
||||
grid.getDataProvider().refreshAll();
|
||||
}, true)
|
||||
.show();
|
||||
}));
|
||||
}, i18n, Messages.NEW_FILE));
|
||||
}
|
||||
return layout;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getCaption() {
|
||||
return "De-Obfuscation";
|
||||
return i18n.get(Messages.DE_OBFUSCATION);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getId() {
|
||||
return "proguard";
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -16,23 +16,21 @@
|
|||
|
||||
package com.faendir.acra.ui.view.base;
|
||||
|
||||
import com.faendir.acra.i18n.I18nLabel;
|
||||
import com.faendir.acra.i18n.Messages;
|
||||
import com.faendir.acra.rest.RestReportInterface;
|
||||
import com.faendir.acra.util.PlainTextUser;
|
||||
import com.faendir.acra.util.Utils;
|
||||
import com.vaadin.shared.ui.ContentMode;
|
||||
import com.vaadin.ui.Label;
|
||||
import org.vaadin.spring.i18n.I18N;
|
||||
|
||||
/**
|
||||
* @author Lukas
|
||||
* @since 18.12.2017
|
||||
*/
|
||||
public class ConfigurationLabel extends Label {
|
||||
public ConfigurationLabel(PlainTextUser user) {
|
||||
super(String.format("Take note of the following ACRA configuration. It cannot be viewed later:<br><code>"
|
||||
+ "@AcraCore(reportFormat = StringFormat.JSON)<br>"
|
||||
+ "@AcraHttpSender(uri = \"%s%s\",<br>"
|
||||
+ "basicAuthLogin = \"%s\",<br>"
|
||||
+ "basicAuthPassword = \"%s\",<br>"
|
||||
+ "httpMethod = HttpSender.Method.POST)<br></code>", Utils.getUrlWithFragment(null), RestReportInterface.REPORT_PATH, user.getUsername(), user.getPlaintextPassword()), ContentMode.HTML);
|
||||
public class ConfigurationLabel extends I18nLabel {
|
||||
public ConfigurationLabel(PlainTextUser user, I18N i18n) {
|
||||
super(i18n, Messages.CONFIGURATION_LABEL, Utils.getUrlWithFragment(null), RestReportInterface.REPORT_PATH, user.getUsername(), user.getPlaintextPassword());
|
||||
setContentMode(ContentMode.HTML);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,26 +16,36 @@
|
|||
|
||||
package com.faendir.acra.ui.view.base;
|
||||
|
||||
import com.faendir.acra.i18n.HasI18n;
|
||||
import com.vaadin.ui.Upload;
|
||||
import org.vaadin.spring.i18n.I18N;
|
||||
import org.vaadin.spring.i18n.support.Translatable;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.util.Locale;
|
||||
|
||||
/**
|
||||
* @author Lukas
|
||||
* @since 19.12.2017
|
||||
*/
|
||||
public class InMemoryUpload extends Upload {
|
||||
public class InMemoryUpload extends Upload implements Translatable, HasI18n {
|
||||
private final I18N i18n;
|
||||
private final String captionId;
|
||||
private final Object[] params;
|
||||
private final ByteArrayOutputStream outputStream;
|
||||
private boolean finished;
|
||||
|
||||
public InMemoryUpload(String caption) {
|
||||
public InMemoryUpload(I18N i18n, String captionId, Object... params) {
|
||||
super();
|
||||
this.i18n = i18n;
|
||||
this.captionId = captionId;
|
||||
this.params = params;
|
||||
outputStream = new ByteArrayOutputStream();
|
||||
finished = false;
|
||||
setCaption(caption);
|
||||
setReceiver((filename, mimeType) -> outputStream);
|
||||
addSucceededListener(event -> finished = true);
|
||||
addFailedListener(event -> finished = false);
|
||||
updateMessageStrings(i18n.getLocale());
|
||||
}
|
||||
|
||||
public boolean isUploaded() {
|
||||
|
@ -45,4 +55,14 @@ public class InMemoryUpload extends Upload {
|
|||
public String getUploadedString() {
|
||||
return outputStream.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateMessageStrings(Locale locale) {
|
||||
setCaption(i18n.get(captionId, locale, params));
|
||||
}
|
||||
|
||||
@Override
|
||||
public I18N getI18n() {
|
||||
return i18n;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,6 +16,8 @@
|
|||
package com.faendir.acra.ui.view.base;
|
||||
|
||||
import com.faendir.acra.dataprovider.QueryDslDataProvider;
|
||||
import com.faendir.acra.i18n.I18nLabel;
|
||||
import com.faendir.acra.i18n.Messages;
|
||||
import com.faendir.acra.model.App;
|
||||
import com.faendir.acra.model.Permission;
|
||||
import com.faendir.acra.model.QReport;
|
||||
|
@ -30,10 +32,10 @@ import com.faendir.acra.util.TimeSpanRenderer;
|
|||
import com.vaadin.icons.VaadinIcons;
|
||||
import com.vaadin.shared.data.sort.SortDirection;
|
||||
import com.vaadin.ui.Grid;
|
||||
import com.vaadin.ui.Label;
|
||||
import com.vaadin.ui.renderers.ButtonRenderer;
|
||||
import com.vaadin.ui.renderers.ImageRenderer;
|
||||
import org.springframework.lang.NonNull;
|
||||
import org.vaadin.spring.i18n.I18N;
|
||||
|
||||
import java.util.function.Consumer;
|
||||
|
||||
|
@ -42,22 +44,20 @@ import java.util.function.Consumer;
|
|||
* @since 14.05.2017
|
||||
*/
|
||||
public class ReportList extends MyGrid<Report> {
|
||||
public static final String CAPTION = "Reports";
|
||||
|
||||
public ReportList(@NonNull App app, @NonNull NavigationManager navigationManager, @NonNull AvatarService avatarService, @NonNull Consumer<Report> reportDeleter, @NonNull QueryDslDataProvider<Report> reportProvider) {
|
||||
super(CAPTION, reportProvider);
|
||||
setId(CAPTION);
|
||||
public ReportList(@NonNull App app, @NonNull NavigationManager navigationManager, @NonNull AvatarService avatarService, @NonNull Consumer<Report> reportDeleter, @NonNull QueryDslDataProvider<Report> reportProvider, I18N i18n) {
|
||||
super(reportProvider, i18n, Messages.REPORTS);
|
||||
setSelectionMode(Grid.SelectionMode.NONE);
|
||||
addColumn(avatarService::getAvatar, new ImageRenderer<>(), QReport.report.installationId, "User");
|
||||
sort(addColumn(Report::getDate, new TimeSpanRenderer(), QReport.report.date, "Date"), SortDirection.DESCENDING);
|
||||
addColumn(report -> report.getStacktrace().getVersion().getCode(), QReport.report.stacktrace.version.code, "App Version");
|
||||
addColumn(Report::getAndroidVersion, QReport.report.androidVersion, "Android Version");
|
||||
addColumn(Report::getPhoneModel, QReport.report.phoneModel, "Device");
|
||||
addColumn(report -> report.getStacktrace().getStacktrace().split("\n", 2)[0], QReport.report.stacktrace.stacktrace, "Stacktrace").setExpandRatio(1)
|
||||
addColumn(avatarService::getAvatar, new ImageRenderer<>(), QReport.report.installationId, Messages.USER);
|
||||
sort(addColumn(Report::getDate, new TimeSpanRenderer(), QReport.report.date, Messages.DATE), SortDirection.DESCENDING);
|
||||
addColumn(report -> report.getStacktrace().getVersion().getCode(), QReport.report.stacktrace.version.code, Messages.APP_VERSION);
|
||||
addColumn(Report::getAndroidVersion, QReport.report.androidVersion, Messages.ANDROID_VERSION);
|
||||
addColumn(Report::getPhoneModel, QReport.report.phoneModel, Messages.DEVICE);
|
||||
addColumn(report -> report.getStacktrace().getStacktrace().split("\n", 2)[0], QReport.report.stacktrace.stacktrace, Messages.STACKTRACE).setExpandRatio(1)
|
||||
.setMinimumWidthFromContent(false);
|
||||
if (SecurityUtils.hasPermission(app, Permission.Level.EDIT)) {
|
||||
ButtonRenderer<Report> renderer = new ButtonRenderer<>(e -> new Popup().setTitle("Confirm")
|
||||
.addComponent(new Label("Are you sure you want to delete this report?"))
|
||||
ButtonRenderer<Report> renderer = new ButtonRenderer<>(e -> new Popup(i18n, Messages.CONFIRM)
|
||||
.addComponent(new I18nLabel(i18n, Messages.DELETE_REPORT_CONFIRM))
|
||||
.addYesNoButtons(p -> {
|
||||
reportDeleter.accept(e.getItem());
|
||||
getDataProvider().refreshAll();
|
||||
|
|
|
@ -29,4 +29,6 @@ public interface ComponentFactory<T> extends Ordered {
|
|||
Component createContent(@NonNull T t, @NonNull NavigationManager navigationManager);
|
||||
|
||||
String getCaption();
|
||||
|
||||
String getId();
|
||||
}
|
||||
|
|
|
@ -13,7 +13,6 @@
|
|||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.faendir.acra.ui.view.base.layout;
|
||||
|
||||
import com.faendir.acra.client.mygrid.GridMiddleClickExtensionConnector;
|
||||
|
@ -30,10 +29,16 @@ import com.vaadin.ui.Grid;
|
|||
import com.vaadin.ui.renderers.AbstractRenderer;
|
||||
import com.vaadin.ui.renderers.TextRenderer;
|
||||
import elemental.json.JsonObject;
|
||||
import org.springframework.data.util.Pair;
|
||||
import org.springframework.lang.NonNull;
|
||||
import org.vaadin.spring.i18n.I18N;
|
||||
import org.vaadin.spring.i18n.support.Translatable;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.EventObject;
|
||||
import java.util.HashMap;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.function.Function;
|
||||
|
||||
|
@ -41,37 +46,58 @@ import java.util.function.Function;
|
|||
* @author Lukas
|
||||
* @since 14.05.2017
|
||||
*/
|
||||
public class MyGrid<T> extends Composite {
|
||||
public class MyGrid<T> extends Composite implements Translatable {
|
||||
private final I18N i18n;
|
||||
private final String captionId;
|
||||
private final Object[] params;
|
||||
private final Map<Grid.Column<T, ?>, Pair<String, Object[]>> columnCaptions;
|
||||
private final ExposingGrid<T> grid;
|
||||
private final QueryDslDataProvider<T> dataProvider;
|
||||
|
||||
public MyGrid(String caption, QueryDslDataProvider<T> dataProvider) {
|
||||
public MyGrid(QueryDslDataProvider<T> dataProvider) {
|
||||
this(dataProvider, null, null);
|
||||
}
|
||||
|
||||
public MyGrid(QueryDslDataProvider<T> dataProvider, I18N i18n, String captionId, Object... params) {
|
||||
this.dataProvider = dataProvider;
|
||||
grid = new ExposingGrid<>(caption, dataProvider);
|
||||
grid = new ExposingGrid<>(dataProvider);
|
||||
setCompositionRoot(grid);
|
||||
setSizeFull();
|
||||
MiddleClickExtension.extend(this);
|
||||
this.i18n = i18n;
|
||||
this.captionId = captionId;
|
||||
columnCaptions = new HashMap<>();
|
||||
this.params = params;
|
||||
if (i18n != null) {
|
||||
updateMessageStrings(i18n.getLocale());
|
||||
}
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public <R> Grid.Column<T, R> addColumn(@NonNull ValueProvider<T, R> valueProvider, @NonNull String caption) {
|
||||
return addColumn(valueProvider, new TextRenderer(), caption);
|
||||
public <R> Grid.Column<T, R> addColumn(@NonNull ValueProvider<T, R> valueProvider, @NonNull String captionId, Object... params) {
|
||||
Grid.Column<T, R> column = addColumn(valueProvider, new TextRenderer(), i18n.get(captionId, params));
|
||||
columnCaptions.put(column, Pair.of(captionId, params));
|
||||
return column;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public <R> Grid.Column<T, R> addColumn(@NonNull ValueProvider<T, R> valueProvider, @NonNull AbstractRenderer<? super T, ? super R> renderer, @NonNull String caption) {
|
||||
return grid.addColumn(valueProvider, renderer).setCaption(caption).setSortable(false);
|
||||
public <R> Grid.Column<T, R> addColumn(@NonNull ValueProvider<T, R> valueProvider, @NonNull AbstractRenderer<? super T, ? super R> renderer, @NonNull String captionId, Object... params) {
|
||||
Grid.Column<T, R> column = addColumn(valueProvider, renderer).setCaption(i18n.get(captionId, params));
|
||||
columnCaptions.put(column, Pair.of(captionId, params));
|
||||
return column;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public <R> Grid.Column<T, R> addColumn(@NonNull ValueProvider<T, R> valueProvider, @NonNull Expression<? extends Comparable> sort, @NonNull String caption) {
|
||||
return addColumn(valueProvider, new TextRenderer(), sort, caption);
|
||||
public <R> Grid.Column<T, R> addColumn(@NonNull ValueProvider<T, R> valueProvider, @NonNull Expression<? extends Comparable> sort, @NonNull String captionId, Object... params) {
|
||||
return addColumn(valueProvider, new TextRenderer(), sort, captionId, params);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public <R> Grid.Column<T, R> addColumn(@NonNull ValueProvider<T, R> valueProvider, @NonNull AbstractRenderer<? super T, ? super R> renderer,
|
||||
@NonNull Expression<? extends Comparable> sort, @NonNull String caption) {
|
||||
return grid.addColumn(valueProvider, renderer).setId(dataProvider.addSortable(sort)).setCaption(caption);
|
||||
@NonNull Expression<? extends Comparable> sort, @NonNull String captionId, Object... params) {
|
||||
Grid.Column<T, R> column = grid.addColumn(valueProvider, renderer).setId(dataProvider.addSortable(sort)).setCaption(i18n.get(captionId, params));
|
||||
columnCaptions.put(column, Pair.of(captionId, params));
|
||||
return column;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
|
@ -116,8 +142,7 @@ public class MyGrid<T> extends Composite {
|
|||
grid.deselectAll();
|
||||
}
|
||||
|
||||
public void addOnClickNavigation(@NonNull NavigationManager navigationManager, Class<? extends BaseView> namedView,
|
||||
Function<Grid.ItemClick<T>, String> parameterGetter) {
|
||||
public void addOnClickNavigation(@NonNull NavigationManager navigationManager, Class<? extends BaseView> namedView, Function<Grid.ItemClick<T>, String> parameterGetter) {
|
||||
grid.addItemClickListener(e -> {
|
||||
boolean newTab = e.getMouseEventDetails().getButton() == MouseEventDetails.MouseButton.MIDDLE || e.getMouseEventDetails().isCtrlKey();
|
||||
navigationManager.navigateTo(namedView, parameterGetter.apply(e), newTab);
|
||||
|
@ -128,6 +153,12 @@ public class MyGrid<T> extends Composite {
|
|||
return dataProvider;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateMessageStrings(Locale locale) {
|
||||
setCaption(i18n.get(captionId, locale, params));
|
||||
columnCaptions.forEach((column, caption) -> column.setCaption(i18n.get(caption.getFirst(), locale, caption.getSecond())));
|
||||
}
|
||||
|
||||
public static class MiddleClickExtension<T> extends Grid.AbstractGridExtension<T> {
|
||||
private MiddleClickExtension(MyGrid<T> myGrid) {
|
||||
ExposingGrid<T> grid = myGrid.grid;
|
||||
|
@ -161,8 +192,8 @@ public class MyGrid<T> extends Composite {
|
|||
}
|
||||
|
||||
private static class ExposingGrid<T> extends Grid<T> {
|
||||
ExposingGrid(String caption, DataProvider<T, ?> dataProvider) {
|
||||
super(caption, dataProvider);
|
||||
ExposingGrid(DataProvider<T, ?> dataProvider) {
|
||||
super(dataProvider);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -29,10 +29,6 @@ import org.springframework.lang.NonNull;
|
|||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Spliterator;
|
||||
import java.util.Spliterators;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.StreamSupport;
|
||||
|
||||
/**
|
||||
|
@ -52,27 +48,14 @@ public class MyTabSheet<T> extends TabSheet {
|
|||
MiddleClickExtension.extend(this);
|
||||
}
|
||||
|
||||
public List<String> getCaptions() {
|
||||
return StreamSupport.stream(Spliterators.spliterator(iterator(), getComponentCount(), Spliterator.ORDERED), false).map(Component::getCaption).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
public void addTab(ComponentFactory<T> tab) {
|
||||
TabWrapper<T> wrapper = new TabWrapper<>(t, navigationManager, tab);
|
||||
addComponent(wrapper);
|
||||
addSelectedTabChangeListener(wrapper);
|
||||
}
|
||||
|
||||
public void setInitialTab(String caption) {
|
||||
StreamSupport.stream(spliterator(), false).filter(c -> c.getCaption().equals(caption)).findAny().ifPresent(component -> {
|
||||
if (getSelectedTab() == component && component instanceof SelectedTabChangeListener) {
|
||||
((SelectedTabChangeListener) component).selectedTabChange(new SelectedTabChangeEvent(this, true));
|
||||
}
|
||||
setSelectedTab(component);
|
||||
});
|
||||
}
|
||||
|
||||
public void setFirstTabAsInitialTab() {
|
||||
Component component = getTab(0).getComponent();
|
||||
public void guessInitialTab(String id) {
|
||||
Component component = StreamSupport.stream(spliterator(), false).filter(c -> c.getId().equals(id)).findAny().orElseGet(() -> getTab(0).getComponent());
|
||||
if (getSelectedTab() == component && component instanceof SelectedTabChangeListener) {
|
||||
((SelectedTabChangeListener) component).selectedTabChange(new SelectedTabChangeEvent(this, true));
|
||||
}
|
||||
|
|
|
@ -16,6 +16,8 @@
|
|||
|
||||
package com.faendir.acra.ui.view.base.popup;
|
||||
|
||||
import com.faendir.acra.i18n.I18nButton;
|
||||
import com.faendir.acra.i18n.Messages;
|
||||
import com.vaadin.ui.Button;
|
||||
import com.vaadin.ui.Component;
|
||||
import com.vaadin.ui.FormLayout;
|
||||
|
@ -25,10 +27,13 @@ import com.vaadin.ui.Window;
|
|||
import com.vaadin.ui.themes.AcraTheme;
|
||||
import org.springframework.data.util.Pair;
|
||||
import org.springframework.lang.NonNull;
|
||||
import org.vaadin.spring.i18n.I18N;
|
||||
import org.vaadin.spring.i18n.support.Translatable;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
|
@ -36,21 +41,22 @@ import java.util.function.Consumer;
|
|||
* @author Lukas
|
||||
* @since 19.12.2017
|
||||
*/
|
||||
public class Popup extends Window {
|
||||
public class Popup extends Window implements Translatable {
|
||||
private final List<Component> components;
|
||||
private final Map<ValidatedField<?, ?>, Pair<Boolean, ValidatedField.Listener>> fields;
|
||||
private final List<Button> buttons;
|
||||
private final I18N i18n;
|
||||
private final String captionId;
|
||||
private final Object[] params;
|
||||
|
||||
public Popup() {
|
||||
public Popup(I18N i18n, String captionId, Object... params) {
|
||||
this.i18n = i18n;
|
||||
this.captionId = captionId;
|
||||
this.params = params;
|
||||
components = new ArrayList<>();
|
||||
fields = new HashMap<>();
|
||||
buttons = new ArrayList<>();
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public Popup setTitle(@NonNull String title) {
|
||||
super.setCaption(title);
|
||||
return this;
|
||||
updateMessageStrings(i18n.getLocale());
|
||||
}
|
||||
|
||||
@NonNull
|
||||
|
@ -60,18 +66,18 @@ public class Popup extends Window {
|
|||
|
||||
@NonNull
|
||||
public Popup addCreateButton(@NonNull Consumer<Popup> onCreateAction, boolean closeAfter) {
|
||||
buttons.add(new Button("Create", event -> {
|
||||
buttons.add(new I18nButton(event -> {
|
||||
onCreateAction.accept(this);
|
||||
if (closeAfter) {
|
||||
close();
|
||||
}
|
||||
}));
|
||||
}, i18n, Messages.CREATE));
|
||||
return this;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public Popup addCloseButton() {
|
||||
buttons.add(new Button("Close", event -> close()));
|
||||
buttons.add(new I18nButton(event -> close(), i18n, Messages.CLOSE));
|
||||
return this;
|
||||
}
|
||||
|
||||
|
@ -82,13 +88,13 @@ public class Popup extends Window {
|
|||
|
||||
@NonNull
|
||||
public Popup addYesNoButtons(@NonNull Consumer<Popup> onYesAction, boolean closeAfter) {
|
||||
buttons.add(new Button("Yes", event -> {
|
||||
buttons.add(new I18nButton(event -> {
|
||||
onYesAction.accept(this);
|
||||
if (closeAfter) {
|
||||
close();
|
||||
}
|
||||
}));
|
||||
buttons.add(new Button("No", event -> close()));
|
||||
}, i18n, Messages.YES));
|
||||
buttons.add(new I18nButton(event -> close(), i18n, Messages.NO));
|
||||
return this;
|
||||
}
|
||||
|
||||
|
@ -147,4 +153,9 @@ public class Popup extends Window {
|
|||
boolean valid = fields.values().stream().map(Pair::getFirst).reduce(Boolean::logicalAnd).orElse(true);
|
||||
buttons.forEach(button -> button.setEnabled(valid));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateMessageStrings(Locale locale) {
|
||||
setCaption(i18n.get(captionId, locale, params));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,10 +16,12 @@
|
|||
|
||||
package com.faendir.acra.ui.view.base.popup;
|
||||
|
||||
import com.faendir.acra.i18n.HasI18n;
|
||||
import com.vaadin.server.UserError;
|
||||
import com.vaadin.ui.AbstractComponent;
|
||||
import com.vaadin.ui.AbstractField;
|
||||
import org.springframework.lang.NonNull;
|
||||
import org.vaadin.spring.i18n.I18N;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
|
@ -36,29 +38,31 @@ import java.util.function.Supplier;
|
|||
public class ValidatedField<V, T extends AbstractComponent> {
|
||||
private final T field;
|
||||
private final Supplier<V> valueSupplier;
|
||||
private final I18N i18n;
|
||||
private final Map<Function<V, Boolean>, String> validators;
|
||||
private final List<Listener> listeners;
|
||||
private boolean valid;
|
||||
|
||||
private ValidatedField(T field, Supplier<V> valueSupplier, Consumer<Consumer<V>> listenerRegistration) {
|
||||
private ValidatedField(T field, Supplier<V> valueSupplier, Consumer<Consumer<V>> listenerRegistration, I18N i18n) {
|
||||
this.field = field;
|
||||
this.valueSupplier = valueSupplier;
|
||||
this.i18n = i18n;
|
||||
this.validators = new HashMap<>();
|
||||
this.listeners = new ArrayList<>();
|
||||
this.valid = false;
|
||||
listenerRegistration.accept(this::validate);
|
||||
}
|
||||
|
||||
public static <V, T extends AbstractField<V>> ValidatedField<V, T> of(T field) {
|
||||
return new ValidatedField<>(field, field::getValue, vConsumer -> field.addValueChangeListener(event -> vConsumer.accept(event.getValue())));
|
||||
public static <V, T extends AbstractField<V> & HasI18n> ValidatedField<V, T> of(T field) {
|
||||
return new ValidatedField<>(field, field::getValue, vConsumer -> field.addValueChangeListener(event -> vConsumer.accept(event.getValue())), field.getI18n());
|
||||
}
|
||||
|
||||
public static <V, T extends AbstractComponent> ValidatedField<V, T> of(T field, Supplier<V> valueSupplier, Consumer<Consumer<V>> listenerRegistration) {
|
||||
return new ValidatedField<>(field, valueSupplier, listenerRegistration);
|
||||
public static <V, T extends AbstractComponent & HasI18n> ValidatedField<V, T> of(T field, Supplier<V> valueSupplier, Consumer<Consumer<V>> listenerRegistration) {
|
||||
return new ValidatedField<>(field, valueSupplier, listenerRegistration, field.getI18n());
|
||||
}
|
||||
|
||||
public ValidatedField<V, T> addValidator(Function<V, Boolean> validator, String errorMessage) {
|
||||
validators.put(validator, errorMessage);
|
||||
public ValidatedField<V, T> addValidator(Function<V, Boolean> validator, String errorMessageId) {
|
||||
validators.put(validator, errorMessageId);
|
||||
return this;
|
||||
}
|
||||
|
||||
|
@ -76,7 +80,7 @@ public class ValidatedField<V, T extends AbstractComponent> {
|
|||
field.setComponentError(null);
|
||||
return true;
|
||||
} else {
|
||||
field.setComponentError(new UserError(entry.getValue()));
|
||||
field.setComponentError(new UserError(i18n.get(entry.getValue())));
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
|
|
@ -13,9 +13,9 @@
|
|||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.faendir.acra.ui.view.base.statistics;
|
||||
|
||||
import com.faendir.acra.i18n.HasI18n;
|
||||
import com.vaadin.ui.Composite;
|
||||
import com.vaadin.ui.Panel;
|
||||
import com.vaadin.ui.UI;
|
||||
|
@ -23,26 +23,41 @@ import com.vaadin.ui.themes.AcraTheme;
|
|||
import org.jfree.chart.JFreeChart;
|
||||
import org.springframework.lang.NonNull;
|
||||
import org.vaadin.addon.JFreeChartWrapper;
|
||||
import org.vaadin.spring.i18n.I18N;
|
||||
import org.vaadin.spring.i18n.support.Translatable;
|
||||
|
||||
import java.awt.*;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @author lukas
|
||||
* @since 01.06.18
|
||||
*/
|
||||
abstract class Chart<T> extends Composite {
|
||||
abstract class Chart<T> extends Composite implements Translatable, HasI18n {
|
||||
private final Panel panel;
|
||||
private final I18N i18n;
|
||||
private final String captionId;
|
||||
private final Object[] params;
|
||||
private JFreeChart chart;
|
||||
|
||||
Chart(@NonNull String caption) {
|
||||
Chart(I18N i18n, String captionId, Object... params) {
|
||||
this.i18n = i18n;
|
||||
this.captionId = captionId;
|
||||
this.params = params;
|
||||
panel = new Panel();
|
||||
panel.setCaption(caption);
|
||||
panel.addStyleName(AcraTheme.NO_BACKGROUND);
|
||||
setCompositionRoot(panel);
|
||||
updateMessageStrings(i18n.getLocale());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateMessageStrings(Locale locale) {
|
||||
setCaption(i18n.get(captionId, locale, params));
|
||||
}
|
||||
|
||||
public void setContent(@NonNull Map<T, Long> map) {
|
||||
JFreeChart chart = createChart(map);
|
||||
chart = createChart(map);
|
||||
JFreeChartWrapper content = new JFreeChartWrapper(chart);
|
||||
content.setWidth(100, Unit.PERCENTAGE);
|
||||
content.setHeight(100, Unit.PERCENTAGE);
|
||||
|
@ -54,4 +69,13 @@ abstract class Chart<T> extends Composite {
|
|||
}
|
||||
|
||||
protected abstract JFreeChart createChart(@NonNull Map<T, Long> map);
|
||||
|
||||
protected JFreeChart getChart() {
|
||||
return chart;
|
||||
}
|
||||
|
||||
@Override
|
||||
public I18N getI18n() {
|
||||
return i18n;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -24,6 +24,7 @@ import org.jfree.chart.plot.PiePlot;
|
|||
import org.jfree.chart.util.SortOrder;
|
||||
import org.jfree.data.general.DefaultPieDataset;
|
||||
import org.springframework.lang.NonNull;
|
||||
import org.vaadin.spring.i18n.I18N;
|
||||
|
||||
import java.awt.*;
|
||||
import java.util.List;
|
||||
|
@ -34,8 +35,8 @@ import java.util.Map;
|
|||
* @since 01.06.18
|
||||
*/
|
||||
class PieChart extends Chart<String> {
|
||||
PieChart(@NonNull String caption) {
|
||||
super(caption);
|
||||
PieChart(I18N i18n, String captionId, Object... params) {
|
||||
super(i18n, captionId, params);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
*/
|
||||
package com.faendir.acra.ui.view.base.statistics;
|
||||
|
||||
import com.faendir.acra.i18n.I18nCheckBox;
|
||||
import com.faendir.acra.service.DataService;
|
||||
import com.querydsl.core.types.Expression;
|
||||
import com.querydsl.core.types.dsl.BooleanExpression;
|
||||
|
@ -28,6 +29,7 @@ import com.vaadin.ui.ComboBox;
|
|||
import com.vaadin.ui.Component;
|
||||
import com.vaadin.ui.ComponentContainer;
|
||||
import org.vaadin.risto.stepper.IntStepper;
|
||||
import org.vaadin.spring.i18n.I18N;
|
||||
|
||||
import java.time.ZonedDateTime;
|
||||
import java.time.temporal.ChronoUnit;
|
||||
|
@ -46,9 +48,9 @@ class Property<F, C extends Component & HasValue<F>, T> {
|
|||
private final Chart<T> chart;
|
||||
private final Expression<T> select;
|
||||
|
||||
private Property(String filterText, C filterComponent, Function<F, BooleanExpression> filter, Chart<T> chart, DataService dataService, Expression<T> select) {
|
||||
private Property(C filterComponent, Function<F, BooleanExpression> filter, Chart<T> chart, DataService dataService, Expression<T> select, I18N i18n, String filterTextId) {
|
||||
this.dataService = dataService;
|
||||
this.checkBox = new CheckBox(filterText);
|
||||
this.checkBox = new I18nCheckBox(i18n, filterTextId);
|
||||
this.filterComponent = filterComponent;
|
||||
this.filter = filter;
|
||||
this.chart = chart;
|
||||
|
@ -85,22 +87,23 @@ class Property<F, C extends Component & HasValue<F>, T> {
|
|||
this.expression = expression;
|
||||
}
|
||||
|
||||
Property<?, ?, ?> createStringProperty(String filterText, String chartTitle, ComparableExpressionBase<String> stringExpression) {
|
||||
Property<?, ?, ?> createStringProperty(ComparableExpressionBase<String> stringExpression, I18N i18n, String filterTextId, String chartTitleId) {
|
||||
ComboBox<String> comboBox = new ComboBox<>(null, dataService.getFromReports(expression, stringExpression));
|
||||
comboBox.setEmptySelectionAllowed(false);
|
||||
return new Property<>(filterText, comboBox, stringExpression::eq, new PieChart(chartTitle), dataService, stringExpression);
|
||||
return new Property<>(comboBox, stringExpression::eq, new PieChart(i18n, chartTitleId), dataService, stringExpression, i18n, filterTextId);
|
||||
}
|
||||
|
||||
Property<?, ?, ?> createAgeProperty(String filterText, String chartTitle, DateTimePath<ZonedDateTime> dateTimeExpression) {
|
||||
Property<?, ?, ?> createAgeProperty(DateTimePath<ZonedDateTime> dateTimeExpression, I18N i18n, String filterTextId, String chartTitleId) {
|
||||
IntStepper stepper = new IntStepper();
|
||||
stepper.setValue(30);
|
||||
stepper.setMinValue(1);
|
||||
return new Property<>(filterText,
|
||||
stepper,
|
||||
return new Property<>(stepper,
|
||||
days -> dateTimeExpression.after(ZonedDateTime.now().minus(days, ChronoUnit.DAYS)),
|
||||
new TimeChart(chartTitle),
|
||||
new TimeChart(i18n, chartTitleId),
|
||||
dataService,
|
||||
SQLExpressions.date(Date.class, dateTimeExpression));
|
||||
SQLExpressions.date(Date.class, dateTimeExpression),
|
||||
i18n,
|
||||
filterTextId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,6 +16,9 @@
|
|||
|
||||
package com.faendir.acra.ui.view.base.statistics;
|
||||
|
||||
import com.faendir.acra.i18n.I18nButton;
|
||||
import com.faendir.acra.i18n.I18nPanel;
|
||||
import com.faendir.acra.i18n.Messages;
|
||||
import com.faendir.acra.model.QReport;
|
||||
import com.faendir.acra.service.DataService;
|
||||
import com.faendir.acra.ui.view.base.layout.FlexLayout;
|
||||
|
@ -27,6 +30,7 @@ import com.vaadin.ui.GridLayout;
|
|||
import com.vaadin.ui.Panel;
|
||||
import com.vaadin.ui.themes.AcraTheme;
|
||||
import org.vaadin.risto.stepper.IntStepper;
|
||||
import org.vaadin.spring.i18n.I18N;
|
||||
|
||||
import java.awt.*;
|
||||
import java.util.ArrayList;
|
||||
|
@ -43,7 +47,7 @@ public class Statistics extends Composite {
|
|||
private final BooleanExpression baseExpression;
|
||||
private final List<Property<?, ?, ?>> properties;
|
||||
|
||||
public Statistics(BooleanExpression baseExpression, DataService dataService) {
|
||||
public Statistics(BooleanExpression baseExpression, DataService dataService, I18N i18n) {
|
||||
this.baseExpression = baseExpression;
|
||||
properties = new ArrayList<>();
|
||||
GridLayout filterLayout = new GridLayout(2, 1);
|
||||
|
@ -57,21 +61,20 @@ public class Statistics extends Composite {
|
|||
dayStepper.setValue(30);
|
||||
dayStepper.setMinValue(1);
|
||||
Property.Factory factory = new Property.Factory(dataService, baseExpression);
|
||||
properties.add(factory.createAgeProperty("Last X days", "Reports over time", QReport.report.date));
|
||||
properties.add(factory.createStringProperty("Android Version", "Reports per Android Version", QReport.report.androidVersion));
|
||||
properties.add(factory.createStringProperty("App Version", "Reports per App Version", QReport.report.stacktrace.version.name));
|
||||
properties.add(factory.createStringProperty("Phone Model", "Reports per Phone Model", QReport.report.phoneModel));
|
||||
properties.add(factory.createStringProperty("Phone Brand", "Reports per Brand", QReport.report.brand));
|
||||
properties.add(factory.createAgeProperty(QReport.report.date, i18n, Messages.LAST_X_DAYS, Messages.REPORTS_OVER_TIME));
|
||||
properties.add(factory.createStringProperty(QReport.report.androidVersion, i18n, Messages.ANDROID_VERSION, Messages.REPORTS_PER_ANDROID_VERSION));
|
||||
properties.add(factory.createStringProperty(QReport.report.stacktrace.version.name, i18n, Messages.APP_VERSION, Messages.REPORTS_PER_APP_VERSION));
|
||||
properties.add(factory.createStringProperty(QReport.report.phoneModel, i18n, Messages.PHONE_MODEL, Messages.REPORTS_PER_PHONE_MODEL));
|
||||
properties.add(factory.createStringProperty(QReport.report.brand, i18n, Messages.PHONE_BRAND, Messages.REPORTS_PER_BRAND));
|
||||
|
||||
Panel filterPanel = new Panel(filterLayout);
|
||||
Panel filterPanel = new I18nPanel(filterLayout, i18n, Messages.FILTER);
|
||||
filterPanel.addStyleName(AcraTheme.NO_BACKGROUND);
|
||||
filterPanel.setCaption("Filter");
|
||||
FlexLayout layout = new FlexLayout(filterPanel);
|
||||
layout.setWidth(100, Unit.PERCENTAGE);
|
||||
|
||||
properties.forEach(property -> property.addTo(filterLayout, layout));
|
||||
|
||||
Button applyButton = new Button("Apply", e -> update());
|
||||
Button applyButton = new I18nButton(e -> update(), i18n, Messages.APPLY);
|
||||
applyButton.setWidth(100, Unit.PERCENTAGE);
|
||||
filterLayout.space();
|
||||
filterLayout.addComponent(applyButton);
|
||||
|
|
|
@ -13,9 +13,9 @@
|
|||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.faendir.acra.ui.view.base.statistics;
|
||||
|
||||
import com.faendir.acra.i18n.Messages;
|
||||
import org.jfree.chart.ChartFactory;
|
||||
import org.jfree.chart.JFreeChart;
|
||||
import org.jfree.chart.axis.NumberTickUnitSource;
|
||||
|
@ -28,9 +28,11 @@ import org.jfree.data.time.Day;
|
|||
import org.jfree.data.time.TimeSeries;
|
||||
import org.jfree.data.time.TimeSeriesCollection;
|
||||
import org.springframework.lang.NonNull;
|
||||
import org.vaadin.spring.i18n.I18N;
|
||||
|
||||
import java.awt.*;
|
||||
import java.util.Date;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
|
@ -38,16 +40,24 @@ import java.util.Map;
|
|||
* @since 01.06.18
|
||||
*/
|
||||
class TimeChart extends Chart<Date> {
|
||||
TimeChart(@NonNull String caption) {
|
||||
super(caption);
|
||||
TimeChart(I18N i18n, String captionId, Object... params) {
|
||||
super(i18n, captionId, params);
|
||||
}
|
||||
|
||||
@Override
|
||||
public JFreeChart createChart(@NonNull Map<Date, Long> map) {
|
||||
TimeSeries series = new TimeSeries("Date");
|
||||
TimeSeries series = new TimeSeries(getI18n().get(Messages.DATE));
|
||||
series.add(new Day(new Date()), 0);
|
||||
map.forEach((date, count) -> series.addOrUpdate(new Day(date), count));
|
||||
JFreeChart chart = ChartFactory.createXYBarChart("", "Date", true, "Reports", new TimeSeriesCollection(series), PlotOrientation.VERTICAL, false, false, false);
|
||||
JFreeChart chart = ChartFactory.createXYBarChart("",
|
||||
getI18n().get(Messages.DATE),
|
||||
true,
|
||||
getI18n().get(Messages.REPORTS),
|
||||
new TimeSeriesCollection(series),
|
||||
PlotOrientation.VERTICAL,
|
||||
false,
|
||||
false,
|
||||
false);
|
||||
chart.setBackgroundPaint(null);
|
||||
XYPlot plot = chart.getXYPlot();
|
||||
plot.getRangeAxis().setStandardTickUnits(new NumberTickUnitSource(true));
|
||||
|
@ -73,4 +83,13 @@ class TimeChart extends Chart<Date> {
|
|||
barRenderer.setMargin(0.2);
|
||||
return chart;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateMessageStrings(Locale locale) {
|
||||
super.updateMessageStrings(locale);
|
||||
XYPlot plot = getChart().getXYPlot();
|
||||
plot.getDomainAxis().setLabel(getI18n().get(Messages.DATE));
|
||||
plot.getRangeAxis().setLabel(getI18n().get(Messages.REPORTS));
|
||||
markAsDirtyRecursive();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -60,8 +60,7 @@ public class BugView extends ParametrizedBaseView<Pair<Bug, String>> {
|
|||
MyTabSheet<Bug> tabSheet = new MyTabSheet<>(bug, getNavigationManager(), tabs);
|
||||
tabSheet.setSizeFull();
|
||||
tabSheet.addSelectedTabChangeListener(e -> getNavigationManager().updatePageParameters(bug.getId() + "/" + e.getTabSheet().getSelectedTab().getCaption()));
|
||||
if (tabSheet.getCaptions().contains(parameter.getSecond())) tabSheet.setInitialTab(parameter.getSecond());
|
||||
else tabSheet.setFirstTabAsInitialTab();
|
||||
tabSheet.guessInitialTab(parameter.getSecond());
|
||||
Panel root = new Panel(tabSheet);
|
||||
root.setSizeFull();
|
||||
root.addStyleNames( AcraTheme.NO_BORDER, AcraTheme.NO_BACKGROUND, AcraTheme.NO_PADDING, AcraTheme.PADDING_LEFT, AcraTheme.PADDING_RIGHT);
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
*/
|
||||
package com.faendir.acra.ui.view.bug.tabs;
|
||||
|
||||
import com.faendir.acra.i18n.Messages;
|
||||
import com.faendir.acra.model.Bug;
|
||||
import com.faendir.acra.model.Permission;
|
||||
import com.faendir.acra.ui.annotation.RequiresAppPermission;
|
||||
|
@ -24,6 +25,7 @@ import com.vaadin.spring.annotation.SpringComponent;
|
|||
import com.vaadin.spring.annotation.ViewScope;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.lang.NonNull;
|
||||
import org.vaadin.spring.i18n.I18N;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
|
@ -35,15 +37,22 @@ import java.util.List;
|
|||
@SpringComponent("bugAdminTab")
|
||||
@ViewScope
|
||||
public class AdminTab extends PanelFlexTab<Bug> implements BugTab {
|
||||
private final I18N i18n;
|
||||
|
||||
@Autowired
|
||||
public AdminTab(@NonNull List<AdminPanel> panels) {
|
||||
public AdminTab(@NonNull List<AdminPanel> panels, I18N i18n) {
|
||||
super(panels);
|
||||
this.i18n = i18n;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getCaption() {
|
||||
return "Admin";
|
||||
return i18n.get(Messages.ADMIN);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getId() {
|
||||
return "admin";
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
|
||||
package com.faendir.acra.ui.view.bug.tabs;
|
||||
|
||||
import com.faendir.acra.i18n.Messages;
|
||||
import com.faendir.acra.model.Bug;
|
||||
import com.faendir.acra.service.AvatarService;
|
||||
import com.faendir.acra.service.DataService;
|
||||
|
@ -26,6 +27,7 @@ import com.vaadin.spring.annotation.ViewScope;
|
|||
import com.vaadin.ui.Component;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.lang.NonNull;
|
||||
import org.vaadin.spring.i18n.I18N;
|
||||
|
||||
/**
|
||||
* @author Lukas
|
||||
|
@ -36,23 +38,30 @@ import org.springframework.lang.NonNull;
|
|||
public class ReportTab implements BugTab {
|
||||
@NonNull private final DataService dataService;
|
||||
@NonNull private final AvatarService avatarService;
|
||||
@NonNull private final I18N i18n;
|
||||
|
||||
@Autowired
|
||||
public ReportTab(@NonNull DataService dataService, @NonNull AvatarService avatarService) {
|
||||
public ReportTab(@NonNull DataService dataService, @NonNull AvatarService avatarService, @NonNull I18N i18n) {
|
||||
this.dataService = dataService;
|
||||
this.avatarService = avatarService;
|
||||
this.i18n = i18n;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Component createContent(@NonNull Bug bug, @NonNull NavigationManager navigationManager) {
|
||||
Component content = new ReportList(bug.getApp(), navigationManager, avatarService, dataService::delete, dataService.getReportProvider(bug));
|
||||
Component content = new ReportList(bug.getApp(), navigationManager, avatarService, dataService::delete, dataService.getReportProvider(bug), i18n);
|
||||
content.setSizeFull();
|
||||
return content;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getCaption() {
|
||||
return ReportList.CAPTION;
|
||||
return i18n.get(Messages.REPORTS);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getId() {
|
||||
return "report";
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -15,6 +15,8 @@
|
|||
*/
|
||||
package com.faendir.acra.ui.view.bug.tabs;
|
||||
|
||||
import com.faendir.acra.i18n.I18nAccordion;
|
||||
import com.faendir.acra.i18n.Messages;
|
||||
import com.faendir.acra.model.Bug;
|
||||
import com.faendir.acra.model.ProguardMapping;
|
||||
import com.faendir.acra.model.Stacktrace;
|
||||
|
@ -24,11 +26,11 @@ import com.faendir.acra.util.Utils;
|
|||
import com.vaadin.shared.ui.ContentMode;
|
||||
import com.vaadin.spring.annotation.SpringComponent;
|
||||
import com.vaadin.spring.annotation.ViewScope;
|
||||
import com.vaadin.ui.Accordion;
|
||||
import com.vaadin.ui.Component;
|
||||
import com.vaadin.ui.Label;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.lang.NonNull;
|
||||
import org.vaadin.spring.i18n.I18N;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
|
@ -40,22 +42,24 @@ import java.util.Optional;
|
|||
@ViewScope
|
||||
public class StackTraceTab implements BugTab {
|
||||
@NonNull private final DataService dataService;
|
||||
@NonNull private final I18N i18n;
|
||||
|
||||
@Autowired
|
||||
public StackTraceTab(@NonNull DataService dataService) {
|
||||
public StackTraceTab(@NonNull DataService dataService, @NonNull I18N i18n) {
|
||||
this.dataService = dataService;
|
||||
this.i18n = i18n;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Component createContent(@NonNull Bug bug, @NonNull NavigationManager navigationManager) {
|
||||
Accordion accordion = new Accordion();
|
||||
I18nAccordion accordion = new I18nAccordion(i18n);
|
||||
for (Stacktrace stacktrace : dataService.getStacktraces(bug)) {
|
||||
Optional<ProguardMapping> mapping = dataService.findMapping(bug.getApp(), stacktrace.getVersion().getCode());
|
||||
String trace = stacktrace.getStacktrace();
|
||||
if (mapping.isPresent()) {
|
||||
trace = Utils.retrace(trace, mapping.get().getMappings());
|
||||
}
|
||||
accordion.addTab(new Label(trace, ContentMode.PREFORMATTED)).setCaption("Version \"" + stacktrace.getVersion().getName() + "\": " + trace.split("\n", 2)[0]);
|
||||
accordion.addTab(new Label(trace, ContentMode.PREFORMATTED), Messages.STACKTRACE_TITLE, stacktrace.getVersion().getName(), trace.split("\n", 2)[0]);
|
||||
}
|
||||
accordion.setSizeFull();
|
||||
return accordion;
|
||||
|
@ -63,7 +67,12 @@ public class StackTraceTab implements BugTab {
|
|||
|
||||
@Override
|
||||
public String getCaption() {
|
||||
return "Stacktraces";
|
||||
return i18n.get(Messages.STACKTRACES);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getId() {
|
||||
return "stacktrace";
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
|
||||
package com.faendir.acra.ui.view.bug.tabs;
|
||||
|
||||
import com.faendir.acra.i18n.Messages;
|
||||
import com.faendir.acra.model.Bug;
|
||||
import com.faendir.acra.model.QReport;
|
||||
import com.faendir.acra.service.DataService;
|
||||
|
@ -28,6 +29,7 @@ import com.vaadin.ui.Panel;
|
|||
import com.vaadin.ui.themes.AcraTheme;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.lang.NonNull;
|
||||
import org.vaadin.spring.i18n.I18N;
|
||||
|
||||
/**
|
||||
* @author lukas
|
||||
|
@ -37,15 +39,17 @@ import org.springframework.lang.NonNull;
|
|||
@ViewScope
|
||||
public class StatisticsTab implements BugTab {
|
||||
@NonNull private final DataService dataService;
|
||||
@NonNull private final I18N i18n;
|
||||
|
||||
@Autowired
|
||||
public StatisticsTab(@NonNull DataService dataService) {
|
||||
public StatisticsTab(@NonNull DataService dataService, @NonNull I18N i18n) {
|
||||
this.dataService = dataService;
|
||||
this.i18n = i18n;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Component createContent(@NonNull Bug bug, @NonNull NavigationManager navigationManager) {
|
||||
Panel root = new Panel(new Statistics(QReport.report.stacktrace.bug.id.eq(bug.getId()), dataService));
|
||||
Panel root = new Panel(new Statistics(QReport.report.stacktrace.bug.id.eq(bug.getId()), dataService, i18n));
|
||||
root.setSizeFull();
|
||||
root.addStyleNames(AcraTheme.NO_BACKGROUND, AcraTheme.NO_BORDER);
|
||||
return root;
|
||||
|
@ -53,7 +57,12 @@ public class StatisticsTab implements BugTab {
|
|||
|
||||
@Override
|
||||
public String getCaption() {
|
||||
return "Statistics";
|
||||
return i18n.get(Messages.STATISTICS);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getId() {
|
||||
return "statistics";
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -15,6 +15,9 @@
|
|||
*/
|
||||
package com.faendir.acra.ui.view.bug.tabs.panels;
|
||||
|
||||
import com.faendir.acra.i18n.I18nButton;
|
||||
import com.faendir.acra.i18n.I18nLabel;
|
||||
import com.faendir.acra.i18n.Messages;
|
||||
import com.faendir.acra.model.Bug;
|
||||
import com.faendir.acra.service.DataService;
|
||||
import com.faendir.acra.ui.navigation.NavigationManager;
|
||||
|
@ -31,6 +34,7 @@ import com.vaadin.ui.VerticalLayout;
|
|||
import com.vaadin.ui.themes.AcraTheme;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.lang.NonNull;
|
||||
import org.vaadin.spring.i18n.I18N;
|
||||
|
||||
/**
|
||||
* @author lukas
|
||||
|
@ -40,32 +44,39 @@ import org.springframework.lang.NonNull;
|
|||
@ViewScope
|
||||
public class DangerPanel implements AdminPanel {
|
||||
@NonNull private final DataService dataService;
|
||||
private final I18N i18n;
|
||||
|
||||
@Autowired
|
||||
public DangerPanel(@NonNull DataService dataService) {
|
||||
public DangerPanel(@NonNull DataService dataService, I18N i18n) {
|
||||
this.dataService = dataService;
|
||||
this.i18n = i18n;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Component createContent(@NonNull Bug bug, @NonNull NavigationManager navigationManager) {
|
||||
Button unmerge = new Button("Disjoin Bug",
|
||||
e -> new Popup().setTitle("Confirm").addComponent(new Label("Are you sure you want to revert merging this bug group?")).addYesNoButtons(p -> {
|
||||
Button unmerge = new I18nButton(
|
||||
e -> new Popup(i18n, Messages.CONFIRM).addComponent(new I18nLabel(i18n, Messages.UNMERGE_BUG_CONFIRM)).addYesNoButtons(p -> {
|
||||
dataService.unmergeBug(bug);
|
||||
navigationManager.navigateBack();
|
||||
}, true).show());
|
||||
}, true).show(), i18n, Messages.UNMERGE_BUG);
|
||||
unmerge.setWidth(100, Sizeable.Unit.PERCENTAGE);
|
||||
Button delete = new Button("Delete Bug",
|
||||
e -> new Popup().setTitle("Confirm").addComponent(new Label("Are you sure you want to delete this bug and all its reports?")).addYesNoButtons(p -> {
|
||||
Button delete = new I18nButton(
|
||||
e -> new Popup(i18n, Messages.CONFIRM).addComponent(new Label(Messages.DELETE_BUG_CONFIRM)).addYesNoButtons(p -> {
|
||||
dataService.delete(bug);
|
||||
navigationManager.navigateBack();
|
||||
}, true).show());
|
||||
}, true).show(), i18n, Messages.DELETE_BUG);
|
||||
delete.setWidth(100, Sizeable.Unit.PERCENTAGE);
|
||||
return new VerticalLayout(unmerge, delete);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getCaption() {
|
||||
return "Danger Zone";
|
||||
return i18n.get(Messages.DANGER_ZONE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getId() {
|
||||
return "danger-zone";
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -16,6 +16,9 @@
|
|||
|
||||
package com.faendir.acra.ui.view.bug.tabs.panels;
|
||||
|
||||
import com.faendir.acra.i18n.I18nButton;
|
||||
import com.faendir.acra.i18n.I18nTextArea;
|
||||
import com.faendir.acra.i18n.Messages;
|
||||
import com.faendir.acra.model.Bug;
|
||||
import com.faendir.acra.service.DataService;
|
||||
import com.faendir.acra.ui.navigation.NavigationManager;
|
||||
|
@ -28,6 +31,7 @@ import com.vaadin.ui.TextArea;
|
|||
import com.vaadin.ui.VerticalLayout;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.lang.NonNull;
|
||||
import org.vaadin.spring.i18n.I18N;
|
||||
|
||||
/**
|
||||
* @author lukas
|
||||
|
@ -37,28 +41,35 @@ import org.springframework.lang.NonNull;
|
|||
@ViewScope
|
||||
public class PropertiesPanel implements AdminPanel {
|
||||
@NonNull private final DataService dataService;
|
||||
@NonNull private final I18N i18n;
|
||||
|
||||
@Autowired
|
||||
public PropertiesPanel(@NonNull DataService dataService) {
|
||||
public PropertiesPanel(@NonNull DataService dataService, @NonNull I18N i18n) {
|
||||
this.dataService = dataService;
|
||||
this.i18n = i18n;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Component createContent(@NonNull Bug bug, @NonNull NavigationManager navigationManager) {
|
||||
TextArea title = new TextArea("Title");
|
||||
TextArea title = new I18nTextArea(i18n, Messages.TITLE);
|
||||
title.setValue(bug.getTitle());
|
||||
title.setSizeFull();
|
||||
Button save = new Button("Save", e -> {
|
||||
Button save = new I18nButton(e -> {
|
||||
bug.setTitle(title.getValue());
|
||||
dataService.store(bug);
|
||||
});
|
||||
}, i18n, Messages.SAVE);
|
||||
save.setWidth(100, Sizeable.Unit.PERCENTAGE);
|
||||
return new VerticalLayout(title, save);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getCaption() {
|
||||
return "Properties";
|
||||
return i18n.get(Messages.PROPERTIES);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getId() {
|
||||
return "properties";
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -13,9 +13,11 @@
|
|||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.faendir.acra.ui.view.report;
|
||||
|
||||
import com.faendir.acra.i18n.I18nLabel;
|
||||
import com.faendir.acra.i18n.I18nPanel;
|
||||
import com.faendir.acra.i18n.Messages;
|
||||
import com.faendir.acra.model.App;
|
||||
import com.faendir.acra.model.Attachment;
|
||||
import com.faendir.acra.model.Permission;
|
||||
|
@ -45,6 +47,7 @@ import com.vaadin.ui.VerticalLayout;
|
|||
import com.vaadin.ui.themes.AcraTheme;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.lang.NonNull;
|
||||
import org.vaadin.spring.i18n.I18N;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.util.Comparator;
|
||||
|
@ -64,11 +67,13 @@ import java.util.stream.Stream;
|
|||
public class ReportView extends ParametrizedBaseView<Report> {
|
||||
@NonNull private final DataService dataService;
|
||||
@NonNull private final AvatarService avatarService;
|
||||
@NonNull private final I18N i18n;
|
||||
|
||||
@Autowired
|
||||
public ReportView(@NonNull DataService dataService, @NonNull AvatarService avatarService) {
|
||||
public ReportView(@NonNull DataService dataService, @NonNull AvatarService avatarService, @NonNull I18N i18n) {
|
||||
this.dataService = dataService;
|
||||
this.avatarService = avatarService;
|
||||
this.i18n = i18n;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -76,30 +81,31 @@ public class ReportView extends ParametrizedBaseView<Report> {
|
|||
HorizontalLayout attachments = new HorizontalLayout();
|
||||
for (Attachment file : dataService.findAttachments(parameter)) {
|
||||
Button button = new Button(file.getFilename());
|
||||
new FileDownloader(new StreamResource(new ExceptionAwareStreamSource(file.getContent()::getBinaryStream), file.getFilename())).extend(button);
|
||||
new FileDownloader(new StreamResource(new ExceptionAwareStreamSource(() -> file.getContent().getBinaryStream()), file.getFilename())).extend(button);
|
||||
attachments.addComponent(button);
|
||||
}
|
||||
attachments.addStyleNames(AcraTheme.MARGIN_BOTTOM, AcraTheme.MARGIN_TOP, AcraTheme.MARGIN_LEFT, AcraTheme.MARGIN_RIGHT);
|
||||
GridLayout summaryGrid = new GridLayout(2, 1);
|
||||
summaryGrid.addStyleName(AcraTheme.BORDERED_GRIDLAYOUT);
|
||||
summaryGrid.addComponents(new Label("Version", ContentMode.PREFORMATTED), new Label(parameter.getStacktrace().getVersion().getName(), ContentMode.PREFORMATTED));
|
||||
summaryGrid.addComponents(new Label("User", ContentMode.PREFORMATTED), new HorizontalLayout(new Image(null, avatarService.getAvatar(parameter)), new Label(parameter.getInstallationId(), ContentMode.PREFORMATTED)));
|
||||
summaryGrid.addComponents(new Label("Email", ContentMode.PREFORMATTED), new Label(parameter.getUserEmail(), ContentMode.PREFORMATTED));
|
||||
summaryGrid.addComponents(new Label("Comment", ContentMode.PREFORMATTED), new Label(parameter.getUserComment(), ContentMode.PREFORMATTED));
|
||||
summaryGrid.addComponents(new I18nLabel(ContentMode.PREFORMATTED, i18n, Messages.VERSION),
|
||||
new Label(parameter.getStacktrace().getVersion().getName(), ContentMode.PREFORMATTED));
|
||||
summaryGrid.addComponents(new I18nLabel(ContentMode.PREFORMATTED, i18n, Messages.USER),
|
||||
new HorizontalLayout(new Image(null, avatarService.getAvatar(parameter)), new Label(parameter.getInstallationId(), ContentMode.PREFORMATTED)));
|
||||
summaryGrid.addComponents(new I18nLabel(ContentMode.PREFORMATTED, i18n, Messages.EMAIL), new Label(parameter.getUserEmail(), ContentMode.PREFORMATTED));
|
||||
summaryGrid.addComponents(new I18nLabel(ContentMode.PREFORMATTED, i18n, Messages.COMMENT), new Label(parameter.getUserComment(), ContentMode.PREFORMATTED));
|
||||
Optional<ProguardMapping> mapping = dataService.findMapping(parameter.getStacktrace().getBug().getApp(), parameter.getStacktrace().getVersion().getCode());
|
||||
if (mapping.isPresent()) {
|
||||
summaryGrid.addComponents(new Label("De-obfuscated Stacktrace", ContentMode.PREFORMATTED),
|
||||
summaryGrid.addComponents(new I18nLabel(ContentMode.PREFORMATTED, i18n, Messages.DE_OBFUSCATED_STACKTRACE),
|
||||
new Label(Utils.retrace(parameter.getStacktrace().getStacktrace(), mapping.get().getMappings()), ContentMode.PREFORMATTED));
|
||||
} else {
|
||||
summaryGrid.addComponents(new Label("Stacktrace (No mapping found)", ContentMode.PREFORMATTED), new Label(parameter.getStacktrace().getStacktrace(), ContentMode.PREFORMATTED));
|
||||
summaryGrid.addComponents(new I18nLabel(ContentMode.PREFORMATTED, i18n, Messages.NO_MAPPING_STACKTRACE),
|
||||
new Label(parameter.getStacktrace().getStacktrace(), ContentMode.PREFORMATTED));
|
||||
}
|
||||
summaryGrid.addComponents(new Label("Attachments", ContentMode.PREFORMATTED), attachments);
|
||||
summaryGrid.addComponents(new I18nLabel(ContentMode.PREFORMATTED, i18n, Messages.ATTACHMENTS), attachments);
|
||||
summaryGrid.setDefaultComponentAlignment(Alignment.MIDDLE_LEFT);
|
||||
summaryGrid.setSizeFull();
|
||||
Panel summary = new Panel(summaryGrid);
|
||||
summary.setCaption("Summary");
|
||||
Panel details = new Panel(getLayoutForMap(parameter.getJsonObject().toMap()));
|
||||
details.setCaption("Details");
|
||||
Panel summary = new I18nPanel(summaryGrid, i18n, Messages.SUMMARY);
|
||||
Panel details = new I18nPanel(getLayoutForMap(parameter.getJsonObject().toMap()), i18n, Messages.DETAILS);
|
||||
VerticalLayout layout = new VerticalLayout(summary, details);
|
||||
layout.setSizeUndefined();
|
||||
layout.setExpandRatio(details, 1);
|
||||
|
@ -117,11 +123,13 @@ public class ReportView extends ParametrizedBaseView<Report> {
|
|||
|
||||
@NonNull
|
||||
private GridLayout getLayoutForMap(@NonNull Map<String, ?> map) {
|
||||
GridLayout layout = new GridLayout(2, 1, map.entrySet()
|
||||
.stream()
|
||||
.sorted(Comparator.comparing(Map.Entry::getKey))
|
||||
.flatMap(entry -> getLayoutForEntry(entry.getKey(), entry.getValue()))
|
||||
.toArray(Component[]::new));
|
||||
GridLayout layout = new GridLayout(2,
|
||||
1,
|
||||
map.entrySet()
|
||||
.stream()
|
||||
.sorted(Comparator.comparing(Map.Entry::getKey))
|
||||
.flatMap(entry -> getLayoutForEntry(entry.getKey(), entry.getValue()))
|
||||
.toArray(Component[]::new));
|
||||
layout.setDefaultComponentAlignment(Alignment.MIDDLE_LEFT);
|
||||
layout.setSpacing(false);
|
||||
layout.setMargin(false);
|
||||
|
@ -146,6 +154,11 @@ public class ReportView extends ParametrizedBaseView<Report> {
|
|||
return new Label(String.valueOf(value), ContentMode.PREFORMATTED);
|
||||
}
|
||||
|
||||
@FunctionalInterface
|
||||
private interface ThrowingSupplier<T> {
|
||||
T get() throws Exception;
|
||||
}
|
||||
|
||||
private static class ExceptionAwareStreamSource implements StreamResource.StreamSource {
|
||||
private final ThrowingSupplier<InputStream> supplier;
|
||||
|
||||
|
@ -155,7 +168,7 @@ public class ReportView extends ParametrizedBaseView<Report> {
|
|||
|
||||
@Override
|
||||
public InputStream getStream() {
|
||||
try{
|
||||
try {
|
||||
return supplier.get();
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
|
@ -164,11 +177,6 @@ public class ReportView extends ParametrizedBaseView<Report> {
|
|||
}
|
||||
}
|
||||
|
||||
@FunctionalInterface
|
||||
private interface ThrowingSupplier<T> {
|
||||
T get() throws Exception;
|
||||
}
|
||||
|
||||
@SpringComponent
|
||||
@UIScope
|
||||
public static class Provider extends SingleParametrizedViewProvider<Report, ReportView> {
|
||||
|
|
|
@ -16,12 +16,15 @@
|
|||
|
||||
package com.faendir.acra.ui.view.user;
|
||||
|
||||
import com.faendir.acra.security.SecurityUtils;
|
||||
import com.faendir.acra.i18n.I18nButton;
|
||||
import com.faendir.acra.i18n.I18nPasswordField;
|
||||
import com.faendir.acra.i18n.Messages;
|
||||
import com.faendir.acra.model.User;
|
||||
import com.faendir.acra.security.SecurityUtils;
|
||||
import com.faendir.acra.service.UserService;
|
||||
import com.faendir.acra.ui.BackendUI;
|
||||
import com.faendir.acra.ui.view.base.navigation.BaseView;
|
||||
import com.faendir.acra.ui.navigation.SingleViewProvider;
|
||||
import com.faendir.acra.ui.view.base.navigation.BaseView;
|
||||
import com.vaadin.navigator.ViewChangeListener;
|
||||
import com.vaadin.server.UserError;
|
||||
import com.vaadin.spring.annotation.SpringComponent;
|
||||
|
@ -34,7 +37,7 @@ import com.vaadin.ui.PasswordField;
|
|||
import com.vaadin.ui.VerticalLayout;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.lang.NonNull;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.vaadin.spring.i18n.I18N;
|
||||
|
||||
/**
|
||||
* @author Lukas
|
||||
|
@ -45,33 +48,35 @@ import org.springframework.lang.Nullable;
|
|||
public class ChangePasswordView extends BaseView {
|
||||
@NonNull private final UserService userService;
|
||||
@NonNull private final BackendUI backendUI;
|
||||
@NonNull private final I18N i18n;
|
||||
|
||||
@Autowired
|
||||
public ChangePasswordView(@NonNull UserService userService, @NonNull BackendUI backendUI) {
|
||||
public ChangePasswordView(@NonNull UserService userService, @NonNull BackendUI backendUI, @NonNull I18N i18n) {
|
||||
this.userService = userService;
|
||||
this.backendUI = backendUI;
|
||||
this.i18n = i18n;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void enter(ViewChangeListener.ViewChangeEvent event) {
|
||||
PasswordField oldPassword = new PasswordField("Old Password");
|
||||
PasswordField newPassword = new PasswordField("New Password");
|
||||
PasswordField repeatPassword = new PasswordField("Repeat Password");
|
||||
Button confirm = new Button("Confirm", e -> {
|
||||
PasswordField oldPassword = new I18nPasswordField(i18n, Messages.OLD_PASSWORD);
|
||||
PasswordField newPassword = new I18nPasswordField(i18n, Messages.NEW_PASSWORD);
|
||||
PasswordField repeatPassword = new I18nPasswordField(i18n, Messages.REPEAT_PASSWORD);
|
||||
Button confirm = new I18nButton(e -> {
|
||||
User user = userService.getUser(SecurityUtils.getUsername());
|
||||
assert user != null;
|
||||
if (newPassword.getValue().equals(repeatPassword.getValue())) {
|
||||
if (userService.changePassword(user, oldPassword.getValue(), newPassword.getValue())) {
|
||||
Notification.show("Successful!");
|
||||
Notification.show(i18n.get(Messages.SUCCESS));
|
||||
getNavigationManager().navigateBack();
|
||||
backendUI.logout();
|
||||
} else {
|
||||
oldPassword.setComponentError(new UserError("Incorrect password"));
|
||||
oldPassword.setComponentError(new UserError(i18n.get(Messages.INCORRECT_PASSWORD)));
|
||||
}
|
||||
} else {
|
||||
repeatPassword.setComponentError(new UserError("Passwords do not match"));
|
||||
repeatPassword.setComponentError(new UserError(i18n.get(Messages.PASSWORDS_NOT_MATCHING)));
|
||||
}
|
||||
});
|
||||
}, i18n, Messages.CONFIRM);
|
||||
confirm.setSizeFull();
|
||||
VerticalLayout layout = new VerticalLayout(oldPassword, newPassword, repeatPassword, confirm);
|
||||
layout.setSizeUndefined();
|
||||
|
@ -84,13 +89,16 @@ public class ChangePasswordView extends BaseView {
|
|||
@SpringComponent
|
||||
@UIScope
|
||||
public static class Provider extends SingleViewProvider<ChangePasswordView> {
|
||||
protected Provider() {
|
||||
@NonNull private final I18N i18n;
|
||||
|
||||
protected Provider(@NonNull I18N i18n) {
|
||||
super(ChangePasswordView.class);
|
||||
this.i18n = i18n;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getTitle(String parameter) {
|
||||
return "Change Password";
|
||||
return i18n.get(Messages.CHANGE_PASSWORD);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -16,6 +16,10 @@
|
|||
|
||||
package com.faendir.acra.ui.view.user;
|
||||
|
||||
import com.faendir.acra.i18n.I18nButton;
|
||||
import com.faendir.acra.i18n.I18nPasswordField;
|
||||
import com.faendir.acra.i18n.I18nTextField;
|
||||
import com.faendir.acra.i18n.Messages;
|
||||
import com.faendir.acra.model.App;
|
||||
import com.faendir.acra.model.Permission;
|
||||
import com.faendir.acra.model.QUser;
|
||||
|
@ -25,9 +29,9 @@ import com.faendir.acra.service.DataService;
|
|||
import com.faendir.acra.service.UserService;
|
||||
import com.faendir.acra.ui.annotation.RequiresRole;
|
||||
import com.faendir.acra.ui.navigation.SingleViewProvider;
|
||||
import com.faendir.acra.ui.view.base.navigation.BaseView;
|
||||
import com.faendir.acra.ui.view.base.MyCheckBox;
|
||||
import com.faendir.acra.ui.view.base.layout.MyGrid;
|
||||
import com.faendir.acra.ui.view.base.navigation.BaseView;
|
||||
import com.faendir.acra.ui.view.base.popup.Popup;
|
||||
import com.faendir.acra.ui.view.base.popup.ValidatedField;
|
||||
import com.vaadin.navigator.ViewChangeListener;
|
||||
|
@ -37,13 +41,12 @@ import com.vaadin.spring.annotation.ViewScope;
|
|||
import com.vaadin.ui.Button;
|
||||
import com.vaadin.ui.ComboBox;
|
||||
import com.vaadin.ui.Grid;
|
||||
import com.vaadin.ui.PasswordField;
|
||||
import com.vaadin.ui.TextField;
|
||||
import com.vaadin.ui.VerticalLayout;
|
||||
import com.vaadin.ui.renderers.ComponentRenderer;
|
||||
import com.vaadin.ui.themes.AcraTheme;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.lang.NonNull;
|
||||
import org.vaadin.spring.i18n.I18N;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
|
@ -57,25 +60,27 @@ import java.util.Arrays;
|
|||
public class UserManagerView extends BaseView {
|
||||
@NonNull private final UserService userService;
|
||||
@NonNull private final DataService dataService;
|
||||
@NonNull private final I18N i18n;
|
||||
private MyGrid<User> userGrid;
|
||||
|
||||
@Autowired
|
||||
public UserManagerView(@NonNull UserService userService, @NonNull DataService dataService) {
|
||||
public UserManagerView(@NonNull UserService userService, @NonNull DataService dataService, @NonNull I18N i18n) {
|
||||
this.userService = userService;
|
||||
this.dataService = dataService;
|
||||
this.i18n = i18n;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void enter(ViewChangeListener.ViewChangeEvent event) {
|
||||
userGrid = new MyGrid<>("Users", userService.getUserProvider());
|
||||
userGrid = new MyGrid<>(userService.getUserProvider(), i18n, Messages.USERS);
|
||||
userGrid.setSelectionMode(Grid.SelectionMode.NONE);
|
||||
userGrid.setBodyRowHeight(42);
|
||||
userGrid.setSizeToRows();
|
||||
userGrid.addColumn(User::getUsername, QUser.user.username, "Username");
|
||||
userGrid.addColumn(User::getUsername, QUser.user.username, Messages.USERNAME);
|
||||
userGrid.addColumn(user -> new MyCheckBox(user.getRoles().contains(User.Role.ADMIN), !user.getUsername().equals(SecurityUtils.getUsername()), e -> {
|
||||
userService.setAdmin(user, e.getValue());
|
||||
userGrid.getDataProvider().refreshAll();
|
||||
}), new ComponentRenderer(), "Admin");
|
||||
}), new ComponentRenderer(), Messages.ADMIN);
|
||||
for (App app : dataService.findAllApps()) {
|
||||
userGrid.addColumn(user -> {
|
||||
Permission.Level permission = SecurityUtils.getPermission(app, user);
|
||||
|
@ -84,9 +89,9 @@ public class UserManagerView extends BaseView {
|
|||
levelComboBox.setValue(permission);
|
||||
levelComboBox.addValueChangeListener(e -> userService.setPermission(user, app, e.getValue()));
|
||||
return levelComboBox;
|
||||
}, new ComponentRenderer(), "Access Permission for " + app.getName());
|
||||
}, new ComponentRenderer(), Messages.ACCESS_PERMISSION, app.getName());
|
||||
}
|
||||
Button newUser = new Button("New User", e -> newUser());
|
||||
Button newUser = new I18nButton( e -> newUser(), i18n, Messages.NEW_USER);
|
||||
VerticalLayout layout = new VerticalLayout(userGrid, newUser);
|
||||
layout.addStyleName(AcraTheme.NO_PADDING);
|
||||
setCompositionRoot(layout);
|
||||
|
@ -94,12 +99,12 @@ public class UserManagerView extends BaseView {
|
|||
}
|
||||
|
||||
private void newUser() {
|
||||
TextField name = new TextField("Username");
|
||||
PasswordField password = new PasswordField("Password");
|
||||
new Popup().setTitle("New User")
|
||||
.addValidatedField(ValidatedField.of(name).addValidator(s -> !s.isEmpty(), "Username cannot be empty"))
|
||||
.addValidatedField(ValidatedField.of(password).addValidator(s -> !s.isEmpty(), "Password cannot be empty"))
|
||||
.addValidatedField(ValidatedField.of(new PasswordField("Repeat Password")).addValidator(s -> s.equals(password.getValue()), "Passwords do not match"))
|
||||
I18nTextField name = new I18nTextField(i18n, Messages.USERNAME);
|
||||
I18nPasswordField password = new I18nPasswordField(i18n, Messages.PASSWORD);
|
||||
new Popup(i18n, Messages.NEW_USER)
|
||||
.addValidatedField(ValidatedField.of(name).addValidator(s -> !s.isEmpty(), Messages.USERNAME_EMPTY))
|
||||
.addValidatedField(ValidatedField.of(password).addValidator(s -> !s.isEmpty(), Messages.PASSWORD_EMPTY))
|
||||
.addValidatedField(ValidatedField.of(new I18nPasswordField(i18n, Messages.REPEAT_PASSWORD)).addValidator(s -> s.equals(password.getValue()), Messages.PASSWORDS_NOT_MATCHING))
|
||||
.addCreateButton(popup -> {
|
||||
userService.createUser(name.getValue().toLowerCase(), password.getValue());
|
||||
userGrid.getDataProvider().refreshAll();
|
||||
|
@ -110,13 +115,16 @@ public class UserManagerView extends BaseView {
|
|||
@SpringComponent
|
||||
@UIScope
|
||||
public static class Provider extends SingleViewProvider<UserManagerView> {
|
||||
protected Provider() {
|
||||
@NonNull private final I18N i18n;
|
||||
|
||||
protected Provider(@NonNull I18N i18n) {
|
||||
super(UserManagerView.class);
|
||||
this.i18n = i18n;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getTitle(String parameter) {
|
||||
return "User Manager";
|
||||
return i18n.get(Messages.USER_MANAGER);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
129
src/main/resources/i18n/com/faendir/acra/messages.properties
Normal file
129
src/main/resources/i18n/com/faendir/acra/messages.properties
Normal file
|
@ -0,0 +1,129 @@
|
|||
#
|
||||
# (C) Copyright 2018 Lukas Morawietz (https://github.com/F43nd1r)
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
|
||||
newApp=New App
|
||||
apps=Apps
|
||||
name=Name
|
||||
bugs=Bugs
|
||||
reports=Reports
|
||||
importAcralyzer=Import from Acralyzer
|
||||
host=Host
|
||||
port=Port
|
||||
ssl=Use SSL
|
||||
databaseName=Database Name
|
||||
importSuccess=Import successful for {0} of {1} reports.
|
||||
acrarium=Acrarium
|
||||
configurationLabel=Take note of the following ACRA configuration. It cannot be viewed later:<br><code>\
|
||||
@AcraCore(reportFormat = StringFormat.JSON)<br>\
|
||||
@AcraHttpSender(uri = "{0}{1}",<br>\
|
||||
basicAuthLogin = "{2}",<br>\
|
||||
basicAuthPassword = "{3}",<br>\
|
||||
httpMethod = HttpSender.Method.POST)<br></code>
|
||||
create=Create
|
||||
close=Close
|
||||
yes=Yes
|
||||
no=No
|
||||
error4xx=This page does not exist or you do not have the permission to view it.
|
||||
admin=Admin
|
||||
hideSolved=Hide Solved
|
||||
mergeBugs=Merge Bugs
|
||||
chooseBugGroupTitle=Choose title for bug group
|
||||
onlyOneBugSelected=Please select at least two bugs
|
||||
latestReport=Latest Report
|
||||
latestVersion=Latest Version
|
||||
affectedUsers=Affected Users
|
||||
title=Title
|
||||
solved=Solved
|
||||
statistics=Statistics
|
||||
newAcraConfig=Create new ACRA Configuration
|
||||
confirm=Confirm
|
||||
newAcraConfigConfirm=Are you sure you want to create a new ACRA configuration?<br>The existing configuration will be invalidated
|
||||
newBugConfig=Configure Bug matching
|
||||
minScore=Minimum Score
|
||||
newBugConfigConfirm=Are you sure you want to save this configuration? All bugs will be checked for merging, which may take some time. Note that a higher minimum score does not unmerge already merged bugs.
|
||||
purge=Purge
|
||||
reportsOlderThan1= Reports older than
|
||||
reportsOlderThan2= Days
|
||||
reportsBeforeVersion= Reports before Version
|
||||
deleteApp=Delete App
|
||||
deleteAppConfirm=Are you sure you want to delete this app and all its associated content?
|
||||
dangerZone=Danger Zone
|
||||
byMail=By Email Address
|
||||
byId=By Installation ID
|
||||
download=Download
|
||||
nothingSelected=Nothing selected
|
||||
export=Export
|
||||
version=Version
|
||||
deleteMappingConfirm=Are you sure you want to delete the mapping for version {0}?
|
||||
newFile=Add File
|
||||
versionCode=Version code
|
||||
mappingFile=Mapping file
|
||||
newMapping=New Mapping Configuration
|
||||
errorUpload=Upload failed
|
||||
deObfuscation=De-Obfuscation
|
||||
lastXDays=Last X Days
|
||||
reportsOverTime=Reports over time
|
||||
androidVersion=Android Version
|
||||
reportsPerAndroidVersion=Reports per Android Version
|
||||
appVersion=App Version
|
||||
reportsPerAppVersion=Reports per App Version
|
||||
phoneModel=Phone Model
|
||||
reportsPerPhoneModel=Reports per Phone Model
|
||||
phoneBrand=Phone Brand
|
||||
reportsPerBrand=Reports per Brand
|
||||
filter=Filter
|
||||
apply=Apply
|
||||
user=User
|
||||
date=Date
|
||||
device=Device
|
||||
stacktrace=Stacktrace
|
||||
deleteReportConfirm=Are you sure you want to delete this report?
|
||||
unmergeBug=Disjoin Bug
|
||||
unmergeBugConfirm=Are you sure you want to revert merging this bug group?
|
||||
deleteBug=Delete Bug
|
||||
deleteBugConfirm=Are you sure you want to delete this bug and all its reports?
|
||||
save=Save
|
||||
properties=Properties
|
||||
stacktraces=stacktraces
|
||||
stacktraceTitle=Version "{0}": {1}
|
||||
email=Email
|
||||
comment=Comment
|
||||
DeObfuscatedStacktrace=De-obfuscated Stacktrace
|
||||
noMappingStacktrace=Stacktrace (No mapping found)
|
||||
attachments=Attachments
|
||||
summary=Summary
|
||||
details=Details
|
||||
oldPassword=Old Password
|
||||
newPassword=New Password
|
||||
repeatPassword=Repeat Password
|
||||
incorrectPassword=Incorrect Password
|
||||
passwordsNotMatching=Passwords do not match
|
||||
changePassword=Change Password
|
||||
success=Successful!
|
||||
users=Users
|
||||
username=Username
|
||||
accessPermission=Access Permission for {0}
|
||||
newUser=New User
|
||||
password=Password
|
||||
usernameEmpty=Username cannot be empty
|
||||
passwordEmpty=Password cannot be empty
|
||||
userManager=User Manager
|
||||
loginFailed=Unknown username/password combination
|
||||
missingRole=Missing required role
|
||||
darkTheme=Dark Theme
|
||||
logout=Logout
|
||||
footer=Acrarium is developed by <a href=https://github.com/F43nd1r>F43nd1r</a>. <a href=https://github.com/F43nd1r/acra-backend>Code</a> is licensed under <a href=https://github.com/F43nd1r/acra-backend/blob/master/LICENSE>Apache License v2</a>.
|
||||
blank=
|
Loading…
Reference in a new issue