Compare commits

..

24 Commits

Author SHA1 Message Date
azivner
9d42c3d802 release 0.28.2 2019-01-21 22:55:12 +01:00
azivner
c654172d33 auto fixer for "undeleted branch of deleted note" consistency check 2019-01-21 22:51:49 +01:00
azivner
e17b26c883 revert consistency checks refactoring for now 2019-01-21 22:46:27 +01:00
azivner
24d02d9cf5 fix in task manager script to not error out so much on deletion 2019-01-21 22:05:37 +01:00
azivner
7208a311ac check that note is not deleted before creating a branch 2019-01-21 21:55:40 +01:00
azivner
ad7355372b issue template 2019-01-21 19:55:07 +01:00
azivner
f18b5babad uncommented issue template 2019-01-20 16:47:49 +01:00
azivner
9831ec0ca9 added issue template 2019-01-20 16:46:56 +01:00
azivner
596544eca3 fix setNoteToParent API method, closes #360 2019-01-20 16:33:09 +01:00
azivner
1f853024ee more autofixers WIP 2019-01-19 09:57:51 +01:00
azivner
0308b13460 expose app info on the backend script api #345 2019-01-18 23:57:08 +01:00
azivner
06b8a82f70 refactoring of consistency checks + some auto fixers 2019-01-18 19:32:59 +01:00
azivner
91ca07929d before we clone the note we must make sure it's not deleted 2019-01-17 23:24:59 +01:00
azivner
afabaa5fdb workaround for hidden last line on mobile 2019-01-17 21:13:53 +01:00
azivner
a6fd3fa77c release 0.28.1-beta 2019-01-17 00:03:17 +01:00
azivner
58a2c08dcd release 0.28.1-beta 2019-01-16 23:58:10 +01:00
azivner
299bbff2f4 fix too large font on mobile 2019-01-16 23:57:09 +01:00
azivner
19d8947123 runOnNoteChange fires also on the frontend, closes #340 2019-01-16 22:52:32 +01:00
azivner
35edce7523 fix crazy clipboard operation on mac setup, closes #348 2019-01-16 22:03:30 +01:00
azivner
bc4cec69a5 use local dates to create day notes, closes #337 2019-01-15 23:46:01 +01:00
azivner
cce8c1b674 create top level note and collapse tree now work relative to hoisted note, closes #343 2019-01-15 20:30:54 +01:00
azivner
aa58788769 added basic DB size diagnostic log 2019-01-15 20:00:24 +01:00
azivner
6c62ab7a52 removing logging params for slow queries 2019-01-15 19:36:04 +01:00
azivner
d6ab638b30 fix file names in github release script 2019-01-15 00:10:47 +01:00
25 changed files with 136 additions and 40 deletions

View File

@@ -17,4 +17,6 @@ rm -r $BUILD_DIR/swiftshader
echo "Packaging linux x64 electron distribution..." echo "Packaging linux x64 electron distribution..."
VERSION=`jq -r ".version" package.json` VERSION=`jq -r ".version" package.json`
tar cJf $BUILD_DIR-${VERSION}.tar.xz $BUILD_DIR cd dist
tar cJf trilium-linux-x64-${VERSION}.tar.xz trilium-linux-x64

View File

@@ -35,4 +35,4 @@ cd ..
VERSION=`jq -r ".version" ../package.json` VERSION=`jq -r ".version" ../package.json`
tar cJf trilium-linux-x64-server-${VERSION}.tar.gz trilium-linux-x64-server tar cJf trilium-linux-x64-server-${VERSION}.tar.xz trilium-linux-x64-server

View File

@@ -42,10 +42,10 @@ git push origin $TAG
bin/build.sh bin/build.sh
LINUX_X64_BUILD=trilium-linux-x64-$VERSION.tar.gz LINUX_X64_BUILD=trilium-linux-x64-$VERSION.tar.xz
WINDOWS_X64_BUILD=trilium-windows-x64-$VERSION.zip WINDOWS_X64_BUILD=trilium-windows-x64-$VERSION.zip
MAC_X64_BUILD=trilium-mac-x64-$VERSION.zip MAC_X64_BUILD=trilium-mac-x64-$VERSION.zip
SERVER_BUILD=trilium-linux-x64-server-$VERSION.tar.gz SERVER_BUILD=trilium-linux-x64-server-$VERSION.tar.xz
echo "Creating release in GitHub" echo "Creating release in GitHub"

