mirror of
https://github.com/laurent22/joplin.git
synced 2025-01-02 12:47:41 +02:00
Desktop, Mobile: Fixed issue where file:// URLs would not be rendered correctly
This commit is contained in:
parent
34c1096307
commit
491714cde6
@ -1388,6 +1388,12 @@ packages/renderer/MdToHtml/rules/mermaid.js.map
|
|||||||
packages/renderer/MdToHtml/rules/sanitize_html.d.ts
|
packages/renderer/MdToHtml/rules/sanitize_html.d.ts
|
||||||
packages/renderer/MdToHtml/rules/sanitize_html.js
|
packages/renderer/MdToHtml/rules/sanitize_html.js
|
||||||
packages/renderer/MdToHtml/rules/sanitize_html.js.map
|
packages/renderer/MdToHtml/rules/sanitize_html.js.map
|
||||||
|
packages/renderer/MdToHtml/setupLinkify.d.ts
|
||||||
|
packages/renderer/MdToHtml/setupLinkify.js
|
||||||
|
packages/renderer/MdToHtml/setupLinkify.js.map
|
||||||
|
packages/renderer/MdToHtml/validateLinks.d.ts
|
||||||
|
packages/renderer/MdToHtml/validateLinks.js
|
||||||
|
packages/renderer/MdToHtml/validateLinks.js.map
|
||||||
packages/renderer/htmlUtils.d.ts
|
packages/renderer/htmlUtils.d.ts
|
||||||
packages/renderer/htmlUtils.js
|
packages/renderer/htmlUtils.js
|
||||||
packages/renderer/htmlUtils.js.map
|
packages/renderer/htmlUtils.js.map
|
||||||
|
6
.gitignore
vendored
6
.gitignore
vendored
@ -1377,6 +1377,12 @@ packages/renderer/MdToHtml/rules/mermaid.js.map
|
|||||||
packages/renderer/MdToHtml/rules/sanitize_html.d.ts
|
packages/renderer/MdToHtml/rules/sanitize_html.d.ts
|
||||||
packages/renderer/MdToHtml/rules/sanitize_html.js
|
packages/renderer/MdToHtml/rules/sanitize_html.js
|
||||||
packages/renderer/MdToHtml/rules/sanitize_html.js.map
|
packages/renderer/MdToHtml/rules/sanitize_html.js.map
|
||||||
|
packages/renderer/MdToHtml/setupLinkify.d.ts
|
||||||
|
packages/renderer/MdToHtml/setupLinkify.js
|
||||||
|
packages/renderer/MdToHtml/setupLinkify.js.map
|
||||||
|
packages/renderer/MdToHtml/validateLinks.d.ts
|
||||||
|
packages/renderer/MdToHtml/validateLinks.js
|
||||||
|
packages/renderer/MdToHtml/validateLinks.js.map
|
||||||
packages/renderer/htmlUtils.d.ts
|
packages/renderer/htmlUtils.d.ts
|
||||||
packages/renderer/htmlUtils.js
|
packages/renderer/htmlUtils.js
|
||||||
packages/renderer/htmlUtils.js.map
|
packages/renderer/htmlUtils.js.map
|
||||||
|
@ -54,5 +54,5 @@ module.exports = {
|
|||||||
|
|
||||||
testEnvironment: 'node',
|
testEnvironment: 'node',
|
||||||
setupFilesAfterEnv: [`${__dirname}/jest.setup.js`],
|
setupFilesAfterEnv: [`${__dirname}/jest.setup.js`],
|
||||||
slowTestThreshold: 20,
|
slowTestThreshold: 40,
|
||||||
};
|
};
|
||||||
|
@ -147,27 +147,86 @@ describe('MdToHtml', function() {
|
|||||||
expect(result.html.trim()).toBe('<div id="rendered-md"><p>just <strong>testing</strong></p>\n</div>');
|
expect(result.html.trim()).toBe('<div id="rendered-md"><p>just <strong>testing</strong></p>\n</div>');
|
||||||
}));
|
}));
|
||||||
|
|
||||||
// it('should render links correctly', (async () => {
|
it('should render links correctly', (async () => {
|
||||||
// const mdToHtml = newTestMdToHtml();
|
const testCases = [
|
||||||
|
// 0: input
|
||||||
|
// 1: output with linkify = off
|
||||||
|
// 2: output with linkify = on
|
||||||
|
[
|
||||||
|
'https://example.com',
|
||||||
|
'https://example.com',
|
||||||
|
'<a data-from-md title=\'https://example.com\' href=\'https://example.com\'>https://example.com</a>',
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'file://C:\\AUTOEXEC.BAT',
|
||||||
|
'file://C:\\AUTOEXEC.BAT',
|
||||||
|
'<a data-from-md title=\'file://C:%5CAUTOEXEC.BAT\' href=\'file://C:%5CAUTOEXEC.BAT\'>file://C:\\AUTOEXEC.BAT</a>',
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'example.com',
|
||||||
|
'example.com',
|
||||||
|
'example.com',
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'oo.ps',
|
||||||
|
'oo.ps',
|
||||||
|
'oo.ps',
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'test@example.com',
|
||||||
|
'test@example.com',
|
||||||
|
'test@example.com',
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'<https://example.com>',
|
||||||
|
'<a data-from-md title=\'https://example.com\' href=\'https://example.com\'>https://example.com</a>',
|
||||||
|
'<a data-from-md title=\'https://example.com\' href=\'https://example.com\'>https://example.com</a>',
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'[ok](https://example.com)',
|
||||||
|
'<a data-from-md title=\'https://example.com\' href=\'https://example.com\'>ok</a>',
|
||||||
|
'<a data-from-md title=\'https://example.com\' href=\'https://example.com\'>ok</a>',
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'[bla.pdf](file:///Users/tessus/Downloads/bla.pdf)',
|
||||||
|
'<a data-from-md title=\'file:///Users/tessus/Downloads/bla.pdf\' href=\'file:///Users/tessus/Downloads/bla.pdf\'>bla.pdf</a>',
|
||||||
|
'<a data-from-md title=\'file:///Users/tessus/Downloads/bla.pdf\' href=\'file:///Users/tessus/Downloads/bla.pdf\'>bla.pdf</a>',
|
||||||
|
],
|
||||||
|
];
|
||||||
|
|
||||||
// const testCases = [
|
const mdToHtmlLinkifyOn = newTestMdToHtml({
|
||||||
// // None of these should result in a link
|
pluginOptions: {
|
||||||
// ['https://example.com', 'https://example.com'],
|
linkify: { enabled: true },
|
||||||
// ['file://C:\\AUTOEXEC.BAT', 'file://C:\\AUTOEXEC.BAT'],
|
},
|
||||||
// ['example.com', 'example.com'],
|
});
|
||||||
// ['oo.ps', 'oo.ps'],
|
|
||||||
// ['test@example.com', 'test@example.com'],
|
|
||||||
|
|
||||||
// // Those should be converted to links
|
const mdToHtmlLinkifyOff = newTestMdToHtml({
|
||||||
// ['<https://example.com>', '<a data-from-md title=\'https://example.com\' href=\'https://example.com\'>https://example.com</a>'],
|
pluginOptions: {
|
||||||
// ['[ok](https://example.com)', '<a data-from-md title=\'https://example.com\' href=\'https://example.com\'>ok</a>'],
|
linkify: { enabled: false },
|
||||||
// ];
|
},
|
||||||
|
});
|
||||||
|
|
||||||
// for (const testCase of testCases) {
|
for (const testCase of testCases) {
|
||||||
// const [input, expected] = testCase;
|
const [input, expectedLinkifyOff, expectedLinkifyOn] = testCase;
|
||||||
// const actual = await mdToHtml.render(input, null, { bodyOnly: true, plainResourceRendering: true });
|
|
||||||
// expect(actual.html).toBe(expected);
|
{
|
||||||
// }
|
const actual = await mdToHtmlLinkifyOn.render(input, null, {
|
||||||
// }));
|
bodyOnly: true,
|
||||||
|
plainResourceRendering: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(actual.html).toBe(expectedLinkifyOn);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
const actual = await mdToHtmlLinkifyOff.render(input, null, {
|
||||||
|
bodyOnly: true,
|
||||||
|
plainResourceRendering: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(actual.html).toBe(expectedLinkifyOff);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
|
||||||
});
|
});
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
|
import { validateLinks } from '@joplin/renderer';
|
||||||
const stringPadding = require('string-padding');
|
const stringPadding = require('string-padding');
|
||||||
const urlUtils = require('./urlUtils');
|
const urlUtils = require('./urlUtils');
|
||||||
const MarkdownIt = require('markdown-it');
|
const MarkdownIt = require('markdown-it');
|
||||||
const { setupLinkify } = require('@joplin/renderer');
|
|
||||||
|
|
||||||
// Taken from codemirror/addon/edit/continuelist.js
|
// Taken from codemirror/addon/edit/continuelist.js
|
||||||
const listRegex = /^(\s*)([*+-] \[[x ]\]\s|[*+-]\s|(\d+)([.)]\s))(\s*)/;
|
const listRegex = /^(\s*)([*+-] \[[x ]\]\s|[*+-]\s|(\d+)([.)]\s))(\s*)/;
|
||||||
@ -47,7 +47,7 @@ const markdownUtils = {
|
|||||||
// Returns the **encoded** URLs, so to be useful they should be decoded again before use.
|
// Returns the **encoded** URLs, so to be useful they should be decoded again before use.
|
||||||
extractImageUrls(md: string) {
|
extractImageUrls(md: string) {
|
||||||
const markdownIt = new MarkdownIt();
|
const markdownIt = new MarkdownIt();
|
||||||
setupLinkify(markdownIt); // Necessary to support file:/// links
|
markdownIt.validateLink = validateLinks; // Necessary to support file:/// links
|
||||||
|
|
||||||
const env = {};
|
const env = {};
|
||||||
const tokens = markdownIt.parse(md, env);
|
const tokens = markdownIt.parse(md, env);
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
import InMemoryCache from './InMemoryCache';
|
import InMemoryCache from './InMemoryCache';
|
||||||
import noteStyle from './noteStyle';
|
import noteStyle from './noteStyle';
|
||||||
import { fileExtension } from './pathUtils';
|
import { fileExtension } from './pathUtils';
|
||||||
|
import setupLinkify from './MdToHtml/setupLinkify';
|
||||||
|
import validateLinks from './MdToHtml/validateLinks';
|
||||||
|
|
||||||
const MarkdownIt = require('markdown-it');
|
const MarkdownIt = require('markdown-it');
|
||||||
const md5 = require('md5');
|
const md5 = require('md5');
|
||||||
@ -41,7 +43,6 @@ const rules: RendererRules = {
|
|||||||
mermaid: require('./MdToHtml/rules/mermaid').default,
|
mermaid: require('./MdToHtml/rules/mermaid').default,
|
||||||
};
|
};
|
||||||
|
|
||||||
const setupLinkify = require('./MdToHtml/setupLinkify');
|
|
||||||
const hljs = require('highlight.js');
|
const hljs = require('highlight.js');
|
||||||
const uslug = require('uslug');
|
const uslug = require('uslug');
|
||||||
const markdownItAnchor = require('markdown-it-anchor');
|
const markdownItAnchor = require('markdown-it-anchor');
|
||||||
@ -517,6 +518,8 @@ export default class MdToHtml {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
markdownIt.validateLink = validateLinks;
|
||||||
|
|
||||||
if (this.pluginEnabled('linkify')) setupLinkify(markdownIt);
|
if (this.pluginEnabled('linkify')) setupLinkify(markdownIt);
|
||||||
|
|
||||||
const renderedBody = markdownIt.render(body, context);
|
const renderedBody = markdownIt.render(body, context);
|
||||||
|
@ -1,39 +0,0 @@
|
|||||||
module.exports = function(markdownIt) {
|
|
||||||
// Add `file:` protocol in linkify to allow text in the format of "file://..." to translate into
|
|
||||||
// file-URL links in html view
|
|
||||||
markdownIt.linkify.add('file:', {
|
|
||||||
validate: function(text, pos, self) {
|
|
||||||
const tail = text.slice(pos);
|
|
||||||
if (!self.re.file) {
|
|
||||||
// matches all local file URI on Win/Unix/MacOS systems including reserved characters in some OS (i.e. no OS specific sanity check)
|
|
||||||
self.re.file = new RegExp('^[\\/]{2,3}[\\S]+');
|
|
||||||
}
|
|
||||||
if (self.re.file.test(tail)) {
|
|
||||||
return tail.match(self.re.file)[0].length;
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
// enable file link URLs in MarkdownIt. Keeps other URL restrictions of MarkdownIt untouched.
|
|
||||||
// Format [link name](file://...)
|
|
||||||
markdownIt.validateLink = function(url) {
|
|
||||||
const BAD_PROTO_RE = /^(vbscript|javascript|data):/;
|
|
||||||
const GOOD_DATA_RE = /^data:image\/(gif|png|jpeg|webp);/;
|
|
||||||
|
|
||||||
// url should be normalized at this point, and existing entities are decoded
|
|
||||||
const str = url.trim().toLowerCase();
|
|
||||||
|
|
||||||
if (str.indexOf('data:image/svg+xml,') === 0) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return BAD_PROTO_RE.test(str) ? (GOOD_DATA_RE.test(str) ? true : false) : true;
|
|
||||||
};
|
|
||||||
|
|
||||||
markdownIt.linkify.set({
|
|
||||||
'fuzzyLink': false,
|
|
||||||
'fuzzyIP': false,
|
|
||||||
'fuzzyEmail': false,
|
|
||||||
});
|
|
||||||
};
|
|
23
packages/renderer/MdToHtml/setupLinkify.ts
Normal file
23
packages/renderer/MdToHtml/setupLinkify.ts
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
export default function(markdownIt: any) {
|
||||||
|
// Add `file:` protocol in linkify to allow text in the format of "file://..." to translate into
|
||||||
|
// file-URL links in html view
|
||||||
|
markdownIt.linkify.add('file:', {
|
||||||
|
validate: function(text: string, pos: number, self: any) {
|
||||||
|
const tail = text.slice(pos);
|
||||||
|
if (!self.re.file) {
|
||||||
|
// matches all local file URI on Win/Unix/MacOS systems including reserved characters in some OS (i.e. no OS specific sanity check)
|
||||||
|
self.re.file = new RegExp('^[\\/]{2,3}[\\S]+');
|
||||||
|
}
|
||||||
|
if (self.re.file.test(tail)) {
|
||||||
|
return tail.match(self.re.file)[0].length;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
markdownIt.linkify.set({
|
||||||
|
'fuzzyLink': false,
|
||||||
|
'fuzzyIP': false,
|
||||||
|
'fuzzyEmail': false,
|
||||||
|
});
|
||||||
|
}
|
15
packages/renderer/MdToHtml/validateLinks.ts
Normal file
15
packages/renderer/MdToHtml/validateLinks.ts
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
// enable file link URLs in MarkdownIt. Keeps other URL restrictions of MarkdownIt untouched.
|
||||||
|
// Format [link name](file://...)
|
||||||
|
export default function(url: string) {
|
||||||
|
const BAD_PROTO_RE = /^(vbscript|javascript|data):/;
|
||||||
|
const GOOD_DATA_RE = /^data:image\/(gif|png|jpeg|webp);/;
|
||||||
|
|
||||||
|
// url should be normalized at this point, and existing entities are decoded
|
||||||
|
const str = url.trim().toLowerCase();
|
||||||
|
|
||||||
|
if (str.indexOf('data:image/svg+xml,') === 0) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return BAD_PROTO_RE.test(str) ? (GOOD_DATA_RE.test(str) ? true : false) : true;
|
||||||
|
}
|
@ -2,7 +2,8 @@ import MarkupToHtml, { MarkupLanguage } from './MarkupToHtml';
|
|||||||
import MdToHtml from './MdToHtml';
|
import MdToHtml from './MdToHtml';
|
||||||
import HtmlToHtml from './HtmlToHtml';
|
import HtmlToHtml from './HtmlToHtml';
|
||||||
import utils from './utils';
|
import utils from './utils';
|
||||||
const setupLinkify = require('./MdToHtml/setupLinkify');
|
import setupLinkify from './MdToHtml/setupLinkify';
|
||||||
|
import validateLinks from './MdToHtml/validateLinks';
|
||||||
const assetsToHeaders = require('./assetsToHeaders');
|
const assetsToHeaders = require('./assetsToHeaders');
|
||||||
|
|
||||||
export {
|
export {
|
||||||
@ -11,6 +12,7 @@ export {
|
|||||||
MdToHtml,
|
MdToHtml,
|
||||||
HtmlToHtml,
|
HtmlToHtml,
|
||||||
setupLinkify,
|
setupLinkify,
|
||||||
|
validateLinks,
|
||||||
assetsToHeaders,
|
assetsToHeaders,
|
||||||
utils,
|
utils,
|
||||||
};
|
};
|
||||||
|
Loading…
Reference in New Issue
Block a user