Compare commits

...

14 Commits

Author SHA1 Message Date
zadam
f6ad1c6aa7 release 0.56.1 2022-10-22 23:07:24 +02:00
zadam
c1127ec429 fix running of runOnNoteCreation hook, #3219 2022-10-22 21:34:38 +02:00
zadam
867f7f3f59 don't display "workspace templates", #3219 2022-10-22 16:03:21 +02:00
zadam
1c52303bb3 don't display "workspace templates", #3219 2022-10-22 16:01:15 +02:00
zadam
876e6caa23 better handling of incorrect operators 2022-10-22 15:58:15 +02:00
zadam
34f07b4376 better inbox desc 2022-10-22 15:22:42 +02:00
zadam
ffc28c8485 better inbox desc 2022-10-22 15:22:03 +02:00
zadam
913e9ef6e0 Merge remote-tracking branch 'origin/master' 2022-10-22 15:01:17 +02:00
zadam
14fb9c76b0 fix backlinks in day note subtree, fixes #3158 2022-10-22 15:01:10 +02:00
zadam
c5435009d7 Merge pull request #3213 from agentydragon/both-protos
Check both http and https in DockerHealthcheck
2022-10-22 14:00:45 +02:00
Rai
1d3132e447 Check both http and https in DockerHealthcheck
I've been getting Docker reporting my Trilium container as unhealthy
because wget was trying to talk HTTP to it while it was expecting
HTTPS.
2022-10-20 23:29:23 -07:00
zadam
63eb22c7ac fix print color to black, closes #3202 2022-10-20 22:32:06 +02:00
zadam
c11cf41f30 don't create app icon for flatpak/debian linux builds, fixes #3201 2022-10-16 20:52:10 +02:00
zadam
01910d3231 flathub release script 2022-10-15 21:09:27 +02:00
16 changed files with 147 additions and 69 deletions

View File

@@ -1,6 +1,13 @@
#!/bin/sh
if wget --spider -S "127.0.0.1:8080/api/health-check" 2>&1 | awk 'NR==2' | grep -w "HTTP/1.1 200 OK" ; then
exit 0
else
exit 1
fi
# Try connecting to /api/health-check using both http and https.
# TODO: we should only be connecting with the actual protocol that is enabled
# TODO: this assumes we use the default port 8080
for proto in http https; do
if wget --spider -S "$proto://127.0.0.1:8080/api/health-check" 2>&1 | awk 'NR==2' | grep -w "HTTP/1.1 200 OK" ; then
exit 0
fi
done
exit 1

View File

@@ -17,21 +17,27 @@ VERSION_DATE=$(git log -1 --format=%aI "v${VERSION}" | cut -c -10)
VERSION_COMMIT=$(git rev-list -n 1 "v${VERSION}")
# expecting the directory at a specific path
cd ~/trilium-flathub
cd ~/trilium-flathub || exit
if ! git diff-index --quiet HEAD --; then
echo "There are uncommitted changes"
exit 1
fi
BASE_BRANCH=master
if [[ "$VERSION" == *"beta"* ]]; then
git switch beta
else
git switch master
BASE_BRANCH=beta
fi
git switch "${BASE_BRANCH}"
git pull
BRANCH=b${VERSION}
git branch "${BRANCH}"
git switch "${BRANCH}"
echo "Updating files with version ${VERSION}, date ${VERSION_DATE} and commit ${VERSION_COMMIT}"
flatpak-node-generator npm ../trilium/package-lock.json
@@ -45,4 +51,7 @@ git add ./com.github.zadam.trilium.metainfo.xml
git add ./com.github.zadam.trilium.yml
git commit -m "release $VERSION"
git push
git push --set-upstream origin "${BRANCH}"
gh pr create --fill -B "${BASE_BRANCH}"
gh pr merge --auto --merge --delete-branch

4
package-lock.json generated
View File

