1
0
mirror of https://github.com/laurent22/joplin.git synced 2025-08-24 20:19:10 +02:00

Compare commits

...

9 Commits

Author SHA1 Message Date
Laurent Cozic
7a985c2c8a Android release v1.0.103 2018-03-02 18:15:09 +00:00
Laurent Cozic
b11ad30a31 Updated translations 2018-03-02 18:12:58 +00:00
Laurent Cozic
5914fc97df Merge branch 'master' of github.com:laurent22/joplin 2018-03-02 18:03:28 +00:00
Laurent Cozic
e41ae1832d Minor tweaks 2018-03-02 18:24:02 +00:00
Laurent Cozic
89b50909ed Electron: Resolves #266: Allow setting text editor font family 2018-03-02 18:16:48 +00:00
Laurent Cozic
edccd7412f Merge pull request #265 from jaredcrowe/docs-correct-checkbox-syntax
correct the documentation about adding checkboxes
2018-03-02 17:59:33 +00:00
Jared Crowe
c76beae057 correct the documentation about adding checkboxes 2018-03-02 09:30:27 +11:00
Laurent Cozic
23c5934a7d Electron: Allow exporting only selected notes or notebook 2018-03-01 20:14:06 +00:00
Laurent Cozic
a078947d6d Allow importing and exporting single notes and notebooks 2018-03-01 18:35:17 +00:00
58 changed files with 384 additions and 126 deletions

View File

@@ -53,9 +53,8 @@ function renderCommandHelp(cmd, width = null) {
desc.push(label);
}
if (md.description) {
desc.push(md.description());
}
const description = Setting.keyDescription(md.key, 'cli');
if (description) desc.push(description);
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)')));

View File

@@ -1126,9 +1126,18 @@ msgstr "Erstellt eine neue Notiz."
msgid "Show tray icon"
msgstr ""
msgid "Set application zoom percentage"
#, fuzzy
msgid "Global zoom percentage"
msgstr "Einstellen des Anwendungszooms"
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 "Die Applikation automatisch aktualisieren"

View File

@@ -993,7 +993,15 @@ msgstr ""
msgid "Show tray icon"
msgstr ""
msgid "Set application zoom percentage"
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"

View File

@@ -1098,9 +1098,18 @@ msgstr "Cuando se crear una nota nueva:"
msgid "Show tray icon"
msgstr "Mostrar icono en la bandeja"
msgid "Set application zoom percentage"
#, fuzzy
msgid "Global zoom percentage"
msgstr "Establecer el porcentaje de aumento de la aplicación"
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 "Actualizar la aplicación automáticamente"

View File

@@ -1101,9 +1101,18 @@ msgstr "Ohar berria sortzen du."
msgid "Show tray icon"
msgstr ""
msgid "Set application zoom percentage"
#, fuzzy
msgid "Global zoom percentage"
msgstr "Ezarri aplikazioaren zoomaren ehunekoa"
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 "Automatikoki eguneratu aplikazioa"

View File

@@ -1101,9 +1101,19 @@ msgstr "Lors de la création d'une note :"
msgid "Show tray icon"
msgstr "Afficher icône dans la zone de notifications"
msgid "Set application zoom percentage"
msgid "Global zoom percentage"
msgstr "Niveau de zoom"
msgid "Editor font family"
msgstr "Police de l'éditeur"
msgid ""
"The font name will not be checked. If incorrect or empty, it will default to "
"a generic monospace font."
msgstr ""
"Le nom de la police ne sera pas vérifié. Si incorrect ou vide une police "
"monospace sera utilisée par défaut."
msgid "Automatically update the application"
msgstr "Mettre à jour le logiciel automatiquement"

View File

@@ -1089,7 +1089,15 @@ msgstr "Stvara novu bilješku."
msgid "Show tray icon"
msgstr ""
msgid "Set application zoom percentage"
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"

View File

@@ -1075,7 +1075,15 @@ msgstr "Crea una nuova nota."
msgid "Show tray icon"
msgstr ""
msgid "Set application zoom percentage"
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"

View File

@@ -1079,7 +1079,15 @@ msgstr "あたらしいノートを作成します。"
msgid "Show tray icon"
msgstr ""
msgid "Set application zoom percentage"
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"

View File

@@ -993,7 +993,15 @@ msgstr ""
msgid "Show tray icon"
msgstr ""
msgid "Set application zoom percentage"
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"

View File

@@ -1105,7 +1105,15 @@ msgstr "Maakt een nieuwe notitie aan."
msgid "Show tray icon"
msgstr ""
msgid "Set application zoom percentage"
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"

View File

@@ -1076,7 +1076,15 @@ msgstr "Cria uma nova nota."
msgid "Show tray icon"
msgstr ""
msgid "Set application zoom percentage"
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"

View File

@@ -1101,9 +1101,18 @@ msgstr "При создании новой заметки:"
msgid "Show tray icon"
msgstr ""
msgid "Set application zoom percentage"
#, fuzzy
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 "Автоматически обновлять приложение"

View File

@@ -1041,7 +1041,15 @@ msgstr "创建新笔记。"
msgid "Show tray icon"
msgstr ""
msgid "Set application zoom percentage"
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"

