Merge remote-tracking branch 'origin/stable' into syncification

# Conflicts:
#	package-lock.json
#	package.json
#	src/routes/custom.js
#	src/services/import/single.js
This commit is contained in:
zadam 2020-06-24 22:08:31 +02:00
commit a574fce13f
13 changed files with 250 additions and 163 deletions

View File

@ -1,19 +1,16 @@
/*
* !!!!!!! This stylesheet is heavily modified compared to the original for similarity with in-editor look !!!!!!!
* This is used for printing and tar HTML export
* CKEditor 5 (v17.0.0) content styles.
* Generated on Fri, 13 Mar 2020 13:27:10 GMT.
* CKEditor 5 (v19.1.1) content styles.
* Generated on Tue, 09 Jun 2020 10:37:49 GMT.
* For more information, check out https://ckeditor.com/docs/ckeditor5/latest/builds/guides/integration/content-styles.html
*/
:root {
--ck-highlight-marker-blue: #72cdfd;
--ck-highlight-marker-green: #63f963;
--ck-highlight-marker-pink: #fc7999;
--ck-highlight-marker-yellow: #fdfd77;
--ck-highlight-pen-green: #118800;
--ck-highlight-pen-red: #e91313;
--ck-highlight-marker-blue: hsl(201, 97%, 72%);
--ck-highlight-marker-green: hsl(120, 93%, 68%);
--ck-highlight-marker-pink: hsl(345, 96%, 73%);
--ck-highlight-marker-yellow: hsl(60, 97%, 73%);
--ck-highlight-pen-green: hsl(112, 100%, 27%);
--ck-highlight-pen-red: hsl(0, 85%, 49%);
--ck-image-style-spacing: 1.5em;
--ck-todo-list-checkmark-size: 16px;
}
@ -32,26 +29,23 @@
.ck-content .image.image_resized > figcaption {
display: block;
}
/* ckeditor5-image/theme/imagecaption.css */
.ck-content .image > figcaption {
display: table-caption;
caption-side: bottom;
word-break: break-word;
color: hsl(0, 0%, 20%);
background-color: hsl(0, 0%, 97%);
padding: .6em;
font-size: .75em;
outline-offset: -1px;
}
/* ckeditor5-basic-styles/theme/code.css */
.ck-content code {
background-color: hsla(0, 0%, 78%, 0.3);
padding: .15em;
border-radius: 2px;
}
/* ckeditor5-image/theme/image.css */
.ck-content .image {
display: table;
clear: both;
text-align: center;
margin: 1em auto;
}
/* ckeditor5-image/theme/image.css */
.ck-content .image > img {
display: block;
margin: 0 auto;
max-width: 100%;
min-width: 50px;
}
/* ckeditor5-image/theme/imagestyle.css */
.ck-content .image-style-side,
.ck-content .image-style-align-left,
@ -79,42 +73,6 @@
float: right;
margin-left: var(--ck-image-style-spacing);
}
/* ckeditor5-page-break/theme/pagebreak.css */
.ck-content .page-break {
position: relative;
clear: both;
padding: 5px 0;
display: flex;
align-items: center;
justify-content: center;
}
/* ckeditor5-page-break/theme/pagebreak.css */
.ck-content .page-break::after {
content: '';
position: absolute;
border-bottom: 2px dashed hsl(0, 0%, 77%);
width: 100%;
}
/* ckeditor5-page-break/theme/pagebreak.css */
.ck-content .page-break__label {
position: relative;
z-index: 1;
padding: .3em .6em;
display: block;
text-transform: uppercase;
border: 1px solid hsl(0, 0%, 77%);
border-radius: 2px;
font-family: Helvetica, Arial, Tahoma, Verdana, Sans-Serif;
font-size: 0.75em;
font-weight: bold;
color: hsl(0, 0%, 20%);
background: #fff;
box-shadow: 2px 2px 1px hsla(0, 0%, 0%, 0.15);
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
}
/* ckeditor5-block-quote/theme/blockquote.css */
.ck-content blockquote {
overflow: hidden;
@ -130,12 +88,46 @@
border-left: 0;
border-right: solid 5px hsl(0, 0%, 80%);
}
/* ckeditor5-media-embed/theme/mediaembed.css */
.ck-content .media {
/* ckeditor5-code-block/theme/codeblock.css */
.ck-content pre {
padding: 1em;
color: hsl(0, 0%, 20.8%);
background: hsla(0, 0%, 78%, 0.3);
border: 1px solid hsl(0, 0%, 77%);
border-radius: 2px;
text-align: left;
direction: ltr;
tab-size: 4;
white-space: pre-wrap;
font-style: normal;
min-width: 200px;
}
/* ckeditor5-code-block/theme/codeblock.css */
.ck-content pre code {
background: unset;
padding: 0;
border-radius: 0;
}
/* ckeditor5-horizontal-line/theme/horizontalline.css */
.ck-content hr {
border-width: 1px 0 0;
border-style: solid;
border-color: hsl(0, 0%, 37%);
margin: 0;
}
/* ckeditor5-image/theme/image.css */
.ck-content .image {
display: table;
clear: both;
margin: 1em 0;
text-align: center;
margin: 1em auto;
}
/* ckeditor5-image/theme/image.css */
.ck-content .image > img {
display: block;
min-width: 15em;
margin: 0 auto;
max-width: 100%;
min-width: 50px;
}
/* ckeditor5-table/theme/table.css */
.ck-content .table {
@ -155,13 +147,28 @@
.ck-content .table table th {
min-width: 2em;
padding: .4em;
border-color: hsl(0, 0%, 75%);
border: 1px solid hsl(0, 0%, 75%);
}
/* ckeditor5-table/theme/table.css */
.ck-content .table table th {
font-weight: bold;
background: hsla(0, 0%, 0%, 5%);
}
/* ckeditor5-table/theme/table.css */
.ck-content[dir="rtl"] .table th {
text-align: right;
}
/* ckeditor5-table/theme/table.css */
.ck-content[dir="ltr"] .table th {
text-align: left;
}
/* ckeditor5-media-embed/theme/mediaembed.css */
.ck-content .media {
clear: both;
margin: 1em 0;
display: block;
min-width: 15em;
}
/* ckeditor5-list/theme/todolist.css */
.ck-content .todo-list {
list-style: none;
@ -229,17 +236,6 @@
.ck-content .todo-list .todo-list__label .todo-list__label__description {
vertical-align: middle;
}
/* ckeditor5-image/theme/imagecaption.css */
.ck-content .image > figcaption {
display: table-caption;
caption-side: bottom;
word-break: break-word;
color: hsl(0, 0%, 20%);
background-color: hsl(0, 0%, 97%);
padding: .6em;
font-size: .75em;
outline-offset: -1px;
}
/* ckeditor5-highlight/theme/highlight.css */
.ck-content .marker-yellow {
background-color: var(--ck-highlight-marker-yellow);
@ -266,32 +262,57 @@
color: var(--ck-highlight-pen-green);
background-color: transparent;
}
/* ckeditor5-horizontal-line/theme/horizontalline.css */
.ck-content hr {
border-width: 1px 0 0;
border-style: solid;
border-color: hsl(0, 0%, 37%);
margin: 0;
/* ckeditor5-page-break/theme/pagebreak.css */
.ck-content .page-break {
position: relative;
clear: both;
padding: 5px 0;
display: flex;
align-items: center;
justify-content: center;
}
/* ckeditor5-code-block/theme/codeblock.css */
.ck-content pre {
padding: 1em;
color: #353535;
background: hsla(0, 0%, 78%, 0.3);
/* ckeditor5-page-break/theme/pagebreak.css */
.ck-content .page-break::after {
content: '';
position: absolute;
border-bottom: 2px dashed hsl(0, 0%, 77%);
width: 100%;
}
/* ckeditor5-page-break/theme/pagebreak.css */
.ck-content .page-break__label {
position: relative;
z-index: 1;
padding: .3em .6em;
display: block;
text-transform: uppercase;
border: 1px solid hsl(0, 0%, 77%);
border-radius: 2px;
text-align: left;
direction: ltr;
tab-size: 4;
white-space: pre-wrap;
font-style: normal;
min-width: 200px;
font-family: Helvetica, Arial, Tahoma, Verdana, Sans-Serif;
font-size: 0.75em;
font-weight: bold;
color: hsl(0, 0%, 20%);
background: hsl(0, 0%, 100%);
box-shadow: 2px 2px 1px hsla(0, 0%, 0%, 0.15);
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
}
/* ckeditor5-code-block/theme/codeblock.css */
.ck-content pre code {
background: unset;
padding: 0;
border-radius: 0;
/* ckeditor5-font/theme/fontsize.css */
.ck-content .text-tiny {
font-size: .7em;
}
/* ckeditor5-font/theme/fontsize.css */
.ck-content .text-small {
font-size: .85em;
}
/* ckeditor5-font/theme/fontsize.css */
.ck-content .text-big {
font-size: 1.4em;
}
/* ckeditor5-font/theme/fontsize.css */
.ck-content .text-huge {
font-size: 1.8em;
}
@media print {
/* ckeditor5-page-break/theme/pagebreak.css */
@ -303,3 +324,9 @@
display: none;
}
}
/* !!!!!! TRILIUM CUSTOM CHANGES !!!!!! */
.ck-widget__type-around { /* gets rid of triangles: https://github.com/zadam/trilium/issues/1129 */
display: none;
}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -64,8 +64,19 @@ function assertArguments() {
}
}
const entityMap = {
'&': '&',
'<': '&lt;',
'>': '&gt;',
'"': '&quot;',
"'": '&#39;',
'/': '&#x2F;',
'`': '&#x60;',
'=': '&#x3D;'
};
function escapeHtml(str) {
return $('<div/>').text(str).html();
return str.replace(/[&<>"'`=\/]/g, s => entityMap[s]);
}
async function stopWatch(what, func) {

View File

@ -249,9 +249,39 @@ export default class NoteTreeWidget extends TabAwareWidget {
this.initialized = this.initFancyTree();
this.setupNoteTitleTooltip();
return this.$widget;
}
setupNoteTitleTooltip() {
// the following will dynamically set tree item's tooltip if the whole item's text is not currently visible
// if the whole text is visible then no tooltip is show since that's unnecessarily distracting
// see https://github.com/zadam/trilium/pull/1120 for discussion
// code inspired by https://gist.github.com/jtsternberg/c272d7de5b967cec2d3d
const isEnclosing = ($container, $sub) => {
const conOffset = $container.offset();
const conDistanceFromTop = conOffset.top + $container.outerHeight(true);
const conDistanceFromLeft = conOffset.left + $container.outerWidth(true);
const subOffset = $sub.offset();
const subDistanceFromTop = subOffset.top + $sub.outerHeight(true);
const subDistanceFromLeft = subOffset.left + $sub.outerWidth(true);
return conDistanceFromTop > subDistanceFromTop
&& conOffset.top < subOffset.top
&& conDistanceFromLeft > subDistanceFromLeft
&& conOffset.left < subOffset.left;
};
this.$tree.on("mouseenter", "span.fancytree-title", e => {
e.currentTarget.title = isEnclosing(this.$tree, $(e.currentTarget))
? ""
: e.currentTarget.innerText;
});
}
get hideArchivedNotes() {
return options.is("hideArchivedNotes_" + this.treeName);
}
@ -751,7 +781,7 @@ export default class NoteTreeWidget extends TabAwareWidget {
utils.assertArguments(notePath);
const hoistedNoteId = hoistedNoteService.getHoistedNoteId();
/** @var {FancytreeNode} */
/** @const {FancytreeNode} */
let parentNode = null;
const runPath = await treeService.getRunPath(notePath, logErrors);
@ -836,13 +866,14 @@ export default class NoteTreeWidget extends TabAwareWidget {
const branch = treeCache.getBranch(node.data.branchId);
const isFolder = this.isFolder(note);
const title = (branch.prefix ? (branch.prefix + " - ") : "") + note.title;
node.data.isProtected = note.isProtected;
node.data.noteType = note.type;
node.folder = isFolder;
node.icon = this.getIcon(note, isFolder);
node.extraClasses = this.getExtraClasses(note);
node.title = (branch.prefix ? (branch.prefix + " - ") : "") + note.title;
node.title = utils.escapeHtml(title);
if (node.isExpanded() !== branch.isExpanded) {
node.setExpanded(branch.isExpanded, {noEvents: true});

View File

@ -164,10 +164,16 @@ function changeTitle(req) {
return [400, `Note ${noteId} is not available for change`];
}
const noteTitleChanged = note.title !== title;
note.title = title;
note.save();
if (noteTitleChanged) {
await noteService.triggerNoteTitleChanged(note);
}
return note;
}

View File

@ -2,64 +2,73 @@ const repository = require('../services/repository');
const log = require('../services/log');
const fileUploadService = require('./api/files.js');
const scriptService = require('../services/script');
const cls = require('../services/cls');
async function handleRequest(req, res) {
// express puts content after first slash into 0 index element
const path = req.params.path + req.params[0];
const attrs = repository.getEntities("SELECT * FROM attributes WHERE isDeleted = 0 AND type = 'label' AND name IN ('customRequestHandler', 'customResourceProvider')");
for (const attr of attrs) {
const regex = new RegExp(attr.value);
let match;
try {
match = path.match(regex);
}
catch (e) {
log.error(`Testing path for label ${attr.attributeId}, regex=${attr.value} failed with error ` + e.stack);
continue;
}
if (!match) {
continue;
}
if (attr.name === 'customRequestHandler') {
const note = attr.getNote();
log.info(`Handling custom request "${path}" with note ${note.noteId}`);
try {
scriptService.executeNote(note, {
pathParams: match.slice(1),
req,
res
});
}
catch (e) {
log.error(`Custom handler ${note.noteId} failed with ${e.message}`);
res.status(500).send(e.message);
}
}
else if (attr.name === 'customResourceProvider') {
fileUploadService.downloadNoteFile(attr.noteId, res);
}
else {
throw new Error("Unrecognized attribute name " + attr.name);
}
return; // only first handler is executed
}
const message = `No handler matched for custom ${path} request.`;
log.info(message);
res.status(404).send(message);
}
function register(router) {
// explicitly no CSRF middleware since it's meant to allow integration from external services
router.all('/custom/:path*', (req, res, next) => {
// express puts content after first slash into 0 index element
const path = req.params.path + req.params[0];
router.all('/custom/:path*', async (req, res, next) => {
cls.namespace.bindEmitter(req);
cls.namespace.bindEmitter(res);
const attrs = repository.getEntities("SELECT * FROM attributes WHERE isDeleted = 0 AND type = 'label' AND name IN ('customRequestHandler', 'customResourceProvider')");
for (const attr of attrs) {
const regex = new RegExp(attr.value);
let match;
try {
match = path.match(regex);
}
catch (e) {
log.error(`Testing path for label ${attr.attributeId}, regex=${attr.value} failed with error ` + e.stack);
continue;
}
if (!match) {
continue;
}
if (attr.name === 'customRequestHandler') {
const note = attr.getNote();
log.info(`Handling custom request "${path}" with note ${note.noteId}`);
try {
scriptService.executeNote(note, {
pathParams: match.slice(1),
req,
res
});
}
catch (e) {
log.error(`Custom handler ${note.noteId} failed with ${e.message}`);
res.status(500).send(e.message);
}
}
else if (attr.name === 'customResourceProvider') {
fileUploadService.downloadNoteFile(attr.noteId, res);
}
else {
throw new Error("Unrecognized attribute name " + attr.name);
}
return; // only first handler is executed
}
const message = `No handler matched for custom ${path} request.`;
log.info(message);
res.status(404).send(message);
cls.init(() => handleRequest(req, res));
});
}

View File

@ -1 +1 @@
module.exports = { buildDate:"2020-06-15T23:26:12+02:00", buildRevision: "9791dab97d9e86c4b02ca593198caffd1b72bbfb" };
module.exports = { buildDate:"2020-06-23T10:11:17+02:00", buildRevision: "89aa4fbc73fc6c357e04e037d6a1191fa4058d80" };

View File

@ -3,7 +3,6 @@
const html = require('html');
const repository = require('../repository');
const dateUtils = require('../date_utils');
const zip = require('tar-stream');
const path = require('path');
const mimeTypes = require('mime-types');
const mdService = require('./md');

View File

@ -68,6 +68,9 @@ eventService.subscribe(eventService.ENTITY_CREATED, ({ entityName, entity }) =>
note.setContent(targetNote.getContent());
}
else if (entity.type === 'label' && entity.name === 'sorted') {
await treeService.sortNotesAlphabetically(entity.noteId);
}
}
else if (entityName === 'notes') {
runAttachedRelations(entity, 'runOnNoteCreation', entity);

View File

@ -24,7 +24,7 @@ function importSingleFile(taskContext, file, parentNote) {
return importCodeNote(taskContext, file, parentNote);
}
if (["image/jpeg", "image/gif", "image/png", "image/webp"].includes(mime)) {
if (mime.startsWith("image/")) {
return importImage(file, parentNote, taskContext);
}

View File

@ -778,5 +778,6 @@ module.exports = {
protectNoteRecursively,
scanForLinks,
duplicateNote,
getUndeletedParentBranches
getUndeletedParentBranches,
triggerNoteTitleChanged
};