sort base view classes into packages. Split AdminTab into panel classes

This commit is contained in:
f43nd1r 2018-06-17 23:54:32 +02:00
parent 74528e1cc7
commit 2aed2d4add
35 changed files with 527 additions and 293 deletions

View file

@ -93,7 +93,7 @@ dependencies {
def queryDslVersion = '4.2.1'
compile "com.querydsl:querydsl-jpa:$queryDslVersion"
compile "com.querydsl:querydsl-sql:$queryDslVersion"
compile "com.querydsl:querydsl-apt:$queryDslVersion:hibernate"
compile "com.querydsl:querydsl-apt:$queryDslVersion:jpa"
//vaadin
compile 'com.vaadin:vaadin-spring-boot-starter'
compile 'com.vaadin:vaadin-push'

View file

@ -16,7 +16,7 @@
package com.faendir.acra.client.mygrid;
import com.faendir.acra.ui.view.base.MyGrid;
import com.faendir.acra.ui.view.base.layout.MyGrid;
import com.google.gwt.dom.client.NativeEvent;
import com.google.gwt.event.dom.client.MouseDownEvent;
import com.vaadin.client.MouseEventDetailsBuilder;

View file

@ -18,7 +18,7 @@ package com.faendir.acra.ui;
import com.faendir.acra.model.User;
import com.faendir.acra.security.SecurityUtils;
import com.faendir.acra.ui.navigation.NavigationManager;
import com.faendir.acra.ui.view.base.Path;
import com.faendir.acra.ui.view.base.navigation.Path;
import com.faendir.acra.ui.view.user.ChangePasswordView;
import com.faendir.acra.ui.view.user.UserManagerView;
import com.vaadin.annotations.Theme;

View file

@ -17,8 +17,8 @@
package com.faendir.acra.ui.navigation;
import com.faendir.acra.ui.view.ErrorView;
import com.faendir.acra.ui.view.base.BaseView;
import com.faendir.acra.ui.view.base.Path;
import com.faendir.acra.ui.view.base.navigation.BaseView;
import com.faendir.acra.ui.view.base.navigation.Path;
import com.faendir.acra.util.Utils;
import com.vaadin.spring.annotation.UIScope;
import com.vaadin.ui.Panel;

View file

@ -19,7 +19,7 @@ package com.faendir.acra.ui.navigation;
import com.faendir.acra.model.App;
import com.faendir.acra.security.SecurityUtils;
import com.faendir.acra.ui.annotation.RequiresAppPermission;
import com.faendir.acra.ui.view.base.ParametrizedBaseView;
import com.faendir.acra.ui.view.base.navigation.ParametrizedBaseView;
/**
* @author Lukas

View file

@ -18,7 +18,7 @@ package com.faendir.acra.ui.navigation;
import com.faendir.acra.security.SecurityUtils;
import com.faendir.acra.ui.annotation.RequiresRole;
import com.faendir.acra.ui.view.base.BaseView;
import com.faendir.acra.ui.view.base.navigation.BaseView;
import com.vaadin.navigator.ViewProvider;
import com.vaadin.spring.internal.ViewCache;
import com.vaadin.spring.internal.ViewScopeImpl;

View file

@ -25,10 +25,10 @@ import com.faendir.acra.security.SecurityUtils;
import com.faendir.acra.service.DataService;
import com.faendir.acra.ui.navigation.SingleViewProvider;
import com.faendir.acra.ui.view.app.AppView;
import com.faendir.acra.ui.view.base.BaseView;
import com.faendir.acra.ui.view.base.navigation.BaseView;
import com.faendir.acra.ui.view.base.ConfigurationLabel;
import com.faendir.acra.ui.view.base.MyGrid;
import com.faendir.acra.ui.view.base.Popup;
import com.faendir.acra.ui.view.base.layout.MyGrid;
import com.faendir.acra.ui.view.base.popup.Popup;
import com.vaadin.navigator.ViewChangeListener;
import com.vaadin.spring.annotation.SpringComponent;
import com.vaadin.spring.annotation.UIScope;

View file

@ -22,8 +22,8 @@ import com.faendir.acra.ui.annotation.RequiresAppPermission;
import com.faendir.acra.ui.navigation.MyNavigator;
import com.faendir.acra.ui.navigation.SingleParametrizedViewProvider;
import com.faendir.acra.ui.view.app.tabs.AppTab;
import com.faendir.acra.ui.view.base.MyTabSheet;
import com.faendir.acra.ui.view.base.ParametrizedBaseView;
import com.faendir.acra.ui.view.base.layout.MyTabSheet;
import com.faendir.acra.ui.view.base.navigation.ParametrizedBaseView;
import com.vaadin.spring.annotation.SpringComponent;
import com.vaadin.spring.annotation.UIScope;
import com.vaadin.spring.annotation.ViewScope;

View file

@ -17,45 +17,15 @@ package com.faendir.acra.ui.view.app.tabs;
import com.faendir.acra.model.App;
import com.faendir.acra.model.Permission;
import com.faendir.acra.model.ProguardMapping;
import com.faendir.acra.model.QProguardMapping;
import com.faendir.acra.model.QReport;
import com.faendir.acra.rest.RestReportInterface;
import com.faendir.acra.security.SecurityUtils;
import com.faendir.acra.service.DataService;
import com.faendir.acra.ui.annotation.RequiresAppPermission;
import com.faendir.acra.ui.navigation.NavigationManager;
import com.faendir.acra.ui.view.base.ConfigurationLabel;
import com.faendir.acra.ui.view.base.FlexLayout;
import com.faendir.acra.ui.view.base.InMemoryUpload;
import com.faendir.acra.ui.view.base.MyGrid;
import com.faendir.acra.ui.view.base.Popup;
import com.faendir.acra.ui.view.base.ValidatedField;
import com.vaadin.icons.VaadinIcons;
import com.vaadin.server.Page;
import com.vaadin.server.Sizeable;
import com.vaadin.shared.data.sort.SortDirection;
import com.vaadin.shared.ui.ContentMode;
import com.faendir.acra.ui.view.app.tabs.panels.AdminPanel;
import com.faendir.acra.ui.view.base.layout.PanelFlexTab;
import com.vaadin.spring.annotation.SpringComponent;
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.ComboBox;
import com.vaadin.ui.Component;
import com.vaadin.ui.HorizontalLayout;
import com.vaadin.ui.Label;
import com.vaadin.ui.Notification;
import com.vaadin.ui.Panel;
import com.vaadin.ui.ProgressBar;
import com.vaadin.ui.UI;
import com.vaadin.ui.VerticalLayout;
import com.vaadin.ui.renderers.ButtonRenderer;
import com.vaadin.ui.themes.AcraTheme;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.lang.NonNull;
import org.springframework.web.util.UriComponentsBuilder;
import org.vaadin.risto.stepper.IntStepper;
import java.util.List;
/**
* @author Lukas
@ -64,155 +34,10 @@ import org.vaadin.risto.stepper.IntStepper;
@RequiresAppPermission(Permission.Level.ADMIN)
@SpringComponent
@ViewScope
public class AdminTab implements AppTab {
@NonNull private final DataService dataService;
public class AdminTab extends PanelFlexTab<App> implements AppTab {
@Autowired
public AdminTab(@NonNull DataService dataService) {
this.dataService = dataService;
}
@Override
public Component createContent(@NonNull App app, @NonNull NavigationManager navigationManager) {
FlexLayout layout = new FlexLayout(proguardPanel(app), exportPanel(app), dangerPanel(app, navigationManager));
layout.setWidth(100, Sizeable.Unit.PERCENTAGE);
Panel root = new Panel(layout);
root.setSizeFull();
root.addStyleNames(AcraTheme.NO_BACKGROUND, AcraTheme.NO_BORDER);
return root;
}
private Panel dangerPanel(@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());
configButton.setWidth(100, Sizeable.Unit.PERCENTAGE);
Button matchingButton = new Button("Configure bug matching", e -> {
App.Configuration configuration = app.getConfiguration();
CheckBox matchByMessage = new CheckBox("Match by exception message", configuration.matchByMessage());
CheckBox ignoreInstanceIds = new CheckBox("Ignore instance ids", configuration.ignoreInstanceIds());
CheckBox ignoreAndroidLineNumbers = new CheckBox("Ignore android SDK line numbers", configuration.ignoreAndroidLineNumbers());
new Popup().addValidatedField(ValidatedField.of(matchByMessage), true)
.addValidatedField(ValidatedField.of(ignoreInstanceIds), true)
.addValidatedField(ValidatedField.of(ignoreAndroidLineNumbers), true)
.addComponent(new Label(
"Are you sure you want to save this configuration? All bugs will be recalculated, which may take some time and will reset the 'solved' status"))
.addYesNoButtons(p -> dataService.changeConfiguration(app,
new App.Configuration(matchByMessage.getValue(), ignoreInstanceIds.getValue(), ignoreAndroidLineNumbers.getValue())), true)
.show();
});
matchingButton.setWidth(100, Sizeable.Unit.PERCENTAGE);
IntStepper age = new IntStepper();
age.setValue(30);
age.setMinValue(0);
age.setWidth(100, Sizeable.Unit.PERCENTAGE);
HorizontalLayout purgeAge = new HorizontalLayout();
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.setExpandRatio(age, 1);
ComboBox<Integer> versionBox = new ComboBox<>(null, dataService.getFromReports(QReport.report.stacktrace.bug.app.eq(app), QReport.report.stacktrace.versionCode));
versionBox.setEmptySelectionAllowed(false);
versionBox.setWidth(100, Sizeable.Unit.PERCENTAGE);
HorizontalLayout purgeVersion = new HorizontalLayout();
purgeVersion.setWidth(100, Sizeable.Unit.PERCENTAGE);
purgeVersion.setDefaultComponentAlignment(Alignment.MIDDLE_CENTER);
purgeVersion.addStyleName(AcraTheme.NO_MARGIN);
purgeVersion.addComponents(new Button("Purge", e -> {
if (versionBox.getValue() != null) {
dataService.deleteReportsBeforeVersion(app, versionBox.getValue());
}
}), new Label(" 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());
VerticalLayout layout = new VerticalLayout(configButton, matchingButton, purgeAge, purgeVersion, deleteButton);
deleteButton.setWidth(100, Sizeable.Unit.PERCENTAGE);
layout.setSizeFull();
layout.addStyleName(AcraTheme.NO_PADDING);
Panel danger = new Panel(layout);
danger.setCaption("Danger Zone");
danger.setIcon(VaadinIcons.EXCLAMATION);
danger.addStyleNames(AcraTheme.NO_BACKGROUND, AcraTheme.RED_PANEL_HEADER);
return danger;
}
private Panel proguardPanel(@NonNull App app) {
VerticalLayout layout = new VerticalLayout();
MyGrid<ProguardMapping> grid = new MyGrid<>(null, dataService.getMappingProvider(app));
grid.setSizeToRows();
grid.sort(grid.addColumn(ProguardMapping::getVersionCode, QProguardMapping.proguardMapping.versionCode, "Version"), SortDirection.ASCENDING);
if (SecurityUtils.hasPermission(app, Permission.Level.EDIT)) {
grid.addColumn(report -> "Delete",
new ButtonRenderer<>(e -> new Popup().setTitle("Confirm")
.addComponent(new Label("Are you sure you want to delete the mapping for version " + e.getItem().getVersionCode() + "?"))
.addYesNoButtons(p -> {
dataService.delete(e.getItem());
grid.getDataProvider().refreshAll();
}, true)
.show()));
}
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:");
ProgressBar progressBar = new ProgressBar();
upload.addProgressListener((readBytes, contentLength) -> layout.getUI().access(() -> progressBar.setValue((float) readBytes / contentLength)));
new Popup().setTitle("New Mapping Configuration")
.addComponent(version)
.addValidatedField(ValidatedField.of(upload, () -> upload, consumer -> upload.addFinishedListener(event -> consumer.accept(upload)))
.addValidator(InMemoryUpload::isUploaded, "Upload failed"))
.addComponent(progressBar)
.addCreateButton(popup -> {
dataService.store(new ProguardMapping(app, version.getValue(), upload.getUploadedString()));
grid.getDataProvider().refreshAll();
}, true)
.show();
}));
}
Panel panel = new Panel(layout);
panel.setCaption("De-Obfuscation");
panel.addStyleName(AcraTheme.NO_BACKGROUND);
return panel;
}
private Panel exportPanel(@NonNull App app) {
ComboBox<String> mailBox = new ComboBox<>("By Email Address", dataService.getFromReports(QReport.report.stacktrace.bug.app.eq(app), QReport.report.userEmail));
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));
idBox.setEmptySelectionAllowed(true);
idBox.setSizeFull();
Button download = new Button("Download", e -> {
if (idBox.getValue() == null && mailBox.getValue() == null) {
Notification.show("Nothing selected", Notification.Type.WARNING_MESSAGE);
} else {
Page page = UI.getCurrent().getPage();
page.open(UriComponentsBuilder.fromUri(page.getLocation())
.fragment(null)
.pathSegment(RestReportInterface.EXPORT_PATH)
.queryParam(RestReportInterface.PARAM_APP, app.getId())
.queryParam(RestReportInterface.PARAM_ID, idBox.getValue())
.queryParam(RestReportInterface.PARAM_MAIL, mailBox.getValue())
.build()
.toUriString(), null);
}
});
download.setSizeFull();
VerticalLayout layout = new VerticalLayout(mailBox, idBox, download);
Panel panel = new Panel(layout);
panel.setCaption("Export");
panel.addStyleName(AcraTheme.NO_BACKGROUND);
return panel;
public AdminTab(@NonNull List<AdminPanel> panels) {
super(panels);
}
@Override

View file

@ -17,7 +17,7 @@
package com.faendir.acra.ui.view.app.tabs;
import com.faendir.acra.model.App;
import com.faendir.acra.ui.view.base.MyTabSheet;
import com.faendir.acra.ui.view.base.layout.ComponentFactory;
import java.io.Serializable;
@ -25,5 +25,5 @@ import java.io.Serializable;
* @author Lukas
* @since 27.03.2018
*/
public interface AppTab extends MyTabSheet.Tab<App>, Serializable {
public interface AppTab extends ComponentFactory<App>, Serializable {
}

View file

@ -25,8 +25,8 @@ import com.faendir.acra.security.SecurityUtils;
import com.faendir.acra.service.DataService;
import com.faendir.acra.ui.navigation.NavigationManager;
import com.faendir.acra.ui.view.base.MyCheckBox;
import com.faendir.acra.ui.view.base.MyGrid;
import com.faendir.acra.ui.view.base.Popup;
import com.faendir.acra.ui.view.base.layout.MyGrid;
import com.faendir.acra.ui.view.base.popup.Popup;
import com.faendir.acra.util.TimeSpanRenderer;
import com.vaadin.server.Sizeable;
import com.vaadin.shared.data.sort.SortDirection;

View file

@ -0,0 +1,27 @@
/*
* (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.ui.view.app.tabs.panels;
import com.faendir.acra.model.App;
import com.faendir.acra.ui.view.base.layout.PanelContentFactory;
/**
* @author lukas
* @since 17.06.18
*/
public interface AdminPanel extends PanelContentFactory<App> {
}

View file

@ -0,0 +1,135 @@
/*
* (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.ui.view.app.tabs.panels;
import com.faendir.acra.model.App;
import com.faendir.acra.model.QReport;
import com.faendir.acra.service.DataService;
import com.faendir.acra.ui.navigation.NavigationManager;
import com.faendir.acra.ui.view.base.ConfigurationLabel;
import com.faendir.acra.ui.view.base.popup.Popup;
import com.faendir.acra.ui.view.base.popup.ValidatedField;
import com.vaadin.icons.VaadinIcons;
import com.vaadin.server.Resource;
import com.vaadin.server.Sizeable;
import com.vaadin.shared.ui.ContentMode;
import com.vaadin.spring.annotation.SpringComponent;
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.ComboBox;
import com.vaadin.ui.Component;
import com.vaadin.ui.HorizontalLayout;
import com.vaadin.ui.Label;
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;
/**
* @author lukas
* @since 17.06.18
*/
@SpringComponent
@ViewScope
public class DangerPanel implements AdminPanel {
@NonNull private final DataService dataService;
@Autowired
public DangerPanel(@NonNull DataService dataService) {
this.dataService = dataService;
}
@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());
configButton.setWidth(100, Sizeable.Unit.PERCENTAGE);
Button matchingButton = new Button("Configure bug matching", e -> {
App.Configuration configuration = app.getConfiguration();
CheckBox matchByMessage = new CheckBox("Match by exception message", configuration.matchByMessage());
CheckBox ignoreInstanceIds = new CheckBox("Ignore instance ids", configuration.ignoreInstanceIds());
CheckBox ignoreAndroidLineNumbers = new CheckBox("Ignore android SDK line numbers", configuration.ignoreAndroidLineNumbers());
new Popup().addValidatedField(ValidatedField.of(matchByMessage), true)
.addValidatedField(ValidatedField.of(ignoreInstanceIds), true)
.addValidatedField(ValidatedField.of(ignoreAndroidLineNumbers), true)
.addComponent(new Label(
"Are you sure you want to save this configuration? All bugs will be recalculated, which may take some time and will reset the 'solved' status"))
.addYesNoButtons(p -> dataService.changeConfiguration(app,
new App.Configuration(matchByMessage.getValue(), ignoreInstanceIds.getValue(), ignoreAndroidLineNumbers.getValue())), true)
.show();
});
matchingButton.setWidth(100, Sizeable.Unit.PERCENTAGE);
IntStepper age = new IntStepper();
age.setValue(30);
age.setMinValue(0);
age.setWidth(100, Sizeable.Unit.PERCENTAGE);
HorizontalLayout purgeAge = new HorizontalLayout();
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.setExpandRatio(age, 1);
ComboBox<Integer> versionBox = new ComboBox<>(null, dataService.getFromReports(QReport.report.stacktrace.bug.app.eq(app), QReport.report.stacktrace.versionCode));
versionBox.setEmptySelectionAllowed(false);
versionBox.setWidth(100, Sizeable.Unit.PERCENTAGE);
HorizontalLayout purgeVersion = new HorizontalLayout();
purgeVersion.setWidth(100, Sizeable.Unit.PERCENTAGE);
purgeVersion.setDefaultComponentAlignment(Alignment.MIDDLE_CENTER);
purgeVersion.addStyleName(AcraTheme.NO_MARGIN);
purgeVersion.addComponents(new Button("Purge", e -> {
if (versionBox.getValue() != null) {
dataService.deleteReportsBeforeVersion(app, versionBox.getValue());
}
}), new Label(" 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());
VerticalLayout layout = new VerticalLayout(configButton, matchingButton, purgeAge, purgeVersion, deleteButton);
deleteButton.setWidth(100, Sizeable.Unit.PERCENTAGE);
layout.setSizeFull();
layout.addStyleName(AcraTheme.NO_PADDING);
return layout;
}
@Override
public String getCaption() {
return "Danger Zone";
}
@Override
public Resource getIcon() {
return VaadinIcons.EXCLAMATION;
}
@Override
public String getStyleName() {
return AcraTheme.RED_PANEL_HEADER;
}
@Override
public int getOrder() {
return 2;
}
}

View file

@ -0,0 +1,86 @@
/*
* (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.ui.view.app.tabs.panels;
import com.faendir.acra.model.App;
import com.faendir.acra.model.QReport;
import com.faendir.acra.rest.RestReportInterface;
import com.faendir.acra.service.DataService;
import com.faendir.acra.ui.navigation.NavigationManager;
import com.vaadin.server.Page;
import com.vaadin.spring.annotation.SpringComponent;
import com.vaadin.spring.annotation.ViewScope;
import com.vaadin.ui.Button;
import com.vaadin.ui.ComboBox;
import com.vaadin.ui.Component;
import com.vaadin.ui.Notification;
import com.vaadin.ui.UI;
import com.vaadin.ui.VerticalLayout;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.lang.NonNull;
import org.springframework.web.util.UriComponentsBuilder;
/**
* @author lukas
* @since 17.06.18
*/
@SpringComponent
@ViewScope
public class ExportPanel implements AdminPanel {
@NonNull private final DataService dataService;
@Autowired
public ExportPanel(@NonNull DataService dataService) {
this.dataService = dataService;
}
@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));
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));
idBox.setEmptySelectionAllowed(true);
idBox.setSizeFull();
Button download = new Button("Download", e -> {
if (idBox.getValue() == null && mailBox.getValue() == null) {
Notification.show("Nothing selected", Notification.Type.WARNING_MESSAGE);
} else {
Page page = UI.getCurrent().getPage();
page.open(UriComponentsBuilder.fromUri(page.getLocation())
.fragment(null)
.pathSegment(RestReportInterface.EXPORT_PATH)
.queryParam(RestReportInterface.PARAM_APP, app.getId())
.queryParam(RestReportInterface.PARAM_ID, idBox.getValue())
.queryParam(RestReportInterface.PARAM_MAIL, mailBox.getValue())
.build()
.toUriString(), null);
}
});
download.setSizeFull();
return new VerticalLayout(mailBox, idBox, download);
}
@Override
public String getCaption() {
return "Export";
}
@Override
public int getOrder() {
return 1;
}
}

