diff --git a/CliClient/app/app.js b/CliClient/app/app.js index 79aac94ba..8eb995527 100644 --- a/CliClient/app/app.js +++ b/CliClient/app/app.js @@ -208,7 +208,7 @@ class Application { } } } - + loadCommands_() { this.onLocaleChanged(); // Ensures that help and exit commands are translated @@ -260,6 +260,41 @@ class Application { if (cmd.hidden()) vorpalCmd.hidden(); }); + + this.vorpal().catch('[args...]', 'Catches undefined commands').action(function(args, end) { + args = args.args; + + function delayExec(command) { + setTimeout(() => { + app().vorpal().exec(command); + }, 100); + } + + if (!args.length) { + end(); + delayExec('help'); + return; + } + + let commandName = args.splice(0, 1); + + let aliases = Setting.value('aliases').trim(); + aliases = aliases.length ? JSON.parse(aliases) : []; + + for (let i = 0; i < aliases.length; i++) { + const alias = aliases[i]; + if (alias.name == commandName) { + let command = alias.command + ' ' + app().shellArgsToString(args); + end(); + delayExec(command); + return; + } + } + + this.log(_("Invalid command. Showing help:")); + end(); + delayExec('help'); + }); } async synchronizer(syncTarget) { diff --git a/CliClient/app/command-alias.js b/CliClient/app/command-alias.js new file mode 100644 index 000000000..32bdabba6 --- /dev/null +++ b/CliClient/app/command-alias.js @@ -0,0 +1,28 @@ +import { BaseCommand } from './base-command.js'; +import { app } from './app.js'; +import { _ } from 'lib/locale.js'; +import { Setting } from 'lib/models/setting.js'; + +class Command extends BaseCommand { + + usage() { + return 'alias '; + } + + description() { + return _('Creates a new command alias which can then be used as a regular command (eg. `alias ll "ls -l"`).'); + } + + async action(args) { + let aliases = Setting.value('aliases').trim(); + aliases = aliases.length ? JSON.parse(aliases) : []; + aliases.push({ + name: args.name, + command: args.command, + }); + Setting.setValue('aliases', JSON.stringify(aliases)); + } + +} + +module.exports = Command; \ No newline at end of file diff --git a/CliClient/app/command-geoloc.js b/CliClient/app/command-geoloc.js new file mode 100644 index 000000000..507312ada --- /dev/null +++ b/CliClient/app/command-geoloc.js @@ -0,0 +1,34 @@ +import { BaseCommand } from './base-command.js'; +import { app } from './app.js'; +import { _ } from 'lib/locale.js'; +import { BaseModel } from 'lib/base-model.js'; +import { Folder } from 'lib/models/folder.js'; +import { Note } from 'lib/models/note.js'; +import { autocompleteItems } from './autocomplete.js'; + +class Command extends BaseCommand { + + usage() { + return 'geoloc '; + } + + description() { + return _('Displays a geolocation URL for the note.'); + } + + autocomplete() { + return { data: autocompleteItems }; + } + + async action(args) { + let title = args['title']; + + let item = await app().loadItem(BaseModel.TYPE_NOTE, title, { parent: app().currentFolder() }); + if (!item) throw new Error(_('Cannot find "%s".', title)); + const url = Note.geolocationUrl(item); + this.log(url); + } + +} + +module.exports = Command; \ No newline at end of file diff --git a/CliClient/locales/en_GB.po b/CliClient/locales/en_GB.po index 770afcb6e..d875f7c53 100644 --- a/CliClient/locales/en_GB.po +++ b/CliClient/locales/en_GB.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: Joplin-CLI 1.0.0\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2017-07-18 13:36+0100\n" +"POT-Creation-Date: 2017-07-18 16:33+0100\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Language-Team: LANGUAGE <LL@li.org>\n" @@ -58,14 +58,24 @@ msgid "Exits the application." msgstr "" #: /media/veracrypt22/src/notes/CliClient/app/app.js:294 +msgid "Invalid command. Showing help:" +msgstr "" + +#: /media/veracrypt22/src/notes/CliClient/app/app.js:329 #, javascript-format msgid "Synchronizing with directory \"%s\"" msgstr "" -#: /media/veracrypt22/src/notes/CliClient/app/app.js:373 +#: /media/veracrypt22/src/notes/CliClient/app/app.js:408 msgid "No notebook is defined. Create one with `mkbook <notebook>`." msgstr "" +#: /media/veracrypt22/src/notes/CliClient/app/command-alias.js:13 +msgid "" +"Creates a new command alias which can then be used as a regular command (eg. " +"`alias ll \"ls -l\"`)." +msgstr "" + #: /media/veracrypt22/src/notes/CliClient/app/command-cat.js:16 msgid "Displays the given note." msgstr "" @@ -77,6 +87,7 @@ msgstr "" #: /media/veracrypt22/src/notes/CliClient/app/command-cat.js:33 #: /media/veracrypt22/src/notes/CliClient/app/command-cp.js:31 #: /media/veracrypt22/src/notes/CliClient/app/command-cp.js:34 +#: /media/veracrypt22/src/notes/CliClient/app/command-geoloc.js:27 #: /media/veracrypt22/src/notes/CliClient/app/command-mv.js:29 #: /media/veracrypt22/src/notes/CliClient/app/command-mv.js:36 #: /media/veracrypt22/src/notes/CliClient/app/command-mv.js:39 @@ -127,6 +138,10 @@ msgstr "" msgid "Starting to edit note. Close the editor to get back to the prompt." msgstr "" +#: /media/veracrypt22/src/notes/CliClient/app/command-geoloc.js:16 +msgid "Displays a geolocation URL for the note." +msgstr "" + #: /media/veracrypt22/src/notes/CliClient/app/command-import-enex.js:16 msgid "Imports an Evernote notebook file (.enex file)." msgstr "" @@ -418,12 +433,16 @@ msgstr "" msgid "Notebooks cannot be named \"%s\", which is a reserved title." msgstr "" -#: /media/veracrypt22/src/notes/ReactNativeClient/lib/models/note.js:194 +#: /media/veracrypt22/src/notes/ReactNativeClient/lib/models/note.js:46 +msgid "This note does not have geolocation information." +msgstr "" + +#: /media/veracrypt22/src/notes/ReactNativeClient/lib/models/note.js:201 #, javascript-format msgid "Cannot copy note to \"%s\" notebook" msgstr "" -#: /media/veracrypt22/src/notes/ReactNativeClient/lib/models/note.js:205 +#: /media/veracrypt22/src/notes/ReactNativeClient/lib/models/note.js:212 #, javascript-format msgid "Cannot move note to \"%s\" notebook" msgstr "" diff --git a/CliClient/locales/fr_FR.po b/CliClient/locales/fr_FR.po index c5e40c585..4de3b58e0 100644 --- a/CliClient/locales/fr_FR.po +++ b/CliClient/locales/fr_FR.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: Joplin-CLI 1.0.0\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2017-07-18 13:36+0100\n" +"POT-Creation-Date: 2017-07-18 16:32+0100\n" "PO-Revision-Date: 2017-07-18 13:27+0100\n" "Last-Translator: \n" "Language-Team: \n" @@ -60,14 +60,25 @@ msgid "Exits the application." msgstr "Quitter le logiciel." #: /media/veracrypt22/src/notes/CliClient/app/app.js:294 +#, fuzzy +msgid "Invalid command. Showing help:" +msgstr "Commande invalie : \"%s\"" + +#: /media/veracrypt22/src/notes/CliClient/app/app.js:329 #, javascript-format msgid "Synchronizing with directory \"%s\"" msgstr "Synchronisation avec dossier \"%s\"" -#: /media/veracrypt22/src/notes/CliClient/app/app.js:373 +#: /media/veracrypt22/src/notes/CliClient/app/app.js:408 msgid "No notebook is defined. Create one with `mkbook <notebook>`." msgstr "Aucun carnet n'est défini. Créez-en un avec `mkbook <carnet>`." +#: /media/veracrypt22/src/notes/CliClient/app/command-alias.js:13 +msgid "" +"Creates a new command alias which can then be used as a regular command (eg. " +"`alias ll \"ls -l\"`)." +msgstr "" + #: /media/veracrypt22/src/notes/CliClient/app/command-cat.js:16 msgid "Displays the given note." msgstr "Affiche la note." @@ -79,6 +90,7 @@ msgstr "Affiche tous les détails de la note." #: /media/veracrypt22/src/notes/CliClient/app/command-cat.js:33 #: /media/veracrypt22/src/notes/CliClient/app/command-cp.js:31 #: /media/veracrypt22/src/notes/CliClient/app/command-cp.js:34 +#: /media/veracrypt22/src/notes/CliClient/app/command-geoloc.js:27 #: /media/veracrypt22/src/notes/CliClient/app/command-mv.js:29 #: /media/veracrypt22/src/notes/CliClient/app/command-mv.js:36 #: /media/veracrypt22/src/notes/CliClient/app/command-mv.js:39 @@ -138,6 +150,11 @@ msgstr "" "Edition de la note en cours. Fermez l'éditeur de texte pour retourner à " "l'invite de commande." +#: /media/veracrypt22/src/notes/CliClient/app/command-geoloc.js:16 +#, fuzzy +msgid "Displays a geolocation URL for the note." +msgstr "Affiche tous les détails de la note." + #: /media/veracrypt22/src/notes/CliClient/app/command-import-enex.js:16 msgid "Imports an Evernote notebook file (.enex file)." msgstr "Importer un carnet Evernote (fichier .enex)." @@ -451,12 +468,16 @@ msgstr "Un carnet avec ce titre existe déjà : \"%s\"" msgid "Notebooks cannot be named \"%s\", which is a reserved title." msgstr "Les carnets ne peuvent être nommés \"%s\" car c'est un nom réservé." -#: /media/veracrypt22/src/notes/ReactNativeClient/lib/models/note.js:194 +#: /media/veracrypt22/src/notes/ReactNativeClient/lib/models/note.js:46 +msgid "This note does not have geolocation information." +msgstr "" + +#: /media/veracrypt22/src/notes/ReactNativeClient/lib/models/note.js:201 #, javascript-format msgid "Cannot copy note to \"%s\" notebook" msgstr "Impossible de copier la note dans le carnet \"%s\"" -#: /media/veracrypt22/src/notes/ReactNativeClient/lib/models/note.js:205 +#: /media/veracrypt22/src/notes/ReactNativeClient/lib/models/note.js:212 #, javascript-format msgid "Cannot move note to \"%s\" notebook" msgstr "Impossible de déplacer la note vers le carnet \"%s\"" diff --git a/CliClient/locales/joplin.pot b/CliClient/locales/joplin.pot index 770afcb6e..d875f7c53 100644 --- a/CliClient/locales/joplin.pot +++ b/CliClient/locales/joplin.pot @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: Joplin-CLI 1.0.0\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2017-07-18 13:36+0100\n" +"POT-Creation-Date: 2017-07-18 16:33+0100\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Language-Team: LANGUAGE <LL@li.org>\n" @@ -58,14 +58,24 @@ msgid "Exits the application." msgstr "" #: /media/veracrypt22/src/notes/CliClient/app/app.js:294 +msgid "Invalid command. Showing help:" +msgstr "" + +#: /media/veracrypt22/src/notes/CliClient/app/app.js:329 #, javascript-format msgid "Synchronizing with directory \"%s\"" msgstr "" -#: /media/veracrypt22/src/notes/CliClient/app/app.js:373 +#: /media/veracrypt22/src/notes/CliClient/app/app.js:408 msgid "No notebook is defined. Create one with `mkbook <notebook>`." msgstr "" +#: /media/veracrypt22/src/notes/CliClient/app/command-alias.js:13 +msgid "" +"Creates a new command alias which can then be used as a regular command (eg. " +"`alias ll \"ls -l\"`)." +msgstr "" + #: /media/veracrypt22/src/notes/CliClient/app/command-cat.js:16 msgid "Displays the given note." msgstr "" @@ -77,6 +87,7 @@ msgstr "" #: /media/veracrypt22/src/notes/CliClient/app/command-cat.js:33 #: /media/veracrypt22/src/notes/CliClient/app/command-cp.js:31 #: /media/veracrypt22/src/notes/CliClient/app/command-cp.js:34 +#: /media/veracrypt22/src/notes/CliClient/app/command-geoloc.js:27 #: /media/veracrypt22/src/notes/CliClient/app/command-mv.js:29 #: /media/veracrypt22/src/notes/CliClient/app/command-mv.js:36 #: /media/veracrypt22/src/notes/CliClient/app/command-mv.js:39 @@ -127,6 +138,10 @@ msgstr "" msgid "Starting to edit note. Close the editor to get back to the prompt." msgstr "" +#: /media/veracrypt22/src/notes/CliClient/app/command-geoloc.js:16 +msgid "Displays a geolocation URL for the note." +msgstr "" + #: /media/veracrypt22/src/notes/CliClient/app/command-import-enex.js:16 msgid "Imports an Evernote notebook file (.enex file)." msgstr "" @@ -418,12 +433,16 @@ msgstr "" msgid "Notebooks cannot be named \"%s\", which is a reserved title." msgstr "" -#: /media/veracrypt22/src/notes/ReactNativeClient/lib/models/note.js:194 +#: /media/veracrypt22/src/notes/ReactNativeClient/lib/models/note.js:46 +msgid "This note does not have geolocation information." +msgstr "" + +#: /media/veracrypt22/src/notes/ReactNativeClient/lib/models/note.js:201 #, javascript-format msgid "Cannot copy note to \"%s\" notebook" msgstr "" -#: /media/veracrypt22/src/notes/ReactNativeClient/lib/models/note.js:205 +#: /media/veracrypt22/src/notes/ReactNativeClient/lib/models/note.js:212 #, javascript-format msgid "Cannot move note to \"%s\" notebook" msgstr "" diff --git a/CliClient/tests/synchronizer.js b/CliClient/tests/synchronizer.js index 206e27bd2..6c5aa2676 100644 --- a/CliClient/tests/synchronizer.js +++ b/CliClient/tests/synchronizer.js @@ -562,5 +562,22 @@ describe('Synchronizer', function() { done(); }); + + it('should not try to delete on remote conflicted notes that have been deleted', async (done) => { + let f1 = await Folder.save({ title: "folder" }); + let n1 = await Note.save({ title: "mynote", parent_id: f1.id }); + await synchronizer().start(); + + await switchClient(2); + + await synchronizer().start(); + await Note.save({ id: n1.id, is_conflict: 1 }); + await Note.delete(n1.id); + const deletedItems = await BaseItem.deletedItems(); + + expect(deletedItems.length).toBe(0); + + done(); + }); }); \ No newline at end of file diff --git a/ReactNativeClient/lib/file-api-driver-onedrive.js b/ReactNativeClient/lib/file-api-driver-onedrive.js index f625bc847..4afd936c0 100644 --- a/ReactNativeClient/lib/file-api-driver-onedrive.js +++ b/ReactNativeClient/lib/file-api-driver-onedrive.js @@ -166,6 +166,11 @@ class FileApiDriverOneDrive { throw new Error('Not implemented'); } + // delta(path) { + // let response = await this.api_.exec('GET', this.makePath_(path) + ':/delta'); + // console.info(response); + // } + } export { FileApiDriverOneDrive }; \ No newline at end of file diff --git a/ReactNativeClient/lib/models/base-item.js b/ReactNativeClient/lib/models/base-item.js index e166d36e7..c15b189e6 100644 --- a/ReactNativeClient/lib/models/base-item.js +++ b/ReactNativeClient/lib/models/base-item.js @@ -109,12 +109,22 @@ class BaseItem extends BaseModel { let trackDeleted = true; if (options && options.trackDeleted !== null && options.trackDeleted !== undefined) trackDeleted = options.trackDeleted; + // Don't create a deleted_items entry when conflicted notes are deleted + // since no other client have (or should have) them. + let conflictNoteIds = []; + if (this.modelType() == BaseModel.TYPE_NOTE) { + const conflictNotes = await this.db().selectAll('SELECT id FROM notes WHERE id IN ("' + ids.join('","') + '") AND is_conflict = 1'); + conflictNoteIds = conflictNotes.map((n) => { return n.id }); + } + await super.batchDelete(ids, options); if (trackDeleted) { let queries = []; let now = time.unixMs(); for (let i = 0; i < ids.length; i++) { + if (conflictNoteIds.indexOf(ids[i]) >= 0) continue; + queries.push({ sql: 'INSERT INTO deleted_items (item_type, item_id, deleted_time) VALUES (?, ?, ?)', params: [this.modelType(), ids[i], now], diff --git a/ReactNativeClient/lib/models/note.js b/ReactNativeClient/lib/models/note.js index 13053eee3..75edf9359 100644 --- a/ReactNativeClient/lib/models/note.js +++ b/ReactNativeClient/lib/models/note.js @@ -1,5 +1,6 @@ import { BaseModel } from 'lib/base-model.js'; import { Log } from 'lib/log.js'; +import { sprintf } from 'sprintf-js'; import { Folder } from 'lib/models/folder.js'; import { BaseItem } from 'lib/models/base-item.js'; import { Setting } from 'lib/models/setting.js'; @@ -40,6 +41,12 @@ class Note extends BaseItem { return super.serialize(note, 'note', fieldNames); } + static geolocationUrl(note) { + if (!('latitude' in note) || !('longitude' in note)) throw new Error('Latitude or longitude missing'); + if (!note.latitude && !note.longitude) throw new Error(_('This note does not have geolocation information.')); + return sprintf('https://www.openstreetmap.org/?lat=%s&lon=%s&zoom=20', note.latitude, note.longitude) + } + static modelType() { return BaseModel.TYPE_NOTE; } diff --git a/ReactNativeClient/lib/models/setting.js b/ReactNativeClient/lib/models/setting.js index 3f6ad8eb4..9432344b1 100644 --- a/ReactNativeClient/lib/models/setting.js +++ b/ReactNativeClient/lib/models/setting.js @@ -150,6 +150,7 @@ Setting.defaults_ = { 'sync.target': { value: 'onedrive', type: 'string', public: true }, 'editor': { value: '', type: 'string', public: true }, 'locale': { value: 'en_GB', type: 'string', public: true }, + 'aliases': { value: '', type: 'string', public: true }, }; // Contains constants that are set by the application and