View File

@@ -209,4 +209,44 @@ describe('services_InteropService', function() {
expect(fileContentEqual(resourcePath1, resourcePath2)).toBe(true);
}));
it('should export and import single notes', asyncTest(async () => {
const service = new InteropService();
const filePath = exportDir() + '/test.jex';
let folder1 = await Folder.save({ title: "folder1" });
let note1 = await Note.save({ title: 'ma note', parent_id: folder1.id });
await service.export({ path: filePath, sourceNoteIds: [note1.id] });
await Note.delete(note1.id);
await Folder.delete(folder1.id);
await service.import({ path: filePath });
expect(await Note.count()).toBe(1);
expect(await Folder.count()).toBe(1);
let folder2 = (await Folder.all())[0];
expect(folder2.title).toBe('test');
}));
it('should export and import single folders', asyncTest(async () => {
const service = new InteropService();
const filePath = exportDir() + '/test.jex';
let folder1 = await Folder.save({ title: "folder1" });
let note1 = await Note.save({ title: 'ma note', parent_id: folder1.id });
await service.export({ path: filePath, sourceFolderIds: [folder1.id] });
await Note.delete(note1.id);
await Folder.delete(folder1.id);
await service.import({ path: filePath });
expect(await Note.count()).toBe(1);
expect(await Folder.count()).toBe(1);
let folder2 = (await Folder.all())[0];
expect(folder2.title).toBe('folder1');
}));
});

View File