View file

@ -0,0 +1,106 @@
/*
* (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.ui.view.app.tabs.panels;
import com.faendir.acra.model.App;
import com.faendir.acra.model.Permission;
import com.faendir.acra.model.ProguardMapping;
import com.faendir.acra.model.QProguardMapping;
import com.faendir.acra.security.SecurityUtils;
import com.faendir.acra.service.DataService;
import com.faendir.acra.ui.navigation.NavigationManager;
import com.faendir.acra.ui.view.base.InMemoryUpload;
import com.faendir.acra.ui.view.base.layout.MyGrid;
import com.faendir.acra.ui.view.base.popup.Popup;
import com.faendir.acra.ui.view.base.popup.ValidatedField;
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;
import com.vaadin.ui.themes.AcraTheme;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.lang.NonNull;
import org.vaadin.risto.stepper.IntStepper;
/**
* @author lukas
* @since 17.06.18
*/
@SpringComponent
@ViewScope
public class ProguardPanel implements AdminPanel{
@NonNull private final DataService dataService;
@Autowired
public ProguardPanel(@NonNull DataService dataService) {
this.dataService = dataService;
}
@Override
public Component createContent(@NonNull App app, @NonNull NavigationManager navigationManager) {
VerticalLayout layout = new VerticalLayout();
MyGrid<ProguardMapping> grid = new MyGrid<>(null, dataService.getMappingProvider(app));
grid.setSizeToRows();
grid.sort(grid.addColumn(ProguardMapping::getVersionCode, QProguardMapping.proguardMapping.versionCode, "Version"), SortDirection.ASCENDING);
if (SecurityUtils.hasPermission(app, Permission.Level.EDIT)) {
grid.addColumn(report -> "Delete",
new ButtonRenderer<>(e -> new Popup().setTitle("Confirm")
.addComponent(new Label("Are you sure you want to delete the mapping for version " + e.getItem().getVersionCode() + "?"))
.addYesNoButtons(p -> {
dataService.delete(e.getItem());
grid.getDataProvider().refreshAll();
}, true)
.show()));
}
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:");
ProgressBar progressBar = new ProgressBar();
upload.addProgressListener((readBytes, contentLength) -> layout.getUI().access(() -> progressBar.setValue((float) readBytes / contentLength)));
new Popup().setTitle("New Mapping Configuration")
.addComponent(version)
.addValidatedField(ValidatedField.of(upload, () -> upload, consumer -> upload.addFinishedListener(event -> consumer.accept(upload)))
.addValidator(InMemoryUpload::isUploaded, "Upload failed"))
.addComponent(progressBar)
.addCreateButton(popup -> {
dataService.store(new ProguardMapping(app, version.getValue(), upload.getUploadedString()));
grid.getDataProvider().refreshAll();
}, true)
.show();
}));
}
return layout;
}
@Override
public String getCaption() {
return "De-Obfuscation";
}
@Override
public int getOrder() {
return 0;
}
}