Binary file not shown.

View File

@@ -81,7 +81,7 @@ app.on('ready', async () => {
const dateNoteService = require('./src/services/date_notes'); const dateNoteService = require('./src/services/date_notes');
const dateUtils = require('./src/services/date_utils'); const dateUtils = require('./src/services/date_utils');
const parentNote = await dateNoteService.getDateNote(dateUtils.nowDate()); const parentNote = await dateNoteService.getDateNote(dateUtils.nowLocalDate());
// window may be hidden / not in focus // window may be hidden / not in focus
mainWindow.focus(); mainWindow.focus();

5
issue_template.md Normal file
View File

@@ -0,0 +1,5 @@
For bug reports, please mention **version of the application** and include **log files** from following location:
* `/home/[user]/.local/share/trilium-data/log` for Linux
* `C:\Users\[user]\AppData\Roaming\trilium-data\log` for Windows Vista and up
* `/Users/[user]/Library/Application Support/trilium-data/log` for Mac OS

View File

@@ -2,7 +2,7 @@
"name": "trilium", "name": "trilium",
"productName": "Trilium Notes", "productName": "Trilium Notes",
"description": "Trilium Notes", "description": "Trilium Notes",
"version": "0.28.0-beta", "version": "0.28.2",
"license": "AGPL-3.0-only", "license": "AGPL-3.0-only",
"main": "electron.js", "main": "electron.js",
"bin": { "bin": {

View File

@@ -160,7 +160,7 @@ async function createPromotedAttributeRow(definitionAttr, valueAttr) {
$input.autocomplete({ $input.autocomplete({
appendTo: document.querySelector('body'), appendTo: document.querySelector('body'),
hint: false, hint: false,
autoselect: true, autoselect: false,
openOnFocus: true, openOnFocus: true,
minLength: 0, minLength: 0,
tabAutocomplete: false tabAutocomplete: false

View File

@@ -30,9 +30,13 @@ async function executeStartupBundles() {
} }
async function executeRelationBundles(note, relationName) { async function executeRelationBundles(note, relationName) {
const bundlesToRun = await server.get("script/relation/" + note.noteId + "/" + relationName); note.bundleCache = note.bundleCache || {};
for (const bundle of bundlesToRun) { if (!note.bundleCache[relationName]) {
note.bundleCache[relationName] = await server.get("script/relation/" + note.noteId + "/" + relationName);
}
for (const bundle of note.bundleCache[relationName]) {
await executeBundle(bundle, note); await executeBundle(bundle, note);
} }
} }

View File

@@ -131,6 +131,9 @@ async function saveNote() {
} }
$savedIndicator.fadeIn(); $savedIndicator.fadeIn();
// run async
bundleService.executeRelationBundles(getCurrentNote(), 'runOnNoteChange');
} }
async function saveNoteIfChanged() { async function saveNoteIfChanged() {

View File

@@ -498,9 +498,11 @@ async function loadTree() {
return await treeBuilder.prepareTree(resp.notes, resp.branches, resp.relations); return await treeBuilder.prepareTree(resp.notes, resp.branches, resp.relations);
} }
function collapseTree(node = null) { async function collapseTree(node = null) {
if (!node) { if (!node) {
node = $tree.fancytree("getRootNode"); const hoistedNoteId = await hoistedNoteService.getHoistedNoteId();
node = getNodesByNoteId(hoistedNoteId)[0];
} }
node.setExpanded(false); node.setExpanded(false);
@@ -541,9 +543,11 @@ async function setNoteTitle(noteId, title) {
} }
async function createNewTopLevelNote() { async function createNewTopLevelNote() {
const rootNode = getNodesByNoteId('root')[0]; const hoistedNoteId = await hoistedNoteService.getHoistedNoteId();
await createNote(rootNode, "root", "into", false); const rootNode = getNodesByNoteId(hoistedNoteId)[0];
await createNote(rootNode, hoistedNoteId, "into", false);
} }
async function createNote(node, parentNoteId, target, isProtected, saveSelection = false) { async function createNote(node, parentNoteId, target, isProtected, saveSelection = false) {

View File

@@ -23,6 +23,7 @@ function formatTimeWithSeconds(date) {
return padNum(date.getHours()) + ":" + padNum(date.getMinutes()) + ":" + padNum(date.getSeconds()); return padNum(date.getHours()) + ":" + padNum(date.getMinutes()) + ":" + padNum(date.getSeconds());
} }
// this is producing local time!
function formatDate(date) { function formatDate(date) {
// return padNum(date.getDate()) + ". " + padNum(date.getMonth() + 1) + ". " + date.getFullYear(); // return padNum(date.getDate()) + ". " + padNum(date.getMonth() + 1) + ". " + date.getFullYear();
// instead of european format we'll just use ISO as that's pretty unambiguous // instead of european format we'll just use ISO as that's pretty unambiguous
@@ -30,6 +31,7 @@ function formatDate(date) {
return formatDateISO(date); return formatDateISO(date);
} }
// this is producing local time!
function formatDateISO(date) { function formatDateISO(date) {
return date.getFullYear() + "-" + padNum(date.getMonth() + 1) + "-" + padNum(date.getDate()); return date.getFullYear() + "-" + padNum(date.getMonth() + 1) + "-" + padNum(date.getDate());
} }
@@ -143,6 +145,7 @@ function bindShortcut(keyboardShortcut, handler) {
} }
$(document).bind('keydown', keyboardShortcut, e => { $(document).bind('keydown', keyboardShortcut, e => {
console.log(e);
handler(); handler();
e.preventDefault(); e.preventDefault();

View File

@@ -24,7 +24,6 @@ html, body {
} }
#tree { #tree {
font-size: larger;
width: 100%; width: 100%;
overflow: auto; overflow: auto;
} }
@@ -45,7 +44,8 @@ html, body {
position: relative; position: relative;
overflow: auto; overflow: auto;
flex-direction: column; flex-direction: column;
height: 100%; /* for some reason detail overflows a little bit so we subtract few pixels */
height: calc(100% - 25px);
/* large left padding is necessary for ckeditor gutter in detail-only (smartphone) layout */ /* large left padding is necessary for ckeditor gutter in detail-only (smartphone) layout */
padding-left: 35px; padding-left: 35px;
} }

View File

@@ -54,6 +54,11 @@ async function getRelationBundles(req) {
for (const noteId of uniqueNoteIds) { for (const noteId of uniqueNoteIds) {
const note = await repository.getNote(noteId); const note = await repository.getNote(noteId);
if (!note.isJavaScript() || note.getScriptEnv() !== 'frontend') {
continue;
}
const bundle = await scriptService.getScriptBundleForFrontend(note); const bundle = await scriptService.getScriptBundleForFrontend(note);
if (bundle) { if (bundle) {

View File

@@ -11,6 +11,7 @@ const repository = require('./repository');
const axios = require('axios'); const axios = require('axios');
const cloningService = require('./cloning'); const cloningService = require('./cloning');
const messagingService = require('./messaging'); const messagingService = require('./messaging');
const appInfo = require('./app_info');
/** /**
* This is the main backend API interface for scripts. It's published in the local "api" object. * This is the main backend API interface for scripts. It's published in the local "api" object.
@@ -234,6 +235,11 @@ function BackendScriptApi(startNote, currentNote, originEntity) {
* @returns {Promise<void>} * @returns {Promise<void>}
*/ */
this.refreshTree = () => messagingService.sendMessageToAllClients({ type: 'refresh-tree' }); this.refreshTree = () => messagingService.sendMessageToAllClients({ type: 'refresh-tree' });
/**
* @return {{syncVersion, appVersion, buildRevision, dbVersion, dataDirectory, buildDate}|*} - object representing basic info about running Trilium version
*/
this.getAppInfo = () => appInfo
} }
module.exports = BackendScriptApi; module.exports = BackendScriptApi;

View File

@@ -1 +1 @@
module.exports = { buildDate:"2019-01-14T23:51:55+01:00", buildRevision: "e50f9cd0a354e29bb40c161cf7288e13b732e0d3" }; module.exports = { buildDate:"2019-01-21T22:55:12+01:00", buildRevision: "c654172d33eed3773b5f76074a2c97479b522920" };

View File

@@ -8,6 +8,10 @@ const repository = require('./repository');
const Branch = require('../entities/branch'); const Branch = require('../entities/branch');
async function cloneNoteToParent(noteId, parentNoteId, prefix) { async function cloneNoteToParent(noteId, parentNoteId, prefix) {
if (await isNoteDeleted(noteId) || await isNoteDeleted(parentNoteId)) {
return { success: false, message: 'Note is deleted.' };
}
const validationResult = await treeService.validateParentChild(parentNoteId, noteId); const validationResult = await treeService.validateParentChild(parentNoteId, noteId);
if (!validationResult.success) { if (!validationResult.success) {
@@ -27,6 +31,10 @@ async function cloneNoteToParent(noteId, parentNoteId, prefix) {
} }
async function ensureNoteIsPresentInParent(noteId, parentNoteId, prefix) { async function ensureNoteIsPresentInParent(noteId, parentNoteId, prefix) {
if (await isNoteDeleted(noteId) || await isNoteDeleted(parentNoteId)) {
return { success: false, message: 'Note is deleted.' };
}
const validationResult = await treeService.validateParentChild(parentNoteId, noteId); const validationResult = await treeService.validateParentChild(parentNoteId, noteId);
if (!validationResult.success) { if (!validationResult.success) {
@@ -61,6 +69,10 @@ async function toggleNoteInParent(present, noteId, parentNoteId, prefix) {
async function cloneNoteAfter(noteId, afterBranchId) { async function cloneNoteAfter(noteId, afterBranchId) {
const afterNote = await treeService.getBranch(afterBranchId); const afterNote = await treeService.getBranch(afterBranchId);
if (await isNoteDeleted(noteId) || await isNoteDeleted(afterNote.parentNoteId)) {
return { success: false, message: 'Note is deleted.' };
}
const validationResult = await treeService.validateParentChild(afterNote.parentNoteId, noteId); const validationResult = await treeService.validateParentChild(afterNote.parentNoteId, noteId);
if (!validationResult.result) { if (!validationResult.result) {
@@ -84,6 +96,12 @@ async function cloneNoteAfter(noteId, afterBranchId) {
return { success: true }; return { success: true };
} }
async function isNoteDeleted(noteId) {
const note = await repository.getNote(noteId);
return note.isDeleted;
}
module.exports = { module.exports = {
cloneNoteToParent, cloneNoteToParent,
ensureNoteIsPresentInParent, ensureNoteIsPresentInParent,

View File

@@ -101,6 +101,26 @@ async function fixEmptyRelationTargets(errorList) {
} }
} }
async function fixUndeletedBranches() {
const undeletedBranches = await sql.getRows(`
SELECT
branchId, noteId
FROM
branches
JOIN notes USING(noteId)
WHERE
notes.isDeleted = 1
AND branches.isDeleted = 0`);
for (const {branchId, noteId} of undeletedBranches) {
const branch = await repository.getBranch(branchId);
branch.isDeleted = true;
await branch.save();
log.info(`Branch ${branchId} has been deleted since associated note ${noteId} is deleted.`);
}
}
async function runAllChecks() { async function runAllChecks() {
const errorList = []; const errorList = [];
@@ -125,16 +145,7 @@ async function runAllChecks() {
notes.noteId IS NULL`, notes.noteId IS NULL`,
"Missing notes records for following branch ID > note ID", errorList); "Missing notes records for following branch ID > note ID", errorList);
await runCheck(` await fixUndeletedBranches();
SELECT
branchId
FROM
branches
JOIN notes USING(noteId)
WHERE
notes.isDeleted = 1
AND branches.isDeleted = 0`,
"Branch is not deleted even though main note is deleted for following branch IDs", errorList);
await runCheck(` await runCheck(`
SELECT SELECT

View File

@@ -47,8 +47,8 @@ async function getRootCalendarNote() {
return rootNote; return rootNote;
} }
async function getYearNote(dateTimeStr, rootNote) { async function getYearNote(dateStr, rootNote) {
const yearStr = dateTimeStr.substr(0, 4); const yearStr = dateStr.substr(0, 4);
let yearNote = await attributeService.getNoteWithLabel(YEAR_LABEL, yearStr); let yearNote = await attributeService.getNoteWithLabel(YEAR_LABEL, yearStr);
@@ -66,19 +66,19 @@ async function getYearNote(dateTimeStr, rootNote) {
return yearNote; return yearNote;
} }
async function getMonthNote(dateTimeStr, rootNote) { async function getMonthNote(dateStr, rootNote) {
const monthStr = dateTimeStr.substr(0, 7); const monthStr = dateStr.substr(0, 7);
const monthNumber = dateTimeStr.substr(5, 2); const monthNumber = dateStr.substr(5, 2);
let monthNote = await attributeService.getNoteWithLabel(MONTH_LABEL, monthStr); let monthNote = await attributeService.getNoteWithLabel(MONTH_LABEL, monthStr);
if (!monthNote) { if (!monthNote) {
const yearNote = await getYearNote(dateTimeStr, rootNote); const yearNote = await getYearNote(dateStr, rootNote);
monthNote = await getNoteStartingWith(yearNote.noteId, monthNumber); monthNote = await getNoteStartingWith(yearNote.noteId, monthNumber);
if (!monthNote) { if (!monthNote) {
const dateObj = dateUtils.parseDate(dateTimeStr); const dateObj = dateUtils.parseLocalDate(dateStr);
const noteTitle = monthNumber + " - " + MONTHS[dateObj.getMonth()]; const noteTitle = monthNumber + " - " + MONTHS[dateObj.getMonth()];
@@ -92,21 +92,20 @@ async function getMonthNote(dateTimeStr, rootNote) {
return monthNote; return monthNote;
} }
async function getDateNote(dateTimeStr) { async function getDateNote(dateStr) {
const rootNote = await getRootCalendarNote(); const rootNote = await getRootCalendarNote();
const dateStr = dateTimeStr.substr(0, 10); const dayNumber = dateStr.substr(8, 2);
const dayNumber = dateTimeStr.substr(8, 2);
let dateNote = await attributeService.getNoteWithLabel(DATE_LABEL, dateStr); let dateNote = await attributeService.getNoteWithLabel(DATE_LABEL, dateStr);
if (!dateNote) { if (!dateNote) {
const monthNote = await getMonthNote(dateTimeStr, rootNote); const monthNote = await getMonthNote(dateStr, rootNote);
dateNote = await getNoteStartingWith(monthNote.noteId, dayNumber); dateNote = await getNoteStartingWith(monthNote.noteId, dayNumber);
if (!dateNote) { if (!dateNote) {
const dateObj = dateUtils.parseDate(dateTimeStr); const dateObj = dateUtils.parseLocalDate(dateStr);
const noteTitle = dayNumber + " - " + DAYS[dateObj.getDay()]; const noteTitle = dayNumber + " - " + DAYS[dateObj.getDay()];

View File

@@ -2,6 +2,16 @@ function nowDate() {
return dateStr(new Date()); return dateStr(new Date());
} }
function nowLocalDate() {
const date = new Date();
return date.getFullYear() + "-" + pad(date.getMonth() + 1) + "-" + pad(date.getDate());
}
function pad(num) {
return num <= 9 ? `0${num}` : `${num}`;
}
function dateStr(date) { function dateStr(date) {
return date.toISOString(); return date.toISOString();
} }
@@ -25,14 +35,23 @@ function parseDate(str) {
return parseDateTime(datePart + "T12:00:00.000Z"); return parseDateTime(datePart + "T12:00:00.000Z");
} }
function parseLocalDate(str) {
const datePart = str.substr(0, 10);
// not specifying the timezone and specifying the time means Date.parse() will use the local timezone
return parseDateTime(datePart + " 12:00:00.000");
}
function getDateTimeForFile() { function getDateTimeForFile() {
return new Date().toISOString().substr(0, 19).replace(/:/g, ''); return new Date().toISOString().substr(0, 19).replace(/:/g, '');
} }
module.exports = { module.exports = {
nowDate, nowDate,
nowLocalDate,
dateStr, dateStr,
parseDate, parseDate,
parseDateTime, parseDateTime,
parseLocalDate,
getDateTimeForFile getDateTimeForFile
}; };

View File

@@ -6,7 +6,7 @@ const sourceIdService = require('./source_id');
const log = require('./log'); const log = require('./log');
async function executeNote(note, originEntity) { async function executeNote(note, originEntity) {
if (!note.isJavaScript() || !note.isContentAvailable) { if (!note.isJavaScript() || note.getScriptEnv() !== 'backend' || !note.isContentAvailable) {
return; return;
} }

View File

@@ -123,7 +123,7 @@ async function execute(query, params = []) {
const milliseconds = Date.now() - startTimestamp; const milliseconds = Date.now() - startTimestamp;
if (milliseconds >= 200) { if (milliseconds >= 200) {
log.info(`Slow query took ${milliseconds}ms: ${query}, params=${params}`); log.info(`Slow query took ${milliseconds}ms: ${query}`);
} }
return result; return result;

View File

@@ -155,6 +155,10 @@ async function dbInitialized() {
await initDbConnection(); await initDbConnection();
} }
dbReady.then(async () => {
log.info("DB size: " + await sql.getValue("SELECT page_count * page_size / 1000 as size FROM pragma_page_count(), pragma_page_size()") + " KB");
});
module.exports = { module.exports = {
dbReady, dbReady,
schemaExists, schemaExists,

View File

@@ -111,6 +111,12 @@ async function sortNotesAlphabetically(parentNoteId) {
} }
async function setNoteToParent(noteId, prefix, parentNoteId) { async function setNoteToParent(noteId, prefix, parentNoteId) {
const parentNote = await repository.getNote(parentNoteId);
if (parentNote && parentNote.isDeleted) {
throw new Error(`Cannot move note to deleted parent note ${parentNoteId}`);
}
// case where there might be more such branches is ignored. It's expected there should be just one // case where there might be more such branches is ignored. It's expected there should be just one
const branch = await repository.getEntity("SELECT * FROM branches WHERE isDeleted = 0 AND noteId = ? AND prefix = ?", [noteId, prefix]); const branch = await repository.getEntity("SELECT * FROM branches WHERE isDeleted = 0 AND noteId = ? AND prefix = ?", [noteId, prefix]);
@@ -126,6 +132,12 @@ async function setNoteToParent(noteId, prefix, parentNoteId) {
await branch.save(); await branch.save();
} }
else if (parentNoteId) { else if (parentNoteId) {
const note = await repository.getNote(noteId);
if (note.isDeleted) {
throw new Error(`Cannot create a branch for ${noteId} which is deleted.`);
}
await new Branch({ await new Branch({
noteId: noteId, noteId: noteId,
parentNoteId: parentNoteId, parentNoteId: parentNoteId,

View File

@@ -128,6 +128,7 @@
<script>if (typeof module === 'object') {window.module = module; module = undefined;}</script> <script>if (typeof module === 'object') {window.module = module; module = undefined;}</script>
<script src="libraries/jquery.min.js"></script> <script src="libraries/jquery.min.js"></script>
<script src="libraries/jquery.hotkeys.js"></script>
<link href="libraries/bootstrap/css/bootstrap.min.css" rel="stylesheet"> <link href="libraries/bootstrap/css/bootstrap.min.css" rel="stylesheet">
<script src="libraries/bootstrap/js/bootstrap.bundle.min.js"></script> <script src="libraries/bootstrap/js/bootstrap.bundle.min.js"></script>