Compare commits

..

17 Commits

Author SHA1 Message Date
zadam
af41e5d115 release 0.45.2 2020-10-29 22:57:25 +01:00
zadam
4f75b6aaaf fix removing stale branches from saved search after refresh, fixes #1354 2020-10-29 22:41:33 +01:00
zadam
82f410f695 fix math rendering in included note and note tooltip, fixes #1340 2020-10-29 21:06:30 +01:00
zadam
2bc06959c3 add a warning to change password dialog, fixes #1344 2020-10-29 20:57:26 +01:00
zadam
b898973ee6 fixed update ckeditor to 23.1.0 2020-10-29 20:09:25 +01:00
zadam
a2b0d8a379 update ckeditor to 23.1.0 2020-10-29 20:02:38 +01:00
zadam
06a4eab7d5 improved detection of image notes in ENEX import, fixes #1348 2020-10-28 23:36:45 +01:00
zadam
25df1a054c fix triggering change event when item is chosen from autocomplete, fixes #1345 2020-10-28 21:48:34 +01:00
zadam
8c4ff7ed2a fix using smart values with .dateCreated, closes #1338 2020-10-27 22:45:22 +01:00
zadam
609829653e fix docker build 2020-10-27 19:39:54 +01:00
zadam
5f20d033a8 release 0.45.1 2020-10-26 22:51:10 +01:00
zadam
93d0324177 fix case where parents of templates are not loaded
(cherry picked from commit a3f4fc7762)
2020-10-26 20:13:11 +01:00
zadam
0afd3c65aa fix setting note title on back/forward button click, closes #1334 2020-10-26 20:11:43 +01:00
zadam
8901c3ec91 fix recent changes showing deleted search note, closes #1331 2020-10-26 19:58:56 +01:00
zadam
c671b0a345 fix OPML import, closes #1333 2020-10-26 19:02:33 +01:00
zadam
9f424836e2 fix adding relation noteId as value, closes #1329 2020-10-26 16:05:34 +01:00
zadam
7f5af4b959 fix broken addTextToActiveEditor API method, closes #1332 2020-10-26 15:57:37 +01:00
25 changed files with 105 additions and 80 deletions

View File

@@ -6,26 +6,11 @@
<option name="TAB_SIZE" value="2" />
</value>
</option>
<H2CodeStyleSettings version="5">
<option name="USE_GENERAL_STYLE" value="false" />
</H2CodeStyleSettings>
<JSCodeStyleSettings version="0">
<option name="USE_EXPLICIT_JS_EXTENSION" value="TRUE" />
</JSCodeStyleSettings>
<JetCodeStyleSettings>
<option name="PACKAGES_TO_USE_STAR_IMPORTS">
<value>
<package name="java.util" alias="false" withSubpackages="false" />
<package name="kotlinx.android.synthetic" alias="false" withSubpackages="true" />
<package name="io.ktor" alias="false" withSubpackages="true" />
</value>
</option>
<option name="PACKAGES_IMPORT_LAYOUT">
<value>
<package name="" alias="false" withSubpackages="true" />
<package name="java" alias="false" withSubpackages="true" />
<package name="javax" alias="false" withSubpackages="true" />
<package name="kotlin" alias="false" withSubpackages="true" />
<package name="" alias="true" withSubpackages="true" />
</value>
</option>
</JetCodeStyleSettings>
</code_scheme>
</component>

View File

@@ -1,4 +1,4 @@
FROM node:12.16.3-alpine
FROM node:12.19.0-alpine
# Create app directory
WORKDIR /usr/src/app

View File

@@ -5,7 +5,7 @@ SERIES=${VERSION:0:4}-latest
cat package.json | grep -v electron > server-package.json
sudo docker build -t zadam/trilium:$VERSION -t zadam/trilium:$SERIES .
sudo docker build -t zadam/trilium:$VERSION --network host -t zadam/trilium:$SERIES .
if [[ $VERSION != *"beta"* ]]; then
sudo docker tag zadam/trilium:$VERSION zadam/trilium:latest

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

8
package-lock.json generated
View File

