diff --git a/src/main/java/org/cryptomator/ui/controls/FontAwesome5Icon.java b/src/main/java/org/cryptomator/ui/controls/FontAwesome5Icon.java index 66eda7556..ea6ba00d3 100644 --- a/src/main/java/org/cryptomator/ui/controls/FontAwesome5Icon.java +++ b/src/main/java/org/cryptomator/ui/controls/FontAwesome5Icon.java @@ -27,6 +27,7 @@ public enum FontAwesome5Icon { FILE("\uF15B"), // FILE_IMPORT("\uF56F"), // FOLDER_OPEN("\uF07C"), // + FUNNEL("\uF0B0"), // HAND_HOLDING_HEART("\uF4BE"), // HEART("\uF004"), // HDD("\uF0A0"), // diff --git a/src/main/java/org/cryptomator/ui/health/CheckDetailController.java b/src/main/java/org/cryptomator/ui/health/CheckDetailController.java index c467a5328..04d96b271 100644 --- a/src/main/java/org/cryptomator/ui/health/CheckDetailController.java +++ b/src/main/java/org/cryptomator/ui/health/CheckDetailController.java @@ -8,15 +8,34 @@ import org.cryptomator.ui.common.FxController; import javax.inject.Inject; import javafx.beans.binding.Binding; +import javafx.beans.binding.Bindings; +import javafx.beans.binding.BooleanBinding; import javafx.beans.binding.BooleanExpression; +import javafx.beans.property.BooleanProperty; import javafx.beans.property.ObjectProperty; +import javafx.beans.property.SimpleBooleanProperty; +import javafx.beans.property.SimpleObjectProperty; import javafx.beans.value.ObservableValue; import javafx.collections.FXCollections; import javafx.fxml.FXML; +import javafx.scene.control.ChoiceBox; import javafx.scene.control.ListView; +import javafx.scene.input.Clipboard; +import javafx.scene.input.ClipboardContent; +import javafx.util.StringConverter; +import java.util.Arrays; +import java.util.ResourceBundle; import java.util.function.Function; +import java.util.function.Predicate; import java.util.stream.Stream; +import static org.cryptomator.cryptofs.health.api.DiagnosticResult.Severity; +import static org.cryptomator.ui.health.Result.FixState.FIXABLE; +import static org.cryptomator.ui.health.Result.FixState.FIXED; +import static org.cryptomator.ui.health.Result.FixState.FIXING; +import static org.cryptomator.ui.health.Result.FixState.FIX_FAILED; +import static org.cryptomator.ui.health.Result.FixState.NOT_FIXABLE; + @HealthCheckScoped public class CheckDetailController implements FxController { @@ -35,30 +54,48 @@ public class CheckDetailController implements FxController { private final Binding countOfCritSeverity; private final Binding warnOrCritsExist; private final ResultListCellFactory resultListCellFactory; + private final ResultFixApplier resultFixApplier; + private final ResourceBundle resourceBundle; + + private final BooleanProperty fixAllInfoResultsExecuted; + private final BooleanBinding fixAllInfoResultsPossible; + private final ObjectProperty> resultsFilter; public ListView resultsListView; + public ChoiceBox severityChoiceBox; + public ChoiceBox fixStateChoiceBox; private Subscription resultSubscription; @Inject - public CheckDetailController(ObjectProperty selectedTask, ResultListCellFactory resultListCellFactory) { + public CheckDetailController(ObjectProperty selectedTask, ResultListCellFactory resultListCellFactory, ResultFixApplier resultFixApplier, ResourceBundle resourceBundle) { this.resultListCellFactory = resultListCellFactory; + this.resultFixApplier = resultFixApplier; + this.resourceBundle = resourceBundle; this.results = EasyBind.wrapList(FXCollections.observableArrayList()); this.check = selectedTask; this.checkState = selectedTask.flatMap(Check::stateProperty); this.checkName = selectedTask.map(Check::getName).orElse(""); this.checkRunning = BooleanExpression.booleanExpression(checkState.map(Check.CheckState.RUNNING::equals).orElse(false)); this.checkScheduled = BooleanExpression.booleanExpression(checkState.map(Check.CheckState.SCHEDULED::equals).orElse(false)); - this.checkSkipped =BooleanExpression.booleanExpression(checkState.map(Check.CheckState.SKIPPED::equals).orElse(false)); + this.checkSkipped = BooleanExpression.booleanExpression(checkState.map(Check.CheckState.SKIPPED::equals).orElse(false)); this.checkSucceeded = BooleanExpression.booleanExpression(checkState.map(Check.CheckState.SUCCEEDED::equals).orElse(false)); this.checkFailed = BooleanExpression.booleanExpression(checkState.map(Check.CheckState.ERROR::equals).orElse(false)); this.checkCancelled = BooleanExpression.booleanExpression(checkState.map(Check.CheckState.CANCELLED::equals).orElse(false)); this.checkFinished = checkSucceeded.or(checkFailed).or(checkCancelled); - this.countOfWarnSeverity = results.reduce(countSeverity(DiagnosticResult.Severity.WARN)); - this.countOfCritSeverity = results.reduce(countSeverity(DiagnosticResult.Severity.CRITICAL)); - this.warnOrCritsExist = EasyBind.combine(checkSucceeded, countOfWarnSeverity, countOfCritSeverity, (suceeded, warns, crits) -> suceeded && (warns.longValue() > 0 || crits.longValue() > 0) ); + this.countOfWarnSeverity = results.reduce(countSeverity(Severity.WARN)); + this.countOfCritSeverity = results.reduce(countSeverity(Severity.CRITICAL)); + this.warnOrCritsExist = EasyBind.combine(checkSucceeded, countOfWarnSeverity, countOfCritSeverity, (suceeded, warns, crits) -> suceeded && (warns.longValue() > 0 || crits.longValue() > 0)); + this.fixAllInfoResultsExecuted = new SimpleBooleanProperty(false); + this.fixAllInfoResultsPossible = Bindings.createBooleanBinding(() -> results.stream().anyMatch(this::isFixableInfoResult), results) // + .and(fixAllInfoResultsExecuted.not()); + this.resultsFilter = new SimpleObjectProperty<>(r -> true); selectedTask.addListener(this::selectedTaskChanged); } + private boolean isFixableInfoResult(Result r) { + return r.diagnosis().getSeverity() == Severity.INFO && r.getState() == FIXABLE; + } + private void selectedTaskChanged(ObservableValue observable, Check oldValue, Check newValue) { if (resultSubscription != null) { resultSubscription.unsubscribe(); @@ -66,6 +103,8 @@ public class CheckDetailController implements FxController { if (newValue != null) { resultSubscription = EasyBind.bindContent(results, newValue.getResults()); } + severityChoiceBox.setValue(null); + fixStateChoiceBox.setValue(null); } private Function, Long> countSeverity(DiagnosticResult.Severity severity) { @@ -74,8 +113,110 @@ public class CheckDetailController implements FxController { @FXML public void initialize() { - resultsListView.setItems(results); + resultsListView.setItems(results.filtered(resultsFilter)); resultsListView.setCellFactory(resultListCellFactory); + + severityChoiceBox.getItems().add(null); + severityChoiceBox.getItems().addAll(Arrays.stream(DiagnosticResult.Severity.values()).toList()); + severityChoiceBox.setConverter(new SeverityStringifier()); + severityChoiceBox.setValue(null); + + fixStateChoiceBox.getItems().add(null); + fixStateChoiceBox.getItems().addAll(Arrays.stream(Result.FixState.values()).toList()); + fixStateChoiceBox.setConverter(new FixStateStringifier()); + fixStateChoiceBox.setValue(null); + + resultsFilter.bind(Bindings.createObjectBinding(() -> this::filterResults, severityChoiceBox.valueProperty(), fixStateChoiceBox.valueProperty())); + } + + private boolean filterResults(Result r) { + var desiredFixState = fixStateChoiceBox.getValue(); + var desiredSeverity = severityChoiceBox.getValue(); + return (desiredFixState == null || r.getState() == desiredFixState) && (desiredSeverity == null || r.diagnosis().getSeverity() == desiredSeverity); + } + + @FXML + public void fixAllInfoResults() { + fixAllInfoResultsExecuted.setValue(true); + results.stream().filter(this::isFixableInfoResult).forEach(resultFixApplier::fix); + } + + + @FXML + public void copyResultDetails() { + var result = resultsListView.getSelectionModel().getSelectedItem(); + if (result != null) { + ClipboardContent clipboardContent = new ClipboardContent(); + clipboardContent.putString(result.diagnosis().toString()); + Clipboard.getSystemClipboard().setContent(clipboardContent); + } + } + + /* -- Internal classes -- */ + + class SeverityStringifier extends StringConverter { + + @Override + public String toString(Severity object) { + if (object == null) { + return resourceBundle.getString("health.result.severityFilter.all"); + } + return switch (object) { + case GOOD -> resourceBundle.getString("health.result.severityFilter.good"); + case INFO -> resourceBundle.getString("health.result.severityFilter.info"); + case WARN -> resourceBundle.getString("health.result.severityFilter.warn"); + case CRITICAL -> resourceBundle.getString("health.result.severityFilter.crit"); + }; + } + + @Override + public Severity fromString(String string) { + if (resourceBundle.getString("health.result.severityFilter.good").equals(string)) { + return Severity.GOOD; + } else if (resourceBundle.getString("health.result.severityFilter.info").equals(string)) { + return Severity.INFO; + } else if (resourceBundle.getString("health.result.severityFilter.warn").equals(string)) { + return Severity.WARN; + } else if (resourceBundle.getString("health.result.severityFilter.crit").equals(string)) { + return Severity.CRITICAL; + } else { + return null; + } + } + } + + class FixStateStringifier extends StringConverter { + + @Override + public String toString(Result.FixState object) { + if (object == null) { + return resourceBundle.getString("health.result.fixStateFilter.all"); + } + return switch (object) { + case FIXABLE -> resourceBundle.getString("health.result.fixStateFilter.fixable"); + case NOT_FIXABLE -> resourceBundle.getString("health.result.fixStateFilter.notFixable"); + case FIXING -> resourceBundle.getString("health.result.fixStateFilter.fixing"); + case FIXED -> resourceBundle.getString("health.result.fixStateFilter.fixed"); + case FIX_FAILED -> resourceBundle.getString("health.result.fixStateFilter.fixFailed"); + }; + } + + @Override + public Result.FixState fromString(String string) { + if (resourceBundle.getString("health.result.fixStateFilter.fixable").equals(string)) { + return FIXABLE; + } else if (resourceBundle.getString("health.result.fixStateFilter.notFixable").equals(string)) { + return NOT_FIXABLE; + } else if (resourceBundle.getString("health.result.fixStateFilter.fixing").equals(string)) { + return FIXING; + } else if (resourceBundle.getString("health.result.fixStateFilter.fixed").equals(string)) { + return FIXED; + } else if (resourceBundle.getString("health.result.fixStateFilter.fixFailed").equals(string)) { + return FIX_FAILED; + } else { + return null; + } + } } /* Getter/Setter */ @@ -175,4 +316,12 @@ public class CheckDetailController implements FxController { public Check getCheck() { return check.get(); } + + public ObservableValue fixAllInfoResultsPossibleProperty() { + return fixAllInfoResultsPossible; + } + + public boolean getFixAllInfoResultsPossible() { + return fixAllInfoResultsPossible.getValue(); + } } diff --git a/src/main/java/org/cryptomator/ui/health/CheckExecutor.java b/src/main/java/org/cryptomator/ui/health/CheckExecutor.java index a9ee9a17f..c347f38cb 100644 --- a/src/main/java/org/cryptomator/ui/health/CheckExecutor.java +++ b/src/main/java/org/cryptomator/ui/health/CheckExecutor.java @@ -70,7 +70,7 @@ public class CheckExecutor { try (var masterkeyClone = masterkey.copy(); // var cryptor = CryptorProvider.forScheme(vaultConfig.getCipherCombo()).provide(masterkeyClone, csprng)) { c.getHealthCheck().check(vaultPath, vaultConfig, masterkeyClone, cryptor, diagnosis -> { - Platform.runLater(() -> c.getResults().add(Result.create(diagnosis))); + Platform.runLater(() -> c.getResults().add(Result.create(diagnosis, vaultPath, vaultConfig, masterkeyClone, cryptor))); highestResultSeverity = Comparators.max(highestResultSeverity, diagnosis.getSeverity()); }); } diff --git a/src/main/java/org/cryptomator/ui/health/CheckStateIconView.java b/src/main/java/org/cryptomator/ui/health/CheckStateIconView.java index 4f1a35f7a..ffb6771ea 100644 --- a/src/main/java/org/cryptomator/ui/health/CheckStateIconView.java +++ b/src/main/java/org/cryptomator/ui/health/CheckStateIconView.java @@ -31,7 +31,7 @@ public class CheckStateIconView extends FontAwesome5IconView { this.severity = EasyBind.wrapNullable(check).mapObservable(Check::highestResultSeverityProperty).asOrdinary(); this.glyph.bind(Bindings.createObjectBinding(this::glyphForState, state, severity)); this.subscriptions = List.of( // - EasyBind.includeWhen(getStyleClass(), "glyph-icon-muted", Bindings.equal(state, Check.CheckState.SKIPPED).or(Bindings.equal(state, Check.CheckState.CANCELLED))), // + EasyBind.includeWhen(getStyleClass(), "glyph-icon-muted", Bindings.equal(state, Check.CheckState.SKIPPED).or(Bindings.equal(state, Check.CheckState.CANCELLED)).or(Bindings.equal(severity, DiagnosticResult.Severity.INFO))), // EasyBind.includeWhen(getStyleClass(), "glyph-icon-primary", Bindings.equal(severity, DiagnosticResult.Severity.GOOD)), // EasyBind.includeWhen(getStyleClass(), "glyph-icon-orange", Bindings.equal(severity, DiagnosticResult.Severity.WARN).or(Bindings.equal(severity, DiagnosticResult.Severity.CRITICAL))), // EasyBind.includeWhen(getStyleClass(), "glyph-icon-red", Bindings.equal(state, Check.CheckState.ERROR)) // diff --git a/src/main/java/org/cryptomator/ui/health/Result.java b/src/main/java/org/cryptomator/ui/health/Result.java index 8327a1130..582e4f843 100644 --- a/src/main/java/org/cryptomator/ui/health/Result.java +++ b/src/main/java/org/cryptomator/ui/health/Result.java @@ -1,10 +1,14 @@ package org.cryptomator.ui.health; +import org.cryptomator.cryptofs.VaultConfig; import org.cryptomator.cryptofs.health.api.DiagnosticResult; +import org.cryptomator.cryptolib.api.Cryptor; +import org.cryptomator.cryptolib.api.Masterkey; import javafx.beans.Observable; import javafx.beans.property.ObjectProperty; import javafx.beans.property.SimpleObjectProperty; +import java.nio.file.Path; record Result(DiagnosticResult diagnosis, ObjectProperty fixState) { @@ -16,8 +20,8 @@ record Result(DiagnosticResult diagnosis, ObjectProperty fixState) { FIX_FAILED } - public static Result create(DiagnosticResult diagnosis) { - FixState initialState = diagnosis.getSeverity() == DiagnosticResult.Severity.WARN ? FixState.FIXABLE : FixState.NOT_FIXABLE; + public static Result create(DiagnosticResult diagnosis, Path vaultPath, VaultConfig config, Masterkey masterkey, Cryptor cryptor) { + FixState initialState = diagnosis.getFix(vaultPath, config, masterkey, cryptor).map( _f -> FixState.FIXABLE).orElse(FixState.NOT_FIXABLE); return new Result(diagnosis, new SimpleObjectProperty<>(initialState)); } diff --git a/src/main/java/org/cryptomator/ui/health/ResultFixApplier.java b/src/main/java/org/cryptomator/ui/health/ResultFixApplier.java index 3dc91e33b..2654da221 100644 --- a/src/main/java/org/cryptomator/ui/health/ResultFixApplier.java +++ b/src/main/java/org/cryptomator/ui/health/ResultFixApplier.java @@ -23,6 +23,8 @@ import java.util.concurrent.atomic.AtomicReference; @HealthCheckScoped class ResultFixApplier { + private static final Logger LOG = LoggerFactory.getLogger(ResultFixApplier.class); + private final Path vaultPath; private final SecureRandom csprng; private final Masterkey masterkey; @@ -40,25 +42,34 @@ class ResultFixApplier { public CompletionStage fix(Result result) { Preconditions.checkArgument(result.getState() == Result.FixState.FIXABLE); - result.setState(Result.FixState.FIXING); - return CompletableFuture.runAsync(() -> fix(result.diagnosis()), sequentialExecutor) + return CompletableFuture.runAsync(() -> result.setState(Result.FixState.FIXING), Platform::runLater) // + .thenRunAsync(() -> fix(result.diagnosis()), sequentialExecutor) // .whenCompleteAsync((unused, throwable) -> { - var fixed = throwable == null ? Result.FixState.FIXED : Result.FixState.FIX_FAILED; - result.setState(fixed); + final Result.FixState s; + if (throwable == null) { + LOG.debug("Fix for {} applied successful.", result.diagnosis().getClass().getName()); + s = Result.FixState.FIXED; + } else { + LOG.error("Failed to apply fix for {}", result.diagnosis().getClass().getName(), throwable); + s = Result.FixState.FIX_FAILED; + } + result.setState(s); }, Platform::runLater); } - public void fix(DiagnosticResult diagnosis) { - Preconditions.checkArgument(diagnosis.getSeverity() == DiagnosticResult.Severity.WARN, "Unfixable result"); + private void fix(DiagnosticResult diagnosis) { try (var masterkeyClone = masterkey.copy(); // var cryptor = CryptorProvider.forScheme(vaultConfig.getCipherCombo()).provide(masterkeyClone, csprng)) { - diagnosis.fix(vaultPath, vaultConfig, masterkeyClone, cryptor); + diagnosis.getFix(vaultPath, vaultConfig, masterkeyClone, cryptor) // + .orElseThrow(() -> new IllegalStateException("No fix for diagnosis " + diagnosis.getClass().getName() + " implemented.")) // + .apply(); } catch (Exception e) { throw new FixFailedException(e); } } public static class FixFailedException extends CompletionException { + private FixFailedException(Throwable cause) { super(cause); } diff --git a/src/main/java/org/cryptomator/ui/health/ResultListCellController.java b/src/main/java/org/cryptomator/ui/health/ResultListCellController.java index d655d0058..35889ec26 100644 --- a/src/main/java/org/cryptomator/ui/health/ResultListCellController.java +++ b/src/main/java/org/cryptomator/ui/health/ResultListCellController.java @@ -12,7 +12,6 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.inject.Inject; -import javafx.application.Platform; import javafx.beans.binding.Bindings; import javafx.beans.binding.BooleanBinding; import javafx.beans.binding.ObjectBinding; @@ -34,7 +33,6 @@ public class ResultListCellController implements FxController { private static final FontAwesome5Icon WARN_ICON = FontAwesome5Icon.EXCLAMATION_TRIANGLE; private static final FontAwesome5Icon CRIT_ICON = FontAwesome5Icon.TIMES; - private final Logger LOG = LoggerFactory.getLogger(ResultListCellController.class); private final ObjectProperty result; private final ObservableValue severity; @@ -42,17 +40,17 @@ public class ResultListCellController implements FxController { private final ResultFixApplier fixApplier; private final ObservableValue fixState; private final ObjectBinding severityGlyph; - private final ObjectBinding fixGlyph; private final BooleanBinding fixable; private final BooleanBinding fixing; private final BooleanBinding fixed; private final BooleanBinding fixFailed; private final BooleanBinding fixRunningOrDone; + private final ObservableValue fixGlyph; private final List subscriptions; - private final Tooltip fixSuccess; - private final Tooltip fixFail; - + private final Tooltip fixStateTip; + private final Tooltip severityTip; private AutoAnimator fixRunningRotator; + private final ResourceBundle resourceBundle; /* FXML */ public FontAwesome5IconView severityView; @@ -60,23 +58,54 @@ public class ResultListCellController implements FxController { @Inject public ResultListCellController(ResultFixApplier fixApplier, ResourceBundle resourceBundle) { + this.resourceBundle = resourceBundle; this.result = new SimpleObjectProperty<>(null); this.severity = result.map(Result::diagnosis).map(DiagnosticResult::getSeverity); this.description = result.map(Result::getDescription).orElse(""); this.fixApplier = fixApplier; this.fixState = result.flatMap(Result::fixState); this.severityGlyph = Bindings.createObjectBinding(this::getSeverityGlyph, result); - this.fixGlyph = Bindings.createObjectBinding(this::getFixGlyph, fixState); this.fixable = Bindings.createBooleanBinding(this::isFixable, fixState); this.fixing = Bindings.createBooleanBinding(this::isFixing, fixState); this.fixed = Bindings.createBooleanBinding(this::isFixed, fixState); this.fixFailed = Bindings.createBooleanBinding(this::isFixFailed, fixState); this.fixRunningOrDone = fixing.or(fixed).or(fixFailed); + this.fixGlyph = fixState.map(this::getFixGlyph); this.subscriptions = new ArrayList<>(); - this.fixSuccess = new Tooltip(resourceBundle.getString("health.fix.successTip")); - this.fixFail = new Tooltip(resourceBundle.getString("health.fix.failTip")); - fixSuccess.setShowDelay(Duration.millis(100)); - fixFail.setShowDelay(Duration.millis(100)); + + this.fixStateTip = new Tooltip(); + fixStateTip.textProperty().bind(fixState.map(this::getFixStateDescription)); + fixStateTip.setShowDelay(Duration.millis(100)); + + this.severityTip = new Tooltip(); + severityTip.textProperty().bind(severity.map(this::getSeverityDescription)); + severityTip.setShowDelay(Duration.millis(150)); + } + + public FontAwesome5Icon getFixGlyph(Result.FixState state) { + return switch (state) { + case FIXING -> FontAwesome5Icon.SPINNER; + case FIXED -> FontAwesome5Icon.CHECK; + case FIX_FAILED -> FontAwesome5Icon.TIMES; + default -> null; + }; + } + + private String getFixStateDescription(Result.FixState fixState) { + return switch (fixState) { + case FIXED -> resourceBundle.getString("health.fix.successTip"); + case FIX_FAILED -> resourceBundle.getString("health.fix.failTip"); + default -> ""; + }; + } + + private String getSeverityDescription(DiagnosticResult.Severity severity) { + return resourceBundle.getString(switch (severity) { + case GOOD -> "health.result.severityTip.good"; + case INFO -> "health.result.severityTip.info"; + case WARN -> "health.result.severityTip.warn"; + case CRITICAL -> "health.result.severityTip.crit"; + }); } @FXML @@ -93,22 +122,19 @@ public class ResultListCellController implements FxController { .onCondition(fixing) // .afterStop(() -> fixView.setRotate(0)) // .build(); + fixState.addListener(((observable, oldValue, newValue) -> { + if (newValue == Result.FixState.FIXED || newValue == Result.FixState.FIX_FAILED) { + Tooltip.install(fixView, fixStateTip); + } + })); + Tooltip.install(severityView, severityTip); } @FXML public void fix() { Result r = result.get(); if (r != null) { - fixApplier.fix(r).whenCompleteAsync(this::fixFinished, Platform::runLater); - } - } - - private void fixFinished(Void unused, Throwable exception) { - if (exception != null) { - LOG.error("Failed to apply fix", exception); - Tooltip.install(fixView, fixFail); - } else { - Tooltip.install(fixView, fixSuccess); + fixApplier.fix(r); } } @@ -152,22 +178,10 @@ public class ResultListCellController implements FxController { }; } - public ObjectBinding fixGlyphProperty() { + public ObservableValue fixGlyphProperty() { return fixGlyph; } - public FontAwesome5Icon getFixGlyph() { - if (fixState.getValue() == null) { - return null; - } - return switch (fixState.getValue()) { - case NOT_FIXABLE, FIXABLE -> null; - case FIXING -> FontAwesome5Icon.SPINNER; - case FIXED -> FontAwesome5Icon.CHECK; - case FIX_FAILED -> FontAwesome5Icon.TIMES; - }; - } - public BooleanBinding fixableProperty() { return fixable; } diff --git a/src/main/resources/fxml/health_check_details.fxml b/src/main/resources/fxml/health_check_details.fxml index 6dd7d224e..ac12a09c1 100644 --- a/src/main/resources/fxml/health_check_details.fxml +++ b/src/main/resources/fxml/health_check_details.fxml @@ -5,25 +5,59 @@ + + + + + + - + spacing="12"> + + + + + + - + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/main/resources/fxml/health_check_list.fxml b/src/main/resources/fxml/health_check_list.fxml index 1edb004df..3124e15c6 100644 --- a/src/main/resources/fxml/health_check_list.fxml +++ b/src/main/resources/fxml/health_check_list.fxml @@ -12,7 +12,7 @@ diff --git a/src/main/resources/fxml/health_start.fxml b/src/main/resources/fxml/health_start.fxml index 9edc65f50..cc65aaaaa 100644 --- a/src/main/resources/fxml/health_start.fxml +++ b/src/main/resources/fxml/health_start.fxml @@ -16,7 +16,7 @@ diff --git a/src/main/resources/i18n/strings.properties b/src/main/resources/i18n/strings.properties index df33193a7..accf9e5b6 100644 --- a/src/main/resources/i18n/strings.properties +++ b/src/main/resources/i18n/strings.properties @@ -219,7 +219,27 @@ health.check.detail.checkFinished=The check finished successfully. health.check.detail.checkFinishedAndFound=The check finished running. Please review the results. health.check.detail.checkFailed=The check exited due to an error. health.check.detail.checkCancelled=The check was cancelled. +health.check.detail.listFilters.label=Filter +health.check.detail.listFilters.severity=Severity +health.check.detail.listFilters.fixState=Fix state +health.check.detail.fixAllSpecificBtn=Fix all of type health.check.exportBtn=Export Report +## Result view +health.result.severityFilter.all=Severity - All +health.result.severityFilter.good=Good +health.result.severityFilter.info=Info +health.result.severityFilter.warn=Warning +health.result.severityFilter.crit=Critical +health.result.severityTip.good=Severity: Good\nNormal vault structure. +health.result.severityTip.info=Severity: Info\nVault structure intact, fix suggested. +health.result.severityTip.warn=Severity: Warning\nVault structure corrupted, fix highly advised. +health.result.severityTip.crit=Severity: Critical\nVault structure corrupted, data loss determined. +health.result.fixStateFilter.all=Fix state - All +health.result.fixStateFilter.fixable=Fixable +health.result.fixStateFilter.notFixable=Not fixable +health.result.fixStateFilter.fixing=Fixing… +health.result.fixStateFilter.fixed=Fixed +health.result.fixStateFilter.fixFailed=Fix failed ## Fix Application health.fix.fixBtn=Fix health.fix.successTip=Fix successful