You've already forked joplin
mirror of
https://github.com/laurent22/joplin.git
synced 2025-09-16 08:56:40 +02:00
tags
This commit is contained in:
@@ -1,3 +1,5 @@
|
|||||||
|
"use strict"
|
||||||
|
|
||||||
require('source-map-support').install();
|
require('source-map-support').install();
|
||||||
require('babel-plugin-transform-runtime');
|
require('babel-plugin-transform-runtime');
|
||||||
|
|
||||||
@@ -94,12 +96,32 @@ async function clientItems(client) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function randomTag(items) {
|
||||||
|
let tags = [];
|
||||||
|
for (let i = 0; i < items.length; i++) {
|
||||||
|
if (items[i].type_ != 5) continue;
|
||||||
|
tags.push(items[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return randomElement(tags);
|
||||||
|
}
|
||||||
|
|
||||||
|
function randomNote(items) {
|
||||||
|
let notes = [];
|
||||||
|
for (let i = 0; i < items.length; i++) {
|
||||||
|
if (items[i].type_ != 1) continue;
|
||||||
|
notes.push(items[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return randomElement(notes);
|
||||||
|
}
|
||||||
|
|
||||||
async function execRandomCommand(client) {
|
async function execRandomCommand(client) {
|
||||||
let possibleCommands = [
|
let possibleCommands = [
|
||||||
['mkbook {word}', 40], // CREATE FOLDER
|
['mkbook {word}', 40], // CREATE FOLDER
|
||||||
['mknote {word}', 70], // CREATE NOTE
|
['mknote {word}', 70], // CREATE NOTE
|
||||||
[async () => { // DELETE RANDOM ITEM
|
[async () => { // DELETE RANDOM ITEM
|
||||||
let items = clientItems(client);
|
let items = await clientItems(client);
|
||||||
let item = randomElement(items);
|
let item = randomElement(items);
|
||||||
if (!item) return;
|
if (!item) return;
|
||||||
|
|
||||||
@@ -107,6 +129,8 @@ async function execRandomCommand(client) {
|
|||||||
return execCommand(client, 'rm -f ' + item.title);
|
return execCommand(client, 'rm -f ' + item.title);
|
||||||
} else if (item.type_ == 2) {
|
} else if (item.type_ == 2) {
|
||||||
return execCommand(client, 'rm -f ' + '../' + item.title);
|
return execCommand(client, 'rm -f ' + '../' + item.title);
|
||||||
|
} else if (item.type_ == 5) {
|
||||||
|
// tag
|
||||||
} else {
|
} else {
|
||||||
throw new Error('Unknown type: ' + item.type_);
|
throw new Error('Unknown type: ' + item.type_);
|
||||||
}
|
}
|
||||||
@@ -122,12 +146,22 @@ async function execRandomCommand(client) {
|
|||||||
return execCommand(client, 'sync --random-failures', options);
|
return execCommand(client, 'sync --random-failures', options);
|
||||||
}, 30],
|
}, 30],
|
||||||
[async () => { // UPDATE RANDOM ITEM
|
[async () => { // UPDATE RANDOM ITEM
|
||||||
let items = clientItems(client);
|
let items = await clientItems(client);
|
||||||
let item = randomElement(items);
|
let item = randomElement(items);
|
||||||
if (!item) return;
|
if (!item) return;
|
||||||
|
|
||||||
return execCommand(client, 'set ' + item.id + ' title "' + randomWord() + '"');
|
return execCommand(client, 'set ' + item.id + ' title "' + randomWord() + '"');
|
||||||
}, 50],
|
}, 50],
|
||||||
|
[async () => { // ADD TAG
|
||||||
|
let items = await clientItems(client);
|
||||||
|
let note = randomNote(items);
|
||||||
|
if (!note) return;
|
||||||
|
|
||||||
|
let tag = randomTag(items);
|
||||||
|
let tagTitle = !tag || Math.random() >= 0.9 ? 'tag-' + randomWord() : tag.title;
|
||||||
|
|
||||||
|
return execCommand(client, 'tag add ' + tagTitle + ' "' + note.title + '"');
|
||||||
|
}, 50],
|
||||||
];
|
];
|
||||||
|
|
||||||
let cmd = null;
|
let cmd = null;
|
||||||
@@ -171,7 +205,16 @@ function compareItems(item1, item2) {
|
|||||||
if (n == 'sync_time') continue;
|
if (n == 'sync_time') continue;
|
||||||
let p1 = item1[n];
|
let p1 = item1[n];
|
||||||
let p2 = item2[n];
|
let p2 = item2[n];
|
||||||
if (p1 !== p2) output.push(n);
|
|
||||||
|
if (n == 'notes_') {
|
||||||
|
p1.sort();
|
||||||
|
p2.sort();
|
||||||
|
if (JSON.stringify(p1) !== JSON.stringify(p2)) {
|
||||||
|
output.push(n);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (p1 !== p2) output.push(n);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return output;
|
return output;
|
||||||
}
|
}
|
||||||
@@ -235,8 +278,8 @@ async function compareClientItems(clientItems) {
|
|||||||
let diff = compareItems(item1, item2);
|
let diff = compareItems(item1, item2);
|
||||||
if (diff.length) {
|
if (diff.length) {
|
||||||
differences.push({
|
differences.push({
|
||||||
item1: item1,
|
item1: JSON.stringify(item1),
|
||||||
item2: item2,
|
item2: JSON.stringify(item2),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -14,6 +14,7 @@ import { Folder } from 'lib/models/folder.js';
|
|||||||
import { Resource } from 'lib/models/resource.js';
|
import { Resource } from 'lib/models/resource.js';
|
||||||
import { BaseItem } from 'lib/models/base-item.js';
|
import { BaseItem } from 'lib/models/base-item.js';
|
||||||
import { Note } from 'lib/models/note.js';
|
import { Note } from 'lib/models/note.js';
|
||||||
|
import { Tag } from 'lib/models/tag.js';
|
||||||
import { Setting } from 'lib/models/setting.js';
|
import { Setting } from 'lib/models/setting.js';
|
||||||
import { Synchronizer } from 'lib/synchronizer.js';
|
import { Synchronizer } from 'lib/synchronizer.js';
|
||||||
import { Logger } from 'lib/logger.js';
|
import { Logger } from 'lib/logger.js';
|
||||||
@@ -59,7 +60,7 @@ commands.push({
|
|||||||
aliases: ['mkdir'],
|
aliases: ['mkdir'],
|
||||||
description: 'Creates a new notebook',
|
description: 'Creates a new notebook',
|
||||||
action: function(args, end) {
|
action: function(args, end) {
|
||||||
Folder.save({ title: args['notebook'] }).then((folder) => {
|
Folder.save({ title: args['notebook'] }, { duplicateCheck: true }).then((folder) => {
|
||||||
switchCurrentFolder(folder);
|
switchCurrentFolder(folder);
|
||||||
}).catch((error) => {
|
}).catch((error) => {
|
||||||
this.log(error);
|
this.log(error);
|
||||||
@@ -70,7 +71,7 @@ commands.push({
|
|||||||
});
|
});
|
||||||
|
|
||||||
commands.push({
|
commands.push({
|
||||||
usage: 'mknote <note-title>',
|
usage: 'mknote <note>',
|
||||||
aliases: ['touch'],
|
aliases: ['touch'],
|
||||||
description: 'Creates a new note',
|
description: 'Creates a new note',
|
||||||
action: function(args, end) {
|
action: function(args, end) {
|
||||||
@@ -81,7 +82,7 @@ commands.push({
|
|||||||
}
|
}
|
||||||
|
|
||||||
let note = {
|
let note = {
|
||||||
title: args['note-title'],
|
title: args['note'],
|
||||||
parent_id: currentFolder.id,
|
parent_id: currentFolder.id,
|
||||||
};
|
};
|
||||||
Note.save(note).catch((error) => {
|
Note.save(note).catch((error) => {
|
||||||
@@ -265,6 +266,45 @@ commands.push({
|
|||||||
autocomplete: autocompleteItems,
|
autocomplete: autocompleteItems,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
commands.push({
|
||||||
|
usage: 'tag <command> [tag] [note]',
|
||||||
|
description: '<command> can be "add", "remove" or "list" to assign or remove [tag] from [note], or to list the notes associated with [tag]. The command `tag list` can be used to list all the tags.',
|
||||||
|
action: async function(args, end) {
|
||||||
|
try {
|
||||||
|
let tag = null;
|
||||||
|
if (args.tag) tag = await loadItem(BaseModel.MODEL_TYPE_TAG, args.tag);
|
||||||
|
let note = null;
|
||||||
|
if (args.note) note = await loadItem(BaseModel.MODEL_TYPE_NOTE, args.note);
|
||||||
|
|
||||||
|
if (args.command == 'remove' && !tag) throw new Error(_('Tag does not exist: "%s"', args.tag));
|
||||||
|
|
||||||
|
if (args.command == 'add') {
|
||||||
|
if (!note) throw new Error(_('Note does not exist: "%s"', args.note));
|
||||||
|
if (!tag) tag = await Tag.save({ title: args.tag });
|
||||||
|
await Tag.addNote(tag.id, note.id);
|
||||||
|
} else if (args.command == 'remove') {
|
||||||
|
if (!tag) throw new Error(_('Tag does not exist: "%s"', args.tag));
|
||||||
|
if (!note) throw new Error(_('Note does not exist: "%s"', args.note));
|
||||||
|
await Tag.removeNote(tag.id, note.id);
|
||||||
|
} else if (args.command == 'list') {
|
||||||
|
if (tag) {
|
||||||
|
let notes = await Tag.notes(tag.id);
|
||||||
|
notes.map((note) => { this.log(note.title); });
|
||||||
|
} else {
|
||||||
|
let tags = await Tag.all();
|
||||||
|
tags.map((tag) => { this.log(tag.title); });
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
throw new Error(_('Invalid command: "%s"', args.command));
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
this.log(error);
|
||||||
|
}
|
||||||
|
|
||||||
|
end();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
commands.push({
|
commands.push({
|
||||||
usage: 'dump',
|
usage: 'dump',
|
||||||
description: 'Dumps the complete database as JSON.',
|
description: 'Dumps the complete database as JSON.',
|
||||||
@@ -278,6 +318,13 @@ commands.push({
|
|||||||
items.push(folder);
|
items.push(folder);
|
||||||
items = items.concat(notes);
|
items = items.concat(notes);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let tags = await Tag.all();
|
||||||
|
for (let i = 0; i < tags.length; i++) {
|
||||||
|
tags[i].notes_ = await Tag.tagNoteIds(tags[i].id);
|
||||||
|
}
|
||||||
|
|
||||||
|
items = items.concat(tags);
|
||||||
|
|
||||||
this.log(JSON.stringify(items));
|
this.log(JSON.stringify(items));
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@@ -285,8 +332,7 @@ commands.push({
|
|||||||
}
|
}
|
||||||
|
|
||||||
end();
|
end();
|
||||||
},
|
}
|
||||||
autocomplete: autocompleteFolders,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
commands.push({
|
commands.push({
|
||||||
@@ -495,6 +541,19 @@ commands.push({
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
async function loadItem(type, pattern) {
|
||||||
|
let output = await loadItems(type, pattern);
|
||||||
|
return output.length ? output[0] : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function loadItems(type, pattern) {
|
||||||
|
let ItemClass = BaseItem.itemClass(type);
|
||||||
|
let item = await ItemClass.loadByTitle(pattern);
|
||||||
|
if (item) return [item];
|
||||||
|
item = await ItemClass.load(pattern);
|
||||||
|
return [item];
|
||||||
|
}
|
||||||
|
|
||||||
function commandByName(name) {
|
function commandByName(name) {
|
||||||
for (let i = 0; i < commands.length; i++) {
|
for (let i = 0; i < commands.length; i++) {
|
||||||
let c = commands[i];
|
let c = commands[i];
|
||||||
@@ -519,58 +578,6 @@ function execCommand(name, args) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// async function execCommand(args) {
|
|
||||||
// var parseArgs = require('minimist');
|
|
||||||
|
|
||||||
// let results = parseArgs(args);
|
|
||||||
// //var results = vorpal.parse(args, { use: 'minimist' });
|
|
||||||
// if (!results['_'].length) throw new Error(_('Invalid command: %s', args));
|
|
||||||
|
|
||||||
// console.info(results);
|
|
||||||
|
|
||||||
// let commandName = results['_'].splice(0, 1);
|
|
||||||
// let cmd = commandByName(commandName);
|
|
||||||
// if (!cmd) throw new Error(_('Unknown command: %s', args));
|
|
||||||
|
|
||||||
|
|
||||||
// let usage = cmd.usage.split(' ');
|
|
||||||
// let commandArgs = [];
|
|
||||||
// usage.splice(0, 1);
|
|
||||||
// for (let i = 0; i < usage.length; i++) {
|
|
||||||
// let u = usage[i].trim();
|
|
||||||
// if (u == '') continue;
|
|
||||||
|
|
||||||
// let required = false;
|
|
||||||
|
|
||||||
// if (u.length >= 3 && u[0] == '<' && u[u.length - 1] == '>') {
|
|
||||||
// required = true;
|
|
||||||
// u = u.substr(1, u.length - 2);
|
|
||||||
// }
|
|
||||||
|
|
||||||
// if (u.length >= 3 && u[0] == '[' && u[u.length - 1] == ']') {
|
|
||||||
// u = u.substr(1, u.length - 2);
|
|
||||||
// }
|
|
||||||
|
|
||||||
// if (required && !results['_'].length) throw new Error(_('Missing argument: %s', args));
|
|
||||||
|
|
||||||
// if (!results['_'].length) break;
|
|
||||||
|
|
||||||
// console.info(u);
|
|
||||||
|
|
||||||
// commandArgs[u] = results['_'].splice(0, 1);
|
|
||||||
// }
|
|
||||||
|
|
||||||
// console.info(commandArgs);
|
|
||||||
|
|
||||||
|
|
||||||
// // usage: 'import-enex <file> [notebook]',
|
|
||||||
// // description: _('Imports en Evernote notebook file (.enex file).'),
|
|
||||||
// // options: [
|
|
||||||
// // ['--fuzzy-matching', 'For debugging purposes. Do not use.'],
|
|
||||||
// // ],
|
|
||||||
|
|
||||||
// }
|
|
||||||
|
|
||||||
async function synchronizer(syncTarget) {
|
async function synchronizer(syncTarget) {
|
||||||
if (synchronizers_[syncTarget]) return synchronizers_[syncTarget];
|
if (synchronizers_[syncTarget]) return synchronizers_[syncTarget];
|
||||||
|
|
||||||
|
@@ -31,7 +31,7 @@ function sleep(n) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function switchClient(id) {
|
async function switchClient(id) {
|
||||||
await time.msleep(200);
|
await time.msleep(200); // Always leave a little time so that updated_time properties don't overlap
|
||||||
await Setting.saveAll();
|
await Setting.saveAll();
|
||||||
|
|
||||||
currentClient_ = id;
|
currentClient_ = id;
|
||||||
|
@@ -128,16 +128,35 @@ class BaseModel {
|
|||||||
if (options.orderByDir) sql += ' ' + options.orderByDir;
|
if (options.orderByDir) sql += ' ' + options.orderByDir;
|
||||||
}
|
}
|
||||||
if (options.limit) sql += ' LIMIT ' + options.limit;
|
if (options.limit) sql += ' LIMIT ' + options.limit;
|
||||||
//if (options.fields && options.fields.length) sql = sql.replace('SELECT *', 'SELECT ' + this.db().escapeFields(options.fields).join(','));
|
|
||||||
|
|
||||||
return { sql: sql, params: params };
|
return { sql: sql, params: params };
|
||||||
}
|
}
|
||||||
|
|
||||||
static async all(options = null) {
|
static async all(options = null) {
|
||||||
let q = this.applySqlOptions(options, 'SELECT * FROM `' + this.tableName() + '`');
|
let q = this.applySqlOptions(options, 'SELECT * FROM `' + this.tableName() + '`');
|
||||||
return this.modelSelectAll(q.sql);
|
return this.modelSelectAll(q.sql);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static async search(options = null) {
|
||||||
|
if (!options) options = {};
|
||||||
|
if (!options.fields) options.fields = '*';
|
||||||
|
|
||||||
|
let conditions = options.conditions ? options.conditions.slice(0) : [];
|
||||||
|
let params = options.conditionsParams ? options.conditionsParams.slice(0) : [];
|
||||||
|
|
||||||
|
if (options.titlePattern) {
|
||||||
|
let pattern = options.titlePattern.replace(/\*/g, '%');
|
||||||
|
conditions.push('title LIKE ?');
|
||||||
|
params.push(pattern);
|
||||||
|
}
|
||||||
|
|
||||||
|
let sql = 'SELECT ' + this.db().escapeFields(options.fields) + ' FROM `' + this.tableName() + '`';
|
||||||
|
if (conditions.length) sql += ' WHERE ' + conditions.join(' AND ');
|
||||||
|
|
||||||
|
let query = this.applySqlOptions(options, sql, params);
|
||||||
|
return this.modelSelectAll(query.sql, query.params);
|
||||||
|
}
|
||||||
|
|
||||||
static modelSelectOne(sql, params = null) {
|
static modelSelectOne(sql, params = null) {
|
||||||
if (params === null) params = [];
|
if (params === null) params = [];
|
||||||
return this.db().selectOne(sql, params).then((model) => {
|
return this.db().selectOne(sql, params).then((model) => {
|
||||||
|
@@ -163,10 +163,13 @@ class Database {
|
|||||||
}
|
}
|
||||||
|
|
||||||
escapeField(field) {
|
escapeField(field) {
|
||||||
|
if (field == '*') return '*';
|
||||||
return '`' + field + '`';
|
return '`' + field + '`';
|
||||||
}
|
}
|
||||||
|
|
||||||
escapeFields(fields) {
|
escapeFields(fields) {
|
||||||
|
if (fields == '*') return '*';
|
||||||
|
|
||||||
let output = [];
|
let output = [];
|
||||||
for (let i = 0; i < fields.length; i++) {
|
for (let i = 0; i < fields.length; i++) {
|
||||||
output.push(this.escapeField(fields[i]));
|
output.push(this.escapeField(fields[i]));
|
||||||
@@ -174,50 +177,46 @@ class Database {
|
|||||||
return output;
|
return output;
|
||||||
}
|
}
|
||||||
|
|
||||||
selectOne(sql, params = null) {
|
async tryCall(callName, sql, params) {
|
||||||
this.logQuery(sql, params);
|
|
||||||
return this.driver().selectOne(sql, params).catch((error) => {
|
|
||||||
throw this.sqliteErrorToJsError(error, sql, params);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
selectAll(sql, params = null) {
|
|
||||||
this.logQuery(sql, params);
|
|
||||||
return this.driver().selectAll(sql, params).catch((error) => {
|
|
||||||
throw this.sqliteErrorToJsError(error, sql, params);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
async exec(sql, params = null) {
|
|
||||||
if (typeof sql === 'object') {
|
if (typeof sql === 'object') {
|
||||||
params = sql.params;
|
params = sql.params;
|
||||||
sql = sql.sql;
|
sql = sql.sql;
|
||||||
}
|
}
|
||||||
|
|
||||||
let result = null;
|
|
||||||
let waitTime = 50;
|
let waitTime = 50;
|
||||||
let totalWaitTime = 0;
|
let totalWaitTime = 0;
|
||||||
while (true) {
|
while (true) {
|
||||||
try {
|
try {
|
||||||
this.logQuery(sql, params);
|
this.logQuery(sql, params);
|
||||||
let result = await this.driver().exec(sql, params);
|
let result = await this.driver()[callName](sql, params);
|
||||||
return result;; // No exception was thrown
|
return result; // No exception was thrown
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
throw error;
|
if (error && (error.code == 'SQLITE_IOERR' || error.code == 'SQLITE_BUSY')) {
|
||||||
if (error && error.code == 'SQLITE_IOERR') {
|
if (totalWaitTime >= 20000) throw this.sqliteErrorToJsError(error, sql, params);
|
||||||
if (totalWaitTime >= 20000) throw error;
|
this.logger().warn(sprintf('Error %s: will retry in %s milliseconds', error.code, waitTime));
|
||||||
this.logger().warn(sprintf('SQLITE_IOERR: will retry in %s milliseconds', waitTime));
|
|
||||||
this.logger().warn('Error was: ' + error.toString());
|
this.logger().warn('Error was: ' + error.toString());
|
||||||
await time.msleep(waitTime);
|
await time.msleep(waitTime);
|
||||||
totalWaitTime += waitTime;
|
totalWaitTime += waitTime;
|
||||||
waitTime *= 1.5;
|
waitTime *= 1.5;
|
||||||
} else {
|
} else {
|
||||||
throw this.sqliteErrorToJsError(error, sql, params);
|
throw this.sqliteErrorToJsError(error, sql, params);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async selectOne(sql, params = null) {
|
||||||
|
return this.tryCall('selectOne', sql, params);
|
||||||
|
}
|
||||||
|
|
||||||
|
async selectAll(sql, params = null) {
|
||||||
|
return this.tryCall('selectAll', sql, params);
|
||||||
|
}
|
||||||
|
|
||||||
|
async exec(sql, params = null) {
|
||||||
|
return this.tryCall('exec', sql, params);
|
||||||
|
}
|
||||||
|
|
||||||
transactionExecBatch(queries) {
|
transactionExecBatch(queries) {
|
||||||
if (queries.length <= 0) return Promise.resolve();
|
if (queries.length <= 0) return Promise.resolve();
|
||||||
|
|
||||||
|
@@ -5,6 +5,13 @@ import { time } from 'lib/time-utils.js';
|
|||||||
|
|
||||||
class FileApiDriverLocal {
|
class FileApiDriverLocal {
|
||||||
|
|
||||||
|
fsErrorToJsError_(error) {
|
||||||
|
let msg = error.toString();
|
||||||
|
let output = new Error(msg);
|
||||||
|
if (error.code) output.code = error.code;
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
stat(path) {
|
stat(path) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
fs.stat(path, (error, s) => {
|
fs.stat(path, (error, s) => {
|
||||||
@@ -12,7 +19,7 @@ class FileApiDriverLocal {
|
|||||||
if (error.code == 'ENOENT') {
|
if (error.code == 'ENOENT') {
|
||||||
resolve(null);
|
resolve(null);
|
||||||
} else {
|
} else {
|
||||||
reject(error);
|
reject(this.fsErrorToJsError_(error));
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -45,7 +52,7 @@ class FileApiDriverLocal {
|
|||||||
let t = Math.floor(timestampMs / 1000);
|
let t = Math.floor(timestampMs / 1000);
|
||||||
fs.utimes(path, t, t, (error) => {
|
fs.utimes(path, t, t, (error) => {
|
||||||
if (error) {
|
if (error) {
|
||||||
reject(error);
|
reject(this.fsErrorToJsError_(error));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
resolve();
|
resolve();
|
||||||
@@ -54,20 +61,24 @@ class FileApiDriverLocal {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async list(path, options) {
|
async list(path, options) {
|
||||||
let items = await fs.readdir(path);
|
try {
|
||||||
let output = [];
|
let items = await fs.readdir(path);
|
||||||
for (let i = 0; i < items.length; i++) {
|
let output = [];
|
||||||
let stat = await this.stat(path + '/' + items[i]);
|
for (let i = 0; i < items.length; i++) {
|
||||||
if (!stat) continue; // Has been deleted between the readdir() call and now
|
let stat = await this.stat(path + '/' + items[i]);
|
||||||
stat.path = items[i];
|
if (!stat) continue; // Has been deleted between the readdir() call and now
|
||||||
output.push(stat);
|
stat.path = items[i];
|
||||||
}
|
output.push(stat);
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
items: output,
|
items: output,
|
||||||
hasMore: false,
|
hasMore: false,
|
||||||
context: null,
|
context: null,
|
||||||
};
|
};
|
||||||
|
} catch(error) {
|
||||||
|
throw this.fsErrorToJsError_(error);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async get(path, options) {
|
async get(path, options) {
|
||||||
@@ -81,7 +92,7 @@ class FileApiDriverLocal {
|
|||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if (error.code == 'ENOENT') return null;
|
if (error.code == 'ENOENT') return null;
|
||||||
throw error;
|
throw this.fsErrorToJsError_(error);
|
||||||
}
|
}
|
||||||
|
|
||||||
return output;
|
return output;
|
||||||
@@ -99,7 +110,7 @@ class FileApiDriverLocal {
|
|||||||
|
|
||||||
mkdirp(path, (error) => {
|
mkdirp(path, (error) => {
|
||||||
if (error) {
|
if (error) {
|
||||||
reject(error);
|
reject(this.fsErrorToJsError_(error));
|
||||||
} else {
|
} else {
|
||||||
resolve();
|
resolve();
|
||||||
}
|
}
|
||||||
@@ -112,7 +123,7 @@ class FileApiDriverLocal {
|
|||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
fs.writeFile(path, content, function(error) {
|
fs.writeFile(path, content, function(error) {
|
||||||
if (error) {
|
if (error) {
|
||||||
reject(error);
|
reject(this.fsErrorToJsError_(error));
|
||||||
} else {
|
} else {
|
||||||
resolve();
|
resolve();
|
||||||
}
|
}
|
||||||
@@ -128,7 +139,7 @@ class FileApiDriverLocal {
|
|||||||
// File doesn't exist - it's fine
|
// File doesn't exist - it's fine
|
||||||
resolve();
|
resolve();
|
||||||
} else {
|
} else {
|
||||||
reject(error);
|
reject(this.fsErrorToJsError_(error));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
resolve();
|
resolve();
|
||||||
@@ -152,7 +163,7 @@ class FileApiDriverLocal {
|
|||||||
await time.sleep(1);
|
await time.sleep(1);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
throw error;
|
throw this.fsErrorToJsError_(error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -85,7 +85,12 @@ class Folder extends BaseItem {
|
|||||||
return Folder.save(folder, { isNew: true });
|
return Folder.save(folder, { isNew: true });
|
||||||
}
|
}
|
||||||
|
|
||||||
static save(o, options = null) {
|
static async save(o, options = null) {
|
||||||
|
if (options && options.duplicateCheck === true && o.title) {
|
||||||
|
let existingFolder = await Folder.loadByTitle(o.title);
|
||||||
|
if (existingFolder) throw new Error(_('A notebook with this title already exists: "%s"', o.title));
|
||||||
|
}
|
||||||
|
|
||||||
return super.save(o, options).then((folder) => {
|
return super.save(o, options).then((folder) => {
|
||||||
this.dispatch({
|
this.dispatch({
|
||||||
type: 'FOLDERS_UPDATE_ONE',
|
type: 'FOLDERS_UPDATE_ONE',
|
||||||
|
@@ -56,28 +56,48 @@ class Note extends BaseItem {
|
|||||||
if (!options) options = {};
|
if (!options) options = {};
|
||||||
if (!options.orderBy) options.orderBy = 'updated_time';
|
if (!options.orderBy) options.orderBy = 'updated_time';
|
||||||
if (!options.orderByDir) options.orderByDir = 'DESC';
|
if (!options.orderByDir) options.orderByDir = 'DESC';
|
||||||
|
if (!options.conditions) options.conditions = [];
|
||||||
|
if (!options.conditionsParams) options.conditionsParams = [];
|
||||||
|
if (!options.fields) options.fields = this.previewFields();
|
||||||
|
|
||||||
|
options.conditions.push('is_conflict = 0');
|
||||||
|
|
||||||
|
options.conditions.push('parent_id = ?');
|
||||||
|
options.conditionsParams.push(parentId);
|
||||||
|
|
||||||
let sql = 'SELECT ' + this.previewFieldsSql() + ' FROM notes WHERE is_conflict = 0 AND parent_id = ?';
|
|
||||||
let params = [parentId];
|
|
||||||
if (options.itemTypes && options.itemTypes.length) {
|
if (options.itemTypes && options.itemTypes.length) {
|
||||||
if (options.itemTypes.indexOf('note') >= 0 && options.itemTypes.indexOf('todo') >= 0) {
|
if (options.itemTypes.indexOf('note') >= 0 && options.itemTypes.indexOf('todo') >= 0) {
|
||||||
// Fetch everything
|
// Fetch everything
|
||||||
} else if (options.itemTypes.indexOf('note') >= 0) {
|
} else if (options.itemTypes.indexOf('note') >= 0) {
|
||||||
sql += ' AND is_todo = 0';
|
options.conditions.push('is_todo = 0');
|
||||||
} else if (options.itemTypes.indexOf('todo') >= 0) {
|
} else if (options.itemTypes.indexOf('todo') >= 0) {
|
||||||
sql += ' AND is_todo = 1';
|
options.conditions.push('is_todo = 1');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (options.titlePattern) {
|
return this.search(options);
|
||||||
let pattern = options.titlePattern.replace(/\*/g, '%');
|
|
||||||
sql += ' AND title LIKE ?';
|
|
||||||
params.push(pattern);
|
|
||||||
}
|
|
||||||
|
|
||||||
let query = this.applySqlOptions(options, sql, params);
|
// let sql = 'SELECT ' + this.previewFieldsSql() + ' FROM notes WHERE is_conflict = 0 AND parent_id = ?';
|
||||||
|
// let params = [parentId];
|
||||||
|
// if (options.itemTypes && options.itemTypes.length) {
|
||||||
|
// if (options.itemTypes.indexOf('note') >= 0 && options.itemTypes.indexOf('todo') >= 0) {
|
||||||
|
// // Fetch everything
|
||||||
|
// } else if (options.itemTypes.indexOf('note') >= 0) {
|
||||||
|
// sql += ' AND is_todo = 0';
|
||||||
|
// } else if (options.itemTypes.indexOf('todo') >= 0) {
|
||||||
|
// sql += ' AND is_todo = 1';
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
return this.modelSelectAll(query.sql, query.params);
|
// if (options.titlePattern) {
|
||||||
|
// let pattern = options.titlePattern.replace(/\*/g, '%');
|
||||||
|
// sql += ' AND title LIKE ?';
|
||||||
|
// params.push(pattern);
|
||||||
|
// }
|
||||||
|
|
||||||
|
// let query = this.applySqlOptions(options, sql, params);
|
||||||
|
|
||||||
|
// return this.modelSelectAll(query.sql, query.params);
|
||||||
}
|
}
|
||||||
|
|
||||||
static preview(noteId) {
|
static preview(noteId) {
|
||||||
|
@@ -1,6 +1,7 @@
|
|||||||
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 { BaseItem } from 'lib/models/base-item.js';
|
import { BaseItem } from 'lib/models/base-item.js';
|
||||||
|
import { Note } from 'lib/models/note.js';
|
||||||
import { time } from 'lib/time-utils.js';
|
import { time } from 'lib/time-utils.js';
|
||||||
import lodash from 'lodash';
|
import lodash from 'lodash';
|
||||||
|
|
||||||
@@ -37,6 +38,19 @@ class Tag extends BaseItem {
|
|||||||
return output;
|
return output;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static async notes(tagId) {
|
||||||
|
let noteIds = await this.tagNoteIds(tagId);
|
||||||
|
if (!noteIds.length) return [];
|
||||||
|
|
||||||
|
let noteIdsSql = noteIds.join('","');
|
||||||
|
noteIdsSql = '"' + noteIdsSql + '"';
|
||||||
|
let options = {
|
||||||
|
conditions: ['id IN (' + noteIdsSql + ')'],
|
||||||
|
};
|
||||||
|
|
||||||
|
return Note.search(options);
|
||||||
|
}
|
||||||
|
|
||||||
static async addNote(tagId, noteId) {
|
static async addNote(tagId, noteId) {
|
||||||
let hasIt = await this.hasNote(tagId, noteId);
|
let hasIt = await this.hasNote(tagId, noteId);
|
||||||
if (hasIt) return;
|
if (hasIt) return;
|
||||||
|
Reference in New Issue
Block a user