Compare commits

...

15 Commits

22 changed files with 110 additions and 66 deletions

16
package-lock.json generated
View File

@@ -1,6 +1,6 @@
{
"name": "trilium",
"version": "0.27.0-beta",
"version": "0.27.1-beta",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
@@ -399,9 +399,9 @@
"integrity": "sha512-ONhaKPIufzzrlNbqtWFFd+jlnemX6lJAgq9ZeiZtS7I1PIf/la7CW4m83rTXRnVnsMbW2k56pGYu7AUFJD9Pow=="
},
"@types/node": {
"version": "8.10.39",
"resolved": "https://registry.npmjs.org/@types/node/-/node-8.10.39.tgz",
"integrity": "sha512-rE7fktr02J8ybFf6eysife+WF+L4sAHWzw09DgdCebEu+qDwMvv4zl6Bc+825ttGZP73kCKxa3dhJOoGJ8+5mA==",
"version": "10.12.18",
"resolved": "https://registry.npmjs.org/@types/node/-/node-10.12.18.tgz",
"integrity": "sha512-fh+pAqt4xRzPfqA6eh3Z2y6fyZavRIumvjhaCL753+TVkGKGhpPeyrJG2JftD0T9q4GF00KjefsQ+PQNDdWQaQ==",
"dev": true
},
"abab": {
@@ -2375,12 +2375,12 @@
"integrity": "sha512-0xy4A/twfrRCnkhfk8ErDi5DqdAsAqeGxht4xkCUrsvhhbQNs7E+4jV0CN7+NKIY0aHE72+XvqtBIXzD31ZbXQ=="
},
"electron": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/electron/-/electron-4.0.0.tgz",
"integrity": "sha512-3XPG/3IXlvnT1oe1K6zEushoD0SKbP8xwdrL10EWGe6k2iOV4hSHqJ8vWnR8yZ7VbSXmBRfomEFDNAo/q/cwKw==",
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/electron/-/electron-4.0.1.tgz",
"integrity": "sha512-kBWDLn1Vq8Tm6+/HpQc8gkjX7wJyQI8v/lf2kAirfi0Q4cXh6vBjozFvV1U/9gGCbyKnIDM+m8/wpyJIjg4w7g==",
"dev": true,
"requires": {
"@types/node": "^8.0.24",
"@types/node": "^10.12.18",
"electron-download": "^4.1.0",
"extract-zip": "^1.0.3"
}

View File