@@ -172,6 +172,8 @@ class ElectronAppWrapper {
}
ensureSingleInstance() {
if (this.env_ === 'dev') return false;
return new Promise((resolve, reject) => {
const alreadyRunning = this.electronApp_.makeSingleInstance((commandLine, workingDirectory) => {
const win = this.window();

View File

@@ -0,0 +1,51 @@
const { _ } = require('lib/locale');
const { bridge } = require('electron').remote.require('./bridge');
const InteropService = require('lib/services/InteropService');
class InteropServiceHelper {
static async export(dispatch, module, options = null) {
if (!options) options = {};
let path = null;
if (module.target === 'file') {
path = bridge().showSaveDialog({
filters: [{ name: module.description, extensions: [module.fileExtension]}]
});
} else {
path = bridge().showOpenDialog({
properties: ['openDirectory', 'createDirectory'],
});
}
if (!path || (Array.isArray(path) && !path.length)) return;
if (Array.isArray(path)) path = path[0];
dispatch({
type: 'WINDOW_COMMAND',
name: 'showModalMessage',
message: _('Exporting to "%s" as "%s" format. Please wait...', path, module.format),
});
const exportOptions = {};
exportOptions.path = path;
exportOptions.format = module.format;
if (options.sourceFolderIds) exportOptions.sourceFolderIds = options.sourceFolderIds;
if (options.sourceNoteIds) exportOptions.sourceNoteIds = options.sourceNoteIds;
const service = new InteropService();
const result = await service.export(exportOptions);
console.info('Export result: ', result);
dispatch({
type: 'WINDOW_COMMAND',
name: 'hideModalMessage',
});
}
}
module.exports = InteropServiceHelper;

View File

@@ -21,6 +21,7 @@ const AlarmService = require('lib/services/AlarmService.js');
const AlarmServiceDriverNode = require('lib/services/AlarmServiceDriverNode');
const DecryptionWorker = require('lib/services/DecryptionWorker');
const InteropService = require('lib/services/InteropService');
const InteropServiceHelper = require('./InteropServiceHelper.js');
const { bridge } = require('electron').remote.require('./bridge');
const Menu = bridge().Menu;
@@ -145,6 +146,10 @@ class Application extends BaseApplication {
this.updateTray();
}
if (action.type == 'SETTING_UPDATE_ONE' && action.key == 'style.editor.fontFamily' || action.type == 'SETTING_UPDATE_ALL') {
this.updateEditorFont();
}
if (['NOTE_UPDATE_ONE', 'NOTE_DELETE', 'FOLDER_UPDATE_ONE', 'FOLDER_DELETE'].indexOf(action.type) >= 0) {
if (!await reg.syncTarget().syncStarted()) reg.scheduleSync();
}
@@ -203,41 +208,7 @@ class Application extends BaseApplication {
label: module.format + ' - ' + module.description,
screens: ['Main'],
click: async () => {
let path = null;
if (module.target === 'file') {
path = bridge().showSaveDialog({
filters: [{ name: module.description, extensions: [module.fileExtension]}]
});
} else {
path = bridge().showOpenDialog({
properties: ['openDirectory', 'createDirectory'],
});
}
if (!path || (Array.isArray(path) && !path.length)) return;
if (Array.isArray(path)) path = path[0];
this.dispatch({
type: 'WINDOW_COMMAND',
name: 'showModalMessage',
message: _('Exporting to "%s" as "%s" format. Please wait...', path, module.format),
});
const exportOptions = {};
exportOptions.path = path;
exportOptions.format = module.format;
const service = new InteropService();
const result = await service.export(exportOptions);
console.info('Export result: ', result);
this.dispatch({
type: 'WINDOW_COMMAND',
name: 'hideModalMessage',
});
await InteropServiceHelper.export(this.dispatch.bind(this), module);
}
});
} else {
@@ -492,8 +463,8 @@ class Application extends BaseApplication {
'Copyright © 2016-2018 Laurent Cozic',
_('%s %s (%s, %s)', p.name, p.version, Setting.value('env'), process.platform),
];
bridge().showMessageBox({
message: message.join('\n'),
bridge().showInfoMessageBox(message.join('\n'), {
icon: bridge().electronApp().buildDir() + '/icons/32x32.png',
});
}
}]
@@ -553,6 +524,21 @@ class Application extends BaseApplication {
}
}
updateEditorFont() {
const fontFamilies = [];
if (Setting.value('style.editor.fontFamily')) fontFamilies.push('"' + Setting.value('style.editor.fontFamily') + '"');
fontFamilies.push('monospace');
// The '*' and '!important' parts are necessary to make sure Russian text is displayed properly
// https://github.com/laurent22/joplin/issues/155
const css = '.ace_editor * { font-family: ' + fontFamilies.join(', ') + ' !important; }';
const styleTag = document.createElement('style');
styleTag.type = 'text/css';
styleTag.appendChild(document.createTextNode(css));
document.head.appendChild(styleTag);
}
async start(argv) {
argv = await super.start(argv);

View File

@@ -26,7 +26,7 @@ class Bridge {
if (!this.window()) return { width: 0, height: 0 };
const s = this.window().getContentSize();
return { width: s[0], height: s[1] };
}
}
windowSize() {
if (!this.window()) return { width: 0, height: 0 };
@@ -62,20 +62,23 @@ class Bridge {
return filePaths;
}
showMessageBox(window, options) {
// Don't use this directly - call one of the showXxxxxxxMessageBox() instead
showMessageBox_(window, options) {
const {dialog} = require('electron');
const nativeImage = require('electron').nativeImage
if (!window) window = this.window();
return dialog.showMessageBox(window, options);
}
showErrorMessageBox(message) {
return this.showMessageBox(this.window(), {
return this.showMessageBox_(this.window(), {
type: 'error',
message: message,
});
}
showConfirmMessageBox(message) {
const result = this.showMessageBox(this.window(), {
const result = this.showMessageBox_(this.window(), {
type: 'question',
message: message,
buttons: [_('OK'), _('Cancel')],
@@ -83,12 +86,12 @@ class Bridge {
return result === 0;
}
showInfoMessageBox(message) {
const result = this.showMessageBox(this.window(), {
showInfoMessageBox(message, options = {}) {
const result = this.showMessageBox_(this.window(), Object.assign({}, {
type: 'info',
message: message,
buttons: [_('OK')],
});
}, options));
return result === 0;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

View File

@@ -59,6 +59,12 @@ class ConfigScreenComponent extends React.Component {
display: 'inline-block',
};
const descriptionStyle = Object.assign({}, theme.textStyle, {
color: theme.colorFaded,
marginTop: 5,
fontStyle: 'italic',
});
const updateSettingValue = (key, value) => {
return shared.updateSettingValue(this, key, value);
}
@@ -67,6 +73,13 @@ class ConfigScreenComponent extends React.Component {
const md = Setting.settingMetadata(key);
const descriptionText = Setting.keyDescription(key, 'desktop');
const descriptionComp = descriptionText ? (
<div style={descriptionStyle}>
{descriptionText}
</div>
) : null;
if (md.isEnum) {
let items = [];
const settingOptions = md.options();
@@ -82,6 +95,7 @@ class ConfigScreenComponent extends React.Component {
<select value={value} style={controlStyle} onChange={(event) => { updateSettingValue(key, event.target.value) }}>
{items}
</select>
{ descriptionComp }
</div>
);
} else if (md.type === Setting.TYPE_BOOL) {
@@ -96,6 +110,7 @@ class ConfigScreenComponent extends React.Component {
<div key={key+value.toString()} style={rowStyle}>
<div style={controlStyle}>
<input id={'setting_checkbox_' + key} type="checkbox" checked={!!value} onChange={(event) => { onCheckboxClick(event) }}/><label onClick={(event) => { onCheckboxClick(event) }} style={labelStyle} htmlFor={'setting_checkbox_' + key}>{md.label()}</label>
{ descriptionComp }
</div>
</div>
);
@@ -111,6 +126,7 @@ class ConfigScreenComponent extends React.Component {
<div key={key} style={rowStyle}>
<div style={labelStyle}><label>{md.label()}</label></div>
<input type={inputType} style={inputStyle} value={this.state.settings[key]} onChange={(event) => {onTextChange(event)}} />
{ descriptionComp }
</div>
);
} else if (md.type === Setting.TYPE_INT) {
@@ -122,6 +138,7 @@ class ConfigScreenComponent extends React.Component {
<div key={key} style={rowStyle}>
<div style={labelStyle}><label>{md.label()}</label></div>
<input type="number" style={controlStyle} value={this.state.settings[key]} onChange={(event) => {onNumChange(event)}} min={md.minimum} max={md.maximum} step={md.step}/>
{ descriptionComp }
</div>
);
} else {

View File

@@ -9,6 +9,8 @@ const { bridge } = require('electron').remote.require('./bridge');
const Menu = bridge().Menu;
const MenuItem = bridge().MenuItem;
const eventManager = require('../eventManager');
const InteropService = require('lib/services/InteropService');
const InteropServiceHelper = require('../InteropServiceHelper.js');
class NoteListComponent extends React.Component {
@@ -91,6 +93,12 @@ class NoteListComponent extends React.Component {
eventManager.emit('noteTypeToggle', { noteId: note.id });
}
}}));
menu.append(new MenuItem({label: _('Export'), click: async () => {
const ioService = new InteropService();
const module = ioService.moduleByFormat_('exporter', 'jex');
await InteropServiceHelper.export(this.props.dispatch.bind(this), module, { sourceNoteIds: noteIds });
}}));
}
menu.append(new MenuItem({label: _('Delete'), click: async () => {

View File

@@ -320,10 +320,7 @@ class NoteTextComponent extends React.Component {
bridge().openItem(filePath);
});
} else {
bridge().showMessageBox({
type: 'error',
message: _('Unsupported link or message: %s', msg),
});
bridge().showErrorMessageBox(_('Unsupported link or message: %s', msg));
}
}

View File

@@ -54,10 +54,7 @@ class OneDriveLoginScreenComponent extends React.Component {
this.props.dispatch({ type: 'NAV_BACK' });
reg.scheduleSync(0);
} catch (error) {
bridge().showMessageBox({
type: 'error',
message: 'Could not login to OneDrive. Please try again.\n\n' + error.message + "\n\n" + url.match(/.{1,64}/g).join('\n'),
});
bridge().showErrorMessageBox('Could not login to OneDrive. Please try again.\n\n' + error.message + "\n\n" + url.match(/.{1,64}/g).join('\n'));
}
this.authCode_ = null;

View File

@@ -11,6 +11,7 @@ const { themeStyle } = require('../theme.js');
const { bridge } = require('electron').remote.require('./bridge');
const Menu = bridge().Menu;
const MenuItem = bridge().MenuItem;
const InteropServiceHelper = require('../InteropServiceHelper.js');
class SideBarComponent extends React.Component {
@@ -136,7 +137,17 @@ class SideBarComponent extends React.Component {
name: 'renameFolder',
id: itemId,
});
}}))
}}));
menu.append(new MenuItem({ type: 'separator' }));
const InteropService = require('lib/services/InteropService.js');
menu.append(new MenuItem({label: _('Export'), click: async () => {
const ioService = new InteropService();
const module = ioService.moduleByFormat_('exporter', 'jex');
await InteropServiceHelper.export(this.props.dispatch.bind(this), module, { sourceFolderIds: [itemId] });
}}));
}
menu.popup(bridge().window());

View File

@@ -17,11 +17,6 @@
.smalltalk .page {
max-width: 30em;
}
.ace_editor * {
/* Necessary to make sure Russian text is displayed properly */
/* https://github.com/laurent22/joplin/issues/155 */
font-family: monospace !important;
}
</style>
</head>
<body>

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -26,7 +26,7 @@ Linux | <a href='https://github.com/laurent22/joplin/releases/download/
Operating System | Download | Alt. Download
-----------------|----------|----------------
Android | <a href='https://play.google.com/store/apps/details?id=net.cozic.joplin&utm_source=GitHub&utm_campaign=README&pcampaignid=MKT-Other-global-all-co-prtnr-py-PartBadge-Mar2515-1'><img alt='Get it on Google Play' height="40px" src='https://raw.githubusercontent.com/laurent22/joplin/master/docs/images/BadgeAndroid.png'/></a> | or [Download APK File](https://github.com/laurent22/joplin-android/releases/download/android-v1.0.102/joplin-v1.0.102.apk)
Android | <a href='https://play.google.com/store/apps/details?id=net.cozic.joplin&utm_source=GitHub&utm_campaign=README&pcampaignid=MKT-Other-global-all-co-prtnr-py-PartBadge-Mar2515-1'><img alt='Get it on Google Play' height="40px" src='https://raw.githubusercontent.com/laurent22/joplin/master/docs/images/BadgeAndroid.png'/></a> | or [Download APK File](https://github.com/laurent22/joplin-android/releases/download/android-v1.0.103/joplin-v1.0.103.apk)
iOS | <a href='https://itunes.apple.com/us/app/joplin/id1315599797'><img alt='Get it on the App Store' height="40px" src='https://raw.githubusercontent.com/laurent22/joplin/master/docs/images/BadgeIOS.png'/></a> | -
## Terminal application
@@ -194,9 +194,9 @@ Here is an example with the Markdown and rendered result side by side:
Checkboxes can be added like so:
-[ ] Milk
-[ ] Rice
-[ ] Eggs
- [ ] Milk
- [ ] Rice
- [ ] Eggs
The checkboxes can then be ticked in the mobile and desktop applications.
@@ -222,16 +222,16 @@ Current translations:
<!-- LOCALE-TABLE-AUTO-GENERATED -->
&nbsp; | Language | Po File | Last translator | Percent done
---|---|---|---|---
![](https://raw.githubusercontent.com/stevenrskelton/flag-icon/master/png/16/es/basque_country.png) | Basque | [eu](https://github.com/laurent22/joplin/blob/master/CliClient/locales/eu.po) | juan.abasolo@ehu.eus | 82%
![](https://raw.githubusercontent.com/stevenrskelton/flag-icon/master/png/16/es/basque_country.png) | Basque | [eu](https://github.com/laurent22/joplin/blob/master/CliClient/locales/eu.po) | juan.abasolo@ehu.eus | 81%
![](https://raw.githubusercontent.com/stevenrskelton/flag-icon/master/png/16/country-4x3/hr.png) | Croatian | [hr_HR](https://github.com/laurent22/joplin/blob/master/CliClient/locales/hr_HR.po) | Hrvoje Mandić <trbuhom@net.hr> | 66%
![](https://raw.githubusercontent.com/stevenrskelton/flag-icon/master/png/16/country-4x3/de.png) | Deutsch | [de_DE](https://github.com/laurent22/joplin/blob/master/CliClient/locales/de_DE.po) | Tobias Strobel <git@strobeltobias.de> | 84%
![](https://raw.githubusercontent.com/stevenrskelton/flag-icon/master/png/16/country-4x3/de.png) | Deutsch | [de_DE](https://github.com/laurent22/joplin/blob/master/CliClient/locales/de_DE.po) | Tobias Strobel <git@strobeltobias.de> | 83%
![](https://raw.githubusercontent.com/stevenrskelton/flag-icon/master/png/16/country-4x3/gb.png) | English | [en_GB](https://github.com/laurent22/joplin/blob/master/CliClient/locales/en_GB.po) | | 100%
![](https://raw.githubusercontent.com/stevenrskelton/flag-icon/master/png/16/country-4x3/es.png) | Español | [es_ES](https://github.com/laurent22/joplin/blob/master/CliClient/locales/es_ES.po) | Fernando Martín <f@mrtn.es> | 94%
![](https://raw.githubusercontent.com/stevenrskelton/flag-icon/master/png/16/country-4x3/es.png) | Español | [es_ES](https://github.com/laurent22/joplin/blob/master/CliClient/locales/es_ES.po) | Fernando Martín <f@mrtn.es> | 93%
![](https://raw.githubusercontent.com/stevenrskelton/flag-icon/master/png/16/country-4x3/fr.png) | Français | [fr_FR](https://github.com/laurent22/joplin/blob/master/CliClient/locales/fr_FR.po) | Laurent Cozic | 100%
![](https://raw.githubusercontent.com/stevenrskelton/flag-icon/master/png/16/country-4x3/it.png) | Italiano | [it_IT](https://github.com/laurent22/joplin/blob/master/CliClient/locales/it_IT.po) | | 68%
![](https://raw.githubusercontent.com/stevenrskelton/flag-icon/master/png/16/country-4x3/be.png) | Nederlands | [nl_BE](https://github.com/laurent22/joplin/blob/master/CliClient/locales/nl_BE.po) | | 82%
![](https://raw.githubusercontent.com/stevenrskelton/flag-icon/master/png/16/country-4x3/br.png) | Português (Brasil) | [pt_BR](https://github.com/laurent22/joplin/blob/master/CliClient/locales/pt_BR.po) | | 66%
![](https://raw.githubusercontent.com/stevenrskelton/flag-icon/master/png/16/country-4x3/ru.png) | Русский | [ru_RU](https://github.com/laurent22/joplin/blob/master/CliClient/locales/ru_RU.po) | Artyom Karlov <artyom.karlov@gmail.com> | 86%
![](https://raw.githubusercontent.com/stevenrskelton/flag-icon/master/png/16/country-4x3/ru.png) | Русский | [ru_RU](https://github.com/laurent22/joplin/blob/master/CliClient/locales/ru_RU.po) | Artyom Karlov <artyom.karlov@gmail.com> | 85%
![](https://raw.githubusercontent.com/stevenrskelton/flag-icon/master/png/16/country-4x3/cn.png) | 中文 (简体) | [zh_CN](https://github.com/laurent22/joplin/blob/master/CliClient/locales/zh_CN.po) | RCJacH <RCJacH@outlook.com> | 68%
![](https://raw.githubusercontent.com/stevenrskelton/flag-icon/master/png/16/country-4x3/jp.png) | 日本語 | [ja_JP](https://github.com/laurent22/joplin/blob/master/CliClient/locales/ja_JP.po) | | 66%
<!-- LOCALE-TABLE-AUTO-GENERATED -->

View File

@@ -90,8 +90,8 @@ android {
applicationId "net.cozic.joplin"
minSdkVersion 16
targetSdkVersion 22
versionCode 2097280
versionName "1.0.102"
versionCode 2097281
versionName "1.0.103"
ndk {
abiFilters "armeabi-v7a", "x86"
}

View File

@@ -83,7 +83,8 @@ class Setting extends BaseModel {
'encryption.enabled': { value: false, type: Setting.TYPE_BOOL, public: false },
'encryption.activeMasterKeyId': { value: '', type: Setting.TYPE_STRING, public: false },
'encryption.passwordCache': { value: {}, type: Setting.TYPE_OBJECT, public: false },
'style.zoom': {value: "100", type: Setting.TYPE_INT, public: true, appTypes: ['desktop'], label: () => _('Set application zoom percentage'), minimum: "50", maximum: "500", step: "10"},
'style.zoom': {value: "100", type: Setting.TYPE_INT, public: true, appTypes: ['desktop'], label: () => _('Global zoom percentage'), minimum: "50", maximum: "500", step: "10"},
'style.editor.fontFamily': {value: "", type: Setting.TYPE_STRING, public: true, appTypes: ['desktop'], label: () => _('Editor font family'), description: () => _('The font name will not be checked. If incorrect or empty, it will default to a generic monospace font.')},
'autoUpdateEnabled': { value: true, type: Setting.TYPE_BOOL, public: true, appTypes: ['desktop'], label: () => _('Automatically update the application') },
'sync.interval': { value: 300, type: Setting.TYPE_INT, isEnum: true, public: true, label: () => _('Synchronisation interval'), options: () => {
return {
@@ -98,7 +99,7 @@ class Setting extends BaseModel {
}},
'noteVisiblePanes': { value: ['editor', 'viewer'], type: Setting.TYPE_ARRAY, public: false, appTypes: ['desktop'] },
'showAdvancedOptions': { value: false, type: Setting.TYPE_BOOL, public: true, appTypes: ['mobile' ], label: () => _('Show advanced options') },
'sync.target': { value: SyncTargetRegistry.nameToId('onedrive'), type: Setting.TYPE_INT, isEnum: true, public: true, label: () => _('Synchronisation target'), description: () => _('The target to synchonise to. Each sync target may have additional parameters which are named as `sync.NUM.NAME` (all documented below).'), options: () => {
'sync.target': { value: SyncTargetRegistry.nameToId('onedrive'), type: Setting.TYPE_INT, isEnum: true, public: true, label: () => _('Synchronisation target'), description: (appType) => { return appType !== 'cli' ? null : _('The target to synchonise to. Each sync target may have additional parameters which are named as `sync.NUM.NAME` (all documented below).') }, options: () => {
return SyncTargetRegistry.idAndLabelPlainObject();
}},
@@ -108,7 +109,7 @@ class Setting extends BaseModel {
} catch (error) {
return false;
}
}, public: true, label: () => _('Directory to synchronise with (absolute path)'), description: () => _('The path to synchronise with when file system synchronisation is enabled. See `sync.target`.') },
}, public: true, label: () => _('Directory to synchronise with (absolute path)'), description: (appType) => { return appType !== 'cli' ? null : _('The path to synchronise with when file system synchronisation is enabled. See `sync.target`.'); } },
'sync.5.path': { value: '', type: Setting.TYPE_STRING, show: (settings) => { return settings['sync.target'] == SyncTargetRegistry.nameToId('nextcloud') }, public: true, label: () => _('Nextcloud WebDAV URL') },
'sync.5.username': { value: '', type: Setting.TYPE_STRING, show: (settings) => { return settings['sync.target'] == SyncTargetRegistry.nameToId('nextcloud') }, public: true, label: () => _('Nextcloud username') },
@@ -143,6 +144,12 @@ class Setting extends BaseModel {
return key in this.metadata();
}
static keyDescription(key, appType = null) {
const md = this.settingMetadata(key);
if (!md.description) return null;
return md.description(appType);
}
static keys(publicOnly = false, appType = null) {
if (!this.keys_) {
const metadata = this.metadata();

View File

@@ -36,6 +36,8 @@ class InteropService_Importer_Jex extends InteropService_Importer_Base {
throw e;
}
if (!('defaultFolderTitle' in this.options_)) this.options_.defaultFolderTitle = filename(this.sourcePath_);
const importer = new InteropService_Importer_Raw();
await importer.init(tempDir, this.options_);
result = await importer.exec(result);

View File

@@ -14,7 +14,6 @@ const { shim } = require('lib/shim');
const { _ } = require('lib/locale');
const { fileExtension } = require('lib/path-utils');
const { uuid } = require('lib/uuid.js');
const { importEnex } = require('lib/import-enex');
class InteropService_Importer_Raw extends InteropService_Importer_Base {
@@ -41,6 +40,25 @@ class InteropService_Importer_Raw extends InteropService_Importer_Base {
}
const stats = await shim.fsDriver().readDirStats(this.sourcePath_);
const folderExists = function(stats, folderId) {
folderId = folderId.toLowerCase();
for (let i = 0; i < stats.length; i++) {
const stat = stats[i];
const statId = BaseItem.pathToId(stat.path);
if (statId.toLowerCase() === folderId) return true;
}
return false;
}
let defaultFolder_ = null;
const defaultFolder = async () => {
if (defaultFolder_) return defaultFolder_;
const folderTitle = await Folder.findUniqueFolderTitle(this.options_.defaultFolderTitle ? this.options_.defaultFolderTitle : 'Imported');
defaultFolder_ = await Folder.save({ title: folderTitle });
return defaultFolder_;
}
for (let i = 0; i < stats.length; i++) {
const stat = stats[i];
if (stat.isDirectory()) continue;
@@ -54,7 +72,22 @@ class InteropService_Importer_Raw extends InteropService_Importer_Base {
delete item.type_;
if (itemType === BaseModel.TYPE_NOTE) {
if (!folderIdMap[item.parent_id]) folderIdMap[item.parent_id] = destinationFolderId ? destinationFolderId : uuid.create();
// Logic is a bit complex here:
// - If a destination folder was specified, move the note to it.
// - Otherwise, if the associated folder exists, use this.
// - If it doesn't exist, use the default folder. This is the case for example when importing JEX archives that contain only one or more notes, but no folder.
if (!folderIdMap[item.parent_id]) {
if (destinationFolderId) {
folderIdMap[item.parent_id] = destinationFolderId;
} else if (!folderExists(stats, item.parent_id)) {
const parentFolder = await defaultFolder();
folderIdMap[item.parent_id] = parentFolder.id;
} else {
folderIdMap[item.parent_id] = uuid.create();
}
}
const noteId = uuid.create();
noteIdMap[item.id] = noteId;
item.id = noteId;

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -247,7 +247,7 @@
<tr>
<td>Android</td>
<td><a href='https://play.google.com/store/apps/details?id=net.cozic.joplin&utm_source=GitHub&utm_campaign=README&pcampaignid=MKT-Other-global-all-co-prtnr-py-PartBadge-Mar2515-1'><img alt='Get it on Google Play' height="40px" src='https://raw.githubusercontent.com/laurent22/joplin/master/docs/images/BadgeAndroid.png'/></a></td>
<td>or <a href="https://github.com/laurent22/joplin-android/releases/download/android-v1.0.102/joplin-v1.0.102.apk">Download APK File</a></td>
<td>or <a href="https://github.com/laurent22/joplin-android/releases/download/android-v1.0.103/joplin-v1.0.103.apk">Download APK File</a></td>
</tr>
<tr>
<td>iOS</td>
@@ -366,9 +366,9 @@ $$
<p><img src="https://raw.githubusercontent.com/laurent22/joplin/master/docs/images/Katex.png" style="max-width: 100%; max-height: 35em;"></p>
<h2 id="checkboxes">Checkboxes</h2>
<p>Checkboxes can be added like so:</p>
<pre><code>-[ ] Milk
-[ ] Rice
-[ ] Eggs
<pre><code>- [ ] Milk
- [ ] Rice
- [ ] Eggs
</code></pre><p>The checkboxes can then be ticked in the mobile and desktop applications.</p>
<h1 id="contributing">Contributing</h1>
<p>Please see the guide for information on how to contribute to the development of Joplin: <a href="https://github.com/laurent22/joplin/blob/master/CONTRIBUTING.md">https://github.com/laurent22/joplin/blob/master/CONTRIBUTING.md</a></p>
@@ -400,21 +400,21 @@ $$
<td>Basque</td>
<td><a href="https://github.com/laurent22/joplin/blob/master/CliClient/locales/eu.po">eu</a></td>
<td>juan.abasolo@ehu.eus</td>
<td>82%</td>
<td>81%</td>
</tr>
<tr>
<td><img src="https://raw.githubusercontent.com/stevenrskelton/flag-icon/master/png/16/country-4x3/hr.png" alt=""></td>
<td>Croatian</td>
<td><a href="https://github.com/laurent22/joplin/blob/master/CliClient/locales/hr_HR.po">hr_HR</a></td>
<td>Hrvoje Mandić <a href="&#109;&#x61;&#x69;&#x6c;&#116;&#x6f;&#58;&#x74;&#114;&#x62;&#x75;&#104;&#111;&#109;&#x40;&#110;&#101;&#x74;&#46;&#104;&#x72;">&#x74;&#114;&#x62;&#x75;&#104;&#111;&#109;&#x40;&#110;&#101;&#x74;&#46;&#104;&#x72;</a></td>
<td>Hrvoje Mandić <a href="&#x6d;&#97;&#105;&#x6c;&#x74;&#111;&#58;&#116;&#114;&#98;&#117;&#104;&#111;&#109;&#64;&#x6e;&#x65;&#x74;&#46;&#104;&#x72;">&#116;&#114;&#98;&#117;&#104;&#111;&#109;&#64;&#x6e;&#x65;&#x74;&#46;&#104;&#x72;</a></td>
<td>66%</td>
</tr>
<tr>
<td><img src="https://raw.githubusercontent.com/stevenrskelton/flag-icon/master/png/16/country-4x3/de.png" alt=""></td>
<td>Deutsch</td>
<td><a href="https://github.com/laurent22/joplin/blob/master/CliClient/locales/de_DE.po">de_DE</a></td>
<td>Tobias Strobel <a href="&#x6d;&#97;&#105;&#108;&#x74;&#x6f;&#58;&#103;&#105;&#116;&#64;&#115;&#x74;&#114;&#x6f;&#98;&#x65;&#108;&#116;&#111;&#98;&#105;&#97;&#x73;&#x2e;&#x64;&#101;">&#103;&#105;&#116;&#64;&#115;&#x74;&#114;&#x6f;&#98;&#x65;&#108;&#116;&#111;&#98;&#105;&#97;&#x73;&#x2e;&#x64;&#101;</a></td>
<td>84%</td>
<td>Tobias Strobel <a href="&#x6d;&#97;&#x69;&#108;&#116;&#x6f;&#x3a;&#x67;&#105;&#116;&#64;&#x73;&#116;&#x72;&#111;&#x62;&#x65;&#108;&#116;&#x6f;&#x62;&#105;&#97;&#x73;&#46;&#100;&#x65;">&#x67;&#105;&#116;&#64;&#x73;&#116;&#x72;&#111;&#x62;&#x65;&#108;&#116;&#x6f;&#x62;&#105;&#97;&#x73;&#46;&#100;&#x65;</a></td>
<td>83%</td>
</tr>
<tr>
<td><img src="https://raw.githubusercontent.com/stevenrskelton/flag-icon/master/png/16/country-4x3/gb.png" alt=""></td>
@@ -427,8 +427,8 @@ $$
<td><img src="https://raw.githubusercontent.com/stevenrskelton/flag-icon/master/png/16/country-4x3/es.png" alt=""></td>
<td>Español</td>
<td><a href="https://github.com/laurent22/joplin/blob/master/CliClient/locales/es_ES.po">es_ES</a></td>
<td>Fernando Martín <a href="&#x6d;&#97;&#105;&#108;&#x74;&#x6f;&#x3a;&#x66;&#64;&#109;&#x72;&#116;&#x6e;&#46;&#x65;&#115;">&#x66;&#64;&#109;&#x72;&#116;&#x6e;&#46;&#x65;&#115;</a></td>
<td>94%</td>
<td>Fernando Martín <a href="&#x6d;&#x61;&#x69;&#x6c;&#x74;&#111;&#58;&#x66;&#64;&#109;&#x72;&#116;&#x6e;&#x2e;&#101;&#115;">&#x66;&#64;&#109;&#x72;&#116;&#x6e;&#x2e;&#101;&#115;</a></td>
<td>93%</td>
</tr>
<tr>
<td><img src="https://raw.githubusercontent.com/stevenrskelton/flag-icon/master/png/16/country-4x3/fr.png" alt=""></td>
@@ -462,14 +462,14 @@ $$
<td><img src="https://raw.githubusercontent.com/stevenrskelton/flag-icon/master/png/16/country-4x3/ru.png" alt=""></td>
<td>Русский</td>
<td><a href="https://github.com/laurent22/joplin/blob/master/CliClient/locales/ru_RU.po">ru_RU</a></td>
<td>Artyom Karlov <a href="&#109;&#x61;&#x69;&#x6c;&#116;&#111;&#58;&#97;&#114;&#x74;&#121;&#111;&#109;&#x2e;&#107;&#x61;&#114;&#108;&#111;&#118;&#64;&#x67;&#109;&#x61;&#105;&#108;&#x2e;&#99;&#111;&#109;">&#97;&#114;&#x74;&#121;&#111;&#109;&#x2e;&#107;&#x61;&#114;&#108;&#111;&#118;&#64;&#x67;&#109;&#x61;&#105;&#108;&#x2e;&#99;&#111;&#109;</a></td>
<td>86%</td>
<td>Artyom Karlov <a href="&#109;&#x61;&#105;&#x6c;&#116;&#111;&#58;&#97;&#x72;&#x74;&#x79;&#x6f;&#x6d;&#46;&#107;&#97;&#x72;&#108;&#111;&#118;&#64;&#103;&#109;&#x61;&#x69;&#108;&#46;&#x63;&#x6f;&#x6d;">&#97;&#x72;&#x74;&#x79;&#x6f;&#x6d;&#46;&#107;&#97;&#x72;&#108;&#111;&#118;&#64;&#103;&#109;&#x61;&#x69;&#108;&#46;&#x63;&#x6f;&#x6d;</a></td>
<td>85%</td>
</tr>
<tr>
<td><img src="https://raw.githubusercontent.com/stevenrskelton/flag-icon/master/png/16/country-4x3/cn.png" alt=""></td>
<td>中文 (简体)</td>
<td><a href="https://github.com/laurent22/joplin/blob/master/CliClient/locales/zh_CN.po">zh_CN</a></td>
<td>RCJacH <a href="&#109;&#x61;&#x69;&#108;&#x74;&#111;&#58;&#x52;&#67;&#x4a;&#97;&#99;&#x48;&#64;&#x6f;&#x75;&#x74;&#x6c;&#x6f;&#x6f;&#x6b;&#x2e;&#99;&#x6f;&#x6d;">&#x52;&#67;&#x4a;&#97;&#99;&#x48;&#64;&#x6f;&#x75;&#x74;&#x6c;&#x6f;&#x6f;&#x6b;&#x2e;&#99;&#x6f;&#x6d;</a></td>
<td>RCJacH <a href="&#109;&#x61;&#x69;&#108;&#x74;&#x6f;&#58;&#82;&#x43;&#x4a;&#x61;&#99;&#72;&#x40;&#111;&#117;&#116;&#x6c;&#x6f;&#111;&#107;&#46;&#99;&#111;&#x6d;">&#82;&#x43;&#x4a;&#x61;&#99;&#72;&#x40;&#111;&#117;&#116;&#x6c;&#x6f;&#111;&#107;&#46;&#99;&#111;&#x6d;</a></td>
<td>68%</td>
</tr>
<tr>