Merge branch 'develop' into feature/vault-format-8

[ci skip]
This commit is contained in:
Sebastian Stenzel
2021-01-19 15:05:15 +01:00
89 changed files with 1397 additions and 574 deletions

View File

@@ -5,28 +5,12 @@ labels: type:bug
---
<!--
**************************************
* *
* ⚠️⚠️⚠️ READ CAREFULLY ⚠️⚠️⚠️ *
* *
**************************************
Do you want to ask a QUESTION? Are you looking for SUPPORT?
We're happy to help you via our support channels! Please read: https://github.com/cryptomator/cryptomator/blob/develop/SUPPORT.md
By filing an issue, you are expected to comply with our code of conduct: https://github.com/cryptomator/cryptomator/blob/develop/.github/CODE_OF_CONDUCT.md
Of course, we also expect you to search for existing similar issues first! ;) https://github.com/cryptomator/cryptomator/issues?q=
Please make sure to:
- Comply with our code of conduct: https://github.com/cryptomator/cryptomator/blob/develop/.github/CODE_OF_CONDUCT.md
- Search for existing similar issues first: https://github.com/cryptomator/cryptomator/issues?q=
⚠️ IMPORTANT: If you don't stick to this template, the issue will get closed.
*****************************************************************************
* *
* To proof that you read this, please remove the X from the line below: *
* *
*****************************************************************************
-->
<!-- oooXooo -->
### Description
@@ -34,7 +18,7 @@ Of course, we also expect you to search for existing similar issues first! ;) ht
### System Setup
* Operating system and version: [Windows/macOS/Linux + Version]
* Operating system and version: [Windows/macOS/Linux + Version ( + Desktop Environment, if Linux)]
* Cryptomator version: [Shown in the settings]
* Volume type: [Dokany/FUSE/WebDAV, shown in the settings]
@@ -61,7 +45,6 @@ Of course, we also expect you to search for existing similar issues first! ;) ht
[Any additional information, log files, screenshots, configuration, or data that might be necessary to reproduce the issue.]
<!--
If you want to add the log file or screenshots, please add them as attachments. If your log file seems empty and doesn't show any errors, you may enable the debug mode first. Here is how to do that: https://community.cryptomator.org/t/how-do-i-enable-debug-mode/36
Then reproduce the problem to ensure all important information is contained in there. You may use test data or redact sensitive information from the log file.
@@ -70,5 +53,4 @@ Log file location:
- Windows: %appdata%/Cryptomator
- macOS: ~/Library/Logs/Cryptomator
- Linux: ~/.local/share/Cryptomator/logs
-->

View File

@@ -1,8 +1,8 @@
blank_issues_enabled: false
contact_links:
- name: Cryptomator Community
- name: Help & Support
url: https://community.cryptomator.org/
about: Please ask and answer questions here
- name: Documentation
about: You will find answers in our community forum
- name: User Manual
url: https://docs.cryptomator.org/
about: Get instructions on how to use Cryptomator
about: Read the Cryptomator documentation here

View File

@@ -5,14 +5,9 @@ labels: type:feature-request
---
<!--
Do you want to ask a QUESTION? Are you looking for SUPPORT?
We're happy to help you via our support channels! Please read: https://github.com/cryptomator/cryptomator/blob/develop/SUPPORT.md
By filing a feature request, you are expected to comply with our code of conduct: https://github.com/cryptomator/cryptomator/blob/develop/.github/CODE_OF_CONDUCT.md
Of course, we also expect you to search for existing similar feature requests first! ;)
Please make sure to:
- Comply with our code of conduct: https://github.com/cryptomator/cryptomator/blob/develop/.github/CODE_OF_CONDUCT.md
- Search for existing similar issues first: https://github.com/cryptomator/cryptomator/issues?q=
-->
### Summary
@@ -29,4 +24,4 @@ Of course, we also expect you to search for existing similar feature requests fi
### Additional Context
[Add any other context or screenshots about the feature request here.]
[Add any other context or screenshots about the feature request here.]

View File

View File

View File

@@ -6,20 +6,32 @@ on:
jobs:
closeTemplateViolation:
name: Close bug reports that violate the issue template
name: Validate bug report against issue template
runs-on: ubuntu-latest
if: contains(github.event.issue.labels.*.name, 'type:bug')
steps:
- if: |
contains(github.event.issue.labels.*.name, 'type:bug')
&& (
!contains(github.event.issue.body, '<!-- oooooo -->')
|| !contains(github.event.issue.body, '### Description')
)
name: Close Issue
- name: Check "Description"
if: |
!contains(github.event.issue.body, env.MUST_CONTAIN)
|| contains(toJson(github.event.issue.body), env.MUST_NOT_CONTAIN)
run: exit 1
env:
MUST_CONTAIN: '### Description'
MUST_NOT_CONTAIN: '### Description\r\n\r\n[Summarize your problem.]\r\n\r\n### System Setup'
- name: Check "Steps to Reproduce"
if: |
!contains(github.event.issue.body, env.MUST_CONTAIN)
|| contains(toJson(github.event.issue.body), env.MUST_NOT_CONTAIN)
run: exit 1
env:
MUST_CONTAIN: '### Steps to Reproduce'
MUST_NOT_CONTAIN: '### Steps to Reproduce\r\n\r\n1. [First step]\r\n2. [Second step]\r\n3. [and so on…]\r\n\r\n#### Expected Behavior'
- name: Close issue if one of the checks failed
if: ${{ failure() }}
uses: peter-evans/close-issue@v1
with:
comment: |
This bug report did ignore our issue template. 😞
Auto-closing this issue, since it is most likely not useful.
_This decision was made by a bot. If you think the bot is wrong, let us know and we'll reopen this issue._
_This decision was made by a bot. If you think the bot is wrong, let us know and we'll reopen this issue._

View File

@@ -2,7 +2,7 @@
<configuration default="false" name="Cryptomator Linux" type="Application" factoryName="Application">
<option name="MAIN_CLASS_NAME" value="org.cryptomator.launcher.Cryptomator" />
<module name="launcher" />
<option name="VM_PARAMETERS" value="-Djdk.gtk.version=2 -Duser.language=en -Dcryptomator.settingsPath=&quot;~/.config/Cryptomator/settings.json&quot; -Dcryptomator.ipcPortPath=&quot;~/.config/Cryptomator/ipcPort.bin&quot; -Dcryptomator.logDir=&quot;~/.local/share/Cryptomator/logs&quot; -Dcryptomator.mountPointsDir=&quot;~/.local/share/Cryptomator/mnt&quot; -Xss20m -Xmx512m" />
<option name="VM_PARAMETERS" value="-Djdk.gtk.version=2 -Duser.language=en -Dcryptomator.settingsPath=&quot;~/.config/Cryptomator/settings.json&quot; -Dcryptomator.ipcPortPath=&quot;~/.config/Cryptomator/ipcPort.bin&quot; -Dcryptomator.logDir=&quot;~/.local/share/Cryptomator/logs&quot; -Dcryptomator.mountPointsDir=&quot;~/.local/share/Cryptomator/mnt&quot; -Dcryptomator.showTrayIcon=true -Xss20m -Xmx512m" />
<method v="2">
<option name="Make" enabled="true" />
</method>

View File

@@ -2,7 +2,7 @@
<configuration default="false" name="Cryptomator Linux Dev" type="Application" factoryName="Application">
<option name="MAIN_CLASS_NAME" value="org.cryptomator.launcher.Cryptomator" />
<module name="launcher" />
<option name="VM_PARAMETERS" value="-Djdk.gtk.version=2 -Duser.language=en -Dcryptomator.settingsPath=&quot;~/.config/Cryptomator-Dev/settings.json&quot; -Dcryptomator.ipcPortPath=&quot;~/.config/Cryptomator-Dev/ipcPort.bin&quot; -Dcryptomator.logDir=&quot;~/.local/share/Cryptomator-Dev/logs&quot; -Dcryptomator.mountPointsDir=&quot;~/.local/share/Cryptomator-Dev/mnt&quot; -Dfuse.experimental=&quot;true&quot; -Xss20m -Xmx512m" />
<option name="VM_PARAMETERS" value="-Djdk.gtk.version=2 -Duser.language=en -Dcryptomator.settingsPath=&quot;~/.config/Cryptomator-Dev/settings.json&quot; -Dcryptomator.ipcPortPath=&quot;~/.config/Cryptomator-Dev/ipcPort.bin&quot; -Dcryptomator.logDir=&quot;~/.local/share/Cryptomator-Dev/logs&quot; -Dcryptomator.mountPointsDir=&quot;~/.local/share/Cryptomator-Dev/mnt&quot; -Dcryptomator.showTrayIcon=true -Dfuse.experimental=&quot;true&quot; -Xss20m -Xmx512m" />
<method v="2">
<option name="Make" enabled="true" />
</method>

View File

@@ -2,7 +2,7 @@
<configuration default="false" name="Cryptomator Windows" type="Application" factoryName="Application">
<option name="MAIN_CLASS_NAME" value="org.cryptomator.launcher.Cryptomator" />
<module name="launcher" />
<option name="VM_PARAMETERS" value="-Duser.language=en -Dcryptomator.settingsPath=&quot;~/AppData/Roaming/Cryptomator/settings.json&quot; -Dcryptomator.ipcPortPath=&quot;~/AppData/Roaming/Cryptomator/ipcPort.bin&quot; -Dcryptomator.logDir=&quot;~/AppData/Roaming/Cryptomator&quot; -Dcryptomator.keychainPath=&quot;~/AppData/Roaming/Cryptomator/keychain.json&quot; -Dcryptomator.mountPointsDir=&quot;~/Cryptomator&quot; -Xss2m -Xmx512m" />
<option name="VM_PARAMETERS" value="-Duser.language=en -Dcryptomator.settingsPath=&quot;~/AppData/Roaming/Cryptomator/settings.json&quot; -Dcryptomator.ipcPortPath=&quot;~/AppData/Roaming/Cryptomator/ipcPort.bin&quot; -Dcryptomator.logDir=&quot;~/AppData/Roaming/Cryptomator&quot; -Dcryptomator.keychainPath=&quot;~/AppData/Roaming/Cryptomator/keychain.json&quot; -Dcryptomator.mountPointsDir=&quot;~/Cryptomator&quot; -Dcryptomator.showTrayIcon=true -Xss2m -Xmx512m" />
<method v="2">
<option name="Make" enabled="true" />
</method>

View File

@@ -2,7 +2,7 @@
<configuration default="false" name="Cryptomator Windows Dev" type="Application" factoryName="Application">
<option name="MAIN_CLASS_NAME" value="org.cryptomator.launcher.Cryptomator" />
<module name="launcher" />
<option name="VM_PARAMETERS" value="-Duser.language=en -Dcryptomator.settingsPath=&quot;~/AppData/Roaming/Cryptomator-Dev/settings.json&quot; -Dcryptomator.ipcPortPath=&quot;~/AppData/Roaming/Cryptomator-Dev/ipcPort.bin&quot; -Dcryptomator.logDir=&quot;~/AppData/Roaming/Cryptomator-Dev&quot; -Dcryptomator.keychainPath=&quot;~/AppData/Roaming/Cryptomator-Dev/keychain.json&quot; -Dcryptomator.mountPointsDir=&quot;~/Cryptomator-Dev&quot; -Dfuse.experimental=&quot;true&quot; -Xss2m -Xmx512m" />
<option name="VM_PARAMETERS" value="-Duser.language=en -Dcryptomator.settingsPath=&quot;~/AppData/Roaming/Cryptomator-Dev/settings.json&quot; -Dcryptomator.ipcPortPath=&quot;~/AppData/Roaming/Cryptomator-Dev/ipcPort.bin&quot; -Dcryptomator.logDir=&quot;~/AppData/Roaming/Cryptomator-Dev&quot; -Dcryptomator.keychainPath=&quot;~/AppData/Roaming/Cryptomator-Dev/keychain.json&quot; -Dcryptomator.mountPointsDir=&quot;~/Cryptomator-Dev&quot; -Dfuse.experimental=&quot;true&quot; -Dcryptomator.showTrayIcon=true -Xss2m -Xmx512m" />
<method v="2">
<option name="Make" enabled="true" />
</method>

View File

@@ -5,7 +5,7 @@
</envs>
<option name="MAIN_CLASS_NAME" value="org.cryptomator.launcher.Cryptomator" />
<module name="launcher" />
<option name="VM_PARAMETERS" value="-Duser.language=en -Dcryptomator.settingsPath=&quot;~/Library/Application Support/Cryptomator/settings.json&quot; -Dcryptomator.ipcPortPath=&quot;~/Library/Application Support/Cryptomator/ipcPort.bin&quot; -Dcryptomator.logDir=&quot;~/Library/Logs/Cryptomator&quot; -Xss2m -Xmx512m -ea" />
<option name="VM_PARAMETERS" value="-Duser.language=en -Dcryptomator.settingsPath=&quot;~/Library/Application Support/Cryptomator/settings.json&quot; -Dcryptomator.ipcPortPath=&quot;~/Library/Application Support/Cryptomator/ipcPort.bin&quot; -Dcryptomator.logDir=&quot;~/Library/Logs/Cryptomator&quot; -Dcryptomator.showTrayIcon=true -Xss2m -Xmx512m -ea" />
<method v="2">
<option name="Make" enabled="true" />
</method>

View File

@@ -5,7 +5,7 @@
</envs>
<option name="MAIN_CLASS_NAME" value="org.cryptomator.launcher.Cryptomator" />
<module name="launcher" />
<option name="VM_PARAMETERS" value="-Duser.language=en -Dcryptomator.settingsPath=&quot;~/Library/Application Support/Cryptomator-Dev/settings.json&quot; -Dcryptomator.ipcPortPath=&quot;~/Library/Application Support/Cryptomator-Dev/ipcPort.bin&quot; -Dcryptomator.logDir=&quot;~/Library/Logs/Cryptomator-Dev&quot; -Xss2m -Xmx512m -ea" />
<option name="VM_PARAMETERS" value="-Duser.language=en -Dcryptomator.settingsPath=&quot;~/Library/Application Support/Cryptomator-Dev/settings.json&quot; -Dcryptomator.ipcPortPath=&quot;~/Library/Application Support/Cryptomator-Dev/ipcPort.bin&quot; -Dcryptomator.logDir=&quot;~/Library/Logs/Cryptomator-Dev&quot; -Dcryptomator.showTrayIcon=true -Xss2m -Xmx512m -ea" />
<method v="2">
<option name="Make" enabled="true" />
</method>

View File