@@ -1,12 +1,12 @@
{
"name": "trilium",
"version": "0.55.1",
"version": "0.56.0-beta",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "trilium",
"version": "0.55.1",
"version": "0.56.0-beta",
"hasInstallScript": true,
"license": "AGPL-3.0-only",
"dependencies": {

View File

@@ -2,7 +2,7 @@
"name": "trilium",
"productName": "Trilium Notes",
"description": "Trilium Notes",
"version": "0.56.0-beta",
"version": "0.56.1",
"license": "AGPL-3.0-only",
"main": "electron.js",
"bin": {
@@ -21,9 +21,9 @@
"build-frontend-docs": "rm -r ./docs/frontend_api && ./node_modules/.bin/jsdoc -c jsdoc-conf.json -d ./docs/frontend_api src/public/app/entities/*.js src/public/app/services/frontend_script_api.js src/public/app/widgets/collapsible_widget.js",
"build-docs": "npm run build-backend-docs && npm run build-frontend-docs",
"webpack": "npx webpack -c webpack-desktop.config.js && npx webpack -c webpack-mobile.config.js && npx webpack -c webpack-setup.config.js",
"test": "jasmine",
"test-jasmine": "jasmine",
"test-es6": "node -r esm spec-es6/attribute_parser.spec.js ",
"test-all": "npm run test && npm run test-es6",
"test": "npm run test-jasmine && npm run test-es6",
"postinstall": "rimraf ./node_modules/canvas"
},
"dependencies": {

View File

@@ -210,7 +210,7 @@ const ATTR_HELP = {
"workspaceTemplate": "This note will appear in the selection of available template when creating new note, but only when hoisted into a workspace containing this template",
"searchHome": "new search notes will be created as children of this note",
"hoistedSearchHome": "new search notes will be created as children of this note when hoisted to some ancestor of this note",
"inbox": "default inbox location for new notes",
"inbox": "default inbox location for new notes - when you create a note using \"new note\" button in the sidebar, notes will be created as child notes in the note marked as with <code>#inbox</code> label.",
"hoistedInbox": "default inbox location for new notes when hoisted to some ancestor of this note",
"sqlConsoleHome": "default location of SQL console notes",
"bookmarked": "note with this label will appear in bookmarks",
@@ -240,15 +240,15 @@ const ATTR_HELP = {
"keyboardShortcut": "Defines a keyboard shortcut which will immediately jump to this note. Example: 'ctrl+alt+e'. Requires frontend reload for the change to take effect."
},
"relation": {
"runOnNoteCreation": "executes when note is created on backend",
"runOnNoteCreation": "executes when note is created on backend. Use this relation if you want to run the script for all notes created under a specific subtree. In that case, create it on the subtree root note and make it inheritable. A new note created within the subtree (any depth) will trigger the script.",
"runOnChildNoteCreation": "executes when new note is created under the note where this relation is defined",
"runOnNoteTitleChange": "executes when note title is changed (includes note creation as well)",
"runOnNoteChange": "executes when note is changed (includes note creation as well)",
"runOnNoteDeletion": "executes when note is being deleted",
"runOnBranchCreation": "executes when a branch is created. Branch is a link between parent note and child note and is created e.g. when cloning or moving note.",
"runOnBranchDeletion": "executes when a branch is deleted. Branch is a link between parent note and child note and is deleted e.g. when moving note (old branch/link is deleted).",
"runOnChildNoteCreation": "executes when new note is created under this note",
"runOnAttributeCreation": "executes when new attribute is created under this note",
"runOnAttributeChange": "executes when attribute is changed under this note",
"runOnAttributeCreation": "executes when new attribute is created for the note which defines this relation",
"runOnAttributeChange": " executes when the attribute is changed of a note which defines this relation. This is triggered also when the attribute is deleted",
"template": "attached note's attributes will be inherited even without parent-child relationship. See template for details.",
"renderNote": 'notes of type "render HTML note" will be rendered using a code note (HTML or script) and it is necessary to point using this relation to which note should be rendered',
"widget": "target of this relation will be executed and rendered as a widget in the sidebar",

View File

@@ -86,7 +86,7 @@ export default class BacklinksWidget extends NoteContextAwareWidget {
this.clearItems();
// can't use froca since that would count only relations from loaded notes
const resp = await server.get(`notes/${this.noteId}/backlink-count`);
const resp = await server.get(`note-map/${this.noteId}/backlink-count`);
if (!resp || !resp.count) {
this.toggle(false);

View File

@@ -69,7 +69,7 @@ export default class EmptyTypeWidget extends TypeWidget {
}
async doRefresh(note) {
const workspaceNotes = await searchService.searchForNotes('#workspace');
const workspaceNotes = await searchService.searchForNotes('#workspace #!template');
this.$workspaceNotes.empty();

View File

@@ -1,5 +1,10 @@
@media print
{
html body {
/* https://github.com/zadam/trilium/issues/3202 */
color: black;
}
.no-print, .no-print *
{
display: none !important;
@@ -8,4 +13,4 @@
.relation-map-wrapper {
height: 100vh !important;
}
}
}

View File

@@ -287,6 +287,27 @@ function findExcerpts(sourceNote, referencedNoteId) {
return excerpts;
}
function getFilteredBacklinks(note) {
return note.getTargetRelations()
// search notes have "ancestor" relations which are not interesting
.filter(note => note.getNote().type !== 'search');
}
function getBacklinkCount(req) {
const {noteId} = req.params;
const note = becca.getNote(noteId);
if (!note) {
return [404, "Not found"];
}
else {
return {
count: getFilteredBacklinks(note).length
};
}
}
function getBacklinks(req) {
const {noteId} = req.params;
const note = becca.getNote(noteId);
@@ -295,11 +316,9 @@ function getBacklinks(req) {
return [404, `Note ${noteId} was not found`];
}
let backlinks = note.getTargetRelations();
let backlinksWithExcerptCount = 0;
return backlinks.filter(note => !note.getNote().hasLabel('excludeFromNoteMap')).map(backlink => {
return getFilteredBacklinks(note).map(backlink => {
const sourceNote = backlink.note;
if (sourceNote.type !== 'text' || backlinksWithExcerptCount > 50) {
@@ -323,5 +342,6 @@ function getBacklinks(req) {
module.exports = {
getLinkMap,
getTreeMap,
getBacklinkCount,
getBacklinks
};

View File

@@ -305,21 +305,6 @@ function uploadModifiedFile(req) {
note.setContent(fileContent);
}
function getBacklinkCount(req) {
const {noteId} = req.params;
const note = becca.getNote(noteId);
if (!note) {
return [404, "Not found"];
}
else {
return {
count: note.getTargetRelations().filter(note => !note.getNote().hasLabel('excludeFromNoteMap')).length
};
}
}
module.exports = {
getNote,
updateNoteContent,
@@ -334,6 +319,5 @@ module.exports = {
duplicateSubtree,
eraseDeletedNotesNow,
getDeleteNotesPreview,
uploadModifiedFile,
getBacklinkCount
uploadModifiedFile
};

View File

@@ -264,7 +264,6 @@ function register(app) {
apiRoute(DELETE, '/api/notes/:noteId/revisions/:noteRevisionId', noteRevisionsApiRoute.eraseNoteRevision);
route(GET, '/api/notes/:noteId/revisions/:noteRevisionId/download', [auth.checkApiAuthOrElectron], noteRevisionsApiRoute.downloadNoteRevision);
apiRoute(PUT, '/api/notes/:noteId/restore-revision/:noteRevisionId', noteRevisionsApiRoute.restoreNoteRevision);
apiRoute(GET, '/api/notes/:noteId/backlink-count', notesApiRoute.getBacklinkCount);
apiRoute(POST, '/api/notes/relation-map', notesApiRoute.getRelationMap);
apiRoute(POST, '/api/notes/erase-deleted-notes-now', notesApiRoute.eraseDeletedNotesNow);
apiRoute(PUT, '/api/notes/:noteId/title', notesApiRoute.changeTitle);
@@ -306,6 +305,7 @@ function register(app) {
apiRoute(POST, '/api/note-map/:noteId/tree', noteMapRoute.getTreeMap);
apiRoute(POST, '/api/note-map/:noteId/link', noteMapRoute.getLinkMap);
apiRoute(GET, '/api/note-map/:noteId/backlink-count', noteMapRoute.getBacklinkCount);
apiRoute(GET, '/api/note-map/:noteId/backlinks', noteMapRoute.getBacklinks);
apiRoute(GET, '/api/special-notes/inbox/:date', specialNotesRoute.getInboxNote);

View File

@@ -28,11 +28,17 @@ function installLocalAppIcon() {
return;
}
if (!fs.existsSync(path.resolve(ELECTRON_APP_ROOT_DIR, "trilium-portable.sh"))) {
// simple heuristic to detect ".tar.xz" linux build (i.e. not flatpak, not debian)
// only in such case it's necessary to create an icon
return;
}
const desktopDir = path.resolve(os.homedir(), '.local/share/applications');
fs.stat(desktopDir, function (err, stats) {
if (err) {
// Directory doesn't exist so we won't attempt to create the .desktop file
// Directory doesn't exist, so we won't attempt to create the .desktop file
return;
}
@@ -64,4 +70,4 @@ function getExePath() {
module.exports = {
installLocalAppIcon
};
};

View File

@@ -1 +1 @@
module.exports = { buildDate:"2022-10-15T12:49:56+02:00", buildRevision: "70c929241391a465a1e818dcb41af30dd176bc34" };
module.exports = { buildDate:"2022-10-22T23:07:24+02:00", buildRevision: "c1127ec4293143e9ff6080cbe9c14a8366a132bd" };

View File

@@ -40,6 +40,10 @@ function disableEntityEvents() {
namespace.set('disableEntityEvents', true);
}
function enableEntityEvents() {
namespace.set('disableEntityEvents', false);
}
function isEntityEventsDisabled() {
return !!namespace.get('disableEntityEvents');
}
@@ -83,6 +87,7 @@ module.exports = {
getComponentId,
getLocalNowDateTime,
disableEntityEvents,
enableEntityEvents,
isEntityEventsDisabled,
reset,
getAndClearEntityChangeIds,

View File

@@ -33,10 +33,6 @@ function getNewNotePosition(parentNoteId) {
return maxNotePos + 10;
}
function triggerChildNoteCreated(childNote, parentNote) {
eventService.emit(eventService.CHILD_NOTE_CREATED, { childNote, parentNote });
}
function triggerNoteTitleChanged(note) {
eventService.emit(eventService.NOTE_TITLE_CHANGED, note);
}
@@ -140,24 +136,43 @@ function createNewNote(params) {
}
return sql.transactional(() => {
const note = new Note({
noteId: params.noteId, // optionally can force specific noteId
title: params.title,
isProtected: !!params.isProtected,
type: params.type,
mime: deriveMime(params.type, params.mime)
}).save();
let note, branch, isEntityEventsDisabled;
note.setContent(params.content);
try {
isEntityEventsDisabled = cls.isEntityEventsDisabled();
const branch = new Branch({
branchId: params.branchId,
noteId: note.noteId,
parentNoteId: params.parentNoteId,
notePosition: params.notePosition !== undefined ? params.notePosition : getNewNotePosition(params.parentNoteId),
prefix: params.prefix,
isExpanded: !!params.isExpanded
}).save();
if (!isEntityEventsDisabled) {
// it doesn't make sense to run note creation events on a partially constructed note, so
// defer them until note creation is completed
cls.disableEntityEvents();
}
note = new Note({
noteId: params.noteId, // optionally can force specific noteId
title: params.title,
isProtected: !!params.isProtected,
type: params.type,
mime: deriveMime(params.type, params.mime)
}).save();
note.setContent(params.content);
branch = new Branch({
branchId: params.branchId,
noteId: note.noteId,
parentNoteId: params.parentNoteId,
notePosition: params.notePosition !== undefined ? params.notePosition : getNewNotePosition(params.parentNoteId),
prefix: params.prefix,
isExpanded: !!params.isExpanded
}).save();
}
finally {
if (!isEntityEventsDisabled) {
// re-enable entity events only if there were previously enabled
// (they can be disabled in case of import)
cls.enableEntityEvents();
}
}
scanForLinks(note);
@@ -175,7 +190,21 @@ function createNewNote(params) {
}
triggerNoteTitleChanged(note);
triggerChildNoteCreated(note, parentNote);
eventService.emit(eventService.ENTITY_CREATED, {
entityName: 'notes',
entity: note
});
eventService.emit(eventService.ENTITY_CREATED, {
entityName: 'branches',
entity: branch
});
eventService.emit(eventService.CHILD_NOTE_CREATED, {
childNote: note,
parentNote: parentNote
});
log.info(`Created new note '${note.noteId}', branch '${branch.branchId}' of type '${note.type}', mime '${note.mime}'`);

View File

@@ -39,12 +39,25 @@ function getFulltext(tokens, searchContext) {
}
}
const OPERATORS = [
"=",
"!=",
"*=*",
"*=",
"=*",
">",
">=",
"<",
"<=",
"%="
];
function isOperator(token) {
if (Array.isArray(token)) {
return false;
}
return token.token.match(/^[!=<>*%]+$/);
return OPERATORS.includes(token.token);
}
function getExpression(tokens, searchContext, level = 0) {