@@ -2,7 +2,7 @@
"name": "trilium",
"productName": "Trilium Notes",
"description": "Trilium Notes",
"version": "0.27.1-beta",
"version": "0.27.2-beta",
"license": "AGPL-3.0-only",
"main": "electron.js",
"bin": {
@@ -66,7 +66,7 @@
},
"devDependencies": {
"devtron": "1.4.0",
"electron": "4.0.0",
"electron": "4.0.1",
"electron-compile": "6.4.3",
"electron-packager": "13.0.1",
"electron-rebuild": "1.8.2",

View File

@@ -15,7 +15,8 @@ const ENTITY_NAME_TO_ENTITY = {
"note_revisions": NoteRevision,
"recent_notes": RecentNote,
"options": Option,
"api_tokens": ApiToken
"api_tokens": ApiToken,
"links": Link
};
function getEntityFromEntityName(entityName) {

View File

@@ -198,15 +198,17 @@ addTabHandler((async function () {
const $syncVersion = $("#sync-version");
const $buildDate = $("#build-date");
const $buildRevision = $("#build-revision");
const $dataDirectory = $("#data-directory");
const appInfo = await server.get('app-info');
$appVersion.html(appInfo.appVersion);
$dbVersion.html(appInfo.dbVersion);
$syncVersion.html(appInfo.syncVersion);
$buildDate.html(appInfo.buildDate);
$buildRevision.html(appInfo.buildRevision);
$appVersion.text(appInfo.appVersion);
$dbVersion.text(appInfo.dbVersion);
$syncVersion.text(appInfo.syncVersion);
$buildDate.text(appInfo.buildDate);
$buildRevision.text(appInfo.buildRevision);
$buildRevision.attr('href', 'https://github.com/zadam/trilium/commit/' + appInfo.buildRevision);
$dataDirectory.text(appInfo.dataDirectory);
return {};
})());

View File

@@ -426,13 +426,17 @@ function initFancyTree(tree) {
clones: {
highlightActiveClones: true
},
renderNode: async function (event, data) {
enhanceTitle: async function (event, data) {
const node = data.node;
const $span = $(node.span);
if (node.data.noteId !== 'root'
&& node.data.noteId === await hoistedNoteService.getHoistedNoteId()
&& $span.find('.unhoist-button').length === 0) {
if (node.data.noteId !== 'root' && node.data.noteId === await hoistedNoteService.getHoistedNoteId()) {
const unhoistButton = $('<span>&nbsp; (<a class="unhoist-button">unhoist</a>)</span>');
$(node.span).append(unhoistButton);
$span.append(unhoistButton);
}
}
});
@@ -649,11 +653,15 @@ messagingService.subscribeToSyncMessages(syncData => {
}
});
utils.bindShortcut('ctrl+o', () => {
utils.bindShortcut('ctrl+o', async () => {
const node = getCurrentNode();
const parentNoteId = node.data.parentNoteId;
const isProtected = treeUtils.getParentProtectedStatus(node);
if (node.data.noteId === 'root' || node.data.noteId === await hoistedNoteService.getHoistedNoteId()) {
return;
}
createNote(node, parentNoteId, 'after', isProtected, true);
});

View File

@@ -13,8 +13,6 @@ import syncService from "./sync.js";
import hoistedNoteService from './hoisted_note.js';
import ContextMenuItemsContainer from './context_menu_items_container.js';
const $tree = $("#tree");
let clipboardIds = [];
let clipboardMode = null;
@@ -110,11 +108,12 @@ async function getContextMenuItems(event) {
const note = await treeCache.getNote(node.data.noteId);
const parentNote = await treeCache.getNote(branch.parentNoteId);
const isNotRoot = note.noteId !== 'root';
const isHoisted = note.noteId === await hoistedNoteService.getHoistedNoteId();
const itemsContainer = new ContextMenuItemsContainer(contextMenuItems);
// Modify menu entries depending on node status
itemsContainer.enableItem("insertNoteAfter", isNotRoot && parentNote.type !== 'search');
itemsContainer.enableItem("insertNoteAfter", isNotRoot && !isHoisted && parentNote.type !== 'search');
itemsContainer.enableItem("insertChildNote", note.type !== 'search');
itemsContainer.enableItem("delete", isNotRoot && parentNote.type !== 'search');
itemsContainer.enableItem("copy", isNotRoot);
@@ -125,10 +124,8 @@ async function getContextMenuItems(event) {
itemsContainer.enableItem("export", note.type !== 'search');
itemsContainer.enableItem("editBranchPrefix", isNotRoot && parentNote.type !== 'search');
const hoistedNoteId = await hoistedNoteService.getHoistedNoteId();
itemsContainer.hideItem("hoist", note.noteId === hoistedNoteId);
itemsContainer.hideItem("unhoist", note.noteId !== hoistedNoteId || !isNotRoot);
itemsContainer.hideItem("hoist", isHoisted);
itemsContainer.hideItem("unhoist", !isHoisted || !isNotRoot);
// Activate node on right-click
node.setActive();

View File

@@ -25,26 +25,20 @@ function SetupModel() {
this.instanceType = utils.isElectron() ? "desktop" : "server";
this.setupTypeSelected = this.getSetupType = () =>
this.setupNewDocument()
|| this.setupSyncFromDesktop()
|| this.setupSyncFromServer();
this.setupTypeSelected = () => !!this.setupType();
this.selectSetupType = () => {
this.step(this.getSetupType());
this.setupType(this.getSetupType());
this.step(this.setupType());
};
this.back = () => {
this.step("setup-type");
this.setupNewDocument(false);
this.setupSyncFromServer(false);
this.setupSyncFromDesktop(false);
this.setupType("");
};
this.finish = async () => {
if (this.setupNewDocument()) {
if (this.setupType() === 'new-document') {
const username = this.username();
const password1 = this.password1();
const password2 = this.password2();
@@ -72,7 +66,7 @@ function SetupModel() {
window.location.replace("/");
});
}
else if (this.setupSyncFromServer()) {
else if (this.setupType() === 'sync-from-server') {
const syncServerHost = this.syncServerHost();
const syncProxy = this.syncProxy();
const username = this.username();

View File

@@ -446,7 +446,7 @@ html.theme-dark body {
display: block;
flex-basis: content;
flex-shrink: 1;
flex-grow: 100;
flex-grow: 0;
overflow: auto;
}

View File

@@ -10,6 +10,7 @@ const appInfo = require('../../services/app_info');
const eventService = require('../../services/events');
const cls = require('../../services/cls');
const sqlInit = require('../../services/sql_init');
const sql = require('../../services/sql');
async function loginSync(req) {
if (!await sqlInit.schemaExists()) {
@@ -44,7 +45,8 @@ async function loginSync(req) {
req.session.loggedIn = true;
return {
sourceId: sourceIdService.getCurrentSourceId()
sourceId: sourceIdService.getCurrentSourceId(),
maxSyncId: await sql.getValue("SELECT MAX(id) FROM sync")
};
}

View File

@@ -2,14 +2,16 @@
const build = require('./build');
const packageJson = require('../../package');
const {TRILIUM_DATA_DIR} = require('./data_dir');
const APP_DB_VERSION = 121;
const SYNC_VERSION = 2;
const SYNC_VERSION = 3;
module.exports = {
appVersion: packageJson.version,
dbVersion: APP_DB_VERSION,
syncVersion: SYNC_VERSION,
buildDate: build.buildDate,
buildRevision: build.buildRevision
buildRevision: build.buildRevision,
dataDirectory: TRILIUM_DATA_DIR
};

View File

@@ -1 +1 @@
module.exports = { buildDate:"2019-01-01T20:54:23+01:00", buildRevision: "1771ddb78783352970ef64906af8c8fe117183d0" };
module.exports = { buildDate:"2019-01-04T23:33:32+01:00", buildRevision: "5d74dcd2564ff1341550ade1250aa9d790abc056" };

View File

@@ -12,6 +12,7 @@ const Attribute = require('../entities/attribute');
const NoteRevision = require('../entities/note_revision');
const RecentNote = require('../entities/recent_note');
const Option = require('../entities/option');
const Link = require('../entities/link');
async function getHash(entityConstructor, whereBranch) {
// subselect is necessary to have correct ordering in GROUP_CONCAT
@@ -37,7 +38,8 @@ async function getHashes() {
recent_notes: await getHash(RecentNote),
options: await getHash(Option, "isSynced = 1"),
attributes: await getHash(Attribute),
api_tokens: await getHash(ApiToken)
api_tokens: await getHash(ApiToken),
links: await getHash(Link)
};
const elapseTimeMs = new Date().getTime() - startTime.getTime();

View File

@@ -55,10 +55,6 @@ function getTriliumDataDir() {
}
const TRILIUM_DATA_DIR = getTriliumDataDir();
// not necessary to log this since if we have logs we already know where data dir is.
console.log("Using data dir:", TRILIUM_DATA_DIR);
const DOCUMENT_PATH = TRILIUM_DATA_DIR + "/document.db";
const BACKUP_DIR = TRILIUM_DATA_DIR + "/backup";
const LOG_DIR = TRILIUM_DATA_DIR + "/log";

View File

@@ -5,13 +5,24 @@ const ENTER_PROTECTED_SESSION = "ENTER_PROTECTED_SESSION";
const ENTITY_CREATED = "ENTITY_CREATED";
const ENTITY_CHANGED = "ENTITY_CHANGED";
const ENTITY_DELETED = "ENTITY_DELETED";
const ENTITY_SYNCED = "ENTITY_SYNCED";
const CHILD_NOTE_CREATED = "CHILD_NOTE_CREATED";
const eventListeners = {};
function subscribe(eventType, listener) {
/**
* @param eventTypes - can be either single event or an array of events
* @param listener
*/
function subscribe(eventTypes, listener) {
if (!Array.isArray(eventTypes)) {
eventTypes = [ eventTypes ];
}
for (const eventType of eventTypes) {
eventListeners[eventType] = eventListeners[eventType] || [];
eventListeners[eventType].push(listener);
}
}
async function emit(eventType, data) {
@@ -39,5 +50,6 @@ module.exports = {
ENTITY_CREATED,
ENTITY_CHANGED,
ENTITY_DELETED,
ENTITY_SYNCED,
CHILD_NOTE_CREATED
};

View File

@@ -45,8 +45,6 @@ function request(req) {
logger.info(req.method + " " + req.url);
}
info("Using data dir: " + dataDir.TRILIUM_DATA_DIR);
module.exports = {
info,
error,

View File

@@ -52,6 +52,7 @@ async function sendMessage(client, message) {
async function sendMessageToAllClients(message) {
const jsonStr = JSON.stringify(message);
if (webSocketServer) {
log.info("Sending message to all clients: " + jsonStr);
webSocketServer.clients.forEach(function each(client) {
@@ -59,6 +60,7 @@ async function sendMessageToAllClients(message) {
client.send(jsonStr);
}
});
}
}
async function sendPing(client, lastSentSyncId) {

View File

@@ -299,7 +299,9 @@ function getNotePath(noteId) {
}
}
eventService.subscribe(eventService.ENTITY_CHANGED, async ({entityName, entity}) => {
eventService.subscribe([eventService.ENTITY_CHANGED, eventService.ENTITY_DELETED, eventService.ENTITY_SYNCED], async ({entityName, entity}) => {
// note that entity can also be just POJO without methods if coming from sync
if (!loaded) {
return;
}

View File

@@ -364,6 +364,8 @@ async function deleteNote(branch) {
// content with non-existent protected session key
// we don't reset content here, that's postponed and done later to give the user a chance to correct a mistake
await sql.execute("UPDATE notes SET isDeleted = 1 WHERE noteId = ?", [note.noteId]);
// need to manually trigger sync since it's not taken care of by note save
await syncTableService.addNoteSync(note.noteId);
for (const noteRevision of await note.getRevisions()) {
await noteRevision.save();

View File

@@ -1,6 +1,5 @@
"use strict";
const url = require('url');
const log = require('./log');
const sql = require('./sql');
const sqlInit = require('./sql_init');
@@ -99,6 +98,16 @@ async function doLogin() {
syncContext.sourceId = resp.sourceId;
const lastSyncedPull = await getLastSyncedPull();
// this is important in a scenario where we setup the sync by manually copying the document
// lastSyncedPull then could be pretty off for the newly cloned client
if (lastSyncedPull > resp.maxSyncId) {
log.info(`Lowering last synced pull from ${lastSyncedPull} to ${resp.maxSyncId}`);
await setLastSyncedPull(resp.maxSyncId);
}
return syncContext;
}
@@ -256,7 +265,7 @@ async function getEntityRow(entityName, entityId) {
&& entity.content !== null
&& (entity.type === 'file' || entity.type === 'image')) {
entity.content = entity.content.toString("binary");
entity.content = entity.content.toString("base64");
}
return entity;

View File

@@ -2,6 +2,7 @@ const sql = require('./sql');
const log = require('./log');
const eventLogService = require('./event_log');
const syncTableService = require('./sync_table');
const eventService = require('./events');
async function updateEntity(sync, entity, sourceId) {
const {entityName} = sync;
@@ -36,11 +37,20 @@ async function updateEntity(sync, entity, sourceId) {
else {
throw new Error(`Unrecognized entity type ${entityName}`);
}
// currently making exception for protected notes and note revisions because here
// the title and content are not available decrypted as listeners would expect
if ((entityName !== 'notes' && entityName !== 'note_revisions') || !entity.isProtected) {
await eventService.emit(eventService.ENTITY_SYNCED, {
entityName,
entity
});
}
}
function deserializeNoteContentBuffer(note) {
if (note.content !== null && (note.type === 'file' || note.type === 'image')) {
note.content = new Buffer(note.content, 'binary');
note.content = Buffer.from(note.content, 'base64');
}
}

View File

@@ -216,6 +216,11 @@
<th>Build revision:</th>
<td><a href="" target="_blank" id="build-revision"></a></td>
</tr>
<tr>
<th>Data directory:</th>
<td id="data-directory"></td>
</tr>
</table>
</div>
</div>

View File

@@ -13,15 +13,15 @@
<div id="setup-type" data-bind="visible: step() == 'setup-type'" style="margin-top: 20px;">
<div class="radio" style="margin-bottom: 15px;">
<label><input type="radio" name="setup-type" value="new-document" data-bind="checked: setupNewDocument">
<label><input type="radio" name="setup-type" value="new-document" data-bind="checked: setupType">
I'm a new user and I want to create new Trilium document for my notes</label>
</div>
<div class="radio" data-bind="if: instanceType == 'server'" style="margin-bottom: 15px;">
<label><input type="radio" name="setup-type" value="sync-from-desktop" data-bind="checked: setupSyncFromDesktop">
<label><input type="radio" name="setup-type" value="sync-from-desktop" data-bind="checked: setupType">
I have desktop instance already and I want to setup sync with it</label>
</div>
<div class="radio" data-bind="if: instanceType == 'desktop'" style="margin-bottom: 15px;">
<label><input type="radio" name="setup-type" value="sync-from-server" data-bind="checked: setupSyncFromServer">
<label><input type="radio" name="setup-type" value="sync-from-server" data-bind="checked: setupType">
I have server instance up and I want to setup sync with it</label>
</div>