View file

@ -1,62 +0,0 @@
/*
* (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.ui.view.base;
import com.vaadin.server.FileDownloader;
import com.vaadin.server.StreamResource;
import com.vaadin.server.VaadinRequest;
import com.vaadin.server.VaadinResponse;
import org.springframework.data.util.Pair;
import org.springframework.lang.NonNull;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.function.Supplier;
/**
* @author lukas
* @since 24.05.18
*/
public class OnDemandFileDownloader extends FileDownloader {
@NonNull
private final Supplier<Pair<InputStream, String>> provider;
public OnDemandFileDownloader(@NonNull Supplier<Pair<InputStream, String>> provider) {
super(new StreamResource(() -> new ByteArrayInputStream(new byte[0]), ""));
this.provider = provider;
}
@Override
public boolean handleConnectorRequest(VaadinRequest request, VaadinResponse response, String path)
throws IOException {
Pair<InputStream, String> source = provider.get();
if (source != null) {
getResource().setStreamSource(source::getFirst);
getResource().setFilename(source.getSecond());
return super.handleConnectorRequest(request, response, path);
}
return false;
}
@NonNull
private StreamResource getResource() {
return (StreamResource) getFileDownloadResource();
}
}

View file

@ -22,6 +22,8 @@ import com.faendir.acra.model.QReport;
import com.faendir.acra.model.Report;
import com.faendir.acra.security.SecurityUtils;
import com.faendir.acra.ui.navigation.NavigationManager;
import com.faendir.acra.ui.view.base.layout.MyGrid;
import com.faendir.acra.ui.view.base.popup.Popup;
import com.faendir.acra.ui.view.report.ReportView;
import com.faendir.acra.util.TimeSpanRenderer;
import com.vaadin.shared.data.sort.SortDirection;