@@ -1,6 +1,6 @@
{
"name": "trilium",
"version": "0.44.9",
"version": "0.45.1",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
@@ -2658,9 +2658,9 @@
}
},
"electron": {
"version": "9.3.2",
"resolved": "https://registry.npmjs.org/electron/-/electron-9.3.2.tgz",
"integrity": "sha512-0lleEf9msAXGDi2GukAuiGdw3VDgSTlONOnJgqDEz1fuSEVsXz5RX+hNPKDsVDerLTFg/C34RuJf4LwHvkKcBA==",
"version": "9.3.3",
"resolved": "https://registry.npmjs.org/electron/-/electron-9.3.3.tgz",
"integrity": "sha512-xghKeUY1qgnEcJ5w2rXo/toH+8NT2Dktx2aAxBNPV7CIJr3mejJJAPwLbycwtddzr37tgKxHeHlc8ivfKtMkJQ==",
"dev": true,
"requires": {
"@electron/get": "^1.0.1",

View File

@@ -2,7 +2,7 @@
"name": "trilium",
"productName": "Trilium Notes",
"description": "Trilium Notes",
"version": "0.45.0-beta",
"version": "0.45.2",
"license": "AGPL-3.0-only",
"main": "electron.js",
"bin": {
@@ -40,7 +40,6 @@
"electron-window-state": "5.0.3",
"express": "4.17.1",
"express-session": "1.17.1",
"file-type": "16.0.0",
"fs-extra": "9.0.1",
"helmet": "4.1.1",
"html": "1.0.0",
@@ -77,7 +76,7 @@
},
"devDependencies": {
"cross-env": "7.0.2",
"electron": "9.3.2",
"electron": "9.3.3",
"electron-builder": "22.9.1",
"electron-packager": "15.1.0",
"electron-rebuild": "2.3.2",

View File

@@ -57,8 +57,15 @@ function id() {
return randtoken.generate(10);
}
function note(title, type = 'text', mime = 'text/html') {
const note = new Note(noteCache, {noteId: id(), title, type, mime});
function note(title, extraParams = {}) {
const row = Object.assign({
noteId: id(),
title: title,
type: 'text',
mime: 'text/html'
}, extraParams);
const note = new Note(noteCache, row);
return new NoteBuilder(note);
}

View File

@@ -247,6 +247,6 @@ describe("Invalid expressions", () => {
searchContext
});
expect(searchContext.error).toEqual('Misplaced or incomplete expression "="')
expect(searchContext.error).toEqual('Relation can be compared only with property, e.g. ~relation.title=hello in ""')
});
});

View File

