Merge remote-tracking branch 'origin/stable'

# Conflicts:
#	src/public/app/services/note_content_renderer.js
This commit is contained in:
zadam 2020-11-08 21:06:48 +01:00
commit 1403acd808
22 changed files with 6102 additions and 5954 deletions

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 912 KiB

After

Width:  |  Height:  |  Size: 952 KiB

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -3,6 +3,7 @@ import utils from "../services/utils.js";
import ws from "../services/ws.js";
import toastService from "../services/toast.js";
import treeCache from "../services/tree_cache.js";
import openService from "../services/open.js";
const $dialog = $("#export-dialog");
const $form = $("#export-form");
@ -73,9 +74,9 @@ $form.on('submit', () => {
function exportBranch(branchId, type, format, version) {
taskId = utils.randomString(10);
const url = utils.getUrlForDownload(`api/notes/${branchId}/export/${type}/${format}/${version}/${taskId}`);
const url = openService.getUrlForDownload(`api/notes/${branchId}/export/${type}/${format}/${version}/${taskId}`);
utils.download(url);
openService.download(url);
}
$('input[name=export-type]').on('change', function () {
@ -133,4 +134,4 @@ ws.subscribeToMessages(async message => {
toastService.showPersistent(toast);
}
});
});

View File

@ -3,6 +3,7 @@ import server from '../services/server.js';
import toastService from "../services/toast.js";
import appContext from "../services/app_context.js";
import libraryLoader from "../services/library_loader.js";
import openService from "../services/open.js";
const $dialog = $("#note-revisions-dialog");
const $list = $("#note-revision-list");
@ -121,11 +122,7 @@ async function setContentPane() {
const $downloadButton = $('<button class="btn btn-sm btn-primary" type="button">Download</button>');
$downloadButton.on('click', () => {
const url = utils.getUrlForDownload(`api/notes/${revisionItem.noteId}/revisions/${revisionItem.noteRevisionId}/download`);
utils.download(url);
});
$downloadButton.on('click', () => openService.downloadNoteRevision(revisionItem.noteId, revisionItem.noteRevisionId));
$titleButtons.append($downloadButton);

View File

@ -81,7 +81,7 @@ export default class KeyboardShortcutsOptions {
.filter(shortcut => !!shortcut);
const opts = {};
opts['keyboardShortcuts' + actionName] = JSON.stringify(shortcuts);
opts['keyboardShortcuts' + actionName.substr(0, 1).toUpperCase() + actionName.substr(1)] = JSON.stringify(shortcuts);
server.put('options', opts);
});
@ -138,4 +138,4 @@ export default class KeyboardShortcutsOptions {
});
});
}
}
}

View File

