1
0
mirror of https://github.com/laurent22/joplin.git synced 2024-12-24 10:27:10 +02:00

Tidy settings and sync creation

This commit is contained in:
Laurent Cozic 2017-07-24 18:58:11 +00:00
parent 9b8376f152
commit a983a9f108
18 changed files with 204 additions and 168 deletions

View File

@ -10,20 +10,30 @@ class Command extends BaseCommand {
} }
description() { description() {
return _('Gets or sets a config value. If [value] is not provided, it will show the value of [name]. If neither [name] nor [value] is provided, it will list the current configuration.'); return _("Gets or sets a config value. If [value] is not provided, it will show the value of [name]. If neither [name] nor [value] is provided, it will list the current configuration.");
} }
async action(args) { async action(args) {
const renderKeyValue = (name) => {
const value = Setting.value(name);
if (Setting.isEnum(name)) {
return _('%s = %s (%s)', name, value, Setting.enumOptionLabel(name, value));
} else {
return _('%s = %s', name, value);
}
}
if (!args.name && !args.value) { if (!args.name && !args.value) {
let keys = Setting.publicKeys(); let keys = Setting.publicKeys();
for (let i = 0; i < keys.length; i++) { for (let i = 0; i < keys.length; i++) {
this.log(keys[i] + ' = ' + Setting.value(keys[i])); this.log(renderKeyValue(keys[i]));
} }
return; return;
} }
if (args.name && !args.value) { if (args.name && !args.value) {
this.log(args.name + ' = ' + Setting.value(args.name)); this.log(renderKeyValue(args.name));
return; return;
} }

View File

@ -16,7 +16,7 @@ class Command extends BaseCommand {
async action(args) { async action(args) {
let service = new ReportService(); let service = new ReportService();
let report = await service.status(Database.enumId('syncTarget', Setting.value('sync.target'))); let report = await service.status(Setting.value('sync.target'));
for (let i = 0; i < report.length; i++) { for (let i = 0; i < report.length; i++) {
let section = report[i]; let section = report[i];

View File

@ -42,7 +42,7 @@ async function createClients() {
for (let clientId = 0; clientId < 2; clientId++) { for (let clientId = 0; clientId < 2; clientId++) {
let client = createClient(clientId); let client = createClient(clientId);
promises.push(fs.remove(client.profileDir)); promises.push(fs.remove(client.profileDir));
promises.push(execCommand(client, 'config sync.target filesystem').then(() => { return execCommand(client, 'config sync.filesystem.path ' + syncDir); })); promises.push(execCommand(client, 'config sync.target 2').then(() => { return execCommand(client, 'config sync.2.path ' + syncDir); }));
output.push(client); output.push(client);
} }

View File

@ -70,6 +70,14 @@ msgid ""
"current configuration." "current configuration."
msgstr "" msgstr ""
#, javascript-format
msgid "%s = %s (%s)"
msgstr ""
#, javascript-format
msgid "%s = %s"
msgstr ""
msgid "" msgid ""
"Duplicates the notes matching <pattern> to [notebook]. If no notebook is " "Duplicates the notes matching <pattern> to [notebook]. If no notebook is "
"specified the note is duplicated in the current notebook." "specified the note is duplicated in the current notebook."
@ -276,8 +284,8 @@ msgid "Please open this URL in your browser to authenticate the application:"
msgstr "" msgstr ""
msgid "" msgid ""
"Please set the \"sync.filesystem.path\" config value to the desired " "Please set the \"sync.2.path\" config value to the desired synchronisation "
"synchronisation destination." "destination."
msgstr "" msgstr ""
#, javascript-format #, javascript-format
@ -338,6 +346,14 @@ msgstr ""
msgid "Cannot move note to \"%s\" notebook" msgid "Cannot move note to \"%s\" notebook"
msgstr "" msgstr ""
#, javascript-format
msgid "Invalid option value: \"%s\". Possible values are: %s."
msgstr ""
#, javascript-format
msgid "%s (%s)"
msgstr ""
msgid "Synchronisation target" msgid "Synchronisation target"
msgstr "" msgstr ""

View File

@ -66,6 +66,7 @@ msgstr "Affiche tous les détails de la note."
msgid "Cannot find \"%s\"." msgid "Cannot find \"%s\"."
msgstr "Impossible de trouver \"%s\"." msgstr "Impossible de trouver \"%s\"."
#, fuzzy
msgid "" msgid ""
"Gets or sets a config value. If [value] is not provided, it will show the " "Gets or sets a config value. If [value] is not provided, it will show the "
"value of [name]. If neither [name] nor [value] is provided, it will list the " "value of [name]. If neither [name] nor [value] is provided, it will list the "
@ -75,6 +76,14 @@ msgstr ""
"fournie, la valeur de [nom] est affichée. Si ni le [nom] ni la [valeur] ne " "fournie, la valeur de [nom] est affichée. Si ni le [nom] ni la [valeur] ne "
"sont fournies, la configuration complète est affichée." "sont fournies, la configuration complète est affichée."
#, fuzzy, javascript-format
msgid "%s = %s (%s)"
msgstr "%s %s (%s)"
#, javascript-format
msgid "%s = %s"
msgstr ""
msgid "" msgid ""
"Duplicates the notes matching <pattern> to [notebook]. If no notebook is " "Duplicates the notes matching <pattern> to [notebook]. If no notebook is "
"specified the note is duplicated in the current notebook." "specified the note is duplicated in the current notebook."
@ -311,8 +320,8 @@ msgstr ""
"logiciel :" "logiciel :"
msgid "" msgid ""
"Please set the \"sync.filesystem.path\" config value to the desired " "Please set the \"sync.2.path\" config value to the desired synchronisation "
"synchronisation destination." "destination."
msgstr "" msgstr ""
#, javascript-format #, javascript-format
@ -373,6 +382,14 @@ msgstr "Impossible de copier la note dans le carnet \"%s\""
msgid "Cannot move note to \"%s\" notebook" msgid "Cannot move note to \"%s\" notebook"
msgstr "Impossible de déplacer la note vers le carnet \"%s\"" msgstr "Impossible de déplacer la note vers le carnet \"%s\""
#, javascript-format
msgid "Invalid option value: \"%s\". Possible values are: %s."
msgstr ""
#, fuzzy, javascript-format
msgid "%s (%s)"
msgstr "%s %s (%s)"
#, fuzzy #, fuzzy
msgid "Synchronisation target" msgid "Synchronisation target"
msgstr "Cible de la synchronisation : %s" msgstr "Cible de la synchronisation : %s"

View File

@ -70,6 +70,14 @@ msgid ""
"current configuration." "current configuration."
msgstr "" msgstr ""
#, javascript-format
msgid "%s = %s (%s)"
msgstr ""
#, javascript-format
msgid "%s = %s"
msgstr ""
msgid "" msgid ""
"Duplicates the notes matching <pattern> to [notebook]. If no notebook is " "Duplicates the notes matching <pattern> to [notebook]. If no notebook is "
"specified the note is duplicated in the current notebook." "specified the note is duplicated in the current notebook."
@ -276,8 +284,8 @@ msgid "Please open this URL in your browser to authenticate the application:"
msgstr "" msgstr ""
msgid "" msgid ""
"Please set the \"sync.filesystem.path\" config value to the desired " "Please set the \"sync.2.path\" config value to the desired synchronisation "
"synchronisation destination." "destination."
msgstr "" msgstr ""
#, javascript-format #, javascript-format
@ -338,6 +346,14 @@ msgstr ""
msgid "Cannot move note to \"%s\" notebook" msgid "Cannot move note to \"%s\" notebook"
msgstr "" msgstr ""
#, javascript-format
msgid "Invalid option value: \"%s\". Possible values are: %s."
msgstr ""
#, javascript-format
msgid "%s (%s)"
msgstr ""
msgid "Synchronisation target" msgid "Synchronisation target"
msgstr "" msgstr ""

View File

@ -38,7 +38,7 @@ async function localItemsSameAsRemote(locals, expect) {
expect(!!remote).toBe(true); expect(!!remote).toBe(true);
if (!remote) continue; if (!remote) continue;
if (syncTargetId() == Database.enumId('syncTarget', 'filesystem')) { if (syncTargetId() == Setting.SYNC_TARGET_FILESYSTEM) {
expect(remote.updated_time).toBe(Math.floor(dbItem.updated_time / 1000) * 1000); expect(remote.updated_time).toBe(Math.floor(dbItem.updated_time / 1000) * 1000);
} else { } else {
expect(remote.updated_time).toBe(dbItem.updated_time); expect(remote.updated_time).toBe(dbItem.updated_time);
@ -142,29 +142,20 @@ describe('Synchronizer', function() {
await switchClient(2); await switchClient(2);
await synchronizer().start(); await synchronizer().start();
await sleep(0.1);
let note2 = await Note.load(note1.id); let note2 = await Note.load(note1.id);
note2.title = "Updated on client 2"; note2.title = "Updated on client 2";
await Note.save(note2); await Note.save(note2);
note2 = await Note.load(note2.id); note2 = await Note.load(note2.id);
await synchronizer().start(); await synchronizer().start();
await switchClient(1); await switchClient(1);
await sleep(0.1);
let note2conf = await Note.load(note1.id); let note2conf = await Note.load(note1.id);
note2conf.title = "Updated on client 1"; note2conf.title = "Updated on client 1";
await Note.save(note2conf); await Note.save(note2conf);
note2conf = await Note.load(note1.id); note2conf = await Note.load(note1.id);
await synchronizer().start(); await synchronizer().start();
let conflictedNotes = await Note.conflictedNotes(); let conflictedNotes = await Note.conflictedNotes();
expect(conflictedNotes.length).toBe(1); expect(conflictedNotes.length).toBe(1);
// Other than the id (since the conflicted note is a duplicate), and the is_conflict property // Other than the id (since the conflicted note is a duplicate), and the is_conflict property

View File

@ -29,10 +29,12 @@ Resource.fsDriver_ = fsDriver;
const logDir = __dirname + '/../tests/logs'; const logDir = __dirname + '/../tests/logs';
fs.mkdirpSync(logDir, 0o755); fs.mkdirpSync(logDir, 0o755);
//const syncTarget = 'filesystem'; //const syncTargetId_ = Setting.SYNC_TARGET_MEMORY;
const syncTarget = 'memory'; const syncTargetId_ = Setting.SYNC_TARGET_FILESYSTEM;
const syncDir = __dirname + '/../tests/sync'; const syncDir = __dirname + '/../tests/sync';
const sleepTime = syncTargetId_ == Setting.SYNC_TARGET_FILESYSTEM ? 1001 : 200;
const logger = new Logger(); const logger = new Logger();
logger.addTarget('file', { path: logDir + '/log.txt' }); logger.addTarget('file', { path: logDir + '/log.txt' });
logger.setLevel(Logger.LEVEL_DEBUG); logger.setLevel(Logger.LEVEL_DEBUG);
@ -47,7 +49,7 @@ Setting.setConstant('appId', 'net.cozic.joplin-cli');
Setting.setConstant('appType', 'cli'); Setting.setConstant('appType', 'cli');
function syncTargetId() { function syncTargetId() {
return JoplinDatabase.enumId('syncTarget', syncTarget); return syncTargetId_;
} }
function sleep(n) { function sleep(n) {
@ -59,7 +61,7 @@ function sleep(n) {
} }
async function switchClient(id) { async function switchClient(id) {
await time.msleep(200); // Always leave a little time so that updated_time properties don't overlap await time.msleep(sleepTime); // Always leave a little time so that updated_time properties don't overlap
await Setting.saveAll(); await Setting.saveAll();
currentClient_ = id; currentClient_ = id;
@ -120,7 +122,7 @@ async function setupDatabaseAndSynchronizer(id = null) {
synchronizers_[id].setLogger(logger); synchronizers_[id].setLogger(logger);
} }
if (syncTarget == 'filesystem') { if (syncTargetId_ == Setting.SYNC_TARGET_FILESYSTEM) {
fs.removeSync(syncDir) fs.removeSync(syncDir)
fs.mkdirpSync(syncDir, 0o755); fs.mkdirpSync(syncDir, 0o755);
} else { } else {
@ -141,17 +143,19 @@ function synchronizer(id = null) {
function fileApi() { function fileApi() {
if (fileApi_) return fileApi_; if (fileApi_) return fileApi_;
if (syncTarget == 'filesystem') { if (syncTargetId_ == Setting.SYNC_TARGET_FILESYSTEM) {
fs.removeSync(syncDir) fs.removeSync(syncDir)
fs.mkdirpSync(syncDir, 0o755); fs.mkdirpSync(syncDir, 0o755);
fileApi_ = new FileApi(syncDir, new FileApiDriverLocal()); fileApi_ = new FileApi(syncDir, new FileApiDriverLocal());
fileApi_.setLogger(logger);
return fileApi_;
} else { } else {
fileApi_ = new FileApi('/root', new FileApiDriverMemory()); fileApi_ = new FileApi('/root', new FileApiDriverMemory());
fileApi_.setLogger(logger); fileApi_.setLogger(logger);
return fileApi_;
} }
fileApi_.setLogger(logger);
fileApi_.setSyncTargetId(syncTargetId_);
return fileApi_;
} }
export { setupDatabase, setupDatabaseAndSynchronizer, db, synchronizer, fileApi, sleep, clearDatabase, switchClient, syncTargetId }; export { setupDatabase, setupDatabaseAndSynchronizer, db, synchronizer, fileApi, sleep, clearDatabase, switchClient, syncTargetId };

View File

@ -41,7 +41,7 @@ class StatusScreenComponent extends BaseScreenComponent {
async resfreshScreen() { async resfreshScreen() {
let service = new ReportService(); let service = new ReportService();
let report = await service.status(Database.enumId('syncTarget', Setting.value('sync.target'))); let report = await service.status(Setting.value('sync.target'));
this.setState({ report: report }); this.setState({ report: report });
} }

View File

@ -157,20 +157,6 @@ class Database {
throw new Error('Unknown enum type or value: ' + type + ', ' + s); throw new Error('Unknown enum type or value: ' + type + ', ' + s);
} }
static enumName(type, id) {
if (type == 'syncTarget') {
if (id === 1) return 'memory';
if (id === 2) return 'filesystem';
if (id === 3) return 'onedrive';
}
throw new Error('Unknown enum type or id: ' + type + ', ' + id);
}
static enumIds(type) {
if (type == 'syncTarget') return [1,2,3];
throw new Error('Unknown enum type: ' + type);
}
static formatValue(type, value) { static formatValue(type, value) {
if (value === null || value === undefined) return null; if (value === null || value === undefined) return null;
if (type == this.TYPE_INT) return Number(value); if (type == this.TYPE_INT) return Number(value);

View File

@ -4,16 +4,21 @@ import moment from 'moment';
import { BaseItem } from 'lib/models/base-item.js'; import { BaseItem } from 'lib/models/base-item.js';
import { time } from 'lib/time-utils.js'; import { time } from 'lib/time-utils.js';
// NOTE: when synchronising with the file system the time resolution is the second (unlike milliseconds for OneDrive for instance).
// What it means is that if, for example, client 1 changes a note at time t, and client 2 changes the same note within the same second,
// both clients will not know about each others updates during the next sync. They will simply both sync their note and whoever
// comes last will overwrite (on the remote storage) the note of the other client. Both client will then have a different note at
// that point and that will only be resolved if one of them changes the note and sync (if they don't change it, it will never get resolved).
//
// This is compound with the fact that we can't have a reliable delta API on the file system so we need to check all the timestamps
// every time and rely on this exclusively to know about changes.
//
// This explains occasional failures of the fuzzing program (it finds that the clients end up with two different notes after sync). To
// check that it is indeed the problem, check log-database.txt of both clients, search for the note ID, and most likely both notes
// will have been modified at the same exact second at some point. If not, it's another bug that needs to be investigated.
class FileApiDriverLocal { class FileApiDriverLocal {
syncTargetId() {
return 2;
}
syncTargetName() {
return 'filesystem';
}
fsErrorToJsError_(error) { fsErrorToJsError_(error) {
let msg = error.toString(); let msg = error.toString();
let output = new Error(msg); let output = new Error(msg);

View File

@ -2,14 +2,6 @@ import { time } from 'lib/time-utils.js';
class FileApiDriverMemory { class FileApiDriverMemory {
syncTargetId() {
return 1;
}
syncTargetName() {
return 'memory';
}
constructor() { constructor() {
this.items_ = []; this.items_ = [];
this.deletedItems_ = []; this.deletedItems_ = [];

View File

@ -9,14 +9,6 @@ class FileApiDriverOneDrive {
this.api_ = api; this.api_ = api;
} }
syncTargetId() {
return 3;
}
syncTargetName() {
return 'onedrive';
}
api() { api() {
return this.api_; return this.api_;
} }

View File

@ -7,12 +7,22 @@ class FileApi {
this.baseDir_ = baseDir; this.baseDir_ = baseDir;
this.driver_ = driver; this.driver_ = driver;
this.logger_ = new Logger(); this.logger_ = new Logger();
this.syncTargetId_ = null;
} }
driver() { driver() {
return this.driver_; return this.driver_;
} }
setSyncTargetId(v) {
this.syncTargetId_ = v;
}
syncTargetId() {
if (this.syncTargetId_ === null) throw new Error('syncTargetId has not been set!!');
return this.syncTargetId_;
}
supportsDelta() { supportsDelta() {
return this.driver_.supportsDelta(); return this.driver_.supportsDelta();
} }

View File

@ -1,5 +1,6 @@
import { BaseModel } from 'lib/base-model.js'; import { BaseModel } from 'lib/base-model.js';
import { Database } from 'lib/database.js'; import { Database } from 'lib/database.js';
import { Setting } from 'lib/models/setting.js';
import { time } from 'lib/time-utils.js'; import { time } from 'lib/time-utils.js';
import { sprintf } from 'sprintf-js'; import { sprintf } from 'sprintf-js';
import moment from 'moment'; import moment from 'moment';
@ -136,7 +137,7 @@ class BaseItem extends BaseModel {
await super.batchDelete(ids, options); await super.batchDelete(ids, options);
if (trackDeleted) { if (trackDeleted) {
const syncTargetIds = Database.enumIds('syncTarget'); const syncTargetIds = Setting.enumOptionValues('sync.target');
let queries = []; let queries = [];
let now = time.unixMs(); let now = time.unixMs();
for (let i = 0; i < ids.length; i++) { for (let i = 0; i < ids.length; i++) {
@ -313,10 +314,6 @@ class BaseItem extends BaseModel {
limit); limit);
let neverSyncedItem = await ItemClass.modelSelectAll(sql); let neverSyncedItem = await ItemClass.modelSelectAll(sql);
//for (let i = 0; i < neverSyncedItem.length; i++) neverSyncedItem[i].sync_time = 0;
// console.info(sql);
// console.info('NEVER', neverSyncedItem);
// Secondly get the items that have been synced under this sync target but that have been changed since then // Secondly get the items that have been synced under this sync target but that have been changed since then
@ -344,8 +341,6 @@ class BaseItem extends BaseModel {
changedItems = await ItemClass.modelSelectAll(sql); changedItems = await ItemClass.modelSelectAll(sql);
} }
// console.info('CHANGED', changedItems);
const items = neverSyncedItem.concat(changedItems); const items = neverSyncedItem.concat(changedItems);
if (i >= classNames.length - 1) { if (i >= classNames.length - 1) {
@ -353,63 +348,6 @@ class BaseItem extends BaseModel {
} else { } else {
if (items.length) return { hasMore: true, items: items }; if (items.length) return { hasMore: true, items: items };
} }
//let extraWhere = className == 'Note' ? 'AND is_conflict = 0' : '';
// First get all the items that have never been synced under this sync target
// let sql = sprintf(`
// SELECT %s FROM %s items
// LEFT JOIN sync_items t ON t.item_id = items.id
// WHERE (t.id IS NULL OR t.sync_target != %d) %s
// LIMIT %d
// `,
// this.db().escapeFields(fieldNames),
// this.db().escapeField(ItemClass.tableName()),
// Number(syncTarget),
// extraWhere,
// limit);
// let neverSyncedItem = await ItemClass.modelSelectAll(sql);
// for (let i = 0; i < neverSyncedItem.length; i++) neverSyncedItem[i].sync_time = 0;
// console.info(sql);
// console.info('NEVER', neverSyncedItem);
// // Secondly get the items that have been synced under this sync target but that have been changed since then
// const newLimit = limit - neverSyncedItem.length;
// let changedItems = [];
// if (newLimit > 0) {
// let sql = sprintf(`
// SELECT %s FROM %s items
// LEFT JOIN sync_items t ON t.item_id = items.id
// WHERE (t.sync_time < items.updated_time AND t.sync_target = %d) %s
// LIMIT %d
// `,
// this.db().escapeFields(fieldNames),
// this.db().escapeField(ItemClass.tableName()),
// Number(syncTarget),
// extraWhere,
// newLimit);
// changedItems = await ItemClass.modelSelectAll(sql);
// }
// console.info('CHANGED', changedItems);
// const items = neverSyncedItem.concat(changedItems);
// if (i >= classNames.length - 1) {
// return { hasMore: items.length >= limit, items: items };
// } else {
// if (items.length) return { hasMore: true, items: items };
// }
} }
throw new Error('Unreachable'); throw new Error('Unreachable');

View File

@ -12,9 +12,9 @@ class Setting extends BaseModel {
return BaseModel.TYPE_SETTING; return BaseModel.TYPE_SETTING;
} }
static defaultSetting(key) { static settingMetadata(key) {
if (!(key in this.defaults_)) throw new Error('Unknown key: ' + key); if (!(key in this.metadata_)) throw new Error('Unknown key: ' + key);
let output = Object.assign({}, this.defaults_[key]); let output = Object.assign({}, this.metadata_[key]);
output.key = key; output.key = key;
return output; return output;
} }
@ -22,8 +22,8 @@ class Setting extends BaseModel {
static keys() { static keys() {
if (this.keys_) return this.keys_; if (this.keys_) return this.keys_;
this.keys_ = []; this.keys_ = [];
for (let n in this.defaults_) { for (let n in this.metadata_) {
if (!this.defaults_.hasOwnProperty(n)) continue; if (!this.metadata_.hasOwnProperty(n)) continue;
this.keys_.push(n); this.keys_.push(n);
} }
return this.keys_; return this.keys_;
@ -31,9 +31,9 @@ class Setting extends BaseModel {
static publicKeys() { static publicKeys() {
let output = []; let output = [];
for (let n in this.defaults_) { for (let n in this.metadata_) {
if (!this.defaults_.hasOwnProperty(n)) continue; if (!this.metadata_.hasOwnProperty(n)) continue;
if (this.defaults_[n].public) output.push(n); if (this.metadata_[n].public) output.push(n);
} }
return output; return output;
} }
@ -55,15 +55,24 @@ class Setting extends BaseModel {
if (!this.cache_) throw new Error('Settings have not been initialized!'); if (!this.cache_) throw new Error('Settings have not been initialized!');
for (let i = 0; i < this.cache_.length; i++) { for (let i = 0; i < this.cache_.length; i++) {
if (this.cache_[i].key == key) { let c = this.cache_[i];
if (this.cache_[i].value === value) return; if (c.key == key) {
this.cache_[i].value = value; const md = this.settingMetadata(key);
if (md.type == 'enum') {
if (!this.isAllowedEnumOption(key, value)) {
throw new Error(_('Invalid option value: "%s". Possible values are: %s.', value, this.enumOptionsDoc(key)));
}
}
if (c.value === value) return;
c.value = value;
this.scheduleUpdate(); this.scheduleUpdate();
return; return;
} }
} }
let s = this.defaultSetting(key); let s = this.settingMetadata(key);
s.value = value; s.value = value;
this.cache_.push(s); this.cache_.push(s);
this.scheduleUpdate(); this.scheduleUpdate();
@ -84,10 +93,54 @@ class Setting extends BaseModel {
} }
} }
let s = this.defaultSetting(key); let s = this.settingMetadata(key);
return s.value; return s.value;
} }
static isEnum(key) {
const md = this.settingMetadata(key);
return md.type == 'enum';
}
static enumOptionValues(key) {
const options = this.enumOptions(key);
let output = [];
for (let n in options) {
if (!options.hasOwnProperty(n)) continue;
output.push(n);
}
return output;
}
static enumOptionLabel(key, value) {
const options = this.enumOptions(key);
for (let n in options) {
if (n == value) return options[n];
}
return '';
}
static enumOptions(key) {
if (!this.metadata_[key]) throw new Error('Unknown key: ' + key);
if (!this.metadata_[key].options) throw new Error('No options for: ' + key);
return this.metadata_[key].options();
}
static enumOptionsDoc(key) {
const options = this.enumOptions(key);
let output = [];
for (let n in options) {
if (!options.hasOwnProperty(n)) continue;
output.push(_('%s (%s)', n, options[n]));
}
return output.join(', ');
}
static isAllowedEnumOption(key, value) {
const options = this.enumOptions(key);
return !!options[value];
}
// Currently only supports objects with properties one level deep // Currently only supports objects with properties one level deep
static object(key) { static object(key) {
let output = {}; let output = {};
@ -149,9 +202,9 @@ class Setting extends BaseModel {
if (!appType) throw new Error('appType is required'); if (!appType) throw new Error('appType is required');
let output = {}; let output = {};
for (let key in Setting.defaults_) { for (let key in Setting.metadata_) {
if (!Setting.defaults_.hasOwnProperty(key)) continue; if (!Setting.metadata_.hasOwnProperty(key)) continue;
let s = Object.assign({}, Setting.defaults_[key]); let s = Object.assign({}, Setting.metadata_[key]);
if (!s.public) continue; if (!s.public) continue;
if (s.appTypes && s.appTypes.indexOf(appType) < 0) continue; if (s.appTypes && s.appTypes.indexOf(appType) < 0) continue;
s.value = this.value(key); s.value = this.value(key);
@ -162,19 +215,24 @@ class Setting extends BaseModel {
} }
Setting.defaults_ = { Setting.SYNC_TARGET_MEMORY = 1;
Setting.SYNC_TARGET_FILESYSTEM = 2;
Setting.SYNC_TARGET_ONEDRIVE = 3;
Setting.metadata_ = {
'activeFolderId': { value: '', type: 'string', public: false }, 'activeFolderId': { value: '', type: 'string', public: false },
'sync.onedrive.auth': { value: '', type: 'string', public: false }, 'sync.2.path': { value: '', type: 'string', public: true, appTypes: ['cli'] },
'sync.filesystem.path': { value: '', type: 'string', public: true, appTypes: ['cli'] }, 'sync.3.auth': { value: '', type: 'string', public: false },
'sync.target': { value: 'onedrive', type: 'enum', public: true, label: () => _('Synchronisation target'), options: () => ({ 'sync.target': { value: 'onedrive', type: 'enum', public: true, label: () => _('Synchronisation target'), options: () => {
1: 'Memory', let output = {};
2: _('File system'), output[Setting.SYNC_TARGET_MEMORY] = 'Memory';
3: _('OneDrive'), output[Setting.SYNC_TARGET_FILESYSTEM] = _('File system');
})}, output[Setting.SYNC_TARGET_ONEDRIVE] = _('OneDrive');
return output;
}},
'sync.context': { value: '', type: 'string', public: false }, 'sync.context': { value: '', type: 'string', public: false },
'editor': { value: '', type: 'string', public: true, appTypes: ['cli'] }, 'editor': { value: '', type: 'string', public: true, appTypes: ['cli'] },
'locale': { value: 'en_GB', type: 'string', public: true }, 'locale': { value: 'en_GB', type: 'string', public: true },
//'aliases': { value: '', type: 'string', public: true },
'todoFilter': { value: 'all', type: 'enum', public: true, appTypes: ['mobile'], label: () => _('Todo filter'), options: () => ({ 'todoFilter': { value: 'all', type: 'enum', public: true, appTypes: ['mobile'], label: () => _('Todo filter'), options: () => ({
all: _('Show all'), all: _('Show all'),
recent: _('Non-completed and recently completed ones'), recent: _('Non-completed and recently completed ones'),

View File

@ -34,10 +34,10 @@ reg.oneDriveApi = () => {
reg.oneDriveApi_.on('authRefreshed', (a) => { reg.oneDriveApi_.on('authRefreshed', (a) => {
reg.logger().info('Saving updated OneDrive auth.'); reg.logger().info('Saving updated OneDrive auth.');
Setting.setValue('sync.onedrive.auth', a ? JSON.stringify(a) : null); Setting.setValue('sync.3.auth', a ? JSON.stringify(a) : null);
}); });
let auth = Setting.value('sync.onedrive.auth'); let auth = Setting.value('sync.3.auth');
if (auth) { if (auth) {
try { try {
auth = JSON.parse(auth); auth = JSON.parse(auth);
@ -61,20 +61,20 @@ reg.synchronizer = async (syncTargetId) => {
let fileApi = null; let fileApi = null;
if (syncTargetId == 'onedrive') { if (syncTargetId == Setting.SYNC_TARGET_ONEDRIVE) {
if (!reg.oneDriveApi().auth()) throw new Error('User is not authentified'); if (!reg.oneDriveApi().auth()) throw new Error('User is not authentified');
let appDir = await reg.oneDriveApi().appDirectory(); let appDir = await reg.oneDriveApi().appDirectory();
fileApi = new FileApi(appDir, new FileApiDriverOneDrive(reg.oneDriveApi())); fileApi = new FileApi(appDir, new FileApiDriverOneDrive(reg.oneDriveApi()));
} else if (syncTargetId == 'memory') { } else if (syncTargetId == Setting.SYNC_TARGET_MEMORY) {
fileApi = new FileApi('joplin', new FileApiDriverMemory()); fileApi = new FileApi('joplin', new FileApiDriverMemory());
} else if (syncTargetId == 'filesystem') { } else if (syncTargetId == Setting.SYNC_TARGET_FILESYSTEM) {
let syncDir = Setting.value('sync.filesystem.path'); let syncDir = Setting.value('sync.2.path');
if (!syncDir) throw new Error(_('Please set the "sync.filesystem.path" config value to the desired synchronisation destination.')); if (!syncDir) throw new Error(_('Please set the "sync.2.path" config value to the desired synchronisation destination.'));
await shim.fs.mkdirp(syncDir, 0o755); await shim.fs.mkdirp(syncDir, 0o755);
fileApi = new FileApi(syncDir, new shim.FileApiDriverLocal()); fileApi = new FileApi(syncDir, new shim.FileApiDriverLocal());
@ -84,6 +84,7 @@ reg.synchronizer = async (syncTargetId) => {
} }
fileApi.setSyncTargetId(syncTargetId);
fileApi.setLogger(reg.logger()); fileApi.setLogger(reg.logger());
let sync = new Synchronizer(reg.db(), fileApi, Setting.value('appType')); let sync = new Synchronizer(reg.db(), fileApi, Setting.value('appType'));

View File

@ -153,7 +153,7 @@ class Synchronizer {
const lastContext = options.context ? options.context : {}; const lastContext = options.context ? options.context : {};
const syncTargetId = this.api().driver().syncTargetId(); const syncTargetId = this.api().syncTargetId();
if (this.state() != 'idle') { if (this.state() != 'idle') {
this.logger().info('Synchronization is already in progress. State: ' + this.state()); this.logger().info('Synchronization is already in progress. State: ' + this.state());
@ -176,7 +176,7 @@ class Synchronizer {
this.dispatch({ type: 'SYNC_STARTED' }); this.dispatch({ type: 'SYNC_STARTED' });
this.logSyncOperation('starting', null, null, 'Starting synchronization to ' + this.api().driver().syncTargetName() + ' (' + syncTargetId + ')... [' + synchronizationId + ']'); this.logSyncOperation('starting', null, null, 'Starting synchronization to target ' + syncTargetId + '... [' + synchronizationId + ']');
try { try {
await this.api().mkdir(this.syncDirName_); await this.api().mkdir(this.syncDirName_);