1
0
mirror of https://github.com/laurent22/joplin.git synced 2024-12-24 10:27:10 +02:00

Desktop: Fixes #5693: Opening a file with ctrl and click leads to an error in the Rich Text editor

This commit is contained in:
Laurent Cozic 2021-11-22 17:17:28 +00:00
parent 73737ce776
commit 0e11273c45
10 changed files with 359 additions and 191 deletions

View File

@ -23,9 +23,9 @@
"dependencyTree": "madge", "dependencyTree": "madge",
"generateDatabaseTypes": "node packages/tools/generate-database-types", "generateDatabaseTypes": "node packages/tools/generate-database-types",
"linkChecker": "linkchecker https://joplinapp.org", "linkChecker": "linkchecker https://joplinapp.org",
"linter-ci": "./node_modules/.bin/eslint --resolve-plugins-relative-to . --quiet --ext .js --ext .jsx --ext .ts --ext .tsx", "linter-ci": "eslint --resolve-plugins-relative-to . --quiet --ext .js --ext .jsx --ext .ts --ext .tsx",
"linter-precommit": "./node_modules/.bin/eslint --resolve-plugins-relative-to . --fix --ext .js --ext .jsx --ext .ts --ext .tsx", "linter-precommit": "eslint --resolve-plugins-relative-to . --fix --ext .js --ext .jsx --ext .ts --ext .tsx",
"linter": "./node_modules/.bin/eslint --resolve-plugins-relative-to . --fix --quiet --ext .js --ext .jsx --ext .ts --ext .tsx", "linter": "eslint --resolve-plugins-relative-to . --fix --quiet --ext .js --ext .jsx --ext .ts --ext .tsx",
"postinstall": "npm run bootstrap --no-ci && npm run build", "postinstall": "npm run bootstrap --no-ci && npm run build",
"publishAll": "git pull && npm run build && lerna version --yes --no-private --no-git-tag-version && gulp completePublishAll", "publishAll": "git pull && npm run build && lerna version --yes --no-private --no-git-tag-version && gulp completePublishAll",
"releaseAndroid": "npm run build && export PATH=\"/usr/local/opt/openjdk@11/bin:$PATH\" && node packages/tools/release-android.js", "releaseAndroid": "npm run build && export PATH=\"/usr/local/opt/openjdk@11/bin:$PATH\" && node packages/tools/release-android.js",

View File

