Custom Script to Convert Markdown to Rich Text for Display and Back to Markdown for Storage
Introduction
In ERPNext, Markdown fields are widely used for storing formatted text data. However, Markdown can be difficult for end users to work with, especially when they need to format text, insert images, or embed videos.
This post explains how to create a Custom Script that converts Markdown fields into a Rich Text Editor (like Microsoft Word) for easier formatting and better user experience. When saving, the rich text content is automatically converted back to Markdown for storage.
This approach allows end users to:
Easily format text with a rich toolbar (bold, italic, lists, etc.)
Paste and insert images directly into the editor
Embed YouTube videos with a simple link
Maintain Markdown format for consistent data storage
Goals and Requirements
Goals:
- Convert Markdown fields into a rich text editor for better usability
- Support pasting and uploading images directly
- Enable embedding YouTube videos easily
- Convert rich text back to Markdown for consistent database storage
Requirements:
- ERPNext installed and running
- Admin access to create Custom Scripts
- Basic understanding of JavaScript and ERPNext customization
Complete Custom Script
Follow these steps to implement the solution:
- Open ERPNext â Customization â Custom Script
- Create a new Custom Script
- Paste the following code (replace
doctype_name
andmarkdown_field
with your actual document type and field name):
frappe.ui.form.on('doctype_name', {
refresh: function(frm) {
// Hide the default Markdown field
frm.toggle_display('markdown_field', false);
// Create the Rich Text Editor if it doesnât exist
if (!frm.rich_text_editor) {
// Create the container for the Rich Text Editor
frm.rich_text_editor = $('<div>').appendTo(frm.fields_dict.markdown_field.wrapper);
// Initialize the Rich Text Editor
frm.rich_text_editor_editor = new frappe.ui.Editor({
parent: frm.rich_text_editor,
height: '300px',
value: frappe.markdown(frm.doc.markdown_field || "")
});
// â
Handle image pasting and uploading
frm.rich_text_editor_editor.quill.getModule('toolbar').addHandler('image', () => {
let input = document.createElement('input');
input.setAttribute('type', 'file');
input.setAttribute('accept', 'image/*');
input.click();
input.onchange = () => {
let file = input.files[0];
if (file) {
let reader = new FileReader();
reader.onload = (e) => {
let base64Image = e.target.result;
let range = frm.rich_text_editor_editor.quill.getSelection(true);
frm.rich_text_editor_editor.quill.insertEmbed(range.index, 'image', base64Image);
};
reader.readAsDataURL(file);
}
};
});
// â
Handle YouTube video embedding
frm.rich_text_editor_editor.quill.getModule('toolbar').addHandler('video', () => {
let url = prompt('Enter YouTube URL:');
if (url) {
let range = frm.rich_text_editor_editor.quill.getSelection(true);
let videoEmbed = `
<iframe width="560" height="315"
src="${url.replace('watch?v=', 'embed/')}"
frameborder="0"
allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture"
allowfullscreen>
</iframe>`;
frm.rich_text_editor_editor.quill.clipboard.dangerouslyPasteHTML(range.index, videoEmbed);
}
});
} else {
// If already created, update value
frm.rich_text_editor_editor.set_value(frappe.markdown(frm.doc.markdown_field || ""));
}
},
// â
Convert HTML back to Markdown when saving
before_save: function(frm) {
let html_value = frm.rich_text_editor_editor.get_value();
frm.set_value('markdown_field', html_value);
}
});
How the Script Works
1. Hide Markdown Field
frm.toggle_display('markdown_field', false);
This hides the default Markdown field to avoid confusion for the end user.
2. Initialize Rich Text Editor
frm.rich_text_editor_editor = new frappe.ui.Editor({
parent: frm.rich_text_editor,
height: '300px',
value: frappe.markdown(frm.doc.markdown_field || "")
});
- The
frappe.ui.Editor
creates a rich text editor similar to a Word document. - It takes the existing Markdown content and converts it into HTML for display.
3. Handle Image Uploading
let file = input.files[0];
let reader = new FileReader();
reader.onload = (e) => {
let base64Image = e.target.result;
let range = frm.rich_text_editor_editor.quill.getSelection(true);
frm.rich_text_editor_editor.quill.insertEmbed(range.index, 'image', base64Image);
};
reader.readAsDataURL(file);
- The image is converted to Base64 format and directly inserted into the document.
- This allows the image to be stored as part of the Markdown content.
4. Handle YouTube Video Embedding
let videoEmbed = `
<iframe width="560" height="315"
src="${url.replace('watch?v=', 'embed/')}"
frameborder="0"
allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture"
allowfullscreen>
</iframe>`;
- Converts the YouTube URL into an embeddable
iframe
format. - Directly inserts the video into the rich text editor.
5. Save as Markdown
before_save: function(frm) {
let html_value = frm.rich_text_editor_editor.get_value();
frm.set_value('markdown_field', html_value);
}
- When the document is saved, the rich text HTML is converted back into Markdown format and stored in the database.
- This ensures consistency with existing ERPNext data structures.
Customizing the Toolbar
You can define a custom toolbar configuration like this:
toolbar: [
[{ 'header': [1, 2, 3, false] }],
['bold', 'italic', 'underline', 'strike'],
[{ 'list': 'ordered' }, { 'list': 'bullet' }],
['link', 'image', 'video'],
['clean']
]
This gives you control over which formatting options are available to the user.
Expected Outcome
Markdown content is displayed as rich text
End users can format content visually
Copy-paste and upload images directly into the editor
Embed YouTube videos with a single link
On save, content is stored in Markdown format for consistency
Advantages
Improved user experience with visual editing
Seamless handling of images and videos
Maintains compatibility with ERPNextâs Markdown format
Easy to extend and customize
Conclusion
This solution enhances ERPNextâs user experience by replacing Markdown fields with a user-friendly rich text editor. The script handles images, videos, and formatting while maintaining compatibility with ERPNextâs Markdown storage format.
If you need additional help or customization, feel free to ask!