mirror of
https://github.com/zadam/trilium.git
synced 2025-06-06 18:08:33 +02:00
Merge branch 'stable'
# Conflicts: # package-lock.json # src/public/javascripts/services/tree_context_menu.js # src/services/import/enex.js
This commit is contained in:
commit
90d10c1ff3
2
libraries/ckeditor/ckeditor.js
vendored
2
libraries/ckeditor/ckeditor.js
vendored
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -498,7 +498,7 @@
|
|||||||
t._started = false;
|
t._started = false;
|
||||||
onRenderStop();
|
onRenderStop();
|
||||||
} else {
|
} else {
|
||||||
setImmediate(step);
|
requestIdleCallback(step, { timeout: 10 });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
4061
package-lock.json
generated
4061
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -2,7 +2,7 @@
|
|||||||
"name": "trilium",
|
"name": "trilium",
|
||||||
"productName": "Trilium Notes",
|
"productName": "Trilium Notes",
|
||||||
"description": "Trilium Notes",
|
"description": "Trilium Notes",
|
||||||
"version": "0.37.4",
|
"version": "0.37.6",
|
||||||
"license": "AGPL-3.0-only",
|
"license": "AGPL-3.0-only",
|
||||||
"main": "electron.js",
|
"main": "electron.js",
|
||||||
"bin": {
|
"bin": {
|
||||||
|
@ -818,6 +818,7 @@ class Note extends Entity {
|
|||||||
delete pojo.isContentAvailable;
|
delete pojo.isContentAvailable;
|
||||||
delete pojo.__attributeCache;
|
delete pojo.__attributeCache;
|
||||||
delete pojo.content;
|
delete pojo.content;
|
||||||
|
/** zero references to contentHash, probably can be removed */
|
||||||
delete pojo.contentHash;
|
delete pojo.contentHash;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -93,7 +93,7 @@ export default class SidebarOptions {
|
|||||||
this.$sidebarMinWidth.val(options.sidebarMinWidth);
|
this.$sidebarMinWidth.val(options.sidebarMinWidth);
|
||||||
this.$sidebarWidthPercent.val(options.sidebarWidthPercent);
|
this.$sidebarWidthPercent.val(options.sidebarWidthPercent);
|
||||||
|
|
||||||
if (parseInt(options.showSidebarInNewTab)) {
|
if (options.showSidebarInNewTab === 'true') {
|
||||||
this.$showSidebarInNewTab.attr("checked", "checked");
|
this.$showSidebarInNewTab.attr("checked", "checked");
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
@ -274,7 +274,9 @@ async function filterTabs(noteId) {
|
|||||||
|
|
||||||
async function noteDeleted(noteId) {
|
async function noteDeleted(noteId) {
|
||||||
for (const tc of tabContexts) {
|
for (const tc of tabContexts) {
|
||||||
if (tc.notePath && tc.notePath.split("/").includes(noteId)) {
|
// not removing active even if it contains deleted note since that one will move to another note (handled by deletion logic)
|
||||||
|
// and we would lose tab context state (e.g. sidebar visibility)
|
||||||
|
if (!tc.isActive() && tc.notePath && tc.notePath.split("/").includes(noteId)) {
|
||||||
await tabRow.removeTab(tc.$tab[0]);
|
await tabRow.removeTab(tc.$tab[0]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -246,11 +246,15 @@ class TabContext {
|
|||||||
}
|
}
|
||||||
|
|
||||||
setCurrentNotePathToHash() {
|
setCurrentNotePathToHash() {
|
||||||
if (this.$tab[0] === this.tabRow.activeTabEl) {
|
if (this.isActive()) {
|
||||||
document.location.hash = (this.notePath || "") + "-" + this.tabId;
|
document.location.hash = (this.notePath || "") + "-" + this.tabId;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
isActive() {
|
||||||
|
return this.$tab[0] === this.tabRow.activeTabEl;
|
||||||
|
}
|
||||||
|
|
||||||
setupClasses() {
|
setupClasses() {
|
||||||
for (const clazz of Array.from(this.$tab[0].classList)) { // create copy to safely iterate over while removing classes
|
for (const clazz of Array.from(this.$tab[0].classList)) { // create copy to safely iterate over while removing classes
|
||||||
if (clazz !== 'note-tab') {
|
if (clazz !== 'note-tab') {
|
||||||
|
@ -42,7 +42,7 @@ class TreeContextMenu {
|
|||||||
|| (selNodes.length === 1 && selNodes[0] === this.node);
|
|| (selNodes.length === 1 && selNodes[0] === this.node);
|
||||||
|
|
||||||
const notSearch = note.type !== 'search';
|
const notSearch = note.type !== 'search';
|
||||||
const parentNotSearch = parentNote.type !== 'search';
|
const parentNotSearch = !parentNote || parentNote.type !== 'search';
|
||||||
const insertNoteAfterEnabled = isNotRoot && !isHoisted && parentNotSearch;
|
const insertNoteAfterEnabled = isNotRoot && !isHoisted && parentNotSearch;
|
||||||
|
|
||||||
return [
|
return [
|
||||||
@ -78,8 +78,8 @@ class TreeContextMenu {
|
|||||||
enabled: !clipboard.isClipboardEmpty() && notSearch && noSelectedNotes },
|
enabled: !clipboard.isClipboardEmpty() && notSearch && noSelectedNotes },
|
||||||
{ title: 'Paste after', cmd: "pasteAfter", uiIcon: "paste",
|
{ title: 'Paste after', cmd: "pasteAfter", uiIcon: "paste",
|
||||||
enabled: !clipboard.isClipboardEmpty() && isNotRoot && !isHoisted && parentNotSearch && noSelectedNotes },
|
enabled: !clipboard.isClipboardEmpty() && isNotRoot && !isHoisted && parentNotSearch && noSelectedNotes },
|
||||||
{ title: 'Duplicate note here', cmd: "duplicateNote", uiIcon: "empty",
|
{ title: "Duplicate note here", cmd: "duplicateNote", uiIcon: "empty",
|
||||||
enabled: noSelectedNotes && parentNotSearch && (!note.isProtected || protectedSessionHolder.isProtectedSessionAvailable()) },
|
enabled: noSelectedNotes && parentNotSearch && isNotRoot && !isHoisted && (!note.isProtected || protectedSessionHolder.isProtectedSessionAvailable()) },
|
||||||
{ title: "----" },
|
{ title: "----" },
|
||||||
{ title: "Export", cmd: "export", uiIcon: "empty",
|
{ title: "Export", cmd: "export", uiIcon: "empty",
|
||||||
enabled: notSearch && noSelectedNotes },
|
enabled: notSearch && noSelectedNotes },
|
||||||
|
@ -127,11 +127,13 @@ async function consumeSyncData() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function connectWebSocket() {
|
function connectWebSocket() {
|
||||||
const protocol = document.location.protocol === 'https:' ? 'wss' : 'ws';
|
const loc = window.location;
|
||||||
|
const webSocketUri = (loc.protocol === "https:" ? "wss:" : "ws:")
|
||||||
|
+ "//" + loc.host + loc.pathname;
|
||||||
|
|
||||||
// use wss for secure messaging
|
// use wss for secure messaging
|
||||||
const ws = new WebSocket(protocol + "://" + location.host);
|
const ws = new WebSocket(webSocketUri);
|
||||||
ws.onopen = () => console.debug(utils.now(), "Connected to server with WebSocket");
|
ws.onopen = () => console.debug(utils.now(), `Connected to server ${webSocketUri} with WebSocket`);
|
||||||
ws.onmessage = handleMessage;
|
ws.onmessage = handleMessage;
|
||||||
// we're not handling ws.onclose here because reconnection is done in sendPing()
|
// we're not handling ws.onclose here because reconnection is done in sendPing()
|
||||||
|
|
||||||
|
@ -97,6 +97,11 @@ body {
|
|||||||
font-size: inherit;
|
font-size: inherit;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#context-menu-container {
|
||||||
|
max-height: 100vh;
|
||||||
|
overflow: auto; /* make it scrollable when exceeding total height of the window */
|
||||||
|
}
|
||||||
|
|
||||||
#context-menu-container, #context-menu-container .dropdown-menu {
|
#context-menu-container, #context-menu-container .dropdown-menu {
|
||||||
padding: 3px 0 0;
|
padding: 3px 0 0;
|
||||||
z-index: 1111;
|
z-index: 1111;
|
||||||
|
@ -1 +1 @@
|
|||||||
module.exports = { buildDate:"2019-11-22T22:38:03+01:00", buildRevision: "7a2c7edd7e9b975bf64f732629e711379baecf48" };
|
module.exports = { buildDate:"2019-11-26T22:50:08+01:00", buildRevision: "5193f073e9e55f5440fe2e71fbd2cdfcdb2d2c6b" };
|
||||||
|
@ -3,6 +3,7 @@ const fileType = require('file-type');
|
|||||||
const stream = require('stream');
|
const stream = require('stream');
|
||||||
const log = require("../log");
|
const log = require("../log");
|
||||||
const utils = require("../utils");
|
const utils = require("../utils");
|
||||||
|
const sql = require("../sql");
|
||||||
const noteService = require("../notes");
|
const noteService = require("../notes");
|
||||||
const imageService = require("../image");
|
const imageService = require("../image");
|
||||||
const protectedSessionService = require('../protected_session');
|
const protectedSessionService = require('../protected_session');
|
||||||
@ -11,7 +12,7 @@ const protectedSessionService = require('../protected_session');
|
|||||||
function parseDate(text) {
|
function parseDate(text) {
|
||||||
// insert - and : to make it ISO format
|
// insert - and : to make it ISO format
|
||||||
text = text.substr(0, 4) + "-" + text.substr(4, 2) + "-" + text.substr(6, 2)
|
text = text.substr(0, 4) + "-" + text.substr(4, 2) + "-" + text.substr(6, 2)
|
||||||
+ "T" + text.substr(9, 2) + ":" + text.substr(11, 2) + ":" + text.substr(13, 2) + "Z";
|
+ " " + text.substr(9, 2) + ":" + text.substr(11, 2) + ":" + text.substr(13, 2) + ".000Z";
|
||||||
|
|
||||||
return text;
|
return text;
|
||||||
}
|
}
|
||||||
@ -153,7 +154,7 @@ async function importEnex(taskContext, file, parentNote) {
|
|||||||
} else if (currentTag === 'created') {
|
} else if (currentTag === 'created') {
|
||||||
note.utcDateCreated = parseDate(text);
|
note.utcDateCreated = parseDate(text);
|
||||||
} else if (currentTag === 'updated') {
|
} else if (currentTag === 'updated') {
|
||||||
// updated is currently ignored since utcDateModified is updated automatically with each save
|
note.utcDateModified = parseDate(text);
|
||||||
} else if (currentTag === 'tag') {
|
} else if (currentTag === 'tag') {
|
||||||
note.attributes.push({
|
note.attributes.push({
|
||||||
type: 'label',
|
type: 'label',
|
||||||
@ -190,9 +191,27 @@ async function importEnex(taskContext, file, parentNote) {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
async function updateDates(noteId, utcDateCreated, utcDateModified) {
|
||||||
|
// it's difficult to force custom dateCreated and dateModified to Note entity so we do it post-creation with SQL
|
||||||
|
await sql.execute(`
|
||||||
|
UPDATE notes
|
||||||
|
SET dateCreated = ?,
|
||||||
|
utcDateCreated = ?,
|
||||||
|
dateModified = ?,
|
||||||
|
utcDateModified = ?
|
||||||
|
WHERE noteId = ?`,
|
||||||
|
[utcDateCreated, utcDateCreated, utcDateModified, utcDateModified, noteId]);
|
||||||
|
|
||||||
|
await sql.execute(`
|
||||||
|
UPDATE note_contents
|
||||||
|
SET utcDateModified = ?
|
||||||
|
WHERE noteId = ?`,
|
||||||
|
[utcDateModified, noteId]);
|
||||||
|
}
|
||||||
|
|
||||||
async function saveNote() {
|
async function saveNote() {
|
||||||
// make a copy because stream continues with the next async call and note gets overwritten
|
// make a copy because stream continues with the next async call and note gets overwritten
|
||||||
let {title, content, attributes, resources, utcDateCreated} = note;
|
let {title, content, attributes, resources, utcDateCreated, utcDateModified} = note;
|
||||||
|
|
||||||
content = extractContent(content);
|
content = extractContent(content);
|
||||||
|
|
||||||
@ -210,6 +229,10 @@ async function importEnex(taskContext, file, parentNote) {
|
|||||||
await noteEntity.addAttribute(attr.type, attr.name, attr.value);
|
await noteEntity.addAttribute(attr.type, attr.name, attr.value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
utcDateCreated = utcDateCreated || noteEntity.utcDateCreated;
|
||||||
|
// sometime date modified is not present in ENEX, then use date created
|
||||||
|
utcDateModified = utcDateModified || utcDateCreated;
|
||||||
|
|
||||||
taskContext.increaseProgressCount();
|
taskContext.increaseProgressCount();
|
||||||
|
|
||||||
let noteContent = await noteEntity.getContent();
|
let noteContent = await noteEntity.getContent();
|
||||||
@ -239,6 +262,8 @@ async function importEnex(taskContext, file, parentNote) {
|
|||||||
await noteEntity.addAttribute(attr.type, attr.name, attr.value);
|
await noteEntity.addAttribute(attr.type, attr.name, attr.value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
await updateDates(resourceNote.noteId, utcDateCreated, utcDateModified);
|
||||||
|
|
||||||
taskContext.increaseProgressCount();
|
taskContext.increaseProgressCount();
|
||||||
|
|
||||||
const resourceLink = `<a href="#root/${resourceNote.noteId}">${utils.escapeHtml(resource.title)}</a>`;
|
const resourceLink = `<a href="#root/${resourceNote.noteId}">${utils.escapeHtml(resource.title)}</a>`;
|
||||||
@ -250,7 +275,9 @@ async function importEnex(taskContext, file, parentNote) {
|
|||||||
try {
|
try {
|
||||||
const originalName = "image." + resource.mime.substr(6);
|
const originalName = "image." + resource.mime.substr(6);
|
||||||
|
|
||||||
const {url} = await imageService.saveImage(noteEntity.noteId, resource.content, originalName, taskContext.data.shrinkImages);
|
const {url, note: imageNote} = await imageService.saveImage(noteEntity.noteId, resource.content, originalName, taskContext.data.shrinkImages);
|
||||||
|
|
||||||
|
await updateDates(imageNote.noteId, utcDateCreated, utcDateModified);
|
||||||
|
|
||||||
const imageLink = `<img src="${url}">`;
|
const imageLink = `<img src="${url}">`;
|
||||||
|
|
||||||
@ -272,6 +299,10 @@ async function importEnex(taskContext, file, parentNote) {
|
|||||||
|
|
||||||
// save updated content with links to files/images
|
// save updated content with links to files/images
|
||||||
await noteEntity.setContent(noteContent);
|
await noteEntity.setContent(noteContent);
|
||||||
|
|
||||||
|
await noteService.scanForLinks(noteEntity.noteId);
|
||||||
|
|
||||||
|
await updateDates(noteEntity.noteId, utcDateCreated, utcDateModified);
|
||||||
}
|
}
|
||||||
|
|
||||||
saxStream.on("closetag", async tag => {
|
saxStream.on("closetag", async tag => {
|
||||||
|
@ -37,7 +37,7 @@ function isProtectedSessionAvailable() {
|
|||||||
function decryptNotes(notes) {
|
function decryptNotes(notes) {
|
||||||
for (const note of notes) {
|
for (const note of notes) {
|
||||||
if (note.isProtected) {
|
if (note.isProtected) {
|
||||||
note.title = decrypt(note.title);
|
note.title = decryptString(note.title);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user