@@ -53,8 +53,8 @@ describe("Search", () => {
it("normal search looks also at type and mime", () => {
rootNote
.child(note("Effective Java", 'book', ''))
.child(note("Hello World.java", 'code', 'text/x-java'));
.child(note("Effective Java", {type: 'book', mime:''}))
.child(note("Hello World.java", {type: 'code', mime: 'text/x-java'}));
const searchContext = new SearchContext();
let searchResults = searchService.findNotesWithQuery('book', searchContext);
@@ -178,7 +178,7 @@ describe("Search", () => {
// dates should not be coerced into numbers which would then give wrong numbers
rootNote
.child(note("My note")
.child(note("My note", {dateCreated: dateUtils.localNowDateTime()})
.label('year', new Date().getFullYear().toString())
.label('month', dateUtils.localNowDate().substr(0, 7))
.label('date', dateUtils.localNowDate())
@@ -209,6 +209,8 @@ describe("Search", () => {
test("#month = month", 1);
test("#month = 'MONTH'", 0);
test("note.dateCreated =* month", 1);
test("#date = TODAY", 1);
test("#date = today", 1);
test("#date = 'today'", 0);
@@ -586,7 +588,7 @@ describe("Search", () => {
const searchContext = new SearchContext();
let searchResults = searchService.findNotesWithQuery('# note.text *=* rati and note.noteId != root', searchContext);
let searchResults = searchService.findNotesWithQuery('# note.text *=* vaki and note.noteId != root', searchContext);
expect(searchResults.length).toEqual(1);
expect(noteCache.notes[searchResults[0].noteId].title).toEqual("Slovakia");
});

View File

@@ -8,6 +8,11 @@ const TPL = `
<p>Your username is <strong id="credentials-username"></strong>.</p>
<h3>Change password</h3>
<div class="alert alert-warning" role="alert" style="font-weight: bold; color: red !important;">
Please take care to remember your new password. Password is used to encrypt protected notes. If you forget your password, then all your protected notes are forever lost with no recovery options.
</div>
<form id="change-password-form">
<div class="form-group">
<label for="old-password">Old password</label>

View File

@@ -3,6 +3,7 @@ import utils from "./utils.js";
import renderService from "./render.js";
import protectedSessionService from "./protected_session.js";
import protectedSessionHolder from "./protected_session_holder.js";
import libraryLoader from "./library_loader.js";
async function getRenderedContent(note) {
const type = getRenderingType(note);
@@ -13,6 +14,12 @@ async function getRenderedContent(note) {
const fullNote = await server.get('notes/' + note.noteId);
$rendered = $('<div class="ck-content">').html(fullNote.content);
if ($rendered.find('span.math-tex').length > 0) {
await libraryLoader.requireLibrary(libraryLoader.KATEX);
renderMathInElement($rendered[0], {});
}
}
else if (type === 'code') {
const fullNote = await server.get('notes/' + note.noteId);

View File

@@ -3,6 +3,7 @@ import linkService from "./link.js";
import treeCache from "./tree_cache.js";
import utils from "./utils.js";
import attributeRenderer from "./attribute_renderer.js";
import libraryLoader from "./library_loader.js";
function setupGlobalTooltip() {
$(document).on("mouseenter", "a", mouseEnterHandler);
@@ -101,7 +102,15 @@ async function renderTooltip(note, noteComplement) {
}
if (note.type === 'text' && !utils.isHtmlEmpty(noteComplement.content)) {
content += '<div class="ck-content">' + noteComplement.content + '</div>';
const $content = $('<div class="ck-content">').append(noteComplement.content);
if ($content.find('span.math-tex').length > 0) {
await libraryLoader.requireLibrary(libraryLoader.KATEX);
renderMathInElement($content[0], {});
}
content += $content[0].outerHTML;
}
else if (note.type === 'code' && noteComplement.content && noteComplement.content.trim()) {
content += $("<pre>")

View File

@@ -115,13 +115,13 @@ export default class TabManager extends Component {
// using pushState instead of directly modifying document.location because it does not trigger hashchange
window.history.pushState(null, "", url);
}
document.title = "Trilium Notes";
document.title = "Trilium Notes";
if (activeTabContext.note) {
// it helps navigating in history if note title is included in the title
document.title += " - " + activeTabContext.note.title;
}
if (activeTabContext.note) {
// it helps navigating in history if note title is included in the title
document.title += " - " + activeTabContext.note.title;
}
this.triggerEvent('activeNoteChanged'); // trigger this even in on popstate event

View File

@@ -54,7 +54,7 @@ function closePersistent(id) {
}
function showMessage(message, delay = 2000) {
console.debug(utils.now(), "message: ", message);
console.debug(utils.now(), "message:", message);
toast({
title: "Info",

View File

@@ -20,6 +20,9 @@ class TreeCache {
async loadInitialTree() {
const resp = await server.get('tree');
// FIXME: we need to do this to cover for ascendants of template notes which are not loaded
await this.loadParents(resp, false);
// clear the cache only directly before adding new content which is important for e.g. switching to protected session
/** @type {Object.<string, NoteShort>} */
@@ -40,6 +43,8 @@ class TreeCache {
async loadSubTree(subTreeNoteId) {
const resp = await server.get('tree?subTreeNoteId=' + subTreeNoteId);
await this.loadParents(resp, true);
this.addResp(resp);
return this.notes[subTreeNoteId];
@@ -191,13 +196,18 @@ class TreeCache {
if (note.type === 'search') {
const searchResultNoteIds = await server.get('search-note/' + note.noteId);
if (!searchResultNoteIds) {
throw new Error(`Search note ${note.noteId} failed.`);
if (!Array.isArray(searchResultNoteIds)) {
throw new Error(`Search note ${note.noteId} failed: ${searchResultNoteIds}`);
}
// force to load all the notes at once instead of one by one
await this.getNotes(searchResultNoteIds);
// reset all the virtual branches from old search results
if (note.noteId in treeCache.notes) {
treeCache.notes[note.noteId].children = [];
}
const branches = resp.branches.filter(b => b.noteId === note.noteId || b.parentNoteId === note.noteId);
searchResultNoteIds.forEach((resultNoteId, index) => branches.push({

View File

@@ -283,7 +283,9 @@ export default class AttributeDetailWidget extends TabAwareWidget {
return false;
}
this.attribute.value = suggestion.notePath;
const pathChunks = suggestion.notePath.split('/');
this.attribute.value = pathChunks[pathChunks.length - 1]; // noteId
this.triggerCommand('updateAttributeList', { attributes: this.allAttributes });
this.updateRelatedNotes();

View File

@@ -154,7 +154,7 @@ export default class PromotedAttributesWidget extends TabAwareWidget {
}
}]);
$input.on('autocomplete:noteselected', e => this.promotedAttributeChanged(e))
$input.on('autocomplete:selected', e => this.promotedAttributeChanged(e))
});
}
else if (definition.labelType === 'number') {

View File

@@ -181,7 +181,7 @@ export default class EditableTextTypeWidget extends AbstractTextTypeWidget {
});
}
addTextToActiveEditorEvent(text) {
addTextToActiveEditorEvent({text}) {
if (!this.isActive()) {
return;
}

View File

@@ -38,7 +38,8 @@ async function searchFromNote(req) {
}
if (note.isDeleted) {
return [400, `Note ${req.params.noteId} is deleted.`];
// this can be triggered from recent changes and it's harmless to return empty list rather than fail
return [];
}
if (note.type !== 'search') {

View File

@@ -1 +1 @@
module.exports = { buildDate:"2020-10-21T22:57:54+02:00", buildRevision: "283808d69181628b84d7d48b5029c51bc5a1cf98" };
module.exports = { buildDate:"2020-10-29T22:57:25+01:00", buildRevision: "4f75b6aaafef8144080fd17f403a605f61f5590d" };

View File

@@ -1,5 +1,4 @@
const sax = require("sax");
const FileType = require('file-type');
const stream = require('stream');
const log = require("../log");
const utils = require("../utils");
@@ -138,17 +137,6 @@ function importEnex(taskContext, file, parentNote) {
}
else if (currentTag === 'mime') {
resource.mime = text.toLowerCase();
if (text.startsWith("image/")) {
resource.title = "image";
// images don't have "file-name" tag so we'll create attribute here
resource.attributes.push({
type: 'label',
name: 'originalFileName',
value: resource.title + "." + text.substr(6) // extension from mime type
});
}
}
}
else if (previousTag === 'note') {
@@ -243,11 +231,7 @@ function importEnex(taskContext, file, parentNote) {
const mediaRegex = new RegExp(`<en-media hash="${hash}"[^>]*>`, 'g');
const fileTypeFromBuffer = FileType.fromBuffer(resource.content);
if (fileTypeFromBuffer) {
// If fileType returns something for buffer, then set the mime given
resource.mime = fileTypeFromBuffer.mime;
}
resource.mime = resource.mime || "application/octet-stream";
const createFileNote = () => {
const resourceNote = noteService.createNewNote({
@@ -260,7 +244,7 @@ function importEnex(taskContext, file, parentNote) {
}).note;
for (const attr of resource.attributes) {
noteEntity.addAttribute(attr.type, attr.name, attr.value);
resourceNote.addAttribute(attr.type, attr.name, attr.value);
}
updateDates(resourceNote.noteId, utcDateCreated, utcDateModified);
@@ -274,10 +258,18 @@ function importEnex(taskContext, file, parentNote) {
if (resource.mime && resource.mime.startsWith('image/')) {
try {
const originalName = "image." + resource.mime.substr(6);
const originalName = (resource.title && resource.title !== 'resource')
? resource.title
: `image.${resource.mime.substr(6)}`; // default if real name is not present
const {url, note: imageNote} = imageService.saveImage(noteEntity.noteId, resource.content, originalName, taskContext.data.shrinkImages);
for (const attr of resource.attributes) {
if (attr.name !== 'originalFileName') { // this one is already saved in imageService
imageNote.addAttribute(attr.type, attr.name, attr.value);
}
}
updateDates(imageNote.noteId, utcDateCreated, utcDateModified);
const imageLink = `<img src="${url}">`;

View File

@@ -11,8 +11,8 @@ const htmlSanitizer = require('../html_sanitizer');
* @param {Note} parentNote
* @return {Promise<*[]|*>}
*/
function importOpml(taskContext, fileBuffer, parentNote) {
const xml = new Promise(function(resolve, reject)
async function importOpml(taskContext, fileBuffer, parentNote) {
const xml = await new Promise(function(resolve, reject)
{
parseString(fileBuffer, function (err, result) {
if (err) {

View File

@@ -5,9 +5,9 @@ const stringComparators = {
">=": comparedValue => (val => val >= comparedValue),
"<": comparedValue => (val => val < comparedValue),
"<=": comparedValue => (val => val <= comparedValue),
"*=": comparedValue => (val => val.endsWith(comparedValue)),
"=*": comparedValue => (val => val.startsWith(comparedValue)),
"*=*": comparedValue => (val => val.includes(comparedValue)),
"*=": comparedValue => (val => val && val.endsWith(comparedValue)),
"=*": comparedValue => (val => val && val.startsWith(comparedValue)),
"*=*": comparedValue => (val => val && val.includes(comparedValue)),
};
const numericComparators = {

View File

@@ -80,10 +80,14 @@ function getExpression(tokens, searchContext, level = 0) {
if (i + 2 < tokens.length) {
if (tokens[i + 1].token === '+') {
delta += parseInt(tokens[i + 2].token);
i += 2;
delta += parseInt(tokens[i].token);
}
else if (tokens[i + 1].token === '-') {
delta -= parseInt(tokens[i + 2].token);
i += 2;
delta -= parseInt(tokens[i].token);
}
}
@@ -196,16 +200,18 @@ function getExpression(tokens, searchContext, level = 0) {
if (PropertyComparisonExp.isProperty(tokens[i].token)) {
const propertyName = tokens[i].token;
const operator = tokens[i + 1].token;
const comparedValue = tokens[i + 2].token;
i += 2;
const comparedValue = resolveConstantOperand();
const comparator = buildComparator(operator, comparedValue);
if (!comparator) {
searchContext.addError(`Can't find operator '${operator}' in ${context(i)}`);
searchContext.addError(`Can't find operator '${operator}' in ${context(i - 2)}`);
return;
}
i += 2;
return new PropertyComparisonExp(propertyName, comparator);
}