@ -60,7 +60,7 @@ class NoteShort {
/** @param {string} content-type, e.g. "application/json" */
this.mime = row.mime;
/** @param {boolean} */
this.isDeleted = row.isDeleted;
this.isDeleted = !!row.isDeleted;
}
addParent(parentNoteId, branchId) {

View File

@ -1,9 +1,9 @@
import server from "./server.js";
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";
import openService from "./open.js";
async function getRenderedContent(note, options = {}) {
options = Object.assign({
@ -36,24 +36,11 @@ async function getRenderedContent(note, options = {}) {
.css("max-width", "100%");
}
else if (type === 'file' || type === 'pdf') {
function getFileUrl() {
return utils.getUrlForDownload(`api/notes/${note.noteId}/download`);
}
const $downloadButton = $('<button class="file-download btn btn-primary" type="button">Download</button>');
const $openButton = $('<button class="file-open btn btn-primary" type="button">Open</button>');
$downloadButton.on('click', () => utils.download(getFileUrl()));
$openButton.on('click', () => {
if (utils.isElectron()) {
const open = utils.dynamicRequire("open");
open(getFileUrl(), {url: true});
}
else {
window.location.href = getFileUrl();
}
});
$downloadButton.on('click', () => openService.downloadFileNote(note.noteId));
$openButton.on('click', () => openService.openFileNote(note.noteId));
// open doesn't work for protected notes since it works through browser which isn't in protected session
$openButton.toggle(!note.isProtected);
@ -62,7 +49,7 @@ async function getRenderedContent(note, options = {}) {
if (type === 'pdf') {
const $pdfPreview = $('<iframe class="pdf-preview" style="width: 100%; flex-grow: 100;"></iframe>');
$pdfPreview.attr("src", utils.getUrlForDownload(`api/notes/${note.noteId}/open`));
$pdfPreview.attr("src", openService.getUrlForDownload("api/notes/" + note.noteId + "/open"));
$rendered.append($pdfPreview);
}

View File

@ -0,0 +1,71 @@
import utils from "./utils.js";
import server from "./server.js";
function getFileUrl(noteId) {
return getUrlForDownload("api/notes/" + noteId + "/download");
}
function download(url) {
if (utils.isElectron()) {
const remote = utils.dynamicRequire('electron').remote;
remote.getCurrentWebContents().downloadURL(url);
} else {
window.location.href = url;
}
}
function downloadFileNote(noteId) {
const url = getFileUrl(noteId) + '?' + Date.now(); // don't use cache
download(url);
}
async function openFileNote(noteId) {
if (utils.isElectron()) {
const resp = await server.post("notes/" + noteId + "/saveToTmpDir");
const electron = utils.dynamicRequire('electron');
const res = await electron.shell.openPath(resp.tmpFilePath);
if (res) {
// fallback in case there's no default application for this file
open(getFileUrl(noteId), {url: true});
}
}
else {
window.location.href = getFileUrl(noteId);
}
}
function downloadNoteRevision(noteId, noteRevisionId) {
const url = getUrlForDownload(`api/notes/${noteId}/revisions/${noteRevisionId}/download`);
download(url);
}
/**
* @param url - should be without initial slash!!!
*/
function getUrlForDownload(url) {
if (utils.isElectron()) {
// electron needs absolute URL so we extract current host, port, protocol
return getHost() + '/' + url;
}
else {
// web server can be deployed on subdomain so we need to use relative path
return url;
}
}
function getHost() {
const url = new URL(window.location.href);
return url.protocol + "//" + url.hostname + ":" + url.port;
}
export default {
downloadFileNote,
openFileNote,
downloadNoteRevision,
getUrlForDownload
}

View File

@ -105,24 +105,6 @@ function formatLabel(label) {
return str;
}
function getHost() {
const url = new URL(window.location.href);
return url.protocol + "//" + url.hostname + ":" + url.port;
}
function download(url) {
url += '?' + Date.now(); // don't use cache
if (isElectron()) {
const remote = dynamicRequire('electron').remote;
remote.getCurrentWebContents().downloadURL(url);
}
else {
window.location.href = url;
}
}
function toObject(array, fn) {
const obj = {};
@ -294,20 +276,6 @@ async function clearBrowserCache() {
}
}
/**
* @param url - should be without initial slash!!!
*/
function getUrlForDownload(url) {
if (isElectron()) {
// electron needs absolute URL so we extract current host, port, protocol
return getHost() + '/' + url;
}
else {
// web server can be deployed on subdomain so we need to use relative path
return url;
}
}
function copySelectionToClipboard() {
const text = window.getSelection().toString();
if (navigator.clipboard) {
@ -366,7 +334,6 @@ export default {
escapeHtml,
stopWatch,
formatLabel,
download,
toObject,
randomString,
bindGlobalShortcut,
@ -384,7 +351,6 @@ export default {
focusSavedElement,
isHtmlEmpty,
clearBrowserCache,
getUrlForDownload,
normalizeShortcut,
copySelectionToClipboard,
isCKEditorInitialized,

View File

@ -1,4 +1,5 @@
import utils from "../../services/utils.js";
import openService from "../../services/open.js";
import server from "../../services/server.js";
import toastService from "../../services/toast.js";
import TypeWidget from "./type_widget.js";
@ -73,24 +74,8 @@ export default class FileTypeWidget extends TypeWidget {
this.$uploadNewRevisionButton = this.$widget.find(".file-upload-new-revision");
this.$uploadNewRevisionInput = this.$widget.find(".file-upload-new-revision-input");
this.$downloadButton.on('click', () => utils.download(this.getFileUrl()));
this.$openButton.on('click', async () => {
if (utils.isElectron()) {
const resp = await server.post("notes/" + this.noteId + "/saveToTmpDir");
const electron = utils.dynamicRequire('electron');
const res = await electron.shell.openPath(resp.tmpFilePath);
if (res) {
// fallback in case there's no default application for this file
open(this.getFileUrl(), {url: true});
}
}
else {
window.location.href = this.getFileUrl();
}
});
this.$downloadButton.on('click', () => openService.downloadFileNote(this.noteId));
this.$openButton.on('click', () => openService.openFileNote(this.noteId));
this.$uploadNewRevisionButton.on("click", () => {
this.$uploadNewRevisionInput.trigger("click");
@ -146,14 +131,10 @@ export default class FileTypeWidget extends TypeWidget {
}
else if (note.mime === 'application/pdf') {
this.$pdfPreview.show();
this.$pdfPreview.attr("src", utils.getUrlForDownload("api/notes/" + this.noteId + "/open"));
this.$pdfPreview.attr("src", openService.getUrlForDownload("api/notes/" + this.noteId + "/open"));
}
// open doesn't work for protected notes since it works through browser which isn't in protected session
this.$openButton.toggle(!note.isProtected);
}
getFileUrl() {
return utils.getUrlForDownload("api/notes/" + this.noteId + "/download");
}
}

View File

@ -1,6 +1,7 @@
import utils from "../../services/utils.js";
import toastService from "../../services/toast.js";
import server from "../../services/server.js";
import openService from "../../services/open.js";
import TypeWidget from "./type_widget.js";
const TPL = `
@ -64,7 +65,7 @@ class ImageTypeWidget extends TypeWidget {
this.$fileSize = this.$widget.find(".image-filesize");
this.$imageDownloadButton = this.$widget.find(".image-download");
this.$imageDownloadButton.on('click', () => utils.download(this.getFileUrl()));
this.$imageDownloadButton.on('click', () => openService.downloadFileNote(this.noteId));
this.$copyToClipboardButton.on('click',() => {
this.$imageWrapper.attr('contenteditable','true');
@ -145,10 +146,6 @@ class ImageTypeWidget extends TypeWidget {
selection.removeAllRanges();
selection.addRange(range);
}
getFileUrl() {
return utils.getUrlForDownload(`api/notes/${this.noteId}/download`);
}
}
export default ImageTypeWidget

View File

@ -59,7 +59,7 @@ ul.fancytree-container {
font-size: x-large;
text-transform: none;
line-height: 1;
content: "\ea1d";
content: "\e9b2";
position: relative;
top: -2px;
margin-right: 5px;
@ -72,7 +72,7 @@ ul.fancytree-container {
.fancytree-node.fancytree-expanded .fancytree-expander:before {
font-family: 'boxicons' !important;
content: "\ea17";
content: "\e9ac";
}
/** some common text styling for cssClass label */

View File

@ -12,6 +12,10 @@ async function handleRequest(req, res) {
const attrs = repository.getEntities("SELECT * FROM attributes WHERE isDeleted = 0 AND type = 'label' AND name IN ('customRequestHandler', 'customResourceProvider')");
for (const attr of attrs) {
if (!attr.value.trim()) {
continue;
}
const regex = new RegExp(attr.value);
let match;

View File

@ -365,7 +365,7 @@ const DEFAULT_KEYBOARD_ACTIONS = [
},
{
actionName: "openDevTools",
defaultShortcuts: ["CommandOrControl+Shift+I"],
defaultShortcuts: isElectron ? ["CommandOrControl+Shift+I"] : [],
scope: "window"
},
{
@ -408,13 +408,7 @@ for (const action of DEFAULT_KEYBOARD_ACTIONS) {
}
}
let cachedActions = null;
function getKeyboardActions() {
if (cachedActions) {
return cachedActions;
}
const actions = JSON.parse(JSON.stringify(DEFAULT_KEYBOARD_ACTIONS));
for (const action of actions) {
@ -442,8 +436,6 @@ function getKeyboardActions() {
}
}
cachedActions = actions;
return actions;
}

View File

@ -687,7 +687,7 @@ function eraseDeletedNotes() {
sql.executeMany(`
UPDATE notes
SET title = '[deleted]',
SET title = '[erased]',
isProtected = 0,
isErased = 1
WHERE noteId IN (???)`, noteIdsToErase);