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

# Conflicts:
#	package-lock.json
#	package.json
#	src/routes/custom.js
#	src/services/import/single.js
pull/255/head
zadam 2020-06-24 22:08:31 +07:00
commit a574fce13f
13 changed files with 239 additions and 152 deletions

@ -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 */
@ -302,4 +323,10 @@
.ck-content .page-break::after {
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

@ -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) {

@ -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});

@ -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;
}

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

@ -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" };

@ -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');

@ -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);

@ -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);
}

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