mirror of
https://github.com/zadam/trilium.git
synced 2025-11-03 11:56:01 +01:00
Compare commits
17 Commits
v0.45.0-be
...
v0.45.2
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
af41e5d115 | ||
|
|
4f75b6aaaf | ||
|
|
82f410f695 | ||
|
|
2bc06959c3 | ||
|
|
b898973ee6 | ||
|
|
a2b0d8a379 | ||
|
|
06a4eab7d5 | ||
|
|
25df1a054c | ||
|
|
8c4ff7ed2a | ||
|
|
609829653e | ||
|
|
5f20d033a8 | ||
|
|
93d0324177 | ||
|
|
0afd3c65aa | ||
|
|
8901c3ec91 | ||
|
|
c671b0a345 | ||
|
|
9f424836e2 | ||
|
|
7f5af4b959 |
21
.idea/codeStyles/Project.xml
generated
21
.idea/codeStyles/Project.xml
generated
@@ -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>
|
||||
@@ -1,4 +1,4 @@
|
||||
FROM node:12.16.3-alpine
|
||||
FROM node:12.19.0-alpine
|
||||
|
||||
# Create app directory
|
||||
WORKDIR /usr/src/app
|
||||
|
||||
@@ -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
|
||||
|
||||
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
8
package-lock.json
generated
8
package-lock.json
generated
@@ -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",
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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 ""')
|
||||
});
|
||||
});
|
||||
|
||||
@@ -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");
|
||||
});
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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>")
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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({
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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') {
|
||||
|
||||
@@ -181,7 +181,7 @@ export default class EditableTextTypeWidget extends AbstractTextTypeWidget {
|
||||
});
|
||||
}
|
||||
|
||||
addTextToActiveEditorEvent(text) {
|
||||
addTextToActiveEditorEvent({text}) {
|
||||
if (!this.isActive()) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -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') {
|
||||
|
||||
@@ -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" };
|
||||
|
||||
@@ -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}">`;
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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 = {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user