View file

@ -0,0 +1,32 @@
/*
* (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.ui.view.base.layout;
import com.faendir.acra.ui.navigation.NavigationManager;
import com.vaadin.ui.Component;
import org.springframework.core.Ordered;
import org.springframework.lang.NonNull;
/**
* @author lukas
* @since 17.06.18
*/
public interface ComponentFactory<T> extends Ordered {
Component createContent(@NonNull T t, @NonNull NavigationManager navigationManager);
String getCaption();
}

View file

@ -14,7 +14,7 @@
* limitations under the License.
*/
package com.faendir.acra.ui.view.base;
package com.faendir.acra.ui.view.base.layout;
import com.vaadin.ui.Component;
import com.vaadin.ui.CssLayout;

View file

@ -14,11 +14,12 @@
* limitations under the License.
*/
package com.faendir.acra.ui.view.base;
package com.faendir.acra.ui.view.base.layout;
import com.faendir.acra.client.mygrid.MiddleClickGridExtensionConnector;
import com.faendir.acra.dataprovider.QueryDslDataProvider;
import com.faendir.acra.ui.navigation.NavigationManager;
import com.faendir.acra.ui.view.base.navigation.BaseView;
import com.querydsl.core.types.Expression;
import com.vaadin.data.ValueProvider;
import com.vaadin.data.provider.DataProvider;

