diff --git a/packages/app-cli/tests/MdToHtml.ts b/packages/app-cli/tests/MdToHtml.ts
index 05efface08..67150002ea 100644
--- a/packages/app-cli/tests/MdToHtml.ts
+++ b/packages/app-cli/tests/MdToHtml.ts
@@ -66,8 +66,10 @@ describe('MdToHtml', () => {
actualHtml,
'--------------------------------- Raw:',
actualHtml.split('\n'),
- '--------------------------------- Expected:',
+ '--------------------------------- Expected (Lines)',
expectedHtml.split('\n'),
+ '--------------------------------- Expected (Text)',
+ expectedHtml,
'--------------------------------------------',
'',
];
diff --git a/packages/app-cli/tests/md_to_html/sanitize_links.html b/packages/app-cli/tests/md_to_html/sanitize_links.html
new file mode 100644
index 0000000000..59bb8c0a3c
--- /dev/null
+++ b/packages/app-cli/tests/md_to_html/sanitize_links.html
@@ -0,0 +1,7 @@
+
Resource link
+ok
+ok
+ok
+ok
+not ok
+not ok
diff --git a/packages/app-cli/tests/md_to_html/sanitize_links.md b/packages/app-cli/tests/md_to_html/sanitize_links.md
new file mode 100644
index 0000000000..1f5d67c85c
--- /dev/null
+++ b/packages/app-cli/tests/md_to_html/sanitize_links.md
@@ -0,0 +1,13 @@
+Resource link
+
+ok
+
+ok
+
+ok
+
+ok
+
+not ok
+
+not ok
diff --git a/packages/app-desktop/gui/NoteEditor/NoteBody/CodeMirror/v5/CodeMirror.tsx b/packages/app-desktop/gui/NoteEditor/NoteBody/CodeMirror/v5/CodeMirror.tsx
index eff98e671d..58bd44a1b0 100644
--- a/packages/app-desktop/gui/NoteEditor/NoteBody/CodeMirror/v5/CodeMirror.tsx
+++ b/packages/app-desktop/gui/NoteEditor/NoteBody/CodeMirror/v5/CodeMirror.tsx
@@ -136,7 +136,11 @@ function CodeMirror(props: NoteBodyEditorProps, ref: ForwardedRef {
@@ -255,7 +259,7 @@ function CodeMirror(props: NoteBodyEditorProps, ref: ForwardedRef {
const resourceMds = await getResourcesFromPasteEvent(event);
diff --git a/packages/app-desktop/gui/NoteEditor/NoteBody/CodeMirror/v6/CodeMirror.tsx b/packages/app-desktop/gui/NoteEditor/NoteBody/CodeMirror/v6/CodeMirror.tsx
index 5a65451f5b..2c2dd24b3f 100644
--- a/packages/app-desktop/gui/NoteEditor/NoteBody/CodeMirror/v6/CodeMirror.tsx
+++ b/packages/app-desktop/gui/NoteEditor/NoteBody/CodeMirror/v6/CodeMirror.tsx
@@ -151,6 +151,7 @@ const CodeMirror = (props: NoteBodyEditorProps, ref: ForwardedRef {
diff --git a/packages/app-desktop/gui/NoteEditor/NoteBody/CodeMirror/v6/useEditorCommands.ts b/packages/app-desktop/gui/NoteEditor/NoteBody/CodeMirror/v6/useEditorCommands.ts
index e415449e12..cc43677e12 100644
--- a/packages/app-desktop/gui/NoteEditor/NoteBody/CodeMirror/v6/useEditorCommands.ts
+++ b/packages/app-desktop/gui/NoteEditor/NoteBody/CodeMirror/v6/useEditorCommands.ts
@@ -7,6 +7,7 @@ import dialogs from '../../../../dialogs';
import { EditorCommandType } from '@joplin/editor/types';
import Logger from '@joplin/utils/Logger';
import CodeMirrorControl from '@joplin/editor/CodeMirror/CodeMirrorControl';
+import { MarkupLanguage } from '@joplin/renderer';
const logger = Logger.create('CodeMirror 6 commands');
@@ -40,6 +41,7 @@ interface Props {
selectionRange: { from: number; to: number };
visiblePanes: string[];
+ contentMarkupLanguage: MarkupLanguage;
}
const useEditorCommands = (props: Props) => {
@@ -57,7 +59,7 @@ const useEditorCommands = (props: Props) => {
editorRef.current.insertText(cmd.markdownTags.join('\n'));
} else if (cmd.type === 'files') {
const pos = props.selectionRange.from;
- const newBody = await commandAttachFileToBody(props.editorContent, cmd.paths, { createFileURL: !!cmd.createFileURL, position: pos });
+ const newBody = await commandAttachFileToBody(props.editorContent, cmd.paths, { createFileURL: !!cmd.createFileURL, position: pos, markupLanguage: props.contentMarkupLanguage });
editorRef.current.updateBody(newBody);
} else {
logger.warn('CodeMirror: unsupported drop item: ', cmd);
@@ -92,7 +94,7 @@ const useEditorCommands = (props: Props) => {
insertText: (value: any) => editorRef.current.insertText(value),
attachFile: async () => {
const newBody = await commandAttachFileToBody(
- props.editorContent, null, { position: props.selectionRange.from },
+ props.editorContent, null, { position: props.selectionRange.from, markupLanguage: props.contentMarkupLanguage },
);
if (newBody) {
editorRef.current.updateBody(newBody);
@@ -129,7 +131,7 @@ const useEditorCommands = (props: Props) => {
}, [
props.visiblePanes, props.editorContent, props.editorCopyText, props.editorCutText, props.editorPaste,
props.selectionRange,
-
+ props.contentMarkupLanguage,
props.webviewRef, editorRef,
]);
};
diff --git a/packages/app-desktop/gui/NoteEditor/editorCommandDeclarations.ts b/packages/app-desktop/gui/NoteEditor/editorCommandDeclarations.ts
index 34b38512b6..a076b5822e 100644
--- a/packages/app-desktop/gui/NoteEditor/editorCommandDeclarations.ts
+++ b/packages/app-desktop/gui/NoteEditor/editorCommandDeclarations.ts
@@ -2,9 +2,14 @@ import { CommandDeclaration } from '@joplin/lib/services/CommandService';
import { _ } from '@joplin/lib/locale';
import { joplinCommandToTinyMceCommands } from './NoteBody/TinyMCE/utils/joplinCommandToTinyMceCommands';
+const workWithHtmlNotes = [
+ 'attachFile',
+];
+
export const enabledCondition = (commandName: string) => {
const markdownEditorOnly = !Object.keys(joplinCommandToTinyMceCommands).includes(commandName);
- return `(!modalDialogVisible || gotoAnythingVisible) ${markdownEditorOnly ? '&& markdownEditorPaneVisible' : ''} && oneNoteSelected && noteIsMarkdown && !noteIsReadOnly`;
+ const noteMustBeMarkdown = !workWithHtmlNotes.includes(commandName);
+ return `(!modalDialogVisible || gotoAnythingVisible) ${markdownEditorOnly ? '&& markdownEditorPaneVisible' : ''} && oneNoteSelected ${noteMustBeMarkdown ? '&& noteIsMarkdown' : ''} && !noteIsReadOnly`;
};
const declarations: CommandDeclaration[] = [
diff --git a/packages/app-desktop/gui/NoteEditor/utils/resourceHandling.ts b/packages/app-desktop/gui/NoteEditor/utils/resourceHandling.ts
index 1f1e4b8bf4..efdefced03 100644
--- a/packages/app-desktop/gui/NoteEditor/utils/resourceHandling.ts
+++ b/packages/app-desktop/gui/NoteEditor/utils/resourceHandling.ts
@@ -9,6 +9,7 @@ import htmlUtils from '@joplin/lib/htmlUtils';
import rendererHtmlUtils, { extractHtmlBody } from '@joplin/renderer/htmlUtils';
import Logger from '@joplin/utils/Logger';
import { fileUriToPath } from '@joplin/utils/url';
+import { MarkupLanguage } from '@joplin/renderer';
const joplinRendererUtils = require('@joplin/renderer').utils;
const { clipboard } = require('electron');
const mimeUtils = require('@joplin/lib/mime-utils.js').mime;
@@ -62,6 +63,7 @@ export async function commandAttachFileToBody(body: string, filePaths: string[]
options = {
createFileURL: false,
position: 0,
+ markupLanguage: MarkupLanguage.Markdown,
...options,
};
@@ -79,6 +81,7 @@ export async function commandAttachFileToBody(body: string, filePaths: string[]
const newBody = await shim.attachFileToNoteBody(body, filePath, options.position, {
createFileURL: options.createFileURL,
resizeLargeImages: Setting.value('imageResizing'),
+ markupLanguage: options.markupLanguage,
});
if (!newBody) {
diff --git a/packages/app-mobile/components/screens/Note.tsx b/packages/app-mobile/components/screens/Note.tsx
index ac6a3ea354..17835a1c31 100644
--- a/packages/app-mobile/components/screens/Note.tsx
+++ b/packages/app-mobile/components/screens/Note.tsx
@@ -727,7 +727,7 @@ class NoteScreenComponent extends BaseScreenComponent {
resource = await Resource.save(resource, { isNew: true });
- const resourceTag = Resource.markdownTag(resource);
+ const resourceTag = Resource.markupTag(resource);
const newNote = { ...this.state.note };
diff --git a/packages/lib/models/Resource.ts b/packages/lib/models/Resource.ts
index 264222d86b..42316f1806 100644
--- a/packages/lib/models/Resource.ts
+++ b/packages/lib/models/Resource.ts
@@ -16,6 +16,8 @@ import itemCanBeEncrypted from './utils/itemCanBeEncrypted';
import { getEncryptionEnabled } from '../services/synchronizer/syncInfoUtils';
import ShareService from '../services/share/ShareService';
import { SaveOptions } from './utils/types';
+import { MarkupLanguage } from '@joplin/renderer';
+import { htmlentities } from '@joplin/utils/html';
export default class Resource extends BaseItem {
@@ -231,18 +233,28 @@ export default class Resource extends BaseItem {
return { path: encryptedPath, resource: resourceCopy };
}
- public static markdownTag(resource: any) {
+ public static markupTag(resource: any, markupLanguage: MarkupLanguage = MarkupLanguage.Markdown) {
let tagAlt = resource.alt ? resource.alt : resource.title;
if (!tagAlt) tagAlt = '';
const lines = [];
if (Resource.isSupportedImageMimeType(resource.mime)) {
- lines.push('`);
+ if (markupLanguage === MarkupLanguage.Markdown) {
+ lines.push('`);
+ } else {
+ const altHtml = tagAlt ? `alt="${htmlentities(tagAlt)}"` : '';
+ lines.push(`
`);
+ }
} else {
- lines.push('[');
- lines.push(markdownUtils.escapeTitleText(tagAlt));
- lines.push(`](:/${resource.id})`);
+ if (markupLanguage === MarkupLanguage.Markdown) {
+ lines.push('[');
+ lines.push(markdownUtils.escapeTitleText(tagAlt));
+ lines.push(`](:/${resource.id})`);
+ } else {
+ const altHtml = tagAlt ? `alt="${htmlentities(tagAlt)}"` : '';
+ lines.push(`${htmlentities(tagAlt ? tagAlt : resource.id)}`);
+ }
}
return lines.join('');
}
@@ -444,7 +456,7 @@ export default class Resource extends BaseItem {
await Note.save({
title: _('Attachment conflict: "%s"', resource.title),
- body: _('There was a [conflict](%s) on the attachment below.\n\n%s', 'https://joplinapp.org/help/apps/conflict', Resource.markdownTag(conflictResource)),
+ body: _('There was a [conflict](%s) on the attachment below.\n\n%s', 'https://joplinapp.org/help/apps/conflict', Resource.markupTag(conflictResource)),
parent_id: await this.resourceConflictFolderId(),
}, { changeSource: ItemChange.SOURCE_SYNC });
}
diff --git a/packages/lib/services/ResourceService.test.ts b/packages/lib/services/ResourceService.test.ts
index 3fa956e6dd..a9f65a5d78 100644
--- a/packages/lib/services/ResourceService.test.ts
+++ b/packages/lib/services/ResourceService.test.ts
@@ -63,7 +63,7 @@ describe('services/ResourceService', () => {
await service.indexNoteResources();
- await Note.save({ id: note2.id, body: Resource.markdownTag(resource1) });
+ await Note.save({ id: note2.id, body: Resource.markupTag(resource1) });
await service.indexNoteResources();
diff --git a/packages/lib/shim-init-node.js b/packages/lib/shim-init-node.js
index 0bc6b8fec6..a073aea825 100644
--- a/packages/lib/shim-init-node.js
+++ b/packages/lib/shim-init-node.js
@@ -332,7 +332,7 @@ function shimInit(options = null) {
};
shim.attachFileToNoteBody = async function(noteBody, filePath, position = null, options = null) {
- options = { createFileURL: false, ...options };
+ options = { createFileURL: false, markupLanguage: 1, ...options };
const { basename } = require('path');
const { escapeTitleText } = require('./markdownUtils').default;
@@ -353,7 +353,7 @@ function shimInit(options = null) {
if (noteBody && position) newBody.push(noteBody.substr(0, position));
if (!options.createFileURL) {
- newBody.push(Resource.markdownTag(resource));
+ newBody.push(Resource.markupTag(resource, options.markupLanguage));
} else {
const filename = escapeTitleText(basename(filePath)); // to get same filename as standard drag and drop
const fileURL = `[${filename}](${toFileProtocolPath(filePath)})`;
@@ -366,6 +366,7 @@ function shimInit(options = null) {
};
shim.attachFileToNote = async function(note, filePath, position = null, options = null) {
+ if (note.markup_language) options.markupLanguage = note.markup_language;
const newBody = await shim.attachFileToNoteBody(note.body, filePath, position, options);
if (!newBody) return null;
diff --git a/packages/renderer/htmlUtils.ts b/packages/renderer/htmlUtils.ts
index e608f3c913..e32b5312f4 100644
--- a/packages/renderer/htmlUtils.ts
+++ b/packages/renderer/htmlUtils.ts
@@ -159,12 +159,14 @@ class HtmlUtils {
.replace(/