You've already forked joplin
mirror of
https://github.com/laurent22/joplin.git
synced 2025-11-06 09:19:22 +02:00
@@ -1,13 +1,14 @@
|
||||
const { _ } = require("lib/locale.js");
|
||||
const { Logger } = require("lib/logger.js");
|
||||
const Resource = require("lib/models/Resource.js");
|
||||
const { netUtils } = require("lib/net-utils.js");
|
||||
const { _ } = require('lib/locale.js');
|
||||
const { Logger } = require('lib/logger.js');
|
||||
const Resource = require('lib/models/Resource.js');
|
||||
const { netUtils } = require('lib/net-utils.js');
|
||||
|
||||
const http = require("http");
|
||||
const urlParser = require("url");
|
||||
const enableServerDestroy = require("server-destroy");
|
||||
const enableServerDestroy = require('server-destroy');
|
||||
|
||||
class ResourceServer {
|
||||
|
||||
constructor() {
|
||||
this.server_ = null;
|
||||
this.logger_ = new Logger();
|
||||
@@ -29,8 +30,8 @@ class ResourceServer {
|
||||
}
|
||||
|
||||
baseUrl() {
|
||||
if (!this.port_) return "";
|
||||
return "http://127.0.0.1:" + this.port_;
|
||||
if (!this.port_) return '';
|
||||
return 'http://127.0.0.1:' + this.port_;
|
||||
}
|
||||
|
||||
setLinkHandler(handler) {
|
||||
@@ -39,34 +40,35 @@ class ResourceServer {
|
||||
|
||||
async start() {
|
||||
this.port_ = await netUtils.findAvailablePort([9167, 9267, 8167, 8267]);
|
||||
if (!this.port_) {
|
||||
this.logger().error("Could not find available port to start resource server. Please report the error at https://github.com/laurent22/joplin");
|
||||
if (!this.port_) {
|
||||
this.logger().error('Could not find available port to start resource server. Please report the error at https://github.com/laurent22/joplin');
|
||||
return;
|
||||
}
|
||||
|
||||
this.server_ = http.createServer();
|
||||
|
||||
this.server_.on("request", async (request, response) => {
|
||||
const writeResponse = message => {
|
||||
this.server_.on('request', async (request, response) => {
|
||||
|
||||
const writeResponse = (message) => {
|
||||
response.write(message);
|
||||
response.end();
|
||||
};
|
||||
}
|
||||
|
||||
const url = urlParser.parse(request.url, true);
|
||||
let resourceId = url.pathname.split("/");
|
||||
let resourceId = url.pathname.split('/');
|
||||
if (resourceId.length < 2) {
|
||||
writeResponse("Error: could not get resource ID from path name: " + url.pathname);
|
||||
writeResponse('Error: could not get resource ID from path name: ' + url.pathname);
|
||||
return;
|
||||
}
|
||||
resourceId = resourceId[1];
|
||||
|
||||
if (!this.linkHandler_) throw new Error("No link handler is defined");
|
||||
if (!this.linkHandler_) throw new Error('No link handler is defined');
|
||||
|
||||
try {
|
||||
const done = await this.linkHandler_(resourceId, response);
|
||||
if (!done) throw new Error("Unhandled resource: " + resourceId);
|
||||
if (!done) throw new Error('Unhandled resource: ' + resourceId);
|
||||
} catch (error) {
|
||||
response.setHeader("Content-Type", "text/plain");
|
||||
response.setHeader('Content-Type', 'text/plain');
|
||||
response.statusCode = 400;
|
||||
response.write(error.message);
|
||||
}
|
||||
@@ -74,8 +76,8 @@ class ResourceServer {
|
||||
response.end();
|
||||
});
|
||||
|
||||
this.server_.on("error", error => {
|
||||
this.logger().error("Resource server:", error);
|
||||
this.server_.on('error', (error) => {
|
||||
this.logger().error('Resource server:', error);
|
||||
});
|
||||
|
||||
this.server_.listen(this.port_);
|
||||
@@ -89,6 +91,7 @@ class ResourceServer {
|
||||
if (this.server_) this.server_.destroy();
|
||||
this.server_ = null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
module.exports = ResourceServer;
|
||||
module.exports = ResourceServer;
|
||||
@@ -1,39 +1,40 @@
|
||||
const { Logger } = require("lib/logger.js");
|
||||
const Folder = require("lib/models/Folder.js");
|
||||
const Tag = require("lib/models/Tag.js");
|
||||
const BaseModel = require("lib/BaseModel.js");
|
||||
const Note = require("lib/models/Note.js");
|
||||
const Resource = require("lib/models/Resource.js");
|
||||
const { cliUtils } = require("./cli-utils.js");
|
||||
const { reducer, defaultState } = require("lib/reducer.js");
|
||||
const { splitCommandString } = require("lib/string-utils.js");
|
||||
const { reg } = require("lib/registry.js");
|
||||
const { _ } = require("lib/locale.js");
|
||||
const { Logger } = require('lib/logger.js');
|
||||
const Folder = require('lib/models/Folder.js');
|
||||
const Tag = require('lib/models/Tag.js');
|
||||
const BaseModel = require('lib/BaseModel.js');
|
||||
const Note = require('lib/models/Note.js');
|
||||
const Resource = require('lib/models/Resource.js');
|
||||
const { cliUtils } = require('./cli-utils.js');
|
||||
const { reducer, defaultState } = require('lib/reducer.js');
|
||||
const { splitCommandString } = require('lib/string-utils.js');
|
||||
const { reg } = require('lib/registry.js');
|
||||
const { _ } = require('lib/locale.js');
|
||||
|
||||
const chalk = require("chalk");
|
||||
const tk = require("terminal-kit");
|
||||
const TermWrapper = require("tkwidgets/framework/TermWrapper.js");
|
||||
const Renderer = require("tkwidgets/framework/Renderer.js");
|
||||
const DecryptionWorker = require("lib/services/DecryptionWorker");
|
||||
const chalk = require('chalk');
|
||||
const tk = require('terminal-kit');
|
||||
const TermWrapper = require('tkwidgets/framework/TermWrapper.js');
|
||||
const Renderer = require('tkwidgets/framework/Renderer.js');
|
||||
const DecryptionWorker = require('lib/services/DecryptionWorker');
|
||||
|
||||
const BaseWidget = require("tkwidgets/BaseWidget.js");
|
||||
const ListWidget = require("tkwidgets/ListWidget.js");
|
||||
const TextWidget = require("tkwidgets/TextWidget.js");
|
||||
const HLayoutWidget = require("tkwidgets/HLayoutWidget.js");
|
||||
const VLayoutWidget = require("tkwidgets/VLayoutWidget.js");
|
||||
const ReduxRootWidget = require("tkwidgets/ReduxRootWidget.js");
|
||||
const RootWidget = require("tkwidgets/RootWidget.js");
|
||||
const WindowWidget = require("tkwidgets/WindowWidget.js");
|
||||
const BaseWidget = require('tkwidgets/BaseWidget.js');
|
||||
const ListWidget = require('tkwidgets/ListWidget.js');
|
||||
const TextWidget = require('tkwidgets/TextWidget.js');
|
||||
const HLayoutWidget = require('tkwidgets/HLayoutWidget.js');
|
||||
const VLayoutWidget = require('tkwidgets/VLayoutWidget.js');
|
||||
const ReduxRootWidget = require('tkwidgets/ReduxRootWidget.js');
|
||||
const RootWidget = require('tkwidgets/RootWidget.js');
|
||||
const WindowWidget = require('tkwidgets/WindowWidget.js');
|
||||
|
||||
const NoteWidget = require("./gui/NoteWidget.js");
|
||||
const ResourceServer = require("./ResourceServer.js");
|
||||
const NoteMetadataWidget = require("./gui/NoteMetadataWidget.js");
|
||||
const FolderListWidget = require("./gui/FolderListWidget.js");
|
||||
const NoteListWidget = require("./gui/NoteListWidget.js");
|
||||
const StatusBarWidget = require("./gui/StatusBarWidget.js");
|
||||
const ConsoleWidget = require("./gui/ConsoleWidget.js");
|
||||
const NoteWidget = require('./gui/NoteWidget.js');
|
||||
const ResourceServer = require('./ResourceServer.js');
|
||||
const NoteMetadataWidget = require('./gui/NoteMetadataWidget.js');
|
||||
const FolderListWidget = require('./gui/FolderListWidget.js');
|
||||
const NoteListWidget = require('./gui/NoteListWidget.js');
|
||||
const StatusBarWidget = require('./gui/StatusBarWidget.js');
|
||||
const ConsoleWidget = require('./gui/ConsoleWidget.js');
|
||||
|
||||
class AppGui {
|
||||
|
||||
constructor(app, store, keymap) {
|
||||
try {
|
||||
this.app_ = app;
|
||||
@@ -46,12 +47,12 @@ class AppGui {
|
||||
// Some keys are directly handled by the tkwidget framework
|
||||
// so they need to be remapped in a different way.
|
||||
this.tkWidgetKeys_ = {
|
||||
focus_next: "TAB",
|
||||
focus_previous: "SHIFT_TAB",
|
||||
move_up: "UP",
|
||||
move_down: "DOWN",
|
||||
page_down: "PAGE_DOWN",
|
||||
page_up: "PAGE_UP",
|
||||
'focus_next': 'TAB',
|
||||
'focus_previous': 'SHIFT_TAB',
|
||||
'move_up': 'UP',
|
||||
'move_down': 'DOWN',
|
||||
'page_down': 'PAGE_DOWN',
|
||||
'page_up': 'PAGE_UP',
|
||||
};
|
||||
|
||||
this.renderer_ = null;
|
||||
@@ -60,7 +61,7 @@ class AppGui {
|
||||
|
||||
this.renderer_ = new Renderer(this.term(), this.rootWidget_);
|
||||
|
||||
this.app_.on("modelAction", async event => {
|
||||
this.app_.on('modelAction', async (event) => {
|
||||
await this.handleModelAction(event.action);
|
||||
});
|
||||
|
||||
@@ -94,7 +95,7 @@ class AppGui {
|
||||
}
|
||||
|
||||
async forceRender() {
|
||||
this.widget("root").invalidate();
|
||||
this.widget('root').invalidate();
|
||||
await this.renderer_.renderRoot();
|
||||
}
|
||||
|
||||
@@ -106,12 +107,12 @@ class AppGui {
|
||||
return this.term().restoreState(state);
|
||||
}
|
||||
|
||||
prompt(initialText = "", promptString = ":", options = null) {
|
||||
return this.widget("statusBar").prompt(initialText, promptString, options);
|
||||
prompt(initialText = '', promptString = ':', options = null) {
|
||||
return this.widget('statusBar').prompt(initialText, promptString, options);
|
||||
}
|
||||
|
||||
stdoutMaxWidth() {
|
||||
return this.widget("console").innerWidth - 1;
|
||||
return this.widget('console').innerWidth - 1;
|
||||
}
|
||||
|
||||
isDummy() {
|
||||
@@ -120,7 +121,7 @@ class AppGui {
|
||||
|
||||
buildUi() {
|
||||
this.rootWidget_ = new ReduxRootWidget(this.store_);
|
||||
this.rootWidget_.name = "root";
|
||||
this.rootWidget_.name = 'root';
|
||||
this.rootWidget_.autoShortcutsEnabled = false;
|
||||
|
||||
const folderList = new FolderListWidget();
|
||||
@@ -128,21 +129,21 @@ class AppGui {
|
||||
borderBottomWidth: 1,
|
||||
borderRightWidth: 1,
|
||||
};
|
||||
folderList.name = "folderList";
|
||||
folderList.name = 'folderList';
|
||||
folderList.vStretch = true;
|
||||
folderList.on("currentItemChange", async event => {
|
||||
folderList.on('currentItemChange', async (event) => {
|
||||
const item = folderList.currentItem;
|
||||
|
||||
if (item === "-") {
|
||||
if (item === '-') {
|
||||
let newIndex = event.currentIndex + (event.previousIndex < event.currentIndex ? +1 : -1);
|
||||
let nextItem = folderList.itemAt(newIndex);
|
||||
if (!nextItem) nextItem = folderList.itemAt(event.previousIndex);
|
||||
|
||||
if (!nextItem) return; // Normally not possible
|
||||
|
||||
let actionType = "FOLDER_SELECT";
|
||||
if (nextItem.type_ === BaseModel.TYPE_TAG) actionType = "TAG_SELECT";
|
||||
if (nextItem.type_ === BaseModel.TYPE_SEARCH) actionType = "SEARCH_SELECT";
|
||||
let actionType = 'FOLDER_SELECT';
|
||||
if (nextItem.type_ === BaseModel.TYPE_TAG) actionType = 'TAG_SELECT';
|
||||
if (nextItem.type_ === BaseModel.TYPE_SEARCH) actionType = 'SEARCH_SELECT';
|
||||
|
||||
this.store_.dispatch({
|
||||
type: actionType,
|
||||
@@ -150,22 +151,22 @@ class AppGui {
|
||||
});
|
||||
} else if (item.type_ === Folder.modelType()) {
|
||||
this.store_.dispatch({
|
||||
type: "FOLDER_SELECT",
|
||||
type: 'FOLDER_SELECT',
|
||||
id: item ? item.id : null,
|
||||
});
|
||||
} else if (item.type_ === Tag.modelType()) {
|
||||
this.store_.dispatch({
|
||||
type: "TAG_SELECT",
|
||||
type: 'TAG_SELECT',
|
||||
id: item ? item.id : null,
|
||||
});
|
||||
} else if (item.type_ === BaseModel.TYPE_SEARCH) {
|
||||
this.store_.dispatch({
|
||||
type: "SEARCH_SELECT",
|
||||
type: 'SEARCH_SELECT',
|
||||
id: item ? item.id : null,
|
||||
});
|
||||
}
|
||||
});
|
||||
this.rootWidget_.connect(folderList, state => {
|
||||
this.rootWidget_.connect(folderList, (state) => {
|
||||
return {
|
||||
selectedFolderId: state.selectedFolderId,
|
||||
selectedTagId: state.selectedTagId,
|
||||
@@ -178,21 +179,21 @@ class AppGui {
|
||||
});
|
||||
|
||||
const noteList = new NoteListWidget();
|
||||
noteList.name = "noteList";
|
||||
noteList.name = 'noteList';
|
||||
noteList.vStretch = true;
|
||||
noteList.style = {
|
||||
borderBottomWidth: 1,
|
||||
borderLeftWidth: 1,
|
||||
borderRightWidth: 1,
|
||||
};
|
||||
noteList.on("currentItemChange", async () => {
|
||||
noteList.on('currentItemChange', async () => {
|
||||
let note = noteList.currentItem;
|
||||
this.store_.dispatch({
|
||||
type: "NOTE_SELECT",
|
||||
type: 'NOTE_SELECT',
|
||||
id: note ? note.id : null,
|
||||
});
|
||||
});
|
||||
this.rootWidget_.connect(noteList, state => {
|
||||
this.rootWidget_.connect(noteList, (state) => {
|
||||
return {
|
||||
selectedNoteId: state.selectedNoteIds.length ? state.selectedNoteIds[0] : null,
|
||||
items: state.notes,
|
||||
@@ -201,12 +202,12 @@ class AppGui {
|
||||
|
||||
const noteText = new NoteWidget();
|
||||
noteText.hStretch = true;
|
||||
noteText.name = "noteText";
|
||||
noteText.name = 'noteText';
|
||||
noteText.style = {
|
||||
borderBottomWidth: 1,
|
||||
borderLeftWidth: 1,
|
||||
};
|
||||
this.rootWidget_.connect(noteText, state => {
|
||||
this.rootWidget_.connect(noteText, (state) => {
|
||||
return {
|
||||
noteId: state.selectedNoteIds.length ? state.selectedNoteIds[0] : null,
|
||||
notes: state.notes,
|
||||
@@ -215,13 +216,13 @@ class AppGui {
|
||||
|
||||
const noteMetadata = new NoteMetadataWidget();
|
||||
noteMetadata.hStretch = true;
|
||||
noteMetadata.name = "noteMetadata";
|
||||
noteMetadata.name = 'noteMetadata';
|
||||
noteMetadata.style = {
|
||||
borderBottomWidth: 1,
|
||||
borderLeftWidth: 1,
|
||||
borderRightWidth: 1,
|
||||
};
|
||||
this.rootWidget_.connect(noteMetadata, state => {
|
||||
this.rootWidget_.connect(noteMetadata, (state) => {
|
||||
return { noteId: state.selectedNoteIds.length ? state.selectedNoteIds[0] : null };
|
||||
});
|
||||
noteMetadata.hide();
|
||||
@@ -237,58 +238,58 @@ class AppGui {
|
||||
statusBar.hStretch = true;
|
||||
|
||||
const noteLayout = new VLayoutWidget();
|
||||
noteLayout.name = "noteLayout";
|
||||
noteLayout.addChild(noteText, { type: "stretch", factor: 1 });
|
||||
noteLayout.addChild(noteMetadata, { type: "stretch", factor: 1 });
|
||||
noteLayout.name = 'noteLayout';
|
||||
noteLayout.addChild(noteText, { type: 'stretch', factor: 1 });
|
||||
noteLayout.addChild(noteMetadata, { type: 'stretch', factor: 1 });
|
||||
|
||||
const hLayout = new HLayoutWidget();
|
||||
hLayout.name = "hLayout";
|
||||
hLayout.addChild(folderList, { type: "stretch", factor: 1 });
|
||||
hLayout.addChild(noteList, { type: "stretch", factor: 1 });
|
||||
hLayout.addChild(noteLayout, { type: "stretch", factor: 2 });
|
||||
hLayout.name = 'hLayout';
|
||||
hLayout.addChild(folderList, { type: 'stretch', factor: 1 });
|
||||
hLayout.addChild(noteList, { type: 'stretch', factor: 1 });
|
||||
hLayout.addChild(noteLayout, { type: 'stretch', factor: 2 });
|
||||
|
||||
const vLayout = new VLayoutWidget();
|
||||
vLayout.name = "vLayout";
|
||||
vLayout.addChild(hLayout, { type: "stretch", factor: 2 });
|
||||
vLayout.addChild(consoleWidget, { type: "stretch", factor: 1 });
|
||||
vLayout.addChild(statusBar, { type: "fixed", factor: 1 });
|
||||
vLayout.name = 'vLayout';
|
||||
vLayout.addChild(hLayout, { type: 'stretch', factor: 2 });
|
||||
vLayout.addChild(consoleWidget, { type: 'stretch', factor: 1 });
|
||||
vLayout.addChild(statusBar, { type: 'fixed', factor: 1 });
|
||||
|
||||
const win1 = new WindowWidget();
|
||||
win1.addChild(vLayout);
|
||||
win1.name = "mainWindow";
|
||||
win1.name = 'mainWindow';
|
||||
|
||||
this.rootWidget_.addChild(win1);
|
||||
}
|
||||
|
||||
showModalOverlay(text) {
|
||||
if (!this.widget("overlayWindow")) {
|
||||
if (!this.widget('overlayWindow')) {
|
||||
const textWidget = new TextWidget();
|
||||
textWidget.hStretch = true;
|
||||
textWidget.vStretch = true;
|
||||
textWidget.text = "testing";
|
||||
textWidget.name = "overlayText";
|
||||
textWidget.text = 'testing';
|
||||
textWidget.name = 'overlayText';
|
||||
|
||||
const win = new WindowWidget();
|
||||
win.name = "overlayWindow";
|
||||
win.name = 'overlayWindow';
|
||||
win.addChild(textWidget);
|
||||
|
||||
this.rootWidget_.addChild(win);
|
||||
}
|
||||
|
||||
this.widget("overlayWindow").activate();
|
||||
this.widget("overlayText").text = text;
|
||||
this.widget('overlayWindow').activate();
|
||||
this.widget('overlayText').text = text;
|
||||
}
|
||||
|
||||
hideModalOverlay() {
|
||||
if (this.widget("overlayWindow")) this.widget("overlayWindow").hide();
|
||||
this.widget("mainWindow").activate();
|
||||
if (this.widget('overlayWindow')) this.widget('overlayWindow').hide();
|
||||
this.widget('mainWindow').activate();
|
||||
}
|
||||
|
||||
addCommandToConsole(cmd) {
|
||||
if (!cmd) return;
|
||||
const isConfigPassword = cmd.indexOf("config ") >= 0 && cmd.indexOf("password") >= 0;
|
||||
const isConfigPassword = cmd.indexOf('config ') >= 0 && cmd.indexOf('password') >= 0;
|
||||
if (isConfigPassword) return;
|
||||
this.stdout(chalk.cyan.bold("> " + cmd));
|
||||
this.stdout(chalk.cyan.bold('> ' + cmd));
|
||||
}
|
||||
|
||||
setupKeymap(keymap) {
|
||||
@@ -297,15 +298,15 @@ class AppGui {
|
||||
for (let i = 0; i < keymap.length; i++) {
|
||||
const item = Object.assign({}, keymap[i]);
|
||||
|
||||
if (!item.command) throw new Error("Missing command for keymap item: " + JSON.stringify(item));
|
||||
if (!item.command) throw new Error('Missing command for keymap item: ' + JSON.stringify(item));
|
||||
|
||||
if (!("type" in item)) item.type = "exec";
|
||||
if (!('type' in item)) item.type = 'exec';
|
||||
|
||||
if (item.command in this.tkWidgetKeys_) {
|
||||
item.type = "tkwidgets";
|
||||
item.type = 'tkwidgets';
|
||||
}
|
||||
|
||||
item.canRunAlongOtherCommands = item.type === "function" && ["toggle_metadata", "toggle_console"].indexOf(item.command) >= 0;
|
||||
item.canRunAlongOtherCommands = item.type === 'function' && ['toggle_metadata', 'toggle_console'].indexOf(item.command) >= 0;
|
||||
|
||||
output.push(item);
|
||||
}
|
||||
@@ -318,7 +319,7 @@ class AppGui {
|
||||
}
|
||||
|
||||
showConsole(doShow = true) {
|
||||
this.widget("console").show(doShow);
|
||||
this.widget('console').show(doShow);
|
||||
}
|
||||
|
||||
hideConsole() {
|
||||
@@ -326,11 +327,11 @@ class AppGui {
|
||||
}
|
||||
|
||||
consoleIsShown() {
|
||||
return this.widget("console").shown;
|
||||
return this.widget('console').shown;
|
||||
}
|
||||
|
||||
maximizeConsole(doMaximize = true) {
|
||||
const consoleWidget = this.widget("console");
|
||||
const consoleWidget = this.widget('console');
|
||||
|
||||
if (consoleWidget.isMaximized__ === undefined) {
|
||||
consoleWidget.isMaximized__ = false;
|
||||
@@ -339,13 +340,13 @@ class AppGui {
|
||||
if (consoleWidget.isMaximized__ === doMaximize) return;
|
||||
|
||||
let constraints = {
|
||||
type: "stretch",
|
||||
type: 'stretch',
|
||||
factor: !doMaximize ? 1 : 4,
|
||||
};
|
||||
|
||||
consoleWidget.isMaximized__ = doMaximize;
|
||||
|
||||
this.widget("vLayout").setWidgetConstraints(consoleWidget, constraints);
|
||||
this.widget('vLayout').setWidgetConstraints(consoleWidget, constraints);
|
||||
}
|
||||
|
||||
minimizeConsole() {
|
||||
@@ -353,11 +354,11 @@ class AppGui {
|
||||
}
|
||||
|
||||
consoleIsMaximized() {
|
||||
return this.widget("console").isMaximized__ === true;
|
||||
return this.widget('console').isMaximized__ === true;
|
||||
}
|
||||
|
||||
showNoteMetadata(show = true) {
|
||||
this.widget("noteMetadata").show(show);
|
||||
this.widget('noteMetadata').show(show);
|
||||
}
|
||||
|
||||
hideNoteMetadata() {
|
||||
@@ -365,11 +366,11 @@ class AppGui {
|
||||
}
|
||||
|
||||
toggleNoteMetadata() {
|
||||
this.showNoteMetadata(!this.widget("noteMetadata").shown);
|
||||
this.showNoteMetadata(!this.widget('noteMetadata').shown);
|
||||
}
|
||||
|
||||
widget(name) {
|
||||
if (name === "root") return this.rootWidget_;
|
||||
if (name === 'root') return this.rootWidget_;
|
||||
return this.rootWidget_.childByName(name);
|
||||
}
|
||||
|
||||
@@ -402,10 +403,10 @@ class AppGui {
|
||||
}
|
||||
|
||||
activeListItem() {
|
||||
const widget = this.widget("mainWindow").focusedWidget;
|
||||
const widget = this.widget('mainWindow').focusedWidget;
|
||||
if (!widget) return null;
|
||||
|
||||
if (widget.name == "noteList" || widget.name == "folderList") {
|
||||
|
||||
if (widget.name == 'noteList' || widget.name == 'folderList') {
|
||||
return widget.currentItem;
|
||||
}
|
||||
|
||||
@@ -413,48 +414,54 @@ class AppGui {
|
||||
}
|
||||
|
||||
async handleModelAction(action) {
|
||||
this.logger().info("Action:", action);
|
||||
this.logger().info('Action:', action);
|
||||
|
||||
let state = Object.assign({}, defaultState);
|
||||
state.notes = this.widget("noteList").items;
|
||||
state.notes = this.widget('noteList').items;
|
||||
|
||||
let newState = reducer(state, action);
|
||||
|
||||
if (newState !== state) {
|
||||
this.widget("noteList").items = newState.notes;
|
||||
this.widget('noteList').items = newState.notes;
|
||||
}
|
||||
}
|
||||
|
||||
async processFunctionCommand(cmd) {
|
||||
if (cmd === "activate") {
|
||||
const w = this.widget("mainWindow").focusedWidget;
|
||||
if (w.name === "folderList") {
|
||||
this.widget("noteList").focus();
|
||||
} else if (w.name === "noteList" || w.name === "noteText") {
|
||||
this.processPromptCommand("edit $n");
|
||||
|
||||
if (cmd === 'activate') {
|
||||
|
||||
const w = this.widget('mainWindow').focusedWidget;
|
||||
if (w.name === 'folderList') {
|
||||
this.widget('noteList').focus();
|
||||
} else if (w.name === 'noteList' || w.name === 'noteText') {
|
||||
this.processPromptCommand('edit $n');
|
||||
}
|
||||
} else if (cmd === "delete") {
|
||||
if (this.widget("folderList").hasFocus) {
|
||||
const item = this.widget("folderList").selectedJoplinItem;
|
||||
|
||||
} else if (cmd === 'delete') {
|
||||
|
||||
if (this.widget('folderList').hasFocus) {
|
||||
const item = this.widget('folderList').selectedJoplinItem;
|
||||
|
||||
if (!item) return;
|
||||
|
||||
if (item.type_ === BaseModel.TYPE_FOLDER) {
|
||||
await this.processPromptCommand("rmbook " + item.id);
|
||||
await this.processPromptCommand('rmbook ' + item.id);
|
||||
} else if (item.type_ === BaseModel.TYPE_TAG) {
|
||||
this.stdout(_("To delete a tag, untag the associated notes."));
|
||||
this.stdout(_('To delete a tag, untag the associated notes.'));
|
||||
} else if (item.type_ === BaseModel.TYPE_SEARCH) {
|
||||
this.store().dispatch({
|
||||
type: "SEARCH_DELETE",
|
||||
type: 'SEARCH_DELETE',
|
||||
id: item.id,
|
||||
});
|
||||
}
|
||||
} else if (this.widget("noteList").hasFocus) {
|
||||
await this.processPromptCommand("rmnote $n");
|
||||
} else if (this.widget('noteList').hasFocus) {
|
||||
await this.processPromptCommand('rmnote $n');
|
||||
} else {
|
||||
this.stdout(_("Please select the note or notebook to be deleted first."));
|
||||
this.stdout(_('Please select the note or notebook to be deleted first.'));
|
||||
}
|
||||
} else if (cmd === "toggle_console") {
|
||||
|
||||
} else if (cmd === 'toggle_console') {
|
||||
|
||||
if (!this.consoleIsShown()) {
|
||||
this.showConsole();
|
||||
this.minimizeConsole();
|
||||
@@ -465,15 +472,22 @@ class AppGui {
|
||||
this.maximizeConsole();
|
||||
}
|
||||
}
|
||||
} else if (cmd === "toggle_metadata") {
|
||||
|
||||
} else if (cmd === 'toggle_metadata') {
|
||||
|
||||
this.toggleNoteMetadata();
|
||||
} else if (cmd === "enter_command_line_mode") {
|
||||
const cmd = await this.widget("statusBar").prompt();
|
||||
|
||||
} else if (cmd === 'enter_command_line_mode') {
|
||||
|
||||
const cmd = await this.widget('statusBar').prompt();
|
||||
if (!cmd) return;
|
||||
this.addCommandToConsole(cmd);
|
||||
await this.processPromptCommand(cmd);
|
||||
await this.processPromptCommand(cmd);
|
||||
|
||||
} else {
|
||||
throw new Error("Unknown command: " + cmd);
|
||||
|
||||
throw new Error('Unknown command: ' + cmd);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -482,21 +496,21 @@ class AppGui {
|
||||
cmd = cmd.trim();
|
||||
if (!cmd.length) return;
|
||||
|
||||
this.logger().info("Got command: " + cmd);
|
||||
this.logger().info('Got command: ' + cmd);
|
||||
|
||||
try {
|
||||
let note = this.widget("noteList").currentItem;
|
||||
let folder = this.widget("folderList").currentItem;
|
||||
try {
|
||||
let note = this.widget('noteList').currentItem;
|
||||
let folder = this.widget('folderList').currentItem;
|
||||
let args = splitCommandString(cmd);
|
||||
|
||||
for (let i = 0; i < args.length; i++) {
|
||||
if (args[i] == "$n") {
|
||||
args[i] = note ? note.id : "";
|
||||
} else if (args[i] == "$b") {
|
||||
args[i] = folder ? folder.id : "";
|
||||
} else if (args[i] == "$c") {
|
||||
if (args[i] == '$n') {
|
||||
args[i] = note ? note.id : '';
|
||||
} else if (args[i] == '$b') {
|
||||
args[i] = folder ? folder.id : '';
|
||||
} else if (args[i] == '$c') {
|
||||
const item = this.activeListItem();
|
||||
args[i] = item ? item.id : "";
|
||||
args[i] = item ? item.id : '';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -505,40 +519,40 @@ class AppGui {
|
||||
this.stdout(error.message);
|
||||
}
|
||||
|
||||
this.widget("console").scrollBottom();
|
||||
|
||||
this.widget('console').scrollBottom();
|
||||
|
||||
// Invalidate so that the screen is redrawn in case inputting a command has moved
|
||||
// the GUI up (in particular due to autocompletion), it's moved back to the right position.
|
||||
this.widget("root").invalidate();
|
||||
this.widget('root').invalidate();
|
||||
}
|
||||
|
||||
async updateFolderList() {
|
||||
const folders = await Folder.all();
|
||||
this.widget("folderList").items = folders;
|
||||
this.widget('folderList').items = folders;
|
||||
}
|
||||
|
||||
async updateNoteList(folderId) {
|
||||
const fields = Note.previewFields();
|
||||
fields.splice(fields.indexOf("body"), 1);
|
||||
fields.splice(fields.indexOf('body'), 1);
|
||||
const notes = folderId ? await Note.previews(folderId, { fields: fields }) : [];
|
||||
this.widget("noteList").items = notes;
|
||||
this.widget('noteList').items = notes;
|
||||
}
|
||||
|
||||
async updateNoteText(note) {
|
||||
const text = note ? note.body : "";
|
||||
this.widget("noteText").text = text;
|
||||
const text = note ? note.body : '';
|
||||
this.widget('noteText').text = text;
|
||||
}
|
||||
|
||||
// Any key after which a shortcut is not possible.
|
||||
isSpecialKey(name) {
|
||||
return [":", "ENTER", "DOWN", "UP", "LEFT", "RIGHT", "DELETE", "BACKSPACE", "ESCAPE", "TAB", "SHIFT_TAB", "PAGE_UP", "PAGE_DOWN"].indexOf(name) >= 0;
|
||||
return [':', 'ENTER', 'DOWN', 'UP', 'LEFT', 'RIGHT', 'DELETE', 'BACKSPACE', 'ESCAPE', 'TAB', 'SHIFT_TAB', 'PAGE_UP', 'PAGE_DOWN'].indexOf(name) >= 0;
|
||||
}
|
||||
|
||||
fullScreen(enable = true) {
|
||||
if (enable) {
|
||||
this.term().fullscreen();
|
||||
this.term().hideCursor();
|
||||
this.widget("root").invalidate();
|
||||
this.widget('root').invalidate();
|
||||
} else {
|
||||
this.term().fullscreen(false);
|
||||
this.term().showCursor();
|
||||
@@ -548,10 +562,10 @@ class AppGui {
|
||||
stdout(text) {
|
||||
if (text === null || text === undefined) return;
|
||||
|
||||
let lines = text.split("\n");
|
||||
let lines = text.split('\n');
|
||||
for (let i = 0; i < lines.length; i++) {
|
||||
const v = typeof lines[i] === "object" ? JSON.stringify(lines[i]) : lines[i];
|
||||
this.widget("console").addLine(v);
|
||||
const v = typeof lines[i] === 'object' ? JSON.stringify(lines[i]) : lines[i];
|
||||
this.widget('console').addLine(v);
|
||||
}
|
||||
|
||||
this.updateStatusBarMessage();
|
||||
@@ -563,40 +577,40 @@ class AppGui {
|
||||
}
|
||||
|
||||
updateStatusBarMessage() {
|
||||
const consoleWidget = this.widget("console");
|
||||
const consoleWidget = this.widget('console');
|
||||
|
||||
let msg = "";
|
||||
let msg = '';
|
||||
|
||||
const text = consoleWidget.lastLine;
|
||||
|
||||
const cmd = this.app().currentCommand();
|
||||
if (cmd) {
|
||||
msg += cmd.name();
|
||||
if (cmd.cancellable()) msg += " [Press Ctrl+C to cancel]";
|
||||
msg += ": ";
|
||||
if (cmd.cancellable()) msg += ' [Press Ctrl+C to cancel]';
|
||||
msg += ': ';
|
||||
}
|
||||
|
||||
if (text && text.length) {
|
||||
msg += text;
|
||||
}
|
||||
|
||||
if (msg !== "") this.widget("statusBar").setItemAt(0, msg);
|
||||
if (msg !== '') this.widget('statusBar').setItemAt(0, msg);
|
||||
}
|
||||
|
||||
async setupResourceServer() {
|
||||
const linkStyle = chalk.blue.underline;
|
||||
const noteTextWidget = this.widget("noteText");
|
||||
const resourceIdRegex = /^:\/[a-f0-9]+$/i;
|
||||
const noteTextWidget = this.widget('noteText');
|
||||
const resourceIdRegex = /^:\/[a-f0-9]+$/i
|
||||
const noteLinks = {};
|
||||
|
||||
const hasProtocol = function(s, protocols) {
|
||||
if (!s) return false;
|
||||
s = s.trim().toLowerCase();
|
||||
for (let i = 0; i < protocols.length; i++) {
|
||||
if (s.indexOf(protocols[i] + "://") === 0) return true;
|
||||
if (s.indexOf(protocols[i] + '://') === 0) return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
}
|
||||
|
||||
// By default, before the server is started, only the regular
|
||||
// URLs appear in blue.
|
||||
@@ -606,7 +620,7 @@ class AppGui {
|
||||
|
||||
if (resourceIdRegex.test(url)) {
|
||||
return url;
|
||||
} else if (hasProtocol(url, ["http", "https"])) {
|
||||
} else if (hasProtocol(url, ['http', 'https'])) {
|
||||
return linkStyle(url);
|
||||
} else {
|
||||
return url;
|
||||
@@ -619,16 +633,16 @@ class AppGui {
|
||||
this.resourceServer_.setLinkHandler(async (path, response) => {
|
||||
const link = noteLinks[path];
|
||||
|
||||
if (link.type === "url") {
|
||||
response.writeHead(302, { Location: link.url });
|
||||
if (link.type === 'url') {
|
||||
response.writeHead(302, { 'Location': link.url });
|
||||
return true;
|
||||
}
|
||||
|
||||
if (link.type === "resource") {
|
||||
if (link.type === 'resource') {
|
||||
const resourceId = link.id;
|
||||
let resource = await Resource.load(resourceId);
|
||||
if (!resource) throw new Error("No resource with ID " + resourceId); // Should be nearly impossible
|
||||
if (resource.mime) response.setHeader("Content-Type", resource.mime);
|
||||
if (!resource) throw new Error('No resource with ID ' + resourceId); // Should be nearly impossible
|
||||
if (resource.mime) response.setHeader('Content-Type', resource.mime);
|
||||
response.write(await Resource.content(resource));
|
||||
return true;
|
||||
}
|
||||
@@ -645,21 +659,21 @@ class AppGui {
|
||||
|
||||
if (resourceIdRegex.test(url)) {
|
||||
noteLinks[index] = {
|
||||
type: "resource",
|
||||
type: 'resource',
|
||||
id: url.substr(2),
|
||||
};
|
||||
} else if (hasProtocol(url, ["http", "https", "file", "ftp"])) {
|
||||
};
|
||||
} else if (hasProtocol(url, ['http', 'https', 'file', 'ftp'])) {
|
||||
noteLinks[index] = {
|
||||
type: "url",
|
||||
type: 'url',
|
||||
url: url,
|
||||
};
|
||||
} else if (url.indexOf("#") === 0) {
|
||||
return ""; // Anchors aren't supported for now
|
||||
} else if (url.indexOf('#') === 0) {
|
||||
return ''; // Anchors aren't supported for now
|
||||
} else {
|
||||
return url;
|
||||
}
|
||||
|
||||
return linkStyle(this.resourceServer_.baseUrl() + "/" + index);
|
||||
return linkStyle(this.resourceServer_.baseUrl() + '/' + index);
|
||||
},
|
||||
};
|
||||
}
|
||||
@@ -674,16 +688,17 @@ class AppGui {
|
||||
|
||||
this.renderer_.start();
|
||||
|
||||
const statusBar = this.widget("statusBar");
|
||||
const statusBar = this.widget('statusBar');
|
||||
|
||||
term.grabInput();
|
||||
|
||||
term.on("key", async (name, matches, data) => {
|
||||
term.on('key', async (name, matches, data) => {
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Handle special shortcuts
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
if (name === "CTRL_D") {
|
||||
if (name === 'CTRL_D') {
|
||||
const cmd = this.app().currentCommand();
|
||||
|
||||
if (cmd && cmd.cancellable() && !this.commandCancelCalled_) {
|
||||
@@ -696,13 +711,13 @@ class AppGui {
|
||||
return;
|
||||
}
|
||||
|
||||
if (name === "CTRL_C") {
|
||||
if (name === 'CTRL_C' ) {
|
||||
const cmd = this.app().currentCommand();
|
||||
if (!cmd || !cmd.cancellable() || this.commandCancelCalled_) {
|
||||
this.stdout(_('Press Ctrl+D or type "exit" to exit the application'));
|
||||
} else {
|
||||
this.commandCancelCalled_ = true;
|
||||
await cmd.cancel();
|
||||
await cmd.cancel()
|
||||
this.commandCancelCalled_ = false;
|
||||
}
|
||||
return;
|
||||
@@ -711,8 +726,8 @@ class AppGui {
|
||||
// -------------------------------------------------------------------------
|
||||
// Build up current shortcut
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
const now = new Date().getTime();
|
||||
|
||||
const now = (new Date()).getTime();
|
||||
|
||||
if (now - this.lastShortcutKeyTime_ > 800 || this.isSpecialKey(name)) {
|
||||
this.currentShortcutKeys_ = [name];
|
||||
@@ -732,7 +747,7 @@ class AppGui {
|
||||
// Process shortcut and execute associated command
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
const shortcutKey = this.currentShortcutKeys_.join("");
|
||||
const shortcutKey = this.currentShortcutKeys_.join('');
|
||||
let keymapItem = this.keymapItemByKey(shortcutKey);
|
||||
|
||||
// If this command is an alias to another command, resolve to the actual command
|
||||
@@ -742,25 +757,25 @@ class AppGui {
|
||||
if (statusBar.promptActive) processShortcutKeys = false;
|
||||
|
||||
if (processShortcutKeys) {
|
||||
this.logger().info("Shortcut:", shortcutKey, keymapItem);
|
||||
this.logger().info('Shortcut:', shortcutKey, keymapItem);
|
||||
|
||||
this.currentShortcutKeys_ = [];
|
||||
|
||||
if (keymapItem.type === "function") {
|
||||
if (keymapItem.type === 'function') {
|
||||
this.processFunctionCommand(keymapItem.command);
|
||||
} else if (keymapItem.type === "prompt") {
|
||||
} else if (keymapItem.type === 'prompt') {
|
||||
let promptOptions = {};
|
||||
if ("cursorPosition" in keymapItem) promptOptions.cursorPosition = keymapItem.cursorPosition;
|
||||
const commandString = await statusBar.prompt(keymapItem.command ? keymapItem.command : "", null, promptOptions);
|
||||
if ('cursorPosition' in keymapItem) promptOptions.cursorPosition = keymapItem.cursorPosition;
|
||||
const commandString = await statusBar.prompt(keymapItem.command ? keymapItem.command : '', null, promptOptions);
|
||||
this.addCommandToConsole(commandString);
|
||||
await this.processPromptCommand(commandString);
|
||||
} else if (keymapItem.type === "exec") {
|
||||
} else if (keymapItem.type === 'exec') {
|
||||
this.stdout(keymapItem.command);
|
||||
await this.processPromptCommand(keymapItem.command);
|
||||
} else if (keymapItem.type === "tkwidgets") {
|
||||
this.widget("root").handleKey(this.tkWidgetKeys_[keymapItem.command]);
|
||||
} else if (keymapItem.type === 'tkwidgets') {
|
||||
this.widget('root').handleKey(this.tkWidgetKeys_[keymapItem.command]);
|
||||
} else {
|
||||
throw new Error("Unknown command type: " + JSON.stringify(keymapItem));
|
||||
throw new Error('Unknown command type: ' + JSON.stringify(keymapItem));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -774,12 +789,13 @@ class AppGui {
|
||||
console.error(error);
|
||||
}
|
||||
|
||||
process.on("unhandledRejection", (reason, p) => {
|
||||
process.on('unhandledRejection', (reason, p) => {
|
||||
this.fullScreen(false);
|
||||
console.error("Unhandled promise rejection", p, "reason:", reason);
|
||||
console.error('Unhandled promise rejection', p, 'reason:', reason);
|
||||
process.exit(1);
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
AppGui.INPUT_MODE_NORMAL = 1;
|
||||
|
||||
@@ -1,29 +1,30 @@
|
||||
const { BaseApplication } = require("lib/BaseApplication");
|
||||
const { createStore, applyMiddleware } = require("redux");
|
||||
const { reducer, defaultState } = require("lib/reducer.js");
|
||||
const { JoplinDatabase } = require("lib/joplin-database.js");
|
||||
const { Database } = require("lib/database.js");
|
||||
const { FoldersScreenUtils } = require("lib/folders-screen-utils.js");
|
||||
const { DatabaseDriverNode } = require("lib/database-driver-node.js");
|
||||
const BaseModel = require("lib/BaseModel.js");
|
||||
const Folder = require("lib/models/Folder.js");
|
||||
const BaseItem = require("lib/models/BaseItem.js");
|
||||
const Note = require("lib/models/Note.js");
|
||||
const Tag = require("lib/models/Tag.js");
|
||||
const Setting = require("lib/models/Setting.js");
|
||||
const { Logger } = require("lib/logger.js");
|
||||
const { sprintf } = require("sprintf-js");
|
||||
const { reg } = require("lib/registry.js");
|
||||
const { fileExtension } = require("lib/path-utils.js");
|
||||
const { shim } = require("lib/shim.js");
|
||||
const { _, setLocale, defaultLocale, closestSupportedLocale } = require("lib/locale.js");
|
||||
const os = require("os");
|
||||
const fs = require("fs-extra");
|
||||
const { cliUtils } = require("./cli-utils.js");
|
||||
const EventEmitter = require("events");
|
||||
const Cache = require("lib/Cache");
|
||||
const { BaseApplication } = require('lib/BaseApplication');
|
||||
const { createStore, applyMiddleware } = require('redux');
|
||||
const { reducer, defaultState } = require('lib/reducer.js');
|
||||
const { JoplinDatabase } = require('lib/joplin-database.js');
|
||||
const { Database } = require('lib/database.js');
|
||||
const { FoldersScreenUtils } = require('lib/folders-screen-utils.js');
|
||||
const { DatabaseDriverNode } = require('lib/database-driver-node.js');
|
||||
const BaseModel = require('lib/BaseModel.js');
|
||||
const Folder = require('lib/models/Folder.js');
|
||||
const BaseItem = require('lib/models/BaseItem.js');
|
||||
const Note = require('lib/models/Note.js');
|
||||
const Tag = require('lib/models/Tag.js');
|
||||
const Setting = require('lib/models/Setting.js');
|
||||
const { Logger } = require('lib/logger.js');
|
||||
const { sprintf } = require('sprintf-js');
|
||||
const { reg } = require('lib/registry.js');
|
||||
const { fileExtension } = require('lib/path-utils.js');
|
||||
const { shim } = require('lib/shim.js');
|
||||
const { _, setLocale, defaultLocale, closestSupportedLocale } = require('lib/locale.js');
|
||||
const os = require('os');
|
||||
const fs = require('fs-extra');
|
||||
const { cliUtils } = require('./cli-utils.js');
|
||||
const EventEmitter = require('events');
|
||||
const Cache = require('lib/Cache');
|
||||
|
||||
class Application extends BaseApplication {
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
@@ -47,7 +48,7 @@ class Application extends BaseApplication {
|
||||
|
||||
async guessTypeAndLoadItem(pattern, options = null) {
|
||||
let type = BaseModel.TYPE_NOTE;
|
||||
if (pattern.indexOf("/") === 0) {
|
||||
if (pattern.indexOf('/') === 0) {
|
||||
type = BaseModel.TYPE_FOLDER;
|
||||
pattern = pattern.substr(1);
|
||||
}
|
||||
@@ -79,13 +80,13 @@ class Application extends BaseApplication {
|
||||
}
|
||||
|
||||
async loadItems(type, pattern, options = null) {
|
||||
if (type === "folderOrNote") {
|
||||
if (type === 'folderOrNote') {
|
||||
const folders = await this.loadItems(BaseModel.TYPE_FOLDER, pattern, options);
|
||||
if (folders.length) return folders;
|
||||
return await this.loadItems(BaseModel.TYPE_NOTE, pattern, options);
|
||||
}
|
||||
|
||||
pattern = pattern ? pattern.toString() : "";
|
||||
pattern = pattern ? pattern.toString() : '';
|
||||
|
||||
if (type == BaseModel.TYPE_FOLDER && (pattern == Folder.conflictFolderTitle() || pattern == Folder.conflictFolderId())) return [Folder.conflictFolder()];
|
||||
|
||||
@@ -94,16 +95,14 @@ class Application extends BaseApplication {
|
||||
const parent = options.parent ? options.parent : app().currentFolder();
|
||||
const ItemClass = BaseItem.itemClass(type);
|
||||
|
||||
if (type == BaseModel.TYPE_NOTE && pattern.indexOf("*") >= 0) {
|
||||
// Handle it as pattern
|
||||
if (!parent) throw new Error(_("No notebook selected."));
|
||||
if (type == BaseModel.TYPE_NOTE && pattern.indexOf('*') >= 0) { // Handle it as pattern
|
||||
if (!parent) throw new Error(_('No notebook selected.'));
|
||||
return await Note.previews(parent.id, { titlePattern: pattern });
|
||||
} else {
|
||||
// Single item
|
||||
} else { // Single item
|
||||
let item = null;
|
||||
if (type == BaseModel.TYPE_NOTE) {
|
||||
if (!parent) throw new Error(_("No notebook has been specified."));
|
||||
item = await ItemClass.loadFolderNoteByField(parent.id, "title", pattern);
|
||||
if (!parent) throw new Error(_('No notebook has been specified.'));
|
||||
item = await ItemClass.loadFolderNoteByField(parent.id, 'title', pattern);
|
||||
} else {
|
||||
item = await ItemClass.loadByTitle(pattern);
|
||||
}
|
||||
@@ -125,34 +124,34 @@ class Application extends BaseApplication {
|
||||
}
|
||||
|
||||
setupCommand(cmd) {
|
||||
cmd.setStdout(text => {
|
||||
cmd.setStdout((text) => {
|
||||
return this.stdout(text);
|
||||
});
|
||||
|
||||
cmd.setDispatcher(action => {
|
||||
cmd.setDispatcher((action) => {
|
||||
if (this.store()) {
|
||||
return this.store().dispatch(action);
|
||||
} else {
|
||||
return action => {};
|
||||
return (action) => {};
|
||||
}
|
||||
});
|
||||
|
||||
cmd.setPrompt(async (message, options) => {
|
||||
if (!options) options = {};
|
||||
if (!options.type) options.type = "boolean";
|
||||
if (!options.booleanAnswerDefault) options.booleanAnswerDefault = "y";
|
||||
if (!options.answers) options.answers = options.booleanAnswerDefault === "y" ? [_("Y"), _("n")] : [_("N"), _("y")];
|
||||
if (!options.type) options.type = 'boolean';
|
||||
if (!options.booleanAnswerDefault) options.booleanAnswerDefault = 'y';
|
||||
if (!options.answers) options.answers = options.booleanAnswerDefault === 'y' ? [_('Y'), _('n')] : [_('N'), _('y')];
|
||||
|
||||
if (options.type == "boolean") {
|
||||
message += " (" + options.answers.join("/") + ")";
|
||||
if (options.type == 'boolean') {
|
||||
message += ' (' + options.answers.join('/') + ')';
|
||||
}
|
||||
|
||||
let answer = await this.gui().prompt("", message + " ", options);
|
||||
let answer = await this.gui().prompt('', message + ' ', options);
|
||||
|
||||
if (options.type === "boolean") {
|
||||
if (options.type === 'boolean') {
|
||||
if (answer === null) return false; // Pressed ESCAPE
|
||||
if (!answer) answer = options.answers[0];
|
||||
let positiveIndex = options.booleanAnswerDefault == "y" ? 0 : 1;
|
||||
let positiveIndex = options.booleanAnswerDefault == 'y' ? 0 : 1;
|
||||
return answer.toLowerCase() === options.answers[positiveIndex].toLowerCase();
|
||||
} else {
|
||||
return answer;
|
||||
@@ -174,7 +173,7 @@ class Application extends BaseApplication {
|
||||
}, 5000);
|
||||
|
||||
if (await reg.syncTarget().syncStarted()) {
|
||||
this.stdout(_("Cancelling background synchronisation... Please wait."));
|
||||
this.stdout(_('Cancelling background synchronisation... Please wait.'));
|
||||
const sync = await reg.syncTarget().synchronizer();
|
||||
await sync.cancel();
|
||||
}
|
||||
@@ -184,12 +183,12 @@ class Application extends BaseApplication {
|
||||
|
||||
commands(uiType = null) {
|
||||
if (!this.allCommandsLoaded_) {
|
||||
fs.readdirSync(__dirname).forEach(path => {
|
||||
if (path.indexOf("command-") !== 0) return;
|
||||
const ext = fileExtension(path);
|
||||
if (ext != "js") return;
|
||||
fs.readdirSync(__dirname).forEach((path) => {
|
||||
if (path.indexOf('command-') !== 0) return;
|
||||
const ext = fileExtension(path)
|
||||
if (ext != 'js') return;
|
||||
|
||||
let CommandClass = require("./" + path);
|
||||
let CommandClass = require('./' + path);
|
||||
let cmd = new CommandClass();
|
||||
if (!cmd.enabled()) return;
|
||||
cmd = this.setupCommand(cmd);
|
||||
@@ -226,7 +225,7 @@ class Application extends BaseApplication {
|
||||
async commandMetadata() {
|
||||
if (this.commandMetadata_) return this.commandMetadata_;
|
||||
|
||||
let output = await this.cache_.getItem("metadata");
|
||||
let output = await this.cache_.getItem('metadata');
|
||||
if (output) {
|
||||
this.commandMetadata_ = output;
|
||||
return Object.assign({}, this.commandMetadata_);
|
||||
@@ -241,7 +240,7 @@ class Application extends BaseApplication {
|
||||
output[n] = cmd.metadata();
|
||||
}
|
||||
|
||||
await this.cache_.setItem("metadata", output, 1000 * 60 * 60 * 24);
|
||||
await this.cache_.setItem('metadata', output, 1000 * 60 * 60 * 24);
|
||||
|
||||
this.commandMetadata_ = output;
|
||||
return Object.assign({}, this.commandMetadata_);
|
||||
@@ -256,11 +255,11 @@ class Application extends BaseApplication {
|
||||
|
||||
let CommandClass = null;
|
||||
try {
|
||||
CommandClass = require(__dirname + "/command-" + name + ".js");
|
||||
CommandClass = require(__dirname + '/command-' + name + '.js');
|
||||
} catch (error) {
|
||||
if (error.message && error.message.indexOf("Cannot find module") >= 0) {
|
||||
let e = new Error(_("No such command: %s", name));
|
||||
e.type = "notFound";
|
||||
if (error.message && error.message.indexOf('Cannot find module') >= 0) {
|
||||
let e = new Error(_('No such command: %s', name));
|
||||
e.type = 'notFound';
|
||||
throw e;
|
||||
} else {
|
||||
throw error;
|
||||
@@ -275,39 +274,31 @@ class Application extends BaseApplication {
|
||||
|
||||
dummyGui() {
|
||||
return {
|
||||
isDummy: () => {
|
||||
return true;
|
||||
},
|
||||
prompt: (initialText = "", promptString = "", options = null) => {
|
||||
return cliUtils.prompt(initialText, promptString, options);
|
||||
},
|
||||
isDummy: () => { return true; },
|
||||
prompt: (initialText = '', promptString = '', options = null) => { return cliUtils.prompt(initialText, promptString, options); },
|
||||
showConsole: () => {},
|
||||
maximizeConsole: () => {},
|
||||
stdout: text => {
|
||||
console.info(text);
|
||||
},
|
||||
fullScreen: (b = true) => {},
|
||||
stdout: (text) => { console.info(text); },
|
||||
fullScreen: (b=true) => {},
|
||||
exit: () => {},
|
||||
showModalOverlay: text => {},
|
||||
showModalOverlay: (text) => {},
|
||||
hideModalOverlay: () => {},
|
||||
stdoutMaxWidth: () => {
|
||||
return 100;
|
||||
},
|
||||
stdoutMaxWidth: () => { return 100; },
|
||||
forceRender: () => {},
|
||||
termSaveState: () => {},
|
||||
termRestoreState: state => {},
|
||||
termRestoreState: (state) => {},
|
||||
};
|
||||
}
|
||||
|
||||
async execCommand(argv) {
|
||||
if (!argv.length) return this.execCommand(["help"]);
|
||||
reg.logger().info("execCommand()", argv);
|
||||
if (!argv.length) return this.execCommand(['help']);
|
||||
reg.logger().info('execCommand()', argv);
|
||||
const commandName = argv[0];
|
||||
this.activeCommand_ = this.findCommandByName(commandName);
|
||||
|
||||
let outException = null;
|
||||
try {
|
||||
if (this.gui().isDummy() && !this.activeCommand_.supportsUi("cli")) throw new Error(_('The command "%s" is only available in GUI mode', this.activeCommand_.name()));
|
||||
if (this.gui().isDummy() && !this.activeCommand_.supportsUi('cli')) throw new Error(_('The command "%s" is only available in GUI mode', this.activeCommand_.name()));
|
||||
const cmdArgs = cliUtils.makeCommandArgs(this.activeCommand_, argv);
|
||||
await this.activeCommand_.action(cmdArgs);
|
||||
} catch (error) {
|
||||
@@ -323,24 +314,24 @@ class Application extends BaseApplication {
|
||||
|
||||
async loadKeymaps() {
|
||||
const defaultKeyMap = [
|
||||
{ keys: [":"], type: "function", command: "enter_command_line_mode" },
|
||||
{ keys: ["TAB"], type: "function", command: "focus_next" },
|
||||
{ keys: ["SHIFT_TAB"], type: "function", command: "focus_previous" },
|
||||
{ keys: ["UP"], type: "function", command: "move_up" },
|
||||
{ keys: ["DOWN"], type: "function", command: "move_down" },
|
||||
{ keys: ["PAGE_UP"], type: "function", command: "page_up" },
|
||||
{ keys: ["PAGE_DOWN"], type: "function", command: "page_down" },
|
||||
{ keys: ["ENTER"], type: "function", command: "activate" },
|
||||
{ keys: ["DELETE", "BACKSPACE"], type: "function", command: "delete" },
|
||||
{ keys: [" "], command: "todo toggle $n" },
|
||||
{ keys: ["tc"], type: "function", command: "toggle_console" },
|
||||
{ keys: ["tm"], type: "function", command: "toggle_metadata" },
|
||||
{ keys: ["/"], type: "prompt", command: 'search ""', cursorPosition: -2 },
|
||||
{ keys: ["mn"], type: "prompt", command: 'mknote ""', cursorPosition: -2 },
|
||||
{ keys: ["mt"], type: "prompt", command: 'mktodo ""', cursorPosition: -2 },
|
||||
{ keys: ["mb"], type: "prompt", command: 'mkbook ""', cursorPosition: -2 },
|
||||
{ keys: ["yn"], type: "prompt", command: 'cp $n ""', cursorPosition: -2 },
|
||||
{ keys: ["dn"], type: "prompt", command: 'mv $n ""', cursorPosition: -2 },
|
||||
{ "keys": [":"], "type": "function", "command": "enter_command_line_mode" },
|
||||
{ "keys": ["TAB"], "type": "function", "command": "focus_next" },
|
||||
{ "keys": ["SHIFT_TAB"], "type": "function", "command": "focus_previous" },
|
||||
{ "keys": ["UP"], "type": "function", "command": "move_up" },
|
||||
{ "keys": ["DOWN"], "type": "function", "command": "move_down" },
|
||||
{ "keys": ["PAGE_UP"], "type": "function", "command": "page_up" },
|
||||
{ "keys": ["PAGE_DOWN"], "type": "function", "command": "page_down" },
|
||||
{ "keys": ["ENTER"], "type": "function", "command": "activate" },
|
||||
{ "keys": ["DELETE", "BACKSPACE"], "type": "function", "command": "delete" },
|
||||
{ "keys": [" "], "command": "todo toggle $n" },
|
||||
{ "keys": ["tc"], "type": "function", "command": "toggle_console" },
|
||||
{ "keys": ["tm"], "type": "function", "command": "toggle_metadata" },
|
||||
{ "keys": ["/"], "type": "prompt", "command": "search \"\"", "cursorPosition": -2 },
|
||||
{ "keys": ["mn"], "type": "prompt", "command": "mknote \"\"", "cursorPosition": -2 },
|
||||
{ "keys": ["mt"], "type": "prompt", "command": "mktodo \"\"", "cursorPosition": -2 },
|
||||
{ "keys": ["mb"], "type": "prompt", "command": "mkbook \"\"", "cursorPosition": -2 },
|
||||
{ "keys": ["yn"], "type": "prompt", "command": "cp $n \"\"", "cursorPosition": -2 },
|
||||
{ "keys": ["dn"], "type": "prompt", "command": "mv $n \"\"", "cursorPosition": -2 }
|
||||
];
|
||||
|
||||
// Filter the keymap item by command so that items in keymap.json can override
|
||||
@@ -348,22 +339,22 @@ class Application extends BaseApplication {
|
||||
const itemsByCommand = {};
|
||||
|
||||
for (let i = 0; i < defaultKeyMap.length; i++) {
|
||||
itemsByCommand[defaultKeyMap[i].command] = defaultKeyMap[i];
|
||||
itemsByCommand[defaultKeyMap[i].command] = defaultKeyMap[i]
|
||||
}
|
||||
|
||||
const filePath = Setting.value("profileDir") + "/keymap.json";
|
||||
const filePath = Setting.value('profileDir') + '/keymap.json';
|
||||
if (await fs.pathExists(filePath)) {
|
||||
try {
|
||||
let configString = await fs.readFile(filePath, "utf-8");
|
||||
configString = configString.replace(/^\s*\/\/.*/, ""); // Strip off comments
|
||||
let configString = await fs.readFile(filePath, 'utf-8');
|
||||
configString = configString.replace(/^\s*\/\/.*/, ''); // Strip off comments
|
||||
const keymap = JSON.parse(configString);
|
||||
for (let keymapIndex = 0; keymapIndex < keymap.length; keymapIndex++) {
|
||||
const item = keymap[keymapIndex];
|
||||
itemsByCommand[item.command] = item;
|
||||
}
|
||||
} catch (error) {
|
||||
let msg = error.message ? error.message : "";
|
||||
msg = "Could not load keymap " + filePath + "\n" + msg;
|
||||
let msg = error.message ? error.message : '';
|
||||
msg = 'Could not load keymap ' + filePath + '\n' + msg;
|
||||
error.message = msg;
|
||||
throw error;
|
||||
}
|
||||
@@ -381,7 +372,7 @@ class Application extends BaseApplication {
|
||||
async start(argv) {
|
||||
argv = await super.start(argv);
|
||||
|
||||
cliUtils.setStdout(object => {
|
||||
cliUtils.setStdout((object) => {
|
||||
return this.stdout(object);
|
||||
});
|
||||
|
||||
@@ -390,7 +381,7 @@ class Application extends BaseApplication {
|
||||
if (argv.length) {
|
||||
this.gui_ = this.dummyGui();
|
||||
|
||||
this.currentFolder_ = await Folder.load(Setting.value("activeFolderId"));
|
||||
this.currentFolder_ = await Folder.load(Setting.value('activeFolderId'));
|
||||
|
||||
try {
|
||||
await this.execCommand(argv);
|
||||
@@ -402,13 +393,12 @@ class Application extends BaseApplication {
|
||||
}
|
||||
process.exit(1);
|
||||
}
|
||||
} else {
|
||||
// Otherwise open the GUI
|
||||
} else { // Otherwise open the GUI
|
||||
this.initRedux();
|
||||
|
||||
const keymap = await this.loadKeymaps();
|
||||
|
||||
const AppGui = require("./app-gui.js");
|
||||
const AppGui = require('./app-gui.js');
|
||||
this.gui_ = new AppGui(this, this.store(), keymap);
|
||||
this.gui_.setLogger(this.logger_);
|
||||
await this.gui_.start();
|
||||
@@ -423,16 +413,17 @@ class Application extends BaseApplication {
|
||||
const tags = await Tag.allWithNotes();
|
||||
|
||||
this.dispatch({
|
||||
type: "TAG_UPDATE_ALL",
|
||||
type: 'TAG_UPDATE_ALL',
|
||||
items: tags,
|
||||
});
|
||||
|
||||
this.store().dispatch({
|
||||
type: "FOLDER_SELECT",
|
||||
id: Setting.value("activeFolderId"),
|
||||
type: 'FOLDER_SELECT',
|
||||
id: Setting.value('activeFolderId'),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
let application_ = null;
|
||||
@@ -443,4 +434,4 @@ function app() {
|
||||
return application_;
|
||||
}
|
||||
|
||||
module.exports = { app };
|
||||
module.exports = { app };
|
||||
@@ -1,10 +1,10 @@
|
||||
var { app } = require("./app.js");
|
||||
var Note = require("lib/models/Note.js");
|
||||
var Folder = require("lib/models/Folder.js");
|
||||
var Tag = require("lib/models/Tag.js");
|
||||
var { cliUtils } = require("./cli-utils.js");
|
||||
var yargParser = require("yargs-parser");
|
||||
var fs = require("fs-extra");
|
||||
var { app } = require('./app.js');
|
||||
var Note = require('lib/models/Note.js');
|
||||
var Folder = require('lib/models/Folder.js');
|
||||
var Tag = require('lib/models/Tag.js');
|
||||
var { cliUtils } = require('./cli-utils.js');
|
||||
var yargParser = require('yargs-parser');
|
||||
var fs = require('fs-extra');
|
||||
|
||||
async function handleAutocompletionPromise(line) {
|
||||
// Auto-complete the command name
|
||||
@@ -14,11 +14,11 @@ async function handleAutocompletionPromise(line) {
|
||||
//should look for commmands it could be
|
||||
if (words.length == 1) {
|
||||
if (names.indexOf(words[0]) === -1) {
|
||||
let x = names.filter(n => n.indexOf(words[0]) === 0);
|
||||
let x = names.filter((n) => n.indexOf(words[0]) === 0);
|
||||
if (x.length === 1) {
|
||||
return x[0] + " ";
|
||||
return x[0] + ' ';
|
||||
}
|
||||
return x.length > 0 ? x.map(a => a + " ") : line;
|
||||
return x.length > 0 ? x.map((a) => a + ' ') : line;
|
||||
} else {
|
||||
return line;
|
||||
}
|
||||
@@ -31,14 +31,14 @@ async function handleAutocompletionPromise(line) {
|
||||
return line;
|
||||
}
|
||||
//complete an option
|
||||
let next = words.length > 1 ? words[words.length - 1] : "";
|
||||
let next = words.length > 1 ? words[words.length - 1] : '';
|
||||
let l = [];
|
||||
if (next[0] === "-") {
|
||||
for (let i = 0; i < metadata.options.length; i++) {
|
||||
const options = metadata.options[i][0].split(" ");
|
||||
//if there are multiple options then they will be seperated by comma and
|
||||
if (next[0] === '-') {
|
||||
for (let i = 0; i<metadata.options.length; i++) {
|
||||
const options = metadata.options[i][0].split(' ');
|
||||
//if there are multiple options then they will be seperated by comma and
|
||||
//space. The comma should be removed
|
||||
if (options[0][options[0].length - 1] === ",") {
|
||||
if (options[0][options[0].length - 1] === ',') {
|
||||
options[0] = options[0].slice(0, -1);
|
||||
}
|
||||
if (words.includes(options[0]) || words.includes(options[1])) {
|
||||
@@ -55,68 +55,70 @@ async function handleAutocompletionPromise(line) {
|
||||
if (l.length === 0) {
|
||||
return line;
|
||||
}
|
||||
let ret = l.map(a => toCommandLine(a));
|
||||
ret.prefix = toCommandLine(words.slice(0, -1)) + " ";
|
||||
let ret = l.map(a=>toCommandLine(a));
|
||||
ret.prefix = toCommandLine(words.slice(0, -1)) + ' ';
|
||||
return ret;
|
||||
}
|
||||
//Complete an argument
|
||||
//Determine the number of positional arguments by counting the number of
|
||||
//words that don't start with a - less one for the command name
|
||||
const positionalArgs = words.filter(a => a.indexOf("-") !== 0).length - 1;
|
||||
const positionalArgs = words.filter((a)=>a.indexOf('-') !== 0).length - 1;
|
||||
|
||||
let cmdUsage = yargParser(metadata.usage)["_"];
|
||||
let cmdUsage = yargParser(metadata.usage)['_'];
|
||||
cmdUsage.splice(0, 1);
|
||||
|
||||
if (cmdUsage.length >= positionalArgs) {
|
||||
|
||||
let argName = cmdUsage[positionalArgs - 1];
|
||||
argName = cliUtils.parseCommandArg(argName).name;
|
||||
|
||||
const currentFolder = app().currentFolder();
|
||||
|
||||
if (argName == "note" || argName == "note-pattern") {
|
||||
const notes = currentFolder ? await Note.previews(currentFolder.id, { titlePattern: next + "*" }) : [];
|
||||
l.push(...notes.map(n => n.title));
|
||||
if (argName == 'note' || argName == 'note-pattern') {
|
||||
const notes = currentFolder ? await Note.previews(currentFolder.id, { titlePattern: next + '*' }) : [];
|
||||
l.push(...notes.map((n) => n.title));
|
||||
}
|
||||
|
||||
if (argName == "notebook") {
|
||||
const folders = await Folder.search({ titlePattern: next + "*" });
|
||||
l.push(...folders.map(n => n.title));
|
||||
if (argName == 'notebook') {
|
||||
const folders = await Folder.search({ titlePattern: next + '*' });
|
||||
l.push(...folders.map((n) => n.title));
|
||||
}
|
||||
|
||||
if (argName == "item") {
|
||||
const notes = currentFolder ? await Note.previews(currentFolder.id, { titlePattern: next + "*" }) : [];
|
||||
const folders = await Folder.search({ titlePattern: next + "*" });
|
||||
l.push(...notes.map(n => n.title), folders.map(n => n.title));
|
||||
if (argName == 'item') {
|
||||
const notes = currentFolder ? await Note.previews(currentFolder.id, { titlePattern: next + '*' }) : [];
|
||||
const folders = await Folder.search({ titlePattern: next + '*' });
|
||||
l.push(...notes.map((n) => n.title), folders.map((n) => n.title));
|
||||
}
|
||||
|
||||
if (argName == "tag") {
|
||||
let tags = await Tag.search({ titlePattern: next + "*" });
|
||||
l.push(...tags.map(n => n.title));
|
||||
if (argName == 'tag') {
|
||||
let tags = await Tag.search({ titlePattern: next + '*' });
|
||||
l.push(...tags.map((n) => n.title));
|
||||
}
|
||||
|
||||
if (argName == "file") {
|
||||
let files = await fs.readdir(".");
|
||||
if (argName == 'file') {
|
||||
let files = await fs.readdir('.');
|
||||
l.push(...files);
|
||||
}
|
||||
|
||||
if (argName == "tag-command") {
|
||||
let c = filterList(["add", "remove", "list"], next);
|
||||
if (argName == 'tag-command') {
|
||||
let c = filterList(['add', 'remove', 'list'], next);
|
||||
l.push(...c);
|
||||
}
|
||||
|
||||
if (argName == "todo-command") {
|
||||
let c = filterList(["toggle", "clear"], next);
|
||||
if (argName == 'todo-command') {
|
||||
let c = filterList(['toggle', 'clear'], next);
|
||||
l.push(...c);
|
||||
}
|
||||
}
|
||||
if (l.length === 1) {
|
||||
return toCommandLine([...words.slice(0, -1), l[0]]);
|
||||
} else if (l.length > 1) {
|
||||
let ret = l.map(a => toCommandLine(a));
|
||||
ret.prefix = toCommandLine(words.slice(0, -1)) + " ";
|
||||
let ret = l.map(a=>toCommandLine(a));
|
||||
ret.prefix = toCommandLine(words.slice(0, -1)) + ' ';
|
||||
return ret;
|
||||
}
|
||||
return line;
|
||||
|
||||
}
|
||||
function handleAutocompletion(str, callback) {
|
||||
handleAutocompletionPromise(str).then(function(res) {
|
||||
@@ -125,35 +127,33 @@ function handleAutocompletion(str, callback) {
|
||||
}
|
||||
function toCommandLine(args) {
|
||||
if (Array.isArray(args)) {
|
||||
return args
|
||||
.map(function(a) {
|
||||
if (a.indexOf('"') !== -1 || a.indexOf(" ") !== -1) {
|
||||
return "'" + a + "'";
|
||||
} else if (a.indexOf("'") !== -1) {
|
||||
return '"' + a + '"';
|
||||
} else {
|
||||
return a;
|
||||
}
|
||||
})
|
||||
.join(" ");
|
||||
return args.map(function(a) {
|
||||
if(a.indexOf('"') !== -1 || a.indexOf(' ') !== -1) {
|
||||
return "'" + a + "'";
|
||||
} else if (a.indexOf("'") !== -1) {
|
||||
return '"' + a + '"';
|
||||
} else {
|
||||
return a;
|
||||
}
|
||||
}).join(' ');
|
||||
} else {
|
||||
if (args.indexOf('"') !== -1 || args.indexOf(" ") !== -1) {
|
||||
if(args.indexOf('"') !== -1 || args.indexOf(' ') !== -1) {
|
||||
return "'" + args + "' ";
|
||||
} else if (args.indexOf("'") !== -1) {
|
||||
return '"' + args + '" ';
|
||||
} else {
|
||||
return args + " ";
|
||||
return args + ' ';
|
||||
}
|
||||
}
|
||||
}
|
||||
function getArguments(line) {
|
||||
let inSingleQuotes = false;
|
||||
let inDoubleQuotes = false;
|
||||
let currentWord = "";
|
||||
let currentWord = '';
|
||||
let parsed = [];
|
||||
for (let i = 0; i < line.length; i++) {
|
||||
if (line[i] === '"') {
|
||||
if (inDoubleQuotes) {
|
||||
for(let i = 0; i<line.length; i++) {
|
||||
if(line[i] === '"') {
|
||||
if(inDoubleQuotes) {
|
||||
inDoubleQuotes = false;
|
||||
//maybe push word to parsed?
|
||||
//currentWord += '"';
|
||||
@@ -161,8 +161,8 @@ function getArguments(line) {
|
||||
inDoubleQuotes = true;
|
||||
//currentWord += '"';
|
||||
}
|
||||
} else if (line[i] === "'") {
|
||||
if (inSingleQuotes) {
|
||||
} else if(line[i] === "'") {
|
||||
if(inSingleQuotes) {
|
||||
inSingleQuotes = false;
|
||||
//maybe push word to parsed?
|
||||
//currentWord += "'";
|
||||
@@ -170,17 +170,18 @@ function getArguments(line) {
|
||||
inSingleQuotes = true;
|
||||
//currentWord += "'";
|
||||
}
|
||||
} else if (/\s/.test(line[i]) && !(inDoubleQuotes || inSingleQuotes)) {
|
||||
if (currentWord !== "") {
|
||||
} else if (/\s/.test(line[i]) &&
|
||||
!(inDoubleQuotes || inSingleQuotes)) {
|
||||
if (currentWord !== '') {
|
||||
parsed.push(currentWord);
|
||||
currentWord = "";
|
||||
currentWord = '';
|
||||
}
|
||||
} else {
|
||||
currentWord += line[i];
|
||||
}
|
||||
}
|
||||
if (!(inSingleQuotes || inDoubleQuotes) && /\s/.test(line[line.length - 1])) {
|
||||
parsed.push("");
|
||||
parsed.push('');
|
||||
} else {
|
||||
parsed.push(currentWord);
|
||||
}
|
||||
|
||||
@@ -1,30 +1,31 @@
|
||||
const { _ } = require("lib/locale.js");
|
||||
const { reg } = require("lib/registry.js");
|
||||
const { _ } = require('lib/locale.js');
|
||||
const { reg } = require('lib/registry.js');
|
||||
|
||||
class BaseCommand {
|
||||
|
||||
constructor() {
|
||||
this.stdout_ = null;
|
||||
this.prompt_ = null;
|
||||
}
|
||||
|
||||
usage() {
|
||||
throw new Error("Usage not defined");
|
||||
throw new Error('Usage not defined');
|
||||
}
|
||||
|
||||
encryptionCheck(item) {
|
||||
if (item && item.encryption_applied) throw new Error(_("Cannot change encrypted item"));
|
||||
if (item && item.encryption_applied) throw new Error(_('Cannot change encrypted item'));
|
||||
}
|
||||
|
||||
description() {
|
||||
throw new Error("Description not defined");
|
||||
throw new Error('Description not defined');
|
||||
}
|
||||
|
||||
async action(args) {
|
||||
throw new Error("Action not defined");
|
||||
throw new Error('Action not defined');
|
||||
}
|
||||
|
||||
compatibleUis() {
|
||||
return ["cli", "gui"];
|
||||
return ['cli', 'gui'];
|
||||
}
|
||||
|
||||
supportsUi(ui) {
|
||||
@@ -50,7 +51,7 @@ class BaseCommand {
|
||||
async cancel() {}
|
||||
|
||||
name() {
|
||||
let r = this.usage().split(" ");
|
||||
let r = this.usage().split(' ');
|
||||
return r[0];
|
||||
}
|
||||
|
||||
@@ -59,7 +60,7 @@ class BaseCommand {
|
||||
}
|
||||
|
||||
dispatch(action) {
|
||||
if (!this.dispatcher_) throw new Error("Dispatcher not defined");
|
||||
if (!this.dispatcher_) throw new Error('Dispatcher not defined');
|
||||
return this.dispatcher_(action);
|
||||
}
|
||||
|
||||
@@ -76,7 +77,7 @@ class BaseCommand {
|
||||
}
|
||||
|
||||
async prompt(message, options = null) {
|
||||
if (!this.prompt_) throw new Error("Prompt is undefined");
|
||||
if (!this.prompt_) throw new Error('Prompt is undefined');
|
||||
return await this.prompt_(message, options);
|
||||
}
|
||||
|
||||
@@ -92,6 +93,7 @@ class BaseCommand {
|
||||
logger() {
|
||||
return reg.logger();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
module.exports = { BaseCommand };
|
||||
module.exports = { BaseCommand };
|
||||
@@ -1,11 +1,11 @@
|
||||
const fs = require("fs-extra");
|
||||
const { fileExtension, basename, dirname } = require("lib/path-utils.js");
|
||||
const wrap_ = require("word-wrap");
|
||||
const { _, setLocale, languageCode } = require("lib/locale.js");
|
||||
const fs = require('fs-extra');
|
||||
const { fileExtension, basename, dirname } = require('lib/path-utils.js');
|
||||
const wrap_ = require('word-wrap');
|
||||
const { _, setLocale, languageCode } = require('lib/locale.js');
|
||||
|
||||
const rootDir = dirname(dirname(__dirname));
|
||||
const MAX_WIDTH = 78;
|
||||
const INDENT = " ";
|
||||
const INDENT = ' ';
|
||||
|
||||
function wrap(text, indent) {
|
||||
return wrap_(text, {
|
||||
@@ -21,8 +21,8 @@ function renderOptions(options) {
|
||||
for (let i = 0; i < options.length; i++) {
|
||||
let option = options[i];
|
||||
const flag = option[0];
|
||||
const indent = INDENT + INDENT + " ".repeat(optionColWidth + 2);
|
||||
|
||||
const indent = INDENT + INDENT + ' '.repeat(optionColWidth + 2);
|
||||
|
||||
let r = wrap(option[1], indent);
|
||||
r = r.substr(flag.length + (INDENT + INDENT).length);
|
||||
r = INDENT + INDENT + flag + r;
|
||||
@@ -35,13 +35,13 @@ function renderOptions(options) {
|
||||
function renderCommand(cmd) {
|
||||
let output = [];
|
||||
output.push(INDENT + cmd.usage());
|
||||
output.push("");
|
||||
output.push('');
|
||||
output.push(wrap(cmd.description(), INDENT + INDENT));
|
||||
|
||||
const optionString = renderOptions(cmd.options());
|
||||
|
||||
if (optionString) {
|
||||
output.push("");
|
||||
output.push('');
|
||||
output.push(optionString);
|
||||
}
|
||||
return output.join("\n");
|
||||
@@ -49,12 +49,12 @@ function renderCommand(cmd) {
|
||||
|
||||
function getCommands() {
|
||||
let output = [];
|
||||
fs.readdirSync(__dirname).forEach(path => {
|
||||
if (path.indexOf("command-") !== 0) return;
|
||||
const ext = fileExtension(path);
|
||||
if (ext != "js") return;
|
||||
fs.readdirSync(__dirname).forEach((path) => {
|
||||
if (path.indexOf('command-') !== 0) return;
|
||||
const ext = fileExtension(path)
|
||||
if (ext != 'js') return;
|
||||
|
||||
let CommandClass = require("./" + path);
|
||||
let CommandClass = require('./' + path);
|
||||
let cmd = new CommandClass();
|
||||
if (!cmd.enabled()) return;
|
||||
if (cmd.hidden()) return;
|
||||
@@ -75,26 +75,24 @@ function getOptionColWidth(options) {
|
||||
function getHeader() {
|
||||
let output = [];
|
||||
|
||||
output.push("NAME");
|
||||
output.push("");
|
||||
output.push(wrap("joplin - a note taking and to-do app with synchronisation capabilities"), INDENT);
|
||||
output.push('NAME');
|
||||
output.push('');
|
||||
output.push(wrap('joplin - a note taking and to-do app with synchronisation capabilities'), INDENT);
|
||||
|
||||
output.push("");
|
||||
output.push('');
|
||||
|
||||
output.push("DESCRIPTION");
|
||||
output.push("");
|
||||
output.push('DESCRIPTION');
|
||||
output.push('');
|
||||
|
||||
let description = [];
|
||||
description.push("Joplin is a note taking and to-do application, which can handle a large number of notes organised into notebooks.");
|
||||
description.push("The notes are searchable, can be copied, tagged and modified with your own text editor.");
|
||||
description.push('Joplin is a note taking and to-do application, which can handle a large number of notes organised into notebooks.');
|
||||
description.push('The notes are searchable, can be copied, tagged and modified with your own text editor.');
|
||||
description.push("\n\n");
|
||||
description.push("The notes can be synchronised with various target including the file system (for example with a network directory) or with Microsoft OneDrive.");
|
||||
description.push('The notes can be synchronised with various target including the file system (for example with a network directory) or with Microsoft OneDrive.');
|
||||
description.push("\n\n");
|
||||
description.push(
|
||||
"Notes exported from Evenotes via .enex files can be imported into Joplin, including the formatted content, resources (images, attachments, etc.) and complete metadata (geolocation, updated time, created time, etc.)."
|
||||
);
|
||||
description.push('Notes exported from Evenotes via .enex files can be imported into Joplin, including the formatted content, resources (images, attachments, etc.) and complete metadata (geolocation, updated time, created time, etc.).');
|
||||
|
||||
output.push(wrap(description.join(""), INDENT));
|
||||
output.push(wrap(description.join(''), INDENT));
|
||||
|
||||
return output.join("\n");
|
||||
}
|
||||
@@ -102,17 +100,17 @@ function getHeader() {
|
||||
function getFooter() {
|
||||
let output = [];
|
||||
|
||||
output.push("WEBSITE");
|
||||
output.push("");
|
||||
output.push(INDENT + "http://joplin.cozic.net");
|
||||
output.push('WEBSITE');
|
||||
output.push('');
|
||||
output.push(INDENT + 'http://joplin.cozic.net');
|
||||
|
||||
output.push("");
|
||||
output.push('');
|
||||
|
||||
output.push("LICENSE");
|
||||
output.push("");
|
||||
let filePath = rootDir + "/LICENSE_" + languageCode();
|
||||
if (!fs.existsSync(filePath)) filePath = rootDir + "/LICENSE";
|
||||
const licenseText = fs.readFileSync(filePath, "utf8");
|
||||
output.push('LICENSE');
|
||||
output.push('');
|
||||
let filePath = rootDir + '/LICENSE_' + languageCode();
|
||||
if (!fs.existsSync(filePath)) filePath = rootDir + '/LICENSE';
|
||||
const licenseText = fs.readFileSync(filePath, 'utf8');
|
||||
output.push(wrap(licenseText, INDENT));
|
||||
|
||||
return output.join("\n");
|
||||
@@ -133,9 +131,9 @@ async function main() {
|
||||
const commandsText = commandBlocks.join("\n\n");
|
||||
const footerText = getFooter();
|
||||
|
||||
console.info(headerText + "\n\n" + "USAGE" + "\n\n" + commandsText + "\n\n" + footerText);
|
||||
console.info(headerText + "\n\n" + 'USAGE' + "\n\n" + commandsText + "\n\n" + footerText);
|
||||
}
|
||||
|
||||
main().catch(error => {
|
||||
main().catch((error) => {
|
||||
console.error(error);
|
||||
});
|
||||
});
|
||||
@@ -1,30 +1,30 @@
|
||||
"use strict";
|
||||
"use strict"
|
||||
|
||||
const fs = require("fs-extra");
|
||||
const { Logger } = require("lib/logger.js");
|
||||
const { dirname } = require("lib/path-utils.js");
|
||||
const { DatabaseDriverNode } = require("lib/database-driver-node.js");
|
||||
const { JoplinDatabase } = require("lib/joplin-database.js");
|
||||
const BaseModel = require("lib/BaseModel.js");
|
||||
const Folder = require("lib/models/Folder.js");
|
||||
const Note = require("lib/models/Note.js");
|
||||
const Setting = require("lib/models/Setting.js");
|
||||
const { sprintf } = require("sprintf-js");
|
||||
const exec = require("child_process").exec;
|
||||
const fs = require('fs-extra');
|
||||
const { Logger } = require('lib/logger.js');
|
||||
const { dirname } = require('lib/path-utils.js');
|
||||
const { DatabaseDriverNode } = require('lib/database-driver-node.js');
|
||||
const { JoplinDatabase } = require('lib/joplin-database.js');
|
||||
const BaseModel = require('lib/BaseModel.js');
|
||||
const Folder = require('lib/models/Folder.js');
|
||||
const Note = require('lib/models/Note.js');
|
||||
const Setting = require('lib/models/Setting.js');
|
||||
const { sprintf } = require('sprintf-js');
|
||||
const exec = require('child_process').exec
|
||||
|
||||
process.on("unhandledRejection", (reason, p) => {
|
||||
console.error("Unhandled promise rejection", p, "reason:", reason);
|
||||
process.on('unhandledRejection', (reason, p) => {
|
||||
console.error('Unhandled promise rejection', p, 'reason:', reason);
|
||||
});
|
||||
|
||||
const baseDir = dirname(__dirname) + "/tests/cli-integration";
|
||||
const joplinAppPath = __dirname + "/main.js";
|
||||
const baseDir = dirname(__dirname) + '/tests/cli-integration';
|
||||
const joplinAppPath = __dirname + '/main.js';
|
||||
|
||||
const logger = new Logger();
|
||||
logger.addTarget("console");
|
||||
logger.addTarget('console');
|
||||
logger.setLevel(Logger.LEVEL_ERROR);
|
||||
|
||||
const dbLogger = new Logger();
|
||||
dbLogger.addTarget("console");
|
||||
dbLogger.addTarget('console');
|
||||
dbLogger.setLevel(Logger.LEVEL_INFO);
|
||||
|
||||
const db = new JoplinDatabase(new DatabaseDriverNode());
|
||||
@@ -32,17 +32,17 @@ db.setLogger(dbLogger);
|
||||
|
||||
function createClient(id) {
|
||||
return {
|
||||
id: id,
|
||||
profileDir: baseDir + "/client" + id,
|
||||
'id': id,
|
||||
'profileDir': baseDir + '/client' + id,
|
||||
};
|
||||
}
|
||||
|
||||
const client = createClient(1);
|
||||
|
||||
function execCommand(client, command, options = {}) {
|
||||
let exePath = "node " + joplinAppPath;
|
||||
let cmd = exePath + " --update-geolocation-disabled --env dev --profile " + client.profileDir + " " + command;
|
||||
logger.info(client.id + ": " + command);
|
||||
let exePath = 'node ' + joplinAppPath;
|
||||
let cmd = exePath + ' --update-geolocation-disabled --env dev --profile ' + client.profileDir + ' ' + command;
|
||||
logger.info(client.id + ': ' + command);
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
exec(cmd, (error, stdout, stderr) => {
|
||||
@@ -58,58 +58,65 @@ function execCommand(client, command, options = {}) {
|
||||
|
||||
function assertTrue(v) {
|
||||
if (!v) throw new Error(sprintf('Expected "true", got "%s"."', v));
|
||||
process.stdout.write(".");
|
||||
process.stdout.write('.');
|
||||
}
|
||||
|
||||
function assertFalse(v) {
|
||||
if (v) throw new Error(sprintf('Expected "false", got "%s"."', v));
|
||||
process.stdout.write(".");
|
||||
process.stdout.write('.');
|
||||
}
|
||||
|
||||
function assertEquals(expected, real) {
|
||||
if (expected !== real) throw new Error(sprintf('Expecting "%s", got "%s"', expected, real));
|
||||
process.stdout.write(".");
|
||||
process.stdout.write('.');
|
||||
}
|
||||
|
||||
async function clearDatabase() {
|
||||
await db.transactionExecBatch(["DELETE FROM folders", "DELETE FROM notes", "DELETE FROM tags", "DELETE FROM note_tags", "DELETE FROM resources", "DELETE FROM deleted_items"]);
|
||||
await db.transactionExecBatch([
|
||||
'DELETE FROM folders',
|
||||
'DELETE FROM notes',
|
||||
'DELETE FROM tags',
|
||||
'DELETE FROM note_tags',
|
||||
'DELETE FROM resources',
|
||||
'DELETE FROM deleted_items',
|
||||
]);
|
||||
}
|
||||
|
||||
const testUnits = {};
|
||||
|
||||
testUnits.testFolders = async () => {
|
||||
await execCommand(client, "mkbook nb1");
|
||||
await execCommand(client, 'mkbook nb1');
|
||||
|
||||
let folders = await Folder.all();
|
||||
assertEquals(1, folders.length);
|
||||
assertEquals("nb1", folders[0].title);
|
||||
assertEquals('nb1', folders[0].title);
|
||||
|
||||
await execCommand(client, "mkbook nb1");
|
||||
await execCommand(client, 'mkbook nb1');
|
||||
|
||||
folders = await Folder.all();
|
||||
assertEquals(1, folders.length);
|
||||
assertEquals("nb1", folders[0].title);
|
||||
assertEquals('nb1', folders[0].title);
|
||||
|
||||
await execCommand(client, "rm -r -f nb1");
|
||||
await execCommand(client, 'rm -r -f nb1');
|
||||
|
||||
folders = await Folder.all();
|
||||
assertEquals(0, folders.length);
|
||||
};
|
||||
}
|
||||
|
||||
testUnits.testNotes = async () => {
|
||||
await execCommand(client, "mkbook nb1");
|
||||
await execCommand(client, "mknote n1");
|
||||
await execCommand(client, 'mkbook nb1');
|
||||
await execCommand(client, 'mknote n1');
|
||||
|
||||
let notes = await Note.all();
|
||||
assertEquals(1, notes.length);
|
||||
assertEquals("n1", notes[0].title);
|
||||
assertEquals('n1', notes[0].title);
|
||||
|
||||
await execCommand(client, "rm -f n1");
|
||||
await execCommand(client, 'rm -f n1');
|
||||
notes = await Note.all();
|
||||
assertEquals(0, notes.length);
|
||||
|
||||
await execCommand(client, "mknote n1");
|
||||
await execCommand(client, "mknote n2");
|
||||
await execCommand(client, 'mknote n1');
|
||||
await execCommand(client, 'mknote n2');
|
||||
|
||||
notes = await Note.all();
|
||||
assertEquals(2, notes.length);
|
||||
@@ -123,86 +130,86 @@ testUnits.testNotes = async () => {
|
||||
|
||||
notes = await Note.all();
|
||||
assertEquals(0, notes.length);
|
||||
};
|
||||
}
|
||||
|
||||
testUnits.testCat = async () => {
|
||||
await execCommand(client, "mkbook nb1");
|
||||
await execCommand(client, "mknote mynote");
|
||||
await execCommand(client, 'mkbook nb1');
|
||||
await execCommand(client, 'mknote mynote');
|
||||
|
||||
let folder = await Folder.loadByTitle("nb1");
|
||||
let note = await Note.loadFolderNoteByField(folder.id, "title", "mynote");
|
||||
let folder = await Folder.loadByTitle('nb1');
|
||||
let note = await Note.loadFolderNoteByField(folder.id, 'title', 'mynote');
|
||||
|
||||
let r = await execCommand(client, "cat mynote");
|
||||
assertTrue(r.indexOf("mynote") >= 0);
|
||||
let r = await execCommand(client, 'cat mynote');
|
||||
assertTrue(r.indexOf('mynote') >= 0);
|
||||
assertFalse(r.indexOf(note.id) >= 0);
|
||||
|
||||
r = await execCommand(client, "cat -v mynote");
|
||||
r = await execCommand(client, 'cat -v mynote');
|
||||
assertTrue(r.indexOf(note.id) >= 0);
|
||||
};
|
||||
}
|
||||
|
||||
testUnits.testConfig = async () => {
|
||||
await execCommand(client, "config editor vim");
|
||||
await execCommand(client, 'config editor vim');
|
||||
await Setting.load();
|
||||
assertEquals("vim", Setting.value("editor"));
|
||||
assertEquals('vim', Setting.value('editor'));
|
||||
|
||||
await execCommand(client, "config editor subl");
|
||||
await execCommand(client, 'config editor subl');
|
||||
await Setting.load();
|
||||
assertEquals("subl", Setting.value("editor"));
|
||||
assertEquals('subl', Setting.value('editor'));
|
||||
|
||||
let r = await execCommand(client, "config");
|
||||
assertTrue(r.indexOf("editor") >= 0);
|
||||
assertTrue(r.indexOf("subl") >= 0);
|
||||
};
|
||||
let r = await execCommand(client, 'config');
|
||||
assertTrue(r.indexOf('editor') >= 0);
|
||||
assertTrue(r.indexOf('subl') >= 0);
|
||||
}
|
||||
|
||||
testUnits.testCp = async () => {
|
||||
await execCommand(client, "mkbook nb2");
|
||||
await execCommand(client, "mkbook nb1");
|
||||
await execCommand(client, "mknote n1");
|
||||
await execCommand(client, 'mkbook nb2');
|
||||
await execCommand(client, 'mkbook nb1');
|
||||
await execCommand(client, 'mknote n1');
|
||||
|
||||
await execCommand(client, "cp n1");
|
||||
await execCommand(client, 'cp n1');
|
||||
|
||||
let f1 = await Folder.loadByTitle("nb1");
|
||||
let f2 = await Folder.loadByTitle("nb2");
|
||||
let f1 = await Folder.loadByTitle('nb1');
|
||||
let f2 = await Folder.loadByTitle('nb2');
|
||||
let notes = await Note.previews(f1.id);
|
||||
|
||||
assertEquals(2, notes.length);
|
||||
|
||||
await execCommand(client, "cp n1 nb2");
|
||||
await execCommand(client, 'cp n1 nb2');
|
||||
let notesF1 = await Note.previews(f1.id);
|
||||
assertEquals(2, notesF1.length);
|
||||
notes = await Note.previews(f2.id);
|
||||
assertEquals(1, notes.length);
|
||||
assertEquals(notesF1[0].title, notes[0].title);
|
||||
};
|
||||
}
|
||||
|
||||
testUnits.testLs = async () => {
|
||||
await execCommand(client, "mkbook nb1");
|
||||
await execCommand(client, "mknote note1");
|
||||
await execCommand(client, "mknote note2");
|
||||
let r = await execCommand(client, "ls");
|
||||
await execCommand(client, 'mkbook nb1');
|
||||
await execCommand(client, 'mknote note1');
|
||||
await execCommand(client, 'mknote note2');
|
||||
let r = await execCommand(client, 'ls');
|
||||
|
||||
assertTrue(r.indexOf("note1") >= 0);
|
||||
assertTrue(r.indexOf("note2") >= 0);
|
||||
};
|
||||
assertTrue(r.indexOf('note1') >= 0);
|
||||
assertTrue(r.indexOf('note2') >= 0);
|
||||
}
|
||||
|
||||
testUnits.testMv = async () => {
|
||||
await execCommand(client, "mkbook nb2");
|
||||
await execCommand(client, "mkbook nb1");
|
||||
await execCommand(client, "mknote n1");
|
||||
await execCommand(client, "mv n1 nb2");
|
||||
await execCommand(client, 'mkbook nb2');
|
||||
await execCommand(client, 'mkbook nb1');
|
||||
await execCommand(client, 'mknote n1');
|
||||
await execCommand(client, 'mv n1 nb2');
|
||||
|
||||
let f1 = await Folder.loadByTitle("nb1");
|
||||
let f2 = await Folder.loadByTitle("nb2");
|
||||
let f1 = await Folder.loadByTitle('nb1');
|
||||
let f2 = await Folder.loadByTitle('nb2');
|
||||
let notes1 = await Note.previews(f1.id);
|
||||
let notes2 = await Note.previews(f2.id);
|
||||
|
||||
assertEquals(0, notes1.length);
|
||||
assertEquals(1, notes2.length);
|
||||
|
||||
await execCommand(client, "mknote note1");
|
||||
await execCommand(client, "mknote note2");
|
||||
await execCommand(client, "mknote note3");
|
||||
await execCommand(client, "mknote blabla");
|
||||
await execCommand(client, 'mknote note1');
|
||||
await execCommand(client, 'mknote note2');
|
||||
await execCommand(client, 'mknote note3');
|
||||
await execCommand(client, 'mknote blabla');
|
||||
await execCommand(client, "mv 'note*' nb2");
|
||||
|
||||
notes1 = await Note.previews(f1.id);
|
||||
@@ -210,19 +217,19 @@ testUnits.testMv = async () => {
|
||||
|
||||
assertEquals(1, notes1.length);
|
||||
assertEquals(4, notes2.length);
|
||||
};
|
||||
}
|
||||
|
||||
async function main(argv) {
|
||||
await fs.remove(baseDir);
|
||||
|
||||
logger.info(await execCommand(client, "version"));
|
||||
logger.info(await execCommand(client, 'version'));
|
||||
|
||||
await db.open({ name: client.profileDir + "/database.sqlite" });
|
||||
await db.open({ name: client.profileDir + '/database.sqlite' });
|
||||
BaseModel.db_ = db;
|
||||
await Setting.load();
|
||||
|
||||
let onlyThisTest = "testMv";
|
||||
onlyThisTest = "";
|
||||
let onlyThisTest = 'testMv';
|
||||
onlyThisTest = '';
|
||||
|
||||
for (let n in testUnits) {
|
||||
if (!testUnits.hasOwnProperty(n)) continue;
|
||||
@@ -230,13 +237,13 @@ async function main(argv) {
|
||||
|
||||
await clearDatabase();
|
||||
let testName = n.substr(4).toLowerCase();
|
||||
process.stdout.write(testName + ": ");
|
||||
process.stdout.write(testName + ': ');
|
||||
await testUnits[n]();
|
||||
console.info("");
|
||||
console.info('');
|
||||
}
|
||||
}
|
||||
|
||||
main(process.argv).catch(error => {
|
||||
console.info("");
|
||||
main(process.argv).catch((error) => {
|
||||
console.info('');
|
||||
logger.error(error);
|
||||
});
|
||||
});
|
||||
@@ -1,12 +1,12 @@
|
||||
const yargParser = require("yargs-parser");
|
||||
const { _ } = require("lib/locale.js");
|
||||
const { time } = require("lib/time-utils.js");
|
||||
const stringPadding = require("string-padding");
|
||||
const yargParser = require('yargs-parser');
|
||||
const { _ } = require('lib/locale.js');
|
||||
const { time } = require('lib/time-utils.js');
|
||||
const stringPadding = require('string-padding');
|
||||
|
||||
const cliUtils = {};
|
||||
|
||||
cliUtils.printArray = function(logFunction, rows, headers = null) {
|
||||
if (!rows.length) return "";
|
||||
if (!rows.length) return '';
|
||||
|
||||
const ALIGN_LEFT = 0;
|
||||
const ALIGN_RIGHT = 1;
|
||||
@@ -16,11 +16,11 @@ cliUtils.printArray = function(logFunction, rows, headers = null) {
|
||||
|
||||
for (let i = 0; i < rows.length; i++) {
|
||||
let row = rows[i];
|
||||
|
||||
|
||||
for (let j = 0; j < row.length; j++) {
|
||||
let item = row[j];
|
||||
let width = item ? item.toString().length : 0;
|
||||
let align = typeof item == "number" ? ALIGN_RIGHT : ALIGN_LEFT;
|
||||
let align = typeof item == 'number' ? ALIGN_RIGHT : ALIGN_LEFT;
|
||||
if (!colWidths[j] || colWidths[j] < width) colWidths[j] = width;
|
||||
if (colAligns.length <= j) colAligns[j] = align;
|
||||
}
|
||||
@@ -33,46 +33,46 @@ cliUtils.printArray = function(logFunction, rows, headers = null) {
|
||||
let item = rows[row][col];
|
||||
let width = colWidths[col];
|
||||
let dir = colAligns[col] == ALIGN_LEFT ? stringPadding.RIGHT : stringPadding.LEFT;
|
||||
line.push(stringPadding(item, width, " ", dir));
|
||||
line.push(stringPadding(item, width, ' ', dir));
|
||||
}
|
||||
logFunction(line.join(" "));
|
||||
logFunction(line.join(' '));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
cliUtils.parseFlags = function(flags) {
|
||||
let output = {};
|
||||
flags = flags.split(",");
|
||||
flags = flags.split(',');
|
||||
for (let i = 0; i < flags.length; i++) {
|
||||
let f = flags[i].trim();
|
||||
|
||||
if (f.substr(0, 2) == "--") {
|
||||
f = f.split(" ");
|
||||
if (f.substr(0, 2) == '--') {
|
||||
f = f.split(' ');
|
||||
output.long = f[0].substr(2).trim();
|
||||
if (f.length == 2) {
|
||||
output.arg = cliUtils.parseCommandArg(f[1].trim());
|
||||
}
|
||||
} else if (f.substr(0, 1) == "-") {
|
||||
} else if (f.substr(0, 1) == '-') {
|
||||
output.short = f.substr(1);
|
||||
}
|
||||
}
|
||||
return output;
|
||||
};
|
||||
}
|
||||
|
||||
cliUtils.parseCommandArg = function(arg) {
|
||||
if (arg.length <= 2) throw new Error("Invalid command arg: " + arg);
|
||||
if (arg.length <= 2) throw new Error('Invalid command arg: ' + arg);
|
||||
|
||||
const c1 = arg[0];
|
||||
const c2 = arg[arg.length - 1];
|
||||
const name = arg.substr(1, arg.length - 2);
|
||||
|
||||
if (c1 == "<" && c2 == ">") {
|
||||
if (c1 == '<' && c2 == '>') {
|
||||
return { required: true, name: name };
|
||||
} else if (c1 == "[" && c2 == "]") {
|
||||
} else if (c1 == '[' && c2 == ']') {
|
||||
return { required: false, name: name };
|
||||
} else {
|
||||
throw new Error("Invalid command arg: " + arg);
|
||||
throw new Error('Invalid command arg: ' + arg);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
cliUtils.makeCommandArgs = function(cmd, argv) {
|
||||
let cmdUsage = cmd.usage();
|
||||
@@ -83,7 +83,7 @@ cliUtils.makeCommandArgs = function(cmd, argv) {
|
||||
let booleanFlags = [];
|
||||
let aliases = {};
|
||||
for (let i = 0; i < options.length; i++) {
|
||||
if (options[i].length != 2) throw new Error("Invalid options: " + options[i]);
|
||||
if (options[i].length != 2) throw new Error('Invalid options: ' + options[i]);
|
||||
let flags = options[i][0];
|
||||
let text = options[i][1];
|
||||
|
||||
@@ -102,96 +102,97 @@ cliUtils.makeCommandArgs = function(cmd, argv) {
|
||||
let args = yargParser(argv, {
|
||||
boolean: booleanFlags,
|
||||
alias: aliases,
|
||||
string: ["_"],
|
||||
string: ['_'],
|
||||
});
|
||||
|
||||
for (let i = 1; i < cmdUsage["_"].length; i++) {
|
||||
const a = cliUtils.parseCommandArg(cmdUsage["_"][i]);
|
||||
if (a.required && !args["_"][i]) throw new Error(_("Missing required argument: %s", a.name));
|
||||
for (let i = 1; i < cmdUsage['_'].length; i++) {
|
||||
const a = cliUtils.parseCommandArg(cmdUsage['_'][i]);
|
||||
if (a.required && !args['_'][i]) throw new Error(_('Missing required argument: %s', a.name));
|
||||
if (i >= a.length) {
|
||||
output[a.name] = null;
|
||||
} else {
|
||||
output[a.name] = args["_"][i];
|
||||
output[a.name] = args['_'][i];
|
||||
}
|
||||
}
|
||||
|
||||
let argOptions = {};
|
||||
for (let key in args) {
|
||||
if (!args.hasOwnProperty(key)) continue;
|
||||
if (key == "_") continue;
|
||||
if (key == '_') continue;
|
||||
argOptions[key] = args[key];
|
||||
}
|
||||
|
||||
output.options = argOptions;
|
||||
|
||||
return output;
|
||||
};
|
||||
}
|
||||
|
||||
cliUtils.promptMcq = function(message, answers) {
|
||||
const readline = require("readline");
|
||||
const readline = require('readline');
|
||||
|
||||
const rl = readline.createInterface({
|
||||
input: process.stdin,
|
||||
output: process.stdout,
|
||||
output: process.stdout
|
||||
});
|
||||
|
||||
message += "\n\n";
|
||||
for (let n in answers) {
|
||||
if (!answers.hasOwnProperty(n)) continue;
|
||||
message += _("%s: %s", n, answers[n]) + "\n";
|
||||
message += _('%s: %s', n, answers[n]) + "\n";
|
||||
}
|
||||
|
||||
message += "\n";
|
||||
message += _("Your choice: ");
|
||||
message += _('Your choice: ');
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
rl.question(message, answer => {
|
||||
rl.question(message, (answer) => {
|
||||
rl.close();
|
||||
|
||||
if (!(answer in answers)) {
|
||||
reject(new Error(_("Invalid answer: %s", answer)));
|
||||
reject(new Error(_('Invalid answer: %s', answer)));
|
||||
return;
|
||||
}
|
||||
|
||||
resolve(answer);
|
||||
});
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
cliUtils.promptConfirm = function(message, answers = null) {
|
||||
if (!answers) answers = [_("Y"), _("n")];
|
||||
const readline = require("readline");
|
||||
if (!answers) answers = [_('Y'), _('n')];
|
||||
const readline = require('readline');
|
||||
|
||||
const rl = readline.createInterface({
|
||||
input: process.stdin,
|
||||
output: process.stdout,
|
||||
output: process.stdout
|
||||
});
|
||||
|
||||
message += " (" + answers.join("/") + ")";
|
||||
message += ' (' + answers.join('/') + ')';
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
rl.question(message + " ", answer => {
|
||||
rl.question(message + ' ', (answer) => {
|
||||
const ok = !answer || answer.toLowerCase() == answers[0].toLowerCase();
|
||||
rl.close();
|
||||
resolve(ok);
|
||||
});
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
// Note: initialText is there to have the same signature as statusBar.prompt() so that
|
||||
// it can be a drop-in replacement, however initialText is not used (and cannot be
|
||||
// with readline.question?).
|
||||
cliUtils.prompt = function(initialText = "", promptString = ":", options = null) {
|
||||
cliUtils.prompt = function(initialText = '', promptString = ':', options = null) {
|
||||
if (!options) options = {};
|
||||
|
||||
const readline = require("readline");
|
||||
const Writable = require("stream").Writable;
|
||||
const readline = require('readline');
|
||||
const Writable = require('stream').Writable;
|
||||
|
||||
const mutableStdout = new Writable({
|
||||
write: function(chunk, encoding, callback) {
|
||||
if (!this.muted) process.stdout.write(chunk, encoding);
|
||||
if (!this.muted)
|
||||
process.stdout.write(chunk, encoding);
|
||||
callback();
|
||||
},
|
||||
}
|
||||
});
|
||||
|
||||
const rl = readline.createInterface({
|
||||
@@ -203,15 +204,15 @@ cliUtils.prompt = function(initialText = "", promptString = ":", options = null)
|
||||
return new Promise((resolve, reject) => {
|
||||
mutableStdout.muted = false;
|
||||
|
||||
rl.question(promptString, answer => {
|
||||
rl.question(promptString, (answer) => {
|
||||
rl.close();
|
||||
if (!!options.secure) this.stdout_("");
|
||||
if (!!options.secure) this.stdout_('');
|
||||
resolve(answer);
|
||||
});
|
||||
|
||||
mutableStdout.muted = !!options.secure;
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
let redrawStarted_ = false;
|
||||
let redrawLastLog_ = null;
|
||||
@@ -219,7 +220,7 @@ let redrawLastUpdateTime_ = 0;
|
||||
|
||||
cliUtils.setStdout = function(v) {
|
||||
this.stdout_ = v;
|
||||
};
|
||||
}
|
||||
|
||||
cliUtils.redraw = function(s) {
|
||||
const now = time.unixMs();
|
||||
@@ -232,8 +233,8 @@ cliUtils.redraw = function(s) {
|
||||
redrawLastLog_ = s;
|
||||
}
|
||||
|
||||
redrawStarted_ = true;
|
||||
};
|
||||
redrawStarted_ = true;
|
||||
}
|
||||
|
||||
cliUtils.redrawDone = function() {
|
||||
if (!redrawStarted_) return;
|
||||
@@ -244,6 +245,6 @@ cliUtils.redrawDone = function() {
|
||||
|
||||
redrawLastLog_ = null;
|
||||
redrawStarted_ = false;
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = { cliUtils };
|
||||
module.exports = { cliUtils };
|
||||
@@ -1,30 +1,32 @@
|
||||
const { BaseCommand } = require("./base-command.js");
|
||||
const { app } = require("./app.js");
|
||||
const { _ } = require("lib/locale.js");
|
||||
const BaseModel = require("lib/BaseModel.js");
|
||||
const { shim } = require("lib/shim.js");
|
||||
const fs = require("fs-extra");
|
||||
const { BaseCommand } = require('./base-command.js');
|
||||
const { app } = require('./app.js');
|
||||
const { _ } = require('lib/locale.js');
|
||||
const BaseModel = require('lib/BaseModel.js');
|
||||
const { shim } = require('lib/shim.js');
|
||||
const fs = require('fs-extra');
|
||||
|
||||
class Command extends BaseCommand {
|
||||
|
||||
usage() {
|
||||
return "attach <note> <file>";
|
||||
return 'attach <note> <file>';
|
||||
}
|
||||
|
||||
description() {
|
||||
return _("Attaches the given file to the note.");
|
||||
return _('Attaches the given file to the note.');
|
||||
}
|
||||
|
||||
async action(args) {
|
||||
let title = args["note"];
|
||||
let title = args['note'];
|
||||
|
||||
let note = await app().loadItem(BaseModel.TYPE_NOTE, title, { parent: app().currentFolder() });
|
||||
this.encryptionCheck(note);
|
||||
if (!note) throw new Error(_('Cannot find "%s".', title));
|
||||
|
||||
const localFilePath = args["file"];
|
||||
const localFilePath = args['file'];
|
||||
|
||||
await shim.attachFileToNote(note, localFilePath);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
module.exports = Command;
|
||||
module.exports = Command;
|
||||
@@ -1,25 +1,28 @@
|
||||
const { BaseCommand } = require("./base-command.js");
|
||||
const { app } = require("./app.js");
|
||||
const { _ } = require("lib/locale.js");
|
||||
const BaseModel = require("lib/BaseModel.js");
|
||||
const Folder = require("lib/models/Folder.js");
|
||||
const Note = require("lib/models/Note.js");
|
||||
const { BaseCommand } = require('./base-command.js');
|
||||
const { app } = require('./app.js');
|
||||
const { _ } = require('lib/locale.js');
|
||||
const BaseModel = require('lib/BaseModel.js');
|
||||
const Folder = require('lib/models/Folder.js');
|
||||
const Note = require('lib/models/Note.js');
|
||||
|
||||
class Command extends BaseCommand {
|
||||
|
||||
usage() {
|
||||
return "cat <note>";
|
||||
return 'cat <note>';
|
||||
}
|
||||
|
||||
description() {
|
||||
return _("Displays the given note.");
|
||||
return _('Displays the given note.');
|
||||
}
|
||||
|
||||
options() {
|
||||
return [["-v, --verbose", _("Displays the complete information about note.")]];
|
||||
return [
|
||||
['-v, --verbose', _('Displays the complete information about note.')],
|
||||
];
|
||||
}
|
||||
|
||||
async action(args) {
|
||||
let title = args["note"];
|
||||
let title = args['note'];
|
||||
|
||||
let item = await app().loadItem(BaseModel.TYPE_NOTE, title, { parent: app().currentFolder() });
|
||||
if (!item) throw new Error(_('Cannot find "%s".', title));
|
||||
@@ -27,13 +30,10 @@ class Command extends BaseCommand {
|
||||
const content = args.options.verbose ? await Note.serialize(item) : await Note.serializeForEdit(item);
|
||||
this.stdout(content);
|
||||
|
||||
app()
|
||||
.gui()
|
||||
.showConsole();
|
||||
app()
|
||||
.gui()
|
||||
.maximizeConsole();
|
||||
app().gui().showConsole();
|
||||
app().gui().maximizeConsole();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
module.exports = Command;
|
||||
module.exports = Command;
|
||||
@@ -1,11 +1,12 @@
|
||||
const { BaseCommand } = require("./base-command.js");
|
||||
const { _, setLocale } = require("lib/locale.js");
|
||||
const { app } = require("./app.js");
|
||||
const Setting = require("lib/models/Setting.js");
|
||||
const { BaseCommand } = require('./base-command.js');
|
||||
const { _, setLocale } = require('lib/locale.js');
|
||||
const { app } = require('./app.js');
|
||||
const Setting = require('lib/models/Setting.js');
|
||||
|
||||
class Command extends BaseCommand {
|
||||
|
||||
usage() {
|
||||
return "config [name] [value]";
|
||||
return 'config [name] [value]';
|
||||
}
|
||||
|
||||
description() {
|
||||
@@ -13,62 +14,57 @@ class Command extends BaseCommand {
|
||||
}
|
||||
|
||||
options() {
|
||||
return [["-v, --verbose", _("Also displays unset and hidden config variables.")]];
|
||||
return [
|
||||
['-v, --verbose', _('Also displays unset and hidden config variables.')],
|
||||
];
|
||||
}
|
||||
|
||||
async action(args) {
|
||||
const verbose = args.options.verbose;
|
||||
|
||||
const renderKeyValue = name => {
|
||||
const renderKeyValue = (name) => {
|
||||
const md = Setting.settingMetadata(name);
|
||||
let value = Setting.value(name);
|
||||
if (typeof value === "object" || Array.isArray(value)) value = JSON.stringify(value);
|
||||
if (md.secure) value = "********";
|
||||
if (typeof value === 'object' || Array.isArray(value)) value = JSON.stringify(value);
|
||||
if (md.secure) value = '********';
|
||||
|
||||
if (Setting.isEnum(name)) {
|
||||
return _("%s = %s (%s)", name, value, Setting.enumOptionsDoc(name));
|
||||
return _('%s = %s (%s)', name, value, Setting.enumOptionsDoc(name));
|
||||
} else {
|
||||
return _("%s = %s", name, value);
|
||||
return _('%s = %s', name, value);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
if (!args.name && !args.value) {
|
||||
let keys = Setting.keys(!verbose, "cli");
|
||||
let keys = Setting.keys(!verbose, 'cli');
|
||||
keys.sort();
|
||||
for (let i = 0; i < keys.length; i++) {
|
||||
const value = Setting.value(keys[i]);
|
||||
if (!verbose && !value) continue;
|
||||
this.stdout(renderKeyValue(keys[i]));
|
||||
}
|
||||
app()
|
||||
.gui()
|
||||
.showConsole();
|
||||
app()
|
||||
.gui()
|
||||
.maximizeConsole();
|
||||
app().gui().showConsole();
|
||||
app().gui().maximizeConsole();
|
||||
return;
|
||||
}
|
||||
|
||||
if (args.name && !args.value) {
|
||||
this.stdout(renderKeyValue(args.name));
|
||||
app()
|
||||
.gui()
|
||||
.showConsole();
|
||||
app()
|
||||
.gui()
|
||||
.maximizeConsole();
|
||||
app().gui().showConsole();
|
||||
app().gui().maximizeConsole();
|
||||
return;
|
||||
}
|
||||
|
||||
Setting.setValue(args.name, args.value);
|
||||
|
||||
if (args.name == "locale") {
|
||||
setLocale(Setting.value("locale"));
|
||||
if (args.name == 'locale') {
|
||||
setLocale(Setting.value('locale'));
|
||||
app().onLocaleChanged();
|
||||
}
|
||||
|
||||
await Setting.saveAll();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
module.exports = Command;
|
||||
module.exports = Command;
|
||||
@@ -1,37 +1,39 @@
|
||||
const { BaseCommand } = require("./base-command.js");
|
||||
const { app } = require("./app.js");
|
||||
const { _ } = require("lib/locale.js");
|
||||
const BaseModel = require("lib/BaseModel.js");
|
||||
const Folder = require("lib/models/Folder.js");
|
||||
const Note = require("lib/models/Note.js");
|
||||
const { BaseCommand } = require('./base-command.js');
|
||||
const { app } = require('./app.js');
|
||||
const { _ } = require('lib/locale.js');
|
||||
const BaseModel = require('lib/BaseModel.js');
|
||||
const Folder = require('lib/models/Folder.js');
|
||||
const Note = require('lib/models/Note.js');
|
||||
|
||||
class Command extends BaseCommand {
|
||||
|
||||
usage() {
|
||||
return "cp <note> [notebook]";
|
||||
return 'cp <note> [notebook]';
|
||||
}
|
||||
|
||||
description() {
|
||||
return _("Duplicates the notes matching <note> to [notebook]. If no notebook is specified the note is duplicated in the current notebook.");
|
||||
return _('Duplicates the notes matching <note> to [notebook]. If no notebook is specified the note is duplicated in the current notebook.');
|
||||
}
|
||||
|
||||
async action(args) {
|
||||
let folder = null;
|
||||
if (args["notebook"]) {
|
||||
folder = await app().loadItem(BaseModel.TYPE_FOLDER, args["notebook"]);
|
||||
if (args['notebook']) {
|
||||
folder = await app().loadItem(BaseModel.TYPE_FOLDER, args['notebook']);
|
||||
} else {
|
||||
folder = app().currentFolder();
|
||||
}
|
||||
|
||||
if (!folder) throw new Error(_('Cannot find "%s".', args["notebook"]));
|
||||
if (!folder) throw new Error(_('Cannot find "%s".', args['notebook']));
|
||||
|
||||
const notes = await app().loadItems(BaseModel.TYPE_NOTE, args["note"]);
|
||||
if (!notes.length) throw new Error(_('Cannot find "%s".', args["note"]));
|
||||
const notes = await app().loadItems(BaseModel.TYPE_NOTE, args['note']);
|
||||
if (!notes.length) throw new Error(_('Cannot find "%s".', args['note']));
|
||||
|
||||
for (let i = 0; i < notes.length; i++) {
|
||||
const newNote = await Note.copyToFolder(notes[i].id, folder.id);
|
||||
Note.updateGeolocation(newNote.id);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
module.exports = Command;
|
||||
module.exports = Command;
|
||||
@@ -1,18 +1,19 @@
|
||||
const { BaseCommand } = require("./base-command.js");
|
||||
const { app } = require("./app.js");
|
||||
const { _ } = require("lib/locale.js");
|
||||
const BaseModel = require("lib/BaseModel.js");
|
||||
const Folder = require("lib/models/Folder.js");
|
||||
const Note = require("lib/models/Note.js");
|
||||
const { time } = require("lib/time-utils.js");
|
||||
const { BaseCommand } = require('./base-command.js');
|
||||
const { app } = require('./app.js');
|
||||
const { _ } = require('lib/locale.js');
|
||||
const BaseModel = require('lib/BaseModel.js');
|
||||
const Folder = require('lib/models/Folder.js');
|
||||
const Note = require('lib/models/Note.js');
|
||||
const { time } = require('lib/time-utils.js');
|
||||
|
||||
class Command extends BaseCommand {
|
||||
|
||||
usage() {
|
||||
return "done <note>";
|
||||
return 'done <note>';
|
||||
}
|
||||
|
||||
description() {
|
||||
return _("Marks a to-do as done.");
|
||||
return _('Marks a to-do as done.');
|
||||
}
|
||||
|
||||
static async handleAction(commandInstance, args, isCompleted) {
|
||||
@@ -34,6 +35,7 @@ class Command extends BaseCommand {
|
||||
async action(args) {
|
||||
await Command.handleAction(this, args, true);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
module.exports = Command;
|
||||
module.exports = Command;
|
||||
@@ -1,17 +1,18 @@
|
||||
const { BaseCommand } = require("./base-command.js");
|
||||
const { app } = require("./app.js");
|
||||
const { _ } = require("lib/locale.js");
|
||||
const Folder = require("lib/models/Folder.js");
|
||||
const Note = require("lib/models/Note.js");
|
||||
const Tag = require("lib/models/Tag.js");
|
||||
const { BaseCommand } = require('./base-command.js');
|
||||
const { app } = require('./app.js');
|
||||
const { _ } = require('lib/locale.js');
|
||||
const Folder = require('lib/models/Folder.js');
|
||||
const Note = require('lib/models/Note.js');
|
||||
const Tag = require('lib/models/Tag.js');
|
||||
|
||||
class Command extends BaseCommand {
|
||||
|
||||
usage() {
|
||||
return "dump";
|
||||
return 'dump';
|
||||
}
|
||||
|
||||
description() {
|
||||
return "Dumps the complete database as JSON.";
|
||||
return 'Dumps the complete database as JSON.';
|
||||
}
|
||||
|
||||
hidden() {
|
||||
@@ -34,9 +35,10 @@ class Command extends BaseCommand {
|
||||
}
|
||||
|
||||
items = items.concat(tags);
|
||||
|
||||
|
||||
this.stdout(JSON.stringify(items));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
module.exports = Command;
|
||||
module.exports = Command;
|
||||
@@ -1,26 +1,27 @@
|
||||
const { BaseCommand } = require("./base-command.js");
|
||||
const { _ } = require("lib/locale.js");
|
||||
const { cliUtils } = require("./cli-utils.js");
|
||||
const EncryptionService = require("lib/services/EncryptionService");
|
||||
const DecryptionWorker = require("lib/services/DecryptionWorker");
|
||||
const MasterKey = require("lib/models/MasterKey");
|
||||
const BaseItem = require("lib/models/BaseItem");
|
||||
const Setting = require("lib/models/Setting.js");
|
||||
const { BaseCommand } = require('./base-command.js');
|
||||
const { _ } = require('lib/locale.js');
|
||||
const { cliUtils } = require('./cli-utils.js');
|
||||
const EncryptionService = require('lib/services/EncryptionService');
|
||||
const DecryptionWorker = require('lib/services/DecryptionWorker');
|
||||
const MasterKey = require('lib/models/MasterKey');
|
||||
const BaseItem = require('lib/models/BaseItem');
|
||||
const Setting = require('lib/models/Setting.js');
|
||||
|
||||
class Command extends BaseCommand {
|
||||
|
||||
usage() {
|
||||
return "e2ee <command> [path]";
|
||||
return 'e2ee <command> [path]';
|
||||
}
|
||||
|
||||
description() {
|
||||
return _("Manages E2EE configuration. Commands are `enable`, `disable`, `decrypt`, `status` and `target-status`.");
|
||||
return _('Manages E2EE configuration. Commands are `enable`, `disable`, `decrypt`, `status` and `target-status`.');
|
||||
}
|
||||
|
||||
options() {
|
||||
return [
|
||||
// This is here mostly for testing - shouldn't be used
|
||||
["-p, --password <password>", "Use this password as master password (For security reasons, it is not recommended to use this option)."],
|
||||
["-v, --verbose", "More verbose output for the `target-status` command"],
|
||||
['-p, --password <password>', 'Use this password as master password (For security reasons, it is not recommended to use this option).'],
|
||||
['-v, --verbose', 'More verbose output for the `target-status` command'],
|
||||
];
|
||||
}
|
||||
|
||||
@@ -29,10 +30,10 @@ class Command extends BaseCommand {
|
||||
|
||||
const options = args.options;
|
||||
|
||||
if (args.command === "enable") {
|
||||
const password = options.password ? options.password.toString() : await this.prompt(_("Enter master password:"), { type: "string", secure: true });
|
||||
if (args.command === 'enable') {
|
||||
const password = options.password ? options.password.toString() : await this.prompt(_('Enter master password:'), { type: 'string', secure: true });
|
||||
if (!password) {
|
||||
this.stdout(_("Operation cancelled"));
|
||||
this.stdout(_('Operation cancelled'));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -40,27 +41,27 @@ class Command extends BaseCommand {
|
||||
return;
|
||||
}
|
||||
|
||||
if (args.command === "disable") {
|
||||
if (args.command === 'disable') {
|
||||
await EncryptionService.instance().disableEncryption();
|
||||
return;
|
||||
}
|
||||
|
||||
if (args.command === "decrypt") {
|
||||
this.stdout(_("Starting decryption... Please wait as it may take several minutes depending on how much there is to decrypt."));
|
||||
if (args.command === 'decrypt') {
|
||||
this.stdout(_('Starting decryption... Please wait as it may take several minutes depending on how much there is to decrypt.'));
|
||||
|
||||
while (true) {
|
||||
try {
|
||||
await DecryptionWorker.instance().start();
|
||||
break;
|
||||
} catch (error) {
|
||||
if (error.code === "masterKeyNotLoaded") {
|
||||
if (error.code === 'masterKeyNotLoaded') {
|
||||
const masterKeyId = error.masterKeyId;
|
||||
const password = await this.prompt(_("Enter master password:"), { type: "string", secure: true });
|
||||
const password = await this.prompt(_('Enter master password:'), { type: 'string', secure: true });
|
||||
if (!password) {
|
||||
this.stdout(_("Operation cancelled"));
|
||||
this.stdout(_('Operation cancelled'));
|
||||
return;
|
||||
}
|
||||
Setting.setObjectKey("encryption.passwordCache", masterKeyId, password);
|
||||
Setting.setObjectKey('encryption.passwordCache', masterKeyId, password);
|
||||
await EncryptionService.instance().loadMasterKeysFromSettings();
|
||||
continue;
|
||||
}
|
||||
@@ -69,31 +70,31 @@ class Command extends BaseCommand {
|
||||
}
|
||||
}
|
||||
|
||||
this.stdout(_("Completed decryption."));
|
||||
this.stdout(_('Completed decryption.'));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (args.command === "status") {
|
||||
this.stdout(_("Encryption is: %s", Setting.value("encryption.enabled") ? _("Enabled") : _("Disabled")));
|
||||
if (args.command === 'status') {
|
||||
this.stdout(_('Encryption is: %s', Setting.value('encryption.enabled') ? _('Enabled') : _('Disabled')));
|
||||
return;
|
||||
}
|
||||
|
||||
if (args.command === "target-status") {
|
||||
const fs = require("fs-extra");
|
||||
const pathUtils = require("lib/path-utils.js");
|
||||
const fsDriver = new (require("lib/fs-driver-node.js")).FsDriverNode();
|
||||
if (args.command === 'target-status') {
|
||||
const fs = require('fs-extra');
|
||||
const pathUtils = require('lib/path-utils.js');
|
||||
const fsDriver = new (require('lib/fs-driver-node.js').FsDriverNode)();
|
||||
|
||||
const targetPath = args.path;
|
||||
if (!targetPath) throw new Error("Please specify the sync target path.");
|
||||
if (!targetPath) throw new Error('Please specify the sync target path.');
|
||||
|
||||
const dirPaths = function(targetPath) {
|
||||
let paths = [];
|
||||
fs.readdirSync(targetPath).forEach(path => {
|
||||
fs.readdirSync(targetPath).forEach((path) => {
|
||||
paths.push(path);
|
||||
});
|
||||
return paths;
|
||||
};
|
||||
}
|
||||
|
||||
let itemCount = 0;
|
||||
let resourceCount = 0;
|
||||
@@ -108,17 +109,17 @@ class Command extends BaseCommand {
|
||||
|
||||
for (let i = 0; i < paths.length; i++) {
|
||||
const path = paths[i];
|
||||
const fullPath = targetPath + "/" + path;
|
||||
const fullPath = targetPath + '/' + path;
|
||||
const stat = await fs.stat(fullPath);
|
||||
|
||||
// this.stdout(fullPath);
|
||||
|
||||
if (path === ".resource") {
|
||||
if (path === '.resource') {
|
||||
let resourcePaths = dirPaths(fullPath);
|
||||
for (let j = 0; j < resourcePaths.length; j++) {
|
||||
const resourcePath = resourcePaths[j];
|
||||
resourceCount++;
|
||||
const fullResourcePath = fullPath + "/" + resourcePath;
|
||||
const fullResourcePath = fullPath + '/' + resourcePath;
|
||||
const isEncrypted = await EncryptionService.instance().fileIsEncrypted(fullResourcePath);
|
||||
if (isEncrypted) {
|
||||
encryptedResourceCount++;
|
||||
@@ -130,7 +131,7 @@ class Command extends BaseCommand {
|
||||
} else if (stat.isDirectory()) {
|
||||
continue;
|
||||
} else {
|
||||
const content = await fs.readFile(fullPath, "utf8");
|
||||
const content = await fs.readFile(fullPath, 'utf8');
|
||||
const item = await BaseItem.unserialize(content);
|
||||
const ItemClass = BaseItem.itemClass(item);
|
||||
|
||||
@@ -152,22 +153,22 @@ class Command extends BaseCommand {
|
||||
}
|
||||
}
|
||||
|
||||
this.stdout("Encrypted items: " + encryptedItemCount + "/" + itemCount);
|
||||
this.stdout("Encrypted resources: " + encryptedResourceCount + "/" + resourceCount);
|
||||
this.stdout("Other items (never encrypted): " + otherItemCount);
|
||||
this.stdout('Encrypted items: ' + encryptedItemCount + '/' + itemCount);
|
||||
this.stdout('Encrypted resources: ' + encryptedResourceCount + '/' + resourceCount);
|
||||
this.stdout('Other items (never encrypted): ' + otherItemCount);
|
||||
|
||||
if (options.verbose) {
|
||||
this.stdout("");
|
||||
this.stdout("# Encrypted paths");
|
||||
this.stdout("");
|
||||
this.stdout('');
|
||||
this.stdout('# Encrypted paths');
|
||||
this.stdout('');
|
||||
for (let i = 0; i < encryptedPaths.length; i++) {
|
||||
const path = encryptedPaths[i];
|
||||
this.stdout(path);
|
||||
}
|
||||
|
||||
this.stdout("");
|
||||
this.stdout("# Decrypted paths");
|
||||
this.stdout("");
|
||||
this.stdout('');
|
||||
this.stdout('# Decrypted paths');
|
||||
this.stdout('');
|
||||
for (let i = 0; i < decryptedPaths.length; i++) {
|
||||
const path = decryptedPaths[i];
|
||||
this.stdout(path);
|
||||
@@ -177,6 +178,7 @@ class Command extends BaseCommand {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
module.exports = Command;
|
||||
module.exports = Command;
|
||||
@@ -1,22 +1,23 @@
|
||||
const fs = require("fs-extra");
|
||||
const { BaseCommand } = require("./base-command.js");
|
||||
const { uuid } = require("lib/uuid.js");
|
||||
const { app } = require("./app.js");
|
||||
const { _ } = require("lib/locale.js");
|
||||
const Folder = require("lib/models/Folder.js");
|
||||
const Note = require("lib/models/Note.js");
|
||||
const Setting = require("lib/models/Setting.js");
|
||||
const BaseModel = require("lib/BaseModel.js");
|
||||
const { cliUtils } = require("./cli-utils.js");
|
||||
const { time } = require("lib/time-utils.js");
|
||||
const fs = require('fs-extra');
|
||||
const { BaseCommand } = require('./base-command.js');
|
||||
const { uuid } = require('lib/uuid.js');
|
||||
const { app } = require('./app.js');
|
||||
const { _ } = require('lib/locale.js');
|
||||
const Folder = require('lib/models/Folder.js');
|
||||
const Note = require('lib/models/Note.js');
|
||||
const Setting = require('lib/models/Setting.js');
|
||||
const BaseModel = require('lib/BaseModel.js');
|
||||
const { cliUtils } = require('./cli-utils.js');
|
||||
const { time } = require('lib/time-utils.js');
|
||||
|
||||
class Command extends BaseCommand {
|
||||
|
||||
usage() {
|
||||
return "edit <note>";
|
||||
return 'edit <note>';
|
||||
}
|
||||
|
||||
description() {
|
||||
return _("Edit note.");
|
||||
return _('Edit note.');
|
||||
}
|
||||
|
||||
async action(args) {
|
||||
@@ -25,22 +26,22 @@ class Command extends BaseCommand {
|
||||
|
||||
const onFinishedEditing = async () => {
|
||||
if (tempFilePath) fs.removeSync(tempFilePath);
|
||||
};
|
||||
}
|
||||
|
||||
const textEditorPath = () => {
|
||||
if (Setting.value("editor")) return Setting.value("editor");
|
||||
if (Setting.value('editor')) return Setting.value('editor');
|
||||
if (process.env.EDITOR) return process.env.EDITOR;
|
||||
throw new Error(_("No text editor is defined. Please set it using `config editor <editor-path>`"));
|
||||
};
|
||||
throw new Error(_('No text editor is defined. Please set it using `config editor <editor-path>`'));
|
||||
}
|
||||
|
||||
try {
|
||||
try {
|
||||
// -------------------------------------------------------------------------
|
||||
// Load note or create it if it doesn't exist
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
let title = args["note"];
|
||||
let title = args['note'];
|
||||
|
||||
if (!app().currentFolder()) throw new Error(_("No active notebook."));
|
||||
if (!app().currentFolder()) throw new Error(_('No active notebook.'));
|
||||
let note = await app().loadItem(BaseModel.TYPE_NOTE, title);
|
||||
|
||||
this.encryptionCheck(note);
|
||||
@@ -57,14 +58,14 @@ class Command extends BaseCommand {
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
let editorPath = textEditorPath();
|
||||
let editorArgs = editorPath.split(" ");
|
||||
let editorArgs = editorPath.split(' ');
|
||||
|
||||
editorPath = editorArgs[0];
|
||||
editorArgs = editorArgs.splice(1);
|
||||
|
||||
const originalContent = await Note.serializeForEdit(note);
|
||||
|
||||
tempFilePath = Setting.value("tempDir") + "/" + uuid.create() + ".md";
|
||||
tempFilePath = Setting.value('tempDir') + '/' + uuid.create() + '.md';
|
||||
editorArgs.push(tempFilePath);
|
||||
|
||||
await fs.writeFile(tempFilePath, originalContent);
|
||||
@@ -73,56 +74,46 @@ class Command extends BaseCommand {
|
||||
// Start editing the file
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
this.logger().info("Disabling fullscreen...");
|
||||
this.logger().info('Disabling fullscreen...');
|
||||
|
||||
app()
|
||||
.gui()
|
||||
.showModalOverlay(_("Starting to edit note. Close the editor to get back to the prompt."));
|
||||
await app()
|
||||
.gui()
|
||||
.forceRender();
|
||||
const termState = app()
|
||||
.gui()
|
||||
.termSaveState();
|
||||
app().gui().showModalOverlay(_('Starting to edit note. Close the editor to get back to the prompt.'));
|
||||
await app().gui().forceRender();
|
||||
const termState = app().gui().termSaveState();
|
||||
|
||||
const spawnSync = require("child_process").spawnSync;
|
||||
const result = spawnSync(editorPath, editorArgs, { stdio: "inherit" });
|
||||
const spawnSync = require('child_process').spawnSync;
|
||||
const result = spawnSync(editorPath, editorArgs, { stdio: 'inherit' });
|
||||
|
||||
if (result.error) this.stdout(_("Error opening note in editor: %s", result.error.message));
|
||||
if (result.error) this.stdout(_('Error opening note in editor: %s', result.error.message));
|
||||
|
||||
app()
|
||||
.gui()
|
||||
.termRestoreState(termState);
|
||||
app()
|
||||
.gui()
|
||||
.hideModalOverlay();
|
||||
app()
|
||||
.gui()
|
||||
.forceRender();
|
||||
app().gui().termRestoreState(termState);
|
||||
app().gui().hideModalOverlay();
|
||||
app().gui().forceRender();
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Save the note and clean up
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
const updatedContent = await fs.readFile(tempFilePath, "utf8");
|
||||
const updatedContent = await fs.readFile(tempFilePath, 'utf8');
|
||||
if (updatedContent !== originalContent) {
|
||||
let updatedNote = await Note.unserializeForEdit(updatedContent);
|
||||
updatedNote.id = note.id;
|
||||
await Note.save(updatedNote);
|
||||
this.stdout(_("Note has been saved."));
|
||||
this.stdout(_('Note has been saved.'));
|
||||
}
|
||||
|
||||
this.dispatch({
|
||||
type: "NOTE_SELECT",
|
||||
type: 'NOTE_SELECT',
|
||||
id: note.id,
|
||||
});
|
||||
|
||||
await onFinishedEditing();
|
||||
} catch (error) {
|
||||
|
||||
} catch(error) {
|
||||
await onFinishedEditing();
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
module.exports = Command;
|
||||
module.exports = Command;
|
||||
@@ -1,23 +1,25 @@
|
||||
const { BaseCommand } = require("./base-command.js");
|
||||
const { app } = require("./app.js");
|
||||
const { _ } = require("lib/locale.js");
|
||||
const { BaseCommand } = require('./base-command.js');
|
||||
const { app } = require('./app.js');
|
||||
const { _ } = require('lib/locale.js');
|
||||
|
||||
class Command extends BaseCommand {
|
||||
|
||||
usage() {
|
||||
return "exit";
|
||||
return 'exit';
|
||||
}
|
||||
|
||||
description() {
|
||||
return _("Exits the application.");
|
||||
return _('Exits the application.');
|
||||
}
|
||||
|
||||
compatibleUis() {
|
||||
return ["gui"];
|
||||
return ['gui'];
|
||||
}
|
||||
|
||||
async action(args) {
|
||||
await app().exit();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
module.exports = Command;
|
||||
module.exports = Command;
|
||||
@@ -1,18 +1,19 @@
|
||||
const { BaseCommand } = require("./base-command.js");
|
||||
const { Database } = require("lib/database.js");
|
||||
const { app } = require("./app.js");
|
||||
const Setting = require("lib/models/Setting.js");
|
||||
const { _ } = require("lib/locale.js");
|
||||
const { ReportService } = require("lib/services/report.js");
|
||||
const fs = require("fs-extra");
|
||||
const { BaseCommand } = require('./base-command.js');
|
||||
const { Database } = require('lib/database.js');
|
||||
const { app } = require('./app.js');
|
||||
const Setting = require('lib/models/Setting.js');
|
||||
const { _ } = require('lib/locale.js');
|
||||
const { ReportService } = require('lib/services/report.js');
|
||||
const fs = require('fs-extra');
|
||||
|
||||
class Command extends BaseCommand {
|
||||
|
||||
usage() {
|
||||
return "export-sync-status";
|
||||
return 'export-sync-status';
|
||||
}
|
||||
|
||||
description() {
|
||||
return "Export sync status";
|
||||
return 'Export sync status';
|
||||
}
|
||||
|
||||
hidden() {
|
||||
@@ -21,18 +22,15 @@ class Command extends BaseCommand {
|
||||
|
||||
async action(args) {
|
||||
const service = new ReportService();
|
||||
const csv = await service.basicItemList({ format: "csv" });
|
||||
const filePath = Setting.value("profileDir") + "/syncReport-" + new Date().getTime() + ".csv";
|
||||
const csv = await service.basicItemList({ format: 'csv' });
|
||||
const filePath = Setting.value('profileDir') + '/syncReport-' + (new Date()).getTime() + '.csv';
|
||||
await fs.writeFileSync(filePath, csv);
|
||||
this.stdout("Sync status exported to " + filePath);
|
||||
this.stdout('Sync status exported to ' + filePath);
|
||||
|
||||
app()
|
||||
.gui()
|
||||
.showConsole();
|
||||
app()
|
||||
.gui()
|
||||
.maximizeConsole();
|
||||
app().gui().showConsole();
|
||||
app().gui().maximizeConsole();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
module.exports = Command;
|
||||
module.exports = Command;
|
||||
@@ -1,56 +1,61 @@
|
||||
const { BaseCommand } = require("./base-command.js");
|
||||
const InteropService = require("lib/services/InteropService.js");
|
||||
const BaseModel = require("lib/BaseModel.js");
|
||||
const Note = require("lib/models/Note.js");
|
||||
const { reg } = require("lib/registry.js");
|
||||
const { app } = require("./app.js");
|
||||
const { _ } = require("lib/locale.js");
|
||||
const fs = require("fs-extra");
|
||||
const { BaseCommand } = require('./base-command.js');
|
||||
const InteropService = require('lib/services/InteropService.js');
|
||||
const BaseModel = require('lib/BaseModel.js');
|
||||
const Note = require('lib/models/Note.js');
|
||||
const { reg } = require('lib/registry.js');
|
||||
const { app } = require('./app.js');
|
||||
const { _ } = require('lib/locale.js');
|
||||
const fs = require('fs-extra');
|
||||
|
||||
class Command extends BaseCommand {
|
||||
|
||||
usage() {
|
||||
return "export <path>";
|
||||
return 'export <path>';
|
||||
}
|
||||
|
||||
description() {
|
||||
return _("Exports Joplin data to the given path. By default, it will export the complete database including notebooks, notes, tags and resources.");
|
||||
return _('Exports Joplin data to the given path. By default, it will export the complete database including notebooks, notes, tags and resources.');
|
||||
}
|
||||
|
||||
options() {
|
||||
const service = new InteropService();
|
||||
const formats = service
|
||||
.modules()
|
||||
.filter(m => m.type === "exporter")
|
||||
.map(m => m.format + (m.description ? " (" + m.description + ")" : ""));
|
||||
const formats = service.modules()
|
||||
.filter(m => m.type === 'exporter')
|
||||
.map(m => m.format + (m.description ? ' (' + m.description + ')' : ''));
|
||||
|
||||
return [
|
||||
["--format <format>", _("Destination format: %s", formats.join(", "))],
|
||||
["--note <note>", _("Exports only the given note.")],
|
||||
["--notebook <notebook>", _("Exports only the given notebook.")],
|
||||
['--format <format>', _('Destination format: %s', formats.join(', '))],
|
||||
['--note <note>', _('Exports only the given note.')],
|
||||
['--notebook <notebook>', _('Exports only the given notebook.')],
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
async action(args) {
|
||||
let exportOptions = {};
|
||||
exportOptions.path = args.path;
|
||||
|
||||
exportOptions.format = args.options.format ? args.options.format : "jex";
|
||||
exportOptions.format = args.options.format ? args.options.format : 'jex';
|
||||
|
||||
if (args.options.note) {
|
||||
|
||||
const notes = await app().loadItems(BaseModel.TYPE_NOTE, args.options.note, { parent: app().currentFolder() });
|
||||
if (!notes.length) throw new Error(_('Cannot find "%s".', args.options.note));
|
||||
exportOptions.sourceNoteIds = notes.map(n => n.id);
|
||||
exportOptions.sourceNoteIds = notes.map((n) => n.id);
|
||||
|
||||
} else if (args.options.notebook) {
|
||||
|
||||
const folders = await app().loadItems(BaseModel.TYPE_FOLDER, args.options.notebook);
|
||||
if (!folders.length) throw new Error(_('Cannot find "%s".', args.options.notebook));
|
||||
exportOptions.sourceFolderIds = folders.map(n => n.id);
|
||||
exportOptions.sourceFolderIds = folders.map((n) => n.id);
|
||||
|
||||
}
|
||||
|
||||
const service = new InteropService();
|
||||
const result = await service.export(exportOptions);
|
||||
|
||||
result.warnings.map(w => this.stdout(w));
|
||||
result.warnings.map((w) => this.stdout(w));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
module.exports = Command;
|
||||
module.exports = Command;
|
||||
@@ -1,31 +1,31 @@
|
||||
const { BaseCommand } = require("./base-command.js");
|
||||
const { app } = require("./app.js");
|
||||
const { _ } = require("lib/locale.js");
|
||||
const BaseModel = require("lib/BaseModel.js");
|
||||
const Folder = require("lib/models/Folder.js");
|
||||
const Note = require("lib/models/Note.js");
|
||||
const { BaseCommand } = require('./base-command.js');
|
||||
const { app } = require('./app.js');
|
||||
const { _ } = require('lib/locale.js');
|
||||
const BaseModel = require('lib/BaseModel.js');
|
||||
const Folder = require('lib/models/Folder.js');
|
||||
const Note = require('lib/models/Note.js');
|
||||
|
||||
class Command extends BaseCommand {
|
||||
|
||||
usage() {
|
||||
return "geoloc <note>";
|
||||
return 'geoloc <note>';
|
||||
}
|
||||
|
||||
description() {
|
||||
return _("Displays a geolocation URL for the note.");
|
||||
return _('Displays a geolocation URL for the note.');
|
||||
}
|
||||
|
||||
async action(args) {
|
||||
let title = args["note"];
|
||||
let title = args['note'];
|
||||
|
||||
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.stdout(url);
|
||||
|
||||
app()
|
||||
.gui()
|
||||
.showConsole();
|
||||
app().gui().showConsole();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
module.exports = Command;
|
||||
module.exports = Command;
|
||||
@@ -1,19 +1,20 @@
|
||||
const { BaseCommand } = require("./base-command.js");
|
||||
const { app } = require("./app.js");
|
||||
const { renderCommandHelp } = require("./help-utils.js");
|
||||
const { Database } = require("lib/database.js");
|
||||
const Setting = require("lib/models/Setting.js");
|
||||
const { wrap } = require("lib/string-utils.js");
|
||||
const { _ } = require("lib/locale.js");
|
||||
const { cliUtils } = require("./cli-utils.js");
|
||||
const { BaseCommand } = require('./base-command.js');
|
||||
const { app } = require('./app.js');
|
||||
const { renderCommandHelp } = require('./help-utils.js');
|
||||
const { Database } = require('lib/database.js');
|
||||
const Setting = require('lib/models/Setting.js');
|
||||
const { wrap } = require('lib/string-utils.js');
|
||||
const { _ } = require('lib/locale.js');
|
||||
const { cliUtils } = require('./cli-utils.js');
|
||||
|
||||
class Command extends BaseCommand {
|
||||
|
||||
usage() {
|
||||
return "help [command]";
|
||||
return 'help [command]';
|
||||
}
|
||||
|
||||
description() {
|
||||
return _("Displays usage information.");
|
||||
return _('Displays usage information.');
|
||||
}
|
||||
|
||||
allCommands() {
|
||||
@@ -27,7 +28,7 @@ class Command extends BaseCommand {
|
||||
output.push(command);
|
||||
}
|
||||
|
||||
output.sort((a, b) => (a.name() < b.name() ? -1 : +1));
|
||||
output.sort((a, b) => a.name() < b.name() ? -1 : +1);
|
||||
|
||||
return output;
|
||||
}
|
||||
@@ -35,69 +36,56 @@ class Command extends BaseCommand {
|
||||
async action(args) {
|
||||
const stdoutWidth = app().commandStdoutMaxWidth();
|
||||
|
||||
if (args.command === "shortcuts" || args.command === "keymap") {
|
||||
this.stdout(_("For information on how to customise the shortcuts please visit %s", "http://joplin.cozic.net/terminal/#shortcuts"));
|
||||
this.stdout("");
|
||||
if (args.command === 'shortcuts' || args.command === 'keymap') {
|
||||
this.stdout(_('For information on how to customise the shortcuts please visit %s', 'http://joplin.cozic.net/terminal/#shortcuts'));
|
||||
this.stdout('');
|
||||
|
||||
if (
|
||||
app()
|
||||
.gui()
|
||||
.isDummy()
|
||||
) {
|
||||
throw new Error(_("Shortcuts are not available in CLI mode."));
|
||||
if (app().gui().isDummy()) {
|
||||
throw new Error(_('Shortcuts are not available in CLI mode.'));
|
||||
}
|
||||
|
||||
const keymap = app()
|
||||
.gui()
|
||||
.keymap();
|
||||
const keymap = app().gui().keymap();
|
||||
|
||||
let rows = [];
|
||||
|
||||
for (let i = 0; i < keymap.length; i++) {
|
||||
const item = keymap[i];
|
||||
const keys = item.keys.map(k => (k === " " ? "(SPACE)" : k));
|
||||
rows.push([keys.join(", "), item.command]);
|
||||
const keys = item.keys.map((k) => k === ' ' ? '(SPACE)' : k);
|
||||
rows.push([keys.join(', '), item.command]);
|
||||
}
|
||||
|
||||
cliUtils.printArray(this.stdout.bind(this), rows);
|
||||
} else if (args.command === "all") {
|
||||
} else if (args.command === 'all') {
|
||||
const commands = this.allCommands();
|
||||
const output = commands.map(c => renderCommandHelp(c));
|
||||
this.stdout(output.join("\n\n"));
|
||||
const output = commands.map((c) => renderCommandHelp(c));
|
||||
this.stdout(output.join('\n\n'));
|
||||
} else if (args.command) {
|
||||
const command = app().findCommandByName(args["command"]);
|
||||
const command = app().findCommandByName(args['command']);
|
||||
if (!command) throw new Error(_('Cannot find "%s".', args.command));
|
||||
this.stdout(renderCommandHelp(command, stdoutWidth));
|
||||
} else {
|
||||
const commandNames = this.allCommands().map(a => a.name());
|
||||
const commandNames = this.allCommands().map((a) => a.name());
|
||||
|
||||
this.stdout(_("Type `help [command]` for more information about a command; or type `help all` for the complete usage information."));
|
||||
this.stdout("");
|
||||
this.stdout(_("The possible commands are:"));
|
||||
this.stdout("");
|
||||
this.stdout(commandNames.join(", "));
|
||||
this.stdout("");
|
||||
this.stdout(
|
||||
_(
|
||||
"In any command, a note or notebook can be refered to by title or ID, or using the shortcuts `$n` or `$b` for, respectively, the currently selected note or notebook. `$c` can be used to refer to the currently selected item."
|
||||
)
|
||||
);
|
||||
this.stdout("");
|
||||
this.stdout(_("To move from one pane to another, press Tab or Shift+Tab."));
|
||||
this.stdout(_("Use the arrows and page up/down to scroll the lists and text areas (including this console)."));
|
||||
this.stdout(_('Type `help [command]` for more information about a command; or type `help all` for the complete usage information.'));
|
||||
this.stdout('');
|
||||
this.stdout(_('The possible commands are:'));
|
||||
this.stdout('');
|
||||
this.stdout(commandNames.join(', '));
|
||||
this.stdout('');
|
||||
this.stdout(_('In any command, a note or notebook can be refered to by title or ID, or using the shortcuts `$n` or `$b` for, respectively, the currently selected note or notebook. `$c` can be used to refer to the currently selected item.'));
|
||||
this.stdout('');
|
||||
this.stdout(_('To move from one pane to another, press Tab or Shift+Tab.'));
|
||||
this.stdout(_('Use the arrows and page up/down to scroll the lists and text areas (including this console).'));
|
||||
this.stdout(_('To maximise/minimise the console, press "TC".'));
|
||||
this.stdout(_('To enter command line mode, press ":"'));
|
||||
this.stdout(_("To exit command line mode, press ESCAPE"));
|
||||
this.stdout(_("For the list of keyboard shortcuts and config options, type `help keymap`"));
|
||||
this.stdout(_('To exit command line mode, press ESCAPE'));
|
||||
this.stdout(_('For the list of keyboard shortcuts and config options, type `help keymap`'));
|
||||
}
|
||||
|
||||
app()
|
||||
.gui()
|
||||
.showConsole();
|
||||
app()
|
||||
.gui()
|
||||
.maximizeConsole();
|
||||
app().gui().showConsole();
|
||||
app().gui().maximizeConsole();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
module.exports = Command;
|
||||
module.exports = Command;
|
||||
@@ -1,34 +1,35 @@
|
||||
const { BaseCommand } = require("./base-command.js");
|
||||
const InteropService = require("lib/services/InteropService.js");
|
||||
const BaseModel = require("lib/BaseModel.js");
|
||||
const Note = require("lib/models/Note.js");
|
||||
const { filename, basename, fileExtension } = require("lib/path-utils.js");
|
||||
const { importEnex } = require("lib/import-enex");
|
||||
const { cliUtils } = require("./cli-utils.js");
|
||||
const { reg } = require("lib/registry.js");
|
||||
const { app } = require("./app.js");
|
||||
const { _ } = require("lib/locale.js");
|
||||
const fs = require("fs-extra");
|
||||
const { BaseCommand } = require('./base-command.js');
|
||||
const InteropService = require('lib/services/InteropService.js');
|
||||
const BaseModel = require('lib/BaseModel.js');
|
||||
const Note = require('lib/models/Note.js');
|
||||
const { filename, basename, fileExtension } = require('lib/path-utils.js');
|
||||
const { importEnex } = require('lib/import-enex');
|
||||
const { cliUtils } = require('./cli-utils.js');
|
||||
const { reg } = require('lib/registry.js');
|
||||
const { app } = require('./app.js');
|
||||
const { _ } = require('lib/locale.js');
|
||||
const fs = require('fs-extra');
|
||||
|
||||
class Command extends BaseCommand {
|
||||
|
||||
usage() {
|
||||
return "import <path> [notebook]";
|
||||
return 'import <path> [notebook]';
|
||||
}
|
||||
|
||||
description() {
|
||||
return _("Imports data into Joplin.");
|
||||
return _('Imports data into Joplin.');
|
||||
}
|
||||
|
||||
options() {
|
||||
const service = new InteropService();
|
||||
const formats = service
|
||||
.modules()
|
||||
.filter(m => m.type === "importer")
|
||||
.map(m => m.format);
|
||||
const formats = service.modules().filter(m => m.type === 'importer').map(m => m.format);
|
||||
|
||||
return [["--format <format>", _("Source format: %s", ["auto"].concat(formats).join(", "))], ["-f, --force", _("Do not ask for confirmation.")]];
|
||||
return [
|
||||
['--format <format>', _('Source format: %s', (['auto'].concat(formats)).join(', '))],
|
||||
['-f, --force', _('Do not ask for confirmation.')],
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
async action(args) {
|
||||
let folder = await app().loadItem(BaseModel.TYPE_FOLDER, args.notebook);
|
||||
|
||||
@@ -36,40 +37,39 @@ class Command extends BaseCommand {
|
||||
|
||||
const importOptions = {};
|
||||
importOptions.path = args.path;
|
||||
importOptions.format = args.options.format ? args.options.format : "auto";
|
||||
importOptions.format = args.options.format ? args.options.format : 'auto';
|
||||
importOptions.destinationFolderId = folder ? folder.id : null;
|
||||
|
||||
let lastProgress = "";
|
||||
let lastProgress = '';
|
||||
|
||||
// onProgress/onError supported by Enex import only
|
||||
|
||||
importOptions.onProgress = progressState => {
|
||||
importOptions.onProgress = (progressState) => {
|
||||
let line = [];
|
||||
line.push(_("Found: %d.", progressState.loaded));
|
||||
line.push(_("Created: %d.", progressState.created));
|
||||
if (progressState.updated) line.push(_("Updated: %d.", progressState.updated));
|
||||
if (progressState.skipped) line.push(_("Skipped: %d.", progressState.skipped));
|
||||
if (progressState.resourcesCreated) line.push(_("Resources: %d.", progressState.resourcesCreated));
|
||||
if (progressState.notesTagged) line.push(_("Tagged: %d.", progressState.notesTagged));
|
||||
lastProgress = line.join(" ");
|
||||
line.push(_('Found: %d.', progressState.loaded));
|
||||
line.push(_('Created: %d.', progressState.created));
|
||||
if (progressState.updated) line.push(_('Updated: %d.', progressState.updated));
|
||||
if (progressState.skipped) line.push(_('Skipped: %d.', progressState.skipped));
|
||||
if (progressState.resourcesCreated) line.push(_('Resources: %d.', progressState.resourcesCreated));
|
||||
if (progressState.notesTagged) line.push(_('Tagged: %d.', progressState.notesTagged));
|
||||
lastProgress = line.join(' ');
|
||||
cliUtils.redraw(lastProgress);
|
||||
};
|
||||
|
||||
importOptions.onError = error => {
|
||||
importOptions.onError = (error) => {
|
||||
let s = error.trace ? error.trace : error.toString();
|
||||
this.stdout(s);
|
||||
};
|
||||
|
||||
app()
|
||||
.gui()
|
||||
.showConsole();
|
||||
this.stdout(_("Importing notes..."));
|
||||
app().gui().showConsole();
|
||||
this.stdout(_('Importing notes...'));
|
||||
const service = new InteropService();
|
||||
const result = await service.import(importOptions);
|
||||
result.warnings.map(w => this.stdout(w));
|
||||
result.warnings.map((w) => this.stdout(w));
|
||||
cliUtils.redrawDone();
|
||||
if (lastProgress) this.stdout(_("The notes have been imported: %s", lastProgress));
|
||||
if (lastProgress) this.stdout(_('The notes have been imported: %s', lastProgress));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
module.exports = Command;
|
||||
module.exports = Command;
|
||||
@@ -1,45 +1,41 @@
|
||||
const { BaseCommand } = require("./base-command.js");
|
||||
const { app } = require("./app.js");
|
||||
const { _ } = require("lib/locale.js");
|
||||
const BaseModel = require("lib/BaseModel.js");
|
||||
const Folder = require("lib/models/Folder.js");
|
||||
const Setting = require("lib/models/Setting.js");
|
||||
const Note = require("lib/models/Note.js");
|
||||
const { sprintf } = require("sprintf-js");
|
||||
const { time } = require("lib/time-utils.js");
|
||||
const { cliUtils } = require("./cli-utils.js");
|
||||
const { BaseCommand } = require('./base-command.js');
|
||||
const { app } = require('./app.js');
|
||||
const { _ } = require('lib/locale.js');
|
||||
const BaseModel = require('lib/BaseModel.js');
|
||||
const Folder = require('lib/models/Folder.js');
|
||||
const Setting = require('lib/models/Setting.js');
|
||||
const Note = require('lib/models/Note.js');
|
||||
const { sprintf } = require('sprintf-js');
|
||||
const { time } = require('lib/time-utils.js');
|
||||
const { cliUtils } = require('./cli-utils.js');
|
||||
|
||||
class Command extends BaseCommand {
|
||||
|
||||
usage() {
|
||||
return "ls [note-pattern]";
|
||||
return 'ls [note-pattern]';
|
||||
}
|
||||
|
||||
description() {
|
||||
return _("Displays the notes in the current notebook. Use `ls /` to display the list of notebooks.");
|
||||
return _('Displays the notes in the current notebook. Use `ls /` to display the list of notebooks.');
|
||||
}
|
||||
|
||||
enabled() {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
options() {
|
||||
return [
|
||||
["-n, --limit <num>", _("Displays only the first top <num> notes.")],
|
||||
["-s, --sort <field>", _("Sorts the item by <field> (eg. title, updated_time, created_time).")],
|
||||
["-r, --reverse", _("Reverses the sorting order.")],
|
||||
[
|
||||
"-t, --type <type>",
|
||||
_(
|
||||
"Displays only the items of the specific type(s). Can be `n` for notes, `t` for to-dos, or `nt` for notes and to-dos (eg. `-tt` would display only the to-dos, while `-ttd` would display notes and to-dos."
|
||||
),
|
||||
],
|
||||
["-f, --format <format>", _('Either "text" or "json"')],
|
||||
["-l, --long", _("Use long list format. Format is ID, NOTE_COUNT (for notebook), DATE, TODO_CHECKED (for to-dos), TITLE")],
|
||||
['-n, --limit <num>', _('Displays only the first top <num> notes.')],
|
||||
['-s, --sort <field>', _('Sorts the item by <field> (eg. title, updated_time, created_time).')],
|
||||
['-r, --reverse', _('Reverses the sorting order.')],
|
||||
['-t, --type <type>', _('Displays only the items of the specific type(s). Can be `n` for notes, `t` for to-dos, or `nt` for notes and to-dos (eg. `-tt` would display only the to-dos, while `-ttd` would display notes and to-dos.')],
|
||||
['-f, --format <format>', _('Either "text" or "json"')],
|
||||
['-l, --long', _('Use long list format. Format is ID, NOTE_COUNT (for notebook), DATE, TODO_CHECKED (for to-dos), TITLE')],
|
||||
];
|
||||
}
|
||||
|
||||
async action(args) {
|
||||
let pattern = args["note-pattern"];
|
||||
let pattern = args['note-pattern'];
|
||||
let items = [];
|
||||
let options = args.options;
|
||||
|
||||
@@ -47,30 +43,30 @@ class Command extends BaseCommand {
|
||||
if (options.limit) queryOptions.limit = options.limit;
|
||||
if (options.sort) {
|
||||
queryOptions.orderBy = options.sort;
|
||||
queryOptions.orderByDir = "ASC";
|
||||
queryOptions.orderByDir = 'ASC';
|
||||
}
|
||||
if (options.reverse === true) queryOptions.orderByDir = queryOptions.orderByDir == "ASC" ? "DESC" : "ASC";
|
||||
if (options.reverse === true) queryOptions.orderByDir = queryOptions.orderByDir == 'ASC' ? 'DESC' : 'ASC';
|
||||
queryOptions.caseInsensitive = true;
|
||||
if (options.type) {
|
||||
queryOptions.itemTypes = [];
|
||||
if (options.type.indexOf("n") >= 0) queryOptions.itemTypes.push("note");
|
||||
if (options.type.indexOf("t") >= 0) queryOptions.itemTypes.push("todo");
|
||||
if (options.type.indexOf('n') >= 0) queryOptions.itemTypes.push('note');
|
||||
if (options.type.indexOf('t') >= 0) queryOptions.itemTypes.push('todo');
|
||||
}
|
||||
if (pattern) queryOptions.titlePattern = pattern;
|
||||
queryOptions.uncompletedTodosOnTop = Setting.value("uncompletedTodosOnTop");
|
||||
queryOptions.uncompletedTodosOnTop = Setting.value('uncompletedTodosOnTop');
|
||||
|
||||
let modelType = null;
|
||||
if (pattern == "/" || !app().currentFolder()) {
|
||||
if (pattern == '/' || !app().currentFolder()) {
|
||||
queryOptions.includeConflictFolder = true;
|
||||
items = await Folder.all(queryOptions);
|
||||
modelType = Folder.modelType();
|
||||
} else {
|
||||
if (!app().currentFolder()) throw new Error(_("Please select a notebook first."));
|
||||
if (!app().currentFolder()) throw new Error(_('Please select a notebook first.'));
|
||||
items = await Note.previews(app().currentFolder().id, queryOptions);
|
||||
modelType = Note.modelType();
|
||||
}
|
||||
|
||||
if (options.format && options.format == "json") {
|
||||
if (options.format && options.format == 'json') {
|
||||
this.stdout(JSON.stringify(items));
|
||||
} else {
|
||||
let hasTodos = false;
|
||||
@@ -102,16 +98,16 @@ class Command extends BaseCommand {
|
||||
|
||||
let title = item.title;
|
||||
if (!shortIdShown && (seenTitles.indexOf(item.title) >= 0 || !item.title)) {
|
||||
title += " (" + BaseModel.shortId(item.id) + ")";
|
||||
title += ' (' + BaseModel.shortId(item.id) + ')';
|
||||
} else {
|
||||
seenTitles.push(item.title);
|
||||
}
|
||||
|
||||
if (hasTodos) {
|
||||
if (item.is_todo) {
|
||||
row.push(sprintf("[%s]", !!item.todo_completed ? "X" : " "));
|
||||
row.push(sprintf('[%s]', !!item.todo_completed ? 'X' : ' '));
|
||||
} else {
|
||||
row.push(" ");
|
||||
row.push(' ');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -122,7 +118,9 @@ class Command extends BaseCommand {
|
||||
|
||||
cliUtils.printArray(this.stdout.bind(this), rows);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
module.exports = Command;
|
||||
module.exports = Command;
|
||||
@@ -1,22 +1,24 @@
|
||||
const { BaseCommand } = require("./base-command.js");
|
||||
const { app } = require("./app.js");
|
||||
const { _ } = require("lib/locale.js");
|
||||
const Folder = require("lib/models/Folder.js");
|
||||
const { reg } = require("lib/registry.js");
|
||||
const { BaseCommand } = require('./base-command.js');
|
||||
const { app } = require('./app.js');
|
||||
const { _ } = require('lib/locale.js');
|
||||
const Folder = require('lib/models/Folder.js');
|
||||
const { reg } = require('lib/registry.js');
|
||||
|
||||
class Command extends BaseCommand {
|
||||
|
||||
usage() {
|
||||
return "mkbook <new-notebook>";
|
||||
return 'mkbook <new-notebook>';
|
||||
}
|
||||
|
||||
description() {
|
||||
return _("Creates a new notebook.");
|
||||
return _('Creates a new notebook.');
|
||||
}
|
||||
|
||||
async action(args) {
|
||||
let folder = await Folder.save({ title: args["new-notebook"] }, { userSideValidation: true });
|
||||
let folder = await Folder.save({ title: args['new-notebook'] }, { userSideValidation: true });
|
||||
app().switchCurrentFolder(folder);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
module.exports = Command;
|
||||
module.exports = Command;
|
||||
@@ -1,22 +1,23 @@
|
||||
const { BaseCommand } = require("./base-command.js");
|
||||
const { app } = require("./app.js");
|
||||
const { _ } = require("lib/locale.js");
|
||||
const Note = require("lib/models/Note.js");
|
||||
const { BaseCommand } = require('./base-command.js');
|
||||
const { app } = require('./app.js');
|
||||
const { _ } = require('lib/locale.js');
|
||||
const Note = require('lib/models/Note.js');
|
||||
|
||||
class Command extends BaseCommand {
|
||||
|
||||
usage() {
|
||||
return "mknote <new-note>";
|
||||
return 'mknote <new-note>';
|
||||
}
|
||||
|
||||
description() {
|
||||
return _("Creates a new note.");
|
||||
return _('Creates a new note.');
|
||||
}
|
||||
|
||||
async action(args) {
|
||||
if (!app().currentFolder()) throw new Error(_("Notes can only be created within a notebook."));
|
||||
if (!app().currentFolder()) throw new Error(_('Notes can only be created within a notebook.'));
|
||||
|
||||
let note = {
|
||||
title: args["new-note"],
|
||||
title: args['new-note'],
|
||||
parent_id: app().currentFolder().id,
|
||||
};
|
||||
|
||||
@@ -25,6 +26,7 @@ class Command extends BaseCommand {
|
||||
|
||||
app().switchCurrentFolder(app().currentFolder());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
module.exports = Command;
|
||||
module.exports = Command;
|
||||
@@ -1,22 +1,23 @@
|
||||
const { BaseCommand } = require("./base-command.js");
|
||||
const { app } = require("./app.js");
|
||||
const { _ } = require("lib/locale.js");
|
||||
const Note = require("lib/models/Note.js");
|
||||
const { BaseCommand } = require('./base-command.js');
|
||||
const { app } = require('./app.js');
|
||||
const { _ } = require('lib/locale.js');
|
||||
const Note = require('lib/models/Note.js');
|
||||
|
||||
class Command extends BaseCommand {
|
||||
|
||||
usage() {
|
||||
return "mktodo <new-todo>";
|
||||
return 'mktodo <new-todo>';
|
||||
}
|
||||
|
||||
description() {
|
||||
return _("Creates a new to-do.");
|
||||
return _('Creates a new to-do.');
|
||||
}
|
||||
|
||||
async action(args) {
|
||||
if (!app().currentFolder()) throw new Error(_("Notes can only be created within a notebook."));
|
||||
if (!app().currentFolder()) throw new Error(_('Notes can only be created within a notebook.'));
|
||||
|
||||
let note = {
|
||||
title: args["new-todo"],
|
||||
title: args['new-todo'],
|
||||
parent_id: app().currentFolder().id,
|
||||
is_todo: 1,
|
||||
};
|
||||
@@ -26,6 +27,7 @@ class Command extends BaseCommand {
|
||||
|
||||
app().switchCurrentFolder(app().currentFolder());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
module.exports = Command;
|
||||
module.exports = Command;
|
||||
@@ -1,24 +1,25 @@
|
||||
const { BaseCommand } = require("./base-command.js");
|
||||
const { app } = require("./app.js");
|
||||
const { _ } = require("lib/locale.js");
|
||||
const BaseModel = require("lib/BaseModel.js");
|
||||
const Folder = require("lib/models/Folder.js");
|
||||
const Note = require("lib/models/Note.js");
|
||||
const { BaseCommand } = require('./base-command.js');
|
||||
const { app } = require('./app.js');
|
||||
const { _ } = require('lib/locale.js');
|
||||
const BaseModel = require('lib/BaseModel.js');
|
||||
const Folder = require('lib/models/Folder.js');
|
||||
const Note = require('lib/models/Note.js');
|
||||
|
||||
class Command extends BaseCommand {
|
||||
|
||||
usage() {
|
||||
return "mv <note> [notebook]";
|
||||
return 'mv <note> [notebook]';
|
||||
}
|
||||
|
||||
description() {
|
||||
return _("Moves the notes matching <note> to [notebook].");
|
||||
return _('Moves the notes matching <note> to [notebook].');
|
||||
}
|
||||
|
||||
async action(args) {
|
||||
const pattern = args["note"];
|
||||
const destination = args["notebook"];
|
||||
|
||||
const folder = await Folder.loadByField("title", destination);
|
||||
const pattern = args['note'];
|
||||
const destination = args['notebook'];
|
||||
|
||||
const folder = await Folder.loadByField('title', destination);
|
||||
if (!folder) throw new Error(_('Cannot find "%s".', destination));
|
||||
|
||||
const notes = await app().loadItems(BaseModel.TYPE_NOTE, pattern);
|
||||
@@ -28,6 +29,7 @@ class Command extends BaseCommand {
|
||||
await Note.moveToFolder(notes[i].id, folder.id);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
module.exports = Command;
|
||||
module.exports = Command;
|
||||
@@ -1,24 +1,25 @@
|
||||
const { BaseCommand } = require("./base-command.js");
|
||||
const { app } = require("./app.js");
|
||||
const { _ } = require("lib/locale.js");
|
||||
const BaseModel = require("lib/BaseModel.js");
|
||||
const Folder = require("lib/models/Folder.js");
|
||||
const Note = require("lib/models/Note.js");
|
||||
const { BaseCommand } = require('./base-command.js');
|
||||
const { app } = require('./app.js');
|
||||
const { _ } = require('lib/locale.js');
|
||||
const BaseModel = require('lib/BaseModel.js');
|
||||
const Folder = require('lib/models/Folder.js');
|
||||
const Note = require('lib/models/Note.js');
|
||||
|
||||
class Command extends BaseCommand {
|
||||
|
||||
usage() {
|
||||
return "ren <item> <name>";
|
||||
return 'ren <item> <name>';
|
||||
}
|
||||
|
||||
description() {
|
||||
return _("Renames the given <item> (note or notebook) to <name>.");
|
||||
return _('Renames the given <item> (note or notebook) to <name>.');
|
||||
}
|
||||
|
||||
async action(args) {
|
||||
const pattern = args["item"];
|
||||
const name = args["name"];
|
||||
const pattern = args['item'];
|
||||
const name = args['name'];
|
||||
|
||||
const item = await app().loadItem("folderOrNote", pattern);
|
||||
const item = await app().loadItem('folderOrNote', pattern);
|
||||
this.encryptionCheck(item);
|
||||
if (!item) throw new Error(_('Cannot find "%s".', pattern));
|
||||
|
||||
@@ -34,6 +35,7 @@ class Command extends BaseCommand {
|
||||
await Note.save(newItem);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
module.exports = Command;
|
||||
module.exports = Command;
|
||||
@@ -1,36 +1,40 @@
|
||||
const { BaseCommand } = require("./base-command.js");
|
||||
const { app } = require("./app.js");
|
||||
const { _ } = require("lib/locale.js");
|
||||
const BaseItem = require("lib/models/BaseItem.js");
|
||||
const Folder = require("lib/models/Folder.js");
|
||||
const Note = require("lib/models/Note.js");
|
||||
const BaseModel = require("lib/BaseModel.js");
|
||||
const { cliUtils } = require("./cli-utils.js");
|
||||
const { BaseCommand } = require('./base-command.js');
|
||||
const { app } = require('./app.js');
|
||||
const { _ } = require('lib/locale.js');
|
||||
const BaseItem = require('lib/models/BaseItem.js');
|
||||
const Folder = require('lib/models/Folder.js');
|
||||
const Note = require('lib/models/Note.js');
|
||||
const BaseModel = require('lib/BaseModel.js');
|
||||
const { cliUtils } = require('./cli-utils.js');
|
||||
|
||||
class Command extends BaseCommand {
|
||||
|
||||
usage() {
|
||||
return "rmbook <notebook>";
|
||||
return 'rmbook <notebook>';
|
||||
}
|
||||
|
||||
description() {
|
||||
return _("Deletes the given notebook.");
|
||||
return _('Deletes the given notebook.');
|
||||
}
|
||||
|
||||
options() {
|
||||
return [["-f, --force", _("Deletes the notebook without asking for confirmation.")]];
|
||||
return [
|
||||
['-f, --force', _('Deletes the notebook without asking for confirmation.')],
|
||||
];
|
||||
}
|
||||
|
||||
async action(args) {
|
||||
const pattern = args["notebook"];
|
||||
const pattern = args['notebook'];
|
||||
const force = args.options && args.options.force === true;
|
||||
|
||||
const folder = await app().loadItem(BaseModel.TYPE_FOLDER, pattern);
|
||||
if (!folder) throw new Error(_('Cannot find "%s".', pattern));
|
||||
const ok = force ? true : await this.prompt(_("Delete notebook? All notes within this notebook will also be deleted."), { booleanAnswerDefault: "n" });
|
||||
const ok = force ? true : await this.prompt(_('Delete notebook? All notes within this notebook will also be deleted.'), { booleanAnswerDefault: 'n' });
|
||||
if (!ok) return;
|
||||
|
||||
await Folder.delete(folder.id);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
module.exports = Command;
|
||||
module.exports = Command;
|
||||
@@ -1,37 +1,41 @@
|
||||
const { BaseCommand } = require("./base-command.js");
|
||||
const { app } = require("./app.js");
|
||||
const { _ } = require("lib/locale.js");
|
||||
const BaseItem = require("lib/models/BaseItem.js");
|
||||
const Folder = require("lib/models/Folder.js");
|
||||
const Note = require("lib/models/Note.js");
|
||||
const BaseModel = require("lib/BaseModel.js");
|
||||
const { cliUtils } = require("./cli-utils.js");
|
||||
const { BaseCommand } = require('./base-command.js');
|
||||
const { app } = require('./app.js');
|
||||
const { _ } = require('lib/locale.js');
|
||||
const BaseItem = require('lib/models/BaseItem.js');
|
||||
const Folder = require('lib/models/Folder.js');
|
||||
const Note = require('lib/models/Note.js');
|
||||
const BaseModel = require('lib/BaseModel.js');
|
||||
const { cliUtils } = require('./cli-utils.js');
|
||||
|
||||
class Command extends BaseCommand {
|
||||
|
||||
usage() {
|
||||
return "rmnote <note-pattern>";
|
||||
return 'rmnote <note-pattern>';
|
||||
}
|
||||
|
||||
description() {
|
||||
return _("Deletes the notes matching <note-pattern>.");
|
||||
return _('Deletes the notes matching <note-pattern>.');
|
||||
}
|
||||
|
||||
options() {
|
||||
return [["-f, --force", _("Deletes the notes without asking for confirmation.")]];
|
||||
return [
|
||||
['-f, --force', _('Deletes the notes without asking for confirmation.')],
|
||||
];
|
||||
}
|
||||
|
||||
async action(args) {
|
||||
const pattern = args["note-pattern"];
|
||||
const pattern = args['note-pattern'];
|
||||
const force = args.options && args.options.force === true;
|
||||
|
||||
const notes = await app().loadItems(BaseModel.TYPE_NOTE, pattern);
|
||||
if (!notes.length) throw new Error(_('Cannot find "%s".', pattern));
|
||||
|
||||
const ok = force ? true : await this.prompt(notes.length > 1 ? _("%d notes match this pattern. Delete them?", notes.length) : _("Delete note?"), { booleanAnswerDefault: "n" });
|
||||
const ok = force ? true : await this.prompt(notes.length > 1 ? _('%d notes match this pattern. Delete them?', notes.length) : _('Delete note?'), { booleanAnswerDefault: 'n' });
|
||||
if (!ok) return;
|
||||
let ids = notes.map(n => n.id);
|
||||
let ids = notes.map((n) => n.id);
|
||||
await Note.batchDelete(ids);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
module.exports = Command;
|
||||
module.exports = Command;
|
||||
@@ -1,29 +1,30 @@
|
||||
const { BaseCommand } = require("./base-command.js");
|
||||
const { app } = require("./app.js");
|
||||
const { _ } = require("lib/locale.js");
|
||||
const BaseModel = require("lib/BaseModel.js");
|
||||
const Folder = require("lib/models/Folder.js");
|
||||
const Note = require("lib/models/Note.js");
|
||||
const { sprintf } = require("sprintf-js");
|
||||
const { time } = require("lib/time-utils.js");
|
||||
const { uuid } = require("lib/uuid.js");
|
||||
const { BaseCommand } = require('./base-command.js');
|
||||
const { app } = require('./app.js');
|
||||
const { _ } = require('lib/locale.js');
|
||||
const BaseModel = require('lib/BaseModel.js');
|
||||
const Folder = require('lib/models/Folder.js');
|
||||
const Note = require('lib/models/Note.js');
|
||||
const { sprintf } = require('sprintf-js');
|
||||
const { time } = require('lib/time-utils.js');
|
||||
const { uuid } = require('lib/uuid.js');
|
||||
|
||||
class Command extends BaseCommand {
|
||||
|
||||
usage() {
|
||||
return "search <pattern> [notebook]";
|
||||
return 'search <pattern> [notebook]';
|
||||
}
|
||||
|
||||
description() {
|
||||
return _("Searches for the given <pattern> in all the notes.");
|
||||
return _('Searches for the given <pattern> in all the notes.');
|
||||
}
|
||||
|
||||
compatibleUis() {
|
||||
return ["gui"];
|
||||
return ['gui'];
|
||||
}
|
||||
|
||||
async action(args) {
|
||||
let pattern = args["pattern"];
|
||||
let folderTitle = args["notebook"];
|
||||
let pattern = args['pattern'];
|
||||
let folderTitle = args['notebook'];
|
||||
|
||||
let folder = null;
|
||||
if (folderTitle) {
|
||||
@@ -34,18 +35,18 @@ class Command extends BaseCommand {
|
||||
const searchId = uuid.create();
|
||||
|
||||
this.dispatch({
|
||||
type: "SEARCH_ADD",
|
||||
type: 'SEARCH_ADD',
|
||||
search: {
|
||||
id: searchId,
|
||||
title: pattern,
|
||||
query_pattern: pattern,
|
||||
query_folder_id: folder ? folder.id : "",
|
||||
query_folder_id: folder ? folder.id : '',
|
||||
type_: BaseModel.TYPE_SEARCH,
|
||||
},
|
||||
});
|
||||
|
||||
this.dispatch({
|
||||
type: "SEARCH_SELECT",
|
||||
type: 'SEARCH_SELECT',
|
||||
id: searchId,
|
||||
});
|
||||
|
||||
@@ -78,6 +79,7 @@ class Command extends BaseCommand {
|
||||
// this.stdout(line);
|
||||
// }
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
module.exports = Command;
|
||||
module.exports = Command;
|
||||
@@ -1,15 +1,16 @@
|
||||
const { BaseCommand } = require("./base-command.js");
|
||||
const { app } = require("./app.js");
|
||||
const { _ } = require("lib/locale.js");
|
||||
const BaseModel = require("lib/BaseModel.js");
|
||||
const { Database } = require("lib/database.js");
|
||||
const Folder = require("lib/models/Folder.js");
|
||||
const Note = require("lib/models/Note.js");
|
||||
const BaseItem = require("lib/models/BaseItem.js");
|
||||
const { BaseCommand } = require('./base-command.js');
|
||||
const { app } = require('./app.js');
|
||||
const { _ } = require('lib/locale.js');
|
||||
const BaseModel = require('lib/BaseModel.js');
|
||||
const { Database } = require('lib/database.js');
|
||||
const Folder = require('lib/models/Folder.js');
|
||||
const Note = require('lib/models/Note.js');
|
||||
const BaseItem = require('lib/models/BaseItem.js');
|
||||
|
||||
class Command extends BaseCommand {
|
||||
|
||||
usage() {
|
||||
return "set <note> <name> [value]";
|
||||
return 'set <note> <name> [value]';
|
||||
}
|
||||
|
||||
description() {
|
||||
@@ -17,18 +18,18 @@ class Command extends BaseCommand {
|
||||
const s = [];
|
||||
for (let i = 0; i < fields.length; i++) {
|
||||
const f = fields[i];
|
||||
if (f.name === "id") continue;
|
||||
s.push(f.name + " (" + Database.enumName("fieldType", f.type) + ")");
|
||||
if (f.name === 'id') continue;
|
||||
s.push(f.name + ' (' + Database.enumName('fieldType', f.type) + ')');
|
||||
}
|
||||
|
||||
return _("Sets the property <name> of the given <note> to the given [value]. Possible properties are:\n\n%s", s.join(", "));
|
||||
return _('Sets the property <name> of the given <note> to the given [value]. Possible properties are:\n\n%s', s.join(', '));
|
||||
}
|
||||
|
||||
async action(args) {
|
||||
let title = args["note"];
|
||||
let propName = args["name"];
|
||||
let propValue = args["value"];
|
||||
if (!propValue) propValue = "";
|
||||
let title = args['note'];
|
||||
let propName = args['name'];
|
||||
let propValue = args['value'];
|
||||
if (!propValue) propValue = '';
|
||||
|
||||
let notes = await app().loadItems(BaseModel.TYPE_NOTE, title);
|
||||
if (!notes.length) throw new Error(_('Cannot find "%s".', title));
|
||||
@@ -44,6 +45,7 @@ class Command extends BaseCommand {
|
||||
await Note.save(newNote);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
module.exports = Command;
|
||||
module.exports = Command;
|
||||
@@ -1,30 +1,31 @@
|
||||
const { BaseCommand } = require("./base-command.js");
|
||||
const { Database } = require("lib/database.js");
|
||||
const { app } = require("./app.js");
|
||||
const Setting = require("lib/models/Setting.js");
|
||||
const { _ } = require("lib/locale.js");
|
||||
const { ReportService } = require("lib/services/report.js");
|
||||
const { BaseCommand } = require('./base-command.js');
|
||||
const { Database } = require('lib/database.js');
|
||||
const { app } = require('./app.js');
|
||||
const Setting = require('lib/models/Setting.js');
|
||||
const { _ } = require('lib/locale.js');
|
||||
const { ReportService } = require('lib/services/report.js');
|
||||
|
||||
class Command extends BaseCommand {
|
||||
|
||||
usage() {
|
||||
return "status";
|
||||
return 'status';
|
||||
}
|
||||
|
||||
description() {
|
||||
return _("Displays summary about the notes and notebooks.");
|
||||
return _('Displays summary about the notes and notebooks.');
|
||||
}
|
||||
|
||||
async action(args) {
|
||||
let service = new ReportService();
|
||||
let report = await service.status(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];
|
||||
|
||||
if (i > 0) this.stdout("");
|
||||
if (i > 0) this.stdout('');
|
||||
|
||||
this.stdout("# " + section.title);
|
||||
this.stdout("");
|
||||
this.stdout('# ' + section.title);
|
||||
this.stdout('');
|
||||
|
||||
for (let n in section.body) {
|
||||
if (!section.body.hasOwnProperty(n)) continue;
|
||||
@@ -33,13 +34,10 @@ class Command extends BaseCommand {
|
||||
}
|
||||
}
|
||||
|
||||
app()
|
||||
.gui()
|
||||
.showConsole();
|
||||
app()
|
||||
.gui()
|
||||
.maximizeConsole();
|
||||
app().gui().showConsole();
|
||||
app().gui().maximizeConsole();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
module.exports = Command;
|
||||
module.exports = Command;
|
||||
@@ -1,18 +1,19 @@
|
||||
const { BaseCommand } = require("./base-command.js");
|
||||
const { app } = require("./app.js");
|
||||
const { _ } = require("lib/locale.js");
|
||||
const { OneDriveApiNodeUtils } = require("./onedrive-api-node-utils.js");
|
||||
const Setting = require("lib/models/Setting.js");
|
||||
const BaseItem = require("lib/models/BaseItem.js");
|
||||
const { Synchronizer } = require("lib/synchronizer.js");
|
||||
const { reg } = require("lib/registry.js");
|
||||
const { cliUtils } = require("./cli-utils.js");
|
||||
const md5 = require("md5");
|
||||
const locker = require("proper-lockfile");
|
||||
const fs = require("fs-extra");
|
||||
const SyncTargetRegistry = require("lib/SyncTargetRegistry");
|
||||
const { BaseCommand } = require('./base-command.js');
|
||||
const { app } = require('./app.js');
|
||||
const { _ } = require('lib/locale.js');
|
||||
const { OneDriveApiNodeUtils } = require('./onedrive-api-node-utils.js');
|
||||
const Setting = require('lib/models/Setting.js');
|
||||
const BaseItem = require('lib/models/BaseItem.js');
|
||||
const { Synchronizer } = require('lib/synchronizer.js');
|
||||
const { reg } = require('lib/registry.js');
|
||||
const { cliUtils } = require('./cli-utils.js');
|
||||
const md5 = require('md5');
|
||||
const locker = require('proper-lockfile');
|
||||
const fs = require('fs-extra');
|
||||
const SyncTargetRegistry = require('lib/SyncTargetRegistry');
|
||||
|
||||
class Command extends BaseCommand {
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this.syncTargetId_ = null;
|
||||
@@ -21,15 +22,17 @@ class Command extends BaseCommand {
|
||||
}
|
||||
|
||||
usage() {
|
||||
return "sync";
|
||||
return 'sync';
|
||||
}
|
||||
|
||||
description() {
|
||||
return _("Synchronises with remote storage.");
|
||||
return _('Synchronises with remote storage.');
|
||||
}
|
||||
|
||||
options() {
|
||||
return [["--target <target>", _("Sync to provided target (defaults to sync.target config value)")]];
|
||||
return [
|
||||
['--target <target>', _('Sync to provided target (defaults to sync.target config value)')],
|
||||
];
|
||||
}
|
||||
|
||||
static lockFile(filePath) {
|
||||
@@ -62,26 +65,23 @@ class Command extends BaseCommand {
|
||||
const syncTarget = reg.syncTarget(this.syncTargetId_);
|
||||
const syncTargetMd = SyncTargetRegistry.idToMetadata(this.syncTargetId_);
|
||||
|
||||
if (this.syncTargetId_ === 3 || this.syncTargetId_ === 4) {
|
||||
// OneDrive
|
||||
if (this.syncTargetId_ === 3 || this.syncTargetId_ === 4) { // OneDrive
|
||||
this.oneDriveApiUtils_ = new OneDriveApiNodeUtils(syncTarget.api());
|
||||
const auth = await this.oneDriveApiUtils_.oauthDance({
|
||||
log: (...s) => {
|
||||
return this.stdout(...s);
|
||||
},
|
||||
log: (...s) => { return this.stdout(...s); }
|
||||
});
|
||||
this.oneDriveApiUtils_ = null;
|
||||
|
||||
Setting.setValue("sync." + this.syncTargetId_ + ".auth", auth ? JSON.stringify(auth) : null);
|
||||
|
||||
Setting.setValue('sync.' + this.syncTargetId_ + '.auth', auth ? JSON.stringify(auth) : null);
|
||||
if (!auth) {
|
||||
this.stdout(_("Authentication was not completed (did not receive an authentication token)."));
|
||||
this.stdout(_('Authentication was not completed (did not receive an authentication token).'));
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
this.stdout(_("Not authentified with %s. Please provide any missing credentials.", syncTarget.label()));
|
||||
this.stdout(_('Not authentified with %s. Please provide any missing credentials.', syncTarget.label()));
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -100,15 +100,15 @@ class Command extends BaseCommand {
|
||||
this.releaseLockFn_ = null;
|
||||
|
||||
// Lock is unique per profile/database
|
||||
const lockFilePath = require("os").tmpdir() + "/synclock_" + md5(escape(Setting.value("profileDir"))); // https://github.com/pvorb/node-md5/issues/41
|
||||
if (!await fs.pathExists(lockFilePath)) await fs.writeFile(lockFilePath, "synclock");
|
||||
const lockFilePath = require('os').tmpdir() + '/synclock_' + md5(escape(Setting.value('profileDir'))); // https://github.com/pvorb/node-md5/issues/41
|
||||
if (!await fs.pathExists(lockFilePath)) await fs.writeFile(lockFilePath, 'synclock');
|
||||
|
||||
try {
|
||||
if (await Command.isLocked(lockFilePath)) throw new Error(_("Synchronisation is already in progress."));
|
||||
if (await Command.isLocked(lockFilePath)) throw new Error(_('Synchronisation is already in progress.'));
|
||||
|
||||
this.releaseLockFn_ = await Command.lockFile(lockFilePath);
|
||||
} catch (error) {
|
||||
if (error.code == "ELOCKED") {
|
||||
if (error.code == 'ELOCKED') {
|
||||
const msg = _('Lock file is already being hold. If you know that no synchronisation is taking place, you may delete the lock file at "%s" and resume the operation.', error.file);
|
||||
this.stdout(msg);
|
||||
return;
|
||||
@@ -125,43 +125,39 @@ class Command extends BaseCommand {
|
||||
};
|
||||
|
||||
try {
|
||||
this.syncTargetId_ = Setting.value("sync.target");
|
||||
this.syncTargetId_ = Setting.value('sync.target');
|
||||
if (args.options.target) this.syncTargetId_ = args.options.target;
|
||||
|
||||
const syncTarget = reg.syncTarget(this.syncTargetId_);
|
||||
|
||||
if (!syncTarget.isAuthenticated()) {
|
||||
app()
|
||||
.gui()
|
||||
.showConsole();
|
||||
app()
|
||||
.gui()
|
||||
.maximizeConsole();
|
||||
app().gui().showConsole();
|
||||
app().gui().maximizeConsole();
|
||||
|
||||
const authDone = await this.doAuth();
|
||||
if (!authDone) return cleanUp();
|
||||
}
|
||||
|
||||
|
||||
const sync = await syncTarget.synchronizer();
|
||||
|
||||
let options = {
|
||||
onProgress: report => {
|
||||
onProgress: (report) => {
|
||||
let lines = Synchronizer.reportToLines(report);
|
||||
if (lines.length) cliUtils.redraw(lines.join(" "));
|
||||
if (lines.length) cliUtils.redraw(lines.join(' '));
|
||||
},
|
||||
onMessage: msg => {
|
||||
onMessage: (msg) => {
|
||||
cliUtils.redrawDone();
|
||||
this.stdout(msg);
|
||||
},
|
||||
};
|
||||
|
||||
this.stdout(_("Synchronisation target: %s (%s)", Setting.enumOptionLabel("sync.target", this.syncTargetId_), this.syncTargetId_));
|
||||
this.stdout(_('Synchronisation target: %s (%s)', Setting.enumOptionLabel('sync.target', this.syncTargetId_), this.syncTargetId_));
|
||||
|
||||
if (!sync) throw new Error(_("Cannot initialize synchroniser."));
|
||||
if (!sync) throw new Error(_('Cannot initialize synchroniser.'));
|
||||
|
||||
this.stdout(_("Starting synchronisation..."));
|
||||
this.stdout(_('Starting synchronisation...'));
|
||||
|
||||
const contextKey = "sync." + this.syncTargetId_ + ".context";
|
||||
const contextKey = 'sync.' + this.syncTargetId_ + '.context';
|
||||
let context = Setting.value(contextKey);
|
||||
|
||||
context = context ? JSON.parse(context) : {};
|
||||
@@ -171,7 +167,7 @@ class Command extends BaseCommand {
|
||||
let newContext = await sync.start(options);
|
||||
Setting.setValue(contextKey, JSON.stringify(newContext));
|
||||
} catch (error) {
|
||||
if (error.code == "alreadyStarted") {
|
||||
if (error.code == 'alreadyStarted') {
|
||||
this.stdout(error.message);
|
||||
} else {
|
||||
throw error;
|
||||
@@ -193,11 +189,11 @@ class Command extends BaseCommand {
|
||||
return;
|
||||
}
|
||||
|
||||
const syncTargetId = this.syncTargetId_ ? this.syncTargetId_ : Setting.value("sync.target");
|
||||
const syncTargetId = this.syncTargetId_ ? this.syncTargetId_ : Setting.value('sync.target');
|
||||
|
||||
cliUtils.redrawDone();
|
||||
|
||||
this.stdout(_("Cancelling... Please wait."));
|
||||
this.stdout(_('Cancelling... Please wait.'));
|
||||
|
||||
const syncTarget = reg.syncTarget(syncTargetId);
|
||||
|
||||
@@ -215,6 +211,7 @@ class Command extends BaseCommand {
|
||||
cancellable() {
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
module.exports = Command;
|
||||
module.exports = Command;
|
||||
@@ -1,20 +1,19 @@
|
||||
const { BaseCommand } = require("./base-command.js");
|
||||
const { app } = require("./app.js");
|
||||
const { _ } = require("lib/locale.js");
|
||||
const Tag = require("lib/models/Tag.js");
|
||||
const BaseModel = require("lib/BaseModel.js");
|
||||
const { BaseCommand } = require('./base-command.js');
|
||||
const { app } = require('./app.js');
|
||||
const { _ } = require('lib/locale.js');
|
||||
const Tag = require('lib/models/Tag.js');
|
||||
const BaseModel = require('lib/BaseModel.js');
|
||||
|
||||
class Command extends BaseCommand {
|
||||
|
||||
usage() {
|
||||
return "tag <tag-command> [tag] [note]";
|
||||
return 'tag <tag-command> [tag] [note]';
|
||||
}
|
||||
|
||||
description() {
|
||||
return _(
|
||||
'<tag-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.'
|
||||
);
|
||||
return _('<tag-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.');
|
||||
}
|
||||
|
||||
|
||||
async action(args) {
|
||||
let tag = null;
|
||||
if (args.tag) tag = await app().loadItem(BaseModel.TYPE_TAG, args.tag);
|
||||
@@ -23,38 +22,35 @@ class Command extends BaseCommand {
|
||||
notes = await app().loadItems(BaseModel.TYPE_NOTE, args.note);
|
||||
}
|
||||
|
||||
const command = args["tag-command"];
|
||||
const command = args['tag-command'];
|
||||
|
||||
if (command == "remove" && !tag) throw new Error(_('Cannot find "%s".', args.tag));
|
||||
if (command == 'remove' && !tag) throw new Error(_('Cannot find "%s".', args.tag));
|
||||
|
||||
if (command == "add") {
|
||||
if (command == 'add') {
|
||||
if (!notes.length) throw new Error(_('Cannot find "%s".', args.note));
|
||||
if (!tag) tag = await Tag.save({ title: args.tag }, { userSideValidation: true });
|
||||
for (let i = 0; i < notes.length; i++) {
|
||||
await Tag.addNote(tag.id, notes[i].id);
|
||||
}
|
||||
} else if (command == "remove") {
|
||||
} else if (command == 'remove') {
|
||||
if (!tag) throw new Error(_('Cannot find "%s".', args.tag));
|
||||
if (!notes.length) throw new Error(_('Cannot find "%s".', args.note));
|
||||
for (let i = 0; i < notes.length; i++) {
|
||||
await Tag.removeNote(tag.id, notes[i].id);
|
||||
}
|
||||
} else if (command == "list") {
|
||||
} else if (command == 'list') {
|
||||
if (tag) {
|
||||
let notes = await Tag.notes(tag.id);
|
||||
notes.map(note => {
|
||||
this.stdout(note.title);
|
||||
});
|
||||
notes.map((note) => { this.stdout(note.title); });
|
||||
} else {
|
||||
let tags = await Tag.all();
|
||||
tags.map(tag => {
|
||||
this.stdout(tag.title);
|
||||
});
|
||||
tags.map((tag) => { this.stdout(tag.title); });
|
||||
}
|
||||
} else {
|
||||
throw new Error(_('Invalid command: "%s"', command));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
module.exports = Command;
|
||||
module.exports = Command;
|
||||
@@ -1,25 +1,24 @@
|
||||
const { BaseCommand } = require("./base-command.js");
|
||||
const { app } = require("./app.js");
|
||||
const { _ } = require("lib/locale.js");
|
||||
const BaseModel = require("lib/BaseModel.js");
|
||||
const Folder = require("lib/models/Folder.js");
|
||||
const Note = require("lib/models/Note.js");
|
||||
const { time } = require("lib/time-utils.js");
|
||||
const { BaseCommand } = require('./base-command.js');
|
||||
const { app } = require('./app.js');
|
||||
const { _ } = require('lib/locale.js');
|
||||
const BaseModel = require('lib/BaseModel.js');
|
||||
const Folder = require('lib/models/Folder.js');
|
||||
const Note = require('lib/models/Note.js');
|
||||
const { time } = require('lib/time-utils.js');
|
||||
|
||||
class Command extends BaseCommand {
|
||||
|
||||
usage() {
|
||||
return "todo <todo-command> <note-pattern>";
|
||||
return 'todo <todo-command> <note-pattern>';
|
||||
}
|
||||
|
||||
description() {
|
||||
return _(
|
||||
'<todo-command> can either be "toggle" or "clear". Use "toggle" to toggle the given to-do between completed and uncompleted state (If the target is a regular note it will be converted to a to-do). Use "clear" to convert the to-do back to a regular note.'
|
||||
);
|
||||
return _('<todo-command> can either be "toggle" or "clear". Use "toggle" to toggle the given to-do between completed and uncompleted state (If the target is a regular note it will be converted to a to-do). Use "clear" to convert the to-do back to a regular note.');
|
||||
}
|
||||
|
||||
async action(args) {
|
||||
const action = args["todo-command"];
|
||||
const pattern = args["note-pattern"];
|
||||
const action = args['todo-command'];
|
||||
const pattern = args['note-pattern'];
|
||||
const notes = await app().loadItems(BaseModel.TYPE_NOTE, pattern);
|
||||
if (!notes.length) throw new Error(_('Cannot find "%s".', pattern));
|
||||
|
||||
@@ -32,19 +31,20 @@ class Command extends BaseCommand {
|
||||
id: note.id,
|
||||
};
|
||||
|
||||
if (action == "toggle") {
|
||||
if (action == 'toggle') {
|
||||
if (!note.is_todo) {
|
||||
toSave = Note.toggleIsTodo(note);
|
||||
} else {
|
||||
toSave.todo_completed = note.todo_completed ? 0 : time.unixMs();
|
||||
}
|
||||
} else if (action == "clear") {
|
||||
} else if (action == 'clear') {
|
||||
toSave.is_todo = 0;
|
||||
}
|
||||
}
|
||||
|
||||
await Note.save(toSave);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
module.exports = Command;
|
||||
module.exports = Command;
|
||||
@@ -1,25 +1,27 @@
|
||||
const { BaseCommand } = require("./base-command.js");
|
||||
const { app } = require("./app.js");
|
||||
const { _ } = require("lib/locale.js");
|
||||
const BaseModel = require("lib/BaseModel.js");
|
||||
const Folder = require("lib/models/Folder.js");
|
||||
const Note = require("lib/models/Note.js");
|
||||
const { time } = require("lib/time-utils.js");
|
||||
const { BaseCommand } = require('./base-command.js');
|
||||
const { app } = require('./app.js');
|
||||
const { _ } = require('lib/locale.js');
|
||||
const BaseModel = require('lib/BaseModel.js');
|
||||
const Folder = require('lib/models/Folder.js');
|
||||
const Note = require('lib/models/Note.js');
|
||||
const { time } = require('lib/time-utils.js');
|
||||
|
||||
const CommandDone = require("./command-done.js");
|
||||
const CommandDone = require('./command-done.js');
|
||||
|
||||
class Command extends BaseCommand {
|
||||
|
||||
usage() {
|
||||
return "undone <note>";
|
||||
return 'undone <note>';
|
||||
}
|
||||
|
||||
description() {
|
||||
return _("Marks a to-do as non-completed.");
|
||||
return _('Marks a to-do as non-completed.');
|
||||
}
|
||||
|
||||
async action(args) {
|
||||
await CommandDone.handleAction(this, args, false);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
module.exports = Command;
|
||||
module.exports = Command;
|
||||
@@ -1,16 +1,17 @@
|
||||
const { BaseCommand } = require("./base-command.js");
|
||||
const { app } = require("./app.js");
|
||||
const { _ } = require("lib/locale.js");
|
||||
const BaseModel = require("lib/BaseModel.js");
|
||||
const Folder = require("lib/models/Folder.js");
|
||||
const { BaseCommand } = require('./base-command.js');
|
||||
const { app } = require('./app.js');
|
||||
const { _ } = require('lib/locale.js');
|
||||
const BaseModel = require('lib/BaseModel.js');
|
||||
const Folder = require('lib/models/Folder.js');
|
||||
|
||||
class Command extends BaseCommand {
|
||||
|
||||
usage() {
|
||||
return "use <notebook>";
|
||||
return 'use <notebook>';
|
||||
}
|
||||
|
||||
description() {
|
||||
return _("Switches to [notebook] - all further operations will happen within this notebook.");
|
||||
return _('Switches to [notebook] - all further operations will happen within this notebook.');
|
||||
}
|
||||
|
||||
autocomplete() {
|
||||
@@ -18,14 +19,15 @@ class Command extends BaseCommand {
|
||||
}
|
||||
|
||||
compatibleUis() {
|
||||
return ["cli"];
|
||||
return ['cli'];
|
||||
}
|
||||
|
||||
async action(args) {
|
||||
let folder = await app().loadItem(BaseModel.TYPE_FOLDER, args["notebook"]);
|
||||
if (!folder) throw new Error(_('Cannot find "%s".', args["notebook"]));
|
||||
let folder = await app().loadItem(BaseModel.TYPE_FOLDER, args['notebook']);
|
||||
if (!folder) throw new Error(_('Cannot find "%s".', args['notebook']));
|
||||
app().switchCurrentFolder(folder);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
module.exports = Command;
|
||||
module.exports = Command;
|
||||
@@ -1,20 +1,22 @@
|
||||
const { BaseCommand } = require("./base-command.js");
|
||||
const Setting = require("lib/models/Setting.js");
|
||||
const { _ } = require("lib/locale.js");
|
||||
const { BaseCommand } = require('./base-command.js');
|
||||
const Setting = require('lib/models/Setting.js');
|
||||
const { _ } = require('lib/locale.js');
|
||||
|
||||
class Command extends BaseCommand {
|
||||
|
||||
usage() {
|
||||
return "version";
|
||||
return 'version';
|
||||
}
|
||||
|
||||
description() {
|
||||
return _("Displays version information");
|
||||
return _('Displays version information');
|
||||
}
|
||||
|
||||
async action(args) {
|
||||
const p = require("./package.json");
|
||||
this.stdout(_("%s %s (%s)", p.name, p.version, Setting.value("env")));
|
||||
const p = require('./package.json');
|
||||
this.stdout(_('%s %s (%s)', p.name, p.version, Setting.value('env')));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
module.exports = Command;
|
||||
module.exports = Command;
|
||||
File diff suppressed because one or more lines are too long
@@ -1,6 +1,7 @@
|
||||
const TextWidget = require("tkwidgets/TextWidget.js");
|
||||
const TextWidget = require('tkwidgets/TextWidget.js');
|
||||
|
||||
class ConsoleWidget extends TextWidget {
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this.lines_ = [];
|
||||
@@ -11,11 +12,11 @@ class ConsoleWidget extends TextWidget {
|
||||
}
|
||||
|
||||
get name() {
|
||||
return "console";
|
||||
return 'console';
|
||||
}
|
||||
|
||||
get lastLine() {
|
||||
return this.lines_.length ? this.lines_[this.lines_.length - 1] : "";
|
||||
return this.lines_.length ? this.lines_[this.lines_.length-1] : '';
|
||||
}
|
||||
|
||||
addLine(line) {
|
||||
@@ -45,6 +46,7 @@ class ConsoleWidget extends TextWidget {
|
||||
|
||||
super.render();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
module.exports = ConsoleWidget;
|
||||
module.exports = ConsoleWidget;
|
||||
@@ -1,10 +1,11 @@
|
||||
const Folder = require("lib/models/Folder.js");
|
||||
const Tag = require("lib/models/Tag.js");
|
||||
const BaseModel = require("lib/BaseModel.js");
|
||||
const ListWidget = require("tkwidgets/ListWidget.js");
|
||||
const _ = require("lib/locale.js")._;
|
||||
const Folder = require('lib/models/Folder.js');
|
||||
const Tag = require('lib/models/Tag.js');
|
||||
const BaseModel = require('lib/BaseModel.js');
|
||||
const ListWidget = require('tkwidgets/ListWidget.js');
|
||||
const _ = require('lib/locale.js')._;
|
||||
|
||||
class FolderListWidget extends ListWidget {
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
@@ -14,26 +15,26 @@ class FolderListWidget extends ListWidget {
|
||||
this.selectedFolderId_ = null;
|
||||
this.selectedTagId_ = null;
|
||||
this.selectedSearchId_ = null;
|
||||
this.notesParentType_ = "Folder";
|
||||
this.notesParentType_ = 'Folder';
|
||||
this.updateIndexFromSelectedFolderId_ = false;
|
||||
this.updateItems_ = false;
|
||||
|
||||
this.itemRenderer = item => {
|
||||
this.itemRenderer = (item) => {
|
||||
let output = [];
|
||||
if (item === "-") {
|
||||
output.push("-".repeat(this.innerWidth));
|
||||
if (item === '-') {
|
||||
output.push('-'.repeat(this.innerWidth));
|
||||
} else if (item.type_ === Folder.modelType()) {
|
||||
output.push(Folder.displayTitle(item));
|
||||
} else if (item.type_ === Tag.modelType()) {
|
||||
output.push("[" + Folder.displayTitle(item) + "]");
|
||||
output.push('[' + Folder.displayTitle(item) + ']');
|
||||
} else if (item.type_ === BaseModel.TYPE_SEARCH) {
|
||||
output.push(_("Search:"));
|
||||
output.push(_('Search:'));
|
||||
output.push(item.title);
|
||||
}
|
||||
}
|
||||
|
||||
// if (item && item.id) output.push(item.id.substr(0, 5));
|
||||
|
||||
return output.join(" ");
|
||||
|
||||
return output.join(' ');
|
||||
};
|
||||
}
|
||||
|
||||
@@ -43,7 +44,7 @@ class FolderListWidget extends ListWidget {
|
||||
|
||||
set selectedFolderId(v) {
|
||||
this.selectedFolderId_ = v;
|
||||
this.updateIndexFromSelectedItemId();
|
||||
this.updateIndexFromSelectedItemId()
|
||||
this.invalidate();
|
||||
}
|
||||
|
||||
@@ -53,7 +54,7 @@ class FolderListWidget extends ListWidget {
|
||||
|
||||
set selectedSearchId(v) {
|
||||
this.selectedSearchId_ = v;
|
||||
this.updateIndexFromSelectedItemId();
|
||||
this.updateIndexFromSelectedItemId()
|
||||
this.invalidate();
|
||||
}
|
||||
|
||||
@@ -63,7 +64,7 @@ class FolderListWidget extends ListWidget {
|
||||
|
||||
set selectedTagId(v) {
|
||||
this.selectedTagId_ = v;
|
||||
this.updateIndexFromSelectedItemId();
|
||||
this.updateIndexFromSelectedItemId()
|
||||
this.invalidate();
|
||||
}
|
||||
|
||||
@@ -74,7 +75,7 @@ class FolderListWidget extends ListWidget {
|
||||
set notesParentType(v) {
|
||||
//if (this.notesParentType_ === v) return;
|
||||
this.notesParentType_ = v;
|
||||
this.updateIndexFromSelectedItemId();
|
||||
this.updateIndexFromSelectedItemId()
|
||||
this.invalidate();
|
||||
}
|
||||
|
||||
@@ -85,7 +86,7 @@ class FolderListWidget extends ListWidget {
|
||||
set searches(v) {
|
||||
this.searches_ = v;
|
||||
this.updateItems_ = true;
|
||||
this.updateIndexFromSelectedItemId();
|
||||
this.updateIndexFromSelectedItemId()
|
||||
this.invalidate();
|
||||
}
|
||||
|
||||
@@ -96,7 +97,7 @@ class FolderListWidget extends ListWidget {
|
||||
set tags(v) {
|
||||
this.tags_ = v;
|
||||
this.updateItems_ = true;
|
||||
this.updateIndexFromSelectedItemId();
|
||||
this.updateIndexFromSelectedItemId()
|
||||
this.invalidate();
|
||||
}
|
||||
|
||||
@@ -107,32 +108,32 @@ class FolderListWidget extends ListWidget {
|
||||
set folders(v) {
|
||||
this.folders_ = v;
|
||||
this.updateItems_ = true;
|
||||
this.updateIndexFromSelectedItemId();
|
||||
this.updateIndexFromSelectedItemId()
|
||||
this.invalidate();
|
||||
}
|
||||
|
||||
|
||||
render() {
|
||||
if (this.updateItems_) {
|
||||
this.logger().debug("Rebuilding items...", this.notesParentType, this.selectedJoplinItemId, this.selectedSearchId);
|
||||
this.logger().debug('Rebuilding items...', this.notesParentType, this.selectedJoplinItemId, this.selectedSearchId);
|
||||
const wasSelectedItemId = this.selectedJoplinItemId;
|
||||
const previousParentType = this.notesParentType;
|
||||
|
||||
let newItems = this.folders.slice();
|
||||
|
||||
if (this.tags.length) {
|
||||
if (newItems.length) newItems.push("-");
|
||||
if (newItems.length) newItems.push('-');
|
||||
newItems = newItems.concat(this.tags);
|
||||
}
|
||||
|
||||
if (this.searches.length) {
|
||||
if (newItems.length) newItems.push("-");
|
||||
if (newItems.length) newItems.push('-');
|
||||
newItems = newItems.concat(this.searches);
|
||||
}
|
||||
|
||||
this.items = newItems;
|
||||
|
||||
this.notesParentType = previousParentType;
|
||||
this.updateIndexFromSelectedItemId(wasSelectedItemId);
|
||||
this.updateIndexFromSelectedItemId(wasSelectedItemId)
|
||||
this.updateItems_ = false;
|
||||
}
|
||||
|
||||
@@ -140,24 +141,25 @@ class FolderListWidget extends ListWidget {
|
||||
}
|
||||
|
||||
get selectedJoplinItemId() {
|
||||
if (!this.notesParentType) return "";
|
||||
if (this.notesParentType === "Folder") return this.selectedFolderId;
|
||||
if (this.notesParentType === "Tag") return this.selectedTagId;
|
||||
if (this.notesParentType === "Search") return this.selectedSearchId;
|
||||
throw new Error("Unknown parent type: " + this.notesParentType);
|
||||
if (!this.notesParentType) return '';
|
||||
if (this.notesParentType === 'Folder') return this.selectedFolderId;
|
||||
if (this.notesParentType === 'Tag') return this.selectedTagId;
|
||||
if (this.notesParentType === 'Search') return this.selectedSearchId;
|
||||
throw new Error('Unknown parent type: ' + this.notesParentType);
|
||||
}
|
||||
|
||||
get selectedJoplinItem() {
|
||||
const id = this.selectedJoplinItemId;
|
||||
const index = this.itemIndexByKey("id", id);
|
||||
const index = this.itemIndexByKey('id', id);
|
||||
return this.itemAt(index);
|
||||
}
|
||||
|
||||
updateIndexFromSelectedItemId(itemId = null) {
|
||||
if (itemId === null) itemId = this.selectedJoplinItemId;
|
||||
const index = this.itemIndexByKey("id", itemId);
|
||||
const index = this.itemIndexByKey('id', itemId);
|
||||
this.currentIndex = index >= 0 ? index : 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
module.exports = FolderListWidget;
|
||||
module.exports = FolderListWidget;
|
||||
@@ -1,17 +1,18 @@
|
||||
const Note = require("lib/models/Note.js");
|
||||
const ListWidget = require("tkwidgets/ListWidget.js");
|
||||
const Note = require('lib/models/Note.js');
|
||||
const ListWidget = require('tkwidgets/ListWidget.js');
|
||||
|
||||
class NoteListWidget extends ListWidget {
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this.selectedNoteId_ = 0;
|
||||
|
||||
this.updateIndexFromSelectedNoteId_ = false;
|
||||
|
||||
this.itemRenderer = note => {
|
||||
this.itemRenderer = (note) => {
|
||||
let label = Note.displayTitle(note); // + ' ' + note.id;
|
||||
if (note.is_todo) {
|
||||
label = "[" + (note.todo_completed ? "X" : " ") + "] " + label;
|
||||
label = '[' + (note.todo_completed ? 'X' : ' ') + '] ' + label;
|
||||
}
|
||||
return label;
|
||||
};
|
||||
@@ -24,13 +25,14 @@ class NoteListWidget extends ListWidget {
|
||||
|
||||
render() {
|
||||
if (this.updateIndexFromSelectedNoteId_) {
|
||||
const index = this.itemIndexByKey("id", this.selectedNoteId_);
|
||||
const index = this.itemIndexByKey('id', this.selectedNoteId_);
|
||||
this.currentIndex = index >= 0 ? index : 0;
|
||||
this.updateIndexFromSelectedNoteId_ = false;
|
||||
}
|
||||
|
||||
super.render();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
module.exports = NoteListWidget;
|
||||
module.exports = NoteListWidget;
|
||||
@@ -1,7 +1,8 @@
|
||||
const Note = require("lib/models/Note.js");
|
||||
const TextWidget = require("tkwidgets/TextWidget.js");
|
||||
const Note = require('lib/models/Note.js');
|
||||
const TextWidget = require('tkwidgets/TextWidget.js');
|
||||
|
||||
class NoteMetadataWidget extends TextWidget {
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this.noteId_ = 0;
|
||||
@@ -26,9 +27,10 @@ class NoteMetadataWidget extends TextWidget {
|
||||
|
||||
if (!this.note_ && this.noteId_) {
|
||||
this.note_ = await Note.load(this.noteId_);
|
||||
this.text = this.note_ ? await Note.minimalSerializeForDisplay(this.note_) : "";
|
||||
this.text = this.note_ ? await Note.minimalSerializeForDisplay(this.note_) : '';
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
module.exports = NoteMetadataWidget;
|
||||
module.exports = NoteMetadataWidget;
|
||||
@@ -1,8 +1,9 @@
|
||||
const Note = require("lib/models/Note.js");
|
||||
const TextWidget = require("tkwidgets/TextWidget.js");
|
||||
const { _ } = require("lib/locale.js");
|
||||
const Note = require('lib/models/Note.js');
|
||||
const TextWidget = require('tkwidgets/TextWidget.js');
|
||||
const { _ } = require('lib/locale.js');
|
||||
|
||||
class NoteWidget extends TextWidget {
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this.noteId_ = 0;
|
||||
@@ -33,9 +34,7 @@ class NoteWidget extends TextWidget {
|
||||
}
|
||||
|
||||
welcomeText() {
|
||||
return _(
|
||||
"Welcome to Joplin!\n\nType `:help shortcuts` for the list of keyboard shortcuts, or just `:help` for usage information.\n\nFor example, to create a notebook press `mb`; to create a note press `mn`."
|
||||
);
|
||||
return _('Welcome to Joplin!\n\nType `:help shortcuts` for the list of keyboard shortcuts, or just `:help` for usage information.\n\nFor example, to create a notebook press `mb`; to create a note press `mn`.');
|
||||
}
|
||||
|
||||
reloadNote() {
|
||||
@@ -43,25 +42,24 @@ class NoteWidget extends TextWidget {
|
||||
this.text = this.welcomeText();
|
||||
this.scrollTop = 0;
|
||||
} else if (this.noteId_) {
|
||||
this.doAsync("loadNote", async () => {
|
||||
this.doAsync('loadNote', async () => {
|
||||
this.note_ = await Note.load(this.noteId_);
|
||||
|
||||
|
||||
if (this.note_ && this.note_.encryption_applied) {
|
||||
this.text = _(
|
||||
"One or more items are currently encrypted and you may need to supply a master password. To do so please type `e2ee decrypt`. If you have already supplied the password, the encrypted items are being decrypted in the background and will be available soon."
|
||||
);
|
||||
this.text = _('One or more items are currently encrypted and you may need to supply a master password. To do so please type `e2ee decrypt`. If you have already supplied the password, the encrypted items are being decrypted in the background and will be available soon.');
|
||||
} else {
|
||||
this.text = this.note_ ? this.note_.title + "\n\n" + this.note_.body : "";
|
||||
this.text = this.note_ ? this.note_.title + "\n\n" + this.note_.body : '';
|
||||
}
|
||||
|
||||
if (this.lastLoadedNoteId_ !== this.noteId_) this.scrollTop = 0;
|
||||
this.lastLoadedNoteId_ = this.noteId_;
|
||||
});
|
||||
} else {
|
||||
this.text = "";
|
||||
this.text = '';
|
||||
this.scrollTop = 0;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
module.exports = NoteWidget;
|
||||
module.exports = NoteWidget;
|
||||
@@ -1,10 +1,11 @@
|
||||
const BaseWidget = require("tkwidgets/BaseWidget.js");
|
||||
const chalk = require("chalk");
|
||||
const termutils = require("tkwidgets/framework/termutils.js");
|
||||
const stripAnsi = require("strip-ansi");
|
||||
const { handleAutocompletion } = require("../autocompletion.js");
|
||||
const BaseWidget = require('tkwidgets/BaseWidget.js');
|
||||
const chalk = require('chalk');
|
||||
const termutils = require('tkwidgets/framework/termutils.js');
|
||||
const stripAnsi = require('strip-ansi');
|
||||
const { handleAutocompletion } = require('../autocompletion.js');
|
||||
|
||||
class StatusBarWidget extends BaseWidget {
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
@@ -15,7 +16,7 @@ class StatusBarWidget extends BaseWidget {
|
||||
}
|
||||
|
||||
get name() {
|
||||
return "statusBar";
|
||||
return 'statusBar';
|
||||
}
|
||||
|
||||
get canHaveFocus() {
|
||||
@@ -27,9 +28,9 @@ class StatusBarWidget extends BaseWidget {
|
||||
this.invalidate();
|
||||
}
|
||||
|
||||
async prompt(initialText = "", promptString = null, options = null) {
|
||||
if (this.promptState_) throw new Error("Another prompt already active");
|
||||
if (promptString === null) promptString = ":";
|
||||
async prompt(initialText = '', promptString = null, options = null) {
|
||||
if (this.promptState_) throw new Error('Another prompt already active');
|
||||
if (promptString === null) promptString = ':';
|
||||
if (options === null) options = {};
|
||||
|
||||
this.root.globalDisableKeyboard(this);
|
||||
@@ -40,8 +41,8 @@ class StatusBarWidget extends BaseWidget {
|
||||
promptString: stripAnsi(promptString),
|
||||
};
|
||||
|
||||
if ("cursorPosition" in options) this.promptState_.cursorPosition = options.cursorPosition;
|
||||
if ("secure" in options) this.promptState_.secure = options.secure;
|
||||
if ('cursorPosition' in options) this.promptState_.cursorPosition = options.cursorPosition;
|
||||
if ('secure' in options) this.promptState_.secure = options.secure;
|
||||
|
||||
this.promptState_.promise = new Promise((resolve, reject) => {
|
||||
this.promptState_.resolve = resolve;
|
||||
@@ -74,7 +75,7 @@ class StatusBarWidget extends BaseWidget {
|
||||
super.render();
|
||||
|
||||
const doSaveCursor = !this.promptActive;
|
||||
|
||||
|
||||
if (doSaveCursor) this.term.saveCursor();
|
||||
|
||||
this.innerClear();
|
||||
@@ -86,13 +87,14 @@ class StatusBarWidget extends BaseWidget {
|
||||
|
||||
//const textStyle = this.promptActive ? (s) => s : chalk.bgBlueBright.white;
|
||||
//const textStyle = (s) => s;
|
||||
const textStyle = this.promptActive ? s => s : chalk.gray;
|
||||
const textStyle = this.promptActive ? (s) => s : chalk.gray;
|
||||
|
||||
this.term.drawHLine(this.absoluteInnerX, this.absoluteInnerY, this.innerWidth, textStyle(" "));
|
||||
this.term.drawHLine(this.absoluteInnerX, this.absoluteInnerY, this.innerWidth, textStyle(' '));
|
||||
|
||||
this.term.moveTo(this.absoluteInnerX, this.absoluteInnerY);
|
||||
|
||||
if (this.promptActive) {
|
||||
|
||||
this.term.write(textStyle(this.promptState_.promptString));
|
||||
|
||||
if (this.inputEventEmitter_) {
|
||||
@@ -111,11 +113,11 @@ class StatusBarWidget extends BaseWidget {
|
||||
history: this.history,
|
||||
default: this.promptState_.initialText,
|
||||
autoComplete: handleAutocompletion,
|
||||
autoCompleteHint: true,
|
||||
autoCompleteMenu: true,
|
||||
autoCompleteHint : true,
|
||||
autoCompleteMenu : true,
|
||||
};
|
||||
|
||||
if ("cursorPosition" in this.promptState_) options.cursorPosition = this.promptState_.cursorPosition;
|
||||
if ('cursorPosition' in this.promptState_) options.cursorPosition = this.promptState_.cursorPosition;
|
||||
if (isSecurePrompt) options.echoChar = true;
|
||||
|
||||
this.inputEventEmitter_ = this.term.inputField(options, (error, input) => {
|
||||
@@ -123,7 +125,7 @@ class StatusBarWidget extends BaseWidget {
|
||||
const resolveFn = this.promptState_.resolve;
|
||||
|
||||
if (error) {
|
||||
this.logger().error("StatusBar: inputField error:", error);
|
||||
this.logger().error('StatusBar: inputField error:', error);
|
||||
} else {
|
||||
if (input === undefined) {
|
||||
// User cancel
|
||||
@@ -131,7 +133,7 @@ class StatusBarWidget extends BaseWidget {
|
||||
resolveResult = input ? input.trim() : input;
|
||||
// Add the command to history but only if it's longer than one character.
|
||||
// Below that it's usually an answer like "y"/"n", etc.
|
||||
const isConfigPassword = input.indexOf("config ") >= 0 && input.indexOf("password") >= 0;
|
||||
const isConfigPassword = input.indexOf('config ') >= 0 && input.indexOf('password') >= 0;
|
||||
if (!isSecurePrompt && input && input.length > 1 && !isConfigPassword) this.history_.push(input);
|
||||
}
|
||||
}
|
||||
@@ -151,15 +153,19 @@ class StatusBarWidget extends BaseWidget {
|
||||
// Only callback once everything has been cleaned up and reset
|
||||
resolveFn(resolveResult);
|
||||
});
|
||||
|
||||
} else {
|
||||
|
||||
for (let i = 0; i < this.items_.length; i++) {
|
||||
const s = this.items_[i].substr(0, this.innerWidth - 1);
|
||||
this.term.write(textStyle(s));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (doSaveCursor) this.term.restoreCursor();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
module.exports = StatusBarWidget;
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
const fs = require("fs-extra");
|
||||
const { wrap } = require("lib/string-utils.js");
|
||||
const Setting = require("lib/models/Setting.js");
|
||||
const { fileExtension, basename, dirname } = require("lib/path-utils.js");
|
||||
const { _, setLocale, languageCode } = require("lib/locale.js");
|
||||
const fs = require('fs-extra');
|
||||
const { wrap } = require('lib/string-utils.js');
|
||||
const Setting = require('lib/models/Setting.js');
|
||||
const { fileExtension, basename, dirname } = require('lib/path-utils.js');
|
||||
const { _, setLocale, languageCode } = require('lib/locale.js');
|
||||
|
||||
const rootDir = dirname(dirname(__dirname));
|
||||
const MAX_WIDTH = 78;
|
||||
const INDENT = " ";
|
||||
const INDENT = ' ';
|
||||
|
||||
function renderTwoColumnData(options, baseIndent, width) {
|
||||
let output = [];
|
||||
@@ -15,8 +15,8 @@ function renderTwoColumnData(options, baseIndent, width) {
|
||||
for (let i = 0; i < options.length; i++) {
|
||||
let option = options[i];
|
||||
const flag = option[0];
|
||||
const indent = baseIndent + INDENT + " ".repeat(optionColWidth + 2);
|
||||
|
||||
const indent = baseIndent + INDENT + ' '.repeat(optionColWidth + 2);
|
||||
|
||||
let r = wrap(option[1], indent, width);
|
||||
r = r.substr(flag.length + (baseIndent + INDENT).length);
|
||||
r = baseIndent + INDENT + flag + r;
|
||||
@@ -29,61 +29,61 @@ function renderTwoColumnData(options, baseIndent, width) {
|
||||
function renderCommandHelp(cmd, width = null) {
|
||||
if (width === null) width = MAX_WIDTH;
|
||||
|
||||
const baseIndent = "";
|
||||
const baseIndent = '';
|
||||
|
||||
let output = [];
|
||||
output.push(baseIndent + cmd.usage());
|
||||
output.push("");
|
||||
output.push('');
|
||||
output.push(wrap(cmd.description(), baseIndent + INDENT, width));
|
||||
|
||||
const optionString = renderTwoColumnData(cmd.options(), baseIndent, width);
|
||||
|
||||
if (optionString) {
|
||||
output.push("");
|
||||
output.push('');
|
||||
output.push(optionString);
|
||||
}
|
||||
|
||||
if (cmd.name() === "config") {
|
||||
const renderMetadata = md => {
|
||||
if (cmd.name() === 'config') {
|
||||
const renderMetadata = (md) => {
|
||||
let desc = [];
|
||||
|
||||
if (md.label) {
|
||||
let label = md.label();
|
||||
if (label.length && label[label.length - 1] !== ".") label += ".";
|
||||
if (label.length && label[label.length - 1] !== '.') label += '.';
|
||||
desc.push(label);
|
||||
}
|
||||
|
||||
const description = Setting.keyDescription(md.key, "cli");
|
||||
const description = Setting.keyDescription(md.key, 'cli');
|
||||
if (description) desc.push(description);
|
||||
|
||||
desc.push(_("Type: %s.", md.isEnum ? _("Enum") : Setting.typeToString(md.type)));
|
||||
if (md.isEnum) desc.push(_("Possible values: %s.", Setting.enumOptionsDoc(md.key, "%s (%s)")));
|
||||
desc.push(_('Type: %s.', md.isEnum ? _('Enum') : Setting.typeToString(md.type)));
|
||||
if (md.isEnum) desc.push(_('Possible values: %s.', Setting.enumOptionsDoc(md.key, '%s (%s)')));
|
||||
|
||||
let defaultString = null;
|
||||
|
||||
if ("value" in md) {
|
||||
if ('value' in md) {
|
||||
if (md.type === Setting.TYPE_STRING) {
|
||||
defaultString = md.value ? '"' + md.value + '"' : null;
|
||||
} else if (md.type === Setting.TYPE_INT) {
|
||||
defaultString = (md.value ? md.value : 0).toString();
|
||||
} else if (md.type === Setting.TYPE_BOOL) {
|
||||
defaultString = md.value === true ? "true" : "false";
|
||||
defaultString = (md.value === true ? 'true' : 'false');
|
||||
}
|
||||
}
|
||||
|
||||
if (defaultString !== null) desc.push(_("Default: %s", defaultString));
|
||||
|
||||
if (defaultString !== null) desc.push(_('Default: %s', defaultString));
|
||||
|
||||
return [md.key, desc.join("\n")];
|
||||
};
|
||||
|
||||
output.push("");
|
||||
output.push(_("Possible keys/values:"));
|
||||
output.push("");
|
||||
output.push('');
|
||||
output.push(_('Possible keys/values:'));
|
||||
output.push('');
|
||||
|
||||
let keysValues = [];
|
||||
const keys = Setting.keys(true, "cli");
|
||||
const keys = Setting.keys(true, 'cli');
|
||||
for (let i = 0; i < keys.length; i++) {
|
||||
if (keysValues.length) keysValues.push(["", ""]);
|
||||
if (keysValues.length) keysValues.push(['','']);
|
||||
const md = Setting.settingMetadata(keys[i]);
|
||||
if (!md.label) continue;
|
||||
keysValues.push(renderMetadata(md));
|
||||
@@ -91,7 +91,7 @@ function renderCommandHelp(cmd, width = null) {
|
||||
|
||||
output.push(renderTwoColumnData(keysValues, baseIndent, width));
|
||||
}
|
||||
|
||||
|
||||
return output.join("\n");
|
||||
}
|
||||
|
||||
@@ -104,4 +104,4 @@ function getOptionColWidth(options) {
|
||||
return output;
|
||||
}
|
||||
|
||||
module.exports = { renderCommandHelp };
|
||||
module.exports = { renderCommandHelp };
|
||||
@@ -1,30 +1,30 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
// Make it possible to require("/lib/...") without specifying full path
|
||||
require("app-module-path").addPath(__dirname);
|
||||
require('app-module-path').addPath(__dirname);
|
||||
|
||||
const compareVersion = require("compare-version");
|
||||
const nodeVersion = process && process.versions && process.versions.node ? process.versions.node : "0.0.0";
|
||||
if (compareVersion(nodeVersion, "8.0.0") < 0) {
|
||||
console.error("Joplin requires Node 8+. Detected version " + nodeVersion);
|
||||
const compareVersion = require('compare-version');
|
||||
const nodeVersion = process && process.versions && process.versions.node ? process.versions.node : '0.0.0';
|
||||
if (compareVersion(nodeVersion, '8.0.0') < 0) {
|
||||
console.error('Joplin requires Node 8+. Detected version ' + nodeVersion);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const { app } = require("./app.js");
|
||||
const Folder = require("lib/models/Folder.js");
|
||||
const Resource = require("lib/models/Resource.js");
|
||||
const BaseItem = require("lib/models/BaseItem.js");
|
||||
const Note = require("lib/models/Note.js");
|
||||
const Tag = require("lib/models/Tag.js");
|
||||
const NoteTag = require("lib/models/NoteTag.js");
|
||||
const MasterKey = require("lib/models/MasterKey");
|
||||
const Setting = require("lib/models/Setting.js");
|
||||
const { Logger } = require("lib/logger.js");
|
||||
const { FsDriverNode } = require("lib/fs-driver-node.js");
|
||||
const { shimInit } = require("lib/shim-init-node.js");
|
||||
const { _ } = require("lib/locale.js");
|
||||
const { FileApiDriverLocal } = require("lib/file-api-driver-local.js");
|
||||
const EncryptionService = require("lib/services/EncryptionService");
|
||||
const { app } = require('./app.js');
|
||||
const Folder = require('lib/models/Folder.js');
|
||||
const Resource = require('lib/models/Resource.js');
|
||||
const BaseItem = require('lib/models/BaseItem.js');
|
||||
const Note = require('lib/models/Note.js');
|
||||
const Tag = require('lib/models/Tag.js');
|
||||
const NoteTag = require('lib/models/NoteTag.js');
|
||||
const MasterKey = require('lib/models/MasterKey');
|
||||
const Setting = require('lib/models/Setting.js');
|
||||
const { Logger } = require('lib/logger.js');
|
||||
const { FsDriverNode } = require('lib/fs-driver-node.js');
|
||||
const { shimInit } = require('lib/shim-init-node.js');
|
||||
const { _ } = require('lib/locale.js');
|
||||
const { FileApiDriverLocal } = require('lib/file-api-driver-local.js');
|
||||
const EncryptionService = require('lib/services/EncryptionService');
|
||||
|
||||
const fsDriver = new FsDriverNode();
|
||||
Logger.fsDriver_ = fsDriver;
|
||||
@@ -34,15 +34,15 @@ FileApiDriverLocal.fsDriver_ = fsDriver;
|
||||
|
||||
// That's not good, but it's to avoid circular dependency issues
|
||||
// in the BaseItem class.
|
||||
BaseItem.loadClass("Note", Note);
|
||||
BaseItem.loadClass("Folder", Folder);
|
||||
BaseItem.loadClass("Resource", Resource);
|
||||
BaseItem.loadClass("Tag", Tag);
|
||||
BaseItem.loadClass("NoteTag", NoteTag);
|
||||
BaseItem.loadClass("MasterKey", MasterKey);
|
||||
BaseItem.loadClass('Note', Note);
|
||||
BaseItem.loadClass('Folder', Folder);
|
||||
BaseItem.loadClass('Resource', Resource);
|
||||
BaseItem.loadClass('Tag', Tag);
|
||||
BaseItem.loadClass('NoteTag', NoteTag);
|
||||
BaseItem.loadClass('MasterKey', MasterKey);
|
||||
|
||||
Setting.setConstant("appId", "net.cozic.joplin-cli");
|
||||
Setting.setConstant("appType", "cli");
|
||||
Setting.setConstant('appId', 'net.cozic.joplin-cli');
|
||||
Setting.setConstant('appType', 'cli');
|
||||
|
||||
shimInit();
|
||||
|
||||
@@ -51,21 +51,25 @@ const application = app();
|
||||
if (process.platform === "win32") {
|
||||
var rl = require("readline").createInterface({
|
||||
input: process.stdin,
|
||||
output: process.stdout,
|
||||
output: process.stdout
|
||||
});
|
||||
|
||||
rl.on("SIGINT", function() {
|
||||
rl.on("SIGINT", function () {
|
||||
process.emit("SIGINT");
|
||||
});
|
||||
}
|
||||
|
||||
process.stdout.on("error", function(err) {
|
||||
process.stdout.on('error', function( err ) {
|
||||
// https://stackoverflow.com/questions/12329816/error-write-epipe-when-piping-node-output-to-head#15884508
|
||||
if (err.code == "EPIPE") {
|
||||
process.exit(0);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// async function main() {
|
||||
// const InteropService = require('lib/services/InteropService');
|
||||
// const service = new InteropService();
|
||||
@@ -75,14 +79,21 @@ process.stdout.on("error", function(err) {
|
||||
|
||||
// main().catch((error) => { console.error(error); });
|
||||
|
||||
application.start(process.argv).catch(error => {
|
||||
if (error.code == "flagError") {
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
application.start(process.argv).catch((error) => {
|
||||
if (error.code == 'flagError') {
|
||||
console.error(error.message);
|
||||
console.error(_("Type `joplin help` for usage information."));
|
||||
console.error(_('Type `joplin help` for usage information.'));
|
||||
} else {
|
||||
console.error(_("Fatal error:"));
|
||||
console.error(_('Fatal error:'));
|
||||
console.error(error);
|
||||
}
|
||||
|
||||
process.exit(1);
|
||||
});
|
||||
});
|
||||
@@ -1,12 +1,13 @@
|
||||
const { _ } = require("lib/locale.js");
|
||||
const { netUtils } = require("lib/net-utils.js");
|
||||
const { _ } = require('lib/locale.js');
|
||||
const { netUtils } = require('lib/net-utils.js');
|
||||
|
||||
const http = require("http");
|
||||
const urlParser = require("url");
|
||||
const FormData = require("form-data");
|
||||
const enableServerDestroy = require("server-destroy");
|
||||
const FormData = require('form-data');
|
||||
const enableServerDestroy = require('server-destroy');
|
||||
|
||||
class OneDriveApiNodeUtils {
|
||||
|
||||
constructor(api) {
|
||||
this.api_ = api;
|
||||
this.oauthServer_ = null;
|
||||
@@ -43,19 +44,19 @@ class OneDriveApiNodeUtils {
|
||||
this.api().setAuth(null);
|
||||
|
||||
const port = await netUtils.findAvailablePort(this.possibleOAuthDancePorts(), 0);
|
||||
if (!port) throw new Error(_("All potential ports are in use - please report the issue at %s", "https://github.com/laurent22/joplin"));
|
||||
if (!port) throw new Error(_('All potential ports are in use - please report the issue at %s', 'https://github.com/laurent22/joplin'));
|
||||
|
||||
let authCodeUrl = this.api().authCodeUrl("http://localhost:" + port);
|
||||
let authCodeUrl = this.api().authCodeUrl('http://localhost:' + port);
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
this.oauthServer_ = http.createServer();
|
||||
let errorMessage = null;
|
||||
|
||||
this.oauthServer_.on("request", (request, response) => {
|
||||
this.oauthServer_.on('request', (request, response) => {
|
||||
const url = urlParser.parse(request.url, true);
|
||||
|
||||
if (url.pathname === "/auth") {
|
||||
response.writeHead(302, { Location: authCodeUrl });
|
||||
if (url.pathname === '/auth') {
|
||||
response.writeHead(302, { 'Location': authCodeUrl });
|
||||
response.end();
|
||||
return;
|
||||
}
|
||||
@@ -63,10 +64,10 @@ class OneDriveApiNodeUtils {
|
||||
const query = url.query;
|
||||
|
||||
const writeResponse = (code, message) => {
|
||||
response.writeHead(code, { "Content-Type": "text/html" });
|
||||
response.writeHead(code, {"Content-Type": "text/html"});
|
||||
response.write(this.makePage(message));
|
||||
response.end();
|
||||
};
|
||||
}
|
||||
|
||||
// After the response has been received, don't destroy the server right
|
||||
// away or the browser might display a connection reset error (even
|
||||
@@ -76,27 +77,24 @@ class OneDriveApiNodeUtils {
|
||||
this.oauthServer_.destroy();
|
||||
this.oauthServer_ = null;
|
||||
}, 1000);
|
||||
};
|
||||
}
|
||||
|
||||
if (!query.code) return writeResponse(400, '"code" query parameter is missing');
|
||||
|
||||
this.api()
|
||||
.execTokenRequest(query.code, "http://localhost:" + port.toString())
|
||||
.then(() => {
|
||||
writeResponse(200, _("The application has been authorised - you may now close this browser tab."));
|
||||
targetConsole.log("");
|
||||
targetConsole.log(_("The application has been successfully authorised."));
|
||||
waitAndDestroy();
|
||||
})
|
||||
.catch(error => {
|
||||
writeResponse(400, error.message);
|
||||
targetConsole.log("");
|
||||
targetConsole.log(error.message);
|
||||
waitAndDestroy();
|
||||
});
|
||||
this.api().execTokenRequest(query.code, 'http://localhost:' + port.toString()).then(() => {
|
||||
writeResponse(200, _('The application has been authorised - you may now close this browser tab.'));
|
||||
targetConsole.log('');
|
||||
targetConsole.log(_('The application has been successfully authorised.'));
|
||||
waitAndDestroy();
|
||||
}).catch((error) => {
|
||||
writeResponse(400, error.message);
|
||||
targetConsole.log('');
|
||||
targetConsole.log(error.message);
|
||||
waitAndDestroy();
|
||||
});
|
||||
});
|
||||
|
||||
this.oauthServer_.on("close", () => {
|
||||
this.oauthServer_.on('close', () => {
|
||||
if (errorMessage) {
|
||||
reject(new Error(errorMessage));
|
||||
} else {
|
||||
@@ -113,15 +111,12 @@ class OneDriveApiNodeUtils {
|
||||
// doesn't get cut in terminals (especially those that don't handle multi
|
||||
// lines URLs).
|
||||
|
||||
targetConsole.log(
|
||||
_(
|
||||
'Please open the following URL in your browser to authenticate the application. The application will create a directory in "Apps/Joplin" and will only read and write files in this directory. It will have no access to any files outside this directory nor to any other personal data. No data will be shared with any third party.'
|
||||
)
|
||||
);
|
||||
targetConsole.log("");
|
||||
targetConsole.log("http://127.0.0.1:" + port + "/auth");
|
||||
targetConsole.log(_('Please open the following URL in your browser to authenticate the application. The application will create a directory in "Apps/Joplin" and will only read and write files in this directory. It will have no access to any files outside this directory nor to any other personal data. No data will be shared with any third party.'));
|
||||
targetConsole.log('');
|
||||
targetConsole.log('http://127.0.0.1:' + port + '/auth');
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
module.exports = { OneDriveApiNodeUtils };
|
||||
module.exports = { OneDriveApiNodeUtils };
|
||||
Reference in New Issue
Block a user