View file

@ -14,13 +14,12 @@
* limitations under the License.
*/
package com.faendir.acra.ui.view.base;
package com.faendir.acra.ui.view.base.layout;
import com.faendir.acra.ui.navigation.NavigationManager;
import com.vaadin.ui.Component;
import com.vaadin.ui.CustomComponent;
import com.vaadin.ui.TabSheet;
import org.springframework.core.Ordered;
import org.springframework.lang.NonNull;
import java.util.Arrays;
@ -40,14 +39,14 @@ public class MyTabSheet<T> extends TabSheet {
private final NavigationManager navigationManager;
@SafeVarargs
public MyTabSheet(@NonNull T t, @NonNull NavigationManager navigationManager, Tab<T>... tabs) {
public MyTabSheet(@NonNull T t, @NonNull NavigationManager navigationManager, ComponentFactory<T>... tabs) {
this(t, navigationManager, Arrays.asList(tabs));
}
public MyTabSheet(@NonNull T t, @NonNull NavigationManager navigationManager, Collection<? extends Tab<T>> tabs) {
public MyTabSheet(@NonNull T t, @NonNull NavigationManager navigationManager, Collection<? extends ComponentFactory<T>> tabs) {
this.t = t;
this.navigationManager = navigationManager;
for (Tab<T> tab : tabs) {
for (ComponentFactory<T> tab : tabs) {
addTab(tab);
}
}
@ -56,7 +55,7 @@ public class MyTabSheet<T> extends TabSheet {
return StreamSupport.stream(Spliterators.spliterator(iterator(), getComponentCount(), Spliterator.ORDERED), false).map(Component::getCaption).collect(Collectors.toList());
}
public void addTab(Tab<T> tab) {
public void addTab(ComponentFactory<T> tab) {
TabWrapper<T> wrapper = new TabWrapper<>(t, navigationManager, tab);
addComponent(wrapper);
addSelectedTabChangeListener(wrapper);
@ -79,18 +78,12 @@ public class MyTabSheet<T> extends TabSheet {
setSelectedTab(component);
}
public interface Tab<T> extends Ordered {
Component createContent(@NonNull T t, @NonNull NavigationManager navigationManager);
String getCaption();
}
private static class TabWrapper<T> extends CustomComponent implements SelectedTabChangeListener {
private final T t;
private final NavigationManager navigationManager;
private final Tab<T> tab;
private final ComponentFactory<T> tab;
private TabWrapper(@NonNull T t, @NonNull NavigationManager navigationManager, @NonNull Tab<T> tab) {
private TabWrapper(@NonNull T t, @NonNull NavigationManager navigationManager, @NonNull ComponentFactory<T> tab) {
this.t = t;
this.navigationManager = navigationManager;
this.tab = tab;

View file

@ -0,0 +1,33 @@
/*
* (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.ui.view.base.layout;
import com.vaadin.server.Resource;
/**
* @author lukas
* @since 17.06.18
*/
public interface PanelContentFactory<T> extends ComponentFactory<T> {
default Resource getIcon() {
return null;
}
default String getStyleName() {
return null;
}
}

View file

@ -0,0 +1,56 @@
/*
* (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.ui.view.base.layout;
import com.faendir.acra.ui.navigation.NavigationManager;
import com.vaadin.server.Sizeable;
import com.vaadin.ui.Component;
import com.vaadin.ui.Panel;
import com.vaadin.ui.themes.AcraTheme;
import org.springframework.lang.NonNull;
import java.util.List;
/**
* @author lukas
* @since 17.06.18
*/
public abstract class PanelFlexTab<T> implements ComponentFactory<T> {
@NonNull private final List<? extends PanelContentFactory<T>> factories;
public PanelFlexTab(@NonNull List<? extends PanelContentFactory<T>> factories) {
this.factories = factories;
}
@Override
public Component createContent(@NonNull T t, @NonNull NavigationManager navigationManager) {
FlexLayout layout = new FlexLayout(factories.stream().map(factory -> {
Panel panel = new Panel(factory.createContent(t, navigationManager));
panel.setCaption(factory.getCaption());
panel.setIcon(factory.getIcon());
panel.addStyleName(AcraTheme.NO_BACKGROUND);
if (factory.getStyleName() != null) {
panel.addStyleName(factory.getStyleName());
}
return panel;
}).toArray(Component[]::new));
layout.setWidth(100, Sizeable.Unit.PERCENTAGE);
Panel root = new Panel(layout);
root.setSizeFull();
root.addStyleNames(AcraTheme.NO_BACKGROUND, AcraTheme.NO_BORDER);
return root;
}
}

View file

@ -14,7 +14,7 @@
* limitations under the License.
*/
package com.faendir.acra.ui.view.base;
package com.faendir.acra.ui.view.base.navigation;
import com.faendir.acra.ui.navigation.NavigationManager;
import com.vaadin.navigator.View;

View file

@ -14,7 +14,7 @@
* limitations under the License.
*/
package com.faendir.acra.ui.view.base;
package com.faendir.acra.ui.view.base.navigation;
import com.vaadin.navigator.ViewChangeListener;
import org.springframework.lang.NonNull;

View file

@ -14,7 +14,7 @@
* limitations under the License.
*/
package com.faendir.acra.ui.view.base;
package com.faendir.acra.ui.view.base.navigation;
import com.faendir.acra.ui.navigation.MyNavigator;
import com.faendir.acra.ui.navigation.SingleViewProvider;

View file

@ -14,7 +14,7 @@
* limitations under the License.
*/
package com.faendir.acra.ui.view.base;
package com.faendir.acra.ui.view.base.popup;
import com.vaadin.ui.Button;
import com.vaadin.ui.Component;

View file

@ -14,7 +14,7 @@
* limitations under the License.
*/
package com.faendir.acra.ui.view.base;
package com.faendir.acra.ui.view.base.popup;
import com.vaadin.server.UserError;
import com.vaadin.ui.AbstractComponent;

View file

@ -18,7 +18,7 @@ package com.faendir.acra.ui.view.base.statistics;
import com.faendir.acra.model.QReport;
import com.faendir.acra.service.DataService;
import com.faendir.acra.ui.view.base.FlexLayout;
import com.faendir.acra.ui.view.base.layout.FlexLayout;
import com.querydsl.core.types.dsl.BooleanExpression;
import com.vaadin.ui.Alignment;
import com.vaadin.ui.Button;

View file

@ -23,8 +23,8 @@ import com.faendir.acra.service.DataService;
import com.faendir.acra.ui.annotation.RequiresAppPermission;
import com.faendir.acra.ui.navigation.MyNavigator;
import com.faendir.acra.ui.navigation.SingleParametrizedViewProvider;
import com.faendir.acra.ui.view.base.MyTabSheet;
import com.faendir.acra.ui.view.base.ParametrizedBaseView;
import com.faendir.acra.ui.view.base.layout.MyTabSheet;
import com.faendir.acra.ui.view.base.navigation.ParametrizedBaseView;
import com.faendir.acra.ui.view.bug.tabs.BugTab;
import com.vaadin.spring.annotation.SpringComponent;
import com.vaadin.spring.annotation.UIScope;

View file

@ -20,7 +20,7 @@ import com.faendir.acra.model.Permission;
import com.faendir.acra.service.DataService;
import com.faendir.acra.ui.annotation.RequiresAppPermission;
import com.faendir.acra.ui.navigation.NavigationManager;
import com.faendir.acra.ui.view.base.Popup;
import com.faendir.acra.ui.view.base.popup.Popup;
import com.vaadin.server.Sizeable;
import com.vaadin.spring.annotation.SpringComponent;
import com.vaadin.spring.annotation.ViewScope;

View file

@ -17,7 +17,7 @@
package com.faendir.acra.ui.view.bug.tabs;
import com.faendir.acra.model.Bug;
import com.faendir.acra.ui.view.base.MyTabSheet;
import com.faendir.acra.ui.view.base.layout.ComponentFactory;
import java.io.Serializable;
@ -25,5 +25,5 @@ import java.io.Serializable;
* @author Lukas
* @since 27.03.2018
*/
public interface BugTab extends MyTabSheet.Tab<Bug>, Serializable {
public interface BugTab extends ComponentFactory<Bug>, Serializable {
}

View file

@ -26,7 +26,7 @@ import com.faendir.acra.model.Report;
import com.faendir.acra.service.DataService;
import com.faendir.acra.ui.annotation.RequiresAppPermission;
import com.faendir.acra.ui.navigation.SingleParametrizedViewProvider;
import com.faendir.acra.ui.view.base.ParametrizedBaseView;
import com.faendir.acra.ui.view.base.navigation.ParametrizedBaseView;
import com.faendir.acra.util.Utils;
import com.vaadin.server.FileDownloader;
import com.vaadin.server.StreamResource;

View file

@ -20,7 +20,7 @@ import com.faendir.acra.security.SecurityUtils;
import com.faendir.acra.model.User;
import com.faendir.acra.service.UserService;
import com.faendir.acra.ui.BackendUI;
import com.faendir.acra.ui.view.base.BaseView;
import com.faendir.acra.ui.view.base.navigation.BaseView;
import com.faendir.acra.ui.navigation.SingleViewProvider;
import com.vaadin.navigator.ViewChangeListener;
import com.vaadin.server.UserError;

View file

@ -25,11 +25,11 @@ 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.BaseView;
import com.faendir.acra.ui.view.base.navigation.BaseView;
import com.faendir.acra.ui.view.base.MyCheckBox;
import com.faendir.acra.ui.view.base.MyGrid;
import com.faendir.acra.ui.view.base.Popup;
import com.faendir.acra.ui.view.base.ValidatedField;
import com.faendir.acra.ui.view.base.layout.MyGrid;
import com.faendir.acra.ui.view.base.popup.Popup;
import com.faendir.acra.ui.view.base.popup.ValidatedField;
import com.vaadin.navigator.ViewChangeListener;
import com.vaadin.spring.annotation.SpringComponent;
import com.vaadin.spring.annotation.UIScope;