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:
parent
9b8376f152
commit
a983a9f108
@ -10,20 +10,30 @@ class Command extends BaseCommand {
|
||||
}
|
||||
|
||||
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) {
|
||||
|
||||
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) {
|
||||
let keys = Setting.publicKeys();
|
||||
for (let i = 0; i < keys.length; i++) {
|
||||
this.log(keys[i] + ' = ' + Setting.value(keys[i]));
|
||||
this.log(renderKeyValue(keys[i]));
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (args.name && !args.value) {
|
||||
this.log(args.name + ' = ' + Setting.value(args.name));
|
||||
this.log(renderKeyValue(args.name));
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -16,7 +16,7 @@ class Command extends BaseCommand {
|
||||
|
||||
async action(args) {
|
||||
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++) {
|
||||
let section = report[i];
|
||||
|
@ -42,7 +42,7 @@ async function createClients() {
|
||||
for (let clientId = 0; clientId < 2; clientId++) {
|
||||
let client = createClient(clientId);
|
||||
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);
|
||||
}
|
||||
|
||||
|
@ -70,6 +70,14 @@ msgid ""
|
||||
"current configuration."
|
||||
msgstr ""
|
||||
|
||||
#, javascript-format
|
||||
msgid "%s = %s (%s)"
|
||||
msgstr ""
|
||||
|
||||
#, javascript-format
|
||||
msgid "%s = %s"
|
||||
msgstr ""
|
||||
|
||||
msgid ""
|
||||
"Duplicates the notes matching <pattern> to [notebook]. If no notebook is "
|
||||
"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 ""
|
||||
|
||||
msgid ""
|
||||
"Please set the \"sync.filesystem.path\" config value to the desired "
|
||||
"synchronisation destination."
|
||||
"Please set the \"sync.2.path\" config value to the desired synchronisation "
|
||||
"destination."
|
||||
msgstr ""
|
||||
|
||||
#, javascript-format
|
||||
@ -338,6 +346,14 @@ msgstr ""
|
||||
msgid "Cannot move note to \"%s\" notebook"
|
||||
msgstr ""
|
||||
|
||||
#, javascript-format
|
||||
msgid "Invalid option value: \"%s\". Possible values are: %s."
|
||||
msgstr ""
|
||||
|
||||
#, javascript-format
|
||||
msgid "%s (%s)"
|
||||
msgstr ""
|
||||
|
||||
msgid "Synchronisation target"
|
||||
msgstr ""
|
||||
|
||||
|
@ -66,6 +66,7 @@ msgstr "Affiche tous les détails de la note."
|
||||
msgid "Cannot find \"%s\"."
|
||||
msgstr "Impossible de trouver \"%s\"."
|
||||
|
||||
#, fuzzy
|
||||
msgid ""
|
||||
"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 "
|
||||
@ -75,6 +76,14 @@ msgstr ""
|
||||
"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."
|
||||
|
||||
#, fuzzy, javascript-format
|
||||
msgid "%s = %s (%s)"
|
||||
msgstr "%s %s (%s)"
|
||||
|
||||
#, javascript-format
|
||||
msgid "%s = %s"
|
||||
msgstr ""
|
||||
|
||||
msgid ""
|
||||
"Duplicates the notes matching <pattern> to [notebook]. If no notebook is "
|
||||
"specified the note is duplicated in the current notebook."
|
||||
@ -311,8 +320,8 @@ msgstr ""
|
||||
"logiciel :"
|
||||
|
||||
msgid ""
|
||||
"Please set the \"sync.filesystem.path\" config value to the desired "
|
||||
"synchronisation destination."
|
||||
"Please set the \"sync.2.path\" config value to the desired synchronisation "
|
||||
"destination."
|
||||
msgstr ""
|
||||
|
||||
#, javascript-format
|
||||
@ -373,6 +382,14 @@ msgstr "Impossible de copier la note dans le carnet \"%s\""
|
||||
msgid "Cannot move note to \"%s\" notebook"
|
||||
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
|
||||
msgid "Synchronisation target"
|
||||
msgstr "Cible de la synchronisation : %s"
|
||||
|
@ -70,6 +70,14 @@ msgid ""
|
||||
"current configuration."
|
||||
msgstr ""
|
||||
|
||||
#, javascript-format
|
||||
msgid "%s = %s (%s)"
|
||||
msgstr ""
|
||||
|
||||
#, javascript-format
|
||||
msgid "%s = %s"
|
||||
msgstr ""
|
||||
|
||||
msgid ""
|
||||
"Duplicates the notes matching <pattern> to [notebook]. If no notebook is "
|
||||
"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 ""
|
||||
|
||||
msgid ""
|
||||
"Please set the \"sync.filesystem.path\" config value to the desired "
|
||||
"synchronisation destination."
|
||||
"Please set the \"sync.2.path\" config value to the desired synchronisation "
|
||||
"destination."
|
||||
msgstr ""
|
||||
|
||||
#, javascript-format
|
||||
@ -338,6 +346,14 @@ msgstr ""
|
||||
msgid "Cannot move note to \"%s\" notebook"
|
||||
msgstr ""
|
||||
|
||||
#, javascript-format
|
||||
msgid "Invalid option value: \"%s\". Possible values are: %s."
|
||||
msgstr ""
|
||||
|
||||
#, javascript-format
|
||||
msgid "%s (%s)"
|
||||
msgstr ""
|
||||
|
||||
msgid "Synchronisation target"
|
||||
msgstr ""
|
||||
|
||||
|
@ -38,7 +38,7 @@ async function localItemsSameAsRemote(locals, expect) {
|
||||
expect(!!remote).toBe(true);
|
||||
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);
|
||||
} else {
|
||||
expect(remote.updated_time).toBe(dbItem.updated_time);
|
||||
@ -142,29 +142,20 @@ describe('Synchronizer', function() {
|
||||
await switchClient(2);
|
||||
|
||||
await synchronizer().start();
|
||||
|
||||
await sleep(0.1);
|
||||
|
||||
let note2 = await Note.load(note1.id);
|
||||
note2.title = "Updated on client 2";
|
||||
await Note.save(note2);
|
||||
note2 = await Note.load(note2.id);
|
||||
|
||||
await synchronizer().start();
|
||||
|
||||
await switchClient(1);
|
||||
|
||||
await sleep(0.1);
|
||||
|
||||
let note2conf = await Note.load(note1.id);
|
||||
note2conf.title = "Updated on client 1";
|
||||
await Note.save(note2conf);
|
||||
note2conf = await Note.load(note1.id);
|
||||
|
||||
await synchronizer().start();
|
||||
|
||||
let conflictedNotes = await Note.conflictedNotes();
|
||||
|
||||
expect(conflictedNotes.length).toBe(1);
|
||||
|
||||
// Other than the id (since the conflicted note is a duplicate), and the is_conflict property
|
||||
|
@ -29,10 +29,12 @@ Resource.fsDriver_ = fsDriver;
|
||||
const logDir = __dirname + '/../tests/logs';
|
||||
fs.mkdirpSync(logDir, 0o755);
|
||||
|
||||
//const syncTarget = 'filesystem';
|
||||
const syncTarget = 'memory';
|
||||
//const syncTargetId_ = Setting.SYNC_TARGET_MEMORY;
|
||||
const syncTargetId_ = Setting.SYNC_TARGET_FILESYSTEM;
|
||||
const syncDir = __dirname + '/../tests/sync';
|
||||
|
||||
const sleepTime = syncTargetId_ == Setting.SYNC_TARGET_FILESYSTEM ? 1001 : 200;
|
||||
|
||||
const logger = new Logger();
|
||||
logger.addTarget('file', { path: logDir + '/log.txt' });
|
||||
logger.setLevel(Logger.LEVEL_DEBUG);
|
||||
@ -47,7 +49,7 @@ Setting.setConstant('appId', 'net.cozic.joplin-cli');
|
||||
Setting.setConstant('appType', 'cli');
|
||||
|
||||
function syncTargetId() {
|
||||
return JoplinDatabase.enumId('syncTarget', syncTarget);
|
||||
return syncTargetId_;
|
||||
}
|
||||
|
||||
function sleep(n) {
|
||||
@ -59,7 +61,7 @@ function sleep(n) {
|
||||
}
|
||||
|
||||
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();
|
||||
|
||||
currentClient_ = id;
|
||||
@ -120,7 +122,7 @@ async function setupDatabaseAndSynchronizer(id = null) {
|
||||
synchronizers_[id].setLogger(logger);
|
||||
}
|
||||
|
||||
if (syncTarget == 'filesystem') {
|
||||
if (syncTargetId_ == Setting.SYNC_TARGET_FILESYSTEM) {
|
||||
fs.removeSync(syncDir)
|
||||
fs.mkdirpSync(syncDir, 0o755);
|
||||
} else {
|
||||
@ -141,17 +143,19 @@ function synchronizer(id = null) {
|
||||
function fileApi() {
|
||||
if (fileApi_) return fileApi_;
|
||||
|
||||
if (syncTarget == 'filesystem') {
|
||||
if (syncTargetId_ == Setting.SYNC_TARGET_FILESYSTEM) {
|
||||
fs.removeSync(syncDir)
|
||||
fs.mkdirpSync(syncDir, 0o755);
|
||||
fileApi_ = new FileApi(syncDir, new FileApiDriverLocal());
|
||||
fileApi_.setLogger(logger);
|
||||
return fileApi_;
|
||||
} else {
|
||||
fileApi_ = new FileApi('/root', new FileApiDriverMemory());
|
||||
fileApi_.setLogger(logger);
|
||||
return fileApi_;
|
||||
|
||||
}
|
||||
|
||||
fileApi_.setLogger(logger);
|
||||
fileApi_.setSyncTargetId(syncTargetId_);
|
||||
return fileApi_;
|
||||
}
|
||||
|
||||
export { setupDatabase, setupDatabaseAndSynchronizer, db, synchronizer, fileApi, sleep, clearDatabase, switchClient, syncTargetId };
|
@ -41,7 +41,7 @@ class StatusScreenComponent extends BaseScreenComponent {
|
||||
|
||||
async resfreshScreen() {
|
||||
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 });
|
||||
}
|
||||
|
||||
|
@ -157,20 +157,6 @@ class Database {
|
||||
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) {
|
||||
if (value === null || value === undefined) return null;
|
||||
if (type == this.TYPE_INT) return Number(value);
|
||||
|
@ -4,16 +4,21 @@ import moment from 'moment';
|
||||
import { BaseItem } from 'lib/models/base-item.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 {
|
||||
|
||||
syncTargetId() {
|
||||
return 2;
|
||||
}
|
||||
|
||||
syncTargetName() {
|
||||
return 'filesystem';
|
||||
}
|
||||
|
||||
fsErrorToJsError_(error) {
|
||||
let msg = error.toString();
|
||||
let output = new Error(msg);
|
||||
|
@ -2,14 +2,6 @@ import { time } from 'lib/time-utils.js';
|
||||
|
||||
class FileApiDriverMemory {
|
||||
|
||||
syncTargetId() {
|
||||
return 1;
|
||||
}
|
||||
|
||||
syncTargetName() {
|
||||
return 'memory';
|
||||
}
|
||||
|
||||
constructor() {
|
||||
this.items_ = [];
|
||||
this.deletedItems_ = [];
|
||||
|
@ -9,14 +9,6 @@ class FileApiDriverOneDrive {
|
||||
this.api_ = api;
|
||||
}
|
||||
|
||||
syncTargetId() {
|
||||
return 3;
|
||||
}
|
||||
|
||||
syncTargetName() {
|
||||
return 'onedrive';
|
||||
}
|
||||
|
||||
api() {
|
||||
return this.api_;
|
||||
}
|
||||
|
@ -7,12 +7,22 @@ class FileApi {
|
||||
this.baseDir_ = baseDir;
|
||||
this.driver_ = driver;
|
||||
this.logger_ = new Logger();
|
||||
this.syncTargetId_ = null;
|
||||
}
|
||||
|
||||
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() {
|
||||
return this.driver_.supportsDelta();
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
import { BaseModel } from 'lib/base-model.js';
|
||||
import { Database } from 'lib/database.js';
|
||||
import { Setting } from 'lib/models/setting.js';
|
||||
import { time } from 'lib/time-utils.js';
|
||||
import { sprintf } from 'sprintf-js';
|
||||
import moment from 'moment';
|
||||
@ -136,7 +137,7 @@ class BaseItem extends BaseModel {
|
||||
await super.batchDelete(ids, options);
|
||||
|
||||
if (trackDeleted) {
|
||||
const syncTargetIds = Database.enumIds('syncTarget');
|
||||
const syncTargetIds = Setting.enumOptionValues('sync.target');
|
||||
let queries = [];
|
||||
let now = time.unixMs();
|
||||
for (let i = 0; i < ids.length; i++) {
|
||||
@ -313,10 +314,6 @@ class BaseItem extends BaseModel {
|
||||
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
|
||||
|
||||
@ -344,8 +341,6 @@ class BaseItem extends BaseModel {
|
||||
changedItems = await ItemClass.modelSelectAll(sql);
|
||||
}
|
||||
|
||||
// console.info('CHANGED', changedItems);
|
||||
|
||||
const items = neverSyncedItem.concat(changedItems);
|
||||
|
||||
if (i >= classNames.length - 1) {
|
||||
@ -353,63 +348,6 @@ class BaseItem extends BaseModel {
|
||||
} else {
|
||||
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');
|
||||
|
@ -12,9 +12,9 @@ class Setting extends BaseModel {
|
||||
return BaseModel.TYPE_SETTING;
|
||||
}
|
||||
|
||||
static defaultSetting(key) {
|
||||
if (!(key in this.defaults_)) throw new Error('Unknown key: ' + key);
|
||||
let output = Object.assign({}, this.defaults_[key]);
|
||||
static settingMetadata(key) {
|
||||
if (!(key in this.metadata_)) throw new Error('Unknown key: ' + key);
|
||||
let output = Object.assign({}, this.metadata_[key]);
|
||||
output.key = key;
|
||||
return output;
|
||||
}
|
||||
@ -22,8 +22,8 @@ class Setting extends BaseModel {
|
||||
static keys() {
|
||||
if (this.keys_) return this.keys_;
|
||||
this.keys_ = [];
|
||||
for (let n in this.defaults_) {
|
||||
if (!this.defaults_.hasOwnProperty(n)) continue;
|
||||
for (let n in this.metadata_) {
|
||||
if (!this.metadata_.hasOwnProperty(n)) continue;
|
||||
this.keys_.push(n);
|
||||
}
|
||||
return this.keys_;
|
||||
@ -31,9 +31,9 @@ class Setting extends BaseModel {
|
||||
|
||||
static publicKeys() {
|
||||
let output = [];
|
||||
for (let n in this.defaults_) {
|
||||
if (!this.defaults_.hasOwnProperty(n)) continue;
|
||||
if (this.defaults_[n].public) output.push(n);
|
||||
for (let n in this.metadata_) {
|
||||
if (!this.metadata_.hasOwnProperty(n)) continue;
|
||||
if (this.metadata_[n].public) output.push(n);
|
||||
}
|
||||
return output;
|
||||
}
|
||||
@ -55,15 +55,24 @@ class Setting extends BaseModel {
|
||||
if (!this.cache_) throw new Error('Settings have not been initialized!');
|
||||
|
||||
for (let i = 0; i < this.cache_.length; i++) {
|
||||
if (this.cache_[i].key == key) {
|
||||
if (this.cache_[i].value === value) return;
|
||||
this.cache_[i].value = value;
|
||||
let c = this.cache_[i];
|
||||
if (c.key == key) {
|
||||
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();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
let s = this.defaultSetting(key);
|
||||
let s = this.settingMetadata(key);
|
||||
s.value = value;
|
||||
this.cache_.push(s);
|
||||
this.scheduleUpdate();
|
||||
@ -84,10 +93,54 @@ class Setting extends BaseModel {
|
||||
}
|
||||
}
|
||||
|
||||
let s = this.defaultSetting(key);
|
||||
let s = this.settingMetadata(key);
|
||||
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
|
||||
static object(key) {
|
||||
let output = {};
|
||||
@ -149,9 +202,9 @@ class Setting extends BaseModel {
|
||||
if (!appType) throw new Error('appType is required');
|
||||
|
||||
let output = {};
|
||||
for (let key in Setting.defaults_) {
|
||||
if (!Setting.defaults_.hasOwnProperty(key)) continue;
|
||||
let s = Object.assign({}, Setting.defaults_[key]);
|
||||
for (let key in Setting.metadata_) {
|
||||
if (!Setting.metadata_.hasOwnProperty(key)) continue;
|
||||
let s = Object.assign({}, Setting.metadata_[key]);
|
||||
if (!s.public) continue;
|
||||
if (s.appTypes && s.appTypes.indexOf(appType) < 0) continue;
|
||||
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 },
|
||||
'sync.onedrive.auth': { value: '', type: 'string', public: false },
|
||||
'sync.filesystem.path': { value: '', type: 'string', public: true, appTypes: ['cli'] },
|
||||
'sync.target': { value: 'onedrive', type: 'enum', public: true, label: () => _('Synchronisation target'), options: () => ({
|
||||
1: 'Memory',
|
||||
2: _('File system'),
|
||||
3: _('OneDrive'),
|
||||
})},
|
||||
'sync.2.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: () => {
|
||||
let output = {};
|
||||
output[Setting.SYNC_TARGET_MEMORY] = 'Memory';
|
||||
output[Setting.SYNC_TARGET_FILESYSTEM] = _('File system');
|
||||
output[Setting.SYNC_TARGET_ONEDRIVE] = _('OneDrive');
|
||||
return output;
|
||||
}},
|
||||
'sync.context': { value: '', type: 'string', public: false },
|
||||
'editor': { value: '', type: 'string', public: true, appTypes: ['cli'] },
|
||||
'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: () => ({
|
||||
all: _('Show all'),
|
||||
recent: _('Non-completed and recently completed ones'),
|
||||
|
@ -34,10 +34,10 @@ reg.oneDriveApi = () => {
|
||||
|
||||
reg.oneDriveApi_.on('authRefreshed', (a) => {
|
||||
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) {
|
||||
try {
|
||||
auth = JSON.parse(auth);
|
||||
@ -61,20 +61,20 @@ reg.synchronizer = async (syncTargetId) => {
|
||||
|
||||
let fileApi = null;
|
||||
|
||||
if (syncTargetId == 'onedrive') {
|
||||
if (syncTargetId == Setting.SYNC_TARGET_ONEDRIVE) {
|
||||
|
||||
if (!reg.oneDriveApi().auth()) throw new Error('User is not authentified');
|
||||
let appDir = await reg.oneDriveApi().appDirectory();
|
||||
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());
|
||||
|
||||
} else if (syncTargetId == 'filesystem') {
|
||||
} else if (syncTargetId == Setting.SYNC_TARGET_FILESYSTEM) {
|
||||
|
||||
let syncDir = Setting.value('sync.filesystem.path');
|
||||
if (!syncDir) throw new Error(_('Please set the "sync.filesystem.path" config value to the desired synchronisation destination.'));
|
||||
let syncDir = Setting.value('sync.2.path');
|
||||
if (!syncDir) throw new Error(_('Please set the "sync.2.path" config value to the desired synchronisation destination.'));
|
||||
await shim.fs.mkdirp(syncDir, 0o755);
|
||||
fileApi = new FileApi(syncDir, new shim.FileApiDriverLocal());
|
||||
|
||||
@ -84,6 +84,7 @@ reg.synchronizer = async (syncTargetId) => {
|
||||
|
||||
}
|
||||
|
||||
fileApi.setSyncTargetId(syncTargetId);
|
||||
fileApi.setLogger(reg.logger());
|
||||
|
||||
let sync = new Synchronizer(reg.db(), fileApi, Setting.value('appType'));
|
||||
|
@ -153,7 +153,7 @@ class Synchronizer {
|
||||
|
||||
const lastContext = options.context ? options.context : {};
|
||||
|
||||
const syncTargetId = this.api().driver().syncTargetId();
|
||||
const syncTargetId = this.api().syncTargetId();
|
||||
|
||||
if (this.state() != 'idle') {
|
||||
this.logger().info('Synchronization is already in progress. State: ' + this.state());
|
||||
@ -176,7 +176,7 @@ class Synchronizer {
|
||||
|
||||
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 {
|
||||
await this.api().mkdir(this.syncDirName_);
|
||||
|
Loading…
Reference in New Issue
Block a user