Compare commits
1 Commits
android-v1
...
android-v0
Author | SHA1 | Date | |
---|---|---|---|
|
10b4907cee |
7
.gitignore
vendored
@@ -35,9 +35,4 @@ _vieux/
|
|||||||
_mydocs
|
_mydocs
|
||||||
.DS_Store
|
.DS_Store
|
||||||
Assets/DownloadBadges*.psd
|
Assets/DownloadBadges*.psd
|
||||||
node_modules
|
node_modules
|
||||||
Tools/github_oauth_token.txt
|
|
||||||
_releases
|
|
||||||
ReactNativeClient/lib/csstojs/
|
|
||||||
ElectronClient/app/gui/note-viewer/fonts/
|
|
||||||
Tools/commit_hook.txt
|
|
18
.travis.yml
@@ -1,16 +1,5 @@
|
|||||||
# Only build tags (Doesn't work - doesn't build anything)
|
|
||||||
if: tag IS present
|
|
||||||
|
|
||||||
rvm: 2.3.3
|
rvm: 2.3.3
|
||||||
|
|
||||||
# It's important to only build production branches otherwise Electron Builder
|
|
||||||
# might take assets from dev branches and overwrite those of production.
|
|
||||||
# https://docs.travis-ci.com/user/customizing-the-build/#Building-Specific-Branches
|
|
||||||
branches:
|
|
||||||
only:
|
|
||||||
- master
|
|
||||||
- /^v\d+\.\d+(\.\d+)?(-\S*)?$/
|
|
||||||
|
|
||||||
matrix:
|
matrix:
|
||||||
include:
|
include:
|
||||||
- os: osx
|
- os: osx
|
||||||
@@ -54,8 +43,7 @@ before_install:
|
|||||||
|
|
||||||
script:
|
script:
|
||||||
- |
|
- |
|
||||||
cd Tools
|
cd ElectronClient/app
|
||||||
|
rsync -aP ../../ReactNativeClient/lib/ lib/
|
||||||
npm install
|
npm install
|
||||||
cd ../ElectronClient/app
|
yarn dist
|
||||||
rsync -aP --delete ../../ReactNativeClient/lib/ lib/
|
|
||||||
npm install && yarn dist
|
|
||||||
|
BIN
Assets/All.psd
Normal file
BIN
Assets/AndroidFeatureGraphic.png
Normal file
After Width: | Height: | Size: 79 KiB |
BIN
Assets/AndroidFeatureGraphic.psd
Normal file
BIN
Assets/DemoDesktop.PNG
Normal file
After Width: | Height: | Size: 92 KiB |
BIN
Assets/DemoDesktop.psd
Normal file
BIN
Assets/Icon-Android-1024.png
Normal file
After Width: | Height: | Size: 92 KiB |
BIN
Assets/Icon-Android-512.png
Normal file
After Width: | Height: | Size: 37 KiB |
BIN
Assets/Icon-Android.psd
Normal file
BIN
Assets/Icon-ios-512.png
Normal file
After Width: | Height: | Size: 30 KiB |
BIN
Assets/Icon-ios.psd
Normal file
BIN
Assets/Icon.psd
Normal file
BIN
Assets/Icon144.png
Normal file
After Width: | Height: | Size: 5.5 KiB |
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 1.5 KiB |
BIN
Assets/Icon512.png
Normal file
After Width: | Height: | Size: 28 KiB |
BIN
Assets/Icon72.png
Normal file
After Width: | Height: | Size: 2.3 KiB |
Before Width: | Height: | Size: 3.3 KiB After Width: | Height: | Size: 3.3 KiB |
BIN
Assets/Laptop-Terminal.psd
Normal file
BIN
Assets/Tablet.psd
Normal file
2
Assets/build_mac_icons.sh
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
iconutil --convert icns macOs.iconset
|
2
Assets/check-square-o.svg
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<svg width="1792" height="1792" viewBox="0 0 1792 1792" xmlns="http://www.w3.org/2000/svg"><path d="M1472 930v318q0 119-84.5 203.5t-203.5 84.5h-832q-119 0-203.5-84.5t-84.5-203.5v-832q0-119 84.5-203.5t203.5-84.5h832q63 0 117 25 15 7 18 23 3 17-9 29l-49 49q-10 10-23 10-3 0-9-2-23-6-45-6h-832q-66 0-113 47t-47 113v832q0 66 47 113t113 47h832q66 0 113-47t47-113v-254q0-13 9-22l64-64q10-10 23-10 6 0 12 3 20 8 20 29zm231-489l-814 814q-24 24-57 24t-57-24l-430-430q-24-24-24-57t24-57l110-110q24-24 57-24t57 24l263 263 647-647q24-24 57-24t57 24l110 110q24 24 24 57t-24 57z"/></svg>
|
After Width: | Height: | Size: 612 B |
2
Assets/check-square.svg
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<svg width="1792" height="1792" viewBox="0 0 1792 1792" xmlns="http://www.w3.org/2000/svg"><path d="M813 1299l614-614q19-19 19-45t-19-45l-102-102q-19-19-45-19t-45 19l-467 467-211-211q-19-19-45-19t-45 19l-102 102q-19 19-19 45t19 45l358 358q19 19 45 19t45-19zm851-883v960q0 119-84.5 203.5t-203.5 84.5h-960q-119 0-203.5-84.5t-84.5-203.5v-960q0-119 84.5-203.5t203.5-84.5h960q119 0 203.5 84.5t84.5 203.5z"/></svg>
|
After Width: | Height: | Size: 447 B |
2
Assets/square-o.svg
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
<?xml version='1.0' encoding='utf-8'?>
|
||||||
|
<svg viewBox='0 0 1792 1792' xmlns='http://www.w3.org/2000/svg'><path d='M1312 256h-832q-66 0-113 47t-47 113v832q0 66 47 113t113 47h832q66 0 113-47t47-113v-832q0-66-47-113t-113-47zm288 160v832q0 119-84.5 203.5t-203.5 84.5h-832q-119 0-203.5-84.5t-84.5-203.5v-832q0-119 84.5-203.5t203.5-84.5h832q119 0 203.5 84.5t84.5 203.5z'/></svg>
|
After Width: | Height: | Size: 370 B |
54
BUILD.md
@@ -1,72 +1,40 @@
|
|||||||
# General information
|
# General information
|
||||||
|
|
||||||
- All the applications share the same library, which, for historical reasons, is in ReactNativeClient/lib. This library is copied to the relevant directories when building each app.
|
- All the applications share the same library, which, for historical reasons, is in ReactNativeClient/lib. This library is copied to the relevant directories when builing each app.
|
||||||
- The translations are built by running CliClient/build-translation.sh. You normally don't need to run this if you haven't updated the translation since the compiled files are on the repository.
|
- The translations are built by running CliClient/build-translation.sh. For this reasons, it's generally better to get the CLI app to build first so that everything is setup correctly.
|
||||||
|
- Note: building translations is no longer required to run the apps, so you can ignore all the below requirements about gettext.
|
||||||
|
|
||||||
## macOS dependencies
|
## macOS dependencies
|
||||||
|
|
||||||
brew install yarn node
|
brew install yarn node xgettext
|
||||||
echo 'export PATH="/usr/local/opt/gettext/bin:$PATH"' >> ~/.bash_profile
|
npm install -g node-gyp
|
||||||
source ~/.bash_profile
|
echo 'export PATH="/usr/local/opt/gettext/bin:$PATH"' >> ~/.bash_profile
|
||||||
|
source ~/.bash_profile
|
||||||
|
|
||||||
If you get a node-gyp related error you might need to manually install it: `npm install -g node-gyp`
|
## Linux and Windows dependencies
|
||||||
|
|
||||||
## Linux and Windows (WSL) dependencies
|
|
||||||
|
|
||||||
- Install yarn - https://yarnpkg.com/lang/en/docs/install/
|
- Install yarn - https://yarnpkg.com/lang/en/docs/install/
|
||||||
- Install node v8.x (check with `node --version`) - https://nodejs.org/en/
|
- Install node v8.x (check with `node --version`) - https://nodejs.org/en/
|
||||||
- If you get a node-gyp related error you might need to manually install it: `npm install -g node-gyp`
|
|
||||||
|
|
||||||
# Building the tools
|
|
||||||
|
|
||||||
Before building any of the applications, you need to build the tools:
|
|
||||||
|
|
||||||
```
|
|
||||||
cd Tools
|
|
||||||
npm install
|
|
||||||
```
|
|
||||||
|
|
||||||
# Building the Electron application
|
# Building the Electron application
|
||||||
|
|
||||||
```
|
```
|
||||||
cd ElectronClient/app
|
cd ElectronClient/app
|
||||||
rsync --delete -a ../../ReactNativeClient/lib/ lib/
|
rsync -a ../../ReactNativeClient/lib/ lib/
|
||||||
npm install
|
npm install
|
||||||
yarn dist
|
yarn dist
|
||||||
```
|
```
|
||||||
|
|
||||||
If there's an error `while loading shared libraries: libgconf-2.so.4: cannot open shared object file: No such file or directory`, run `sudo apt-get install libgconf-2-4`
|
If there's an error `while loading shared libraries: libgconf-2.so.4: cannot open shared object file: No such file or directory`, run `sudo apt-get install libgconf-2-4`
|
||||||
|
|
||||||
For node-gyp to work, you might need to install the `windows-build-tools` using `npm install --global windows-build-tools`.
|
|
||||||
|
|
||||||
That will create the executable file in the `dist` directory.
|
That will create the executable file in the `dist` directory.
|
||||||
|
|
||||||
From `/ElectronClient` you can also run `run.sh` to run the app for testing.
|
From `/ElectronClient` you can also run `run.sh` to run the app for testing.
|
||||||
|
|
||||||
## Building Electron application on Windows
|
|
||||||
|
|
||||||
```
|
|
||||||
cd Tools
|
|
||||||
npm install
|
|
||||||
cd ..\ElectronClient\app
|
|
||||||
xcopy /C /I /H /R /Y /S ..\..\ReactNativeClient\lib lib
|
|
||||||
npm install
|
|
||||||
yarn dist
|
|
||||||
```
|
|
||||||
|
|
||||||
# Building the Mobile application
|
# Building the Mobile application
|
||||||
|
|
||||||
First you need to setup React Native to build projects with native code. For this, follow the instructions on the [Get Started](https://facebook.github.io/react-native/docs/getting-started.html) tutorial, in the "Building Projects with Native Code" tab.
|
From `/ReactNativeClient`, run `npm install`, then `react-native run-ios` or `react-native run-android`.
|
||||||
|
|
||||||
Then, from `/ReactNativeClient`, run `npm install`, then `react-native run-ios` or `react-native run-android`.
|
|
||||||
|
|
||||||
# Building the Terminal application
|
# Building the Terminal application
|
||||||
|
|
||||||
```
|
From `/CliClient`, run `npm install` then run `run.sh`. If you get an error about `xgettext`, comment out the command `node build-translation.js --silent` in build.sh
|
||||||
cd CliClient
|
|
||||||
npm install
|
|
||||||
./build.sh
|
|
||||||
rsync --delete -aP ../ReactNativeClient/locales/ build/locales/
|
|
||||||
```
|
|
||||||
|
|
||||||
Run `run.sh` to start the application for testing.
|
|
||||||
|
@@ -1,15 +0,0 @@
|
|||||||
# Reporting a bug
|
|
||||||
|
|
||||||
Please check first that it [has not already been reported](https://github.com/laurent22/joplin/issues?utf8=%E2%9C%93&q=is%3Aissue). Also consider [enabling debug mode](https://github.com/laurent22/joplin/blob/master/readme/debugging.md) before reporting the issue so that you can provide as much details as possible to help fix it.
|
|
||||||
|
|
||||||
If possible, **please provide a screenshot**. A screenshot showing the problem is often more useful than a paragraph describing it as it can make it immediately clear what the issue is.
|
|
||||||
|
|
||||||
# Feature requests
|
|
||||||
|
|
||||||
Again, please check that it has not already been requested. If it has, simply **up-vote the issue** - the ones with the most up-votes are likely to be implemented. Adding a "+1" comment does nothing.
|
|
||||||
|
|
||||||
# Adding new features
|
|
||||||
|
|
||||||
If you want to add a new feature, consider asking about it before implementing it or checking existing discussions to make sure it is within the scope of the project. Of course you are free to create the pull request directly but it is not guaranteed it is going to be accepted.
|
|
||||||
|
|
||||||
Building the apps is relatively easy - please [see the build instructions](https://github.com/laurent22/joplin/blob/master/BUILD.md) for more details.
|
|
3
CliClient/.gitignore
vendored
@@ -18,5 +18,4 @@ tests/cli-integration/
|
|||||||
tests/sync
|
tests/sync
|
||||||
out.txt
|
out.txt
|
||||||
linkToLocal.sh
|
linkToLocal.sh
|
||||||
yarn-error.log
|
yarn-error.log
|
||||||
tests/support/dropbox-auth.txt
|
|
@@ -1,6 +1,6 @@
|
|||||||
const { _ } = require('lib/locale.js');
|
const { _ } = require('lib/locale.js');
|
||||||
const { Logger } = require('lib/logger.js');
|
const { Logger } = require('lib/logger.js');
|
||||||
const Resource = require('lib/models/Resource.js');
|
const { Resource } = require('lib/models/resource.js');
|
||||||
const { netUtils } = require('lib/net-utils.js');
|
const { netUtils } = require('lib/net-utils.js');
|
||||||
|
|
||||||
const http = require("http");
|
const http = require("http");
|
||||||
|
@@ -1,9 +1,9 @@
|
|||||||
const { Logger } = require('lib/logger.js');
|
const { Logger } = require('lib/logger.js');
|
||||||
const Folder = require('lib/models/Folder.js');
|
const { Folder } = require('lib/models/folder.js');
|
||||||
const Tag = require('lib/models/Tag.js');
|
const { Tag } = require('lib/models/tag.js');
|
||||||
const BaseModel = require('lib/BaseModel.js');
|
const { BaseModel } = require('lib/base-model.js');
|
||||||
const Note = require('lib/models/Note.js');
|
const { Note } = require('lib/models/note.js');
|
||||||
const Resource = require('lib/models/Resource.js');
|
const { Resource } = require('lib/models/resource.js');
|
||||||
const { cliUtils } = require('./cli-utils.js');
|
const { cliUtils } = require('./cli-utils.js');
|
||||||
const { reducer, defaultState } = require('lib/reducer.js');
|
const { reducer, defaultState } = require('lib/reducer.js');
|
||||||
const { splitCommandString } = require('lib/string-utils.js');
|
const { splitCommandString } = require('lib/string-utils.js');
|
||||||
@@ -14,7 +14,6 @@ const chalk = require('chalk');
|
|||||||
const tk = require('terminal-kit');
|
const tk = require('terminal-kit');
|
||||||
const TermWrapper = require('tkwidgets/framework/TermWrapper.js');
|
const TermWrapper = require('tkwidgets/framework/TermWrapper.js');
|
||||||
const Renderer = require('tkwidgets/framework/Renderer.js');
|
const Renderer = require('tkwidgets/framework/Renderer.js');
|
||||||
const DecryptionWorker = require('lib/services/DecryptionWorker');
|
|
||||||
|
|
||||||
const BaseWidget = require('tkwidgets/BaseWidget.js');
|
const BaseWidget = require('tkwidgets/BaseWidget.js');
|
||||||
const ListWidget = require('tkwidgets/ListWidget.js');
|
const ListWidget = require('tkwidgets/ListWidget.js');
|
||||||
@@ -35,55 +34,37 @@ const ConsoleWidget = require('./gui/ConsoleWidget.js');
|
|||||||
|
|
||||||
class AppGui {
|
class AppGui {
|
||||||
|
|
||||||
constructor(app, store, keymap) {
|
constructor(app, store) {
|
||||||
try {
|
this.app_ = app;
|
||||||
this.app_ = app;
|
this.store_ = store;
|
||||||
this.store_ = store;
|
|
||||||
|
|
||||||
BaseWidget.setLogger(app.logger());
|
BaseWidget.setLogger(app.logger());
|
||||||
|
|
||||||
this.term_ = new TermWrapper(tk.terminal);
|
this.term_ = new TermWrapper(tk.terminal);
|
||||||
|
|
||||||
// Some keys are directly handled by the tkwidget framework
|
this.renderer_ = null;
|
||||||
// so they need to be remapped in a different way.
|
this.logger_ = new Logger();
|
||||||
this.tkWidgetKeys_ = {
|
this.buildUi();
|
||||||
'focus_next': 'TAB',
|
|
||||||
'focus_previous': 'SHIFT_TAB',
|
|
||||||
'move_up': 'UP',
|
|
||||||
'move_down': 'DOWN',
|
|
||||||
'page_down': 'PAGE_DOWN',
|
|
||||||
'page_up': 'PAGE_UP',
|
|
||||||
};
|
|
||||||
|
|
||||||
this.renderer_ = null;
|
this.renderer_ = new Renderer(this.term(), this.rootWidget_);
|
||||||
this.logger_ = new Logger();
|
|
||||||
this.buildUi();
|
|
||||||
|
|
||||||
this.renderer_ = new Renderer(this.term(), this.rootWidget_);
|
this.app_.on('modelAction', async (event) => {
|
||||||
|
await this.handleModelAction(event.action);
|
||||||
|
});
|
||||||
|
|
||||||
this.app_.on('modelAction', async (event) => {
|
this.shortcuts_ = this.setupShortcuts();
|
||||||
await this.handleModelAction(event.action);
|
|
||||||
});
|
|
||||||
|
|
||||||
this.keymap_ = this.setupKeymap(keymap);
|
this.inputMode_ = AppGui.INPUT_MODE_NORMAL;
|
||||||
|
|
||||||
this.inputMode_ = AppGui.INPUT_MODE_NORMAL;
|
this.commandCancelCalled_ = false;
|
||||||
|
|
||||||
this.commandCancelCalled_ = false;
|
this.currentShortcutKeys_ = [];
|
||||||
|
this.lastShortcutKeyTime_ = 0;
|
||||||
|
|
||||||
this.currentShortcutKeys_ = [];
|
// Recurrent sync is setup only when the GUI is started. In
|
||||||
this.lastShortcutKeyTime_ = 0;
|
// a regular command it's not necessary since the process
|
||||||
|
// exits right away.
|
||||||
// Recurrent sync is setup only when the GUI is started. In
|
reg.setupRecurrentSync();
|
||||||
// a regular command it's not necessary since the process
|
|
||||||
// exits right away.
|
|
||||||
reg.setupRecurrentSync();
|
|
||||||
DecryptionWorker.instance().scheduleStart();
|
|
||||||
} catch (error) {
|
|
||||||
this.fullScreen(false);
|
|
||||||
console.error(error);
|
|
||||||
process.exit(1);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
store() {
|
store() {
|
||||||
@@ -99,16 +80,8 @@ class AppGui {
|
|||||||
await this.renderer_.renderRoot();
|
await this.renderer_.renderRoot();
|
||||||
}
|
}
|
||||||
|
|
||||||
termSaveState() {
|
prompt(initialText = '', promptString = ':') {
|
||||||
return this.term().saveState();
|
return this.widget('statusBar').prompt(initialText, promptString);
|
||||||
}
|
|
||||||
|
|
||||||
termRestoreState(state) {
|
|
||||||
return this.term().restoreState(state);
|
|
||||||
}
|
|
||||||
|
|
||||||
prompt(initialText = '', promptString = ':', options = null) {
|
|
||||||
return this.widget('statusBar').prompt(initialText, promptString, options);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
stdoutMaxWidth() {
|
stdoutMaxWidth() {
|
||||||
@@ -122,7 +95,6 @@ class AppGui {
|
|||||||
buildUi() {
|
buildUi() {
|
||||||
this.rootWidget_ = new ReduxRootWidget(this.store_);
|
this.rootWidget_ = new ReduxRootWidget(this.store_);
|
||||||
this.rootWidget_.name = 'root';
|
this.rootWidget_.name = 'root';
|
||||||
this.rootWidget_.autoShortcutsEnabled = false;
|
|
||||||
|
|
||||||
const folderList = new FolderListWidget();
|
const folderList = new FolderListWidget();
|
||||||
folderList.style = {
|
folderList.style = {
|
||||||
@@ -287,31 +259,155 @@ class AppGui {
|
|||||||
|
|
||||||
addCommandToConsole(cmd) {
|
addCommandToConsole(cmd) {
|
||||||
if (!cmd) return;
|
if (!cmd) return;
|
||||||
const isConfigPassword = cmd.indexOf('config ') >= 0 && cmd.indexOf('password') >= 0;
|
|
||||||
if (isConfigPassword) return;
|
|
||||||
this.stdout(chalk.cyan.bold('> ' + cmd));
|
this.stdout(chalk.cyan.bold('> ' + cmd));
|
||||||
}
|
}
|
||||||
|
|
||||||
setupKeymap(keymap) {
|
setupShortcuts() {
|
||||||
const output = [];
|
const shortcuts = {};
|
||||||
|
|
||||||
for (let i = 0; i < keymap.length; i++) {
|
shortcuts['TAB'] = {
|
||||||
const item = Object.assign({}, keymap[i]);
|
friendlyName: 'Tab',
|
||||||
|
description: () => _('Give focus to next pane'),
|
||||||
if (!item.command) throw new Error('Missing command for keymap item: ' + JSON.stringify(item));
|
isDocOnly: true,
|
||||||
|
|
||||||
if (!('type' in item)) item.type = 'exec';
|
|
||||||
|
|
||||||
if (item.command in this.tkWidgetKeys_) {
|
|
||||||
item.type = 'tkwidgets';
|
|
||||||
}
|
|
||||||
|
|
||||||
item.canRunAlongOtherCommands = item.type === 'function' && ['toggle_metadata', 'toggle_console'].indexOf(item.command) >= 0;
|
|
||||||
|
|
||||||
output.push(item);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return output;
|
shortcuts['SHIFT_TAB'] = {
|
||||||
|
friendlyName: 'Shift+Tab',
|
||||||
|
description: () => _('Give focus to previous pane'),
|
||||||
|
isDocOnly: true,
|
||||||
|
}
|
||||||
|
|
||||||
|
shortcuts[':'] = {
|
||||||
|
description: () => _('Enter command line mode'),
|
||||||
|
action: async () => {
|
||||||
|
const cmd = await this.widget('statusBar').prompt();
|
||||||
|
if (!cmd) return;
|
||||||
|
this.addCommandToConsole(cmd);
|
||||||
|
await this.processCommand(cmd);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
shortcuts['ESC'] = { // Built into terminal-kit inputField
|
||||||
|
description: () => _('Exit command line mode'),
|
||||||
|
isDocOnly: true,
|
||||||
|
};
|
||||||
|
|
||||||
|
shortcuts['ENTER'] = {
|
||||||
|
description: () => _('Edit the selected note'),
|
||||||
|
action: () => {
|
||||||
|
const w = this.widget('mainWindow').focusedWidget;
|
||||||
|
if (w.name === 'folderList') {
|
||||||
|
this.widget('noteList').focus();
|
||||||
|
} else if (w.name === 'noteList' || w.name === 'noteText') {
|
||||||
|
this.processCommand('edit $n');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
shortcuts['CTRL_C'] = {
|
||||||
|
description: () => _('Cancel the current command.'),
|
||||||
|
friendlyName: 'Ctrl+C',
|
||||||
|
isDocOnly: true,
|
||||||
|
}
|
||||||
|
|
||||||
|
shortcuts['CTRL_D'] = {
|
||||||
|
description: () => _('Exit the application.'),
|
||||||
|
friendlyName: 'Ctrl+D',
|
||||||
|
isDocOnly: true,
|
||||||
|
}
|
||||||
|
|
||||||
|
shortcuts['DELETE'] = {
|
||||||
|
description: () => _('Delete the currently selected note or notebook.'),
|
||||||
|
action: async () => {
|
||||||
|
if (this.widget('folderList').hasFocus) {
|
||||||
|
const item = this.widget('folderList').selectedJoplinItem;
|
||||||
|
|
||||||
|
if (!item) return;
|
||||||
|
|
||||||
|
if (item.type_ === BaseModel.TYPE_FOLDER) {
|
||||||
|
await this.processCommand('rmbook ' + item.id);
|
||||||
|
} else if (item.type_ === BaseModel.TYPE_TAG) {
|
||||||
|
this.stdout(_('To delete a tag, untag the associated notes.'));
|
||||||
|
} else if (item.type_ === BaseModel.TYPE_SEARCH) {
|
||||||
|
this.store().dispatch({
|
||||||
|
type: 'SEARCH_DELETE',
|
||||||
|
id: item.id,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} else if (this.widget('noteList').hasFocus) {
|
||||||
|
await this.processCommand('rmnote $n');
|
||||||
|
} else {
|
||||||
|
this.stdout(_('Please select the note or notebook to be deleted first.'));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
shortcuts['BACKSPACE'] = {
|
||||||
|
alias: 'DELETE',
|
||||||
|
};
|
||||||
|
|
||||||
|
shortcuts[' '] = {
|
||||||
|
friendlyName: 'SPACE',
|
||||||
|
description: () => _('Set a to-do as completed / not completed'),
|
||||||
|
action: 'todo toggle $n',
|
||||||
|
}
|
||||||
|
|
||||||
|
shortcuts['tc'] = {
|
||||||
|
description: () => _('[t]oggle [c]onsole between maximized/minimized/hidden/visible.'),
|
||||||
|
action: () => {
|
||||||
|
if (!this.consoleIsShown()) {
|
||||||
|
this.showConsole();
|
||||||
|
this.minimizeConsole();
|
||||||
|
} else {
|
||||||
|
if (this.consoleIsMaximized()) {
|
||||||
|
this.hideConsole();
|
||||||
|
} else {
|
||||||
|
this.maximizeConsole();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
canRunAlongOtherCommands: true,
|
||||||
|
}
|
||||||
|
|
||||||
|
shortcuts['/'] = {
|
||||||
|
description: () => _('Search'),
|
||||||
|
action: { type: 'prompt', initialText: 'search ""', cursorPosition: -2 },
|
||||||
|
};
|
||||||
|
|
||||||
|
shortcuts['tm'] = {
|
||||||
|
description: () => _('[t]oggle note [m]etadata.'),
|
||||||
|
action: () => {
|
||||||
|
this.toggleNoteMetadata();
|
||||||
|
},
|
||||||
|
canRunAlongOtherCommands: true,
|
||||||
|
}
|
||||||
|
|
||||||
|
shortcuts['mn'] = {
|
||||||
|
description: () => _('[M]ake a new [n]ote'),
|
||||||
|
action: { type: 'prompt', initialText: 'mknote ""', cursorPosition: -2 },
|
||||||
|
}
|
||||||
|
|
||||||
|
shortcuts['mt'] = {
|
||||||
|
description: () => _('[M]ake a new [t]odo'),
|
||||||
|
action: { type: 'prompt', initialText: 'mktodo ""', cursorPosition: -2 },
|
||||||
|
}
|
||||||
|
|
||||||
|
shortcuts['mb'] = {
|
||||||
|
description: () => _('[M]ake a new note[b]ook'),
|
||||||
|
action: { type: 'prompt', initialText: 'mkbook ""', cursorPosition: -2 },
|
||||||
|
}
|
||||||
|
|
||||||
|
shortcuts['yn'] = {
|
||||||
|
description: () => _('Copy ([Y]ank) the [n]ote to a notebook.'),
|
||||||
|
action: { type: 'prompt', initialText: 'cp $n ""', cursorPosition: -2 },
|
||||||
|
}
|
||||||
|
|
||||||
|
shortcuts['dn'] = {
|
||||||
|
description: () => _('Move the note to a notebook.'),
|
||||||
|
action: { type: 'prompt', initialText: 'mv $n ""', cursorPosition: -2 },
|
||||||
|
}
|
||||||
|
|
||||||
|
return shortcuts;
|
||||||
}
|
}
|
||||||
|
|
||||||
toggleConsole() {
|
toggleConsole() {
|
||||||
@@ -386,16 +482,8 @@ class AppGui {
|
|||||||
return this.logger_;
|
return this.logger_;
|
||||||
}
|
}
|
||||||
|
|
||||||
keymap() {
|
shortcuts() {
|
||||||
return this.keymap_;
|
return this.shortcuts_;
|
||||||
}
|
|
||||||
|
|
||||||
keymapItemByKey(key) {
|
|
||||||
for (let i = 0; i < this.keymap_.length; i++) {
|
|
||||||
const item = this.keymap_[i];
|
|
||||||
if (item.keys.indexOf(key) >= 0) return item;
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
term() {
|
term() {
|
||||||
@@ -426,77 +514,17 @@ class AppGui {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async processFunctionCommand(cmd) {
|
async processCommand(cmd) {
|
||||||
|
|
||||||
if (cmd === 'activate') {
|
|
||||||
|
|
||||||
const w = this.widget('mainWindow').focusedWidget;
|
|
||||||
if (w.name === 'folderList') {
|
|
||||||
this.widget('noteList').focus();
|
|
||||||
} else if (w.name === 'noteList' || w.name === 'noteText') {
|
|
||||||
this.processPromptCommand('edit $n');
|
|
||||||
}
|
|
||||||
|
|
||||||
} else if (cmd === 'delete') {
|
|
||||||
|
|
||||||
if (this.widget('folderList').hasFocus) {
|
|
||||||
const item = this.widget('folderList').selectedJoplinItem;
|
|
||||||
|
|
||||||
if (!item) return;
|
|
||||||
|
|
||||||
if (item.type_ === BaseModel.TYPE_FOLDER) {
|
|
||||||
await this.processPromptCommand('rmbook ' + item.id);
|
|
||||||
} else if (item.type_ === BaseModel.TYPE_TAG) {
|
|
||||||
this.stdout(_('To delete a tag, untag the associated notes.'));
|
|
||||||
} else if (item.type_ === BaseModel.TYPE_SEARCH) {
|
|
||||||
this.store().dispatch({
|
|
||||||
type: 'SEARCH_DELETE',
|
|
||||||
id: item.id,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
} else if (this.widget('noteList').hasFocus) {
|
|
||||||
await this.processPromptCommand('rmnote $n');
|
|
||||||
} else {
|
|
||||||
this.stdout(_('Please select the note or notebook to be deleted first.'));
|
|
||||||
}
|
|
||||||
|
|
||||||
} else if (cmd === 'toggle_console') {
|
|
||||||
|
|
||||||
if (!this.consoleIsShown()) {
|
|
||||||
this.showConsole();
|
|
||||||
this.minimizeConsole();
|
|
||||||
} else {
|
|
||||||
if (this.consoleIsMaximized()) {
|
|
||||||
this.hideConsole();
|
|
||||||
} else {
|
|
||||||
this.maximizeConsole();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
} else if (cmd === 'toggle_metadata') {
|
|
||||||
|
|
||||||
this.toggleNoteMetadata();
|
|
||||||
|
|
||||||
} else if (cmd === 'enter_command_line_mode') {
|
|
||||||
|
|
||||||
const cmd = await this.widget('statusBar').prompt();
|
|
||||||
if (!cmd) return;
|
|
||||||
this.addCommandToConsole(cmd);
|
|
||||||
await this.processPromptCommand(cmd);
|
|
||||||
|
|
||||||
} else {
|
|
||||||
|
|
||||||
throw new Error('Unknown command: ' + cmd);
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async processPromptCommand(cmd) {
|
|
||||||
if (!cmd) return;
|
if (!cmd) return;
|
||||||
cmd = cmd.trim();
|
cmd = cmd.trim();
|
||||||
if (!cmd.length) return;
|
if (!cmd.length) return;
|
||||||
|
|
||||||
// this.logger().debug('Got command: ' + cmd);
|
this.logger().info('Got command: ' + cmd);
|
||||||
|
|
||||||
|
if (cmd === 'q' || cmd === 'wq' || cmd === 'qa') { // Vim bonus
|
||||||
|
await this.app().exit();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
let note = this.widget('noteList').currentItem;
|
let note = this.widget('noteList').currentItem;
|
||||||
@@ -520,10 +548,6 @@ class AppGui {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.widget('console').scrollBottom();
|
this.widget('console').scrollBottom();
|
||||||
|
|
||||||
// Invalidate so that the screen is redrawn in case inputting a command has moved
|
|
||||||
// the GUI up (in particular due to autocompletion), it's moved back to the right position.
|
|
||||||
this.widget('root').invalidate();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async updateFolderList() {
|
async updateFolderList() {
|
||||||
@@ -748,34 +772,35 @@ class AppGui {
|
|||||||
// -------------------------------------------------------------------------
|
// -------------------------------------------------------------------------
|
||||||
|
|
||||||
const shortcutKey = this.currentShortcutKeys_.join('');
|
const shortcutKey = this.currentShortcutKeys_.join('');
|
||||||
let keymapItem = this.keymapItemByKey(shortcutKey);
|
let cmd = shortcutKey in this.shortcuts_ ? this.shortcuts_[shortcutKey] : null;
|
||||||
|
|
||||||
// If this command is an alias to another command, resolve to the actual command
|
// If this command is an alias to another command, resolve to the actual command
|
||||||
|
if (cmd && cmd.alias) cmd = this.shortcuts_[cmd.alias];
|
||||||
|
|
||||||
let processShortcutKeys = !this.app().currentCommand() && keymapItem;
|
let processShortcutKeys = !this.app().currentCommand() && cmd;
|
||||||
if (keymapItem && keymapItem.canRunAlongOtherCommands) processShortcutKeys = true;
|
if (cmd && cmd.canRunAlongOtherCommands) processShortcutKeys = true;
|
||||||
if (statusBar.promptActive) processShortcutKeys = false;
|
if (statusBar.promptActive) processShortcutKeys = false;
|
||||||
|
if (cmd && cmd.isDocOnly) processShortcutKeys = false;
|
||||||
|
|
||||||
if (processShortcutKeys) {
|
if (processShortcutKeys) {
|
||||||
this.logger().debug('Shortcut:', shortcutKey, keymapItem);
|
this.logger().info('Shortcut:', shortcutKey, cmd.description());
|
||||||
|
|
||||||
this.currentShortcutKeys_ = [];
|
this.currentShortcutKeys_ = [];
|
||||||
|
if (typeof cmd.action === 'function') {
|
||||||
if (keymapItem.type === 'function') {
|
await cmd.action();
|
||||||
this.processFunctionCommand(keymapItem.command);
|
} else if (typeof cmd.action === 'object') {
|
||||||
} else if (keymapItem.type === 'prompt') {
|
if (cmd.action.type === 'prompt') {
|
||||||
let promptOptions = {};
|
let promptOptions = {};
|
||||||
if ('cursorPosition' in keymapItem) promptOptions.cursorPosition = keymapItem.cursorPosition;
|
if ('cursorPosition' in cmd.action) promptOptions.cursorPosition = cmd.action.cursorPosition;
|
||||||
const commandString = await statusBar.prompt(keymapItem.command ? keymapItem.command : '', null, promptOptions);
|
const commandString = await statusBar.prompt(cmd.action.initialText ? cmd.action.initialText : '', null, promptOptions);
|
||||||
this.addCommandToConsole(commandString);
|
this.addCommandToConsole(commandString);
|
||||||
await this.processPromptCommand(commandString);
|
await this.processCommand(commandString);
|
||||||
} else if (keymapItem.type === 'exec') {
|
} else {
|
||||||
this.stdout(keymapItem.command);
|
throw new Error('Unknown command: ' + JSON.stringify(cmd.action));
|
||||||
await this.processPromptCommand(keymapItem.command);
|
}
|
||||||
} else if (keymapItem.type === 'tkwidgets') {
|
} else { // String
|
||||||
this.widget('root').handleKey(this.tkWidgetKeys_[keymapItem.command]);
|
this.stdout(cmd.action);
|
||||||
} else {
|
await this.processCommand(cmd.action);
|
||||||
throw new Error('Unknown command type: ' + JSON.stringify(keymapItem));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -801,4 +826,4 @@ class AppGui {
|
|||||||
AppGui.INPUT_MODE_NORMAL = 1;
|
AppGui.INPUT_MODE_NORMAL = 1;
|
||||||
AppGui.INPUT_MODE_META = 2;
|
AppGui.INPUT_MODE_META = 2;
|
||||||
|
|
||||||
module.exports = AppGui;
|
module.exports = AppGui;
|
@@ -5,13 +5,12 @@ const { JoplinDatabase } = require('lib/joplin-database.js');
|
|||||||
const { Database } = require('lib/database.js');
|
const { Database } = require('lib/database.js');
|
||||||
const { FoldersScreenUtils } = require('lib/folders-screen-utils.js');
|
const { FoldersScreenUtils } = require('lib/folders-screen-utils.js');
|
||||||
const { DatabaseDriverNode } = require('lib/database-driver-node.js');
|
const { DatabaseDriverNode } = require('lib/database-driver-node.js');
|
||||||
const ResourceService = require('lib/services/ResourceService');
|
const { BaseModel } = require('lib/base-model.js');
|
||||||
const BaseModel = require('lib/BaseModel.js');
|
const { Folder } = require('lib/models/folder.js');
|
||||||
const Folder = require('lib/models/Folder.js');
|
const { BaseItem } = require('lib/models/base-item.js');
|
||||||
const BaseItem = require('lib/models/BaseItem.js');
|
const { Note } = require('lib/models/note.js');
|
||||||
const Note = require('lib/models/Note.js');
|
const { Tag } = require('lib/models/tag.js');
|
||||||
const Tag = require('lib/models/Tag.js');
|
const { Setting } = require('lib/models/setting.js');
|
||||||
const Setting = require('lib/models/Setting.js');
|
|
||||||
const { Logger } = require('lib/logger.js');
|
const { Logger } = require('lib/logger.js');
|
||||||
const { sprintf } = require('sprintf-js');
|
const { sprintf } = require('sprintf-js');
|
||||||
const { reg } = require('lib/registry.js');
|
const { reg } = require('lib/registry.js');
|
||||||
@@ -22,7 +21,6 @@ const os = require('os');
|
|||||||
const fs = require('fs-extra');
|
const fs = require('fs-extra');
|
||||||
const { cliUtils } = require('./cli-utils.js');
|
const { cliUtils } = require('./cli-utils.js');
|
||||||
const EventEmitter = require('events');
|
const EventEmitter = require('events');
|
||||||
const Cache = require('lib/Cache');
|
|
||||||
|
|
||||||
class Application extends BaseApplication {
|
class Application extends BaseApplication {
|
||||||
|
|
||||||
@@ -36,7 +34,6 @@ class Application extends BaseApplication {
|
|||||||
this.allCommandsLoaded_ = false;
|
this.allCommandsLoaded_ = false;
|
||||||
this.showStackTraces_ = false;
|
this.showStackTraces_ = false;
|
||||||
this.gui_ = null;
|
this.gui_ = null;
|
||||||
this.cache_ = new Cache();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
gui() {
|
gui() {
|
||||||
@@ -147,15 +144,13 @@ class Application extends BaseApplication {
|
|||||||
message += ' (' + options.answers.join('/') + ')';
|
message += ' (' + options.answers.join('/') + ')';
|
||||||
}
|
}
|
||||||
|
|
||||||
let answer = await this.gui().prompt('', message + ' ', options);
|
let answer = await this.gui().prompt('', message + ' ');
|
||||||
|
|
||||||
if (options.type === 'boolean') {
|
if (options.type === 'boolean') {
|
||||||
if (answer === null) return false; // Pressed ESCAPE
|
if (answer === null) return false; // Pressed ESCAPE
|
||||||
if (!answer) answer = options.answers[0];
|
if (!answer) answer = options.answers[0];
|
||||||
let positiveIndex = options.booleanAnswerDefault == 'y' ? 0 : 1;
|
let positiveIndex = options.booleanAnswerDefault == 'y' ? 0 : 1;
|
||||||
return answer.toLowerCase() === options.answers[positiveIndex].toLowerCase();
|
return answer.toLowerCase() === options.answers[positiveIndex].toLowerCase();
|
||||||
} else {
|
|
||||||
return answer;
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -182,33 +177,22 @@ class Application extends BaseApplication {
|
|||||||
await doExit();
|
await doExit();
|
||||||
}
|
}
|
||||||
|
|
||||||
commands(uiType = null) {
|
commands() {
|
||||||
if (!this.allCommandsLoaded_) {
|
if (this.allCommandsLoaded_) return this.commands_;
|
||||||
fs.readdirSync(__dirname).forEach((path) => {
|
|
||||||
if (path.indexOf('command-') !== 0) return;
|
|
||||||
const ext = fileExtension(path)
|
|
||||||
if (ext != 'js') return;
|
|
||||||
|
|
||||||
let CommandClass = require('./' + path);
|
fs.readdirSync(__dirname).forEach((path) => {
|
||||||
let cmd = new CommandClass();
|
if (path.indexOf('command-') !== 0) return;
|
||||||
if (!cmd.enabled()) return;
|
const ext = fileExtension(path)
|
||||||
cmd = this.setupCommand(cmd);
|
if (ext != 'js') return;
|
||||||
this.commands_[cmd.name()] = cmd;
|
|
||||||
});
|
|
||||||
|
|
||||||
this.allCommandsLoaded_ = true;
|
let CommandClass = require('./' + path);
|
||||||
}
|
let cmd = new CommandClass();
|
||||||
|
if (!cmd.enabled()) return;
|
||||||
|
cmd = this.setupCommand(cmd);
|
||||||
|
this.commands_[cmd.name()] = cmd;
|
||||||
|
});
|
||||||
|
|
||||||
if (uiType !== null) {
|
this.allCommandsLoaded_ = true;
|
||||||
let temp = [];
|
|
||||||
for (let n in this.commands_) {
|
|
||||||
if (!this.commands_.hasOwnProperty(n)) continue;
|
|
||||||
const c = this.commands_[n];
|
|
||||||
if (!c.supportsUi(uiType)) continue;
|
|
||||||
temp[n] = c;
|
|
||||||
}
|
|
||||||
return temp;
|
|
||||||
}
|
|
||||||
|
|
||||||
return this.commands_;
|
return this.commands_;
|
||||||
}
|
}
|
||||||
@@ -226,8 +210,12 @@ class Application extends BaseApplication {
|
|||||||
async commandMetadata() {
|
async commandMetadata() {
|
||||||
if (this.commandMetadata_) return this.commandMetadata_;
|
if (this.commandMetadata_) return this.commandMetadata_;
|
||||||
|
|
||||||
let output = await this.cache_.getItem('metadata');
|
const osTmpdir = require('os-tmpdir');
|
||||||
if (output) {
|
const storage = require('node-persist');
|
||||||
|
await storage.init({ dir: osTmpdir() + '/commandMetadata', ttl: 1000 * 60 * 60 * 24 });
|
||||||
|
|
||||||
|
let output = await storage.getItem('metadata');
|
||||||
|
if (Setting.value('env') != 'dev' && output) {
|
||||||
this.commandMetadata_ = output;
|
this.commandMetadata_ = output;
|
||||||
return Object.assign({}, this.commandMetadata_);
|
return Object.assign({}, this.commandMetadata_);
|
||||||
}
|
}
|
||||||
@@ -241,7 +229,7 @@ class Application extends BaseApplication {
|
|||||||
output[n] = cmd.metadata();
|
output[n] = cmd.metadata();
|
||||||
}
|
}
|
||||||
|
|
||||||
await this.cache_.setItem('metadata', output, 1000 * 60 * 60 * 24);
|
await storage.setItem('metadata', output);
|
||||||
|
|
||||||
this.commandMetadata_ = output;
|
this.commandMetadata_ = output;
|
||||||
return Object.assign({}, this.commandMetadata_);
|
return Object.assign({}, this.commandMetadata_);
|
||||||
@@ -258,13 +246,9 @@ class Application extends BaseApplication {
|
|||||||
try {
|
try {
|
||||||
CommandClass = require(__dirname + '/command-' + name + '.js');
|
CommandClass = require(__dirname + '/command-' + name + '.js');
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if (error.message && error.message.indexOf('Cannot find module') >= 0) {
|
let e = new Error('No such command: ' + name);
|
||||||
let e = new Error(_('No such command: %s', name));
|
e.type = 'notFound';
|
||||||
e.type = 'notFound';
|
throw e;
|
||||||
throw e;
|
|
||||||
} else {
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let cmd = new CommandClass();
|
let cmd = new CommandClass();
|
||||||
@@ -276,7 +260,7 @@ class Application extends BaseApplication {
|
|||||||
dummyGui() {
|
dummyGui() {
|
||||||
return {
|
return {
|
||||||
isDummy: () => { return true; },
|
isDummy: () => { return true; },
|
||||||
prompt: (initialText = '', promptString = '', options = null) => { return cliUtils.prompt(initialText, promptString, options); },
|
prompt: (initialText = '', promptString = '') => { return cliUtils.prompt(initialText, promptString); },
|
||||||
showConsole: () => {},
|
showConsole: () => {},
|
||||||
maximizeConsole: () => {},
|
maximizeConsole: () => {},
|
||||||
stdout: (text) => { console.info(text); },
|
stdout: (text) => { console.info(text); },
|
||||||
@@ -284,16 +268,13 @@ class Application extends BaseApplication {
|
|||||||
exit: () => {},
|
exit: () => {},
|
||||||
showModalOverlay: (text) => {},
|
showModalOverlay: (text) => {},
|
||||||
hideModalOverlay: () => {},
|
hideModalOverlay: () => {},
|
||||||
stdoutMaxWidth: () => { return 100; },
|
stdoutMaxWidth: () => { return 78; }
|
||||||
forceRender: () => {},
|
|
||||||
termSaveState: () => {},
|
|
||||||
termRestoreState: (state) => {},
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
async execCommand(argv) {
|
async execCommand(argv) {
|
||||||
if (!argv.length) return this.execCommand(['help']);
|
if (!argv.length) return this.execCommand(['help']);
|
||||||
// reg.logger().debug('execCommand()', argv);
|
reg.logger().info('execCommand()', argv);
|
||||||
const commandName = argv[0];
|
const commandName = argv[0];
|
||||||
this.activeCommand_ = this.findCommandByName(commandName);
|
this.activeCommand_ = this.findCommandByName(commandName);
|
||||||
|
|
||||||
@@ -313,63 +294,6 @@ class Application extends BaseApplication {
|
|||||||
return this.activeCommand_;
|
return this.activeCommand_;
|
||||||
}
|
}
|
||||||
|
|
||||||
async loadKeymaps() {
|
|
||||||
const defaultKeyMap = [
|
|
||||||
{ "keys": [":"], "type": "function", "command": "enter_command_line_mode" },
|
|
||||||
{ "keys": ["TAB"], "type": "function", "command": "focus_next" },
|
|
||||||
{ "keys": ["SHIFT_TAB"], "type": "function", "command": "focus_previous" },
|
|
||||||
{ "keys": ["UP"], "type": "function", "command": "move_up" },
|
|
||||||
{ "keys": ["DOWN"], "type": "function", "command": "move_down" },
|
|
||||||
{ "keys": ["PAGE_UP"], "type": "function", "command": "page_up" },
|
|
||||||
{ "keys": ["PAGE_DOWN"], "type": "function", "command": "page_down" },
|
|
||||||
{ "keys": ["ENTER"], "type": "function", "command": "activate" },
|
|
||||||
{ "keys": ["DELETE", "BACKSPACE"], "type": "function", "command": "delete" },
|
|
||||||
{ "keys": [" "], "command": "todo toggle $n" },
|
|
||||||
{ "keys": ["tc"], "type": "function", "command": "toggle_console" },
|
|
||||||
{ "keys": ["tm"], "type": "function", "command": "toggle_metadata" },
|
|
||||||
{ "keys": ["/"], "type": "prompt", "command": "search \"\"", "cursorPosition": -2 },
|
|
||||||
{ "keys": ["mn"], "type": "prompt", "command": "mknote \"\"", "cursorPosition": -2 },
|
|
||||||
{ "keys": ["mt"], "type": "prompt", "command": "mktodo \"\"", "cursorPosition": -2 },
|
|
||||||
{ "keys": ["mb"], "type": "prompt", "command": "mkbook \"\"", "cursorPosition": -2 },
|
|
||||||
{ "keys": ["yn"], "type": "prompt", "command": "cp $n \"\"", "cursorPosition": -2 },
|
|
||||||
{ "keys": ["dn"], "type": "prompt", "command": "mv $n \"\"", "cursorPosition": -2 }
|
|
||||||
];
|
|
||||||
|
|
||||||
// Filter the keymap item by command so that items in keymap.json can override
|
|
||||||
// the default ones.
|
|
||||||
const itemsByCommand = {};
|
|
||||||
|
|
||||||
for (let i = 0; i < defaultKeyMap.length; i++) {
|
|
||||||
itemsByCommand[defaultKeyMap[i].command] = defaultKeyMap[i]
|
|
||||||
}
|
|
||||||
|
|
||||||
const filePath = Setting.value('profileDir') + '/keymap.json';
|
|
||||||
if (await fs.pathExists(filePath)) {
|
|
||||||
try {
|
|
||||||
let configString = await fs.readFile(filePath, 'utf-8');
|
|
||||||
configString = configString.replace(/^\s*\/\/.*/, ''); // Strip off comments
|
|
||||||
const keymap = JSON.parse(configString);
|
|
||||||
for (let keymapIndex = 0; keymapIndex < keymap.length; keymapIndex++) {
|
|
||||||
const item = keymap[keymapIndex];
|
|
||||||
itemsByCommand[item.command] = item;
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
let msg = error.message ? error.message : '';
|
|
||||||
msg = 'Could not load keymap ' + filePath + '\n' + msg;
|
|
||||||
error.message = msg;
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const output = [];
|
|
||||||
for (let n in itemsByCommand) {
|
|
||||||
if (!itemsByCommand.hasOwnProperty(n)) continue;
|
|
||||||
output.push(itemsByCommand[n]);
|
|
||||||
}
|
|
||||||
|
|
||||||
return output;
|
|
||||||
}
|
|
||||||
|
|
||||||
async start(argv) {
|
async start(argv) {
|
||||||
argv = await super.start(argv);
|
argv = await super.start(argv);
|
||||||
|
|
||||||
@@ -382,25 +306,20 @@ class Application extends BaseApplication {
|
|||||||
if (argv.length) {
|
if (argv.length) {
|
||||||
this.gui_ = this.dummyGui();
|
this.gui_ = this.dummyGui();
|
||||||
|
|
||||||
this.currentFolder_ = await Folder.load(Setting.value('activeFolderId'));
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await this.execCommand(argv);
|
await this.execCommand(argv);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if (this.showStackTraces_) {
|
if (this.showStackTraces_) {
|
||||||
console.error(error);
|
console.info(error);
|
||||||
} else {
|
} else {
|
||||||
console.info(error.message);
|
console.info(error.message);
|
||||||
}
|
}
|
||||||
process.exit(1);
|
|
||||||
}
|
}
|
||||||
} else { // Otherwise open the GUI
|
} else { // Otherwise open the GUI
|
||||||
this.initRedux();
|
this.initRedux();
|
||||||
|
|
||||||
const keymap = await this.loadKeymaps();
|
|
||||||
|
|
||||||
const AppGui = require('./app-gui.js');
|
const AppGui = require('./app-gui.js');
|
||||||
this.gui_ = new AppGui(this, this.store(), keymap);
|
this.gui_ = new AppGui(this, this.store());
|
||||||
this.gui_.setLogger(this.logger_);
|
this.gui_.setLogger(this.logger_);
|
||||||
await this.gui_.start();
|
await this.gui_.start();
|
||||||
|
|
||||||
@@ -413,11 +332,9 @@ class Application extends BaseApplication {
|
|||||||
|
|
||||||
const tags = await Tag.allWithNotes();
|
const tags = await Tag.allWithNotes();
|
||||||
|
|
||||||
ResourceService.runInBackground();
|
|
||||||
|
|
||||||
this.dispatch({
|
this.dispatch({
|
||||||
type: 'TAG_UPDATE_ALL',
|
type: 'TAG_UPDATE_ALL',
|
||||||
items: tags,
|
tags: tags,
|
||||||
});
|
});
|
||||||
|
|
||||||
this.store().dispatch({
|
this.store().dispatch({
|
||||||
|
@@ -1,199 +0,0 @@
|
|||||||
var { app } = require('./app.js');
|
|
||||||
var Note = require('lib/models/Note.js');
|
|
||||||
var Folder = require('lib/models/Folder.js');
|
|
||||||
var Tag = require('lib/models/Tag.js');
|
|
||||||
var { cliUtils } = require('./cli-utils.js');
|
|
||||||
var yargParser = require('yargs-parser');
|
|
||||||
var fs = require('fs-extra');
|
|
||||||
|
|
||||||
async function handleAutocompletionPromise(line) {
|
|
||||||
// Auto-complete the command name
|
|
||||||
const names = await app().commandNames();
|
|
||||||
let words = getArguments(line);
|
|
||||||
//If there is only one word and it is not already a command name then you
|
|
||||||
//should look for commmands it could be
|
|
||||||
if (words.length == 1) {
|
|
||||||
if (names.indexOf(words[0]) === -1) {
|
|
||||||
let x = names.filter((n) => n.indexOf(words[0]) === 0);
|
|
||||||
if (x.length === 1) {
|
|
||||||
return x[0] + ' ';
|
|
||||||
}
|
|
||||||
return x.length > 0 ? x.map((a) => a + ' ') : line;
|
|
||||||
} else {
|
|
||||||
return line;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
//There is more than one word and it is a command
|
|
||||||
const metadata = (await app().commandMetadata())[words[0]];
|
|
||||||
//If for some reason this command does not have any associated metadata
|
|
||||||
//just don't autocomplete. However, this should not happen.
|
|
||||||
if (metadata === undefined) {
|
|
||||||
return line;
|
|
||||||
}
|
|
||||||
//complete an option
|
|
||||||
let next = words.length > 1 ? words[words.length - 1] : '';
|
|
||||||
let l = [];
|
|
||||||
if (next[0] === '-') {
|
|
||||||
for (let i = 0; i<metadata.options.length; i++) {
|
|
||||||
const options = metadata.options[i][0].split(' ');
|
|
||||||
//if there are multiple options then they will be seperated by comma and
|
|
||||||
//space. The comma should be removed
|
|
||||||
if (options[0][options[0].length - 1] === ',') {
|
|
||||||
options[0] = options[0].slice(0, -1);
|
|
||||||
}
|
|
||||||
if (words.includes(options[0]) || words.includes(options[1])) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
//First two elements are the flag and the third is the description
|
|
||||||
//Only autocomplete long
|
|
||||||
if (options.length > 1 && options[1].indexOf(next) === 0) {
|
|
||||||
l.push(options[1]);
|
|
||||||
} else if (options[0].indexOf(next) === 0) {
|
|
||||||
l.push(options[0]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (l.length === 0) {
|
|
||||||
return line;
|
|
||||||
}
|
|
||||||
let ret = l.map(a=>toCommandLine(a));
|
|
||||||
ret.prefix = toCommandLine(words.slice(0, -1)) + ' ';
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
//Complete an argument
|
|
||||||
//Determine the number of positional arguments by counting the number of
|
|
||||||
//words that don't start with a - less one for the command name
|
|
||||||
const positionalArgs = words.filter((a)=>a.indexOf('-') !== 0).length - 1;
|
|
||||||
|
|
||||||
let cmdUsage = yargParser(metadata.usage)['_'];
|
|
||||||
cmdUsage.splice(0, 1);
|
|
||||||
|
|
||||||
if (cmdUsage.length >= positionalArgs) {
|
|
||||||
|
|
||||||
let argName = cmdUsage[positionalArgs - 1];
|
|
||||||
argName = cliUtils.parseCommandArg(argName).name;
|
|
||||||
|
|
||||||
const currentFolder = app().currentFolder();
|
|
||||||
|
|
||||||
if (argName == 'note' || argName == 'note-pattern') {
|
|
||||||
const notes = currentFolder ? await Note.previews(currentFolder.id, { titlePattern: next + '*' }) : [];
|
|
||||||
l.push(...notes.map((n) => n.title));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (argName == 'notebook') {
|
|
||||||
const folders = await Folder.search({ titlePattern: next + '*' });
|
|
||||||
l.push(...folders.map((n) => n.title));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (argName == 'item') {
|
|
||||||
const notes = currentFolder ? await Note.previews(currentFolder.id, { titlePattern: next + '*' }) : [];
|
|
||||||
const folders = await Folder.search({ titlePattern: next + '*' });
|
|
||||||
l.push(...notes.map((n) => n.title), folders.map((n) => n.title));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (argName == 'tag') {
|
|
||||||
let tags = await Tag.search({ titlePattern: next + '*' });
|
|
||||||
l.push(...tags.map((n) => n.title));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (argName == 'file') {
|
|
||||||
let files = await fs.readdir('.');
|
|
||||||
l.push(...files);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (argName == 'tag-command') {
|
|
||||||
let c = filterList(['add', 'remove', 'list'], next);
|
|
||||||
l.push(...c);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (argName == 'todo-command') {
|
|
||||||
let c = filterList(['toggle', 'clear'], next);
|
|
||||||
l.push(...c);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (l.length === 1) {
|
|
||||||
return toCommandLine([...words.slice(0, -1), l[0]]);
|
|
||||||
} else if (l.length > 1) {
|
|
||||||
let ret = l.map(a=>toCommandLine(a));
|
|
||||||
ret.prefix = toCommandLine(words.slice(0, -1)) + ' ';
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
return line;
|
|
||||||
|
|
||||||
}
|
|
||||||
function handleAutocompletion(str, callback) {
|
|
||||||
handleAutocompletionPromise(str).then(function(res) {
|
|
||||||
callback(undefined, res);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
function toCommandLine(args) {
|
|
||||||
if (Array.isArray(args)) {
|
|
||||||
return args.map(function(a) {
|
|
||||||
if(a.indexOf('"') !== -1 || a.indexOf(' ') !== -1) {
|
|
||||||
return "'" + a + "'";
|
|
||||||
} else if (a.indexOf("'") !== -1) {
|
|
||||||
return '"' + a + '"';
|
|
||||||
} else {
|
|
||||||
return a;
|
|
||||||
}
|
|
||||||
}).join(' ');
|
|
||||||
} else {
|
|
||||||
if(args.indexOf('"') !== -1 || args.indexOf(' ') !== -1) {
|
|
||||||
return "'" + args + "' ";
|
|
||||||
} else if (args.indexOf("'") !== -1) {
|
|
||||||
return '"' + args + '" ';
|
|
||||||
} else {
|
|
||||||
return args + ' ';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
function getArguments(line) {
|
|
||||||
let inSingleQuotes = false;
|
|
||||||
let inDoubleQuotes = false;
|
|
||||||
let currentWord = '';
|
|
||||||
let parsed = [];
|
|
||||||
for(let i = 0; i<line.length; i++) {
|
|
||||||
if(line[i] === '"') {
|
|
||||||
if(inDoubleQuotes) {
|
|
||||||
inDoubleQuotes = false;
|
|
||||||
//maybe push word to parsed?
|
|
||||||
//currentWord += '"';
|
|
||||||
} else {
|
|
||||||
inDoubleQuotes = true;
|
|
||||||
//currentWord += '"';
|
|
||||||
}
|
|
||||||
} else if(line[i] === "'") {
|
|
||||||
if(inSingleQuotes) {
|
|
||||||
inSingleQuotes = false;
|
|
||||||
//maybe push word to parsed?
|
|
||||||
//currentWord += "'";
|
|
||||||
} else {
|
|
||||||
inSingleQuotes = true;
|
|
||||||
//currentWord += "'";
|
|
||||||
}
|
|
||||||
} else if (/\s/.test(line[i]) &&
|
|
||||||
!(inDoubleQuotes || inSingleQuotes)) {
|
|
||||||
if (currentWord !== '') {
|
|
||||||
parsed.push(currentWord);
|
|
||||||
currentWord = '';
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
currentWord += line[i];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!(inSingleQuotes || inDoubleQuotes) && /\s/.test(line[line.length - 1])) {
|
|
||||||
parsed.push('');
|
|
||||||
} else {
|
|
||||||
parsed.push(currentWord);
|
|
||||||
}
|
|
||||||
return parsed;
|
|
||||||
}
|
|
||||||
function filterList(list, next) {
|
|
||||||
let output = [];
|
|
||||||
for (let i = 0; i < list.length; i++) {
|
|
||||||
if (list[i].indexOf(next) !== 0) continue;
|
|
||||||
output.push(list[i]);
|
|
||||||
}
|
|
||||||
return output;
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = { handleAutocompletion };
|
|
@@ -12,10 +12,6 @@ class BaseCommand {
|
|||||||
throw new Error('Usage not defined');
|
throw new Error('Usage not defined');
|
||||||
}
|
}
|
||||||
|
|
||||||
encryptionCheck(item) {
|
|
||||||
if (item && item.encryption_applied) throw new Error(_('Cannot change encrypted item'));
|
|
||||||
}
|
|
||||||
|
|
||||||
description() {
|
description() {
|
||||||
throw new Error('Description not defined');
|
throw new Error('Description not defined');
|
||||||
}
|
}
|
||||||
@@ -32,6 +28,10 @@ class BaseCommand {
|
|||||||
return this.compatibleUis().indexOf(ui) >= 0;
|
return this.compatibleUis().indexOf(ui) >= 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
aliases() {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
options() {
|
options() {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
@@ -102,7 +102,7 @@ function getFooter() {
|
|||||||
|
|
||||||
output.push('WEBSITE');
|
output.push('WEBSITE');
|
||||||
output.push('');
|
output.push('');
|
||||||
output.push(INDENT + 'https://joplin.cozic.net');
|
output.push(INDENT + 'http://joplin.cozic.net');
|
||||||
|
|
||||||
output.push('');
|
output.push('');
|
||||||
|
|
||||||
|
@@ -5,10 +5,10 @@ const { Logger } = require('lib/logger.js');
|
|||||||
const { dirname } = require('lib/path-utils.js');
|
const { dirname } = require('lib/path-utils.js');
|
||||||
const { DatabaseDriverNode } = require('lib/database-driver-node.js');
|
const { DatabaseDriverNode } = require('lib/database-driver-node.js');
|
||||||
const { JoplinDatabase } = require('lib/joplin-database.js');
|
const { JoplinDatabase } = require('lib/joplin-database.js');
|
||||||
const BaseModel = require('lib/BaseModel.js');
|
const { BaseModel } = require('lib/base-model.js');
|
||||||
const Folder = require('lib/models/Folder.js');
|
const { Folder } = require('lib/models/folder.js');
|
||||||
const Note = require('lib/models/Note.js');
|
const { Note } = require('lib/models/note.js');
|
||||||
const Setting = require('lib/models/Setting.js');
|
const { Setting } = require('lib/models/setting.js');
|
||||||
const { sprintf } = require('sprintf-js');
|
const { sprintf } = require('sprintf-js');
|
||||||
const exec = require('child_process').exec
|
const exec = require('child_process').exec
|
||||||
|
|
||||||
|
@@ -178,39 +178,38 @@ cliUtils.promptConfirm = function(message, answers = null) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Note: initialText is there to have the same signature as statusBar.prompt() so that
|
cliUtils.promptInput = function(message) {
|
||||||
// it can be a drop-in replacement, however initialText is not used (and cannot be
|
|
||||||
// with readline.question?).
|
|
||||||
cliUtils.prompt = function(initialText = '', promptString = ':', options = null) {
|
|
||||||
if (!options) options = {};
|
|
||||||
|
|
||||||
const readline = require('readline');
|
const readline = require('readline');
|
||||||
const Writable = require('stream').Writable;
|
|
||||||
|
|
||||||
const mutableStdout = new Writable({
|
|
||||||
write: function(chunk, encoding, callback) {
|
|
||||||
if (!this.muted)
|
|
||||||
process.stdout.write(chunk, encoding);
|
|
||||||
callback();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
const rl = readline.createInterface({
|
const rl = readline.createInterface({
|
||||||
input: process.stdin,
|
input: process.stdin,
|
||||||
output: mutableStdout,
|
output: process.stdout
|
||||||
terminal: true,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
mutableStdout.muted = false;
|
rl.question(message + ' ', (answer) => {
|
||||||
|
|
||||||
rl.question(promptString, (answer) => {
|
|
||||||
rl.close();
|
rl.close();
|
||||||
if (!!options.secure) this.stdout_('');
|
|
||||||
resolve(answer);
|
resolve(answer);
|
||||||
});
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
mutableStdout.muted = !!options.secure;
|
// Note: initialText is there to have the same signature as statusBar.prompt() so that
|
||||||
|
// it can be a drop-in replacement, however initialText is not used (and cannot be
|
||||||
|
// with readline.question?).
|
||||||
|
cliUtils.prompt = function(initialText = '', promptString = ':') {
|
||||||
|
const readline = require('readline');
|
||||||
|
|
||||||
|
const rl = readline.createInterface({
|
||||||
|
input: process.stdin,
|
||||||
|
output: process.stdout
|
||||||
|
});
|
||||||
|
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
rl.question(promptString, (answer) => {
|
||||||
|
rl.close();
|
||||||
|
resolve(answer);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -1,7 +1,7 @@
|
|||||||
const { BaseCommand } = require('./base-command.js');
|
const { BaseCommand } = require('./base-command.js');
|
||||||
const { app } = require('./app.js');
|
const { app } = require('./app.js');
|
||||||
const { _ } = require('lib/locale.js');
|
const { _ } = require('lib/locale.js');
|
||||||
const BaseModel = require('lib/BaseModel.js');
|
const { BaseModel } = require('lib/base-model.js');
|
||||||
const { shim } = require('lib/shim.js');
|
const { shim } = require('lib/shim.js');
|
||||||
const fs = require('fs-extra');
|
const fs = require('fs-extra');
|
||||||
|
|
||||||
@@ -19,7 +19,6 @@ class Command extends BaseCommand {
|
|||||||
let title = args['note'];
|
let title = args['note'];
|
||||||
|
|
||||||
let note = await app().loadItem(BaseModel.TYPE_NOTE, title, { parent: app().currentFolder() });
|
let note = await app().loadItem(BaseModel.TYPE_NOTE, title, { parent: app().currentFolder() });
|
||||||
this.encryptionCheck(note);
|
|
||||||
if (!note) throw new Error(_('Cannot find "%s".', title));
|
if (!note) throw new Error(_('Cannot find "%s".', title));
|
||||||
|
|
||||||
const localFilePath = args['file'];
|
const localFilePath = args['file'];
|
||||||
|
@@ -1,9 +1,9 @@
|
|||||||
const { BaseCommand } = require('./base-command.js');
|
const { BaseCommand } = require('./base-command.js');
|
||||||
const { app } = require('./app.js');
|
const { app } = require('./app.js');
|
||||||
const { _ } = require('lib/locale.js');
|
const { _ } = require('lib/locale.js');
|
||||||
const BaseModel = require('lib/BaseModel.js');
|
const { BaseModel } = require('lib/base-model.js');
|
||||||
const Folder = require('lib/models/Folder.js');
|
const { Folder } = require('lib/models/folder.js');
|
||||||
const Note = require('lib/models/Note.js');
|
const { Note } = require('lib/models/note.js');
|
||||||
|
|
||||||
class Command extends BaseCommand {
|
class Command extends BaseCommand {
|
||||||
|
|
||||||
@@ -21,6 +21,10 @@ class Command extends BaseCommand {
|
|||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enabled() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
async action(args) {
|
async action(args) {
|
||||||
let title = args['note'];
|
let title = args['note'];
|
||||||
|
|
||||||
@@ -29,9 +33,6 @@ class Command extends BaseCommand {
|
|||||||
|
|
||||||
const content = args.options.verbose ? await Note.serialize(item) : await Note.serializeForEdit(item);
|
const content = args.options.verbose ? await Note.serialize(item) : await Note.serializeForEdit(item);
|
||||||
this.stdout(content);
|
this.stdout(content);
|
||||||
|
|
||||||
app().gui().showConsole();
|
|
||||||
app().gui().maximizeConsole();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -1,7 +1,7 @@
|
|||||||
const { BaseCommand } = require('./base-command.js');
|
const { BaseCommand } = require('./base-command.js');
|
||||||
const { _, setLocale } = require('lib/locale.js');
|
const { _, setLocale } = require('lib/locale.js');
|
||||||
const { app } = require('./app.js');
|
const { app } = require('./app.js');
|
||||||
const Setting = require('lib/models/Setting.js');
|
const { Setting } = require('lib/models/setting.js');
|
||||||
|
|
||||||
class Command extends BaseCommand {
|
class Command extends BaseCommand {
|
||||||
|
|
||||||
@@ -23,11 +23,7 @@ class Command extends BaseCommand {
|
|||||||
const verbose = args.options.verbose;
|
const verbose = args.options.verbose;
|
||||||
|
|
||||||
const renderKeyValue = (name) => {
|
const renderKeyValue = (name) => {
|
||||||
const md = Setting.settingMetadata(name);
|
const value = Setting.value(name);
|
||||||
let value = Setting.value(name);
|
|
||||||
if (typeof value === 'object' || Array.isArray(value)) value = JSON.stringify(value);
|
|
||||||
if (md.secure) value = '********';
|
|
||||||
|
|
||||||
if (Setting.isEnum(name)) {
|
if (Setting.isEnum(name)) {
|
||||||
return _('%s = %s (%s)', name, value, Setting.enumOptionsDoc(name));
|
return _('%s = %s (%s)', name, value, Setting.enumOptionsDoc(name));
|
||||||
} else {
|
} else {
|
||||||
|
@@ -1,9 +1,9 @@
|
|||||||
const { BaseCommand } = require('./base-command.js');
|
const { BaseCommand } = require('./base-command.js');
|
||||||
const { app } = require('./app.js');
|
const { app } = require('./app.js');
|
||||||
const { _ } = require('lib/locale.js');
|
const { _ } = require('lib/locale.js');
|
||||||
const BaseModel = require('lib/BaseModel.js');
|
const { BaseModel } = require('lib/base-model.js');
|
||||||
const Folder = require('lib/models/Folder.js');
|
const { Folder } = require('lib/models/folder.js');
|
||||||
const Note = require('lib/models/Note.js');
|
const { Note } = require('lib/models/note.js');
|
||||||
|
|
||||||
class Command extends BaseCommand {
|
class Command extends BaseCommand {
|
||||||
|
|
||||||
|
@@ -1,9 +1,9 @@
|
|||||||
const { BaseCommand } = require('./base-command.js');
|
const { BaseCommand } = require('./base-command.js');
|
||||||
const { app } = require('./app.js');
|
const { app } = require('./app.js');
|
||||||
const { _ } = require('lib/locale.js');
|
const { _ } = require('lib/locale.js');
|
||||||
const BaseModel = require('lib/BaseModel.js');
|
const { BaseModel } = require('lib/base-model.js');
|
||||||
const Folder = require('lib/models/Folder.js');
|
const { Folder } = require('lib/models/folder.js');
|
||||||
const Note = require('lib/models/Note.js');
|
const { Note } = require('lib/models/note.js');
|
||||||
const { time } = require('lib/time-utils.js');
|
const { time } = require('lib/time-utils.js');
|
||||||
|
|
||||||
class Command extends BaseCommand {
|
class Command extends BaseCommand {
|
||||||
@@ -16,9 +16,8 @@ class Command extends BaseCommand {
|
|||||||
return _('Marks a to-do as done.');
|
return _('Marks a to-do as done.');
|
||||||
}
|
}
|
||||||
|
|
||||||
static async handleAction(commandInstance, args, isCompleted) {
|
static async handleAction(args, isCompleted) {
|
||||||
const note = await app().loadItem(BaseModel.TYPE_NOTE, args.note);
|
const note = await app().loadItem(BaseModel.TYPE_NOTE, args.note);
|
||||||
commandInstance.encryptionCheck(note);
|
|
||||||
if (!note) throw new Error(_('Cannot find "%s".', args.note));
|
if (!note) throw new Error(_('Cannot find "%s".', args.note));
|
||||||
if (!note.is_todo) throw new Error(_('Note is not a to-do: "%s"', args.note));
|
if (!note.is_todo) throw new Error(_('Note is not a to-do: "%s"', args.note));
|
||||||
|
|
||||||
@@ -33,7 +32,7 @@ class Command extends BaseCommand {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async action(args) {
|
async action(args) {
|
||||||
await Command.handleAction(this, args, true);
|
await Command.handleAction(args, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -1,9 +1,9 @@
|
|||||||
const { BaseCommand } = require('./base-command.js');
|
const { BaseCommand } = require('./base-command.js');
|
||||||
const { app } = require('./app.js');
|
const { app } = require('./app.js');
|
||||||
const { _ } = require('lib/locale.js');
|
const { _ } = require('lib/locale.js');
|
||||||
const Folder = require('lib/models/Folder.js');
|
const { Folder } = require('lib/models/folder.js');
|
||||||
const Note = require('lib/models/Note.js');
|
const { Note } = require('lib/models/note.js');
|
||||||
const Tag = require('lib/models/Tag.js');
|
const { Tag } = require('lib/models/tag.js');
|
||||||
|
|
||||||
class Command extends BaseCommand {
|
class Command extends BaseCommand {
|
||||||
|
|
||||||
|
@@ -1,184 +0,0 @@
|
|||||||
const { BaseCommand } = require('./base-command.js');
|
|
||||||
const { _ } = require('lib/locale.js');
|
|
||||||
const { cliUtils } = require('./cli-utils.js');
|
|
||||||
const EncryptionService = require('lib/services/EncryptionService');
|
|
||||||
const DecryptionWorker = require('lib/services/DecryptionWorker');
|
|
||||||
const MasterKey = require('lib/models/MasterKey');
|
|
||||||
const BaseItem = require('lib/models/BaseItem');
|
|
||||||
const Setting = require('lib/models/Setting.js');
|
|
||||||
|
|
||||||
class Command extends BaseCommand {
|
|
||||||
|
|
||||||
usage() {
|
|
||||||
return 'e2ee <command> [path]';
|
|
||||||
}
|
|
||||||
|
|
||||||
description() {
|
|
||||||
return _('Manages E2EE configuration. Commands are `enable`, `disable`, `decrypt`, `status` and `target-status`.');
|
|
||||||
}
|
|
||||||
|
|
||||||
options() {
|
|
||||||
return [
|
|
||||||
// This is here mostly for testing - shouldn't be used
|
|
||||||
['-p, --password <password>', 'Use this password as master password (For security reasons, it is not recommended to use this option).'],
|
|
||||||
['-v, --verbose', 'More verbose output for the `target-status` command'],
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
async action(args) {
|
|
||||||
// change-password
|
|
||||||
|
|
||||||
const options = args.options;
|
|
||||||
|
|
||||||
if (args.command === 'enable') {
|
|
||||||
const password = options.password ? options.password.toString() : await this.prompt(_('Enter master password:'), { type: 'string', secure: true });
|
|
||||||
if (!password) {
|
|
||||||
this.stdout(_('Operation cancelled'));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
await EncryptionService.instance().generateMasterKeyAndEnableEncryption(password);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (args.command === 'disable') {
|
|
||||||
await EncryptionService.instance().disableEncryption();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (args.command === 'decrypt') {
|
|
||||||
this.stdout(_('Starting decryption... Please wait as it may take several minutes depending on how much there is to decrypt.'));
|
|
||||||
|
|
||||||
while (true) {
|
|
||||||
try {
|
|
||||||
await DecryptionWorker.instance().start();
|
|
||||||
break;
|
|
||||||
} catch (error) {
|
|
||||||
if (error.code === 'masterKeyNotLoaded') {
|
|
||||||
const masterKeyId = error.masterKeyId;
|
|
||||||
const password = await this.prompt(_('Enter master password:'), { type: 'string', secure: true });
|
|
||||||
if (!password) {
|
|
||||||
this.stdout(_('Operation cancelled'));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
Setting.setObjectKey('encryption.passwordCache', masterKeyId, password);
|
|
||||||
await EncryptionService.instance().loadMasterKeysFromSettings();
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
this.stdout(_('Completed decryption.'));
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (args.command === 'status') {
|
|
||||||
this.stdout(_('Encryption is: %s', Setting.value('encryption.enabled') ? _('Enabled') : _('Disabled')));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (args.command === 'target-status') {
|
|
||||||
const fs = require('fs-extra');
|
|
||||||
const pathUtils = require('lib/path-utils.js');
|
|
||||||
const fsDriver = new (require('lib/fs-driver-node.js').FsDriverNode)();
|
|
||||||
|
|
||||||
const targetPath = args.path;
|
|
||||||
if (!targetPath) throw new Error('Please specify the sync target path.');
|
|
||||||
|
|
||||||
const dirPaths = function(targetPath) {
|
|
||||||
let paths = [];
|
|
||||||
fs.readdirSync(targetPath).forEach((path) => {
|
|
||||||
paths.push(path);
|
|
||||||
});
|
|
||||||
return paths;
|
|
||||||
}
|
|
||||||
|
|
||||||
let itemCount = 0;
|
|
||||||
let resourceCount = 0;
|
|
||||||
let encryptedItemCount = 0;
|
|
||||||
let encryptedResourceCount = 0;
|
|
||||||
let otherItemCount = 0;
|
|
||||||
|
|
||||||
let encryptedPaths = [];
|
|
||||||
let decryptedPaths = [];
|
|
||||||
|
|
||||||
let paths = dirPaths(targetPath);
|
|
||||||
|
|
||||||
for (let i = 0; i < paths.length; i++) {
|
|
||||||
const path = paths[i];
|
|
||||||
const fullPath = targetPath + '/' + path;
|
|
||||||
const stat = await fs.stat(fullPath);
|
|
||||||
|
|
||||||
// this.stdout(fullPath);
|
|
||||||
|
|
||||||
if (path === '.resource') {
|
|
||||||
let resourcePaths = dirPaths(fullPath);
|
|
||||||
for (let j = 0; j < resourcePaths.length; j++) {
|
|
||||||
const resourcePath = resourcePaths[j];
|
|
||||||
resourceCount++;
|
|
||||||
const fullResourcePath = fullPath + '/' + resourcePath;
|
|
||||||
const isEncrypted = await EncryptionService.instance().fileIsEncrypted(fullResourcePath);
|
|
||||||
if (isEncrypted) {
|
|
||||||
encryptedResourceCount++;
|
|
||||||
encryptedPaths.push(fullResourcePath);
|
|
||||||
} else {
|
|
||||||
decryptedPaths.push(fullResourcePath);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if (stat.isDirectory()) {
|
|
||||||
continue;
|
|
||||||
} else {
|
|
||||||
const content = await fs.readFile(fullPath, 'utf8');
|
|
||||||
const item = await BaseItem.unserialize(content);
|
|
||||||
const ItemClass = BaseItem.itemClass(item);
|
|
||||||
|
|
||||||
if (!ItemClass.encryptionSupported()) {
|
|
||||||
otherItemCount++;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
itemCount++;
|
|
||||||
|
|
||||||
const isEncrypted = await EncryptionService.instance().itemIsEncrypted(item);
|
|
||||||
|
|
||||||
if (isEncrypted) {
|
|
||||||
encryptedItemCount++;
|
|
||||||
encryptedPaths.push(fullPath);
|
|
||||||
} else {
|
|
||||||
decryptedPaths.push(fullPath);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
this.stdout('Encrypted items: ' + encryptedItemCount + '/' + itemCount);
|
|
||||||
this.stdout('Encrypted resources: ' + encryptedResourceCount + '/' + resourceCount);
|
|
||||||
this.stdout('Other items (never encrypted): ' + otherItemCount);
|
|
||||||
|
|
||||||
if (options.verbose) {
|
|
||||||
this.stdout('');
|
|
||||||
this.stdout('# Encrypted paths');
|
|
||||||
this.stdout('');
|
|
||||||
for (let i = 0; i < encryptedPaths.length; i++) {
|
|
||||||
const path = encryptedPaths[i];
|
|
||||||
this.stdout(path);
|
|
||||||
}
|
|
||||||
|
|
||||||
this.stdout('');
|
|
||||||
this.stdout('# Decrypted paths');
|
|
||||||
this.stdout('');
|
|
||||||
for (let i = 0; i < decryptedPaths.length; i++) {
|
|
||||||
const path = decryptedPaths[i];
|
|
||||||
this.stdout(path);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = Command;
|
|
@@ -3,10 +3,10 @@ const { BaseCommand } = require('./base-command.js');
|
|||||||
const { uuid } = require('lib/uuid.js');
|
const { uuid } = require('lib/uuid.js');
|
||||||
const { app } = require('./app.js');
|
const { app } = require('./app.js');
|
||||||
const { _ } = require('lib/locale.js');
|
const { _ } = require('lib/locale.js');
|
||||||
const Folder = require('lib/models/Folder.js');
|
const { Folder } = require('lib/models/folder.js');
|
||||||
const Note = require('lib/models/Note.js');
|
const { Note } = require('lib/models/note.js');
|
||||||
const Setting = require('lib/models/Setting.js');
|
const { Setting } = require('lib/models/setting.js');
|
||||||
const BaseModel = require('lib/BaseModel.js');
|
const { BaseModel } = require('lib/base-model.js');
|
||||||
const { cliUtils } = require('./cli-utils.js');
|
const { cliUtils } = require('./cli-utils.js');
|
||||||
const { time } = require('lib/time-utils.js');
|
const { time } = require('lib/time-utils.js');
|
||||||
|
|
||||||
@@ -44,8 +44,6 @@ class Command extends BaseCommand {
|
|||||||
if (!app().currentFolder()) throw new Error(_('No active notebook.'));
|
if (!app().currentFolder()) throw new Error(_('No active notebook.'));
|
||||||
let note = await app().loadItem(BaseModel.TYPE_NOTE, title);
|
let note = await app().loadItem(BaseModel.TYPE_NOTE, title);
|
||||||
|
|
||||||
this.encryptionCheck(note);
|
|
||||||
|
|
||||||
if (!note) {
|
if (!note) {
|
||||||
const ok = await this.prompt(_('Note does not exist: "%s". Create it?', title));
|
const ok = await this.prompt(_('Note does not exist: "%s". Create it?', title));
|
||||||
if (!ok) return;
|
if (!ok) return;
|
||||||
@@ -78,14 +76,12 @@ class Command extends BaseCommand {
|
|||||||
|
|
||||||
app().gui().showModalOverlay(_('Starting to edit note. Close the editor to get back to the prompt.'));
|
app().gui().showModalOverlay(_('Starting to edit note. Close the editor to get back to the prompt.'));
|
||||||
await app().gui().forceRender();
|
await app().gui().forceRender();
|
||||||
const termState = app().gui().termSaveState();
|
const termState = app().gui().term().saveState();
|
||||||
|
|
||||||
const spawnSync = require('child_process').spawnSync;
|
const spawnSync = require('child_process').spawnSync;
|
||||||
const result = spawnSync(editorPath, editorArgs, { stdio: 'inherit' });
|
spawnSync(editorPath, editorArgs, { stdio: 'inherit' });
|
||||||
|
|
||||||
if (result.error) this.stdout(_('Error opening note in editor: %s', result.error.message));
|
app().gui().term().restoreState(termState);
|
||||||
|
|
||||||
app().gui().termRestoreState(termState);
|
|
||||||
app().gui().hideModalOverlay();
|
app().gui().hideModalOverlay();
|
||||||
app().gui().forceRender();
|
app().gui().forceRender();
|
||||||
|
|
||||||
|
@@ -12,10 +12,6 @@ class Command extends BaseCommand {
|
|||||||
return _('Exits the application.');
|
return _('Exits the application.');
|
||||||
}
|
}
|
||||||
|
|
||||||
compatibleUis() {
|
|
||||||
return ['gui'];
|
|
||||||
}
|
|
||||||
|
|
||||||
async action(args) {
|
async action(args) {
|
||||||
await app().exit();
|
await app().exit();
|
||||||
}
|
}
|
||||||
|
@@ -1,7 +1,7 @@
|
|||||||
const { BaseCommand } = require('./base-command.js');
|
const { BaseCommand } = require('./base-command.js');
|
||||||
const { Database } = require('lib/database.js');
|
const { Database } = require('lib/database.js');
|
||||||
const { app } = require('./app.js');
|
const { app } = require('./app.js');
|
||||||
const Setting = require('lib/models/Setting.js');
|
const { Setting } = require('lib/models/setting.js');
|
||||||
const { _ } = require('lib/locale.js');
|
const { _ } = require('lib/locale.js');
|
||||||
const { ReportService } = require('lib/services/report.js');
|
const { ReportService } = require('lib/services/report.js');
|
||||||
const fs = require('fs-extra');
|
const fs = require('fs-extra');
|
||||||
|
@@ -1,7 +1,7 @@
|
|||||||
const { BaseCommand } = require('./base-command.js');
|
const { BaseCommand } = require('./base-command.js');
|
||||||
const InteropService = require('lib/services/InteropService.js');
|
const { Exporter } = require('lib/services/exporter.js');
|
||||||
const BaseModel = require('lib/BaseModel.js');
|
const { BaseModel } = require('lib/base-model.js');
|
||||||
const Note = require('lib/models/Note.js');
|
const { Note } = require('lib/models/note.js');
|
||||||
const { reg } = require('lib/registry.js');
|
const { reg } = require('lib/registry.js');
|
||||||
const { app } = require('./app.js');
|
const { app } = require('./app.js');
|
||||||
const { _ } = require('lib/locale.js');
|
const { _ } = require('lib/locale.js');
|
||||||
@@ -10,21 +10,15 @@ const fs = require('fs-extra');
|
|||||||
class Command extends BaseCommand {
|
class Command extends BaseCommand {
|
||||||
|
|
||||||
usage() {
|
usage() {
|
||||||
return 'export <path>';
|
return 'export <directory>';
|
||||||
}
|
}
|
||||||
|
|
||||||
description() {
|
description() {
|
||||||
return _('Exports Joplin data to the given path. By default, it will export the complete database including notebooks, notes, tags and resources.');
|
return _('Exports Joplin data to the given directory. By default, it will export the complete database including notebooks, notes, tags and resources.');
|
||||||
}
|
}
|
||||||
|
|
||||||
options() {
|
options() {
|
||||||
const service = new InteropService();
|
|
||||||
const formats = service.modules()
|
|
||||||
.filter(m => m.type === 'exporter')
|
|
||||||
.map(m => m.format + (m.description ? ' (' + m.description + ')' : ''));
|
|
||||||
|
|
||||||
return [
|
return [
|
||||||
['--format <format>', _('Destination format: %s', formats.join(', '))],
|
|
||||||
['--note <note>', _('Exports only the given note.')],
|
['--note <note>', _('Exports only the given note.')],
|
||||||
['--notebook <notebook>', _('Exports only the given notebook.')],
|
['--notebook <notebook>', _('Exports only the given notebook.')],
|
||||||
];
|
];
|
||||||
@@ -32,9 +26,13 @@ class Command extends BaseCommand {
|
|||||||
|
|
||||||
async action(args) {
|
async action(args) {
|
||||||
let exportOptions = {};
|
let exportOptions = {};
|
||||||
exportOptions.path = args.path;
|
exportOptions.destDir = args.directory;
|
||||||
|
exportOptions.writeFile = (filePath, data) => {
|
||||||
exportOptions.format = args.options.format ? args.options.format : 'jex';
|
return fs.writeFile(filePath, data);
|
||||||
|
};
|
||||||
|
exportOptions.copyFile = (source, dest) => {
|
||||||
|
return fs.copy(source, dest, { overwrite: true });
|
||||||
|
};
|
||||||
|
|
||||||
if (args.options.note) {
|
if (args.options.note) {
|
||||||
|
|
||||||
@@ -50,10 +48,10 @@ class Command extends BaseCommand {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const service = new InteropService();
|
const exporter = new Exporter();
|
||||||
const result = await service.export(exportOptions);
|
const result = await exporter.export(exportOptions);
|
||||||
|
|
||||||
result.warnings.map((w) => this.stdout(w));
|
reg.logger().info('Export result: ', result);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -1,9 +1,9 @@
|
|||||||
const { BaseCommand } = require('./base-command.js');
|
const { BaseCommand } = require('./base-command.js');
|
||||||
const { app } = require('./app.js');
|
const { app } = require('./app.js');
|
||||||
const { _ } = require('lib/locale.js');
|
const { _ } = require('lib/locale.js');
|
||||||
const BaseModel = require('lib/BaseModel.js');
|
const { BaseModel } = require('lib/base-model.js');
|
||||||
const Folder = require('lib/models/Folder.js');
|
const { Folder } = require('lib/models/folder.js');
|
||||||
const Note = require('lib/models/Note.js');
|
const { Note } = require('lib/models/note.js');
|
||||||
|
|
||||||
class Command extends BaseCommand {
|
class Command extends BaseCommand {
|
||||||
|
|
||||||
|
@@ -2,7 +2,7 @@ const { BaseCommand } = require('./base-command.js');
|
|||||||
const { app } = require('./app.js');
|
const { app } = require('./app.js');
|
||||||
const { renderCommandHelp } = require('./help-utils.js');
|
const { renderCommandHelp } = require('./help-utils.js');
|
||||||
const { Database } = require('lib/database.js');
|
const { Database } = require('lib/database.js');
|
||||||
const Setting = require('lib/models/Setting.js');
|
const { Setting } = require('lib/models/setting.js');
|
||||||
const { wrap } = require('lib/string-utils.js');
|
const { wrap } = require('lib/string-utils.js');
|
||||||
const { _ } = require('lib/locale.js');
|
const { _ } = require('lib/locale.js');
|
||||||
const { cliUtils } = require('./cli-utils.js');
|
const { cliUtils } = require('./cli-utils.js');
|
||||||
@@ -18,7 +18,7 @@ class Command extends BaseCommand {
|
|||||||
}
|
}
|
||||||
|
|
||||||
allCommands() {
|
allCommands() {
|
||||||
const commands = app().commands(app().uiType());
|
const commands = app().commands();
|
||||||
let output = [];
|
let output = [];
|
||||||
for (let n in commands) {
|
for (let n in commands) {
|
||||||
if (!commands.hasOwnProperty(n)) continue;
|
if (!commands.hasOwnProperty(n)) continue;
|
||||||
@@ -36,22 +36,21 @@ class Command extends BaseCommand {
|
|||||||
async action(args) {
|
async action(args) {
|
||||||
const stdoutWidth = app().commandStdoutMaxWidth();
|
const stdoutWidth = app().commandStdoutMaxWidth();
|
||||||
|
|
||||||
if (args.command === 'shortcuts' || args.command === 'keymap') {
|
if (args.command === 'shortcuts') {
|
||||||
this.stdout(_('For information on how to customise the shortcuts please visit %s', 'https://joplin.cozic.net/terminal/#shortcuts'));
|
|
||||||
this.stdout('');
|
|
||||||
|
|
||||||
if (app().gui().isDummy()) {
|
if (app().gui().isDummy()) {
|
||||||
throw new Error(_('Shortcuts are not available in CLI mode.'));
|
throw new Error(_('Shortcuts are not available in CLI mode.'));
|
||||||
}
|
}
|
||||||
|
|
||||||
const keymap = app().gui().keymap();
|
const shortcuts = app().gui().shortcuts();
|
||||||
|
|
||||||
let rows = [];
|
let rows = [];
|
||||||
|
|
||||||
for (let i = 0; i < keymap.length; i++) {
|
for (let n in shortcuts) {
|
||||||
const item = keymap[i];
|
if (!shortcuts.hasOwnProperty(n)) continue;
|
||||||
const keys = item.keys.map((k) => k === ' ' ? '(SPACE)' : k);
|
const shortcut = shortcuts[n];
|
||||||
rows.push([keys.join(', '), item.command]);
|
if (!shortcut.description) continue;
|
||||||
|
n = shortcut.friendlyName ? shortcut.friendlyName : n;
|
||||||
|
rows.push([n, shortcut.description()]);
|
||||||
}
|
}
|
||||||
|
|
||||||
cliUtils.printArray(this.stdout.bind(this), rows);
|
cliUtils.printArray(this.stdout.bind(this), rows);
|
||||||
@@ -66,7 +65,7 @@ class Command extends BaseCommand {
|
|||||||
} else {
|
} else {
|
||||||
const commandNames = this.allCommands().map((a) => a.name());
|
const commandNames = this.allCommands().map((a) => a.name());
|
||||||
|
|
||||||
this.stdout(_('Type `help [command]` for more information about a command; or type `help all` for the complete usage information.'));
|
this.stdout(_('Type `help [command]` for more information about a command.'));
|
||||||
this.stdout('');
|
this.stdout('');
|
||||||
this.stdout(_('The possible commands are:'));
|
this.stdout(_('The possible commands are:'));
|
||||||
this.stdout('');
|
this.stdout('');
|
||||||
@@ -79,7 +78,7 @@ class Command extends BaseCommand {
|
|||||||
this.stdout(_('To maximise/minimise the console, press "TC".'));
|
this.stdout(_('To maximise/minimise the console, press "TC".'));
|
||||||
this.stdout(_('To enter command line mode, press ":"'));
|
this.stdout(_('To enter command line mode, press ":"'));
|
||||||
this.stdout(_('To exit command line mode, press ESCAPE'));
|
this.stdout(_('To exit command line mode, press ESCAPE'));
|
||||||
this.stdout(_('For the list of keyboard shortcuts and config options, type `help keymap`'));
|
this.stdout(_('For the complete list of available keyboard shortcuts, type `help shortcuts`'));
|
||||||
}
|
}
|
||||||
|
|
||||||
app().gui().showConsole();
|
app().gui().showConsole();
|
||||||
|
68
CliClient/app/command-import-enex.js
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
const { BaseCommand } = require('./base-command.js');
|
||||||
|
const { app } = require('./app.js');
|
||||||
|
const { _ } = require('lib/locale.js');
|
||||||
|
const { Folder } = require('lib/models/folder.js');
|
||||||
|
const { importEnex } = require('lib/import-enex');
|
||||||
|
const { filename, basename } = require('lib/path-utils.js');
|
||||||
|
const { cliUtils } = require('./cli-utils.js');
|
||||||
|
|
||||||
|
class Command extends BaseCommand {
|
||||||
|
|
||||||
|
usage() {
|
||||||
|
return 'import-enex <file> [notebook]';
|
||||||
|
}
|
||||||
|
|
||||||
|
description() {
|
||||||
|
return _('Imports an Evernote notebook file (.enex file).');
|
||||||
|
}
|
||||||
|
|
||||||
|
options() {
|
||||||
|
return [
|
||||||
|
['-f, --force', _('Do not ask for confirmation.')],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
async action(args) {
|
||||||
|
let filePath = args.file;
|
||||||
|
let folder = null;
|
||||||
|
let folderTitle = args['notebook'];
|
||||||
|
let force = args.options.force === true;
|
||||||
|
|
||||||
|
if (!folderTitle) folderTitle = filename(filePath);
|
||||||
|
folder = await Folder.loadByField('title', folderTitle);
|
||||||
|
const msg = folder ? _('File "%s" will be imported into existing notebook "%s". Continue?', basename(filePath), folderTitle) : _('New notebook "%s" will be created and file "%s" will be imported into it. Continue?', folderTitle, basename(filePath));
|
||||||
|
const ok = force ? true : await this.prompt(msg);
|
||||||
|
if (!ok) return;
|
||||||
|
|
||||||
|
let lastProgress = '';
|
||||||
|
|
||||||
|
let options = {
|
||||||
|
onProgress: (progressState) => {
|
||||||
|
let line = [];
|
||||||
|
line.push(_('Found: %d.', progressState.loaded));
|
||||||
|
line.push(_('Created: %d.', progressState.created));
|
||||||
|
if (progressState.updated) line.push(_('Updated: %d.', progressState.updated));
|
||||||
|
if (progressState.skipped) line.push(_('Skipped: %d.', progressState.skipped));
|
||||||
|
if (progressState.resourcesCreated) line.push(_('Resources: %d.', progressState.resourcesCreated));
|
||||||
|
if (progressState.notesTagged) line.push(_('Tagged: %d.', progressState.notesTagged));
|
||||||
|
lastProgress = line.join(' ');
|
||||||
|
cliUtils.redraw(lastProgress);
|
||||||
|
},
|
||||||
|
onError: (error) => {
|
||||||
|
let s = error.trace ? error.trace : error.toString();
|
||||||
|
this.stdout(s);
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
folder = !folder ? await Folder.save({ title: folderTitle }) : folder;
|
||||||
|
|
||||||
|
app().gui().showConsole();
|
||||||
|
this.stdout(_('Importing notes...'));
|
||||||
|
await importEnex(folder.id, filePath, options);
|
||||||
|
cliUtils.redrawDone();
|
||||||
|
this.stdout(_('The notes have been imported: %s', lastProgress));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = Command;
|
@@ -1,75 +0,0 @@
|
|||||||
const { BaseCommand } = require('./base-command.js');
|
|
||||||
const InteropService = require('lib/services/InteropService.js');
|
|
||||||
const BaseModel = require('lib/BaseModel.js');
|
|
||||||
const Note = require('lib/models/Note.js');
|
|
||||||
const { filename, basename, fileExtension } = require('lib/path-utils.js');
|
|
||||||
const { importEnex } = require('lib/import-enex');
|
|
||||||
const { cliUtils } = require('./cli-utils.js');
|
|
||||||
const { reg } = require('lib/registry.js');
|
|
||||||
const { app } = require('./app.js');
|
|
||||||
const { _ } = require('lib/locale.js');
|
|
||||||
const fs = require('fs-extra');
|
|
||||||
|
|
||||||
class Command extends BaseCommand {
|
|
||||||
|
|
||||||
usage() {
|
|
||||||
return 'import <path> [notebook]';
|
|
||||||
}
|
|
||||||
|
|
||||||
description() {
|
|
||||||
return _('Imports data into Joplin.');
|
|
||||||
}
|
|
||||||
|
|
||||||
options() {
|
|
||||||
const service = new InteropService();
|
|
||||||
const formats = service.modules().filter(m => m.type === 'importer').map(m => m.format);
|
|
||||||
|
|
||||||
return [
|
|
||||||
['--format <format>', _('Source format: %s', (['auto'].concat(formats)).join(', '))],
|
|
||||||
['-f, --force', _('Do not ask for confirmation.')],
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
async action(args) {
|
|
||||||
let folder = await app().loadItem(BaseModel.TYPE_FOLDER, args.notebook);
|
|
||||||
|
|
||||||
if (args.notebook && !folder) throw new Error(_('Cannot find "%s".', args.notebook));
|
|
||||||
|
|
||||||
const importOptions = {};
|
|
||||||
importOptions.path = args.path;
|
|
||||||
importOptions.format = args.options.format ? args.options.format : 'auto';
|
|
||||||
importOptions.destinationFolderId = folder ? folder.id : null;
|
|
||||||
|
|
||||||
let lastProgress = '';
|
|
||||||
|
|
||||||
// onProgress/onError supported by Enex import only
|
|
||||||
|
|
||||||
importOptions.onProgress = (progressState) => {
|
|
||||||
let line = [];
|
|
||||||
line.push(_('Found: %d.', progressState.loaded));
|
|
||||||
line.push(_('Created: %d.', progressState.created));
|
|
||||||
if (progressState.updated) line.push(_('Updated: %d.', progressState.updated));
|
|
||||||
if (progressState.skipped) line.push(_('Skipped: %d.', progressState.skipped));
|
|
||||||
if (progressState.resourcesCreated) line.push(_('Resources: %d.', progressState.resourcesCreated));
|
|
||||||
if (progressState.notesTagged) line.push(_('Tagged: %d.', progressState.notesTagged));
|
|
||||||
lastProgress = line.join(' ');
|
|
||||||
cliUtils.redraw(lastProgress);
|
|
||||||
};
|
|
||||||
|
|
||||||
importOptions.onError = (error) => {
|
|
||||||
let s = error.trace ? error.trace : error.toString();
|
|
||||||
this.stdout(s);
|
|
||||||
};
|
|
||||||
|
|
||||||
app().gui().showConsole();
|
|
||||||
this.stdout(_('Importing notes...'));
|
|
||||||
const service = new InteropService();
|
|
||||||
const result = await service.import(importOptions);
|
|
||||||
result.warnings.map((w) => this.stdout(w));
|
|
||||||
cliUtils.redrawDone();
|
|
||||||
if (lastProgress) this.stdout(_('The notes have been imported: %s', lastProgress));
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = Command;
|
|
@@ -1,10 +1,10 @@
|
|||||||
const { BaseCommand } = require('./base-command.js');
|
const { BaseCommand } = require('./base-command.js');
|
||||||
const { app } = require('./app.js');
|
const { app } = require('./app.js');
|
||||||
const { _ } = require('lib/locale.js');
|
const { _ } = require('lib/locale.js');
|
||||||
const BaseModel = require('lib/BaseModel.js');
|
const { BaseModel } = require('lib/base-model.js');
|
||||||
const Folder = require('lib/models/Folder.js');
|
const { Folder } = require('lib/models/folder.js');
|
||||||
const Setting = require('lib/models/Setting.js');
|
const { Setting } = require('lib/models/setting.js');
|
||||||
const Note = require('lib/models/Note.js');
|
const { Note } = require('lib/models/note.js');
|
||||||
const { sprintf } = require('sprintf-js');
|
const { sprintf } = require('sprintf-js');
|
||||||
const { time } = require('lib/time-utils.js');
|
const { time } = require('lib/time-utils.js');
|
||||||
const { cliUtils } = require('./cli-utils.js');
|
const { cliUtils } = require('./cli-utils.js');
|
||||||
|
@@ -1,7 +1,7 @@
|
|||||||
const { BaseCommand } = require('./base-command.js');
|
const { BaseCommand } = require('./base-command.js');
|
||||||
const { app } = require('./app.js');
|
const { app } = require('./app.js');
|
||||||
const { _ } = require('lib/locale.js');
|
const { _ } = require('lib/locale.js');
|
||||||
const Folder = require('lib/models/Folder.js');
|
const { Folder } = require('lib/models/folder.js');
|
||||||
const { reg } = require('lib/registry.js');
|
const { reg } = require('lib/registry.js');
|
||||||
|
|
||||||
class Command extends BaseCommand {
|
class Command extends BaseCommand {
|
||||||
@@ -14,6 +14,10 @@ class Command extends BaseCommand {
|
|||||||
return _('Creates a new notebook.');
|
return _('Creates a new notebook.');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
aliases() {
|
||||||
|
return ['mkdir'];
|
||||||
|
}
|
||||||
|
|
||||||
async action(args) {
|
async action(args) {
|
||||||
let folder = await Folder.save({ title: args['new-notebook'] }, { userSideValidation: true });
|
let folder = await Folder.save({ title: args['new-notebook'] }, { userSideValidation: true });
|
||||||
app().switchCurrentFolder(folder);
|
app().switchCurrentFolder(folder);
|
||||||
|
@@ -1,7 +1,7 @@
|
|||||||
const { BaseCommand } = require('./base-command.js');
|
const { BaseCommand } = require('./base-command.js');
|
||||||
const { app } = require('./app.js');
|
const { app } = require('./app.js');
|
||||||
const { _ } = require('lib/locale.js');
|
const { _ } = require('lib/locale.js');
|
||||||
const Note = require('lib/models/Note.js');
|
const { Note } = require('lib/models/note.js');
|
||||||
|
|
||||||
class Command extends BaseCommand {
|
class Command extends BaseCommand {
|
||||||
|
|
||||||
|
@@ -1,7 +1,7 @@
|
|||||||
const { BaseCommand } = require('./base-command.js');
|
const { BaseCommand } = require('./base-command.js');
|
||||||
const { app } = require('./app.js');
|
const { app } = require('./app.js');
|
||||||
const { _ } = require('lib/locale.js');
|
const { _ } = require('lib/locale.js');
|
||||||
const Note = require('lib/models/Note.js');
|
const { Note } = require('lib/models/note.js');
|
||||||
|
|
||||||
class Command extends BaseCommand {
|
class Command extends BaseCommand {
|
||||||
|
|
||||||
|
@@ -1,9 +1,9 @@
|
|||||||
const { BaseCommand } = require('./base-command.js');
|
const { BaseCommand } = require('./base-command.js');
|
||||||
const { app } = require('./app.js');
|
const { app } = require('./app.js');
|
||||||
const { _ } = require('lib/locale.js');
|
const { _ } = require('lib/locale.js');
|
||||||
const BaseModel = require('lib/BaseModel.js');
|
const { BaseModel } = require('lib/base-model.js');
|
||||||
const Folder = require('lib/models/Folder.js');
|
const { Folder } = require('lib/models/folder.js');
|
||||||
const Note = require('lib/models/Note.js');
|
const { Note } = require('lib/models/note.js');
|
||||||
|
|
||||||
class Command extends BaseCommand {
|
class Command extends BaseCommand {
|
||||||
|
|
||||||
|
@@ -1,9 +1,9 @@
|
|||||||
const { BaseCommand } = require('./base-command.js');
|
const { BaseCommand } = require('./base-command.js');
|
||||||
const { app } = require('./app.js');
|
const { app } = require('./app.js');
|
||||||
const { _ } = require('lib/locale.js');
|
const { _ } = require('lib/locale.js');
|
||||||
const BaseModel = require('lib/BaseModel.js');
|
const { BaseModel } = require('lib/base-model.js');
|
||||||
const Folder = require('lib/models/Folder.js');
|
const { Folder } = require('lib/models/folder.js');
|
||||||
const Note = require('lib/models/Note.js');
|
const { Note } = require('lib/models/note.js');
|
||||||
|
|
||||||
class Command extends BaseCommand {
|
class Command extends BaseCommand {
|
||||||
|
|
||||||
@@ -20,7 +20,6 @@ class Command extends BaseCommand {
|
|||||||
const name = args['name'];
|
const name = args['name'];
|
||||||
|
|
||||||
const item = await app().loadItem('folderOrNote', pattern);
|
const item = await app().loadItem('folderOrNote', pattern);
|
||||||
this.encryptionCheck(item);
|
|
||||||
if (!item) throw new Error(_('Cannot find "%s".', pattern));
|
if (!item) throw new Error(_('Cannot find "%s".', pattern));
|
||||||
|
|
||||||
const newItem = {
|
const newItem = {
|
||||||
|
@@ -1,10 +1,10 @@
|
|||||||
const { BaseCommand } = require('./base-command.js');
|
const { BaseCommand } = require('./base-command.js');
|
||||||
const { app } = require('./app.js');
|
const { app } = require('./app.js');
|
||||||
const { _ } = require('lib/locale.js');
|
const { _ } = require('lib/locale.js');
|
||||||
const BaseItem = require('lib/models/BaseItem.js');
|
const { BaseItem } = require('lib/models/base-item.js');
|
||||||
const Folder = require('lib/models/Folder.js');
|
const { Folder } = require('lib/models/folder.js');
|
||||||
const Note = require('lib/models/Note.js');
|
const { Note } = require('lib/models/note.js');
|
||||||
const BaseModel = require('lib/BaseModel.js');
|
const { BaseModel } = require('lib/base-model.js');
|
||||||
const { cliUtils } = require('./cli-utils.js');
|
const { cliUtils } = require('./cli-utils.js');
|
||||||
|
|
||||||
class Command extends BaseCommand {
|
class Command extends BaseCommand {
|
||||||
@@ -29,7 +29,7 @@ class Command extends BaseCommand {
|
|||||||
|
|
||||||
const folder = await app().loadItem(BaseModel.TYPE_FOLDER, pattern);
|
const folder = await app().loadItem(BaseModel.TYPE_FOLDER, pattern);
|
||||||
if (!folder) throw new Error(_('Cannot find "%s".', pattern));
|
if (!folder) throw new Error(_('Cannot find "%s".', pattern));
|
||||||
const ok = force ? true : await this.prompt(_('Delete notebook? All notes within this notebook will also be deleted.'), { booleanAnswerDefault: 'n' });
|
const ok = force ? true : await this.prompt(_('Delete notebook "%s"?', folder.title), { booleanAnswerDefault: 'n' });
|
||||||
if (!ok) return;
|
if (!ok) return;
|
||||||
|
|
||||||
await Folder.delete(folder.id);
|
await Folder.delete(folder.id);
|
||||||
|
@@ -1,10 +1,10 @@
|
|||||||
const { BaseCommand } = require('./base-command.js');
|
const { BaseCommand } = require('./base-command.js');
|
||||||
const { app } = require('./app.js');
|
const { app } = require('./app.js');
|
||||||
const { _ } = require('lib/locale.js');
|
const { _ } = require('lib/locale.js');
|
||||||
const BaseItem = require('lib/models/BaseItem.js');
|
const { BaseItem } = require('lib/models/base-item.js');
|
||||||
const Folder = require('lib/models/Folder.js');
|
const { Folder } = require('lib/models/folder.js');
|
||||||
const Note = require('lib/models/Note.js');
|
const { Note } = require('lib/models/note.js');
|
||||||
const BaseModel = require('lib/BaseModel.js');
|
const { BaseModel } = require('lib/base-model.js');
|
||||||
const { cliUtils } = require('./cli-utils.js');
|
const { cliUtils } = require('./cli-utils.js');
|
||||||
|
|
||||||
class Command extends BaseCommand {
|
class Command extends BaseCommand {
|
||||||
|
@@ -1,9 +1,9 @@
|
|||||||
const { BaseCommand } = require('./base-command.js');
|
const { BaseCommand } = require('./base-command.js');
|
||||||
const { app } = require('./app.js');
|
const { app } = require('./app.js');
|
||||||
const { _ } = require('lib/locale.js');
|
const { _ } = require('lib/locale.js');
|
||||||
const BaseModel = require('lib/BaseModel.js');
|
const { BaseModel } = require('lib/base-model.js');
|
||||||
const Folder = require('lib/models/Folder.js');
|
const { Folder } = require('lib/models/folder.js');
|
||||||
const Note = require('lib/models/Note.js');
|
const { Note } = require('lib/models/note.js');
|
||||||
const { sprintf } = require('sprintf-js');
|
const { sprintf } = require('sprintf-js');
|
||||||
const { time } = require('lib/time-utils.js');
|
const { time } = require('lib/time-utils.js');
|
||||||
const { uuid } = require('lib/uuid.js');
|
const { uuid } = require('lib/uuid.js');
|
||||||
|
@@ -1,11 +1,10 @@
|
|||||||
const { BaseCommand } = require('./base-command.js');
|
const { BaseCommand } = require('./base-command.js');
|
||||||
const { app } = require('./app.js');
|
const { app } = require('./app.js');
|
||||||
const { _ } = require('lib/locale.js');
|
const { _ } = require('lib/locale.js');
|
||||||
const BaseModel = require('lib/BaseModel.js');
|
const { BaseModel } = require('lib/base-model.js');
|
||||||
const { Database } = require('lib/database.js');
|
const { Folder } = require('lib/models/folder.js');
|
||||||
const Folder = require('lib/models/Folder.js');
|
const { Note } = require('lib/models/note.js');
|
||||||
const Note = require('lib/models/Note.js');
|
const { BaseItem } = require('lib/models/base-item.js');
|
||||||
const BaseItem = require('lib/models/BaseItem.js');
|
|
||||||
|
|
||||||
class Command extends BaseCommand {
|
class Command extends BaseCommand {
|
||||||
|
|
||||||
@@ -13,16 +12,16 @@ class Command extends BaseCommand {
|
|||||||
return 'set <note> <name> [value]';
|
return 'set <note> <name> [value]';
|
||||||
}
|
}
|
||||||
|
|
||||||
description() {
|
enabled() {
|
||||||
const fields = Note.fields();
|
return false;
|
||||||
const s = [];
|
}
|
||||||
for (let i = 0; i < fields.length; i++) {
|
|
||||||
const f = fields[i];
|
|
||||||
if (f.name === 'id') continue;
|
|
||||||
s.push(f.name + ' (' + Database.enumName('fieldType', f.type) + ')');
|
|
||||||
}
|
|
||||||
|
|
||||||
return _('Sets the property <name> of the given <note> to the given [value]. Possible properties are:\n\n%s', s.join(', '));
|
description() {
|
||||||
|
return _('Sets the property <name> of the given <note> to the given [value].');
|
||||||
|
}
|
||||||
|
|
||||||
|
hidden() {
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
async action(args) {
|
async action(args) {
|
||||||
@@ -35,8 +34,6 @@ class Command extends BaseCommand {
|
|||||||
if (!notes.length) throw new Error(_('Cannot find "%s".', title));
|
if (!notes.length) throw new Error(_('Cannot find "%s".', title));
|
||||||
|
|
||||||
for (let i = 0; i < notes.length; i++) {
|
for (let i = 0; i < notes.length; i++) {
|
||||||
this.encryptionCheck(notes[i]);
|
|
||||||
|
|
||||||
let newNote = {
|
let newNote = {
|
||||||
id: notes[i].id,
|
id: notes[i].id,
|
||||||
type_: notes[i].type_,
|
type_: notes[i].type_,
|
||||||
|
@@ -1,7 +1,7 @@
|
|||||||
const { BaseCommand } = require('./base-command.js');
|
const { BaseCommand } = require('./base-command.js');
|
||||||
const { Database } = require('lib/database.js');
|
const { Database } = require('lib/database.js');
|
||||||
const { app } = require('./app.js');
|
const { app } = require('./app.js');
|
||||||
const Setting = require('lib/models/Setting.js');
|
const { Setting } = require('lib/models/setting.js');
|
||||||
const { _ } = require('lib/locale.js');
|
const { _ } = require('lib/locale.js');
|
||||||
const { ReportService } = require('lib/services/report.js');
|
const { ReportService } = require('lib/services/report.js');
|
||||||
|
|
||||||
|
@@ -2,15 +2,15 @@ const { BaseCommand } = require('./base-command.js');
|
|||||||
const { app } = require('./app.js');
|
const { app } = require('./app.js');
|
||||||
const { _ } = require('lib/locale.js');
|
const { _ } = require('lib/locale.js');
|
||||||
const { OneDriveApiNodeUtils } = require('./onedrive-api-node-utils.js');
|
const { OneDriveApiNodeUtils } = require('./onedrive-api-node-utils.js');
|
||||||
const Setting = require('lib/models/Setting.js');
|
const { Setting } = require('lib/models/setting.js');
|
||||||
const BaseItem = require('lib/models/BaseItem.js');
|
const { BaseItem } = require('lib/models/base-item.js');
|
||||||
const { Synchronizer } = require('lib/synchronizer.js');
|
const { Synchronizer } = require('lib/synchronizer.js');
|
||||||
const { reg } = require('lib/registry.js');
|
const { reg } = require('lib/registry.js');
|
||||||
const { cliUtils } = require('./cli-utils.js');
|
const { cliUtils } = require('./cli-utils.js');
|
||||||
const md5 = require('md5');
|
const md5 = require('md5');
|
||||||
const locker = require('proper-lockfile');
|
const locker = require('proper-lockfile');
|
||||||
const fs = require('fs-extra');
|
const fs = require('fs-extra');
|
||||||
const SyncTargetRegistry = require('lib/SyncTargetRegistry');
|
const osTmpdir = require('os-tmpdir');
|
||||||
|
|
||||||
class Command extends BaseCommand {
|
class Command extends BaseCommand {
|
||||||
|
|
||||||
@@ -32,6 +32,7 @@ class Command extends BaseCommand {
|
|||||||
options() {
|
options() {
|
||||||
return [
|
return [
|
||||||
['--target <target>', _('Sync to provided target (defaults to sync.target config value)')],
|
['--target <target>', _('Sync to provided target (defaults to sync.target config value)')],
|
||||||
|
['--random-failures', 'For debugging purposes. Do not use.'],
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -61,44 +62,14 @@ class Command extends BaseCommand {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async doAuth() {
|
async doAuth(syncTargetId) {
|
||||||
const syncTarget = reg.syncTarget(this.syncTargetId_);
|
const syncTarget = reg.syncTarget(this.syncTargetId_);
|
||||||
const syncTargetMd = SyncTargetRegistry.idToMetadata(this.syncTargetId_);
|
this.oneDriveApiUtils_ = new OneDriveApiNodeUtils(syncTarget.api());
|
||||||
|
const auth = await this.oneDriveApiUtils_.oauthDance({
|
||||||
if (this.syncTargetId_ === 3 || this.syncTargetId_ === 4) { // OneDrive
|
log: (...s) => { return this.stdout(...s); }
|
||||||
this.oneDriveApiUtils_ = new OneDriveApiNodeUtils(syncTarget.api());
|
});
|
||||||
const auth = await this.oneDriveApiUtils_.oauthDance({
|
this.oneDriveApiUtils_ = null;
|
||||||
log: (...s) => { return this.stdout(...s); }
|
return auth;
|
||||||
});
|
|
||||||
this.oneDriveApiUtils_ = null;
|
|
||||||
|
|
||||||
Setting.setValue('sync.' + this.syncTargetId_ + '.auth', auth ? JSON.stringify(auth) : null);
|
|
||||||
if (!auth) {
|
|
||||||
this.stdout(_('Authentication was not completed (did not receive an authentication token).'));
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
} else if (syncTargetMd.name === 'dropbox') { // Dropbox
|
|
||||||
const api = await syncTarget.api();
|
|
||||||
const loginUrl = api.loginUrl();
|
|
||||||
this.stdout(_('To allow Joplin to synchronise with Dropbox, please follow the steps below:'));
|
|
||||||
this.stdout(_('Step 1: Open this URL in your browser to authorise the application:'));
|
|
||||||
this.stdout(loginUrl);
|
|
||||||
const authCode = await this.prompt(_('Step 2: Enter the code provided by Dropbox:'), { type: 'string' });
|
|
||||||
if (!authCode) {
|
|
||||||
this.stdout(_('Authentication was not completed (did not receive an authentication token).'));
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
const response = await api.execAuthToken(authCode);
|
|
||||||
Setting.setValue('sync.' + this.syncTargetId_ + '.auth', response.access_token);
|
|
||||||
api.setAuthToken(response.access_token);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.stdout(_('Not authentified with %s. Please provide any missing credentials.', syncTargetMd.label));
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
cancelAuth() {
|
cancelAuth() {
|
||||||
@@ -116,8 +87,7 @@ class Command extends BaseCommand {
|
|||||||
this.releaseLockFn_ = null;
|
this.releaseLockFn_ = null;
|
||||||
|
|
||||||
// Lock is unique per profile/database
|
// Lock is unique per profile/database
|
||||||
// TODO: use SQLite database to do lock?
|
const lockFilePath = osTmpdir() + '/synclock_' + md5(Setting.value('profileDir'));
|
||||||
const lockFilePath = require('os').tmpdir() + '/synclock_' + md5(escape(Setting.value('profileDir'))); // https://github.com/pvorb/node-md5/issues/41
|
|
||||||
if (!await fs.pathExists(lockFilePath)) await fs.writeFile(lockFilePath, 'synclock');
|
if (!await fs.pathExists(lockFilePath)) await fs.writeFile(lockFilePath, 'synclock');
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@@ -147,12 +117,16 @@ class Command extends BaseCommand {
|
|||||||
|
|
||||||
const syncTarget = reg.syncTarget(this.syncTargetId_);
|
const syncTarget = reg.syncTarget(this.syncTargetId_);
|
||||||
|
|
||||||
if (!await syncTarget.isAuthenticated()) {
|
if (!syncTarget.isAuthenticated()) {
|
||||||
app().gui().showConsole();
|
app().gui().showConsole();
|
||||||
app().gui().maximizeConsole();
|
app().gui().maximizeConsole();
|
||||||
|
|
||||||
const authDone = await this.doAuth();
|
const auth = await this.doAuth(this.syncTargetId_);
|
||||||
if (!authDone) return cleanUp();
|
Setting.setValue('sync.' + this.syncTargetId_ + '.auth', auth ? JSON.stringify(auth) : null);
|
||||||
|
if (!auth) {
|
||||||
|
this.stdout(_('Authentication was not completed (did not receive an authentication token).'));
|
||||||
|
return cleanUp();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const sync = await syncTarget.synchronizer();
|
const sync = await syncTarget.synchronizer();
|
||||||
@@ -166,6 +140,7 @@ class Command extends BaseCommand {
|
|||||||
cliUtils.redrawDone();
|
cliUtils.redrawDone();
|
||||||
this.stdout(msg);
|
this.stdout(msg);
|
||||||
},
|
},
|
||||||
|
randomFailures: args.options['random-failures'] === true,
|
||||||
};
|
};
|
||||||
|
|
||||||
this.stdout(_('Synchronisation target: %s (%s)', Setting.enumOptionLabel('sync.target', this.syncTargetId_), this.syncTargetId_));
|
this.stdout(_('Synchronisation target: %s (%s)', Setting.enumOptionLabel('sync.target', this.syncTargetId_), this.syncTargetId_));
|
||||||
@@ -214,7 +189,7 @@ class Command extends BaseCommand {
|
|||||||
|
|
||||||
const syncTarget = reg.syncTarget(syncTargetId);
|
const syncTarget = reg.syncTarget(syncTargetId);
|
||||||
|
|
||||||
if (await syncTarget.isAuthenticated()) {
|
if (syncTarget.isAuthenticated()) {
|
||||||
const sync = await syncTarget.synchronizer();
|
const sync = await syncTarget.synchronizer();
|
||||||
if (sync) await sync.cancel();
|
if (sync) await sync.cancel();
|
||||||
} else {
|
} else {
|
||||||
|
@@ -1,8 +1,8 @@
|
|||||||
const { BaseCommand } = require('./base-command.js');
|
const { BaseCommand } = require('./base-command.js');
|
||||||
const { app } = require('./app.js');
|
const { app } = require('./app.js');
|
||||||
const { _ } = require('lib/locale.js');
|
const { _ } = require('lib/locale.js');
|
||||||
const Tag = require('lib/models/Tag.js');
|
const { Tag } = require('lib/models/tag.js');
|
||||||
const BaseModel = require('lib/BaseModel.js');
|
const { BaseModel } = require('lib/base-model.js');
|
||||||
|
|
||||||
class Command extends BaseCommand {
|
class Command extends BaseCommand {
|
||||||
|
|
||||||
|
@@ -1,9 +1,9 @@
|
|||||||
const { BaseCommand } = require('./base-command.js');
|
const { BaseCommand } = require('./base-command.js');
|
||||||
const { app } = require('./app.js');
|
const { app } = require('./app.js');
|
||||||
const { _ } = require('lib/locale.js');
|
const { _ } = require('lib/locale.js');
|
||||||
const BaseModel = require('lib/BaseModel.js');
|
const { BaseModel } = require('lib/base-model.js');
|
||||||
const Folder = require('lib/models/Folder.js');
|
const { Folder } = require('lib/models/folder.js');
|
||||||
const Note = require('lib/models/Note.js');
|
const { Note } = require('lib/models/note.js');
|
||||||
const { time } = require('lib/time-utils.js');
|
const { time } = require('lib/time-utils.js');
|
||||||
|
|
||||||
class Command extends BaseCommand {
|
class Command extends BaseCommand {
|
||||||
@@ -25,8 +25,6 @@ class Command extends BaseCommand {
|
|||||||
for (let i = 0; i < notes.length; i++) {
|
for (let i = 0; i < notes.length; i++) {
|
||||||
const note = notes[i];
|
const note = notes[i];
|
||||||
|
|
||||||
this.encryptionCheck(note);
|
|
||||||
|
|
||||||
let toSave = {
|
let toSave = {
|
||||||
id: note.id,
|
id: note.id,
|
||||||
};
|
};
|
||||||
|
@@ -1,9 +1,9 @@
|
|||||||
const { BaseCommand } = require('./base-command.js');
|
const { BaseCommand } = require('./base-command.js');
|
||||||
const { app } = require('./app.js');
|
const { app } = require('./app.js');
|
||||||
const { _ } = require('lib/locale.js');
|
const { _ } = require('lib/locale.js');
|
||||||
const BaseModel = require('lib/BaseModel.js');
|
const { BaseModel } = require('lib/base-model.js');
|
||||||
const Folder = require('lib/models/Folder.js');
|
const { Folder } = require('lib/models/folder.js');
|
||||||
const Note = require('lib/models/Note.js');
|
const { Note } = require('lib/models/note.js');
|
||||||
const { time } = require('lib/time-utils.js');
|
const { time } = require('lib/time-utils.js');
|
||||||
|
|
||||||
const CommandDone = require('./command-done.js');
|
const CommandDone = require('./command-done.js');
|
||||||
@@ -19,7 +19,7 @@ class Command extends BaseCommand {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async action(args) {
|
async action(args) {
|
||||||
await CommandDone.handleAction(this, args, false);
|
await CommandDone.handleAction(args, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -1,8 +1,8 @@
|
|||||||
const { BaseCommand } = require('./base-command.js');
|
const { BaseCommand } = require('./base-command.js');
|
||||||
const { app } = require('./app.js');
|
const { app } = require('./app.js');
|
||||||
const { _ } = require('lib/locale.js');
|
const { _ } = require('lib/locale.js');
|
||||||
const BaseModel = require('lib/BaseModel.js');
|
const { BaseModel } = require('lib/base-model.js');
|
||||||
const Folder = require('lib/models/Folder.js');
|
const { Folder } = require('lib/models/folder.js');
|
||||||
|
|
||||||
class Command extends BaseCommand {
|
class Command extends BaseCommand {
|
||||||
|
|
||||||
@@ -18,8 +18,8 @@ class Command extends BaseCommand {
|
|||||||
return { data: autocompleteFolders };
|
return { data: autocompleteFolders };
|
||||||
}
|
}
|
||||||
|
|
||||||
compatibleUis() {
|
enabled() {
|
||||||
return ['cli'];
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
async action(args) {
|
async action(args) {
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
const { BaseCommand } = require('./base-command.js');
|
const { BaseCommand } = require('./base-command.js');
|
||||||
const Setting = require('lib/models/Setting.js');
|
const { Setting } = require('lib/models/setting.js');
|
||||||
const { _ } = require('lib/locale.js');
|
const { _ } = require('lib/locale.js');
|
||||||
|
|
||||||
class Command extends BaseCommand {
|
class Command extends BaseCommand {
|
||||||
|
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
const { time } = require('lib/time-utils.js');
|
const { time } = require('lib/time-utils.js');
|
||||||
const { Logger } = require('lib/logger.js');
|
const { Logger } = require('lib/logger.js');
|
||||||
const Resource = require('lib/models/Resource.js');
|
const { Resource } = require('lib/models/resource.js');
|
||||||
const { dirname } = require('lib/path-utils.js');
|
const { dirname } = require('lib/path-utils.js');
|
||||||
const { FsDriverNode } = require('./fs-driver-node.js');
|
const { FsDriverNode } = require('./fs-driver-node.js');
|
||||||
const lodash = require('lodash');
|
const lodash = require('lodash');
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
const Folder = require('lib/models/Folder.js');
|
const Folder = require('lib/models/folder.js').Folder;
|
||||||
const Tag = require('lib/models/Tag.js');
|
const Tag = require('lib/models/tag.js').Tag;
|
||||||
const BaseModel = require('lib/BaseModel.js');
|
const BaseModel = require('lib/base-model.js').BaseModel;
|
||||||
const ListWidget = require('tkwidgets/ListWidget.js');
|
const ListWidget = require('tkwidgets/ListWidget.js');
|
||||||
const _ = require('lib/locale.js')._;
|
const _ = require('lib/locale.js')._;
|
||||||
|
|
||||||
@@ -24,9 +24,9 @@ class FolderListWidget extends ListWidget {
|
|||||||
if (item === '-') {
|
if (item === '-') {
|
||||||
output.push('-'.repeat(this.innerWidth));
|
output.push('-'.repeat(this.innerWidth));
|
||||||
} else if (item.type_ === Folder.modelType()) {
|
} else if (item.type_ === Folder.modelType()) {
|
||||||
output.push(Folder.displayTitle(item));
|
output.push(item.title);
|
||||||
} else if (item.type_ === Tag.modelType()) {
|
} else if (item.type_ === Tag.modelType()) {
|
||||||
output.push('[' + Folder.displayTitle(item) + ']');
|
output.push('[' + item.title + ']');
|
||||||
} else if (item.type_ === BaseModel.TYPE_SEARCH) {
|
} else if (item.type_ === BaseModel.TYPE_SEARCH) {
|
||||||
output.push(_('Search:'));
|
output.push(_('Search:'));
|
||||||
output.push(item.title);
|
output.push(item.title);
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
const Note = require('lib/models/Note.js');
|
const Note = require('lib/models/note.js').Note;
|
||||||
const ListWidget = require('tkwidgets/ListWidget.js');
|
const ListWidget = require('tkwidgets/ListWidget.js');
|
||||||
|
|
||||||
class NoteListWidget extends ListWidget {
|
class NoteListWidget extends ListWidget {
|
||||||
@@ -10,7 +10,7 @@ class NoteListWidget extends ListWidget {
|
|||||||
this.updateIndexFromSelectedNoteId_ = false;
|
this.updateIndexFromSelectedNoteId_ = false;
|
||||||
|
|
||||||
this.itemRenderer = (note) => {
|
this.itemRenderer = (note) => {
|
||||||
let label = Note.displayTitle(note); // + ' ' + note.id;
|
let label = note.title; // + ' ' + note.id;
|
||||||
if (note.is_todo) {
|
if (note.is_todo) {
|
||||||
label = '[' + (note.todo_completed ? 'X' : ' ') + '] ' + label;
|
label = '[' + (note.todo_completed ? 'X' : ' ') + '] ' + label;
|
||||||
}
|
}
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
const Note = require('lib/models/Note.js');
|
const Note = require('lib/models/note.js').Note;
|
||||||
const TextWidget = require('tkwidgets/TextWidget.js');
|
const TextWidget = require('tkwidgets/TextWidget.js');
|
||||||
|
|
||||||
class NoteMetadataWidget extends TextWidget {
|
class NoteMetadataWidget extends TextWidget {
|
||||||
|
@@ -1,6 +1,5 @@
|
|||||||
const Note = require('lib/models/Note.js');
|
const Note = require('lib/models/note.js').Note;
|
||||||
const TextWidget = require('tkwidgets/TextWidget.js');
|
const TextWidget = require('tkwidgets/TextWidget.js');
|
||||||
const { _ } = require('lib/locale.js');
|
|
||||||
|
|
||||||
class NoteWidget extends TextWidget {
|
class NoteWidget extends TextWidget {
|
||||||
|
|
||||||
@@ -33,24 +32,11 @@ class NoteWidget extends TextWidget {
|
|||||||
this.reloadNote();
|
this.reloadNote();
|
||||||
}
|
}
|
||||||
|
|
||||||
welcomeText() {
|
|
||||||
return _('Welcome to Joplin!\n\nType `:help shortcuts` for the list of keyboard shortcuts, or just `:help` for usage information.\n\nFor example, to create a notebook press `mb`; to create a note press `mn`.');
|
|
||||||
}
|
|
||||||
|
|
||||||
reloadNote() {
|
reloadNote() {
|
||||||
if (!this.noteId_ && !this.notes.length) {
|
if (this.noteId_) {
|
||||||
this.text = this.welcomeText();
|
|
||||||
this.scrollTop = 0;
|
|
||||||
} else if (this.noteId_) {
|
|
||||||
this.doAsync('loadNote', async () => {
|
this.doAsync('loadNote', async () => {
|
||||||
this.note_ = await Note.load(this.noteId_);
|
this.note_ = await Note.load(this.noteId_);
|
||||||
|
this.text = this.note_ ? this.note_.title + "\n\n" + this.note_.body : '';
|
||||||
if (this.note_ && this.note_.encryption_applied) {
|
|
||||||
this.text = _('One or more items are currently encrypted and you may need to supply a master password. To do so please type `e2ee decrypt`. If you have already supplied the password, the encrypted items are being decrypted in the background and will be available soon.');
|
|
||||||
} else {
|
|
||||||
this.text = this.note_ ? this.note_.title + "\n\n" + this.note_.body : '';
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.lastLoadedNoteId_ !== this.noteId_) this.scrollTop = 0;
|
if (this.lastLoadedNoteId_ !== this.noteId_) this.scrollTop = 0;
|
||||||
this.lastLoadedNoteId_ = this.noteId_;
|
this.lastLoadedNoteId_ = this.noteId_;
|
||||||
});
|
});
|
||||||
|
@@ -2,7 +2,6 @@ const BaseWidget = require('tkwidgets/BaseWidget.js');
|
|||||||
const chalk = require('chalk');
|
const chalk = require('chalk');
|
||||||
const termutils = require('tkwidgets/framework/termutils.js');
|
const termutils = require('tkwidgets/framework/termutils.js');
|
||||||
const stripAnsi = require('strip-ansi');
|
const stripAnsi = require('strip-ansi');
|
||||||
const { handleAutocompletion } = require('../autocompletion.js');
|
|
||||||
|
|
||||||
class StatusBarWidget extends BaseWidget {
|
class StatusBarWidget extends BaseWidget {
|
||||||
|
|
||||||
@@ -42,7 +41,6 @@ class StatusBarWidget extends BaseWidget {
|
|||||||
};
|
};
|
||||||
|
|
||||||
if ('cursorPosition' in options) this.promptState_.cursorPosition = options.cursorPosition;
|
if ('cursorPosition' in options) this.promptState_.cursorPosition = options.cursorPosition;
|
||||||
if ('secure' in options) this.promptState_.secure = options.secure;
|
|
||||||
|
|
||||||
this.promptState_.promise = new Promise((resolve, reject) => {
|
this.promptState_.promise = new Promise((resolve, reject) => {
|
||||||
this.promptState_.resolve = resolve;
|
this.promptState_.resolve = resolve;
|
||||||
@@ -106,19 +104,13 @@ class StatusBarWidget extends BaseWidget {
|
|||||||
|
|
||||||
this.term.showCursor(true);
|
this.term.showCursor(true);
|
||||||
|
|
||||||
const isSecurePrompt = !!this.promptState_.secure;
|
|
||||||
|
|
||||||
let options = {
|
let options = {
|
||||||
cancelable: true,
|
cancelable: true,
|
||||||
history: this.history,
|
history: this.history,
|
||||||
default: this.promptState_.initialText,
|
default: this.promptState_.initialText,
|
||||||
autoComplete: handleAutocompletion,
|
|
||||||
autoCompleteHint : true,
|
|
||||||
autoCompleteMenu : true,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
if ('cursorPosition' in this.promptState_) options.cursorPosition = this.promptState_.cursorPosition;
|
if ('cursorPosition' in this.promptState_) options.cursorPosition = this.promptState_.cursorPosition;
|
||||||
if (isSecurePrompt) options.echoChar = true;
|
|
||||||
|
|
||||||
this.inputEventEmitter_ = this.term.inputField(options, (error, input) => {
|
this.inputEventEmitter_ = this.term.inputField(options, (error, input) => {
|
||||||
let resolveResult = null;
|
let resolveResult = null;
|
||||||
@@ -133,8 +125,7 @@ class StatusBarWidget extends BaseWidget {
|
|||||||
resolveResult = input ? input.trim() : input;
|
resolveResult = input ? input.trim() : input;
|
||||||
// Add the command to history but only if it's longer than one character.
|
// Add the command to history but only if it's longer than one character.
|
||||||
// Below that it's usually an answer like "y"/"n", etc.
|
// Below that it's usually an answer like "y"/"n", etc.
|
||||||
const isConfigPassword = input.indexOf('config ') >= 0 && input.indexOf('password') >= 0;
|
if (input && input.length > 1) this.history_.push(input);
|
||||||
if (!isSecurePrompt && input && input.length > 1 && !isConfigPassword) this.history_.push(input);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -168,4 +159,4 @@ class StatusBarWidget extends BaseWidget {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = StatusBarWidget;
|
module.exports = StatusBarWidget;
|
@@ -1,6 +1,6 @@
|
|||||||
const fs = require('fs-extra');
|
const fs = require('fs-extra');
|
||||||
const { wrap } = require('lib/string-utils.js');
|
const { wrap } = require('lib/string-utils.js');
|
||||||
const Setting = require('lib/models/Setting.js');
|
const { Setting } = require('lib/models/setting.js');
|
||||||
const { fileExtension, basename, dirname } = require('lib/path-utils.js');
|
const { fileExtension, basename, dirname } = require('lib/path-utils.js');
|
||||||
const { _, setLocale, languageCode } = require('lib/locale.js');
|
const { _, setLocale, languageCode } = require('lib/locale.js');
|
||||||
|
|
||||||
@@ -53,8 +53,9 @@ function renderCommandHelp(cmd, width = null) {
|
|||||||
desc.push(label);
|
desc.push(label);
|
||||||
}
|
}
|
||||||
|
|
||||||
const description = Setting.keyDescription(md.key, 'cli');
|
if (md.description) {
|
||||||
if (description) desc.push(description);
|
desc.push(md.description());
|
||||||
|
}
|
||||||
|
|
||||||
desc.push(_('Type: %s.', md.isEnum ? _('Enum') : Setting.typeToString(md.type)));
|
desc.push(_('Type: %s.', md.isEnum ? _('Enum') : Setting.typeToString(md.type)));
|
||||||
if (md.isEnum) desc.push(_('Possible values: %s.', Setting.enumOptionsDoc(md.key, '%s (%s)')));
|
if (md.isEnum) desc.push(_('Possible values: %s.', Setting.enumOptionsDoc(md.key, '%s (%s)')));
|
||||||
|
@@ -1,36 +1,26 @@
|
|||||||
#!/usr/bin/env node
|
#!/usr/bin/env node
|
||||||
|
|
||||||
// Make it possible to require("/lib/...") without specifying full path
|
// Loading time: 20170803: 1.5s with no commands
|
||||||
|
|
||||||
require('app-module-path').addPath(__dirname);
|
require('app-module-path').addPath(__dirname);
|
||||||
|
|
||||||
const compareVersion = require('compare-version');
|
|
||||||
const nodeVersion = process && process.versions && process.versions.node ? process.versions.node : '0.0.0';
|
|
||||||
if (compareVersion(nodeVersion, '8.0.0') < 0) {
|
|
||||||
console.error('Joplin requires Node 8+. Detected version ' + nodeVersion);
|
|
||||||
process.exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
const { app } = require('./app.js');
|
const { app } = require('./app.js');
|
||||||
const Folder = require('lib/models/Folder.js');
|
const { BaseModel } = require('lib/base-model.js');
|
||||||
const Resource = require('lib/models/Resource.js');
|
const { Folder } = require('lib/models/folder.js');
|
||||||
const BaseItem = require('lib/models/BaseItem.js');
|
const { Resource } = require('lib/models/resource.js');
|
||||||
const Note = require('lib/models/Note.js');
|
const { BaseItem } = require('lib/models/base-item.js');
|
||||||
const Tag = require('lib/models/Tag.js');
|
const { Note } = require('lib/models/note.js');
|
||||||
const NoteTag = require('lib/models/NoteTag.js');
|
const { Tag } = require('lib/models/tag.js');
|
||||||
const MasterKey = require('lib/models/MasterKey');
|
const { NoteTag } = require('lib/models/note-tag.js');
|
||||||
const Setting = require('lib/models/Setting.js');
|
const { Setting } = require('lib/models/setting.js');
|
||||||
const { Logger } = require('lib/logger.js');
|
const { Logger } = require('lib/logger.js');
|
||||||
const { FsDriverNode } = require('lib/fs-driver-node.js');
|
const { FsDriverNode } = require('lib/fs-driver-node.js');
|
||||||
const { shimInit } = require('lib/shim-init-node.js');
|
const { shimInit } = require('lib/shim-init-node.js');
|
||||||
const { _ } = require('lib/locale.js');
|
const { _ } = require('lib/locale.js');
|
||||||
const { FileApiDriverLocal } = require('lib/file-api-driver-local.js');
|
|
||||||
const EncryptionService = require('lib/services/EncryptionService');
|
|
||||||
|
|
||||||
const fsDriver = new FsDriverNode();
|
const fsDriver = new FsDriverNode();
|
||||||
Logger.fsDriver_ = fsDriver;
|
Logger.fsDriver_ = fsDriver;
|
||||||
Resource.fsDriver_ = fsDriver;
|
Resource.fsDriver_ = fsDriver;
|
||||||
EncryptionService.fsDriver_ = fsDriver;
|
|
||||||
FileApiDriverLocal.fsDriver_ = fsDriver;
|
|
||||||
|
|
||||||
// That's not good, but it's to avoid circular dependency issues
|
// That's not good, but it's to avoid circular dependency issues
|
||||||
// in the BaseItem class.
|
// in the BaseItem class.
|
||||||
@@ -39,7 +29,6 @@ BaseItem.loadClass('Folder', Folder);
|
|||||||
BaseItem.loadClass('Resource', Resource);
|
BaseItem.loadClass('Resource', Resource);
|
||||||
BaseItem.loadClass('Tag', Tag);
|
BaseItem.loadClass('Tag', Tag);
|
||||||
BaseItem.loadClass('NoteTag', NoteTag);
|
BaseItem.loadClass('NoteTag', NoteTag);
|
||||||
BaseItem.loadClass('MasterKey', MasterKey);
|
|
||||||
|
|
||||||
Setting.setConstant('appId', 'net.cozic.joplin-cli');
|
Setting.setConstant('appId', 'net.cozic.joplin-cli');
|
||||||
Setting.setConstant('appType', 'cli');
|
Setting.setConstant('appType', 'cli');
|
||||||
@@ -66,34 +55,7 @@ process.stdout.on('error', function( err ) {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// async function main() {
|
|
||||||
// const InteropService = require('lib/services/InteropService');
|
|
||||||
// const service = new InteropService();
|
|
||||||
// console.info(service.moduleByFormat('importer', 'enex'));
|
|
||||||
// //await service.modules();
|
|
||||||
// }
|
|
||||||
|
|
||||||
// main().catch((error) => { console.error(error); });
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
application.start(process.argv).catch((error) => {
|
application.start(process.argv).catch((error) => {
|
||||||
if (error.code == 'flagError') {
|
console.error(_('Fatal error:'));
|
||||||
console.error(error.message);
|
console.error(error);
|
||||||
console.error(_('Type `joplin help` for usage information.'));
|
|
||||||
} else {
|
|
||||||
console.error(_('Fatal error:'));
|
|
||||||
console.error(error);
|
|
||||||
}
|
|
||||||
|
|
||||||
process.exit(1);
|
|
||||||
});
|
});
|
@@ -4,6 +4,6 @@ ROOT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
|
|||||||
BUILD_DIR="$ROOT_DIR/build"
|
BUILD_DIR="$ROOT_DIR/build"
|
||||||
|
|
||||||
rsync -a --exclude "node_modules/" "$ROOT_DIR/app/" "$BUILD_DIR/"
|
rsync -a --exclude "node_modules/" "$ROOT_DIR/app/" "$BUILD_DIR/"
|
||||||
rsync -a --delete "$ROOT_DIR/../ReactNativeClient/lib/" "$BUILD_DIR/lib/"
|
rsync -a "$ROOT_DIR/../ReactNativeClient/lib/" "$BUILD_DIR/lib/"
|
||||||
cp "$ROOT_DIR/package.json" "$BUILD_DIR"
|
cp "$ROOT_DIR/package.json" "$BUILD_DIR"
|
||||||
chmod 755 "$BUILD_DIR/main.js"
|
chmod 755 "$BUILD_DIR/main.js"
|
@@ -15,12 +15,63 @@ msgstr ""
|
|||||||
"Content-Type: text/plain; charset=CHARSET\n"
|
"Content-Type: text/plain; charset=CHARSET\n"
|
||||||
"Content-Transfer-Encoding: 8bit\n"
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
|
|
||||||
|
msgid "Give focus to next pane"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Give focus to previous pane"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Enter command line mode"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Exit command line mode"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Edit the selected note"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Cancel the current command."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Exit the application."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Delete the currently selected note or notebook."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
msgid "To delete a tag, untag the associated notes."
|
msgid "To delete a tag, untag the associated notes."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid "Please select the note or notebook to be deleted first."
|
msgid "Please select the note or notebook to be deleted first."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Set a to-do as completed / not completed"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "[t]oggle [c]onsole between maximized/minimized/hidden/visible."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Search"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "[t]oggle note [m]etadata."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "[M]ake a new [n]ote"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "[M]ake a new [t]odo"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "[M]ake a new note[b]ook"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Copy ([Y]ank) the [n]ote to a notebook."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Move the note to a notebook."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
msgid "Press Ctrl+D or type \"exit\" to exit the application"
|
msgid "Press Ctrl+D or type \"exit\" to exit the application"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@@ -49,17 +100,10 @@ msgstr ""
|
|||||||
msgid "Cancelling background synchronisation... Please wait."
|
msgid "Cancelling background synchronisation... Please wait."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#, javascript-format
|
|
||||||
msgid "No such command: %s"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#, javascript-format
|
#, javascript-format
|
||||||
msgid "The command \"%s\" is only available in GUI mode"
|
msgid "The command \"%s\" is only available in GUI mode"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid "Cannot change encrypted item"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#, javascript-format
|
#, javascript-format
|
||||||
msgid "Missing required argument: %s"
|
msgid "Missing required argument: %s"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -117,35 +161,6 @@ msgstr ""
|
|||||||
msgid "Note is not a to-do: \"%s\""
|
msgid "Note is not a to-do: \"%s\""
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid ""
|
|
||||||
"Manages E2EE configuration. Commands are `enable`, `disable`, `decrypt`, "
|
|
||||||
"`status` and `target-status`."
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Enter master password:"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Operation cancelled"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid ""
|
|
||||||
"Starting decryption... Please wait as it may take several minutes depending "
|
|
||||||
"on how much there is to decrypt."
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Completed decryption."
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Enabled"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Disabled"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#, javascript-format
|
|
||||||
msgid "Encryption is: %s"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Edit note."
|
msgid "Edit note."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@@ -163,10 +178,6 @@ msgstr ""
|
|||||||
msgid "Starting to edit note. Close the editor to get back to the prompt."
|
msgid "Starting to edit note. Close the editor to get back to the prompt."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#, javascript-format
|
|
||||||
msgid "Error opening note in editor: %s"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Note has been saved."
|
msgid "Note has been saved."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@@ -174,14 +185,10 @@ msgid "Exits the application."
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid ""
|
msgid ""
|
||||||
"Exports Joplin data to the given path. By default, it will export the "
|
"Exports Joplin data to the given directory. By default, it will export the "
|
||||||
"complete database including notebooks, notes, tags and resources."
|
"complete database including notebooks, notes, tags and resources."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#, javascript-format
|
|
||||||
msgid "Destination format: %s"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Exports only the given note."
|
msgid "Exports only the given note."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@@ -194,16 +201,10 @@ msgstr ""
|
|||||||
msgid "Displays usage information."
|
msgid "Displays usage information."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#, javascript-format
|
|
||||||
msgid "For information on how to customise the shortcuts please visit %s"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Shortcuts are not available in CLI mode."
|
msgid "Shortcuts are not available in CLI mode."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid ""
|
msgid "Type `help [command]` for more information about a command."
|
||||||
"Type `help [command]` for more information about a command; or type `help "
|
|
||||||
"all` for the complete usage information."
|
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid "The possible commands are:"
|
msgid "The possible commands are:"
|
||||||
@@ -233,19 +234,25 @@ msgid "To exit command line mode, press ESCAPE"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid ""
|
msgid ""
|
||||||
"For the list of keyboard shortcuts and config options, type `help keymap`"
|
"For the complete list of available keyboard shortcuts, type `help shortcuts`"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid "Imports data into Joplin."
|
msgid "Imports an Evernote notebook file (.enex file)."
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#, javascript-format
|
|
||||||
msgid "Source format: %s"
|
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid "Do not ask for confirmation."
|
msgid "Do not ask for confirmation."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#, javascript-format
|
||||||
|
msgid "File \"%s\" will be imported into existing notebook \"%s\". Continue?"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#, javascript-format
|
||||||
|
msgid ""
|
||||||
|
"New notebook \"%s\" will be created and file \"%s\" will be imported into "
|
||||||
|
"it. Continue?"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#, javascript-format
|
#, javascript-format
|
||||||
msgid "Found: %d."
|
msgid "Found: %d."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -332,7 +339,8 @@ msgstr ""
|
|||||||
msgid "Deletes the notebook without asking for confirmation."
|
msgid "Deletes the notebook without asking for confirmation."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid "Delete notebook? All notes within this notebook will also be deleted."
|
#, javascript-format
|
||||||
|
msgid "Delete notebook \"%s\"?"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid "Deletes the notes matching <note-pattern>."
|
msgid "Deletes the notes matching <note-pattern>."
|
||||||
@@ -351,12 +359,7 @@ msgstr ""
|
|||||||
msgid "Searches for the given <pattern> in all the notes."
|
msgid "Searches for the given <pattern> in all the notes."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#, javascript-format
|
msgid "Sets the property <name> of the given <note> to the given [value]."
|
||||||
msgid ""
|
|
||||||
"Sets the property <name> of the given <note> to the given [value]. Possible "
|
|
||||||
"properties are:\n"
|
|
||||||
"\n"
|
|
||||||
"%s"
|
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid "Displays summary about the notes and notebooks."
|
msgid "Displays summary about the notes and notebooks."
|
||||||
@@ -368,24 +371,6 @@ msgstr ""
|
|||||||
msgid "Sync to provided target (defaults to sync.target config value)"
|
msgid "Sync to provided target (defaults to sync.target config value)"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid ""
|
|
||||||
"Authentication was not completed (did not receive an authentication token)."
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid ""
|
|
||||||
"To allow Joplin to synchronise with Dropbox, please follow the steps below:"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Step 1: Open this URL in your browser to authorise the application:"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Step 2: Enter the code provided by Dropbox:"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#, javascript-format
|
|
||||||
msgid "Not authentified with %s. Please provide any missing credentials."
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Synchronisation is already in progress."
|
msgid "Synchronisation is already in progress."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@@ -396,6 +381,10 @@ msgid ""
|
|||||||
"operation."
|
"operation."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
msgid ""
|
||||||
|
"Authentication was not completed (did not receive an authentication token)."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#, javascript-format
|
#, javascript-format
|
||||||
msgid "Synchronisation target: %s (%s)"
|
msgid "Synchronisation target: %s (%s)"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -459,9 +448,6 @@ msgstr ""
|
|||||||
msgid "Possible keys/values:"
|
msgid "Possible keys/values:"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid "Type `joplin help` for usage information."
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Fatal error:"
|
msgid "Fatal error:"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@@ -483,33 +469,6 @@ msgstr ""
|
|||||||
msgid "Search:"
|
msgid "Search:"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid ""
|
|
||||||
"Welcome to Joplin!\n"
|
|
||||||
"\n"
|
|
||||||
"Type `:help shortcuts` for the list of keyboard shortcuts, or just `:help` "
|
|
||||||
"for usage information.\n"
|
|
||||||
"\n"
|
|
||||||
"For example, to create a notebook press `mb`; to create a note press `mn`."
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid ""
|
|
||||||
"One or more items are currently encrypted and you may need to supply a "
|
|
||||||
"master password. To do so please type `e2ee decrypt`. If you have already "
|
|
||||||
"supplied the password, the encrypted items are being decrypted in the "
|
|
||||||
"background and will be available soon."
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#, javascript-format
|
|
||||||
msgid "Exporting to \"%s\" as \"%s\" format. Please wait..."
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#, javascript-format
|
|
||||||
msgid "Importing from \"%s\" as \"%s\" format. Please wait..."
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "PDF File"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "File"
|
msgid "File"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@@ -522,17 +481,10 @@ msgstr ""
|
|||||||
msgid "New notebook"
|
msgid "New notebook"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid "Import"
|
msgid "Import Evernote notes"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid "Export"
|
msgid "Evernote Export Files"
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Print"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#, javascript-format
|
|
||||||
msgid "Hide %s"
|
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid "Quit"
|
msgid "Quit"
|
||||||
@@ -553,22 +505,10 @@ msgstr ""
|
|||||||
msgid "Search in all the notes"
|
msgid "Search in all the notes"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid "View"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Toggle editor layout"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Tools"
|
msgid "Tools"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid "Synchronisation status"
|
msgid "Options"
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Encryption options"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "General Options"
|
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid "Help"
|
msgid "Help"
|
||||||
@@ -577,12 +517,6 @@ msgstr ""
|
|||||||
msgid "Website and documentation"
|
msgid "Website and documentation"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid "Make a donation"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Check for updates..."
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "About Joplin"
|
msgid "About Joplin"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@@ -590,113 +524,12 @@ msgstr ""
|
|||||||
msgid "%s %s (%s, %s)"
|
msgid "%s %s (%s, %s)"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#, javascript-format
|
|
||||||
msgid "Open %s"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Exit"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "OK"
|
msgid "OK"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid "Cancel"
|
msgid "Cancel"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid "Current version is up-to-date."
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "An update is available, do you want to download it now?"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Yes"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "No"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Check synchronisation configuration"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#, javascript-format
|
|
||||||
msgid "Notes and settings are stored in: %s"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Save"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Submit"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid ""
|
|
||||||
"Disabling encryption means *all* your notes and attachments are going to be "
|
|
||||||
"re-synchronised and sent unencrypted to the sync target. Do you wish to "
|
|
||||||
"continue?"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid ""
|
|
||||||
"Enabling encryption means *all* your notes and attachments are going to be "
|
|
||||||
"re-synchronised and sent encrypted to the sync target. Do not lose the "
|
|
||||||
"password as, for security purposes, this will be the *only* way to decrypt "
|
|
||||||
"the data! To enable encryption, please enter your password below."
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Disable encryption"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Enable encryption"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Master Keys"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Active"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "ID"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Source"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Created"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Updated"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Password"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Password OK"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid ""
|
|
||||||
"Note: Only one master key is going to be used for encryption (the one marked "
|
|
||||||
"as \"active\"). Any of the keys might be used for decryption, depending on "
|
|
||||||
"how the notes or notebooks were originally encrypted."
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Missing Master Keys"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid ""
|
|
||||||
"The master keys with these IDs are used to encrypt some of your items, "
|
|
||||||
"however the application does not currently have access to them. It is likely "
|
|
||||||
"they will eventually be downloaded via synchronisation."
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid ""
|
|
||||||
"For more information about End-To-End Encryption (E2EE) and advices on how "
|
|
||||||
"to enable it please check the documentation"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Status"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Encryption is:"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Back"
|
msgid "Back"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@@ -708,9 +541,15 @@ msgstr ""
|
|||||||
msgid "Please create a notebook first."
|
msgid "Please create a notebook first."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Note title:"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
msgid "Please create a notebook first"
|
msgid "Please create a notebook first"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "To-do title:"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
msgid "Notebook title:"
|
msgid "Notebook title:"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@@ -729,21 +568,6 @@ msgstr ""
|
|||||||
msgid "Layout"
|
msgid "Layout"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid "Search..."
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Some items cannot be synchronised."
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "View them now"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Some items cannot be decrypted."
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Set the password"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Add or remove tags"
|
msgid "Add or remove tags"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@@ -759,16 +583,6 @@ msgstr ""
|
|||||||
msgid "No notes in here. Create one by clicking on \"New note\"."
|
msgid "No notes in here. Create one by clicking on \"New note\"."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid ""
|
|
||||||
"There is currently no notebook. Create one by clicking on \"New notebook\"."
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Open..."
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Save as..."
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#, javascript-format
|
#, javascript-format
|
||||||
msgid "Unsupported link or message: %s"
|
msgid "Unsupported link or message: %s"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -776,28 +590,9 @@ msgstr ""
|
|||||||
msgid "Attach file"
|
msgid "Attach file"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid "Tags"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Set alarm"
|
msgid "Set alarm"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#, javascript-format
|
|
||||||
msgid ""
|
|
||||||
"This note has no content. Click on \"%s\" to toggle the editor and edit the "
|
|
||||||
"note."
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "to-do"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "note"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#, javascript-format
|
|
||||||
msgid "Creating new %s..."
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Refresh"
|
msgid "Refresh"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@@ -807,16 +602,10 @@ msgstr ""
|
|||||||
msgid "OneDrive Login"
|
msgid "OneDrive Login"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid "Dropbox Login"
|
msgid "Import"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid "Options"
|
msgid "Delete notebook?"
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Synchronisation Status"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Encryption Options"
|
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid "Remove this tag from all the notes?"
|
msgid "Remove this tag from all the notes?"
|
||||||
@@ -834,7 +623,10 @@ msgstr ""
|
|||||||
msgid "Notebooks"
|
msgid "Notebooks"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid "Please select where the sync status should be exported to"
|
msgid "Tags"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Searches"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#, javascript-format
|
#, javascript-format
|
||||||
@@ -845,24 +637,15 @@ msgstr ""
|
|||||||
msgid "Unknown flag: %s"
|
msgid "Unknown flag: %s"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid "Dropbox"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "File system"
|
msgid "File system"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid "Nextcloud"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "OneDrive"
|
msgid "OneDrive"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid "OneDrive Dev (For testing only)"
|
msgid "OneDrive Dev (For testing only)"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid "WebDAV"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#, javascript-format
|
#, javascript-format
|
||||||
msgid "Unknown log level: %s"
|
msgid "Unknown log level: %s"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -914,11 +697,7 @@ msgid "Deleted remote items: %d."
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#, javascript-format
|
#, javascript-format
|
||||||
msgid "Fetched items: %d/%d."
|
msgid "State: \"%s\"."
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#, javascript-format
|
|
||||||
msgid "State: %s."
|
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid "Cancelling..."
|
msgid "Cancelling..."
|
||||||
@@ -928,26 +707,10 @@ msgstr ""
|
|||||||
msgid "Completed: %s"
|
msgid "Completed: %s"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#, javascript-format
|
|
||||||
msgid "Last error: %s"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Idle"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "In progress"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#, javascript-format
|
#, javascript-format
|
||||||
msgid "Synchronisation is already in progress. State: %s"
|
msgid "Synchronisation is already in progress. State: %s"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid "Encrypted"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Encrypted items cannot be modified"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Conflicts"
|
msgid "Conflicts"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@@ -973,6 +736,14 @@ msgstr ""
|
|||||||
msgid "Cannot move note to \"%s\" notebook"
|
msgid "Cannot move note to \"%s\" notebook"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "File system synchronisation target directory"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid ""
|
||||||
|
"The path to synchronise with when file system synchronisation is enabled. "
|
||||||
|
"See `sync.target`."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
msgid "Text editor"
|
msgid "Text editor"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@@ -999,50 +770,18 @@ msgstr ""
|
|||||||
msgid "Dark"
|
msgid "Dark"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid "Uncompleted to-dos on top"
|
msgid "Show uncompleted todos on top of the lists"
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Sort notes by"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Reverse sort order"
|
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid "Save geo-location with notes"
|
msgid "Save geo-location with notes"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid "When creating a new to-do:"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Focus title"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Focus body"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "When creating a new note:"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Show tray icon"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Global zoom percentage"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Editor font family"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid ""
|
|
||||||
"The font name will not be checked. If incorrect or empty, it will default to "
|
|
||||||
"a generic monospace font."
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Automatically update the application"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Synchronisation interval"
|
msgid "Synchronisation interval"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Disabled"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#, javascript-format
|
#, javascript-format
|
||||||
msgid "%d minutes"
|
msgid "%d minutes"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -1055,6 +794,9 @@ msgstr ""
|
|||||||
msgid "%d hours"
|
msgid "%d hours"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Automatically update the application"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
msgid "Show advanced options"
|
msgid "Show advanced options"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@@ -1062,88 +804,14 @@ msgid "Synchronisation target"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid ""
|
msgid ""
|
||||||
"The target to synchonise to. Each sync target may have additional parameters "
|
"The target to synchonise to. If synchronising with the file system, set "
|
||||||
"which are named as `sync.NUM.NAME` (all documented below)."
|
"`sync.2.path` to specify the target directory."
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Directory to synchronise with (absolute path)"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid ""
|
|
||||||
"The path to synchronise with when file system synchronisation is enabled. "
|
|
||||||
"See `sync.target`."
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Nextcloud WebDAV URL"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Nextcloud username"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Nextcloud password"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "WebDAV URL"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "WebDAV username"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "WebDAV password"
|
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#, javascript-format
|
#, javascript-format
|
||||||
msgid "Invalid option value: \"%s\". Possible values are: %s."
|
msgid "Invalid option value: \"%s\". Possible values are: %s."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid "Joplin Export File"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Markdown"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Joplin Export Directory"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Evernote Export File"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Directory"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#, javascript-format
|
|
||||||
msgid "Cannot load \"%s\" module for format \"%s\""
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#, javascript-format
|
|
||||||
msgid "Please specify import format for %s"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#, javascript-format
|
|
||||||
msgid ""
|
|
||||||
"This item is currently encrypted: %s \"%s\". Please wait for all items to be "
|
|
||||||
"decrypted and try again."
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "There is no data to export."
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Please specify the notebook where the notes should be imported to."
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Items that cannot be synchronised"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#, javascript-format
|
|
||||||
msgid "%s (%s): %s"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid ""
|
|
||||||
"These items will remain on the device but will not be uploaded to the sync "
|
|
||||||
"target. In order to find these items, either search for the title or the ID "
|
|
||||||
"(which is displayed in brackets above)."
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Sync status (synced items / total items)"
|
msgid "Sync status (synced items / total items)"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@@ -1186,10 +854,10 @@ msgstr ""
|
|||||||
msgid "Log"
|
msgid "Log"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid "Export Debug Report"
|
msgid "Status"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid "Encryption Config"
|
msgid "Export Debug Report"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid "Configuration"
|
msgid "Configuration"
|
||||||
@@ -1202,9 +870,6 @@ msgstr ""
|
|||||||
msgid "Move %d notes to notebook \"%s\"?"
|
msgid "Move %d notes to notebook \"%s\"?"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid "Press to set the decryption password."
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Select date"
|
msgid "Select date"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@@ -1214,40 +879,6 @@ msgstr ""
|
|||||||
msgid "Cancel synchronisation"
|
msgid "Cancel synchronisation"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid "New tags:"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Type new tags or select from list"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Joplin website"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Login with Dropbox"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#, javascript-format
|
|
||||||
msgid "Master Key %s"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#, javascript-format
|
|
||||||
msgid "Created: %s"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Password:"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Password cannot be empty"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Enable"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid ""
|
|
||||||
"For more information about End-To-End Encryption (E2EE) and advices on how "
|
|
||||||
"to enable it please check the documentation:"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#, javascript-format
|
#, javascript-format
|
||||||
msgid "The notebook could not be saved: %s"
|
msgid "The notebook could not be saved: %s"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -1255,12 +886,6 @@ msgstr ""
|
|||||||
msgid "Edit notebook"
|
msgid "Edit notebook"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid "Show all"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Errors only"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "This note has been modified:"
|
msgid "This note has been modified:"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@@ -1301,9 +926,6 @@ msgstr ""
|
|||||||
msgid "Login with OneDrive"
|
msgid "Login with OneDrive"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid "Search"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid ""
|
msgid ""
|
||||||
"Click on the (+) button to create a new note or notebook. Click on the side "
|
"Click on the (+) button to create a new note or notebook. Click on the side "
|
||||||
"menu to access your existing notebooks."
|
"menu to access your existing notebooks."
|
||||||
|
1002
CliClient/locales/es_CR.po
Normal file
@@ -15,12 +15,63 @@ msgstr ""
|
|||||||
"Content-Type: text/plain; charset=CHARSET\n"
|
"Content-Type: text/plain; charset=CHARSET\n"
|
||||||
"Content-Transfer-Encoding: 8bit\n"
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
|
|
||||||
|
msgid "Give focus to next pane"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Give focus to previous pane"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Enter command line mode"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Exit command line mode"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Edit the selected note"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Cancel the current command."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Exit the application."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Delete the currently selected note or notebook."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
msgid "To delete a tag, untag the associated notes."
|
msgid "To delete a tag, untag the associated notes."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid "Please select the note or notebook to be deleted first."
|
msgid "Please select the note or notebook to be deleted first."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Set a to-do as completed / not completed"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "[t]oggle [c]onsole between maximized/minimized/hidden/visible."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Search"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "[t]oggle note [m]etadata."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "[M]ake a new [n]ote"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "[M]ake a new [t]odo"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "[M]ake a new note[b]ook"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Copy ([Y]ank) the [n]ote to a notebook."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Move the note to a notebook."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
msgid "Press Ctrl+D or type \"exit\" to exit the application"
|
msgid "Press Ctrl+D or type \"exit\" to exit the application"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@@ -49,17 +100,10 @@ msgstr ""
|
|||||||
msgid "Cancelling background synchronisation... Please wait."
|
msgid "Cancelling background synchronisation... Please wait."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#, javascript-format
|
|
||||||
msgid "No such command: %s"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#, javascript-format
|
#, javascript-format
|
||||||
msgid "The command \"%s\" is only available in GUI mode"
|
msgid "The command \"%s\" is only available in GUI mode"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid "Cannot change encrypted item"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#, javascript-format
|
#, javascript-format
|
||||||
msgid "Missing required argument: %s"
|
msgid "Missing required argument: %s"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -117,35 +161,6 @@ msgstr ""
|
|||||||
msgid "Note is not a to-do: \"%s\""
|
msgid "Note is not a to-do: \"%s\""
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid ""
|
|
||||||
"Manages E2EE configuration. Commands are `enable`, `disable`, `decrypt`, "
|
|
||||||
"`status` and `target-status`."
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Enter master password:"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Operation cancelled"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid ""
|
|
||||||
"Starting decryption... Please wait as it may take several minutes depending "
|
|
||||||
"on how much there is to decrypt."
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Completed decryption."
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Enabled"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Disabled"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#, javascript-format
|
|
||||||
msgid "Encryption is: %s"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Edit note."
|
msgid "Edit note."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@@ -163,10 +178,6 @@ msgstr ""
|
|||||||
msgid "Starting to edit note. Close the editor to get back to the prompt."
|
msgid "Starting to edit note. Close the editor to get back to the prompt."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#, javascript-format
|
|
||||||
msgid "Error opening note in editor: %s"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Note has been saved."
|
msgid "Note has been saved."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@@ -174,14 +185,10 @@ msgid "Exits the application."
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid ""
|
msgid ""
|
||||||
"Exports Joplin data to the given path. By default, it will export the "
|
"Exports Joplin data to the given directory. By default, it will export the "
|
||||||
"complete database including notebooks, notes, tags and resources."
|
"complete database including notebooks, notes, tags and resources."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#, javascript-format
|
|
||||||
msgid "Destination format: %s"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Exports only the given note."
|
msgid "Exports only the given note."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@@ -194,16 +201,10 @@ msgstr ""
|
|||||||
msgid "Displays usage information."
|
msgid "Displays usage information."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#, javascript-format
|
|
||||||
msgid "For information on how to customise the shortcuts please visit %s"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Shortcuts are not available in CLI mode."
|
msgid "Shortcuts are not available in CLI mode."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid ""
|
msgid "Type `help [command]` for more information about a command."
|
||||||
"Type `help [command]` for more information about a command; or type `help "
|
|
||||||
"all` for the complete usage information."
|
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid "The possible commands are:"
|
msgid "The possible commands are:"
|
||||||
@@ -233,19 +234,25 @@ msgid "To exit command line mode, press ESCAPE"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid ""
|
msgid ""
|
||||||
"For the list of keyboard shortcuts and config options, type `help keymap`"
|
"For the complete list of available keyboard shortcuts, type `help shortcuts`"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid "Imports data into Joplin."
|
msgid "Imports an Evernote notebook file (.enex file)."
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#, javascript-format
|
|
||||||
msgid "Source format: %s"
|
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid "Do not ask for confirmation."
|
msgid "Do not ask for confirmation."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#, javascript-format
|
||||||
|
msgid "File \"%s\" will be imported into existing notebook \"%s\". Continue?"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#, javascript-format
|
||||||
|
msgid ""
|
||||||
|
"New notebook \"%s\" will be created and file \"%s\" will be imported into "
|
||||||
|
"it. Continue?"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#, javascript-format
|
#, javascript-format
|
||||||
msgid "Found: %d."
|
msgid "Found: %d."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -332,7 +339,8 @@ msgstr ""
|
|||||||
msgid "Deletes the notebook without asking for confirmation."
|
msgid "Deletes the notebook without asking for confirmation."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid "Delete notebook? All notes within this notebook will also be deleted."
|
#, javascript-format
|
||||||
|
msgid "Delete notebook \"%s\"?"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid "Deletes the notes matching <note-pattern>."
|
msgid "Deletes the notes matching <note-pattern>."
|
||||||
@@ -351,12 +359,7 @@ msgstr ""
|
|||||||
msgid "Searches for the given <pattern> in all the notes."
|
msgid "Searches for the given <pattern> in all the notes."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#, javascript-format
|
msgid "Sets the property <name> of the given <note> to the given [value]."
|
||||||
msgid ""
|
|
||||||
"Sets the property <name> of the given <note> to the given [value]. Possible "
|
|
||||||
"properties are:\n"
|
|
||||||
"\n"
|
|
||||||
"%s"
|
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid "Displays summary about the notes and notebooks."
|
msgid "Displays summary about the notes and notebooks."
|
||||||
@@ -368,24 +371,6 @@ msgstr ""
|
|||||||
msgid "Sync to provided target (defaults to sync.target config value)"
|
msgid "Sync to provided target (defaults to sync.target config value)"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid ""
|
|
||||||
"Authentication was not completed (did not receive an authentication token)."
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid ""
|
|
||||||
"To allow Joplin to synchronise with Dropbox, please follow the steps below:"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Step 1: Open this URL in your browser to authorise the application:"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Step 2: Enter the code provided by Dropbox:"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#, javascript-format
|
|
||||||
msgid "Not authentified with %s. Please provide any missing credentials."
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Synchronisation is already in progress."
|
msgid "Synchronisation is already in progress."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@@ -396,6 +381,10 @@ msgid ""
|
|||||||
"operation."
|
"operation."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
msgid ""
|
||||||
|
"Authentication was not completed (did not receive an authentication token)."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#, javascript-format
|
#, javascript-format
|
||||||
msgid "Synchronisation target: %s (%s)"
|
msgid "Synchronisation target: %s (%s)"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -459,9 +448,6 @@ msgstr ""
|
|||||||
msgid "Possible keys/values:"
|
msgid "Possible keys/values:"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid "Type `joplin help` for usage information."
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Fatal error:"
|
msgid "Fatal error:"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@@ -483,33 +469,6 @@ msgstr ""
|
|||||||
msgid "Search:"
|
msgid "Search:"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid ""
|
|
||||||
"Welcome to Joplin!\n"
|
|
||||||
"\n"
|
|
||||||
"Type `:help shortcuts` for the list of keyboard shortcuts, or just `:help` "
|
|
||||||
"for usage information.\n"
|
|
||||||
"\n"
|
|
||||||
"For example, to create a notebook press `mb`; to create a note press `mn`."
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid ""
|
|
||||||
"One or more items are currently encrypted and you may need to supply a "
|
|
||||||
"master password. To do so please type `e2ee decrypt`. If you have already "
|
|
||||||
"supplied the password, the encrypted items are being decrypted in the "
|
|
||||||
"background and will be available soon."
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#, javascript-format
|
|
||||||
msgid "Exporting to \"%s\" as \"%s\" format. Please wait..."
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#, javascript-format
|
|
||||||
msgid "Importing from \"%s\" as \"%s\" format. Please wait..."
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "PDF File"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "File"
|
msgid "File"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@@ -522,17 +481,10 @@ msgstr ""
|
|||||||
msgid "New notebook"
|
msgid "New notebook"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid "Import"
|
msgid "Import Evernote notes"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid "Export"
|
msgid "Evernote Export Files"
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Print"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#, javascript-format
|
|
||||||
msgid "Hide %s"
|
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid "Quit"
|
msgid "Quit"
|
||||||
@@ -553,22 +505,10 @@ msgstr ""
|
|||||||
msgid "Search in all the notes"
|
msgid "Search in all the notes"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid "View"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Toggle editor layout"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Tools"
|
msgid "Tools"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid "Synchronisation status"
|
msgid "Options"
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Encryption options"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "General Options"
|
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid "Help"
|
msgid "Help"
|
||||||
@@ -577,12 +517,6 @@ msgstr ""
|
|||||||
msgid "Website and documentation"
|
msgid "Website and documentation"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid "Make a donation"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Check for updates..."
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "About Joplin"
|
msgid "About Joplin"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@@ -590,113 +524,12 @@ msgstr ""
|
|||||||
msgid "%s %s (%s, %s)"
|
msgid "%s %s (%s, %s)"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#, javascript-format
|
|
||||||
msgid "Open %s"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Exit"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "OK"
|
msgid "OK"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid "Cancel"
|
msgid "Cancel"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid "Current version is up-to-date."
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "An update is available, do you want to download it now?"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Yes"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "No"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Check synchronisation configuration"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#, javascript-format
|
|
||||||
msgid "Notes and settings are stored in: %s"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Save"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Submit"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid ""
|
|
||||||
"Disabling encryption means *all* your notes and attachments are going to be "
|
|
||||||
"re-synchronised and sent unencrypted to the sync target. Do you wish to "
|
|
||||||
"continue?"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid ""
|
|
||||||
"Enabling encryption means *all* your notes and attachments are going to be "
|
|
||||||
"re-synchronised and sent encrypted to the sync target. Do not lose the "
|
|
||||||
"password as, for security purposes, this will be the *only* way to decrypt "
|
|
||||||
"the data! To enable encryption, please enter your password below."
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Disable encryption"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Enable encryption"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Master Keys"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Active"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "ID"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Source"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Created"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Updated"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Password"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Password OK"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid ""
|
|
||||||
"Note: Only one master key is going to be used for encryption (the one marked "
|
|
||||||
"as \"active\"). Any of the keys might be used for decryption, depending on "
|
|
||||||
"how the notes or notebooks were originally encrypted."
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Missing Master Keys"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid ""
|
|
||||||
"The master keys with these IDs are used to encrypt some of your items, "
|
|
||||||
"however the application does not currently have access to them. It is likely "
|
|
||||||
"they will eventually be downloaded via synchronisation."
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid ""
|
|
||||||
"For more information about End-To-End Encryption (E2EE) and advices on how "
|
|
||||||
"to enable it please check the documentation"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Status"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Encryption is:"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Back"
|
msgid "Back"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@@ -708,9 +541,15 @@ msgstr ""
|
|||||||
msgid "Please create a notebook first."
|
msgid "Please create a notebook first."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Note title:"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
msgid "Please create a notebook first"
|
msgid "Please create a notebook first"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "To-do title:"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
msgid "Notebook title:"
|
msgid "Notebook title:"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@@ -729,21 +568,6 @@ msgstr ""
|
|||||||
msgid "Layout"
|
msgid "Layout"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid "Search..."
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Some items cannot be synchronised."
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "View them now"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Some items cannot be decrypted."
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Set the password"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Add or remove tags"
|
msgid "Add or remove tags"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@@ -759,16 +583,6 @@ msgstr ""
|
|||||||
msgid "No notes in here. Create one by clicking on \"New note\"."
|
msgid "No notes in here. Create one by clicking on \"New note\"."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid ""
|
|
||||||
"There is currently no notebook. Create one by clicking on \"New notebook\"."
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Open..."
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Save as..."
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#, javascript-format
|
#, javascript-format
|
||||||
msgid "Unsupported link or message: %s"
|
msgid "Unsupported link or message: %s"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -776,28 +590,9 @@ msgstr ""
|
|||||||
msgid "Attach file"
|
msgid "Attach file"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid "Tags"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Set alarm"
|
msgid "Set alarm"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#, javascript-format
|
|
||||||
msgid ""
|
|
||||||
"This note has no content. Click on \"%s\" to toggle the editor and edit the "
|
|
||||||
"note."
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "to-do"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "note"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#, javascript-format
|
|
||||||
msgid "Creating new %s..."
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Refresh"
|
msgid "Refresh"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@@ -807,16 +602,10 @@ msgstr ""
|
|||||||
msgid "OneDrive Login"
|
msgid "OneDrive Login"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid "Dropbox Login"
|
msgid "Import"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid "Options"
|
msgid "Delete notebook?"
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Synchronisation Status"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Encryption Options"
|
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid "Remove this tag from all the notes?"
|
msgid "Remove this tag from all the notes?"
|
||||||
@@ -834,7 +623,10 @@ msgstr ""
|
|||||||
msgid "Notebooks"
|
msgid "Notebooks"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid "Please select where the sync status should be exported to"
|
msgid "Tags"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Searches"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#, javascript-format
|
#, javascript-format
|
||||||
@@ -845,24 +637,15 @@ msgstr ""
|
|||||||
msgid "Unknown flag: %s"
|
msgid "Unknown flag: %s"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid "Dropbox"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "File system"
|
msgid "File system"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid "Nextcloud"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "OneDrive"
|
msgid "OneDrive"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid "OneDrive Dev (For testing only)"
|
msgid "OneDrive Dev (For testing only)"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid "WebDAV"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#, javascript-format
|
#, javascript-format
|
||||||
msgid "Unknown log level: %s"
|
msgid "Unknown log level: %s"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -914,11 +697,7 @@ msgid "Deleted remote items: %d."
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#, javascript-format
|
#, javascript-format
|
||||||
msgid "Fetched items: %d/%d."
|
msgid "State: \"%s\"."
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#, javascript-format
|
|
||||||
msgid "State: %s."
|
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid "Cancelling..."
|
msgid "Cancelling..."
|
||||||
@@ -928,26 +707,10 @@ msgstr ""
|
|||||||
msgid "Completed: %s"
|
msgid "Completed: %s"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#, javascript-format
|
|
||||||
msgid "Last error: %s"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Idle"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "In progress"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#, javascript-format
|
#, javascript-format
|
||||||
msgid "Synchronisation is already in progress. State: %s"
|
msgid "Synchronisation is already in progress. State: %s"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid "Encrypted"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Encrypted items cannot be modified"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Conflicts"
|
msgid "Conflicts"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@@ -973,6 +736,14 @@ msgstr ""
|
|||||||
msgid "Cannot move note to \"%s\" notebook"
|
msgid "Cannot move note to \"%s\" notebook"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "File system synchronisation target directory"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid ""
|
||||||
|
"The path to synchronise with when file system synchronisation is enabled. "
|
||||||
|
"See `sync.target`."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
msgid "Text editor"
|
msgid "Text editor"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@@ -999,50 +770,18 @@ msgstr ""
|
|||||||
msgid "Dark"
|
msgid "Dark"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid "Uncompleted to-dos on top"
|
msgid "Show uncompleted todos on top of the lists"
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Sort notes by"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Reverse sort order"
|
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid "Save geo-location with notes"
|
msgid "Save geo-location with notes"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid "When creating a new to-do:"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Focus title"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Focus body"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "When creating a new note:"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Show tray icon"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Global zoom percentage"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Editor font family"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid ""
|
|
||||||
"The font name will not be checked. If incorrect or empty, it will default to "
|
|
||||||
"a generic monospace font."
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Automatically update the application"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Synchronisation interval"
|
msgid "Synchronisation interval"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Disabled"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#, javascript-format
|
#, javascript-format
|
||||||
msgid "%d minutes"
|
msgid "%d minutes"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -1055,6 +794,9 @@ msgstr ""
|
|||||||
msgid "%d hours"
|
msgid "%d hours"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Automatically update the application"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
msgid "Show advanced options"
|
msgid "Show advanced options"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@@ -1062,88 +804,14 @@ msgid "Synchronisation target"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid ""
|
msgid ""
|
||||||
"The target to synchonise to. Each sync target may have additional parameters "
|
"The target to synchonise to. If synchronising with the file system, set "
|
||||||
"which are named as `sync.NUM.NAME` (all documented below)."
|
"`sync.2.path` to specify the target directory."
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Directory to synchronise with (absolute path)"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid ""
|
|
||||||
"The path to synchronise with when file system synchronisation is enabled. "
|
|
||||||
"See `sync.target`."
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Nextcloud WebDAV URL"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Nextcloud username"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Nextcloud password"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "WebDAV URL"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "WebDAV username"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "WebDAV password"
|
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#, javascript-format
|
#, javascript-format
|
||||||
msgid "Invalid option value: \"%s\". Possible values are: %s."
|
msgid "Invalid option value: \"%s\". Possible values are: %s."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid "Joplin Export File"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Markdown"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Joplin Export Directory"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Evernote Export File"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Directory"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#, javascript-format
|
|
||||||
msgid "Cannot load \"%s\" module for format \"%s\""
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#, javascript-format
|
|
||||||
msgid "Please specify import format for %s"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#, javascript-format
|
|
||||||
msgid ""
|
|
||||||
"This item is currently encrypted: %s \"%s\". Please wait for all items to be "
|
|
||||||
"decrypted and try again."
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "There is no data to export."
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Please specify the notebook where the notes should be imported to."
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Items that cannot be synchronised"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#, javascript-format
|
|
||||||
msgid "%s (%s): %s"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid ""
|
|
||||||
"These items will remain on the device but will not be uploaded to the sync "
|
|
||||||
"target. In order to find these items, either search for the title or the ID "
|
|
||||||
"(which is displayed in brackets above)."
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Sync status (synced items / total items)"
|
msgid "Sync status (synced items / total items)"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@@ -1186,10 +854,10 @@ msgstr ""
|
|||||||
msgid "Log"
|
msgid "Log"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid "Export Debug Report"
|
msgid "Status"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid "Encryption Config"
|
msgid "Export Debug Report"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid "Configuration"
|
msgid "Configuration"
|
||||||
@@ -1202,9 +870,6 @@ msgstr ""
|
|||||||
msgid "Move %d notes to notebook \"%s\"?"
|
msgid "Move %d notes to notebook \"%s\"?"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid "Press to set the decryption password."
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Select date"
|
msgid "Select date"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@@ -1214,40 +879,6 @@ msgstr ""
|
|||||||
msgid "Cancel synchronisation"
|
msgid "Cancel synchronisation"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid "New tags:"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Type new tags or select from list"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Joplin website"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Login with Dropbox"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#, javascript-format
|
|
||||||
msgid "Master Key %s"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#, javascript-format
|
|
||||||
msgid "Created: %s"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Password:"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Password cannot be empty"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Enable"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid ""
|
|
||||||
"For more information about End-To-End Encryption (E2EE) and advices on how "
|
|
||||||
"to enable it please check the documentation:"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#, javascript-format
|
#, javascript-format
|
||||||
msgid "The notebook could not be saved: %s"
|
msgid "The notebook could not be saved: %s"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -1255,12 +886,6 @@ msgstr ""
|
|||||||
msgid "Edit notebook"
|
msgid "Edit notebook"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid "Show all"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Errors only"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "This note has been modified:"
|
msgid "This note has been modified:"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@@ -1301,9 +926,6 @@ msgstr ""
|
|||||||
msgid "Login with OneDrive"
|
msgid "Login with OneDrive"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid "Search"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid ""
|
msgid ""
|
||||||
"Click on the (+) button to create a new note or notebook. Click on the side "
|
"Click on the (+) button to create a new note or notebook. Click on the side "
|
||||||
"menu to access your existing notebooks."
|
"menu to access your existing notebooks."
|
||||||
|
144
CliClient/package-lock.json
generated
@@ -1,13 +1,13 @@
|
|||||||
{
|
{
|
||||||
"name": "joplin",
|
"name": "joplin",
|
||||||
"version": "1.0.106",
|
"version": "0.10.77",
|
||||||
"lockfileVersion": 1,
|
"lockfileVersion": 1,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"ajv": {
|
"ajv": {
|
||||||
"version": "5.5.2",
|
"version": "5.3.0",
|
||||||
"resolved": "https://registry.npmjs.org/ajv/-/ajv-5.5.2.tgz",
|
"resolved": "https://registry.npmjs.org/ajv/-/ajv-5.3.0.tgz",
|
||||||
"integrity": "sha1-c7Xuyj+rZT49P5Qis0GtQiBdyWU=",
|
"integrity": "sha1-RBT/dKUIecII7l/cgm4ywwNUnto=",
|
||||||
"requires": {
|
"requires": {
|
||||||
"co": "4.6.0",
|
"co": "4.6.0",
|
||||||
"fast-deep-equal": "1.0.0",
|
"fast-deep-equal": "1.0.0",
|
||||||
@@ -64,11 +64,6 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"async-mutex": {
|
|
||||||
"version": "0.1.3",
|
|
||||||
"resolved": "https://registry.npmjs.org/async-mutex/-/async-mutex-0.1.3.tgz",
|
|
||||||
"integrity": "sha1-Cq0hEjaXlas/F+M3RFVtLs9UdWY="
|
|
||||||
},
|
|
||||||
"asynckit": {
|
"asynckit": {
|
||||||
"version": "0.4.0",
|
"version": "0.4.0",
|
||||||
"resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
|
"resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
|
||||||
@@ -90,11 +85,6 @@
|
|||||||
"integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=",
|
"integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"base-64": {
|
|
||||||
"version": "0.1.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/base-64/-/base-64-0.1.0.tgz",
|
|
||||||
"integrity": "sha1-eAqZyE59YAJgNhURxId2E78k9rs="
|
|
||||||
},
|
|
||||||
"bcrypt-pbkdf": {
|
"bcrypt-pbkdf": {
|
||||||
"version": "1.0.1",
|
"version": "1.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.1.tgz",
|
||||||
@@ -207,11 +197,6 @@
|
|||||||
"delayed-stream": "1.0.0"
|
"delayed-stream": "1.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"compare-version": {
|
|
||||||
"version": "0.1.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/compare-version/-/compare-version-0.1.2.tgz",
|
|
||||||
"integrity": "sha1-AWLsLZNR9d3VmpICy6k1NmpyUIA="
|
|
||||||
},
|
|
||||||
"concat-map": {
|
"concat-map": {
|
||||||
"version": "0.0.1",
|
"version": "0.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
|
||||||
@@ -426,23 +411,15 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"fs-extra": {
|
"fs-extra": {
|
||||||
"version": "5.0.0",
|
"version": "3.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-5.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-3.0.1.tgz",
|
||||||
"integrity": "sha512-66Pm4RYbjzdyeuqudYqhFiNBbCIuI9kgRqLPSHIlXHidW8NIQtVdkM1yeZ4lXwuhbTETv3EUGMNHAAw6hiundQ==",
|
"integrity": "sha1-N5TzeMWLNC6n27sjCVEJxLO2IpE=",
|
||||||
"requires": {
|
"requires": {
|
||||||
"graceful-fs": "4.1.11",
|
"graceful-fs": "4.1.11",
|
||||||
"jsonfile": "4.0.0",
|
"jsonfile": "3.0.1",
|
||||||
"universalify": "0.1.1"
|
"universalify": "0.1.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"fs-minipass": {
|
|
||||||
"version": "1.2.5",
|
|
||||||
"resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-1.2.5.tgz",
|
|
||||||
"integrity": "sha512-JhBl0skXjUPCFH7x6x61gQxrKyXsxB5gcgePLZCwfyCGGsTISMoIeObbrvVeP6Xmyaudw4TT43qV2Gz+iyd2oQ==",
|
|
||||||
"requires": {
|
|
||||||
"minipass": "2.2.1"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"fs.realpath": {
|
"fs.realpath": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
|
||||||
@@ -460,7 +437,7 @@
|
|||||||
"ndarray": "1.0.18",
|
"ndarray": "1.0.18",
|
||||||
"ndarray-pack": "1.2.1",
|
"ndarray-pack": "1.2.1",
|
||||||
"node-bitmap": "0.0.1",
|
"node-bitmap": "0.0.1",
|
||||||
"omggif": "1.0.9",
|
"omggif": "1.0.8",
|
||||||
"parse-data-uri": "0.2.0",
|
"parse-data-uri": "0.2.0",
|
||||||
"pngjs": "2.3.1",
|
"pngjs": "2.3.1",
|
||||||
"request": "2.83.0",
|
"request": "2.83.0",
|
||||||
@@ -512,7 +489,7 @@
|
|||||||
"resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.0.3.tgz",
|
||||||
"integrity": "sha1-ukAsJmGU8VlW7xXg/PJCmT9qff0=",
|
"integrity": "sha1-ukAsJmGU8VlW7xXg/PJCmT9qff0=",
|
||||||
"requires": {
|
"requires": {
|
||||||
"ajv": "5.5.2",
|
"ajv": "5.3.0",
|
||||||
"har-schema": "2.0.0"
|
"har-schema": "2.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -751,9 +728,9 @@
|
|||||||
"integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus="
|
"integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus="
|
||||||
},
|
},
|
||||||
"jsonfile": {
|
"jsonfile": {
|
||||||
"version": "4.0.0",
|
"version": "3.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-3.0.1.tgz",
|
||||||
"integrity": "sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=",
|
"integrity": "sha1-pezG9l9T9mLEQVx2daAzHQmS7GY=",
|
||||||
"requires": {
|
"requires": {
|
||||||
"graceful-fs": "4.1.11"
|
"graceful-fs": "4.1.11"
|
||||||
}
|
}
|
||||||
@@ -866,9 +843,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"minizlib": {
|
"minizlib": {
|
||||||
"version": "1.1.0",
|
"version": "1.0.4",
|
||||||
"resolved": "https://registry.npmjs.org/minizlib/-/minizlib-1.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/minizlib/-/minizlib-1.0.4.tgz",
|
||||||
"integrity": "sha512-4T6Ur/GctZ27nHfpt9THOdRZNgyJ9FZchYO1ceg5S8Q3DNLCKYy44nCZzgCJgcvx2UM8czmqak5BCxJMrq37lA==",
|
"integrity": "sha512-sN4U9tIJtBRwKbwgFh9qJfrPIQ/GGTRr1MGqkgOeMTLy8/lM0FcWU//FqlnZ3Vb7gJ+Mxh3FOg1EklibdajbaQ==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"minipass": "2.2.1"
|
"minipass": "2.2.1"
|
||||||
}
|
}
|
||||||
@@ -971,9 +948,9 @@
|
|||||||
"integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM="
|
"integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM="
|
||||||
},
|
},
|
||||||
"omggif": {
|
"omggif": {
|
||||||
"version": "1.0.9",
|
"version": "1.0.8",
|
||||||
"resolved": "https://registry.npmjs.org/omggif/-/omggif-1.0.9.tgz",
|
"resolved": "https://registry.npmjs.org/omggif/-/omggif-1.0.8.tgz",
|
||||||
"integrity": "sha1-3LcCTazVDFK00wPwSALJHAV8dl8="
|
"integrity": "sha1-F483sqsLPXtG7ToORr0HkLWNNTA="
|
||||||
},
|
},
|
||||||
"once": {
|
"once": {
|
||||||
"version": "1.4.0",
|
"version": "1.4.0",
|
||||||
@@ -983,6 +960,11 @@
|
|||||||
"wrappy": "1.0.2"
|
"wrappy": "1.0.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"os-tmpdir": {
|
||||||
|
"version": "1.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz",
|
||||||
|
"integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ="
|
||||||
|
},
|
||||||
"parse-data-uri": {
|
"parse-data-uri": {
|
||||||
"version": "0.2.0",
|
"version": "0.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/parse-data-uri/-/parse-data-uri-0.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/parse-data-uri/-/parse-data-uri-0.2.0.tgz",
|
||||||
@@ -1063,11 +1045,6 @@
|
|||||||
"strict-uri-encode": "1.1.0"
|
"strict-uri-encode": "1.1.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"querystringify": {
|
|
||||||
"version": "1.0.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/querystringify/-/querystringify-1.0.0.tgz",
|
|
||||||
"integrity": "sha1-YoYkIRLFtxL6ZU5SZlK/ahP/Bcs="
|
|
||||||
},
|
|
||||||
"readable-stream": {
|
"readable-stream": {
|
||||||
"version": "2.3.3",
|
"version": "2.3.3",
|
||||||
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.3.tgz",
|
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.3.tgz",
|
||||||
@@ -1122,11 +1099,6 @@
|
|||||||
"uuid": "3.1.0"
|
"uuid": "3.1.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"requires-port": {
|
|
||||||
"version": "1.0.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz",
|
|
||||||
"integrity": "sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8="
|
|
||||||
},
|
|
||||||
"retry": {
|
"retry": {
|
||||||
"version": "0.10.1",
|
"version": "0.10.1",
|
||||||
"resolved": "https://registry.npmjs.org/retry/-/retry-0.10.1.tgz",
|
"resolved": "https://registry.npmjs.org/retry/-/retry-0.10.1.tgz",
|
||||||
@@ -1164,20 +1136,6 @@
|
|||||||
"semver": "5.4.1",
|
"semver": "5.4.1",
|
||||||
"simple-get": "2.7.0",
|
"simple-get": "2.7.0",
|
||||||
"tar": "3.2.1"
|
"tar": "3.2.1"
|
||||||
},
|
|
||||||
"dependencies": {
|
|
||||||
"tar": {
|
|
||||||
"version": "3.2.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/tar/-/tar-3.2.1.tgz",
|
|
||||||
"integrity": "sha512-ZSzds1E0IqutvMU8HxjMaU8eB7urw2fGwTq88ukDOVuUIh0656l7/P7LiVPxhO5kS4flcRJQk8USG+cghQbTUQ==",
|
|
||||||
"requires": {
|
|
||||||
"chownr": "1.0.1",
|
|
||||||
"minipass": "2.2.1",
|
|
||||||
"minizlib": "1.1.0",
|
|
||||||
"mkdirp": "0.5.1",
|
|
||||||
"yallist": "3.0.2"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"simple-concat": {
|
"simple-concat": {
|
||||||
@@ -1957,9 +1915,9 @@
|
|||||||
"integrity": "sha1-J5siXfHVgrH1TmWt3UNS4Y+qBxM="
|
"integrity": "sha1-J5siXfHVgrH1TmWt3UNS4Y+qBxM="
|
||||||
},
|
},
|
||||||
"string-kit": {
|
"string-kit": {
|
||||||
"version": "0.6.4",
|
"version": "0.6.3",
|
||||||
"resolved": "https://registry.npmjs.org/string-kit/-/string-kit-0.6.4.tgz",
|
"resolved": "https://registry.npmjs.org/string-kit/-/string-kit-0.6.3.tgz",
|
||||||
"integrity": "sha512-imrOojdsXlL6xzfERCxvc/iA9Zwpzbfs+qeP6VB0s0rQVnMc3Nwkyhge0e8Uoayph7PVAwPNmLpohox27G3fgA==",
|
"integrity": "sha512-G2T92klsuE+S9mqdKQyWurFweNQV5X+FRzSKTqYHRdaVUN/4dL6urbYJJ+xb9ep/4XWm+4RNT8j3acncNhFRBg==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"xregexp": "3.2.0"
|
"xregexp": "3.2.0"
|
||||||
}
|
}
|
||||||
@@ -2022,14 +1980,13 @@
|
|||||||
"integrity": "sha1-Kb9hXUqnEhvdiYsi1LP5vE4qoD0="
|
"integrity": "sha1-Kb9hXUqnEhvdiYsi1LP5vE4qoD0="
|
||||||
},
|
},
|
||||||
"tar": {
|
"tar": {
|
||||||
"version": "4.4.0",
|
"version": "3.2.1",
|
||||||
"resolved": "https://registry.npmjs.org/tar/-/tar-4.4.0.tgz",
|
"resolved": "https://registry.npmjs.org/tar/-/tar-3.2.1.tgz",
|
||||||
"integrity": "sha512-gJlTiiErwo96K904FnoYWl+5+FBgS+FimU6GMh66XLdLa55al8+d4jeDfPoGwSNHdtWI5FJP6xurmVqhBuGJpQ==",
|
"integrity": "sha512-ZSzds1E0IqutvMU8HxjMaU8eB7urw2fGwTq88ukDOVuUIh0656l7/P7LiVPxhO5kS4flcRJQk8USG+cghQbTUQ==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"chownr": "1.0.1",
|
"chownr": "1.0.1",
|
||||||
"fs-minipass": "1.2.5",
|
|
||||||
"minipass": "2.2.1",
|
"minipass": "2.2.1",
|
||||||
"minizlib": "1.1.0",
|
"minizlib": "1.0.4",
|
||||||
"mkdirp": "0.5.1",
|
"mkdirp": "0.5.1",
|
||||||
"yallist": "3.0.2"
|
"yallist": "3.0.2"
|
||||||
}
|
}
|
||||||
@@ -2057,15 +2014,15 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"terminal-kit": {
|
"terminal-kit": {
|
||||||
"version": "1.14.3",
|
"version": "1.14.0",
|
||||||
"resolved": "https://registry.npmjs.org/terminal-kit/-/terminal-kit-1.14.3.tgz",
|
"resolved": "https://registry.npmjs.org/terminal-kit/-/terminal-kit-1.14.0.tgz",
|
||||||
"integrity": "sha512-ZHtuElnBhK0IXOYNvQ7eYgaArwEoOv7saQc4Q0Z9p02JeC7iajC20/odV77BKB3jw/Qthvf9mpASf8gNDYv7xQ==",
|
"integrity": "sha512-ir0I2QtcBDSg2w0UvohlqdDpGlS3S2UYBG4NnYKnK/4VywgnbfxgdpXN3el0uCH3OeH6fG38luW7RmDM96FqUw==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"async-kit": "2.2.3",
|
"async-kit": "2.2.3",
|
||||||
"get-pixels": "3.3.0",
|
"get-pixels": "3.3.0",
|
||||||
"ndarray": "1.0.18",
|
"ndarray": "1.0.18",
|
||||||
"nextgen-events": "0.10.2",
|
"nextgen-events": "0.10.2",
|
||||||
"string-kit": "0.6.4",
|
"string-kit": "0.6.3",
|
||||||
"tree-kit": "0.5.26"
|
"tree-kit": "0.5.26"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -2075,16 +2032,16 @@
|
|||||||
"integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU="
|
"integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU="
|
||||||
},
|
},
|
||||||
"tkwidgets": {
|
"tkwidgets": {
|
||||||
"version": "0.5.25",
|
"version": "0.5.20",
|
||||||
"resolved": "https://registry.npmjs.org/tkwidgets/-/tkwidgets-0.5.25.tgz",
|
"resolved": "https://registry.npmjs.org/tkwidgets/-/tkwidgets-0.5.20.tgz",
|
||||||
"integrity": "sha512-f+12QbxNCLg9Jou5JoPJxATGLmzpDAQeM7QRTXvuqdEB/QvPD9+UlPUL7eYJP1QJv2zzT6EIWWbdpDkXPEtzCQ==",
|
"integrity": "sha512-9wGsMrrFJvE/6TKUc0dEFFhwxvZLeNsYOxnpy1JCwyk/hYCEF70nuvk7VvJeG4TPaQBaGKPj6c7pCgdREvz4Jw==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"chalk": "2.3.0",
|
"chalk": "2.3.0",
|
||||||
"emphasize": "1.5.0",
|
"emphasize": "1.5.0",
|
||||||
"node-emoji": "git+https://github.com/laurent22/node-emoji.git#9fa01eac463e94dde1316ef8c53089eeef4973b5",
|
"node-emoji": "git+https://github.com/laurent22/node-emoji.git#9fa01eac463e94dde1316ef8c53089eeef4973b5",
|
||||||
"slice-ansi": "1.0.0",
|
"slice-ansi": "1.0.0",
|
||||||
"string-width": "2.1.1",
|
"string-width": "2.1.1",
|
||||||
"terminal-kit": "1.14.3",
|
"terminal-kit": "1.14.0",
|
||||||
"wrap-ansi": "3.0.1"
|
"wrap-ansi": "3.0.1"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
@@ -2138,15 +2095,6 @@
|
|||||||
"resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.1.tgz",
|
||||||
"integrity": "sha1-+nG63UQ3r0wUiEHjs7Fl+enlkLc="
|
"integrity": "sha1-+nG63UQ3r0wUiEHjs7Fl+enlkLc="
|
||||||
},
|
},
|
||||||
"url-parse": {
|
|
||||||
"version": "1.2.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.2.0.tgz",
|
|
||||||
"integrity": "sha512-DT1XbYAfmQP65M/mE6OALxmXzZ/z1+e5zk2TcSKe/KiYbNGZxgtttzC0mR/sjopbpOXcbniq7eIKmocJnUWlEw==",
|
|
||||||
"requires": {
|
|
||||||
"querystringify": "1.0.0",
|
|
||||||
"requires-port": "1.0.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"url-to-options": {
|
"url-to-options": {
|
||||||
"version": "1.0.1",
|
"version": "1.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/url-to-options/-/url-to-options-1.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/url-to-options/-/url-to-options-1.0.1.tgz",
|
||||||
@@ -2191,20 +2139,6 @@
|
|||||||
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
|
||||||
"integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8="
|
"integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8="
|
||||||
},
|
},
|
||||||
"xml2js": {
|
|
||||||
"version": "0.4.19",
|
|
||||||
"resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.19.tgz",
|
|
||||||
"integrity": "sha512-esZnJZJOiJR9wWKMyuvSE1y6Dq5LCuJanqhxslH2bxM6duahNZ+HMpCLhBQGZkbX6xRf8x1Y2eJlgt2q3qo49Q==",
|
|
||||||
"requires": {
|
|
||||||
"sax": "1.2.4",
|
|
||||||
"xmlbuilder": "9.0.4"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"xmlbuilder": {
|
|
||||||
"version": "9.0.4",
|
|
||||||
"resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-9.0.4.tgz",
|
|
||||||
"integrity": "sha1-UZy0ymhtAFqEINNJbz8MruzKWA8="
|
|
||||||
},
|
|
||||||
"xregexp": {
|
"xregexp": {
|
||||||
"version": "3.2.0",
|
"version": "3.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/xregexp/-/xregexp-3.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/xregexp/-/xregexp-3.2.0.tgz",
|
||||||
|
@@ -14,12 +14,11 @@
|
|||||||
"title": "Joplin CLI",
|
"title": "Joplin CLI",
|
||||||
"years": [
|
"years": [
|
||||||
2016,
|
2016,
|
||||||
2017,
|
2017
|
||||||
2018
|
|
||||||
],
|
],
|
||||||
"owner": "Laurent Cozic"
|
"owner": "Laurent Cozic"
|
||||||
},
|
},
|
||||||
"version": "1.0.106",
|
"version": "0.10.77",
|
||||||
"bin": {
|
"bin": {
|
||||||
"joplin": "./main.js"
|
"joplin": "./main.js"
|
||||||
},
|
},
|
||||||
@@ -28,12 +27,9 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"app-module-path": "^2.2.0",
|
"app-module-path": "^2.2.0",
|
||||||
"async-mutex": "^0.1.3",
|
|
||||||
"base-64": "^0.1.0",
|
|
||||||
"compare-version": "^0.1.2",
|
|
||||||
"follow-redirects": "^1.2.4",
|
"follow-redirects": "^1.2.4",
|
||||||
"form-data": "^2.1.4",
|
"form-data": "^2.1.4",
|
||||||
"fs-extra": "^5.0.0",
|
"fs-extra": "^3.0.1",
|
||||||
"html-entities": "^1.2.1",
|
"html-entities": "^1.2.1",
|
||||||
"jssha": "^2.3.0",
|
"jssha": "^2.3.0",
|
||||||
"levenshtein": "^1.0.5",
|
"levenshtein": "^1.0.5",
|
||||||
@@ -44,6 +40,7 @@
|
|||||||
"node-emoji": "^1.8.1",
|
"node-emoji": "^1.8.1",
|
||||||
"node-fetch": "^1.7.1",
|
"node-fetch": "^1.7.1",
|
||||||
"node-persist": "^2.1.0",
|
"node-persist": "^2.1.0",
|
||||||
|
"os-tmpdir": "^1.0.2",
|
||||||
"promise": "^7.1.1",
|
"promise": "^7.1.1",
|
||||||
"proper-lockfile": "^2.0.1",
|
"proper-lockfile": "^2.0.1",
|
||||||
"query-string": "4.3.4",
|
"query-string": "4.3.4",
|
||||||
@@ -56,13 +53,10 @@
|
|||||||
"string-padding": "^1.0.2",
|
"string-padding": "^1.0.2",
|
||||||
"string-to-stream": "^1.1.0",
|
"string-to-stream": "^1.1.0",
|
||||||
"strip-ansi": "^4.0.0",
|
"strip-ansi": "^4.0.0",
|
||||||
"tar": "^4.4.0",
|
|
||||||
"tcp-port-used": "^0.1.2",
|
"tcp-port-used": "^0.1.2",
|
||||||
"tkwidgets": "^0.5.25",
|
"tkwidgets": "^0.5.20",
|
||||||
"url-parse": "^1.2.0",
|
|
||||||
"uuid": "^3.0.1",
|
"uuid": "^3.0.1",
|
||||||
"word-wrap": "^1.2.3",
|
"word-wrap": "^1.2.3",
|
||||||
"xml2js": "^0.4.19",
|
|
||||||
"yargs-parser": "^7.0.0"
|
"yargs-parser": "^7.0.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
@@ -9,10 +9,4 @@ bash $SCRIPT_DIR/build.sh
|
|||||||
cp "$SCRIPT_DIR/package.json" build/
|
cp "$SCRIPT_DIR/package.json" build/
|
||||||
cp "$SCRIPT_DIR/../README.md" build/
|
cp "$SCRIPT_DIR/../README.md" build/
|
||||||
cd "$SCRIPT_DIR/build"
|
cd "$SCRIPT_DIR/build"
|
||||||
npm publish
|
npm publish
|
||||||
|
|
||||||
NEW_VERSION=$(cat package.json | jq -r .version)
|
|
||||||
git add -A
|
|
||||||
git commit -m "CLI v$NEW_VERSION"
|
|
||||||
git tag "cli-v$NEW_VERSION"
|
|
||||||
git push && git push --tags
|
|
@@ -4,4 +4,4 @@ CLIENT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
|
|||||||
|
|
||||||
bash "$CLIENT_DIR/build.sh" && node "$CLIENT_DIR/build/main.js" --profile ~/Temp/TestNotes2 --stack-trace-enabled --log-level debug --env dev "$@"
|
bash "$CLIENT_DIR/build.sh" && node "$CLIENT_DIR/build/main.js" --profile ~/Temp/TestNotes2 --stack-trace-enabled --log-level debug --env dev "$@"
|
||||||
|
|
||||||
# bash $CLIENT_DIR/build.sh && NODE_PATH="$CLIENT_DIR/build/" node build/main.js --profile ~/.config/joplin --stack-trace-enabled --log-level debug "$@"
|
#bash $CLIENT_DIR/build.sh && NODE_PATH="$CLIENT_DIR/build/" node build/main.js --profile ~/Temp/TestNotes2 --stack-trace-enabled --log-level debug --env dev "$@"
|
@@ -1,15 +1,10 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
ROOT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
|
ROOT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
|
||||||
BUILD_DIR="$ROOT_DIR/tests-build"
|
BUILD_DIR="$ROOT_DIR/tests-build"
|
||||||
TEST_FILE="$1"
|
|
||||||
|
|
||||||
rsync -a --exclude "node_modules/" "$ROOT_DIR/tests/" "$BUILD_DIR/"
|
rsync -a --exclude "node_modules/" "$ROOT_DIR/tests/" "$BUILD_DIR/"
|
||||||
rsync -a "$ROOT_DIR/../ReactNativeClient/lib/" "$BUILD_DIR/lib/"
|
rsync -a "$ROOT_DIR/../ReactNativeClient/lib/" "$BUILD_DIR/lib/"
|
||||||
rsync -a "$ROOT_DIR/build/locales/" "$BUILD_DIR/locales/"
|
rsync -a "$ROOT_DIR/build/locales/" "$BUILD_DIR/locales/"
|
||||||
mkdir -p "$BUILD_DIR/data"
|
mkdir -p "$BUILD_DIR/data"
|
||||||
|
|
||||||
if [[ $TEST_FILE == "" ]]; then
|
(cd "$ROOT_DIR" && npm test tests-build/synchronizer.js)
|
||||||
(cd "$ROOT_DIR" && npm test tests-build/synchronizer.js tests-build/encryption.js tests-build/ArrayUtils.js tests-build/models_Setting.js tests-build/services_InteropService.js)
|
|
||||||
else
|
|
||||||
(cd "$ROOT_DIR" && npm test tests-build/$TEST_FILE.js)
|
|
||||||
fi
|
|
@@ -1,47 +0,0 @@
|
|||||||
require('app-module-path').addPath(__dirname);
|
|
||||||
|
|
||||||
const { time } = require('lib/time-utils.js');
|
|
||||||
const { fileContentEqual, setupDatabase, setupDatabaseAndSynchronizer, db, synchronizer, fileApi, sleep, clearDatabase, switchClient, syncTargetId, objectsEqual, checkThrowAsync } = require('test-utils.js');
|
|
||||||
const ArrayUtils = require('lib/ArrayUtils.js');
|
|
||||||
|
|
||||||
process.on('unhandledRejection', (reason, p) => {
|
|
||||||
console.log('Unhandled Rejection at: Promise', p, 'reason:', reason);
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('ArrayUtils', function() {
|
|
||||||
|
|
||||||
beforeEach(async (done) => {
|
|
||||||
done();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should remove array elements', async (done) => {
|
|
||||||
let a = ['un', 'deux', 'trois'];
|
|
||||||
a = ArrayUtils.removeElement(a, 'deux');
|
|
||||||
|
|
||||||
expect(a[0]).toBe('un');
|
|
||||||
expect(a[1]).toBe('trois');
|
|
||||||
expect(a.length).toBe(2);
|
|
||||||
|
|
||||||
a = ['un', 'deux', 'trois'];
|
|
||||||
a = ArrayUtils.removeElement(a, 'not in there');
|
|
||||||
expect(a.length).toBe(3);
|
|
||||||
|
|
||||||
done();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should find items using binary search', async (done) => {
|
|
||||||
let items = ['aaa', 'ccc', 'bbb'];
|
|
||||||
expect(ArrayUtils.binarySearch(items, 'bbb')).toBe(-1); // Array not sorted!
|
|
||||||
items.sort();
|
|
||||||
expect(ArrayUtils.binarySearch(items, 'bbb')).toBe(1);
|
|
||||||
expect(ArrayUtils.binarySearch(items, 'ccc')).toBe(2);
|
|
||||||
expect(ArrayUtils.binarySearch(items, 'oops')).toBe(-1);
|
|
||||||
expect(ArrayUtils.binarySearch(items, 'aaa')).toBe(0);
|
|
||||||
|
|
||||||
items = [];
|
|
||||||
expect(ArrayUtils.binarySearch(items, 'aaa')).toBe(-1);
|
|
||||||
|
|
||||||
done();
|
|
||||||
});
|
|
||||||
|
|
||||||
});
|
|
@@ -1,180 +0,0 @@
|
|||||||
require('app-module-path').addPath(__dirname);
|
|
||||||
|
|
||||||
const { time } = require('lib/time-utils.js');
|
|
||||||
const { fileContentEqual, setupDatabase, setupDatabaseAndSynchronizer, db, synchronizer, fileApi, sleep, clearDatabase, switchClient, syncTargetId, objectsEqual, checkThrowAsync } = require('test-utils.js');
|
|
||||||
const Folder = require('lib/models/Folder.js');
|
|
||||||
const Note = require('lib/models/Note.js');
|
|
||||||
const Tag = require('lib/models/Tag.js');
|
|
||||||
const { Database } = require('lib/database.js');
|
|
||||||
const Setting = require('lib/models/Setting.js');
|
|
||||||
const BaseItem = require('lib/models/BaseItem.js');
|
|
||||||
const BaseModel = require('lib/BaseModel.js');
|
|
||||||
const MasterKey = require('lib/models/MasterKey');
|
|
||||||
const SyncTargetRegistry = require('lib/SyncTargetRegistry.js');
|
|
||||||
const EncryptionService = require('lib/services/EncryptionService.js');
|
|
||||||
|
|
||||||
process.on('unhandledRejection', (reason, p) => {
|
|
||||||
console.log('Unhandled Rejection at: Promise', p, 'reason:', reason);
|
|
||||||
});
|
|
||||||
|
|
||||||
jasmine.DEFAULT_TIMEOUT_INTERVAL = 15000; // The first test is slow because the database needs to be built
|
|
||||||
|
|
||||||
let service = null;
|
|
||||||
|
|
||||||
describe('Encryption', function() {
|
|
||||||
|
|
||||||
beforeEach(async (done) => {
|
|
||||||
await setupDatabaseAndSynchronizer(1);
|
|
||||||
//await setupDatabaseAndSynchronizer(2);
|
|
||||||
//await switchClient(1);
|
|
||||||
service = new EncryptionService();
|
|
||||||
BaseItem.encryptionService_ = service;
|
|
||||||
Setting.setValue('encryption.enabled', true);
|
|
||||||
done();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should encode and decode header', async (done) => {
|
|
||||||
const header = {
|
|
||||||
encryptionMethod: EncryptionService.METHOD_SJCL,
|
|
||||||
masterKeyId: '01234568abcdefgh01234568abcdefgh',
|
|
||||||
};
|
|
||||||
|
|
||||||
const encodedHeader = service.encodeHeader_(header);
|
|
||||||
const decodedHeader = service.decodeHeader_(encodedHeader);
|
|
||||||
delete decodedHeader.length;
|
|
||||||
|
|
||||||
expect(objectsEqual(header, decodedHeader)).toBe(true);
|
|
||||||
|
|
||||||
done();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should generate and decrypt a master key', async (done) => {
|
|
||||||
const masterKey = await service.generateMasterKey('123456');
|
|
||||||
expect(!!masterKey.checksum).toBe(true);
|
|
||||||
expect(!!masterKey.content).toBe(true);
|
|
||||||
|
|
||||||
let hasThrown = false;
|
|
||||||
try {
|
|
||||||
await service.decryptMasterKey(masterKey, 'wrongpassword');
|
|
||||||
} catch (error) {
|
|
||||||
hasThrown = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
expect(hasThrown).toBe(true);
|
|
||||||
|
|
||||||
const decryptedMasterKey = await service.decryptMasterKey(masterKey, '123456');
|
|
||||||
expect(decryptedMasterKey.length).toBe(512);
|
|
||||||
|
|
||||||
done();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should encrypt and decrypt with a master key', async (done) => {
|
|
||||||
let masterKey = await service.generateMasterKey('123456');
|
|
||||||
masterKey = await MasterKey.save(masterKey);
|
|
||||||
|
|
||||||
await service.loadMasterKey(masterKey, '123456', true);
|
|
||||||
|
|
||||||
const cipherText = await service.encryptString('some secret');
|
|
||||||
const plainText = await service.decryptString(cipherText);
|
|
||||||
|
|
||||||
expect(plainText).toBe('some secret');
|
|
||||||
|
|
||||||
// Test that a long string, that is going to be split into multiple chunks, encrypt
|
|
||||||
// and decrypt properly too.
|
|
||||||
let veryLongSecret = '';
|
|
||||||
for (let i = 0; i < service.chunkSize() * 3; i++) veryLongSecret += Math.floor(Math.random() * 9);
|
|
||||||
|
|
||||||
const cipherText2 = await service.encryptString(veryLongSecret);
|
|
||||||
const plainText2 = await service.decryptString(cipherText2);
|
|
||||||
|
|
||||||
expect(plainText2 === veryLongSecret).toBe(true);
|
|
||||||
|
|
||||||
done();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should fail to decrypt if master key not present', async (done) => {
|
|
||||||
let masterKey = await service.generateMasterKey('123456');
|
|
||||||
masterKey = await MasterKey.save(masterKey);
|
|
||||||
|
|
||||||
await service.loadMasterKey(masterKey, '123456', true);
|
|
||||||
|
|
||||||
const cipherText = await service.encryptString('some secret');
|
|
||||||
|
|
||||||
await service.unloadMasterKey(masterKey);
|
|
||||||
|
|
||||||
let hasThrown = await checkThrowAsync(async () => await service.decryptString(cipherText));
|
|
||||||
|
|
||||||
expect(hasThrown).toBe(true);
|
|
||||||
|
|
||||||
done();
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
it('should fail to decrypt if data tampered with', async (done) => {
|
|
||||||
let masterKey = await service.generateMasterKey('123456');
|
|
||||||
masterKey = await MasterKey.save(masterKey);
|
|
||||||
|
|
||||||
await service.loadMasterKey(masterKey, '123456', true);
|
|
||||||
|
|
||||||
let cipherText = await service.encryptString('some secret');
|
|
||||||
cipherText += "ABCDEFGHIJ";
|
|
||||||
|
|
||||||
let hasThrown = await checkThrowAsync(async () => await service.decryptString(cipherText));
|
|
||||||
|
|
||||||
expect(hasThrown).toBe(true);
|
|
||||||
|
|
||||||
done();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should encrypt and decrypt notes and folders', async (done) => {
|
|
||||||
let masterKey = await service.generateMasterKey('123456');
|
|
||||||
masterKey = await MasterKey.save(masterKey);
|
|
||||||
await service.loadMasterKey(masterKey, '123456', true);
|
|
||||||
|
|
||||||
let folder = await Folder.save({ title: 'folder' });
|
|
||||||
let note = await Note.save({ title: 'encrypted note', body: 'something', parent_id: folder.id });
|
|
||||||
let serialized = await Note.serializeForSync(note);
|
|
||||||
let deserialized = Note.filter(await Note.unserialize(serialized));
|
|
||||||
|
|
||||||
// Check that required properties are not encrypted
|
|
||||||
expect(deserialized.id).toBe(note.id);
|
|
||||||
expect(deserialized.parent_id).toBe(note.parent_id);
|
|
||||||
expect(deserialized.updated_time).toBe(note.updated_time);
|
|
||||||
|
|
||||||
// Check that at least title and body are encrypted
|
|
||||||
expect(!deserialized.title).toBe(true);
|
|
||||||
expect(!deserialized.body).toBe(true);
|
|
||||||
|
|
||||||
// Check that encrypted data is there
|
|
||||||
expect(!!deserialized.encryption_cipher_text).toBe(true);
|
|
||||||
|
|
||||||
encryptedNote = await Note.save(deserialized);
|
|
||||||
decryptedNote = await Note.decrypt(encryptedNote);
|
|
||||||
|
|
||||||
expect(decryptedNote.title).toBe(note.title);
|
|
||||||
expect(decryptedNote.body).toBe(note.body);
|
|
||||||
expect(decryptedNote.id).toBe(note.id);
|
|
||||||
expect(decryptedNote.parent_id).toBe(note.parent_id);
|
|
||||||
|
|
||||||
done();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should encrypt and decrypt files', async (done) => {
|
|
||||||
let masterKey = await service.generateMasterKey('123456');
|
|
||||||
masterKey = await MasterKey.save(masterKey);
|
|
||||||
await service.loadMasterKey(masterKey, '123456', true);
|
|
||||||
|
|
||||||
const sourcePath = __dirname + '/../tests/support/photo.jpg';
|
|
||||||
const encryptedPath = __dirname + '/data/photo.crypted';
|
|
||||||
const decryptedPath = __dirname + '/data/photo.jpg';
|
|
||||||
|
|
||||||
await service.encryptFile(sourcePath, encryptedPath);
|
|
||||||
await service.decryptFile(encryptedPath, decryptedPath);
|
|
||||||
|
|
||||||
expect(fileContentEqual(sourcePath, encryptedPath)).toBe(false);
|
|
||||||
expect(fileContentEqual(sourcePath, decryptedPath)).toBe(true);
|
|
||||||
|
|
||||||
done();
|
|
||||||
});
|
|
||||||
|
|
||||||
});
|
|