@ -7,12 +7,12 @@ const bridge = require('@electron/remote').require('./bridge').default;
import ResourceFetcher from '@joplin/lib/services/ResourceFetcher'; import ResourceFetcher from '@joplin/lib/services/ResourceFetcher';
import htmlUtils from '@joplin/lib/htmlUtils'; import htmlUtils from '@joplin/lib/htmlUtils';
import Logger from '@joplin/lib/Logger'; import Logger from '@joplin/lib/Logger';
const { fileUriToPath } = require('@joplin/lib/urlUtils');
const joplinRendererUtils = require('@joplin/renderer').utils; const joplinRendererUtils = require('@joplin/renderer').utils;
const { clipboard } = require('electron'); const { clipboard } = require('electron');
const mimeUtils = require('@joplin/lib/mime-utils.js').mime; const mimeUtils = require('@joplin/lib/mime-utils.js').mime;
const md5 = require('md5'); const md5 = require('md5');
const path = require('path'); const path = require('path');
const uri2path = require('file-uri-to-path');
const logger = Logger.create('resourceHandling'); const logger = Logger.create('resourceHandling');
@ -150,7 +150,7 @@ export async function processPastedHtml(html: string) {
if (!mappedResources[imageSrc]) { if (!mappedResources[imageSrc]) {
try { try {
if (imageSrc.startsWith('file')) { if (imageSrc.startsWith('file')) {
const imageFilePath = path.normalize(uri2path(imageSrc)); const imageFilePath = path.normalize(fileUriToPath(imageSrc));
const resourceDirPath = path.normalize(Setting.value('resourceDir')); const resourceDirPath = path.normalize(Setting.value('resourceDir'));
if (imageFilePath.startsWith(resourceDirPath)) { if (imageFilePath.startsWith(resourceDirPath)) {

View File

@ -4,12 +4,12 @@ import contextMenu, { openItemById } from './contextMenu';
import { _ } from '@joplin/lib/locale'; import { _ } from '@joplin/lib/locale';
import CommandService from '@joplin/lib/services/CommandService'; import CommandService from '@joplin/lib/services/CommandService';
import PostMessageService from '@joplin/lib/services/PostMessageService'; import PostMessageService from '@joplin/lib/services/PostMessageService';
import ResourceFetcher from '@joplin/lib/services/ResourceFetcher';
import { reg } from '@joplin/lib/registry';
const bridge = require('@electron/remote').require('./bridge').default; const bridge = require('@electron/remote').require('./bridge').default;
const { urlDecode } = require('@joplin/lib/string-utils'); const { urlDecode } = require('@joplin/lib/string-utils');
const urlUtils = require('@joplin/lib/urlUtils'); const urlUtils = require('@joplin/lib/urlUtils');
import ResourceFetcher from '@joplin/lib/services/ResourceFetcher'; const { fileUriToPath } = require('@joplin/lib/urlUtils');
import { reg } from '@joplin/lib/registry';
const uri2path = require('file-uri-to-path');
export default function useMessageHandler(scrollWhenReady: any, setScrollWhenReady: Function, editorRef: any, setLocalSearchResultCount: Function, dispatch: Function, formNote: FormNote) { export default function useMessageHandler(scrollWhenReady: any, setScrollWhenReady: Function, editorRef: any, setLocalSearchResultCount: Function, dispatch: Function, formNote: FormNote) {
return useCallback(async (event: any) => { return useCallback(async (event: any) => {
@ -17,7 +17,7 @@ export default function useMessageHandler(scrollWhenReady: any, setScrollWhenRea
const args = event.args; const args = event.args;
const arg0 = args && args.length >= 1 ? args[0] : null; const arg0 = args && args.length >= 1 ? args[0] : null;
// if (msg !== 'percentScroll') console.info(`Got ipc-message: ${msg}`, arg0); if (msg !== 'percentScroll') console.info(`Got ipc-message: ${msg}`, arg0);
if (msg.indexOf('error:') === 0) { if (msg.indexOf('error:') === 0) {
const s = msg.split(':'); const s = msg.split(':');
@ -58,7 +58,7 @@ export default function useMessageHandler(scrollWhenReady: any, setScrollWhenRea
// shell.openPath seems to work with file:// urls on Windows, // shell.openPath seems to work with file:// urls on Windows,
// but doesn't on macOS, so we need to convert it to a path // but doesn't on macOS, so we need to convert it to a path
// before passing it to openPath. // before passing it to openPath.
const decodedPath = uri2path(urlDecode(msg)); const decodedPath = fileUriToPath(urlDecode(msg));
require('electron').shell.openPath(decodedPath); require('electron').shell.openPath(decodedPath);
} else { } else {
require('electron').shell.openExternal(msg); require('electron').shell.openExternal(msg);

File diff suppressed because it is too large Load Diff

View File

@ -146,7 +146,6 @@
"countable": "^3.0.1", "countable": "^3.0.1",
"debounce": "^1.2.0", "debounce": "^1.2.0",
"electron-window-state": "^4.1.1", "electron-window-state": "^4.1.1",
"file-uri-to-path": "^2.0.0",
"formatcoords": "^1.1.3", "formatcoords": "^1.1.3",
"fs-extra": "^5.0.0", "fs-extra": "^5.0.0",
"highlight.js": "^10.2.1", "highlight.js": "^10.2.1",

View File

@ -21,7 +21,7 @@
"diff-match-patch": "^1.0.4", "diff-match-patch": "^1.0.4",
"es6-promise-pool": "^2.5.0", "es6-promise-pool": "^2.5.0",
"fast-deep-equal": "^3.1.3", "fast-deep-equal": "^3.1.3",
"file-uri-to-path": "^1.0.0", "file-uri-to-path": "^2.0.0",
"follow-redirects": "^1.2.4", "follow-redirects": "^1.2.4",
"form-data": "^2.1.4", "form-data": "^2.1.4",
"fs-extra": "^5.0.0", "fs-extra": "^5.0.0",
@ -2955,8 +2955,12 @@
} }
}, },
"node_modules/file-uri-to-path": { "node_modules/file-uri-to-path": {
"version": "1.0.0", "version": "2.0.0",
"integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==" "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-2.0.0.tgz",
"integrity": "sha512-hjPFI8oE/2iQPVe4gbrJ73Pp+Xfub2+WI2LlXDbsaJBwT5wuMh35WNWVYYTpnz895shtwfyutMFLFywpQAFdLg==",
"engines": {
"node": ">= 6"
}
}, },
"node_modules/fill-range": { "node_modules/fill-range": {
"version": "7.0.1", "version": "7.0.1",
@ -10668,8 +10672,9 @@
"integrity": "sha512-uzk64HRpUZyTGZtVuvrjP0FYxzQrBf4rojot6J65YMEbwBLB0CWm0CLojVpwpmFmxcE/lkvYICgfcGozbBq6rw==" "integrity": "sha512-uzk64HRpUZyTGZtVuvrjP0FYxzQrBf4rojot6J65YMEbwBLB0CWm0CLojVpwpmFmxcE/lkvYICgfcGozbBq6rw=="
}, },
"file-uri-to-path": { "file-uri-to-path": {
"version": "1.0.0", "version": "2.0.0",
"integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==" "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-2.0.0.tgz",
"integrity": "sha512-hjPFI8oE/2iQPVe4gbrJ73Pp+Xfub2+WI2LlXDbsaJBwT5wuMh35WNWVYYTpnz895shtwfyutMFLFywpQAFdLg=="
}, },
"fill-range": { "fill-range": {
"version": "7.0.1", "version": "7.0.1",

View File

@ -46,7 +46,7 @@
"diff-match-patch": "^1.0.4", "diff-match-patch": "^1.0.4",
"es6-promise-pool": "^2.5.0", "es6-promise-pool": "^2.5.0",
"fast-deep-equal": "^3.1.3", "fast-deep-equal": "^3.1.3",
"file-uri-to-path": "^1.0.0", "file-uri-to-path": "^2.0.0",
"follow-redirects": "^1.2.4", "follow-redirects": "^1.2.4",
"form-data": "^2.1.4", "form-data": "^2.1.4",
"fs-extra": "^5.0.0", "fs-extra": "^5.0.0",

View File

@ -25,7 +25,7 @@ const urlUtils = require('../../../urlUtils.js');
const ArrayUtils = require('../../../ArrayUtils.js'); const ArrayUtils = require('../../../ArrayUtils.js');
const { mimeTypeFromHeaders } = require('../../../net-utils'); const { mimeTypeFromHeaders } = require('../../../net-utils');
const { fileExtension, safeFileExtension, safeFilename, filename } = require('../../../path-utils'); const { fileExtension, safeFileExtension, safeFilename, filename } = require('../../../path-utils');
const uri2path = require('file-uri-to-path'); const { fileUriToPath } = require('../../../urlUtils');
const { MarkupToHtml } = require('@joplin/renderer'); const { MarkupToHtml } = require('@joplin/renderer');
const { ErrorNotFound } = require('../utils/errors'); const { ErrorNotFound } = require('../utils/errors');
@ -178,7 +178,7 @@ async function downloadImage(url: string /* , allowFileProtocolImages */) {
} else if (urlUtils.urlProtocol(url).toLowerCase() === 'file:') { } else if (urlUtils.urlProtocol(url).toLowerCase() === 'file:') {
// Can't think of any reason to disallow this at this point // Can't think of any reason to disallow this at this point
// if (!allowFileProtocolImages) throw new Error('For security reasons, this URL with file:// protocol cannot be downloaded'); // if (!allowFileProtocolImages) throw new Error('For security reasons, this URL with file:// protocol cannot be downloaded');
const localPath = uri2path(url); const localPath = fileUriToPath(url);
await shim.fsDriver().copy(localPath, imagePath); await shim.fsDriver().copy(localPath, imagePath);
} else { } else {
const response = await shim.fetchBlob(url, { path: imagePath, maxRetry: 1 }); const response = await shim.fetchBlob(url, { path: imagePath, maxRetry: 1 });

View File

@ -1,5 +1,6 @@
const { rtrimSlashes } = require('./path-utils'); const { rtrimSlashes } = require('./path-utils');
const { urlDecode } = require('./string-utils'); const { urlDecode } = require('./string-utils');
const fileUriToPath = require('file-uri-to-path');
const urlUtils = {}; const urlUtils = {};
@ -105,4 +106,34 @@ urlUtils.objectToQueryString = function(query) {
return queryString; return queryString;
}; };
urlUtils.fileUriToPath = (path) => {
const output = fileUriToPath(path);
// The file-uri-to-path module converts Windows path such as
//
// file://c:/autoexec.bat => \\c:\autoexec.bat
//
// Probably because a file:// that starts with only two slashes is not
// quite valid. If we use three slashes, it works:
//
// file:///c:/autoexec.bat => c:\autoexec.bat
//
// However there are various places in the app where we can find
// paths with only two slashes because paths are often constructed
// as `file://${resourcePath}` - which works in all OSes except
// Windows.
//
// So here we introduce a special case - if we detect that we have
// an invalid Windows path that starts with \\x:, we just remove
// the first two backslashes.
//
// https://github.com/laurent22/joplin/issues/5693
if (output.match(/^\\\\[a-zA-Z]:/)) {
return output.substr(2);
}
return output;
};
module.exports = urlUtils; module.exports = urlUtils;

View File

@ -71,4 +71,13 @@ describe('urlUtils', function() {
} }
})); }));
it('should convert a file URI to a file path', (async () => {
// This function is a wrapper around the file-uri-to-path module,
// with a fix for the Windows paths. We assume that the wrapped
// function works so we don't test everything, only the cases
// specific to our wrapper.
expect(urlUtils.fileUriToPath('file://c:/not/quite/right')).toBe('c:\\not\\quite\\right');
expect(urlUtils.fileUriToPath('file:///d:/better')).toBe('d:\\better');
}));
}); });