@@ -21,14 +21,13 @@ import java.util.stream.StreamSupport;
public class Environment {
private static final Logger LOG = LoggerFactory.getLogger(Environment.class);
private static final String USER_HOME = System.getProperty("user.home");
private static final Path RELATIVE_HOME_DIR = Paths.get("~");
private static final Path ABSOLUTE_HOME_DIR = Paths.get(USER_HOME);
private static final char PATH_LIST_SEP = ':';
private static final int DEFAULT_MIN_PW_LENGTH = 8;
@Inject
public Environment() {
LOG.debug("user.home: {}", System.getProperty("user.home"));
LOG.debug("java.library.path: {}", System.getProperty("java.library.path"));
LOG.debug("user.language: {}", System.getProperty("user.language"));
LOG.debug("user.region: {}", System.getProperty("user.region"));
@@ -40,6 +39,7 @@ public class Environment {
LOG.debug("cryptomator.mountPointsDir: {}", System.getProperty("cryptomator.mountPointsDir"));
LOG.debug("cryptomator.minPwLength: {}", System.getProperty("cryptomator.minPwLength"));
LOG.debug("cryptomator.buildNumber: {}", System.getProperty("cryptomator.buildNumber"));
LOG.debug("cryptomator.showTrayIcon: {}", System.getProperty("cryptomator.showTrayIcon"));
LOG.debug("fuse.experimental: {}", Boolean.getBoolean("fuse.experimental"));
}
@@ -75,6 +75,11 @@ public class Environment {
return getInt("cryptomator.minPwLength", DEFAULT_MIN_PW_LENGTH);
}
public boolean showTrayIcon() {
return Boolean.getBoolean("cryptomator.showTrayIcon");
}
@Deprecated // TODO: remove as soon as custom mount path works properly on Win+Fuse
public boolean useExperimentalFuse() {
return Boolean.getBoolean("fuse.experimental");
}
@@ -92,8 +97,13 @@ public class Environment {
String value = System.getProperty(propertyName);
return Optional.ofNullable(value).map(Paths::get);
}
// visible for testing
// visible for testing
Path getHomeDir() {
return getPath("user.home").orElseThrow();
}
// visible for testing
Stream<Path> getPaths(String propertyName) {
Stream<String> rawSettingsPaths = getRawList(propertyName, PATH_LIST_SEP);
return rawSettingsPaths.filter(Predicate.not(Strings::isNullOrEmpty)).map(Paths::get).map(this::replaceHomeDir);
@@ -101,7 +111,7 @@ public class Environment {
private Path replaceHomeDir(Path path) {
if (path.startsWith(RELATIVE_HOME_DIR)) {
return ABSOLUTE_HOME_DIR.resolve(RELATIVE_HOME_DIR.relativize(path));
return getHomeDir().resolve(RELATIVE_HOME_DIR.relativize(path));
} else {
return path;
}

View File

@@ -9,6 +9,7 @@
package org.cryptomator.common.settings;
import org.apache.commons.lang3.SystemUtils;
import org.cryptomator.common.Environment;
import javafx.beans.Observable;
import javafx.beans.property.BooleanProperty;
@@ -39,7 +40,8 @@ public class Settings {
public static final UiTheme DEFAULT_THEME = UiTheme.LIGHT;
public static final KeychainBackend DEFAULT_KEYCHAIN_BACKEND = SystemUtils.IS_OS_WINDOWS ? KeychainBackend.WIN_SYSTEM_KEYCHAIN : SystemUtils.IS_OS_MAC ? KeychainBackend.MAC_SYSTEM_KEYCHAIN : KeychainBackend.GNOME;
public static final NodeOrientation DEFAULT_USER_INTERFACE_ORIENTATION = NodeOrientation.LEFT_TO_RIGHT;
private static final String DEFAULT_LICENSE_KEY = "";
public static final String DEFAULT_LICENSE_KEY = "";
public static final boolean DEFAULT_SHOW_MINIMIZE_BUTTON = false;
private final ObservableList<VaultSettings> directories = FXCollections.observableArrayList(VaultSettings::observables);
private final BooleanProperty askedForUpdateCheck = new SimpleBooleanProperty(DEFAULT_ASKED_FOR_UPDATE_CHECK);
@@ -54,13 +56,17 @@ public class Settings {
private final ObjectProperty<KeychainBackend> keychainBackend = new SimpleObjectProperty<>(DEFAULT_KEYCHAIN_BACKEND);
private final ObjectProperty<NodeOrientation> userInterfaceOrientation = new SimpleObjectProperty<>(DEFAULT_USER_INTERFACE_ORIENTATION);
private final StringProperty licenseKey = new SimpleStringProperty(DEFAULT_LICENSE_KEY);
private final BooleanProperty showMinimizeButton = new SimpleBooleanProperty(DEFAULT_SHOW_MINIMIZE_BUTTON);
private final BooleanProperty showTrayIcon;
private Consumer<Settings> saveCmd;
/**
* Package-private constructor; use {@link SettingsProvider}.
*/
Settings() {
Settings(Environment env) {
this.showTrayIcon = new SimpleBooleanProperty(env.showTrayIcon());
directories.addListener(this::somethingChanged);
askedForUpdateCheck.addListener(this::somethingChanged);
checkForUpdates.addListener(this::somethingChanged);
@@ -74,6 +80,8 @@ public class Settings {
keychainBackend.addListener(this::somethingChanged);
userInterfaceOrientation.addListener(this::somethingChanged);
licenseKey.addListener(this::somethingChanged);
showMinimizeButton.addListener(this::somethingChanged);
showTrayIcon.addListener(this::somethingChanged);
}
void setSaveCmd(Consumer<Settings> saveCmd) {
@@ -141,4 +149,12 @@ public class Settings {
public StringProperty licenseKey() {
return licenseKey;
}
public BooleanProperty showMinimizeButton() {
return showMinimizeButton;
}
public BooleanProperty showTrayIcon() {
return showTrayIcon;
}
}

View File

@@ -9,19 +9,29 @@ import com.google.gson.TypeAdapter;
import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonToken;
import com.google.gson.stream.JsonWriter;
import org.cryptomator.common.Environment;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.inject.Inject;
import javax.inject.Singleton;
import javafx.geometry.NodeOrientation;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
@Singleton
public class SettingsJsonAdapter extends TypeAdapter<Settings> {
private static final Logger LOG = LoggerFactory.getLogger(SettingsJsonAdapter.class);
private final VaultSettingsJsonAdapter vaultSettingsJsonAdapter = new VaultSettingsJsonAdapter();
private final Environment env;
@Inject
public SettingsJsonAdapter(Environment env) {
this.env = env;
}
@Override
public void write(JsonWriter out, Settings value) throws IOException {
@@ -40,6 +50,8 @@ public class SettingsJsonAdapter extends TypeAdapter<Settings> {
out.name("uiOrientation").value(value.userInterfaceOrientation().get().name());
out.name("keychainBackend").value(value.keychainBackend().get().name());
out.name("licenseKey").value(value.licenseKey().get());
out.name("showMinimizeButton").value(value.showMinimizeButton().get());
out.name("showTrayIcon").value(value.showTrayIcon().get());
out.endObject();
}
@@ -53,7 +65,7 @@ public class SettingsJsonAdapter extends TypeAdapter<Settings> {
@Override
public Settings read(JsonReader in) throws IOException {
Settings settings = new Settings();
Settings settings = new Settings(env);
in.beginObject();
while (in.hasNext()) {
@@ -72,6 +84,8 @@ public class SettingsJsonAdapter extends TypeAdapter<Settings> {
case "uiOrientation" -> settings.userInterfaceOrientation().set(parseUiOrientation(in.nextString()));
case "keychainBackend" -> settings.keychainBackend().set(parseKeychainBackend(in.nextString()));
case "licenseKey" -> settings.licenseKey().set(in.nextString());
case "showMinimizeButton" -> settings.showMinimizeButton().set(in.nextBoolean());
case "showTrayIcon" -> settings.showTrayIcon().set(in.nextBoolean());
default -> {
LOG.warn("Unsupported vault setting found in JSON: " + name);
in.skipValue();
@@ -137,5 +151,4 @@ public class SettingsJsonAdapter extends TypeAdapter<Settings> {
in.endArray();
return result;
}
}

View File

@@ -49,13 +49,14 @@ public class SettingsProvider implements Supplier<Settings> {
private final AtomicReference<ScheduledFuture<?>> scheduledSaveCmd = new AtomicReference<>();
private final Supplier<Settings> settings = Suppliers.memoize(this::load);
private final SettingsJsonAdapter settingsJsonAdapter = new SettingsJsonAdapter();
private final SettingsJsonAdapter settingsJsonAdapter;
private final Environment env;
private final ScheduledExecutorService scheduler;
private final Gson gson;
@Inject
public SettingsProvider(Environment env, ScheduledExecutorService scheduler) {
public SettingsProvider(SettingsJsonAdapter settingsJsonAdapter, Environment env, ScheduledExecutorService scheduler) {
this.settingsJsonAdapter = settingsJsonAdapter;
this.env = env;
this.scheduler = scheduler;
this.gson = new GsonBuilder() //
@@ -70,7 +71,7 @@ public class SettingsProvider implements Supplier<Settings> {
}
private Settings load() {
Settings settings = env.getSettingsPath().flatMap(this::tryLoad).findFirst().orElse(new Settings());
Settings settings = env.getSettingsPath().flatMap(this::tryLoad).findFirst().orElse(new Settings(env));
settings.setSaveCmd(this::scheduleSave);
return settings;
}
@@ -90,7 +91,7 @@ public class SettingsProvider implements Supplier<Settings> {
}
} catch (NoSuchFileException e) {
return Stream.empty();
} catch (IOException e) {
} catch (IOException | JsonParseException e) {
LOG.warn("Exception while loading settings from " + path, e);
return Stream.empty();
}

View File

@@ -71,10 +71,14 @@ public class DokanyVolume extends AbstractVolume {
@Override
public void unmountForced() {
mount.close(); //TODO: with next dokany-nio-release, change this to unmountForced()
mount.unmountForced();
cleanupMountPoint();
}
@Override
public boolean supportsForcedUnmount() {
return true;
}
@Override
public boolean isSupported() {
return DokanyVolume.isSupportedStatic();

View File

@@ -106,7 +106,7 @@ public class Vault {
if (vaultSettings.usesReadOnlyMode().get()) {
flags.add(FileSystemFlags.READONLY);
}
if (vaultSettings.filenameLengthLimit().get() == -1) {
if (!flags.contains(FileSystemFlags.READONLY) && vaultSettings.filenameLengthLimit().get() == -1) {
LOG.debug("Determining file name length limitations...");
int limit = new FileSystemCapabilityChecker().determineSupportedFileNameLength(getPath());
vaultSettings.filenameLengthLimit().set(limit);
@@ -126,12 +126,28 @@ public class Vault {
}
}
private void destroyCryptoFileSystem() {
CryptoFileSystem fs = cryptoFileSystem.getAndSet(null);
if (fs != null) {
try {
fs.close();
} catch (IOException e) {
LOG.error("Error closing file system.", e);
}
}
}
public synchronized void unlock(CharSequence passphrase) throws CryptoException, IOException, VolumeException, InvalidMountPointException {
if (cryptoFileSystem.get() == null) {
CryptoFileSystem fs = createCryptoFileSystem(passphrase);
cryptoFileSystem.set(fs);
volume = volumeProvider.get();
volume.mount(fs, getEffectiveMountFlags());
try {
volume = volumeProvider.get();
volume.mount(fs, getEffectiveMountFlags());
} catch (IOException | InvalidMountPointException | VolumeException e) {
destroyCryptoFileSystem();
throw e;
}
} else {
throw new IllegalStateException("Already unlocked.");
}
@@ -143,14 +159,7 @@ public class Vault {
} else {
volume.unmount();
}
CryptoFileSystem fs = cryptoFileSystem.getAndSet(null);
if (fs != null) {
try {
fs.close();
} catch (IOException e) {
LOG.error("Error closing file system.", e);
}
}
destroyCryptoFileSystem();
}
public void reveal() throws VolumeException {

View File

@@ -156,7 +156,7 @@ public class VaultModule {
//See: https://github.com/billziss-gh/winfsp/issues/319
if (!readOnly.get()) {
flags.append(" -ouid=-1");
flags.append(" -ogid=-1");
flags.append(" -ogid=11");
}
flags.append(" -ovolname=").append('"').append(mountName.get()).append('"');
//Dokany requires this option to be set, WinFSP doesn't seem to share this peculiarity,

View File

@@ -8,6 +8,7 @@ import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
import org.mockito.Mockito;
import java.nio.file.Path;
import java.nio.file.Paths;
@@ -20,14 +21,10 @@ class EnvironmentTest {
private Environment env;
@BeforeAll
static void init() {
System.setProperty("user.home", "/home/testuser");
}
@BeforeEach
void initEach() {
env = new Environment();
void init() {
env = Mockito.spy(new Environment());
Mockito.when(env.getHomeDir()).thenReturn(Path.of("/home/testuser"));
}
@Test

View File

@@ -5,16 +5,19 @@
*******************************************************************************/
package org.cryptomator.common.settings;
import org.cryptomator.common.Environment;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;
import org.mockito.Mockito;
import java.io.IOException;
public class SettingsJsonAdapterTest {
private final SettingsJsonAdapter adapter = new SettingsJsonAdapter();
private final Environment env = Mockito.mock(Environment.class);
private final SettingsJsonAdapter adapter = new SettingsJsonAdapter(env);
@Test
public void testDeserialize() throws IOException {

View File

@@ -5,6 +5,7 @@
*******************************************************************************/
package org.cryptomator.common.settings;
import org.cryptomator.common.Environment;
import org.junit.jupiter.api.Test;
import org.mockito.Mockito;
@@ -14,8 +15,10 @@ public class SettingsTest {
@Test
public void testAutoSave() {
Environment env = Mockito.mock(Environment.class);
@SuppressWarnings("unchecked") Consumer<Settings> changeListener = Mockito.mock(Consumer.class);
Settings settings = new Settings();
Settings settings = new Settings(env);
settings.setSaveCmd(changeListener);
VaultSettings vaultSettings = VaultSettings.withRandomId();
Mockito.verify(changeListener, Mockito.times(0)).accept(settings);

View File

@@ -26,12 +26,12 @@
<!-- cryptomator dependencies -->
<cryptomator.cryptofs.version>2.0.0-beta1</cryptomator.cryptofs.version>
<cryptomator.integrations.version>0.1.6</cryptomator.integrations.version>
<cryptomator.integrations.win.version>0.1.0-beta1</cryptomator.integrations.win.version>
<cryptomator.integrations.win.version>0.2.1</cryptomator.integrations.win.version>
<cryptomator.integrations.mac.version>0.1.0-beta3</cryptomator.integrations.mac.version>
<cryptomator.integrations.linux.version>0.1.0-beta2</cryptomator.integrations.linux.version>
<cryptomator.fuse.version>1.2.5</cryptomator.fuse.version>
<cryptomator.dokany.version>1.2.0</cryptomator.dokany.version>
<cryptomator.webdav.version>1.0.13</cryptomator.webdav.version>
<cryptomator.fuse.version>1.2.6</cryptomator.fuse.version>
<cryptomator.dokany.version>1.2.1</cryptomator.dokany.version>
<cryptomator.webdav.version>1.0.14</cryptomator.webdav.version>
<!-- 3rd party dependencies -->
<javafx.version>15</javafx.version>
@@ -230,6 +230,13 @@
<version>${javafx.version}</version>
<scope>test</scope>
</dependency>
<!-- TODO: temporary fix for XXE attack, can be removed once java-jwt is updated -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.10.5.1</version>
</dependency>
</dependencies>
</dependencyManagement>
@@ -332,6 +339,32 @@
</dependency>
</dependencies>
</profile>
<profile>
<id>dependency-check</id>
<build>
<plugins>
<plugin>
<groupId>org.owasp</groupId>
<artifactId>dependency-check-maven</artifactId>
<version>6.0.3</version>
<configuration>
<cveValidForHours>24</cveValidForHours>
<failBuildOnCVSS>0</failBuildOnCVSS>
<skipTestScope>true</skipTestScope>
<detail>true</detail>
<suppressionFile>suppression.xml</suppressionFile>
</configuration>
<executions>
<execution>
<goals>
<goal>check</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</profile>
</profiles>
<build>

19
main/suppression.xml Normal file
View File

@@ -0,0 +1,19 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- This file lists false positives found by org.owasp:dependency-check-maven build plugin -->
<suppressions xmlns="https://jeremylong.github.io/DependencyCheck/dependency-suppression.1.2.xsd">
<suppress>
<notes><![CDATA[ Upstream fix backported from 2.11.0 to 2.10.5.1, see https://github.com/FasterXML/jackson-databind/issues/2589#issuecomment-714833837. ]]></notes>
<gav>com.fasterxml.jackson.core:jackson-databind:2.10.5.1</gav>
<cve>CVE-2020-25649</cve>
</suppress>
<suppress>
<notes><![CDATA[ Suppress known vulnerabilities in FUSE libraries for fuse-nio-adapter. For more info, see suppression.xml of https://github.com/cryptomator/fuse-nio-adapter ]]></notes>
<gav regex="true">^org\.cryptomator:fuse-nio-adapter:.*$</gav>
<cvssBelow>9</cvssBelow>
</suppress>
<suppress>
<notes><![CDATA[ Suppress known vulnerabilities in FUSE libraries for jnr-fuse (dependency of fuse-nio-adapter). ]]></notes>
<gav regex="true">^com\.github\.serceman:jnr-fuse:.*$</gav>
<cvssBelow>9</cvssBelow>
</suppress>
</suppressions>

View File

@@ -11,6 +11,8 @@ public enum FxmlFile {
CHANGEPASSWORD("/fxml/changepassword.fxml"), //
ERROR("/fxml/error.fxml"), //
FORGET_PASSWORD("/fxml/forget_password.fxml"), //
LOCK_FORCED("/fxml/lock_forced.fxml"), //
LOCK_FAILED("/fxml/lock_failed.fxml"), //
MAIN_WINDOW("/fxml/main_window.fxml"), //
MIGRATION_CAPABILITY_ERROR("/fxml/migration_capability_error.fxml"), //
MIGRATION_IMPOSSIBLE("/fxml/migration_impossible.fxml"),

View File

@@ -65,6 +65,7 @@ public class VaultService {
public Task<Vault> createLockTask(Vault vault, boolean forced) {
Task<Vault> task = new LockVaultTask(vault, forced);
task.setOnSucceeded(evt -> LOG.info("Locked {}", vault.getDisplayName()));
task.setOnFailed(evt -> LOG.info("Failed to lock {}.", vault.getDisplayName(), evt.getSource().getException()));
return task;
}

View File

@@ -11,6 +11,7 @@ import org.cryptomator.integrations.uiappearance.UiAppearanceException;
import org.cryptomator.integrations.uiappearance.UiAppearanceListener;
import org.cryptomator.integrations.uiappearance.UiAppearanceProvider;
import org.cryptomator.ui.common.VaultService;
import org.cryptomator.ui.lock.LockComponent;
import org.cryptomator.ui.mainwindow.MainWindowComponent;
import org.cryptomator.ui.preferences.PreferencesComponent;
import org.cryptomator.ui.preferences.SelectedPreferencesTab;
@@ -31,6 +32,8 @@ import javafx.stage.Stage;
import javafx.stage.Window;
import java.awt.desktop.QuitResponse;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionStage;
@FxApplicationScoped
public class FxApplication extends Application {
@@ -42,6 +45,7 @@ public class FxApplication extends Application {
private final Lazy<PreferencesComponent> preferencesWindow;
private final Lazy<QuitComponent> quitWindow;
private final Provider<UnlockComponent.Builder> unlockWindowBuilderProvider;
private final Provider<LockComponent.Builder> lockWindowBuilderProvider;
private final Optional<TrayIntegrationProvider> trayIntegration;
private final Optional<UiAppearanceProvider> appearanceProvider;
private final VaultService vaultService;
@@ -51,11 +55,12 @@ public class FxApplication extends Application {
private final UiAppearanceListener systemInterfaceThemeListener = this::systemInterfaceThemeChanged;
@Inject
FxApplication(Settings settings, Lazy<MainWindowComponent> mainWindow, Lazy<PreferencesComponent> preferencesWindow, Provider<UnlockComponent.Builder> unlockWindowBuilderProvider, Lazy<QuitComponent> quitWindow, Optional<TrayIntegrationProvider> trayIntegration, Optional<UiAppearanceProvider> appearanceProvider, VaultService vaultService, LicenseHolder licenseHolder) {
FxApplication(Settings settings, Lazy<MainWindowComponent> mainWindow, Lazy<PreferencesComponent> preferencesWindow, Provider<UnlockComponent.Builder> unlockWindowBuilderProvider, Provider<LockComponent.Builder> lockWindowBuilderProvider, Lazy<QuitComponent> quitWindow, Optional<TrayIntegrationProvider> trayIntegration, Optional<UiAppearanceProvider> appearanceProvider, VaultService vaultService, LicenseHolder licenseHolder) {
this.settings = settings;
this.mainWindow = mainWindow;
this.preferencesWindow = preferencesWindow;
this.unlockWindowBuilderProvider = unlockWindowBuilderProvider;
this.lockWindowBuilderProvider = lockWindowBuilderProvider;
this.quitWindow = quitWindow;
this.trayIntegration = trayIntegration;
this.appearanceProvider = appearanceProvider;
@@ -96,11 +101,14 @@ public class FxApplication extends Application {
});
}
public void showMainWindow() {
public CompletionStage<Stage> showMainWindow() {
CompletableFuture<Stage> future = new CompletableFuture<>();
Platform.runLater(() -> {
mainWindow.get().showMainWindow();
var win = mainWindow.get().showMainWindow();
LOG.debug("Showing MainWindow");
future.complete(win);
});
return future;
}
public void startUnlockWorkflow(Vault vault, Optional<Stage> owner) {
@@ -110,6 +118,13 @@ public class FxApplication extends Application {
});
}
public void startLockWorkflow(Vault vault, Optional<Stage> owner) {
Platform.runLater(() -> {
lockWindowBuilderProvider.get().vault(vault).owner(owner).build().startLockWorkflow();
LOG.debug("Start lock workflow for {}", vault.getDisplayName());
});
}
public void showQuitWindow(QuitResponse response) {
Platform.runLater(() -> {
quitWindow.get().showQuitWindow(response);

View File

@@ -5,11 +5,8 @@
*******************************************************************************/
package org.cryptomator.ui.fxapp;
import dagger.BindsInstance;
import dagger.Subcomponent;
import javax.inject.Named;
@FxApplicationScoped
@Subcomponent(modules = FxApplicationModule.class)
public interface FxApplicationComponent {
@@ -19,9 +16,6 @@ public interface FxApplicationComponent {
@Subcomponent.Builder
interface Builder {
@BindsInstance
Builder trayMenuSupported(@Named("trayMenuSupported") boolean trayMenuSupported);
FxApplicationComponent build();
}

View File

@@ -11,6 +11,7 @@ import dagger.Provides;
import org.apache.commons.lang3.SystemUtils;
import org.cryptomator.ui.common.ErrorComponent;
import org.cryptomator.ui.common.StageFactory;
import org.cryptomator.ui.lock.LockComponent;
import org.cryptomator.ui.mainwindow.MainWindowComponent;
import org.cryptomator.ui.preferences.PreferencesComponent;
import org.cryptomator.ui.quit.QuitComponent;
@@ -27,7 +28,7 @@ import java.io.UncheckedIOException;
import java.util.Collections;
import java.util.List;
@Module(includes = {UpdateCheckerModule.class}, subcomponents = {MainWindowComponent.class, PreferencesComponent.class, UnlockComponent.class, QuitComponent.class, ErrorComponent.class})
@Module(includes = {UpdateCheckerModule.class}, subcomponents = {MainWindowComponent.class, PreferencesComponent.class, UnlockComponent.class, LockComponent.class, QuitComponent.class, ErrorComponent.class})
abstract class FxApplicationModule {
@Provides

View File

@@ -34,15 +34,15 @@ class AppLaunchEventHandler {
this.vaultListManager = vaultListManager;
}
public void startHandlingLaunchEvents(boolean hasTrayIcon) {
executorService.submit(() -> handleLaunchEvents(hasTrayIcon));
public void startHandlingLaunchEvents() {
executorService.submit(this::handleLaunchEvents);
}
private void handleLaunchEvents(boolean hasTrayIcon) {
private void handleLaunchEvents() {
try {
while (!Thread.interrupted()) {
AppLaunchEvent event = launchEventQueue.take();
handleLaunchEvent(hasTrayIcon, event);
handleLaunchEvent(event);
}
} catch (InterruptedException e) {
LOG.warn("Interrupted launch event handler.");
@@ -50,10 +50,10 @@ class AppLaunchEventHandler {
}
}
private void handleLaunchEvent(boolean hasTrayIcon, AppLaunchEvent event) {
private void handleLaunchEvent(AppLaunchEvent event) {
switch (event.getType()) {
case REVEAL_APP -> fxApplicationStarter.get(hasTrayIcon).thenAccept(FxApplication::showMainWindow);
case OPEN_FILE -> fxApplicationStarter.get(hasTrayIcon).thenRun(() -> {
case REVEAL_APP -> fxApplicationStarter.get().thenAccept(FxApplication::showMainWindow);
case OPEN_FILE -> fxApplicationStarter.get().thenRun(() -> {
Platform.runLater(() -> {
event.getPathsToOpen().forEach(this::addVault);
});

View File

@@ -83,7 +83,7 @@ public class AppLifecycleListener {
if (allowQuitWithoutPrompt.get()) {
decoratedQuitResponse.performQuit();
} else {
fxApplicationStarter.get(true).thenAccept(app -> app.showQuitWindow(decoratedQuitResponse));
fxApplicationStarter.get().thenAccept(app -> app.showQuitWindow(decoratedQuitResponse));
}
}
@@ -113,11 +113,11 @@ public class AppLifecycleListener {
}
private void showPreferencesWindow(@SuppressWarnings("unused") EventObject actionEvent) {
fxApplicationStarter.get(true).thenAccept(app -> app.showPreferencesWindow(SelectedPreferencesTab.ANY));
fxApplicationStarter.get().thenAccept(app -> app.showPreferencesWindow(SelectedPreferencesTab.ANY));
}
private void showAboutWindow(@SuppressWarnings("unused") AboutEvent aboutEvent) {
fxApplicationStarter.get(true).thenAccept(app -> app.showPreferencesWindow(SelectedPreferencesTab.ABOUT));
fxApplicationStarter.get().thenAccept(app -> app.showPreferencesWindow(SelectedPreferencesTab.ABOUT));
}
private void forceUnmountRemainingVaults() {

View File

@@ -1,5 +1,6 @@
package org.cryptomator.ui.launcher;
import dagger.Lazy;
import org.cryptomator.ui.fxapp.FxApplication;
import org.cryptomator.ui.fxapp.FxApplicationComponent;
import org.slf4j.Logger;
@@ -18,37 +19,36 @@ public class FxApplicationStarter {
private static final Logger LOG = LoggerFactory.getLogger(FxApplicationStarter.class);
private final FxApplicationComponent.Builder fxAppComponent;
private final Lazy<FxApplicationComponent> fxAppComponent;
private final ExecutorService executor;
private final AtomicBoolean started;
private final CompletableFuture<FxApplication> future;
@Inject
public FxApplicationStarter(FxApplicationComponent.Builder fxAppComponent, ExecutorService executor) {
public FxApplicationStarter(Lazy<FxApplicationComponent> fxAppComponent, ExecutorService executor) {
this.fxAppComponent = fxAppComponent;
this.executor = executor;
this.started = new AtomicBoolean();
this.future = new CompletableFuture<>();
}
public CompletionStage<FxApplication> get(boolean hasTrayIcon) {
public CompletionStage<FxApplication> get() {
if (!started.getAndSet(true)) {
start(hasTrayIcon);
start();
}
return future;
}
private void start(boolean hasTrayIcon) {
private void start() {
executor.submit(() -> {
LOG.debug("Starting JavaFX runtime...");
Platform.startup(() -> {
assert Platform.isFxApplicationThread();
LOG.info("JavaFX Runtime started.");
FxApplication app = fxAppComponent.trayMenuSupported(hasTrayIcon).build().application();
FxApplication app = fxAppComponent.get().application();
app.start();
future.complete(app);
});
});
}
}

View File

@@ -1,5 +1,6 @@
package org.cryptomator.ui.launcher;
import dagger.Lazy;
import org.cryptomator.common.settings.Settings;
import org.cryptomator.common.vaults.Vault;
import org.cryptomator.integrations.tray.TrayIntegrationProvider;
@@ -24,60 +25,66 @@ public class UiLauncher {
private final Settings settings;
private final ObservableList<Vault> vaults;
private final TrayMenuComponent.Builder trayComponent;
private final Lazy<TrayMenuComponent> trayMenu;
private final FxApplicationStarter fxApplicationStarter;
private final AppLaunchEventHandler launchEventHandler;
private final Optional<TrayIntegrationProvider> trayIntegration;
@Inject
public UiLauncher(Settings settings, ObservableList<Vault> vaults, TrayMenuComponent.Builder trayComponent, FxApplicationStarter fxApplicationStarter, AppLaunchEventHandler launchEventHandler, Optional<TrayIntegrationProvider> trayIntegration) {
public UiLauncher(Settings settings, ObservableList<Vault> vaults, Lazy<TrayMenuComponent> trayMenu, FxApplicationStarter fxApplicationStarter, AppLaunchEventHandler launchEventHandler, Optional<TrayIntegrationProvider> trayIntegration) {
this.settings = settings;
this.vaults = vaults;
this.trayComponent = trayComponent;
this.trayMenu = trayMenu;
this.fxApplicationStarter = fxApplicationStarter;
this.launchEventHandler = launchEventHandler;
this.trayIntegration = trayIntegration;
}
public void launch() {
final boolean hasTrayIcon;
if (SystemTray.isSupported()) {
trayComponent.build().addIconToSystemTray();
hasTrayIcon = true;
boolean hidden = settings.startHidden().get();
if (SystemTray.isSupported() && settings.showTrayIcon().get()) {
trayMenu.get().initializeTrayIcon();
launch(true, hidden);
} else {
hasTrayIcon = false;
launch(false, hidden);
}
}
// show window on start?
if (hasTrayIcon && settings.startHidden().get()) {
private void launch(boolean withTrayIcon, boolean hidden) {
// start hidden, minimized or normal?
if (withTrayIcon && hidden) {
LOG.debug("Hiding application...");
trayIntegration.ifPresent(TrayIntegrationProvider::minimizedToTray);
} else if (!withTrayIcon && hidden) {
LOG.debug("Minimizing application...");
showMainWindowAsync(true);
} else {
showMainWindowAsync(hasTrayIcon);
LOG.debug("Showing application...");
showMainWindowAsync(false);
}
// register app reopen listener
Desktop.getDesktop().addAppEventListener((AppReopenedListener) e -> showMainWindowAsync(hasTrayIcon));
Desktop.getDesktop().addAppEventListener((AppReopenedListener) e -> showMainWindowAsync(false));
// auto unlock
Collection<Vault> vaultsToAutoUnlock = vaults.filtered(this::shouldAttemptAutoUnlock);
if (!vaultsToAutoUnlock.isEmpty()) {
fxApplicationStarter.get(hasTrayIcon).thenAccept(app -> {
fxApplicationStarter.get().thenAccept(app -> {
for (Vault vault : vaultsToAutoUnlock) {
app.startUnlockWorkflow(vault, Optional.empty());
}
});
}
launchEventHandler.startHandlingLaunchEvents(hasTrayIcon);
launchEventHandler.startHandlingLaunchEvents();
}
private boolean shouldAttemptAutoUnlock(Vault vault) {
return vault.isLocked() && vault.getVaultSettings().unlockAfterStartup().get();
}
private void showMainWindowAsync(boolean hasTrayIcon) {
fxApplicationStarter.get(hasTrayIcon).thenAccept(FxApplication::showMainWindow);
private void showMainWindowAsync(boolean minimize) {
fxApplicationStarter.get().thenCompose(FxApplication::showMainWindow).thenAccept(win -> win.setIconified(minimize));
}
}

View File

@@ -19,6 +19,18 @@ import java.util.concurrent.BlockingQueue;
@Module(subcomponents = {TrayMenuComponent.class, FxApplicationComponent.class})
public abstract class UiLauncherModule {
@Provides
@Singleton
static TrayMenuComponent provideTrayMenuComponent(TrayMenuComponent.Builder builder) {
return builder.build();
}
@Provides
@Singleton
static FxApplicationComponent provideFxApplicationComponent(FxApplicationComponent.Builder builder) {
return builder.build();
}
@Provides
@Singleton
static Optional<UiAppearanceProvider> provideAppearanceProvider() {

View File

@@ -0,0 +1,39 @@
package org.cryptomator.ui.lock;
import dagger.BindsInstance;
import dagger.Subcomponent;
import org.cryptomator.common.vaults.Vault;
import javax.inject.Named;
import javafx.stage.Stage;
import java.util.Optional;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Future;
@LockScoped
@Subcomponent(modules = {LockModule.class})
public interface LockComponent {
ExecutorService defaultExecutorService();
LockWorkflow lockWorkflow();
default Future<Void> startLockWorkflow() {
LockWorkflow workflow = lockWorkflow();
defaultExecutorService().submit(workflow);
return workflow;
}
@Subcomponent.Builder
interface Builder {
@BindsInstance
LockComponent.Builder vault(@LockWindow Vault vault);
@BindsInstance
LockComponent.Builder owner(@Named("lockWindowOwner") Optional<Stage> owner);
LockComponent build();
}
}

View File

@@ -0,0 +1,31 @@
package org.cryptomator.ui.lock;
import org.cryptomator.common.vaults.Vault;
import org.cryptomator.ui.common.FxController;
import javax.inject.Inject;
import javafx.fxml.FXML;
import javafx.stage.Stage;
@LockScoped
public class LockFailedController implements FxController {
private final Stage window;
private final Vault vault;
@Inject
public LockFailedController(@LockWindow Stage window, @LockWindow Vault vault) {
this.window = window;
this.vault = vault;
}
@FXML
public void close() {
window.close();
}
// ----- Getter & Setter -----
public String getVaultName() {
return vault.getDisplayName();
}
}

View File

@@ -0,0 +1,57 @@
package org.cryptomator.ui.lock;
import org.cryptomator.common.vaults.Vault;
import org.cryptomator.ui.common.FxController;
import org.cryptomator.ui.common.UserInteractionLock;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.inject.Inject;
import javafx.fxml.FXML;
import javafx.stage.Stage;
import javafx.stage.WindowEvent;
@LockScoped
public class LockForcedController implements FxController {
private static final Logger LOG = LoggerFactory.getLogger(LockForcedController.class);
private final Stage window;
private final Vault vault;
private final UserInteractionLock<LockModule.ForceLockDecision> forceLockDecisionLock;
@Inject
public LockForcedController(@LockWindow Stage window, @LockWindow Vault vault, UserInteractionLock<LockModule.ForceLockDecision> forceLockDecisionLock) {
this.window = window;
this.vault = vault;
this.forceLockDecisionLock = forceLockDecisionLock;
this.window.setOnHiding(this::windowClosed);
}
@FXML
public void cancel() {
forceLockDecisionLock.interacted(LockModule.ForceLockDecision.CANCEL);
window.close();
}
@FXML
public void confirmForcedLock() {
forceLockDecisionLock.interacted(LockModule.ForceLockDecision.FORCE);
window.close();
}
private void windowClosed(WindowEvent windowEvent) {
// if not already interacted, set the decision to CANCEL
if (forceLockDecisionLock.awaitingInteraction().get()) {
LOG.debug("Lock canceled in force-lock-phase by user.");
forceLockDecisionLock.interacted(LockModule.ForceLockDecision.CANCEL);
}
}
// ----- Getter & Setter -----
public String getVaultName() {
return vault.getDisplayName();
}
}

View File

@@ -0,0 +1,89 @@
package org.cryptomator.ui.lock;
import dagger.Binds;
import dagger.Module;
import dagger.Provides;
import dagger.multibindings.IntoMap;
import org.cryptomator.common.vaults.Vault;
import org.cryptomator.ui.common.DefaultSceneFactory;
import org.cryptomator.ui.common.FXMLLoaderFactory;
import org.cryptomator.ui.common.FxController;
import org.cryptomator.ui.common.FxControllerKey;
import org.cryptomator.ui.common.FxmlFile;
import org.cryptomator.ui.common.FxmlScene;
import org.cryptomator.ui.common.StageFactory;
import org.cryptomator.ui.common.UserInteractionLock;
import javax.inject.Named;
import javax.inject.Provider;
import javafx.scene.Scene;
import javafx.stage.Modality;
import javafx.stage.Stage;
import java.util.Map;
import java.util.Optional;
import java.util.ResourceBundle;
@Module
abstract class LockModule {
enum ForceLockDecision {
CANCEL,
FORCE;
}
@Provides
@LockScoped
static UserInteractionLock<LockModule.ForceLockDecision> provideForceLockDecisionLock() {
return new UserInteractionLock<>(null);
}
@Provides
@LockWindow
@LockScoped
static FXMLLoaderFactory provideFxmlLoaderFactory(Map<Class<? extends FxController>, Provider<FxController>> factories, DefaultSceneFactory sceneFactory, ResourceBundle resourceBundle) {
return new FXMLLoaderFactory(factories, sceneFactory, resourceBundle);
}
@Provides
@LockWindow
@LockScoped
static Stage provideWindow(StageFactory factory, @LockWindow Vault vault, @Named("lockWindowOwner") Optional<Stage> owner) {
Stage stage = factory.create();
stage.setTitle(vault.getDisplayName());
stage.setResizable(false);
if (owner.isPresent()) {
stage.initOwner(owner.get());
stage.initModality(Modality.WINDOW_MODAL);
} else {
stage.initModality(Modality.APPLICATION_MODAL);
}
return stage;
}
@Provides
@FxmlScene(FxmlFile.LOCK_FORCED)
@LockScoped
static Scene provideForceLockScene(@LockWindow FXMLLoaderFactory fxmlLoaders) {
return fxmlLoaders.createScene("/fxml/lock_forced.fxml");
}
@Provides
@FxmlScene(FxmlFile.LOCK_FAILED)
@LockScoped
static Scene provideLockFailedScene(@LockWindow FXMLLoaderFactory fxmlLoaders) {
return fxmlLoaders.createScene("/fxml/lock_failed.fxml");
}
// ------------------
@Binds
@IntoMap
@FxControllerKey(LockForcedController.class)
abstract FxController bindLockForcedController(LockForcedController controller);
@Binds
@IntoMap
@FxControllerKey(LockFailedController.class)
abstract FxController bindLockFailedController(LockFailedController controller);
}

View File

@@ -0,0 +1,13 @@
package org.cryptomator.ui.lock;
import javax.inject.Scope;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@Scope
@Documented
@Retention(RetentionPolicy.RUNTIME)
@interface LockScoped {
}

View File

@@ -0,0 +1,14 @@
package org.cryptomator.ui.lock;
import javax.inject.Qualifier;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
@Qualifier
@Documented
@Retention(RUNTIME)
@interface LockWindow {
}

View File

@@ -0,0 +1,105 @@
package org.cryptomator.ui.lock;
import dagger.Lazy;
import org.cryptomator.common.vaults.Vault;
import org.cryptomator.common.vaults.VaultState;
import org.cryptomator.common.vaults.Volume;
import org.cryptomator.ui.common.FxmlFile;
import org.cryptomator.ui.common.FxmlScene;
import org.cryptomator.ui.common.UserInteractionLock;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.inject.Inject;
import javafx.application.Platform;
import javafx.concurrent.Task;
import javafx.scene.Scene;
import javafx.stage.Stage;
import javafx.stage.Window;
/**
* The sequence of actions performed and checked during lock of a vault.
* <p>
* This class implements the Task interface, sucht that it can run in the background with some possible forground operations/requests to the ui, without blocking the main app.
* If the task state is
* <li>succeeded, the vault was successfully locked;</li>
* <li>canceled, the lock was canceled;</li>
* <li>failed, the lock failed due to an exception.</li>
*/
public class LockWorkflow extends Task<Void> {
private static final Logger LOG = LoggerFactory.getLogger(LockWorkflow.class);
private final Stage lockWindow;
private final Vault vault;
private final UserInteractionLock<LockModule.ForceLockDecision> forceLockDecisionLock;
private final Lazy<Scene> lockForcedScene;
private final Lazy<Scene> lockFailedScene;
@Inject
public LockWorkflow(@LockWindow Stage lockWindow, @LockWindow Vault vault, UserInteractionLock<LockModule.ForceLockDecision> forceLockDecisionLock, @FxmlScene(FxmlFile.LOCK_FORCED) Lazy<Scene> lockForcedScene, @FxmlScene(FxmlFile.LOCK_FAILED) Lazy<Scene> lockFailedScene) {
this.lockWindow = lockWindow;
this.vault = vault;
this.forceLockDecisionLock = forceLockDecisionLock;
this.lockForcedScene = lockForcedScene;
this.lockFailedScene = lockFailedScene;
}
@Override
protected Void call() throws Volume.VolumeException, InterruptedException {
try {
vault.lock(false);
} catch (Volume.VolumeException e) {
LOG.debug("Regular lock of {} failed.", vault.getDisplayName(), e);
var decision = askUserForAction();
switch (decision) {
case FORCE -> vault.lock(true);
case CANCEL -> cancel(false);
}
}
return null;
}
private LockModule.ForceLockDecision askUserForAction() throws InterruptedException {
// show forcedLock dialogue ...
Platform.runLater(() -> {
lockWindow.setScene(lockForcedScene.get());
lockWindow.show();
Window owner = lockWindow.getOwner();
if (owner != null) {
lockWindow.setX(owner.getX() + (owner.getWidth() - lockWindow.getWidth()) / 2);
lockWindow.setY(owner.getY() + (owner.getHeight() - lockWindow.getHeight()) / 2);
} else {
lockWindow.centerOnScreen();
}
});
// ... and wait for answer
return forceLockDecisionLock.awaitInteraction();
}
@Override
protected void scheduled() {
vault.setState(VaultState.PROCESSING);
}
@Override
protected void succeeded() {
LOG.info("Lock of {} succeeded.", vault.getDisplayName());
vault.setState(VaultState.LOCKED);
}
@Override
protected void failed() {
LOG.warn("Failed to lock {}.", vault.getDisplayName());
vault.setState(VaultState.UNLOCKED);
lockWindow.setScene(lockFailedScene.get());
lockWindow.show();
}
@Override
protected void cancelled() {
LOG.debug("Lock of {} canceled.", vault.getDisplayName());
vault.setState(VaultState.UNLOCKED);
}
}

View File

@@ -26,7 +26,6 @@ public interface MainWindowComponent {
default Stage showMainWindow() {
Stage stage = window();
stage.setScene(scene().get());
stage.setIconified(false);
stage.show();
stage.toFront();
stage.requestFocus();

View File

@@ -7,13 +7,14 @@ import org.cryptomator.ui.fxapp.FxApplication;
import org.cryptomator.ui.fxapp.UpdateChecker;
import org.cryptomator.ui.launcher.AppLifecycleListener;
import org.cryptomator.ui.preferences.SelectedPreferencesTab;
import org.cryptomator.ui.traymenu.TrayMenuComponent;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.inject.Inject;
import javax.inject.Named;
import javafx.beans.binding.Bindings;
import javafx.beans.binding.BooleanBinding;
import javafx.beans.property.ReadOnlyBooleanProperty;
import javafx.fxml.FXML;
import javafx.scene.layout.HBox;
import javafx.stage.Stage;
@@ -23,32 +24,31 @@ public class MainWindowTitleController implements FxController {
private static final Logger LOG = LoggerFactory.getLogger(MainWindowTitleController.class);
public HBox titleBar;
private final AppLifecycleListener appLifecycle;
private final Stage window;
private final FxApplication application;
private final boolean minimizeToSysTray;
private final boolean trayMenuInitialized;
private final UpdateChecker updateChecker;
private final BooleanBinding updateAvailable;
private final LicenseHolder licenseHolder;
private final Settings settings;
private final BooleanBinding debugModeEnabled;
private final BooleanBinding showMinimizeButton;
public HBox titleBar;
private double xOffset;
private double yOffset;
@Inject
MainWindowTitleController(AppLifecycleListener appLifecycle, @MainWindow Stage window, FxApplication application, @Named("trayMenuSupported") boolean minimizeToSysTray, UpdateChecker updateChecker, LicenseHolder licenseHolder, Settings settings) {
MainWindowTitleController(AppLifecycleListener appLifecycle, @MainWindow Stage window, FxApplication application, TrayMenuComponent trayMenu, UpdateChecker updateChecker, LicenseHolder licenseHolder, Settings settings) {
this.appLifecycle = appLifecycle;
this.window = window;
this.application = application;
this.minimizeToSysTray = minimizeToSysTray;
this.trayMenuInitialized = trayMenu.isInitialized();
this.updateChecker = updateChecker;
this.updateAvailable = updateChecker.latestVersionProperty().isNotNull();
this.licenseHolder = licenseHolder;
this.settings = settings;
this.debugModeEnabled = Bindings.createBooleanBinding(this::isDebugModeEnabled, settings.debugMode());
this.showMinimizeButton = Bindings.createBooleanBinding(this::isShowMinimizeButton, settings.showMinimizeButton(), settings.showTrayIcon());
}
@FXML
@@ -71,7 +71,7 @@ public class MainWindowTitleController implements FxController {
@FXML
public void close() {
if (minimizeToSysTray) {
if (trayMenuInitialized) {
window.close();
} else {
appLifecycle.quit();
@@ -112,15 +112,24 @@ public class MainWindowTitleController implements FxController {
return updateAvailable.get();
}
public boolean isMinimizeToSysTray() {
return minimizeToSysTray;
public boolean isTrayIconPresent() {
return trayMenuInitialized;
}
public BooleanBinding debugModeEnabledProperty() {
return debugModeEnabled;
public ReadOnlyBooleanProperty debugModeEnabledProperty() {
return settings.debugMode();
}
public boolean isDebugModeEnabled() {
return settings.debugMode().get();
return debugModeEnabledProperty().get();
}
public BooleanBinding showMinimizeButtonProperty() {
return showMinimizeButton;
}
public boolean isShowMinimizeButton() {
// always show the minimize button if no tray icon is present OR it is explicitily enabled
return !trayMenuInitialized || settings.showMinimizeButton().get();
}
}

View File

@@ -6,25 +6,32 @@ import com.google.common.cache.LoadingCache;
import org.cryptomator.common.vaults.Vault;
import org.cryptomator.ui.common.FxController;
import org.cryptomator.ui.common.VaultService;
import org.cryptomator.ui.fxapp.FxApplication;
import org.cryptomator.ui.stats.VaultStatisticsComponent;
import javax.inject.Inject;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.ReadOnlyObjectProperty;
import javafx.fxml.FXML;
import javafx.stage.Stage;
import java.util.Optional;
@MainWindowScoped
public class VaultDetailUnlockedController implements FxController {
private final ReadOnlyObjectProperty<Vault> vault;
private final FxApplication application;
private final VaultService vaultService;
private final Stage mainWindow;
private final LoadingCache<Vault, VaultStatisticsComponent> vaultStats;
private final VaultStatisticsComponent.Builder vaultStatsBuilder;
@Inject
public VaultDetailUnlockedController(ObjectProperty<Vault> vault, VaultService vaultService, VaultStatisticsComponent.Builder vaultStatsBuilder) {
public VaultDetailUnlockedController(ObjectProperty<Vault> vault, FxApplication application, VaultService vaultService, VaultStatisticsComponent.Builder vaultStatsBuilder, @MainWindow Stage mainWindow) {
this.vault = vault;
this.application = application;
this.vaultService = vaultService;
this.mainWindow = mainWindow;
this.vaultStats = CacheBuilder.newBuilder().weakValues().build(CacheLoader.from(this::buildVaultStats));
this.vaultStatsBuilder = vaultStatsBuilder;
}
@@ -40,8 +47,7 @@ public class VaultDetailUnlockedController implements FxController {
@FXML
public void lock() {
vaultService.lock(vault.get(), false);
// TODO count lock attempts, and allow forced lock
application.startLockWorkflow(vault.get(), Optional.of(mainWindow));
}
@FXML

View File

@@ -1,46 +0,0 @@
package org.cryptomator.ui.preferences;
import org.cryptomator.integrations.autostart.AutoStartProvider;
import org.cryptomator.integrations.autostart.ToggleAutoStartFailedException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionStage;
@Deprecated
class AutoStartMacStrategy implements AutoStartStrategy {
private static final Logger LOG = LoggerFactory.getLogger(AutoStartMacStrategy.class);
private final AutoStartProvider autoStartProvider;
public AutoStartMacStrategy(AutoStartProvider autoStartProvider) {
this.autoStartProvider = autoStartProvider;
}
@Override
public CompletionStage<Boolean> isAutoStartEnabled() {
return CompletableFuture.completedFuture(autoStartProvider.isEnabled());
}
@Override
public void enableAutoStart() throws TogglingAutoStartFailedException {
try {
autoStartProvider.enable();
LOG.debug("Added login item.");
} catch (ToggleAutoStartFailedException e) {
throw new TogglingAutoStartFailedException("Failed to add login item.");
}
}
@Override
public void disableAutoStart() throws TogglingAutoStartFailedException {
try {
autoStartProvider.disable();
LOG.debug("Removed login item.");
} catch (ToggleAutoStartFailedException e) {
throw new TogglingAutoStartFailedException("Failed to remove login item.");
}
}
}

View File

@@ -1,27 +0,0 @@
package org.cryptomator.ui.preferences;
import dagger.Module;
import dagger.Provides;
import org.apache.commons.lang3.SystemUtils;
import org.cryptomator.integrations.autostart.AutoStartProvider;
import java.util.Optional;
@Deprecated
@Module
abstract class AutoStartModule {
@Provides
@PreferencesScoped
public static Optional<AutoStartStrategy> provideAutoStartStrategy(Optional<AutoStartProvider> autoStartProvider) {
if (SystemUtils.IS_OS_MAC_OSX && autoStartProvider.isPresent()) {
return Optional.of(new AutoStartMacStrategy(autoStartProvider.get()));
} else if (SystemUtils.IS_OS_WINDOWS) {
Optional<String> exeName = ProcessHandle.current().info().command();
return exeName.map(AutoStartWinStrategy::new);
} else {
return Optional.empty();
}
}
}

View File

@@ -1,25 +0,0 @@
package org.cryptomator.ui.preferences;
import java.util.concurrent.CompletionStage;
@Deprecated
public interface AutoStartStrategy {
CompletionStage<Boolean> isAutoStartEnabled();
void enableAutoStart() throws TogglingAutoStartFailedException;
void disableAutoStart() throws TogglingAutoStartFailedException;
class TogglingAutoStartFailedException extends Exception {
public TogglingAutoStartFailedException(String message) {
super(message);
}
public TogglingAutoStartFailedException(String message, Throwable cause) {
super(message, cause);
}
}
}

View File

@@ -1,209 +0,0 @@
package org.cryptomator.ui.preferences;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.NoSuchFileException;
import java.nio.file.Path;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionStage;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
/**
* OS specific class to check, en- and disable the auto start on Windows.
* <p>
* Two strategies are implemented for this feature, the first uses the registry and the second one the autostart folder.
* <p>
* The registry strategy checks/add/removes at the registry key {@value HKCU_AUTOSTART_KEY} an entry for Cryptomator.
* The folder strategy checks/add/removes at the location {@value WINDOWS_START_MENU_ENTRY}.
* <p>
* To check if the feature is active, both strategies are applied.
* To enable the feature, first the registry is tried and only on failure the autostart folder is used.
* To disable it, first it is determined by an internal state, which strategies must be used and in the second step those are executed.
*
* @apiNote This class is not thread safe, hence it should be avoided to call its methods simultaniously by different threads.
* @deprecated To be moved to integration-win project
*/
@Deprecated
class AutoStartWinStrategy implements AutoStartStrategy {
private static final Logger LOG = LoggerFactory.getLogger(AutoStartWinStrategy.class);
private static final String HKCU_AUTOSTART_KEY = "\"HKCU\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run\"";
private static final String AUTOSTART_VALUE = "Cryptomator";
private static final String WINDOWS_START_MENU_ENTRY = "\\AppData\\Roaming\\Microsoft\\Windows\\Start Menu\\Programs\\Cryptomator.lnk";
private final String exePath;
private boolean activatedUsingFolder;
private boolean activatedUsingRegistry;
public AutoStartWinStrategy(String exePath) {
this.exePath = exePath;
this.activatedUsingFolder = false;
this.activatedUsingRegistry = false;
}
@Override
public CompletionStage<Boolean> isAutoStartEnabled() {
return isAutoStartEnabledUsingRegistry().thenCombine(isAutoStartEnabledUsingFolder(), (bReg, bFolder) -> bReg || bFolder);
}
private CompletableFuture<Boolean> isAutoStartEnabledUsingFolder() {
Path autoStartEntry = Path.of(System.getProperty("user.home") + WINDOWS_START_MENU_ENTRY);
this.activatedUsingFolder = Files.exists(autoStartEntry);
return CompletableFuture.completedFuture(activatedUsingFolder);
}
private CompletableFuture<Boolean> isAutoStartEnabledUsingRegistry() {
ProcessBuilder regQuery = new ProcessBuilder("reg", "query", HKCU_AUTOSTART_KEY, //
"/v", AUTOSTART_VALUE);
try {
Process proc = regQuery.start();
return proc.onExit().thenApply(p -> {
this.activatedUsingRegistry = p.exitValue() == 0;
return activatedUsingRegistry;
});
} catch (IOException e) {
LOG.debug("Failed to query {} from registry key {}", AUTOSTART_VALUE, HKCU_AUTOSTART_KEY);
return CompletableFuture.completedFuture(false);
}
}
@Override
public void enableAutoStart() throws TogglingAutoStartFailedException {
try {
enableAutoStartUsingRegistry().thenAccept((Void v) -> this.activatedUsingRegistry = true).exceptionallyCompose(e -> {
LOG.debug("Falling back to using autostart folder.");
return this.enableAutoStartUsingFolder();
}).thenAccept((Void v) -> this.activatedUsingFolder = true).get();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
throw new TogglingAutoStartFailedException("Execution of enabling auto start setting was interrupted.");
} catch (ExecutionException e) {
throw new TogglingAutoStartFailedException("Enabling auto start failed both using registry and auto start folder.");
}
}
private CompletableFuture<Void> enableAutoStartUsingRegistry() {
ProcessBuilder regAdd = new ProcessBuilder("reg", "add", HKCU_AUTOSTART_KEY, //
"/v", AUTOSTART_VALUE, //
"/t", "REG_SZ", //
"/d", "\"" + exePath + "\"", //
"/f");
try {
Process proc = regAdd.start();
boolean finishedInTime = waitForProcessOrCancel(proc, 5, TimeUnit.SECONDS);
if (finishedInTime && proc.exitValue() == 0) {
LOG.debug("Added {} to registry key {}.", AUTOSTART_VALUE, HKCU_AUTOSTART_KEY);
return CompletableFuture.completedFuture(null);
} else {
throw new IOException("Process exited with error code " + proc.exitValue());
}
} catch (IOException e) {
LOG.debug("Registry could not be edited to set auto start.", e);
return CompletableFuture.failedFuture(new SystemCommandException("Adding registry value failed."));
}
}
private CompletableFuture<Void> enableAutoStartUsingFolder() {
String autoStartFolderEntry = System.getProperty("user.home") + WINDOWS_START_MENU_ENTRY;
String createShortcutCommand = "$s=(New-Object -COM WScript.Shell).CreateShortcut('" + autoStartFolderEntry + "');$s.TargetPath='" + exePath + "';$s.Save();";
ProcessBuilder shortcutAdd = new ProcessBuilder("cmd", "/c", "Start powershell " + createShortcutCommand);
try {
Process proc = shortcutAdd.start();
boolean finishedInTime = waitForProcessOrCancel(proc, 5, TimeUnit.SECONDS);
if (finishedInTime && proc.exitValue() == 0) {
LOG.debug("Created file {} for auto start.", autoStartFolderEntry);
return CompletableFuture.completedFuture(null);
} else {
throw new IOException("Process exited with error code " + proc.exitValue());
}
} catch (IOException e) {
LOG.debug("Adding entry to auto start folder failed.", e);
return CompletableFuture.failedFuture(new SystemCommandException("Adding entry to auto start folder failed."));
}
}
@Override
public void disableAutoStart() throws TogglingAutoStartFailedException {
if (activatedUsingRegistry) {
disableAutoStartUsingRegistry().whenComplete((voit, ex) -> {
if (ex == null) {
this.activatedUsingRegistry = false;
}
});
}
if (activatedUsingFolder) {
disableAutoStartUsingFolder().whenComplete((voit, ex) -> {
if (ex == null) {
this.activatedUsingFolder = false;
}
});
}
if (activatedUsingRegistry || activatedUsingFolder) {
throw new TogglingAutoStartFailedException("Disabling auto start failed using registry and/or auto start folder.");
}
}
public CompletableFuture<Void> disableAutoStartUsingRegistry() {
ProcessBuilder regRemove = new ProcessBuilder("reg", "delete", HKCU_AUTOSTART_KEY, //
"/v", AUTOSTART_VALUE, //
"/f");
try {
Process proc = regRemove.start();
boolean finishedInTime = waitForProcessOrCancel(proc, 5, TimeUnit.SECONDS);
if (finishedInTime && proc.exitValue() == 0) {
LOG.debug("Removed {} from registry key {}.", AUTOSTART_VALUE, HKCU_AUTOSTART_KEY);
return CompletableFuture.completedFuture(null);
} else {
throw new IOException("Process exited with error code " + proc.exitValue());
}
} catch (IOException e) {
LOG.debug("Registry could not be edited to remove auto start.", e);
return CompletableFuture.failedFuture(new SystemCommandException("Removing registry value failed."));
}
}
private CompletableFuture<Void> disableAutoStartUsingFolder() {
try {
Files.delete(Path.of(WINDOWS_START_MENU_ENTRY));
LOG.debug("Successfully deleted {}.", WINDOWS_START_MENU_ENTRY);
return CompletableFuture.completedFuture(null);
} catch (NoSuchFileException e) {
//that is also okay
return CompletableFuture.completedFuture(null);
} catch (IOException e) {
LOG.debug("Failed to delete entry from auto start folder.", e);
return CompletableFuture.failedFuture(e);
}
}
private static boolean waitForProcessOrCancel(Process proc, int timeout, TimeUnit timeUnit) {
boolean finishedInTime = false;
try {
finishedInTime = proc.waitFor(timeout, timeUnit);
} catch (InterruptedException e) {
LOG.error("Timeout while reading registry", e);
Thread.currentThread().interrupt();
} finally {
if (!finishedInTime) {
proc.destroyForcibly();
}
}
return finishedInTime;
}
private class SystemCommandException extends RuntimeException {
public SystemCommandException(String msg) {
super(msg);
}
}
}

View File

@@ -5,19 +5,19 @@ import org.cryptomator.common.LicenseHolder;
import org.cryptomator.common.settings.KeychainBackend;
import org.cryptomator.common.settings.Settings;
import org.cryptomator.common.settings.UiTheme;
import org.cryptomator.integrations.autostart.AutoStartProvider;
import org.cryptomator.integrations.autostart.ToggleAutoStartFailedException;
import org.cryptomator.integrations.keychain.KeychainAccessProvider;
import org.cryptomator.ui.common.ErrorComponent;
import org.cryptomator.ui.common.FxController;
import org.cryptomator.ui.traymenu.TrayMenuComponent;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.inject.Inject;
import javax.inject.Named;
import javafx.application.Application;
import javafx.application.Platform;
import javafx.beans.property.ObjectProperty;
import javafx.beans.value.ObservableValue;
import javafx.concurrent.Task;
import javafx.fxml.FXML;
import javafx.geometry.NodeOrientation;
import javafx.scene.control.CheckBox;
@@ -31,7 +31,6 @@ import java.util.Arrays;
import java.util.Optional;
import java.util.ResourceBundle;
import java.util.Set;
import java.util.concurrent.ExecutorService;
import java.util.stream.Collectors;
@PreferencesScoped
@@ -41,11 +40,11 @@ public class GeneralPreferencesController implements FxController {
private final Stage window;
private final Settings settings;
private final boolean trayMenuInitialized;
private final boolean trayMenuSupported;
private final Optional<AutoStartStrategy> autoStartStrategy;
private final Optional<AutoStartProvider> autoStartProvider;
private final ObjectProperty<SelectedPreferencesTab> selectedTabProperty;
private final LicenseHolder licenseHolder;
private final ExecutorService executor;
private final ResourceBundle resourceBundle;
private final Application application;
private final Environment environment;
@@ -53,6 +52,8 @@ public class GeneralPreferencesController implements FxController {
private final ErrorComponent.Builder errorComponent;
public ChoiceBox<UiTheme> themeChoiceBox;
public ChoiceBox<KeychainBackend> keychainBackendChoiceBox;
public CheckBox showMinimizeButtonCheckbox;
public CheckBox showTrayIconCheckbox;
public CheckBox startHiddenCheckbox;
public CheckBox debugModeCheckbox;
public CheckBox autoStartCheckbox;
@@ -60,16 +61,17 @@ public class GeneralPreferencesController implements FxController {
public RadioButton nodeOrientationLtr;
public RadioButton nodeOrientationRtl;
@Inject
GeneralPreferencesController(@PreferencesWindow Stage window, Settings settings, @Named("trayMenuSupported") boolean trayMenuSupported, Optional<AutoStartStrategy> autoStartStrategy, Set<KeychainAccessProvider> keychainAccessProviders, ObjectProperty<SelectedPreferencesTab> selectedTabProperty, LicenseHolder licenseHolder, ExecutorService executor, ResourceBundle resourceBundle, Application application, Environment environment, ErrorComponent.Builder errorComponent) {
GeneralPreferencesController(@PreferencesWindow Stage window, Settings settings, TrayMenuComponent trayMenu, Optional<AutoStartProvider> autoStartProvider, Set<KeychainAccessProvider> keychainAccessProviders, ObjectProperty<SelectedPreferencesTab> selectedTabProperty, LicenseHolder licenseHolder, ResourceBundle resourceBundle, Application application, Environment environment, ErrorComponent.Builder errorComponent) {
this.window = window;
this.settings = settings;
this.trayMenuSupported = trayMenuSupported;
this.autoStartStrategy = autoStartStrategy;
this.trayMenuInitialized = trayMenu.isInitialized();
this.trayMenuSupported = trayMenu.isSupported();
this.autoStartProvider = autoStartProvider;
this.keychainAccessProviders = keychainAccessProviders;
this.selectedTabProperty = selectedTabProperty;
this.licenseHolder = licenseHolder;
this.executor = executor;
this.resourceBundle = resourceBundle;
this.application = application;
this.environment = environment;
@@ -85,15 +87,15 @@ public class GeneralPreferencesController implements FxController {
themeChoiceBox.valueProperty().bindBidirectional(settings.theme());
themeChoiceBox.setConverter(new UiThemeConverter(resourceBundle));
showMinimizeButtonCheckbox.selectedProperty().bindBidirectional(settings.showMinimizeButton());
showTrayIconCheckbox.selectedProperty().bindBidirectional(settings.showTrayIcon());
startHiddenCheckbox.selectedProperty().bindBidirectional(settings.startHidden());
debugModeCheckbox.selectedProperty().bindBidirectional(settings.debugMode());
autoStartStrategy.ifPresent(autoStart -> {
autoStart.isAutoStartEnabled().thenAccept(enabled -> {
Platform.runLater(() -> autoStartCheckbox.setSelected(enabled));
});
});
autoStartProvider.ifPresent(autoStart -> autoStartCheckbox.setSelected(autoStart.isEnabled()));
nodeOrientationLtr.setSelected(settings.userInterfaceOrientation().get() == NodeOrientation.LEFT_TO_RIGHT);
nodeOrientationRtl.setSelected(settings.userInterfaceOrientation().get() == NodeOrientation.RIGHT_TO_LEFT);
@@ -109,12 +111,16 @@ public class GeneralPreferencesController implements FxController {
return Arrays.stream(KeychainBackend.values()).filter(value -> namesOfAvailableProviders.contains(value.getProviderClass())).toArray(KeychainBackend[]::new);
}
public boolean isTrayMenuInitialized() {
return trayMenuInitialized;
}
public boolean isTrayMenuSupported() {
return this.trayMenuSupported;
return trayMenuSupported;
}
public boolean isAutoStartSupported() {
return autoStartStrategy.isPresent();
return autoStartProvider.isPresent();
}
private void toggleNodeOrientation(@SuppressWarnings("unused") ObservableValue<? extends Toggle> observable, @SuppressWarnings("unused") Toggle oldValue, Toggle newValue) {
@@ -129,15 +135,19 @@ public class GeneralPreferencesController implements FxController {
@FXML
public void toggleAutoStart() {
autoStartStrategy.ifPresent(autoStart -> {
autoStartProvider.ifPresent(autoStart -> {
boolean enableAutoStart = autoStartCheckbox.isSelected();
Task<Void> toggleTask = new ToggleAutoStartTask(autoStart, enableAutoStart);
toggleTask.setOnFailed(event -> {
try {
if (enableAutoStart) {
autoStart.enable();
} else {
autoStart.disable();
}
} catch (ToggleAutoStartFailedException e) {
autoStartCheckbox.setSelected(!enableAutoStart); // restore previous state
LOG.error("Failed to toggle autostart.", event.getSource().getException());
errorComponent.cause(event.getSource().getException()).window(window).returnToScene(window.getScene()).build().showErrorScene();
});
executor.execute(toggleTask);
LOG.error("Failed to toggle autostart.", e);
errorComponent.cause(e).window(window).returnToScene(window.getScene()).build().showErrorScene();
}
});
}
@@ -175,6 +185,7 @@ public class GeneralPreferencesController implements FxController {
public UiTheme fromString(String string) {
throw new UnsupportedOperationException();
}
}
private static class KeychainBackendConverter extends StringConverter<KeychainBackend> {
@@ -194,29 +205,6 @@ public class GeneralPreferencesController implements FxController {
public KeychainBackend fromString(String string) {
throw new UnsupportedOperationException();
}
}
private static class ToggleAutoStartTask extends Task<Void> {
private final AutoStartStrategy autoStart;
private final boolean enable;
public ToggleAutoStartTask(AutoStartStrategy autoStart, boolean enable) {
this.autoStart = autoStart;
this.enable = enable;
setOnFailed(event -> LOG.error("Failed to toggle Autostart", getException()));
}
@Override
protected Void call() throws Exception {
if (enable) {
autoStart.enableAutoStart();
} else {
autoStart.disableAutoStart();
}
return null;
}
}
}

View File

@@ -20,7 +20,7 @@ import javafx.stage.Stage;
import java.util.Map;
import java.util.ResourceBundle;
@Module(includes = {AutoStartModule.class})
@Module
abstract class PreferencesModule {
@Provides

View File

@@ -1,5 +1,6 @@
package org.cryptomator.ui.traymenu;
import com.google.common.base.Preconditions;
import org.apache.commons.lang3.SystemUtils;
import org.cryptomator.integrations.uiappearance.Theme;
import org.cryptomator.integrations.uiappearance.UiAppearanceException;
@@ -22,6 +23,7 @@ public class TrayIconController {
private final Optional<UiAppearanceProvider> appearanceProvider;
private final TrayMenuController trayMenuController;
private final TrayIcon trayIcon;
private volatile boolean initialized;
@Inject
TrayIconController(TrayImageFactory imageFactory, TrayMenuController trayMenuController, Optional<UiAppearanceProvider> appearanceProvider) {
@@ -31,7 +33,9 @@ public class TrayIconController {
this.trayIcon = new TrayIcon(imageFactory.loadImage(), "Cryptomator", trayMenuController.getMenu());
}
public void initializeTrayIcon() {
public synchronized void initializeTrayIcon() throws IllegalStateException {
Preconditions.checkState(!initialized);
appearanceProvider.ifPresent(appearanceProvider -> {
try {
appearanceProvider.addListener(this::systemInterfaceThemeChanged);
@@ -53,10 +57,15 @@ public class TrayIconController {
}
trayMenuController.initTrayMenu();
this.initialized = true;
}
private void systemInterfaceThemeChanged(Theme theme) {
trayIcon.setImage(imageFactory.loadImage()); // TODO refactor "theme" is re-queried in loadImage()
}
public boolean isInitialized() {
return initialized;
}
}

View File

@@ -5,6 +5,7 @@
*******************************************************************************/
package org.cryptomator.ui.traymenu;
import dagger.Lazy;
import dagger.Subcomponent;
import java.awt.SystemTray;
@@ -13,11 +14,30 @@ import java.awt.SystemTray;
@Subcomponent
public interface TrayMenuComponent {
TrayIconController trayIconController();
Lazy<TrayIconController> trayIconController();
default void addIconToSystemTray() {
assert SystemTray.isSupported();
trayIconController().initializeTrayIcon();
/**
* @return <code>true</code> if a tray icon can be installed
*/
default boolean isSupported() {
return SystemTray.isSupported();
}
/**
* @return <code>true</code> if a tray icon has been installed
*/
default boolean isInitialized() {
return isSupported() && trayIconController().get().isInitialized();
}
/**
* Installs a tray icon to the system tray.
*
* @throws IllegalStateException If already added
*/
default void initializeTrayIcon() throws IllegalStateException {
assert isSupported();
trayIconController().get().initializeTrayIcon();
}
@Subcomponent.Builder

View File

@@ -1,6 +1,7 @@
package org.cryptomator.ui.traymenu;
import org.cryptomator.common.vaults.Vault;
import org.cryptomator.ui.fxapp.FxApplication;
import org.cryptomator.ui.launcher.AppLifecycleListener;
import org.cryptomator.ui.launcher.FxApplicationStarter;
import org.cryptomator.ui.preferences.SelectedPreferencesTab;
@@ -103,32 +104,36 @@ class TrayMenuController {
return actionEvent -> consumer.accept(vault);
}
private void unlockVault(Vault vault) {
fxApplicationStarter.get(true).thenAccept(app -> app.startUnlockWorkflow(vault, Optional.empty()));
}
private void lockVault(Vault vault) {
fxApplicationStarter.get(true).thenAccept(app -> app.getVaultService().lock(vault, false));
}
private void lockAllVaults(ActionEvent actionEvent) {
fxApplicationStarter.get(true).thenAccept(app -> app.getVaultService().lockAll(vaults.filtered(Vault::isUnlocked), false));
}
private void revealVault(Vault vault) {
fxApplicationStarter.get(true).thenAccept(app -> app.getVaultService().reveal(vault));
}
void showMainWindow(@SuppressWarnings("unused") ActionEvent actionEvent) {
fxApplicationStarter.get(true).thenAccept(app -> app.showMainWindow());
}
private void showPreferencesWindow(@SuppressWarnings("unused") EventObject actionEvent) {
fxApplicationStarter.get(true).thenAccept(app -> app.showPreferencesWindow(SelectedPreferencesTab.ANY));
}
private void quitApplication(EventObject actionEvent) {
appLifecycle.quit();
}
private void unlockVault(Vault vault) {
showMainAppAndThen(app -> app.startUnlockWorkflow(vault, Optional.empty()));
}
private void lockVault(Vault vault) {
showMainAppAndThen(app -> app.startLockWorkflow(vault, Optional.empty()));
}
private void lockAllVaults(ActionEvent actionEvent) {
showMainAppAndThen(app -> app.getVaultService().lockAll(vaults.filtered(Vault::isUnlocked), false));
}
private void revealVault(Vault vault) {
showMainAppAndThen(app -> app.getVaultService().reveal(vault));
}
void showMainWindow(@SuppressWarnings("unused") ActionEvent actionEvent) {
showMainAppAndThen(app -> app.showMainWindow());
}
private void showPreferencesWindow(@SuppressWarnings("unused") EventObject actionEvent) {
showMainAppAndThen(app -> app.showPreferencesWindow(SelectedPreferencesTab.ANY));
}
private void showMainAppAndThen(Consumer<FxApplication> action) {
fxApplicationStarter.get().thenAccept(action);
}
}

View File

@@ -0,0 +1,37 @@
<?xml version="1.0" encoding="UTF-8"?>
<?import org.cryptomator.ui.controls.FontAwesome5IconView?>
<?import org.cryptomator.ui.controls.FormattedLabel?>
<?import javafx.geometry.Insets?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.layout.HBox?>
<?import javafx.scene.layout.StackPane?>
<?import javafx.scene.layout.VBox?>
<?import javafx.scene.shape.Circle?>
<VBox xmlns:fx="http://javafx.com/fxml"
xmlns="http://javafx.com/javafx"
fx:controller="org.cryptomator.ui.lock.LockFailedController"
minWidth="400"
maxWidth="400"
minHeight="145"
spacing="12">
<padding>
<Insets topRightBottomLeft="12"/>
</padding>
<children>
<HBox spacing="12" alignment="CENTER_LEFT" VBox.vgrow="ALWAYS">
<StackPane alignment="CENTER" HBox.hgrow="NEVER">
<Circle styleClass="glyph-icon-red" radius="24"/>
<FontAwesome5IconView styleClass="glyph-icon-white" glyph="TIMES" glyphSize="24"/>
</StackPane>
<VBox spacing="6">
<Label styleClass="label-large" text="%lock.fail.heading"/>
<FormattedLabel format="%lock.fail.message" arg1="${controller.vaultName}" wrapText="true"/>
</VBox>
</HBox>
<VBox alignment="BOTTOM_CENTER" VBox.vgrow="ALWAYS">
<Button text="OK" defaultButton="false" VBox.vgrow="ALWAYS" cancelButton="true" onAction="#close"/>
</VBox>
</children>
</VBox>

View File

@@ -0,0 +1,45 @@
<?xml version="1.0" encoding="UTF-8"?>
<?import org.cryptomator.ui.controls.FontAwesome5IconView?>
<?import org.cryptomator.ui.controls.FormattedLabel?>
<?import javafx.geometry.Insets?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.ButtonBar?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.layout.HBox?>
<?import javafx.scene.layout.StackPane?>
<?import javafx.scene.layout.VBox?>
<?import javafx.scene.shape.Circle?>
<VBox xmlns:fx="http://javafx.com/fxml"
xmlns="http://javafx.com/javafx"
fx:controller="org.cryptomator.ui.lock.LockForcedController"
minWidth="400"
maxWidth="400"
minHeight="145"
spacing="12">
<padding>
<Insets topRightBottomLeft="12"/>
</padding>
<children>
<HBox spacing="12" alignment="CENTER_LEFT" VBox.vgrow="ALWAYS">
<StackPane alignment="CENTER" HBox.hgrow="NEVER">
<Circle styleClass="glyph-icon-orange" radius="24"/>
<FontAwesome5IconView styleClass="glyph-icon-white" glyph="EXCLAMATION" glyphSize="24"/>
</StackPane>
<VBox spacing="6">
<Label styleClass="label-large" text="%lock.forced.heading"/>
<FormattedLabel format="%lock.forced.message" arg1="${controller.vaultName}" wrapText="true"/>
</VBox>
</HBox>
<VBox alignment="BOTTOM_CENTER" VBox.vgrow="ALWAYS">
<ButtonBar buttonMinWidth="120" buttonOrder="+CI">
<buttons>
<Button text="%generic.button.cancel" ButtonBar.buttonData="CANCEL_CLOSE" defaultButton="true" cancelButton="true" onAction="#cancel"/>
<!-- TODO: third button with retry? -->
<Button text="%lock.forced.confirmBtn" ButtonBar.buttonData="FINISH" onAction="#confirmForcedLock"/>
</buttons>
</ButtonBar>
</VBox>
</children>
</VBox>

View File

@@ -54,7 +54,7 @@
<Tooltip text="%main.preferencesBtn.tooltip"/>
</tooltip>
</Button>
<Button contentDisplay="GRAPHIC_ONLY" mnemonicParsing="false" onAction="#minimize" focusTraversable="false" visible="${!controller.minimizeToSysTray}" managed="${!controller.minimizeToSysTray}">
<Button contentDisplay="GRAPHIC_ONLY" mnemonicParsing="false" onAction="#minimize" focusTraversable="false" visible="${controller.showMinimizeButton}" managed="${controller.showMinimizeButton}">
<graphic>
<FontAwesome5IconView glyph="WINDOW_MINIMIZE" glyphSize="12"/>
</graphic>

View File

@@ -22,7 +22,7 @@
</ImageView>
<VBox spacing="3" HBox.hgrow="ALWAYS" alignment="CENTER_LEFT">
<FormattedLabel styleClass="label-large" format="Cryptomator %s" arg1="${controller.applicationVersion}"/>
<Label text="© 2016 2020 Skymatic GmbH"/>
<Label text="© 2016 2021 Skymatic GmbH"/>
</VBox>
</HBox>

View File

@@ -32,7 +32,11 @@
<RadioButton fx:id="nodeOrientationRtl" text="%preferences.general.interfaceOrientation.rtl" alignment="CENTER_RIGHT" toggleGroup="${nodeOrientation}"/>
</HBox>
<CheckBox fx:id="startHiddenCheckbox" text="%preferences.general.startHidden" visible="${controller.trayMenuSupported}" managed="${controller.trayMenuSupported}"/>
<CheckBox fx:id="showMinimizeButtonCheckbox" text="%preferences.general.showMinimizeButton" visible="${controller.trayMenuInitialized}" managed="${controller.trayMenuInitialized}"/>
<CheckBox fx:id="showTrayIconCheckbox" text="%preferences.general.showTrayIcon" visible="${controller.trayMenuSupported}" managed="${controller.trayMenuSupported}"/>
<CheckBox fx:id="startHiddenCheckbox" text="%preferences.general.startHidden" />
<HBox spacing="6" alignment="CENTER_LEFT">
<CheckBox fx:id="debugModeCheckbox" text="%preferences.general.debugLogging"/>

View File

@@ -108,6 +108,15 @@ unlock.error.heading=Unable to unlock vault
unlock.error.invalidMountPoint.notExisting=Mount point "%s" is not a directory, not empty or does not exist.
unlock.error.invalidMountPoint.existing=Mount point "%s" already exists or parent folder is missing.
# Lock
## Force
lock.forced.heading=Graceful lock failed
lock.forced.message=Locking "%s" was blocked by pending operations or open files. You can force lock this vault, however interrupting I/O may result in the loss of unsaved data.
lock.forced.confirmBtn=Force Lock
## Failure
lock.fail.heading=Locking vault failed.
lock.fail.message=Vault "%s" could not be locked. Ensure unsaved work is saved elsewhere and important Read/Write operations are finished. In order to close the vault, kill the Cryptomator process.
# Migration
migration.title=Upgrade Vault
## Start
@@ -141,6 +150,8 @@ preferences.general.theme.automatic=Automatic
preferences.general.theme.light=Light
preferences.general.theme.dark=Dark
preferences.general.unlockThemes=Unlock dark mode
preferences.general.showMinimizeButton=Show minimize button
preferences.general.showTrayIcon=Show tray icon (requires restart)
preferences.general.startHidden=Hide window when starting Cryptomator
preferences.general.debugLogging=Enable debug logging
preferences.general.debugDirectory=Reveal log files

View File

@@ -102,10 +102,17 @@ unlock.success.message=تم فتح المخزن "%s" بنجاح! يمكنك ال
unlock.success.rememberChoice=تذكر اختياري ولا تظهر هذا مرة أخرى
unlock.success.revealBtn=افتح الحافظة
## Failure
unlock.error.heading=غير قادر على فتح الخزنة
### Invalid Mount Point
unlock.error.invalidMountPoint.notExisting=نقطة التحميل ليست مجلد فارغ أو غير موجودة: %s
unlock.error.invalidMountPoint.existing=نقطة/مجلد التحميل موجود بالفعل أو المجلد الأصل مفقود: %s
# Lock
## Force
lock.forced.confirmBtn=فرض القفل
## Failure
lock.fail.heading=فشلت عملية اقفال الخزنة.
# Migration
migration.title=ترقية الحافظة
## Start
@@ -171,8 +178,12 @@ preferences.donationKey.getDonationKey=الحصول على مفتاح تبرع
preferences.about=حول البرنامج
# Vault Statistics
stats.cacheHitRate=معدل استخدام الكاش
## Read
stats.read.throughput.idle=قراءة: خامل
stats.decr.total.data.none=تم فك تشفير البيانات:-
## Write
stats.encr.total.data.none=البيانات المشفرة: -
# Main Window
main.closeBtn.tooltip=إغلاق
@@ -202,9 +213,11 @@ main.vaultDetail.accessLocation=يمكن الوصول إلى محتويات مخ
main.vaultDetail.revealBtn=اظهار القرص
main.vaultDetail.lockBtn=قفل
main.vaultDetail.bytesPerSecondRead=قراءة:
main.vaultDetail.bytesPerSecondWritten=كتابة:
main.vaultDetail.throughput.idle=خمول
main.vaultDetail.throughput.kbps=%.1f كيلوبايت/ث
main.vaultDetail.throughput.mbps=%.1f ميجابايت/ث
main.vaultDetail.stats=إحصائيات الخزنة
### Missing
main.vaultDetail.missing.info=لم يتمكن Cryptomator من العثور على خزنة في هذا المسار.
main.vaultDetail.missing.recheck=إعادة الفحص

View File

@@ -106,6 +106,10 @@ unlock.error.heading=Sef nije moguće otključati
### Invalid Mount Point
unlock.error.invalidMountPoint.notExisting=Tačka postavljanja "%s" nije direktorij, nije prazna ili ne postoji.
# Lock
## Force
## Failure
# Migration
## Start
## Run

View File

@@ -17,6 +17,7 @@ generic.error.title=S'ha produït un error inesperat
generic.error.instruction=Això no hauria d'haver passat. Si us plau, informeu del text de l'error i inclogueu una descripció de quins passos han dut a aquest error.
# Defaults
defaults.vault.vaultName=Caixa forta
# Tray Menu
traymenu.showMainWindow=Mostra
@@ -44,6 +45,7 @@ addvaultwizard.new.directoryPickerLabel=Ubicació personalitzada
addvaultwizard.new.directoryPickerButton=Trieu…
addvaultwizard.new.directoryPickerTitle=Seleccioneu el directori
addvaultwizard.new.fileAlreadyExists=No es pot crear una caixa forta en aquest camí perquè ja hi ha algun objecte.
addvaultwizard.new.locationDoesNotExist=No ha estat possible crear la caixa forta en aquest camí perquè, si més no, un component no existeix.
addvaultwizard.new.invalidName=El nom de la caixa forta no és vàlid. Si us plau, escribiu un mom de directori amb caràcters estàndard.
### Password
addvaultwizard.new.createVaultBtn=Crea la caixa forta
@@ -100,10 +102,20 @@ unlock.success.message="%s" s'ha desbloquejat correctament! Ja es pot accedir a
unlock.success.rememberChoice=Recorda l'elecció. No ho tornis a mostrar.
unlock.success.revealBtn=Mostra la caixa forta
## Failure
unlock.error.heading=No ha estat possible desblocar la caixa forta
### Invalid Mount Point
unlock.error.invalidMountPoint.notExisting=El punt de muntatge no és un directori buit, o no existeix: %s
unlock.error.invalidMountPoint.existing=El punt de muntatge o la carpeta ja existeix, o no es pot accedir al directori superior: %s
# Lock
## Force
lock.forced.heading=Ha fallat el blocatge
lock.forced.message=No s'ha blocat "%s" perquè hi ha operacions pendents o fitxers oberts. Podeu forçar-ne el blocatge però heu de saber que interrompre l'entrada/sortida pot produir la pèrdua de dades.
lock.forced.confirmBtn=Força el blocatge
## Failure
lock.fail.heading=Ha fallat el blocatge de la caixa forta.
lock.fail.message=La caixa forta "%s" no s'ha pogut blocar. Assegureu-vos que el treball s'ha desat en algun altre lloc i que les operacions de lectura/escriptura han acabat. Per tal de tancar la caixa, mateu el procés Cryptomator.
# Migration
migration.title=Actualitza la caixa forta
## Start
@@ -133,13 +145,21 @@ preferences.title=Preferències
## General
preferences.general=General
preferences.general.theme=Apariència
preferences.general.theme.automatic=Automàtic
preferences.general.theme.light=Clar
preferences.general.theme.dark=Fosc
preferences.general.unlockThemes=Desbloqueja el tema fosc
preferences.general.startHidden=Amaga la finestra al iniciar Cryptomator
preferences.general.showMinimizeButton=Mostra el botó de minimitzar
preferences.general.showTrayIcon=Mostra la icona en la barra (cal reiniciar)
preferences.general.startHidden=Amaga la finestra quan s'inicia Cryptomator
preferences.general.debugLogging=Habilita el registre de depuració
preferences.general.debugDirectory=Mostra els fitxers de registres
preferences.general.autoStart=Executa Cryptomator en engegar el sistema
preferences.general.keychainBackend=Desar contrasenyes amb
preferences.general.keychainBackend.org.cryptomator.linux.keychain.SecretServiceKeychainAccess=Anell de claus de Gnome
preferences.general.keychainBackend.org.cryptomator.linux.keychain.KDEWalletKeychainAccess=Cartera de KDE
preferences.general.keychainBackend.org.cryptomator.macos.keychain.MacSystemKeychainAccess=Accés a clauers macOS
preferences.general.keychainBackend.org.cryptomator.windows.keychain.WindowsProtectedKeychainAccess=Windows Data Protection
preferences.general.interfaceOrientation=Orientació de la interfície
preferences.general.interfaceOrientation.ltr=Esquerra a dreta
preferences.general.interfaceOrientation.rtl=Dreta a esquerra
@@ -163,38 +183,73 @@ preferences.donationKey.getDonationKey=Obtingau una clau de donació
preferences.about=Quant a
# Vault Statistics
stats.title=Estadístiques per a %s
stats.cacheHitRate=Relació d'encerts de la memòria cau
## Read
stats.read.throughput.idle=Llegit: inactiu
stats.read.throughput.kibs=Llegit: %.2f kiB/s
stats.read.throughput.mibs=Llegit: %.2f MiB/s
stats.read.total.data.none=Dades llegides: -
stats.read.total.data.kib=Dades llegides: %.1f kiB
stats.read.total.data.mib=Dades llegides: %.1f MiB
stats.read.total.data.gib=Dades llegides: %.1f GiB
stats.decr.total.data.none=Dades desxifrades: -
stats.decr.total.data.kib=Dades desxifrades: %.1f kiB
stats.decr.total.data.mib=Dades desxifrades: %.1f MiB
stats.decr.total.data.gib=Dades desxifrades: %.1f GiB
stats.read.accessCount=Lectures en total: %d
## Write
stats.write.throughput.idle=Escriu: inactiu
stats.write.throughput.kibs=Escriu: %.2f kiB/s
stats.write.throughput.mibs=Escriu: %.2f MiB/s
stats.write.total.data.none=Dades llegides: -
stats.write.total.data.kib=Dades escrites: %.1f kiB
stats.write.total.data.mib=Dades escrites: %.1f MiB
stats.write.total.data.gib=Dades escrites: %.1f GiB
stats.encr.total.data.none=Dades xifrades: -
stats.encr.total.data.kib=Dades xifrades: %.1f kiB
stats.encr.total.data.mib=Dades xifrades: %.1f MiB
stats.encr.total.data.gib=Dades xifrades: %.1f GiB
stats.write.accessCount=Total escrits: %d
# Main Window
main.closeBtn.tooltip=Tanca
main.minimizeBtn.tooltip=Minimitza
main.preferencesBtn.tooltip=Preferències
main.debugModeEnabled.tooltip=Mode depuració activat
main.donationKeyMissing.tooltip=Si us plau, considereu fer una donació
## Drag 'n' Drop
main.dropZone.dropVault=Afegeix aquesta caixa forta
main.dropZone.unknownDragboardContent=Si voleu afegir una caixa forta, arrossegueu-la a aquesta finestra
## Vault List
main.vaultlist.emptyList.onboardingInstruction=Feu clic aquí per afegir una caixa forta
main.vaultlist.contextMenu.remove=Elimina la caixa forta…
main.vaultlist.addVaultBtn=Afegir una caixa forta
## Vault Detail
### Welcome
main.vaultDetail.welcomeOnboarding=Gràcies per escollir Cryptomator per protegir els vostres fitxers. Si vos cal ajuda, llegiu les nostres guies per donar els Primers passos:
### Locked
main.vaultDetail.lockedStatus=BLOQUEJADA
main.vaultDetail.unlockBtn=Desbloca…
main.vaultDetail.unlockNowBtn=Desbloqueja ara
main.vaultDetail.optionsBtn=Opcions de la caixa forta
main.vaultDetail.passwordSavedInKeychain=Contrasenya desada
### Unlocked
main.vaultDetail.unlockedStatus=DESBLOQUEJADA
main.vaultDetail.accessLocation=Els continguts de la vostra caixa forta són accessibles aquí:
main.vaultDetail.revealBtn=Mostra la unitat
main.vaultDetail.lockBtn=Bloqueja
main.vaultDetail.bytesPerSecondRead=Lectura:
main.vaultDetail.bytesPerSecondWritten=Escriu:
main.vaultDetail.throughput.idle=inactiu
main.vaultDetail.throughput.kbps=%.1f kiB/s
main.vaultDetail.throughput.mbps=%.1f MiB/s
main.vaultDetail.stats=Estadístiques de la caixa forta
### Missing
main.vaultDetail.missing.info=Cryptomator no ha trobat una caixa forta en aquest camí.
main.vaultDetail.missing.recheck=Torna a comprovar
main.vaultDetail.missing.remove=Eliminar de la llista de la caixa forta…
main.vaultDetail.missing.changeLocation=Canvia la localització de la caixa forta…
### Needs Migration
main.vaultDetail.migrateButton=Actualitza la caixa forta
main.vaultDetail.migratePrompt=Per accedir a la vostra caixa forta abans cal actualitzar-la al nou format
@@ -213,7 +268,7 @@ wrongFileAlert.link=Per rebre assistència, visiteu
## General
vaultOptions.general=General
vaultOptions.general.vaultName=Nom de la caixa forta
vaultOptions.general.unlockAfterStartup=Desbloqueja la caixa forta al iniciar Cryptomator
vaultOptions.general.unlockAfterStartup=Desbloqueja la caixa forta quan s'inicia Cryptomator
vaultOptions.general.actionAfterUnlock=Després d'un desbloqueig correcte
vaultOptions.general.actionAfterUnlock.ignore=No facis res
vaultOptions.general.actionAfterUnlock.reveal=Mostra la unitat
@@ -232,6 +287,7 @@ vaultOptions.mount.mountPoint.directoryPickerTitle=Esculliu un directori buit
## Master Key
vaultOptions.masterkey=Contrasenya
vaultOptions.masterkey.changePasswordBtn=Canvi de contrasenya
vaultOptions.masterkey.forgetSavedPasswordBtn=Oblida la contrasenya desada
vaultOptions.masterkey.recoveryKeyExpanation=La clau de recuperació és l'unic mitjà de restaurar l'accès a la caixa forta en cas de perdre la contrasenya.
vaultOptions.masterkey.showRecoveryKeyBtn=Mostra la clau de recuperació
vaultOptions.masterkey.recoverPasswordBtn=Recupera la contrasenya

View File

@@ -107,6 +107,10 @@ unlock.error.heading=Nelze odemknout trezor
unlock.error.invalidMountPoint.notExisting=Připojovací bod %s není složkou, není prázdný nebo neexistuje.
unlock.error.invalidMountPoint.existing=Připojovací bod %s již existuje nebo nadřazená složka chybí.
# Lock
## Force
## Failure
# Migration
migration.title=Upgrade trezoru
## Start
@@ -140,6 +144,8 @@ preferences.general.theme.automatic=Automaticky
preferences.general.theme.light=Světlý
preferences.general.theme.dark=Tmavý
preferences.general.unlockThemes=Odemknout tmavý režim
preferences.general.showMinimizeButton=Zobrazit tlačítko minimalizovat
preferences.general.showTrayIcon=Zobrazit ikonu v liště (vyžaduje restart)
preferences.general.startHidden=Skrýt okno Cryptomatoru při spuštění
preferences.general.debugLogging=Ladicí režim
preferences.general.debugDirectory=Ukázat soubory se záznamy událostí (log)

View File

@@ -107,6 +107,15 @@ unlock.error.heading=Tresor konnte nicht entsperrt werden
unlock.error.invalidMountPoint.notExisting=Einhängepunkt ist kein leeres Verzeichnis oder existiert nicht: %s
unlock.error.invalidMountPoint.existing=Einhängepunkt/Ordner bereits vorhanden oder übergeordneter Ordner fehlt: %s
# Lock
## Force
lock.forced.heading=Sperren fehlgeschlagen
lock.forced.message=Aufgrund von Zugriffen laufender Prozesse oder geöffneter Dateien konnte „%s“ nicht gesperrt werden. Du kannst das Sperren dieses Tresors erzwingen, allerdings kann dies zum Verlust ungespeicherter Daten führen.
lock.forced.confirmBtn=Sperren erzwingen
## Failure
lock.fail.heading=Tresor konnte nicht gesperrt werden.
lock.fail.message=Der Tresor „%s“ konnte nicht gesperrt werden. Stellen Sie sicher, dass Sie Ihre ungespeicherten Arbeit an anderer Stelle speichern und wichtige Lese-/Schreibvorgänge abgeschlossen sind. Um den Tresor zu schließen, beenden Sie den Cryptomator-Prozess.
# Migration
migration.title=Tresor aktualisieren
## Start
@@ -140,6 +149,8 @@ preferences.general.theme.automatic=Automatisch
preferences.general.theme.light=Hell
preferences.general.theme.dark=Dunkel
preferences.general.unlockThemes=Dunklen Modus freischalten
preferences.general.showMinimizeButton=Minimieren-Schaltfläche anzeigen
preferences.general.showTrayIcon=Symbol im Infobereich anzeigen (Neustart erforderlich)
preferences.general.startHidden=Cryptomator im Hintergrund starten
preferences.general.debugLogging=Diagnoseprotokoll aktivieren
preferences.general.debugDirectory=Protokolldateien anzeigen

View File

@@ -102,10 +102,20 @@ unlock.success.message="%s" ξεκλειδώθηκε επιτυχώς! Το vaul
unlock.success.rememberChoice=Απομνημόνευση επιλογής, μην ρωτήσεις ξανά
unlock.success.revealBtn=Αποκάλυψη Vault
## Failure
unlock.error.heading=Αδυναμία ξεκλειδώματος vault
### Invalid Mount Point
unlock.error.invalidMountPoint.notExisting=Το σημείο προσάρτησης δεν είναι κενός φάκελος ή δεν υπάρχει: %s
unlock.error.invalidMountPoint.existing=Το σημείο/φάκελος προσάρτησης υπάρχει ήδη ή ο γονικός φάκελος λείπει: %s
# Lock
## Force
lock.forced.heading=Το κανονικό κλείδωμα απέτυχε
lock.forced.message=Το κλείδωμα "%s" μπλοκαρίστηκε από εκκρεμείς διεργασίες ή ανοιχτά αρχεία. Μπορείτε να εξαναγκάσετε το κλείδωμα του vault, αλλά η διακοπή Ι/Ο ενδέχεται να οδηγήσει σε απώλεια μη αποθηκευμένων δεδομένων.
lock.forced.confirmBtn=Εξαναγκασμένο κλείδωμα
## Failure
lock.fail.heading=Το κλείδωμα του vault απέτυχε.
lock.fail.message=Το Vault "%s" δεν κλειδώθηκε. Εξασφαλίστε την αποθήκευση της εργασίας σε άλλο σημείο και πως οι σημαντικές διεργασίας Ανάγνωσης/Εγγραφής έχουν ολοκληρωθεί. Για να κλείσετε το vault, τερματίστε τη διεργασία του Cryptomator.
# Migration
migration.title=Αναβάθμιση Vault
## Start

View File

@@ -107,6 +107,15 @@ unlock.error.heading=No se puede desbloquear la bóveda
unlock.error.invalidMountPoint.notExisting=El punto de montaje no es un directorio vacío o no existe: %s
unlock.error.invalidMountPoint.existing=El punto de montaje/carpeta ya existe o falta la carpeta padre: %s
# Lock
## Force
lock.forced.heading=Bloqueo automático fallido
lock.forced.message=El bloqueo de "%s" fue bloqueado por operaciones pendientes o archivos abiertos. Puede forzar el bloqueo de esta bóveda, sin embargo, interrumpir la I/O puede provocar la pérdida de datos no guardados.
lock.forced.confirmBtn=Forzar bloqueo
## Failure
lock.fail.heading=Falló al bloquear la bóveda.
lock.fail.message=No se pudo bloquear la bóveda "%s". Asegúrese de que el trabajo no guardado se ha guardado en otro lugar y las operaciones de lectura/escritura importantes han finalizado. Para cerrar la bóveda termine el proceso de Cryptomator.
# Migration
migration.title=Mejorar bóveda
## Start
@@ -140,6 +149,8 @@ preferences.general.theme.automatic=Automático
preferences.general.theme.light=Claro
preferences.general.theme.dark=Oscuro
preferences.general.unlockThemes=Desbloquear el modo oscuro
preferences.general.showMinimizeButton=Mostrar botón minimizar
preferences.general.showTrayIcon=Mostrar icono de bandeja (requiere reiniciar)
preferences.general.startHidden=Ocultar ventana al iniciar Cryptomator
preferences.general.debugLogging=Habilitar registro de depuración
preferences.general.debugDirectory=Revelar archivos de registro

View File

@@ -107,6 +107,15 @@ unlock.error.heading=Impossible de déverrouiller le coffre
unlock.error.invalidMountPoint.notExisting=Le point de montage «%s» n'est pas un répertoire, n'est pas vide ou n'existe pas.
unlock.error.invalidMountPoint.existing=Le point de montage/le répertoire existe déjà ou le répertoire parent est manquant: %s
# Lock
## Force
lock.forced.heading=Le verrouillage normal a échoué
lock.forced.message=Le verrouillage de «%s» a été bloqué par des opérations en attente ou des fichiers ouverts. Vous pouvez forcer le verrouillage de ce coffre, mais l'interruption d'E/S peut entraîner la perte de données non enregistrées.
lock.forced.confirmBtn=Forcer le verrouillage
## Failure
lock.fail.heading=Le verrouillage du coffre a échoué.
lock.fail.message=Le coffre-fort "%s" n'a pas pu être verrouillé. Assurez-vous que le travail non sauvegardé est sauvegardé ailleurs et que les opérations importantes de lecture/écriture sont bien terminées. Pour fermer le coffre-fort, tuez le processus Cryptomator.
# Migration
migration.title=Mettre à jour le coffre
## Start
@@ -140,6 +149,8 @@ preferences.general.theme.automatic=Automatique
preferences.general.theme.light=Clair
preferences.general.theme.dark=Sombre
preferences.general.unlockThemes=Débloquer le mode nuit
preferences.general.showMinimizeButton=Afficher le bouton de réduction
preferences.general.showTrayIcon=Afficher l'icône de la barre des tâches (redémarrage requis)
preferences.general.startHidden=Démarrer Cryptomator en mode caché
preferences.general.debugLogging=Activer les logs debug
preferences.general.debugDirectory=Afficher le journal

View File

@@ -17,9 +17,11 @@ generic.error.title=कोई अनपेक्षित त्रुटि ह
generic.error.instruction=ऐसा नहीं होना चाहिए था। कृपया नीचे त्रुटि पाठ की रिपोर्ट करें और इस त्रुटि के लिए क्या कदम उठाए, इसका विवरण शामिल करें।
# Defaults
defaults.vault.vaultName=गुप्त तिजोरी
# Tray Menu
traymenu.showMainWindow=दिखाएँ
traymenu.showPreferencesWindow=प्राथमिकताएं
traymenu.lockAllVaults=सभी को लॉक करें
traymenu.quitApplication=बाहर निकलें
traymenu.vault.unlock=अनलॉक करें
@@ -42,9 +44,21 @@ addvaultwizard.new.locationPrompt=…
addvaultwizard.new.directoryPickerLabel=अपने पसंद की जगह डालें
addvaultwizard.new.directoryPickerButton=चुनें…
addvaultwizard.new.directoryPickerTitle=निर्देशिका चुनें
addvaultwizard.new.fileAlreadyExists=तिजोरी इस रास्ते पर नहीं बनाई जा सकती क्योंकि कुछ वस्तु पहले से मौजूद है।
addvaultwizard.new.locationDoesNotExist=तिजोरी इस रास्ते पर नहीं बनाई जा सकती क्योंकि कम से कम एक पथ घटक मौजूद नहीं है।
addvaultwizard.new.invalidName=अमान्य वॉल्ट नाम। कृपया एक नियमित निर्देशिका नाम पर विचार करें।
### Password
addvaultwizard.new.createVaultBtn=वॉल्ट बनाएं
addvaultwizard.new.generateRecoveryKeyChoice=आप अपने पासवर्ड के बिना अपने डेटा तक नहीं पहुंच पाएंगे। क्या आप उस वक़्त के लिए एक पुनर्प्राप्ति कुंजी चाहते हैं जब आप अपना पासवर्ड खो देते हैं?
addvaultwizard.new.generateRecoveryKeyChoice.yes=हाँ कृपया, पछतावा से बेहतर सुरक्षा
addvaultwizard.new.generateRecoveryKeyChoice.no=नहीं धन्यवाद, मैं अपना पासवर्ड नहीं खोऊंगा
### Information
addvault.new.readme.storageLocation.fileName=IMPORTANT.rtf
addvault.new.readme.storageLocation.1=⚠️ तिजोरी की फ़ाइलें ⚠️
addvault.new.readme.storageLocation.2=यह आपकी तिजोरी का भंडारण स्थान है।
addvault.new.readme.storageLocation.3=ऐसा न करें
addvault.new.readme.storageLocation.4=• इस निर्देशिका के भीतर किसी भी फाइल को बदल दें या
addvault.new.readme.storageLocation.5=• • इस डायरेक्टरी में एन्क्रिप्शन के लिए कोई भी फाइल पेस्ट करें।
## Existing
addvaultwizard.existing.chooseBtn=चुनें…
## Success
@@ -62,6 +76,10 @@ unlock.unlockBtn=अनलॉक करें
## Failure
### Invalid Mount Point
# Lock
## Force
## Failure
# Migration
migration.title=वाउल्ट को अपग्रेड करें
## Start
@@ -71,6 +89,7 @@ migration.title=वाउल्ट को अपग्रेड करें
## Impossible
# Preferences
preferences.title=प्राथमिकताएं
## General
preferences.general=सामान्य
## Volume
@@ -84,6 +103,7 @@ preferences.general=सामान्य
# Main Window
main.closeBtn.tooltip=बंद करें
main.preferencesBtn.tooltip=प्राथमिकताएं
## Drag 'n' Drop
## Vault List
main.vaultlist.addVaultBtn=वाउल्ट डालें

View File

@@ -29,6 +29,10 @@
## Failure
### Invalid Mount Point
# Lock
## Force
## Failure
# Migration
## Start
## Run

View File

@@ -81,6 +81,10 @@ unlock.unlockBtn=Buka Gembok
## Failure
### Invalid Mount Point
# Lock
## Force
## Failure
# Migration
## Start
## Run

View File

@@ -102,10 +102,20 @@ unlock.success.message=Sbloccato "%s" con successo! La tua cassaforte è ora acc
unlock.success.rememberChoice=Ricorda la scelta, non mostrare ancora
unlock.success.revealBtn=Rivela Cassaforte
## Failure
unlock.error.heading=Impossibile sbloccare la cassaforte
### Invalid Mount Point
unlock.error.invalidMountPoint.notExisting=Il punto di montaggio non è una directory vuota o non esiste: %s
unlock.error.invalidMountPoint.existing=Il punto di Mount/cartella esiste già o la cartella superiore è mancante: %s
# Lock
## Force
lock.forced.heading=Blocco normale fallito
lock.forced.message=Il bloccaggio di "%s" è stato impedito da operazioni in sospeso o da file aperti. È possibile forzare il blocco di questa cassaforte, tuttavia interrompere I/O potrebbe causare la perdita di dati non salvati.
lock.forced.confirmBtn=Forza Blocco
## Failure
lock.fail.heading=Blocco cassaforte fallito.
lock.fail.message=Impossibile bloccare la cassaforte "%s". Assicurati che il lavoro non salvato sia salvato ovunque e che le operazioni di Lettura/Scrittura importanti siano concluse. Per chiudere la cassaforte, termina il processo di Cryptomator.
# Migration
migration.title=Aggiorna Cassaforte
## Start
@@ -139,6 +149,7 @@ preferences.general.theme.automatic=Automatico
preferences.general.theme.light=Chiaro
preferences.general.theme.dark=Scuro
preferences.general.unlockThemes=Sblocca modalità scura
preferences.general.showMinimizeButton=Mostra pulsante riduci a icona
preferences.general.startHidden=Nascondi la finestra all'avvio di Cryptomator
preferences.general.debugLogging=Abilita i registri di debug
preferences.general.debugDirectory=Mostra file log
@@ -171,8 +182,33 @@ preferences.donationKey.getDonationKey=Ottieni una chiave di donazione
preferences.about=Informazioni
# Vault Statistics
stats.title=Statistiche per %s
## Read
stats.read.throughput.idle=Lettura: inattivo
stats.read.throughput.kibs=Lettura: %.2f kiB/s
stats.read.throughput.mibs=Lettura: %.2f MiB/s
stats.read.total.data.none=Dati letti: -
stats.read.total.data.kib=Dati letti: %.1f kiB
stats.read.total.data.mib=Dati letti: %.1f MiB
stats.read.total.data.gib=Dati letti: %.1f GiB
stats.decr.total.data.none=Dati decriptati: -
stats.decr.total.data.kib=Dati decrittografati: %.1f kiB
stats.decr.total.data.mib=Dati decriptati: %.1f MiB
stats.decr.total.data.gib=Dati decrittografati: %.1f GiB
stats.read.accessCount=Totale lettura: %d
## Write
stats.write.throughput.idle=Scrivi: inattivo
stats.write.throughput.kibs=Scrittura: %.2f kiB/s
stats.write.throughput.mibs=Scrittura: %.2f MiB/s
stats.write.total.data.none=Dati letti: -
stats.write.total.data.kib=Dati scritti: %.1f kiB
stats.write.total.data.mib=Dati scritti: %.1f MiB
stats.write.total.data.gib=Dati scritti: %.1f GiB
stats.encr.total.data.none=Dati crittografati: -
stats.encr.total.data.kib=Dati crittografati: %.1f kiB
stats.encr.total.data.mib=Dati crittografati: %.1f MiB
stats.encr.total.data.gib=Dati crittografati: %.1f GiB
stats.write.accessCount=Totale scritture: %d
# Main Window
main.closeBtn.tooltip=Chiudi
@@ -202,9 +238,11 @@ main.vaultDetail.accessLocation=I contenuti della tua cassaforte sono accessibil
main.vaultDetail.revealBtn=Visualizza disco
main.vaultDetail.lockBtn=Blocca
main.vaultDetail.bytesPerSecondRead=Lettura:
main.vaultDetail.bytesPerSecondWritten=Scrittura:
main.vaultDetail.throughput.idle=inattivo
main.vaultDetail.throughput.kbps=%.1f kiB/s
main.vaultDetail.throughput.mbps=%.1f MiB/s
main.vaultDetail.stats=Statistiche Cassaforte
### Missing
main.vaultDetail.missing.info=Cryptomator non ha potuto trovare una cassaforte in questo percorso.
main.vaultDetail.missing.recheck=Ricontrollare

View File

@@ -106,6 +106,15 @@ unlock.error.heading=金庫の解錠に失敗
### Invalid Mount Point
unlock.error.invalidMountPoint.notExisting=マウントポイントが空のディレクトリか存在していません: %s
# Lock
## Force
lock.forced.heading=正常な施錠に失敗しました
lock.forced.message=保留中の操作または、開かれたファイルによって、"%s" の施錠が中断されました。この金庫を強制的に施錠することはできますが、I/O を中断すると保存されていないデータを失う可能性があります。
lock.forced.confirmBtn=強制施錠
## Failure
lock.fail.heading=金庫の施錠に失敗しました。
lock.fail.message=金庫 "%s" を施錠できませんでした。保存されていないデータがほかの場所に保存され、重要な読み込み/書き込み操作が完了していることを確認してください。金庫を閉じるには、Cryptomator のプロセスを強制終了してください。
# Migration
migration.title=金庫をアップグレード
## Start
@@ -139,6 +148,8 @@ preferences.general.theme.automatic=自動
preferences.general.theme.light=ライト
preferences.general.theme.dark=ダーク
preferences.general.unlockThemes=ダークモードの解錠
preferences.general.showMinimizeButton=最小化ボタンを表示
preferences.general.showTrayIcon=トレイアイコンを表示 (再起動が必要)
preferences.general.startHidden=Cryptomator を開始したときウィンドウを隠す
preferences.general.debugLogging=ログを有効にする
preferences.general.debugDirectory=ログ ファイルを表示

View File

@@ -107,6 +107,15 @@ unlock.error.heading=Vault 잠금을 해제 할 수 없습니다.
unlock.error.invalidMountPoint.notExisting=구성지점이 존재하지 않거나 빈 디렉터리가 아닙니다: %s
unlock.error.invalidMountPoint.existing=구성지점/폴더가 이미 존재하거나 부모 폴더가 없습니다: %s
# Lock
## Force
lock.forced.heading=정상적인 잠금을 실패하였습니다.
lock.forced.message=대기 중인 작동이나 파일이 열려있어 "%s"를 잠그는데 실패하였습니다. 이 Vault를 강제로 잠글 수 있으나, 입/출력의 중단은 저장되지 않은 데이터의 유실을 초래할 수 있습니다.
lock.forced.confirmBtn=강제 잠금
## Failure
lock.fail.heading=Vault 잠금에 실패하였습니다.
lock.fail.message="%s" Vault를 잠글 수 없습니다. 저장되지 않은 작업이 다른 곳에 저장된 것과 중요한 읽기/쓰기 동작이 완료되었는지 확인 하십시요. Vault를 닫기 위해, Cryptomator 프로세스를 강제로 종료 하십시요.
# Migration
migration.title=Vault 업그레이드
## Start

View File

@@ -101,6 +101,10 @@ unlock.success.revealBtn=Atklāt glabātuvi
## Failure
### Invalid Mount Point
# Lock
## Force
## Failure
# Migration
migration.title=Jaunināt glabātuvi
## Start

View File

@@ -102,7 +102,16 @@ unlock.success.message=Vellykket opplåsning av "%s"! Hvelvet ditt er nå tilgje
unlock.success.rememberChoice=Husk valget - ikke vis dette igjen
unlock.success.revealBtn=Gjør hvelvet synlig
## Failure
unlock.error.heading=Klarer ikke å låse opp hvelvet
### Invalid Mount Point
unlock.error.invalidMountPoint.notExisting=Monteringspunktet "%s" er enten ikke en mappe, ikke tom eller eksisterer ikke.
unlock.error.invalidMountPoint.existing=Monteringspunktet "%s" finnes enten allerede eller at overordnet mappe mangler.
# Lock
## Force
lock.forced.confirmBtn=Tving låsing
## Failure
lock.fail.heading=Låsing av hvelvet mislyktes.
# Migration
migration.title=Oppgrader hvelv
@@ -141,6 +150,7 @@ preferences.general.startHidden=Skjul vinduet når du starter Cryptomator
preferences.general.debugLogging=Aktiver loggføring av feilsøk
preferences.general.debugDirectory=Vis loggfiler
preferences.general.autoStart=Start Cryptomator ved systemstart
preferences.general.keychainBackend=Lagre passord med
preferences.general.interfaceOrientation=Grensesnittorientering
preferences.general.interfaceOrientation.ltr=Fra venstre til høyre
preferences.general.interfaceOrientation.rtl=Fra høyre til venstre
@@ -164,8 +174,34 @@ preferences.donationKey.getDonationKey=Få en donasjonsnøkkel
preferences.about=Om
# Vault Statistics
stats.title=Statistikk for %s
stats.cacheHitRate=Cache treffrate
## Read
stats.read.throughput.idle=Les: inaktiv
stats.read.throughput.kibs=Lest: %.2f kiB/s
stats.read.throughput.mibs=Lest: %.2f MiB/s
stats.read.total.data.none=Data lest: -
stats.read.total.data.kib=Data lest: %.1f kiB
stats.read.total.data.mib=Data lest: %.1f MiB
stats.read.total.data.gib=Data lest: %.1f GiB
stats.decr.total.data.none=Data dekryptert: -
stats.decr.total.data.kib=Data dekryptert: %.1f kiB
stats.decr.total.data.mib=Data dekryptert: %.1f MiB
stats.decr.total.data.gib=Data dekryptert: %.1f GiB
stats.read.accessCount=Lesninger totalt: %d
## Write
stats.write.throughput.idle=Skrive: inaktiv
stats.write.throughput.kibs=Skriver: %.2f kiB/s
stats.write.throughput.mibs=Skriver: %.2f MiB/s
stats.write.total.data.none=Data lest: -
stats.write.total.data.kib=Data skrevet: %.1f kiB
stats.write.total.data.mib=Data skrevet: %.1f MiB
stats.write.total.data.gib=Data skrevet: %.1f GiB
stats.encr.total.data.none=Data kryptert: -
stats.encr.total.data.kib=Data kryptert: %.1f kiB
stats.encr.total.data.mib=Data kryptert: %.1f MiB
stats.encr.total.data.gib=Data kryptert: %.1f GiB
stats.write.accessCount=Skrivninger totalt: %d
# Main Window
main.closeBtn.tooltip=Lukk
@@ -195,9 +231,11 @@ main.vaultDetail.accessLocation=Innholdet i hvelvet ditt er tilgjengelig her:
main.vaultDetail.revealBtn=Gjør enheten synlig
main.vaultDetail.lockBtn=Lås
main.vaultDetail.bytesPerSecondRead=Lesehastighet:
main.vaultDetail.bytesPerSecondWritten=Skriv:
main.vaultDetail.throughput.idle=inaktiv
main.vaultDetail.throughput.kbps=%.1f kiB/s
main.vaultDetail.throughput.mbps=%.1f MiB/s
main.vaultDetail.stats=Hvelvstatistikk
### Missing
main.vaultDetail.missing.info=Cryptomator kunne ikke finne et hvelv på denne søkestien.
main.vaultDetail.missing.recheck=Kontroller igjen

View File

@@ -45,6 +45,7 @@ addvaultwizard.new.directoryPickerLabel=Andere locatie
addvaultwizard.new.directoryPickerButton=Kies…
addvaultwizard.new.directoryPickerTitle=Selecteer map
addvaultwizard.new.fileAlreadyExists=Er kan op deze locatie geen kluis aangemaakt worden, omdat er een bepaald object bestaat.
addvaultwizard.new.locationDoesNotExist=Er kan geen kluis op dit pad aangemaakt worden omdat ten minste één onderdeel van het pad niet bestaat.
addvaultwizard.new.invalidName=Ongeldige kluisnaam. Overweeg een standaard mapnaam.
### Password
addvaultwizard.new.createVaultBtn=Kluis aanmaken
@@ -98,9 +99,22 @@ unlock.savePassword=Wachtwoord Opslaan
unlock.unlockBtn=Ontgrendel
## Success
unlock.success.message="%s" is met succes ontgrendeld! Uw kluis is nu toegankelijk.
unlock.success.rememberChoice=Keuze onthouden en dit niet opnieuw tonen
unlock.success.revealBtn=Toon kluis
## Failure
unlock.error.heading=Kan kluis niet ontgrendelen
### Invalid Mount Point
unlock.error.invalidMountPoint.notExisting=Koppelpunt "%s" is geen map, is niet leeg of bestaat niet.
unlock.error.invalidMountPoint.existing=Koppelpunt "%s" bestaat reeds of de bovenliggende map ontbreekt.
# Lock
## Force
lock.forced.heading=De kluis kon niet op een gecontroleerde manier vergrendeld worden
lock.forced.message=Het vergrendelen van "%s" werd voorkomen door lopende processen of geopende bestanden. U kunt de vergrendeling op deze kluis forceren, maar dit kan leiden tot het verlies van niet-opgeslagen gegevens.
lock.forced.confirmBtn=Forceer vergrendeling
## Failure
lock.fail.heading=Kluis kan niet vergrendeld worden.
lock.fail.message=Kluis "%s" kan niet vergrendeld worden. Zorg ervoor dat u uw niet-opgeslagen werk ergens anders opslaat en belangrijke lees-/schrijfbewerkingen hebt voltooid. Om de kluis te sluiten, beëindigt u het Cryptomator-proces.
# Migration
migration.title=Kluis upgraden
@@ -110,6 +124,7 @@ migration.start.confirm=Ja, mijn kluis is volledig gesynchroniseerd
## Run
migration.run.enterPassword=Voer wachtwoord voor "%s" in
migration.run.startMigrationBtn=Kluis migreren
migration.run.progressHint=Dit kan enige tijd duren…
## Sucess
migration.success.nextStepsInstructions="%s" is succesvol gemigreerd.\nU kunt nu uw kluis ontgrendelen.
migration.success.unlockNow=Nu Ontgrendelen
@@ -118,17 +133,33 @@ migration.error.missingFileSystemCapabilities.title=Niet ondersteund bestandssys
migration.error.missingFileSystemCapabilities.description=Migratie is niet gestart, omdat uw kluis zich op een niet-adequaat bestandssysteem bevindt.
migration.error.missingFileSystemCapabilities.reason.LONG_FILENAMES=Het bestandssysteem ondersteunt geen lange bestandsnamen.
migration.error.missingFileSystemCapabilities.reason.LONG_PATHS=Het bestandssysteem ondersteunt geen lange paden.
migration.error.missingFileSystemCapabilities.reason.READ_ACCESS=Het bestandssysteem staat lezen niet toe.
migration.error.missingFileSystemCapabilities.reason.WRITE_ACCESS=Het bestandssysteem staat schrijven niet toe.
## Impossible
migration.impossible.heading=Kluis kan niet gemigreerd worden
migration.impossible.reason=De kluis kan niet automatisch gemigreerd worden omdat de opslaglocatie of het toegangspunt niet compatibel is.
migration.impossible.moreInfo=De kluis is nog te openen met een oudere versie. Instructies voor het handmatig migreren van een kluis zijn te vinden op
# Preferences
preferences.title=Voorkeuren
## General
preferences.general=Algemeen
preferences.general.theme=Uiterlijk
preferences.general.theme.automatic=Automatisch
preferences.general.theme.light=Licht
preferences.general.theme.dark=Donker
preferences.general.unlockThemes=Ontgrendel donkere modus
preferences.general.showMinimizeButton=Knop minimaliseren weergeven
preferences.general.showTrayIcon=Pictogram weergeven in systeemvak (herstart vereist)
preferences.general.startHidden=Verberg venster bij het opstarten van Cryptomator
preferences.general.debugLogging=Debug logging aanzetten
preferences.general.debugDirectory=Logboekbestanden bekijken
preferences.general.autoStart=Start Cryptomator als het systeem opstart
preferences.general.keychainBackend=Bewaar wachtwoorden met
preferences.general.keychainBackend.org.cryptomator.linux.keychain.SecretServiceKeychainAccess=Gnome sleutelhanger
preferences.general.keychainBackend.org.cryptomator.linux.keychain.KDEWalletKeychainAccess=KDE Wallet
preferences.general.keychainBackend.org.cryptomator.macos.keychain.MacSystemKeychainAccess=macOS-sleutelhangertoegang
preferences.general.keychainBackend.org.cryptomator.windows.keychain.WindowsProtectedKeychainAccess=Windows-gegevensbescherming
preferences.general.interfaceOrientation=Interface oriëntatie
preferences.general.interfaceOrientation.ltr=Links naar rechts
preferences.general.interfaceOrientation.rtl=Rechts naar links
@@ -152,38 +183,73 @@ preferences.donationKey.getDonationKey=Verkrijg een donatiesleutel
preferences.about=Over
# Vault Statistics
stats.title=Statistieken voor %s
stats.cacheHitRate=Succespercentage van cache
## Read
stats.read.throughput.idle=Lezen: inactief
stats.read.throughput.kibs=Lezen: %.2f kiB/s
stats.read.throughput.mibs=Lezen: %.2f MiB/s
stats.read.total.data.none=Gegevens gelezen: -
stats.read.total.data.kib=Gegevens gelezen: %.1f kiB
stats.read.total.data.mib=Gegevens gelezen: %.1f MiB
stats.read.total.data.gib=Gegevens gelezen: %.1f GiB
stats.decr.total.data.none=Gegevens ontsleuteld: -
stats.decr.total.data.kib=Gegevens ontsleuteld: %.1f kiB
stats.decr.total.data.mib=Gegevens ontsleuteld: %.1f MiB
stats.decr.total.data.gib=Gegevens ontsleuteld: %.1f GiB
stats.read.accessCount=Totaal gelezen: %d
## Write
stats.write.throughput.idle=Schrijven: inactief
stats.write.throughput.kibs=Schrijven: %.2f kiB/s
stats.write.throughput.mibs=Schrijven: %.2f MiB/s
stats.write.total.data.none=Gegevens gelezen: -
stats.write.total.data.kib=Gegevens geschreven: %.1f kiB
stats.write.total.data.mib=Gegevens geschreven: %.1f MiB
stats.write.total.data.gib=Gegevens geschreven: %.1f GiB
stats.encr.total.data.none=Gegevens versleuteld: -
stats.encr.total.data.kib=Gegevens versleuteld: %.1f kiB
stats.encr.total.data.mib=Gegevens versleuteld: %.1f MiB
stats.encr.total.data.gib=Gegevens versleuteld: %.1f GiB
stats.write.accessCount=Totaal geschreven: %d
# Main Window
main.closeBtn.tooltip=Sluiten
main.minimizeBtn.tooltip=Minimaliseer
main.preferencesBtn.tooltip=Voorkeuren
main.debugModeEnabled.tooltip=Foutopsporingsmodus is ingeschakeld
main.donationKeyMissing.tooltip=Overweeg alstublieft om een donatie te doen
## Drag 'n' Drop
main.dropZone.dropVault=Voeg deze kluis toe
main.dropZone.unknownDragboardContent=Als u een kluis wilt toevoegen, sleep deze dan naar dit venster
## Vault List
main.vaultlist.emptyList.onboardingInstruction=Klik hier om een kluis toe te voegen
main.vaultlist.contextMenu.remove=Verwijder Kluis…
main.vaultlist.addVaultBtn=Kluis toevoegen
## Vault Detail
### Welcome
main.vaultDetail.welcomeOnboarding=Bedankt dat u Cryptomator heeft gekozen om uw bestanden te beschermen. Voor assistentie verwijzen we u naar de starthandleidingen:
### Locked
main.vaultDetail.lockedStatus=VERGRENDELD
main.vaultDetail.unlockBtn=Ontgrendelen…
main.vaultDetail.unlockNowBtn=Nu Ontgrendelen
main.vaultDetail.optionsBtn=Kluis-instellingen
main.vaultDetail.passwordSavedInKeychain=Wachtwoord opgeslagen
### Unlocked
main.vaultDetail.unlockedStatus=ONTGRENDELD
main.vaultDetail.accessLocation=De inhoud van uw kluis is hier toegankelijk:
main.vaultDetail.revealBtn=Toon Schijf
main.vaultDetail.lockBtn=Vergrendel
main.vaultDetail.bytesPerSecondRead=Lezen:
main.vaultDetail.bytesPerSecondWritten=Schrijven:
main.vaultDetail.throughput.idle=inactief
main.vaultDetail.throughput.kbps=%.1f kiB/s
main.vaultDetail.throughput.mbps=%.1f MiB/s
main.vaultDetail.stats=Kluisstatistieken
### Missing
main.vaultDetail.missing.info=Cryptomator kon op dit pad geen kluis vinden.
main.vaultDetail.missing.recheck=Controleer nog eens
main.vaultDetail.missing.remove=Verwijderen van kluislijst…
main.vaultDetail.missing.changeLocation=Verander de locatie van de kluis…
### Needs Migration
main.vaultDetail.migrateButton=Kluis upgraden
main.vaultDetail.migratePrompt=Uw kluis moet worden bijgewerkt naar een nieuw formaat, voordat u deze kunt openen
@@ -203,7 +269,10 @@ wrongFileAlert.link=Voor verdere ondersteuning, bezoek
vaultOptions.general=Algemeen
vaultOptions.general.vaultName=Kluisnaam
vaultOptions.general.unlockAfterStartup=Ontgrendel kluis bij het starten van Cryptomator
vaultOptions.general.actionAfterUnlock=Na een succesvolle ontgrendeling
vaultOptions.general.actionAfterUnlock.ignore=Niets doen
vaultOptions.general.actionAfterUnlock.reveal=Toon Schijf
vaultOptions.general.actionAfterUnlock.ask=Vragen
## Mount
vaultOptions.mount=Aankoppelen
vaultOptions.mount.readonly=Alleen-Lezen
@@ -218,6 +287,7 @@ vaultOptions.mount.mountPoint.directoryPickerTitle=Kies een lege map
## Master Key
vaultOptions.masterkey=Wachtwoord
vaultOptions.masterkey.changePasswordBtn=Wijzig wachtwoord
vaultOptions.masterkey.forgetSavedPasswordBtn=Opgeslagen wachtwoord vergeten
vaultOptions.masterkey.recoveryKeyExpanation=Een herstelsleutel is je enige manier om de toegang tot een kluis te herstellen als je je wachtwoord kwijtraakt.
vaultOptions.masterkey.showRecoveryKeyBtn=Toon herstelsleutel
vaultOptions.masterkey.recoverPasswordBtn=Wachtwoord herstellen

View File

@@ -103,6 +103,10 @@ unlock.success.revealBtn=Gjer kvelven synleg
## Failure
### Invalid Mount Point
# Lock
## Force
## Failure
# Migration
migration.title=Oppgrader kvelv
## Start

View File

@@ -107,6 +107,15 @@ unlock.error.heading=ਵਾਲਟ ਅਣ-ਲਾਕ ਕਰਨ ਲਈ ਅਸਮਰ
unlock.error.invalidMountPoint.notExisting="%s" ਮਾਊਂਟ ਪੁਆਇੰਟ ਡਾਇਰੈਕਟਰੀ ਨਹੀਂ, ਖਾਲੀ ਨਹੀਂ ਜਾਂ ਮੌਜੂਦ ਹੀ ਨਹੀਂ ਹੈ।
unlock.error.invalidMountPoint.existing="%s" ਮਾਊਂਟ ਪੁਆਇੰਟ ਪਹਿਲਾਂ ਹੀ ਮੌਜੂਦ ਹੈ ਜਾਂ ਉਸ ਦਾ ਮੂਲ ਫੋਲਡਰ ਗੁੰਮ ਹੈ।
# Lock
## Force
lock.forced.heading=ਸਧਾਰਨ ਲਾਕ ਕਰਨਾ ਅਸਫ਼ਲ ਹੈ
lock.forced.message=ਬਾਕੀ ਰਹਿੰਦੀਆਂ ਕਾਰਵਾਈਆਂ ਜਾਂ ਫ਼ਾਈਲਾਂ ਖੁੱਲ੍ਹਣ ਕਰਕੇ "%s" ਲਾਕ ਕਰਨ ਨੂੰ ਰੋਕਿਆ ਗਿਆ ਹੈ। ਤੁਸੀਂ ਇਸ ਵਾਲਟ ਨੂੰ ਧੱਕੇ ਨਾਲ ਲਾਕ ਕਰ ਸਕਦੇ ਹੋ, ਪਰ I/O ਵਿੱਚ ਰੁਕਾਵਟ ਪਾਉਣ ਨਾਲ ਨਾ-ਸੰਭਾਲਿਆ ਡਾਟਾ ਖਤਮ ਹੋ ਜਾ ਸਕਦਾ ਹੈ।
lock.forced.confirmBtn=ਧੱਕੇ ਨਾਲ ਲਾਕ ਕਰੋ
## Failure
lock.fail.heading=ਵਾਲਟ ਲਾਕ ਕਰਨਾ ਅਸਫ਼ਲ ਹੈ।
lock.fail.message=ਵਾਲਟ "%s" ਨੂੰ ਲਾਕ ਨਹੀਂ ਕੀਤਾ ਜਾ ਸਕਿਆ। ਯਕੀਨੀ ਬਣਾਓ ਕਿ ਨਾ-ਸੰਭਾਲੇ ਕੰਮ ਨੂੰ ਹੋਰ ਥਾਂ ਸੰਭਾਲ ਲਿਆ ਹੈ ਅਤੇ ਖਾਸ ਪੜ੍ਹਨ/ਲਿਖਣ ਕਾਰਵਾਈਆਂ ਪੂਰੀਆਂ ਹੋਈਆਂ ਹਨ। ਵਾਲਟ ਨੂੰ ਬੰਦ ਕਰਨ ਲਈ Cryptomator ਕਾਰਵਾਈ ਨੂੰ ਖਤਮ ਕਰੋ।
# Migration
migration.title=ਵਾਲਟ ਅੱਪਗਰੇਡ ਕਰੋ
## Start

View File

@@ -107,6 +107,15 @@ unlock.error.heading=Nie można odblokować sejfu
unlock.error.invalidMountPoint.notExisting=Punkt montowania nie jest pustym katalogiem lub nie istnieje: %s
unlock.error.invalidMountPoint.existing=Punkt montowania już istnieje lub brakuje katalogu nadrzędnego: %s
# Lock
## Force
lock.forced.heading=Blokada nie powiodła się
lock.forced.message=Zamknięcie "%s" zostało zablokowane przez oczekujące operacje lub otwarte pliki. Możesz wymusić zamknięcie tego sejfu, ale może to spowodować utratę niezapisanych danych.
lock.forced.confirmBtn=Wymuś zablokowanie
## Failure
lock.fail.heading=Błąd blokowania sejfu.
lock.fail.message=Nie można zablokować sejfu "%s". Zapisz wszelkie zmiany w bezpiecznym miejscu i upewnij się, że nie ma żadnych ważnych oczekujących operacji odczytu/zapisu. W celu zamknięcia sejfu ubij Cryptomator.
# Migration
migration.title=Aktualizuj sejf
## Start
@@ -140,10 +149,13 @@ preferences.general.theme.automatic=Automatycznie
preferences.general.theme.light=Jasny
preferences.general.theme.dark=Ciemny
preferences.general.unlockThemes=Odblokuj tryb ciemny
preferences.general.showMinimizeButton=Pokaż przycisk minimalizacji
preferences.general.showTrayIcon=Pokaż ikonę zasobnika (wymaga ponownego uruchomienia)
preferences.general.startHidden=Ukryj okno podczas uruchamiania programu Cryptomator
preferences.general.debugLogging=Włącz logowanie w trybie debug
preferences.general.debugDirectory=Pokaż pliki logowania
preferences.general.autoStart=Uruchom Cryptomator po uruchomieniu systemu
preferences.general.keychainBackend=Przechowuj hasła za pomocą
preferences.general.keychainBackend.org.cryptomator.linux.keychain.SecretServiceKeychainAccess=Brelok kluczy Gnome
preferences.general.keychainBackend.org.cryptomator.linux.keychain.KDEWalletKeychainAccess=Portfel KDE
preferences.general.keychainBackend.org.cryptomator.macos.keychain.MacSystemKeychainAccess=Dostęp do pęku kluczy macOS
@@ -171,6 +183,8 @@ preferences.donationKey.getDonationKey=Zdobądź klucz donacji
preferences.about=O programie
# Vault Statistics
stats.title=Statystyki dla %s
stats.cacheHitRate=Trafność cache
## Read
stats.read.throughput.idle=Odczyt: bezczynny
stats.read.throughput.kibs=Odczyt: %.2f kiB/s

View File

@@ -95,6 +95,10 @@ unlock.success.revealBtn=Revelar Cofre
## Failure
### Invalid Mount Point
# Lock
## Force
## Failure
# Migration
migration.title=Atualizar Cofre
## Start

View File

@@ -102,10 +102,20 @@ unlock.success.message="%s" foi desbloqueado com sucesso! Seu cofre agora está
unlock.success.rememberChoice=Lembrar opção escolhida, não mostrar isto novamente
unlock.success.revealBtn=Revelar Cofre
## Failure
unlock.error.heading=Não foi possível desbloquear o cofre
### Invalid Mount Point
unlock.error.invalidMountPoint.notExisting=O ponto de montagem não é um diretório vazio ou não existe: %s
unlock.error.invalidMountPoint.existing=Ponto de montagem/pasta já existe ou a pasta pai está faltando: %s
# Lock
## Force
lock.forced.heading=Bloqueio normal falhou
lock.forced.message=Trancar "%s" foi bloqueado por operações pendentes ou arquivos abertos. Você pode forçar o bloqueio deste cofre, no entanto, a interrupção pode resultar em perda de dados não salvos.
lock.forced.confirmBtn=Forçar Bloqueio
## Failure
lock.fail.heading=O bloqueio do cofre falhou.
lock.fail.message=Cofre "%s" não pôde ser bloqueado. Certifique-se de que o trabalho não salvo está salvo em outro lugar e que operações de Leitura/Escrita importantes sejam concluídas. Para fechar o cofre, encerre o processo do Cryptomator.
# Migration
migration.title=Atualizar Cofre
## Start
@@ -139,13 +149,17 @@ preferences.general.theme.automatic=Automático
preferences.general.theme.light=Claro
preferences.general.theme.dark=Escuro
preferences.general.unlockThemes=Desbloquear o modo escuro
preferences.general.showMinimizeButton=Mostrar botão minimizar
preferences.general.showTrayIcon=Mostrar ícone na barra do sistema (requer reinicialização)
preferences.general.startHidden=Ocultar janela ao iniciar o Cryptomator
preferences.general.debugLogging=Ativar log de debug
preferences.general.debugDirectory=Mostrar arquivos de log
preferences.general.autoStart=Iniciar o Cryptomator durante inicialização do sistema
preferences.general.keychainBackend=Armazenar senhas no
preferences.general.keychainBackend.org.cryptomator.linux.keychain.SecretServiceKeychainAccess=Keyring do Gnome
preferences.general.keychainBackend.org.cryptomator.linux.keychain.KDEWalletKeychainAccess=Carteira KDE
preferences.general.keychainBackend.org.cryptomator.macos.keychain.MacSystemKeychainAccess=Keychain Access do macOS
preferences.general.keychainBackend.org.cryptomator.windows.keychain.WindowsProtectedKeychainAccess=Proteção de Dados do Windows
preferences.general.interfaceOrientation=Orientação da interface
preferences.general.interfaceOrientation.ltr=Da esquerda para a direita
preferences.general.interfaceOrientation.rtl=Da direita para a esquerda
@@ -169,8 +183,34 @@ preferences.donationKey.getDonationKey=Obtenha uma chave de doação
preferences.about=Sobre
# Vault Statistics
stats.title=Estatísticas para %s
stats.cacheHitRate=Taxa de Utilização do Cache
## Read
stats.read.throughput.idle=Leitura: ociosa
stats.read.throughput.kibs=Leitura: %.2f kiB/s
stats.read.throughput.mibs=Leitura: %.2f MiB/s
stats.read.total.data.none=Dados lidos: -
stats.read.total.data.kib=Dados lidos: %.1f kiB
stats.read.total.data.mib=Dados lidos: %.1f MiB
stats.read.total.data.gib=Dados lidos: %.1f GiB
stats.decr.total.data.none=Dados descriptografados: -
stats.decr.total.data.kib=Dados descriptografados: %.1f kiB
stats.decr.total.data.mib=Dados descriptografados: %.1f MiB
stats.decr.total.data.gib=Dados descriptografados: %.1f GiB
stats.read.accessCount=Total de leituras: %d
## Write
stats.write.throughput.idle=Escrita: ociosa
stats.write.throughput.kibs=Escrita: %.2f kiB/s
stats.write.throughput.mibs=Escrita: %.2f MiB/s
stats.write.total.data.none=Dados lidos: -
stats.write.total.data.kib=Dados gravados: %.1f kiB
stats.write.total.data.mib=Dados gravados: %.1f MiB
stats.write.total.data.gib=Dados gravados: %.1f GiB
stats.encr.total.data.none=Dados criptografados: -
stats.encr.total.data.kib=Dados criptografados: %.1f kiB
stats.encr.total.data.mib=Dados criptografados: %.1f MiB
stats.encr.total.data.gib=Dados criptografados: %.1f GiB
stats.write.accessCount=Total gravado: %d
# Main Window
main.closeBtn.tooltip=Fechar
@@ -200,9 +240,11 @@ main.vaultDetail.accessLocation=O conteúdo do seu cofre está disponível aqui:
main.vaultDetail.revealBtn=Revelar Volume
main.vaultDetail.lockBtn=Bloquear
main.vaultDetail.bytesPerSecondRead=Leitura:
main.vaultDetail.bytesPerSecondWritten=Escrita:
main.vaultDetail.throughput.idle=ocioso
main.vaultDetail.throughput.kbps=%.1f kiB/s
main.vaultDetail.throughput.mbps=%.1f MiB/s
main.vaultDetail.stats=Estatísticas do Cofre
### Missing
main.vaultDetail.missing.info=O Cryptomator não conseguiu encontrar um cofre neste caminho.
main.vaultDetail.missing.recheck=Verificar novamente

View File

@@ -29,6 +29,10 @@
## Failure
### Invalid Mount Point
# Lock
## Force
## Failure
# Migration
## Start
## Run

View File

@@ -38,7 +38,7 @@ addvaultwizard.welcome.existingButton=Открыть имеющееся хран
addvaultwizard.new.nameInstruction=Выберите имя для хранилища
addvaultwizard.new.namePrompt=Имя хранилища
### Location
addvaultwizard.new.locationInstruction=Где Cryptomator должен хранить зашифрованные файлы вашего хранилища?
addvaultwizard.new.locationInstruction=Где Cryptomator должен хранить зашифрованные файлы хранилища?
addvaultwizard.new.locationLabel=Место хранения
addvaultwizard.new.locationPrompt=
addvaultwizard.new.directoryPickerLabel=Своё место
@@ -51,13 +51,13 @@ addvaultwizard.new.invalidName=Неверное имя хранилища. Ук
addvaultwizard.new.createVaultBtn=Создать хранилище
addvaultwizard.new.generateRecoveryKeyChoice=Вы не сможете получить доступ к своим данным без пароля. Хотите создать ключ для восстановления на случай потери пароля?
addvaultwizard.new.generateRecoveryKeyChoice.yes=Да, лучше предостеречься, чем потом жалеть
addvaultwizard.new.generateRecoveryKeyChoice.no=Нет, спасибо, я не потеряю свой пароль
addvaultwizard.new.generateRecoveryKeyChoice.no=Нет, спасибо, я не потеряю пароль
### Information
addvault.new.readme.storageLocation.fileName=ВАЖНО.rtf
addvault.new.readme.storageLocation.1=⚠️ ФАЙЛЫ ХРАНИЛИЩА ⚠️
addvault.new.readme.storageLocation.2=Это место, где находится ваше хранилище.
addvault.new.readme.storageLocation.3=НЕТ
addvault.new.readme.storageLocation.4=• изменяйте любые файлы в этой папке или
addvault.new.readme.storageLocation.3=НЕЛЬЗЯ
addvault.new.readme.storageLocation.4= изменять любые файлы в этой папке или
addvault.new.readme.storageLocation.5=• добавляйте в эту папку любые файлы для шифрования.
addvault.new.readme.storageLocation.6=Чтобы зашифровать файлы и просмотреть содержимое хранилища, сделайте следующее:
addvault.new.readme.storageLocation.7=1. Добавьте это хранилище в Cryptomator.
@@ -102,11 +102,20 @@ unlock.success.message=Разблокировка "%s" успешно выпол
unlock.success.rememberChoice=Запомнить выбор и больше не спрашивать
unlock.success.revealBtn=Показать хранилище
## Failure
unlock.error.heading=Невозможно разблокировать хранилище
unlock.error.heading=Не удалось разблокировать хранилище
### Invalid Mount Point
unlock.error.invalidMountPoint.notExisting=Точка монтирования %s - не папка, не пуста или не существует.
unlock.error.invalidMountPoint.notExisting=Точка монтирования "%s" не является пустой папкой или не существует.
unlock.error.invalidMountPoint.existing=Точка монтирования %s уже существует, либо отсутствует родительская папка.
# Lock
## Force
lock.forced.heading=Не удалось выполнить мягкую блокировку
lock.forced.message=Блокировка "%s" невозможна из-за незавершённых операций или открытых файлов. Вы можете заблокировать это хранилище принудительно, однако прерывание ввода-вывода может привести к потере несохранённых данных.
lock.forced.confirmBtn=Принудительная блокировка
## Failure
lock.fail.heading=Не удалось заблокировать хранилище.
lock.fail.message=Хранилище "%s" не удалось заблокировать. Убедитесь, что несохранённые данные сохранены в другом месте и завершены важные операции чтения/записи. Чтобы закрыть хранилище, завершите процесс Cryptomator.
# Migration
migration.title=Обновить хранилище
## Start
@@ -135,20 +144,23 @@ migration.impossible.moreInfo=Хранилище по-прежнему можн
preferences.title=Настройки
## General
preferences.general=Общие
preferences.general.theme=Тема
preferences.general.theme=Оформление
preferences.general.theme.automatic=Автоматически
preferences.general.theme.light=Светлая
preferences.general.theme.dark=Тёмная
preferences.general.unlockThemes=Разблокировать тёмный режим
preferences.general.showMinimizeButton=Показать кнопку сворачивания
preferences.general.showTrayIcon=Показать значок в панели задач (требуется перезапуск)
preferences.general.startHidden=Скрывать окно при запуске Cryptomator
preferences.general.debugLogging=Вести журнал отладки
preferences.general.debugDirectory=Показать файлы журнала
preferences.general.autoStart=Запускать Cryptomator при старте системы
preferences.general.keychainBackend=Хранить пароли в
preferences.general.keychainBackend.org.cryptomator.linux.keychain.SecretServiceKeychainAccess=Связка ключей Gnome
preferences.general.keychainBackend.org.cryptomator.linux.keychain.KDEWalletKeychainAccess=Бумажник KDE
preferences.general.keychainBackend.org.cryptomator.linux.keychain.KDEWalletKeychainAccess=Хранилище ключей KDE
preferences.general.keychainBackend.org.cryptomator.macos.keychain.MacSystemKeychainAccess=Доступ к связке ключей macOS
preferences.general.keychainBackend.org.cryptomator.windows.keychain.WindowsProtectedKeychainAccess=Защита данных Windows
preferences.general.interfaceOrientation=Ориентация интерфейса
preferences.general.interfaceOrientation=Интерфейс
preferences.general.interfaceOrientation.ltr=Слева направо
preferences.general.interfaceOrientation.rtl=Справа налево
## Volume
@@ -165,7 +177,7 @@ preferences.updates.updateAvailable=Доступно обновление до
## Donation Key
preferences.donationKey=Пожертвование
preferences.donationKey.registeredFor=Зарегистрировано: %s
preferences.donationKey.noDonationKey=Не найден ключ пожертвования. Он напоминает лицензионный ключ, но для удивительных людей, использующих свободное ПО ;-)
preferences.donationKey.noDonationKey=Не найден ключ пожертвования. Этот ключ похож на лицензионный, но для тех, кто использует бесплатное ПО. ;-)
preferences.donationKey.getDonationKey=Получить ключ пожертвования
## About
preferences.about=О программе
@@ -221,7 +233,7 @@ main.vaultDetail.lockedStatus=ЗАБЛОКИРОВАНО
main.vaultDetail.unlockBtn=Разблокировка…
main.vaultDetail.unlockNowBtn=Разблокировать
main.vaultDetail.optionsBtn=Параметры хранилища
main.vaultDetail.passwordSavedInKeychain=Пароль сохранен
main.vaultDetail.passwordSavedInKeychain=Пароль сохранён
### Unlocked
main.vaultDetail.unlockedStatus=РАЗБЛОКИРОВАНО
main.vaultDetail.accessLocation=Содержимое хранилища доступно здесь:
@@ -247,7 +259,7 @@ wrongFileAlert.title=Как шифровать файлы
wrongFileAlert.header.title=Вы пытались зашифровать эти файлы?
wrongFileAlert.header.lead=Для этого Cryptomator создаёт том в системном диспетчере файлов.
wrongFileAlert.instruction.0=Чтобы зашифровать файлы, выполните следующее:
wrongFileAlert.instruction.1=1. Разблокируйте ваше хранилище.
wrongFileAlert.instruction.1=1. Разблокируйте хранилище.
wrongFileAlert.instruction.2=2. Нажмите кнопку "Показать", чтобы открыть том в диспетчере файлов.
wrongFileAlert.instruction.3=3. Добавьте файлы в этот том.
wrongFileAlert.link=Если нужна помощь, посетите
@@ -267,7 +279,7 @@ vaultOptions.mount.readonly=Только чтение
vaultOptions.mount.customMountFlags=Свои флаги монтирования
vaultOptions.mount.winDriveLetterOccupied=занято
vaultOptions.mount.mountPoint=Точка монтирования
vaultOptions.mount.mountPoint.auto=Автоматически выбирать подходящую
vaultOptions.mount.mountPoint.auto=Автоматически выбрать подходящее расположение
vaultOptions.mount.mountPoint.driveLetter=Использовать назначенную букву диска
vaultOptions.mount.mountPoint.custom=Свой путь
vaultOptions.mount.mountPoint.directoryPickerButton=Выбрать…
@@ -302,5 +314,5 @@ passwordStrength.messageLabel.3=Сильный
passwordStrength.messageLabel.4=Очень сильный
# Quit
quit.prompt=Выйти из приложения? Есть разблокированные хранилища.
quit.prompt=Выйти из приложения? Есть незаблокированные хранилища.
quit.lockAndQuit=Заблокировать и выйти

View File

@@ -104,6 +104,15 @@ unlock.error.heading=Nie je možné odomknúť účet
unlock.error.invalidMountPoint.notExisting=Bod pripojenia "%s" nie je adresár, nie je prázdny alebo neexistuje.
unlock.error.invalidMountPoint.existing=Bod pripojenia "%s" už existuje alebo chýba nadradený adresár.
# Lock
## Force
lock.forced.heading=Bežné uzamknutie zlyhalo
lock.forced.message=Zamknutie "%s" bolo zablokované prebiehajúcimi operáciami alebo otvorenými súbormi. Smiete vynútiť uzamknutie tejto peňaženky, ale prerušením I/O môže viesť k strate alebo neuloženiu dát.
lock.forced.confirmBtn=Vynútené uzamknutie
## Failure
lock.fail.heading=Uzatváranie peňaženky zlyhalo.
lock.fail.message=Peňaženku "%s" nie je možné uzamknúť. Uistite sa že neuložená páca je uložená inde a dôležité Read/Write operácie sú ukončené. Ináč uzavretím peňaženky, ukončíte proces Cryptomator-a.
# Migration
## Start
## Run

View File

@@ -107,6 +107,10 @@ unlock.error.heading=Kan inte låsa upp valvet
unlock.error.invalidMountPoint.notExisting=Monteringspunkten "%s" saknas eller är inte tom.
unlock.error.invalidMountPoint.existing=Monteringspunkten "%s" finns redan eller så saknas överordnad mapp.
# Lock
## Force
## Failure
# Migration
migration.title=Uppgradera valv
## Start

View File

@@ -102,10 +102,20 @@ unlock.success.message="%s" 'nin kilidi başarıyla açıldı! Kasanız şimdi e
unlock.success.rememberChoice=Seçimi hatırla, bunu bir daha gösterme
unlock.success.revealBtn=Kasayı Göster
## Failure
unlock.error.heading=Kasanın kilidi açılamıyor
### Invalid Mount Point
unlock.error.invalidMountPoint.notExisting=Bağlantı noktası boş bir dizin değil veya mevcut değil: %s
unlock.error.invalidMountPoint.existing=Bağlama noktası / klasör zaten var veya ana klasör eksik: %s
# Lock
## Force
lock.forced.heading=Normal kilitleme başarısız oldu
lock.forced.message="%s" nin kilitlenmesi, bekleyen işlemler veya açık dosyalar tarafından engellendi. Bu kasayı zorla kilitleyebilirsiniz, ancak G/Ç'nin kesilmesi kaydedilmemiş verilerin kaybına neden olabilir.
lock.forced.confirmBtn=Kilitlemeyi Zorla
## Failure
lock.fail.heading=Kasa kilitlenemedi.
lock.fail.message="%s" kasası kilitlenemedi. Kaydedilmemiş çalışmanın başka bir yere kaydedildiğinden ve önemli Okuma / Yazma işlemlerinin tamamlandığından emin olun. Kasayı kapatmak için Cryptomator işlemini sonlandırın.
# Migration
migration.title=Kasayı Güncelle
## Start
@@ -139,13 +149,17 @@ preferences.general.theme.automatic=Otomatik
preferences.general.theme.light=ık
preferences.general.theme.dark=Koyu
preferences.general.unlockThemes=Koyu modun kilidini aç
preferences.general.showMinimizeButton=Küçültme düğmesini göster
preferences.general.showTrayIcon=Sistem tepsisi simgesini göster (Yeniden başlatma gerekir)
preferences.general.startHidden=Cryptomator'ı başlatırken pencereyi gizle
preferences.general.debugLogging=Hata ayıklama günlüğünü etkinleştir
preferences.general.debugDirectory=Kayıt dosyalarını göster
preferences.general.autoStart=Cryptomator'u sistem başlangıcında çalıştır
preferences.general.keychainBackend=Şifreleri şununla depola:
preferences.general.keychainBackend.org.cryptomator.linux.keychain.SecretServiceKeychainAccess=Gnome Keyring
preferences.general.keychainBackend.org.cryptomator.linux.keychain.KDEWalletKeychainAccess=KDE Cüzdan
preferences.general.keychainBackend.org.cryptomator.macos.keychain.MacSystemKeychainAccess=macOS Anahtar Zinciri Erişimi
preferences.general.keychainBackend.org.cryptomator.windows.keychain.WindowsProtectedKeychainAccess=Windows Veri Koruması
preferences.general.interfaceOrientation=Arayüz Yönü
preferences.general.interfaceOrientation.ltr=Sola Yaslı
preferences.general.interfaceOrientation.rtl=Sağa Yaslı
@@ -169,8 +183,34 @@ preferences.donationKey.getDonationKey=Bir bağış anahtarı al
preferences.about=Hakkında
# Vault Statistics
stats.title=%s İçin İstatistikler
stats.cacheHitRate=Önbellek Kullanım Oranı
## Read
stats.read.throughput.idle=Okuma: boşta
stats.read.throughput.kibs=Okuma: %.2f kB/s
stats.read.throughput.mibs=Okuma: %.2f MB/s
stats.read.total.data.none=Okunan veri: -
stats.read.total.data.kib=Okunan veri: %.1f kB
stats.read.total.data.mib=Okunan veri: %.1f MB
stats.read.total.data.gib=Okunan veri: %.1f GB
stats.decr.total.data.none=Şifresi çözülen veri: -
stats.decr.total.data.kib=Şifresi çözülen veri: %.1f kB
stats.decr.total.data.mib=Şifresi çözülen veri: %.1f MB
stats.decr.total.data.gib=Şifresi çözülen veri: %.1f GB
stats.read.accessCount=Toplam okuma: %d
## Write
stats.write.throughput.idle=Yazma: boşta
stats.write.throughput.kibs=Yazma: %.2f kB/s
stats.write.throughput.mibs=Yazma: %.2f MB/s
stats.write.total.data.none=Yazılan veri: -
stats.write.total.data.kib=Yazılan veri: %.1f kB
stats.write.total.data.mib=Yazılan veri: %.1f MB
stats.write.total.data.gib=Yazılan veri: %.1f GB
stats.encr.total.data.none=Şifrelenen veri: -
stats.encr.total.data.kib=Şifrelenen veri: %.1f kB
stats.encr.total.data.mib=Şifrelenen veri: %.1f MB
stats.encr.total.data.gib=Şifrelenen veri: %.1f GB
stats.write.accessCount=Toplam yazma: %d
# Main Window
main.closeBtn.tooltip=Kapat
@@ -200,9 +240,11 @@ main.vaultDetail.accessLocation=Kasa içeriğinize buradan erişilebilir:
main.vaultDetail.revealBtn=Sürücüyü Göster
main.vaultDetail.lockBtn=Kilitle
main.vaultDetail.bytesPerSecondRead=Okuma:
main.vaultDetail.bytesPerSecondWritten=Yazma:
main.vaultDetail.throughput.idle=boşta
main.vaultDetail.throughput.kbps=%.1f kiB/s
main.vaultDetail.throughput.mbps=%.1f MiB/s
main.vaultDetail.stats=Kasa İstatistikleri
### Missing
main.vaultDetail.missing.info=Şifreleyici bu dosya yolunda bir kasa bulamadı.
main.vaultDetail.missing.recheck=Yeniden denetle

View File

@@ -95,11 +95,11 @@ forgetPassword.confirmBtn=忘记密码
# Unlock
unlock.title=解锁保险库
unlock.passwordPrompt=输入 "%s" 的密码
unlock.savePassword=保存密码
unlock.savePassword=记住密码
unlock.unlockBtn=解锁
## Success
unlock.success.message=已成功解锁 "%s"! 您现在可以访问该保险库
unlock.success.rememberChoice=记住该选择,不要再显示
unlock.success.rememberChoice=记住选项且不再显示
unlock.success.revealBtn=显示保险库
## Failure
unlock.error.heading=无法解锁保险库
@@ -107,6 +107,15 @@ unlock.error.heading=无法解锁保险库
unlock.error.invalidMountPoint.notExisting=挂载点 "%s" 不是目录、非空或不存在
unlock.error.invalidMountPoint.existing=挂载点 "%s" 已存在或缺少父文件夹
# Lock
## Force
lock.forced.heading=常规锁定失败
lock.forced.message=锁定 "%s" 被挂起的操作或使用中的文件中断。您可以强制锁定此保险库,不过请注意打断 I/O 可能导致未保存的数据丢失
lock.forced.confirmBtn=强制锁定
## Failure
lock.fail.heading=锁定保险库失败
lock.fail.message=保险库 "%s" 无法锁定。请确保在其他地方保存未保存的工作,以及重要的 "读/写" 操作已完成。为了顺利关闭保险库,请查杀 Cryptomator 进程
# Migration
migration.title=升级保险库
## Start
@@ -140,6 +149,8 @@ preferences.general.theme.automatic=自动
preferences.general.theme.light=亮色
preferences.general.theme.dark=暗色
preferences.general.unlockThemes=解锁暗黑模式
preferences.general.showMinimizeButton=显示最小化按钮
preferences.general.showTrayIcon=显示托盘图标 (需重启)
preferences.general.startHidden=最小化启动 Cryptomator 到系统托盘
preferences.general.debugLogging=启用调试日志
preferences.general.debugDirectory=显示日志文件

View File

@@ -102,10 +102,20 @@ unlock.success.message=成功解鎖 "%s"!您現在可以存取您的加密檔
unlock.success.rememberChoice=記得這個決定,不要再顯示
unlock.success.revealBtn=顯示加密檔案庫
## Failure
unlock.error.heading=無法解鎖加密檔案庫
### Invalid Mount Point
unlock.error.invalidMountPoint.notExisting=掛載點不是空目錄或是不存在:%s
unlock.error.invalidMountPoint.existing=掛載點已經存在或上層資料夾不存在:%s
# Lock
## Force
lock.forced.heading=正常鎖定失敗
lock.forced.message=仍有未完成的操作或開啟中的檔案以致無法鎖定 "%s"。您可以強制鎖定這個加密檔案庫,不過中斷讀寫可能會導致資料遺失或未被儲存。
lock.forced.confirmBtn=強制鎖定
## Failure
lock.fail.heading=鎖定加密檔案庫失敗。
lock.fail.message=加密檔案庫 "%s" 無法被鎖定。請確保未存檔的工作已儲存在別的地方以及重要的讀寫工作都已經完成。請強制結束 Cryptomator 以關閉加密檔案庫。
# Migration
migration.title=升級加密檔案庫
## Start
@@ -139,12 +149,17 @@ preferences.general.theme.automatic=自動
preferences.general.theme.light=亮色
preferences.general.theme.dark=暗色
preferences.general.unlockThemes=解鎖暗色模式
preferences.general.showMinimizeButton=顯示最小化按鈕
preferences.general.showTrayIcon=顯示系統工作列圖示 (需要重新啟動)
preferences.general.startHidden=啟動 Cryptomator 時隱藏視窗
preferences.general.debugLogging=啟用除錯日誌
preferences.general.debugDirectory=顯示日誌檔
preferences.general.autoStart=系統啟動時同時啟動 Cryptomator
preferences.general.keychainBackend=儲存密碼使用
preferences.general.keychainBackend.org.cryptomator.linux.keychain.SecretServiceKeychainAccess=Gnome 鑰匙圈
preferences.general.keychainBackend.org.cryptomator.linux.keychain.KDEWalletKeychainAccess=KDE 錢包
preferences.general.keychainBackend.org.cryptomator.macos.keychain.MacSystemKeychainAccess=macOS 鑰匙圈
preferences.general.keychainBackend.org.cryptomator.windows.keychain.WindowsProtectedKeychainAccess=Windows 數據保護
preferences.general.interfaceOrientation=界面排版方向
preferences.general.interfaceOrientation.ltr=由左至右
preferences.general.interfaceOrientation.rtl=由右至左
@@ -168,8 +183,34 @@ preferences.donationKey.getDonationKey=取得贊助金鑰
preferences.about=關於
# Vault Statistics
stats.title=%s 統計數據
stats.cacheHitRate=快取命中率
## Read
stats.read.throughput.idle=讀取:閒置
stats.read.throughput.kibs=讀取:%.2f kiB/s
stats.read.throughput.mibs=讀取:%.2f MiB/s
stats.read.total.data.none=資料讀取:無
stats.read.total.data.kib=資料讀取:%.1f kiB
stats.read.total.data.mib=資料讀取:%.1f MiB
stats.read.total.data.gib=資料讀取:%.1f GiB
stats.decr.total.data.none=資料解密:無
stats.decr.total.data.kib=資料解密:%.1f kiB
stats.decr.total.data.mib=資料解密:%.1f MiB
stats.decr.total.data.gib=資料解密:%.1f GiB
stats.read.accessCount=總讀取:%d
## Write
stats.write.throughput.idle=寫入:閒置
stats.write.throughput.kibs=寫入:%.2f kiB/s
stats.write.throughput.mibs=寫入:%.2f MiB/s
stats.write.total.data.none=資料讀取:無
stats.write.total.data.kib=資料寫入:%.1f kiB
stats.write.total.data.mib=資料寫入:%.1f MiB
stats.write.total.data.gib=資料寫入:%.1f GiB
stats.encr.total.data.none=資料加密:無
stats.encr.total.data.kib=資料加密:%.1f kiB
stats.encr.total.data.mib=資料加密:%.1f MiB
stats.encr.total.data.gib=資料加密:%.1f GiB
stats.write.accessCount=總寫入:%d
# Main Window
main.closeBtn.tooltip=關閉
@@ -199,9 +240,11 @@ main.vaultDetail.accessLocation=您可以從這裡取得您加密檔案庫的內
main.vaultDetail.revealBtn=顯示磁碟
main.vaultDetail.lockBtn=鎖定
main.vaultDetail.bytesPerSecondRead=讀取:
main.vaultDetail.bytesPerSecondWritten=寫入:
main.vaultDetail.throughput.idle=閒置
main.vaultDetail.throughput.kbps=%.1f kiB/s
main.vaultDetail.throughput.mbps=%.1f MiB/s
main.vaultDetail.stats=加密檔案庫統計數據
### Missing
main.vaultDetail.missing.info=Cryptomator 無法在指定位置找到加密檔案庫。
main.vaultDetail.missing.recheck=重新檢查

View File

@@ -11,7 +11,7 @@ GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see http://www.gnu.org/licenses/.
Cryptomator uses 46 third-party dependencies under the following licenses:
Cryptomator uses 47 third-party dependencies under the following licenses:
Apache License v2.0:
- jffi (com.github.jnr:jffi:1.2.23 - http://github.com/jnr/jffi)
- jnr-a64asm (com.github.jnr:jnr-a64asm:1.0.0 - http://nexus.sonatype.org/oss-repository-hosting.html/jnr-a64asm)
@@ -28,19 +28,20 @@ Cryptomator uses 46 third-party dependencies under the following licenses:
- Apache Commons CLI (commons-cli:commons-cli:1.4 - http://commons.apache.org/proper/commons-cli/)
- Apache Commons IO (commons-io:commons-io:2.6 - http://commons.apache.org/proper/commons-io/)
- javax.inject (javax.inject:javax.inject:1 - http://code.google.com/p/atinject/)
- Java Native Access (net.java.dev.jna:jna:5.5.0 - https://github.com/java-native-access/jna)
- Java Native Access (net.java.dev.jna:jna:5.6.0 - https://github.com/java-native-access/jna)
- Java Native Access Platform (net.java.dev.jna:jna-platform:5.5.0 - https://github.com/java-native-access/jna)
- Apache Commons Lang (org.apache.commons:commons-lang3:3.11 - https://commons.apache.org/proper/commons-lang/)
- Apache HttpCore (org.apache.httpcomponents:httpcore:4.4.13 - http://hc.apache.org/httpcomponents-core-ga)
- Jackrabbit WebDAV Library (org.apache.jackrabbit:jackrabbit-webdav:2.21.3 - http://jackrabbit.apache.org/jackrabbit-webdav/)
- Jetty :: Http Utility (org.eclipse.jetty:jetty-http:9.4.33.v20201020 - https://eclipse.org/jetty/jetty-http)
- Jetty :: IO Utility (org.eclipse.jetty:jetty-io:9.4.33.v20201020 - https://eclipse.org/jetty/jetty-io)
- Jetty :: Security (org.eclipse.jetty:jetty-security:9.4.33.v20201020 - https://eclipse.org/jetty/jetty-security)
- Jetty :: Server Core (org.eclipse.jetty:jetty-server:9.4.33.v20201020 - https://eclipse.org/jetty/jetty-server)
- Jetty :: Servlet Handling (org.eclipse.jetty:jetty-servlet:9.4.33.v20201020 - https://eclipse.org/jetty/jetty-servlet)
- Jetty :: Utilities (org.eclipse.jetty:jetty-util:9.4.33.v20201020 - https://eclipse.org/jetty/jetty-util)
- Jetty :: Webapp Application Support (org.eclipse.jetty:jetty-webapp:9.4.33.v20201020 - https://eclipse.org/jetty/jetty-webapp)
- Jetty :: XML utilities (org.eclipse.jetty:jetty-xml:9.4.33.v20201020 - https://eclipse.org/jetty/jetty-xml)
- Jetty :: Http Utility (org.eclipse.jetty:jetty-http:9.4.35.v20201120 - https://eclipse.org/jetty/jetty-http)
- Jetty :: IO Utility (org.eclipse.jetty:jetty-io:9.4.35.v20201120 - https://eclipse.org/jetty/jetty-io)
- Jetty :: Security (org.eclipse.jetty:jetty-security:9.4.35.v20201120 - https://eclipse.org/jetty/jetty-security)
- Jetty :: Server Core (org.eclipse.jetty:jetty-server:9.4.35.v20201120 - https://eclipse.org/jetty/jetty-server)
- Jetty :: Servlet Handling (org.eclipse.jetty:jetty-servlet:9.4.35.v20201120 - https://eclipse.org/jetty/jetty-servlet)
- Jetty :: Utilities (org.eclipse.jetty:jetty-util:9.4.35.v20201120 - https://eclipse.org/jetty/jetty-util)
- Jetty :: Utilities :: Ajax(JSON) (org.eclipse.jetty:jetty-util-ajax:9.4.35.v20201120 - https://eclipse.org/jetty/jetty-util-ajax)
- Jetty :: Webapp Application Support (org.eclipse.jetty:jetty-webapp:9.4.35.v20201120 - https://eclipse.org/jetty/jetty-webapp)
- Jetty :: XML utilities (org.eclipse.jetty:jetty-xml:9.4.35.v20201120 - https://eclipse.org/jetty/jetty-xml)
BSD:
- asm (org.ow2.asm:asm:7.1 - http://asm.ow2.org/)
- asm-analysis (org.ow2.asm:asm-analysis:7.1 - http://asm.ow2.org/)
@@ -48,14 +49,15 @@ Cryptomator uses 46 third-party dependencies under the following licenses:
- asm-tree (org.ow2.asm:asm-tree:7.1 - http://asm.ow2.org/)
- asm-util (org.ow2.asm:asm-util:7.1 - http://asm.ow2.org/)
Eclipse Public License - Version 1.0:
- Jetty :: Http Utility (org.eclipse.jetty:jetty-http:9.4.33.v20201020 - https://eclipse.org/jetty/jetty-http)
- Jetty :: IO Utility (org.eclipse.jetty:jetty-io:9.4.33.v20201020 - https://eclipse.org/jetty/jetty-io)
- Jetty :: Security (org.eclipse.jetty:jetty-security:9.4.33.v20201020 - https://eclipse.org/jetty/jetty-security)
- Jetty :: Server Core (org.eclipse.jetty:jetty-server:9.4.33.v20201020 - https://eclipse.org/jetty/jetty-server)
- Jetty :: Servlet Handling (org.eclipse.jetty:jetty-servlet:9.4.33.v20201020 - https://eclipse.org/jetty/jetty-servlet)
- Jetty :: Utilities (org.eclipse.jetty:jetty-util:9.4.33.v20201020 - https://eclipse.org/jetty/jetty-util)
- Jetty :: Webapp Application Support (org.eclipse.jetty:jetty-webapp:9.4.33.v20201020 - https://eclipse.org/jetty/jetty-webapp)
- Jetty :: XML utilities (org.eclipse.jetty:jetty-xml:9.4.33.v20201020 - https://eclipse.org/jetty/jetty-xml)
- Jetty :: Http Utility (org.eclipse.jetty:jetty-http:9.4.35.v20201120 - https://eclipse.org/jetty/jetty-http)
- Jetty :: IO Utility (org.eclipse.jetty:jetty-io:9.4.35.v20201120 - https://eclipse.org/jetty/jetty-io)
- Jetty :: Security (org.eclipse.jetty:jetty-security:9.4.35.v20201120 - https://eclipse.org/jetty/jetty-security)
- Jetty :: Server Core (org.eclipse.jetty:jetty-server:9.4.35.v20201120 - https://eclipse.org/jetty/jetty-server)
- Jetty :: Servlet Handling (org.eclipse.jetty:jetty-servlet:9.4.35.v20201120 - https://eclipse.org/jetty/jetty-servlet)
- Jetty :: Utilities (org.eclipse.jetty:jetty-util:9.4.35.v20201120 - https://eclipse.org/jetty/jetty-util)
- Jetty :: Utilities :: Ajax(JSON) (org.eclipse.jetty:jetty-util-ajax:9.4.35.v20201120 - https://eclipse.org/jetty/jetty-util-ajax)
- Jetty :: Webapp Application Support (org.eclipse.jetty:jetty-webapp:9.4.35.v20201120 - https://eclipse.org/jetty/jetty-webapp)
- Jetty :: XML utilities (org.eclipse.jetty:jetty-xml:9.4.35.v20201120 - https://eclipse.org/jetty/jetty-xml)
Eclipse Public License - v 2.0:
- jnr-posix (com.github.jnr:jnr-posix:3.0.54 - http://nexus.sonatype.org/oss-repository-hosting.html/jnr-posix)
GPLv2:
@@ -68,7 +70,7 @@ Cryptomator uses 46 third-party dependencies under the following licenses:
- javafx-graphics (org.openjfx:javafx-graphics:15 - https://openjdk.java.net/projects/openjfx/javafx-graphics/)
LGPL 2.1:
- jnr-posix (com.github.jnr:jnr-posix:3.0.54 - http://nexus.sonatype.org/oss-repository-hosting.html/jnr-posix)
- Java Native Access (net.java.dev.jna:jna:5.5.0 - https://github.com/java-native-access/jna)
- Java Native Access (net.java.dev.jna:jna:5.6.0 - https://github.com/java-native-access/jna)
- Java Native Access Platform (net.java.dev.jna:jna-platform:5.5.0 - https://github.com/java-native-access/jna)
MIT License:
- java jwt (com.auth0:java-jwt:3.11.0 - https://github.com/auth0/java-jwt)