| 
									
										
										
										
											2021-06-25 14:38:31 +00:00
										 |  |  | __filename__ = "markdown.py" | 
					
						
							|  |  |  | __author__ = "Bob Mottram" | 
					
						
							|  |  |  | __license__ = "AGPL3+" | 
					
						
							| 
									
										
										
										
											2022-02-03 13:58:20 +00:00
										 |  |  | __version__ = "1.3.0" | 
					
						
							| 
									
										
										
										
											2021-06-25 14:38:31 +00:00
										 |  |  | __maintainer__ = "Bob Mottram" | 
					
						
							| 
									
										
										
										
											2021-09-10 16:14:50 +00:00
										 |  |  | __email__ = "bob@libreserver.org" | 
					
						
							| 
									
										
										
										
											2021-06-25 14:38:31 +00:00
										 |  |  | __status__ = "Production" | 
					
						
							|  |  |  | __module_group__ = "Web Interface" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-12-29 21:55:09 +00:00
										 |  |  | def _markdown_emphasis_html(markdown: str) -> str: | 
					
						
							| 
									
										
										
										
											2021-06-25 14:38:31 +00:00
										 |  |  |     """Add italics and bold html markup to the given markdown
 | 
					
						
							|  |  |  |     """
 | 
					
						
							|  |  |  |     replacements = { | 
					
						
							|  |  |  |         ' **': ' <b>', | 
					
						
							|  |  |  |         '** ': '</b> ', | 
					
						
							|  |  |  |         '**.': '</b>.', | 
					
						
							|  |  |  |         '**:': '</b>:', | 
					
						
							|  |  |  |         '**;': '</b>;', | 
					
						
							|  |  |  |         '**,': '</b>,', | 
					
						
							|  |  |  |         '**\n': '</b>\n', | 
					
						
							|  |  |  |         ' *': ' <i>', | 
					
						
							|  |  |  |         '* ': '</i> ', | 
					
						
							|  |  |  |         '*.': '</i>.', | 
					
						
							|  |  |  |         '*:': '</i>:', | 
					
						
							|  |  |  |         '*;': '</i>;', | 
					
						
							|  |  |  |         '*,': '</i>,', | 
					
						
							|  |  |  |         '*\n': '</i>\n', | 
					
						
							|  |  |  |         ' _': ' <ul>', | 
					
						
							|  |  |  |         '_ ': '</ul> ', | 
					
						
							|  |  |  |         '_.': '</ul>.', | 
					
						
							|  |  |  |         '_:': '</ul>:', | 
					
						
							|  |  |  |         '_;': '</ul>;', | 
					
						
							|  |  |  |         '_,': '</ul>,', | 
					
						
							|  |  |  |         '_\n': '</ul>\n' | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2022-01-02 22:35:39 +00:00
										 |  |  |     for md_str, html in replacements.items(): | 
					
						
							|  |  |  |         markdown = markdown.replace(md_str, html) | 
					
						
							| 
									
										
										
										
											2021-06-25 14:38:31 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     if markdown.startswith('**'): | 
					
						
							|  |  |  |         markdown = markdown[2:] + '<b>' | 
					
						
							|  |  |  |     elif markdown.startswith('*'): | 
					
						
							|  |  |  |         markdown = markdown[1:] + '<i>' | 
					
						
							|  |  |  |     elif markdown.startswith('_'): | 
					
						
							|  |  |  |         markdown = markdown[1:] + '<ul>' | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if markdown.endswith('**'): | 
					
						
							|  |  |  |         markdown = markdown[:len(markdown) - 2] + '</b>' | 
					
						
							|  |  |  |     elif markdown.endswith('*'): | 
					
						
							|  |  |  |         markdown = markdown[:len(markdown) - 1] + '</i>' | 
					
						
							|  |  |  |     elif markdown.endswith('_'): | 
					
						
							|  |  |  |         markdown = markdown[:len(markdown) - 1] + '</ul>' | 
					
						
							|  |  |  |     return markdown | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-12-29 21:55:09 +00:00
										 |  |  | def _markdown_replace_quotes(markdown: str) -> str: | 
					
						
							| 
									
										
										
										
											2021-06-25 14:38:31 +00:00
										 |  |  |     """Replaces > quotes with html blockquote
 | 
					
						
							|  |  |  |     """
 | 
					
						
							|  |  |  |     if '> ' not in markdown: | 
					
						
							|  |  |  |         return markdown | 
					
						
							|  |  |  |     lines = markdown.split('\n') | 
					
						
							|  |  |  |     result = '' | 
					
						
							| 
									
										
										
										
											2022-01-02 22:35:39 +00:00
										 |  |  |     prev_quote_line = None | 
					
						
							| 
									
										
										
										
											2021-06-25 14:38:31 +00:00
										 |  |  |     for line in lines: | 
					
						
							|  |  |  |         if '> ' not in line: | 
					
						
							|  |  |  |             result += line + '\n' | 
					
						
							| 
									
										
										
										
											2022-01-02 22:35:39 +00:00
										 |  |  |             prev_quote_line = None | 
					
						
							| 
									
										
										
										
											2021-06-25 14:38:31 +00:00
										 |  |  |             continue | 
					
						
							| 
									
										
										
										
											2022-01-02 22:35:39 +00:00
										 |  |  |         line_str = line.strip() | 
					
						
							|  |  |  |         if not line_str.startswith('> '): | 
					
						
							| 
									
										
										
										
											2021-06-25 14:38:31 +00:00
										 |  |  |             result += line + '\n' | 
					
						
							| 
									
										
										
										
											2022-01-02 22:35:39 +00:00
										 |  |  |             prev_quote_line = None | 
					
						
							| 
									
										
										
										
											2021-06-25 14:38:31 +00:00
										 |  |  |             continue | 
					
						
							| 
									
										
										
										
											2022-01-02 22:35:39 +00:00
										 |  |  |         line_str = line_str.replace('> ', '', 1).strip() | 
					
						
							|  |  |  |         if prev_quote_line: | 
					
						
							|  |  |  |             new_prev_line = prev_quote_line.replace('</i></blockquote>\n', '') | 
					
						
							|  |  |  |             result = result.replace(prev_quote_line, new_prev_line) + ' ' | 
					
						
							|  |  |  |             line_str += '</i></blockquote>\n' | 
					
						
							| 
									
										
										
										
											2021-06-25 14:38:31 +00:00
										 |  |  |         else: | 
					
						
							| 
									
										
										
										
											2022-01-02 22:35:39 +00:00
										 |  |  |             line_str = '<blockquote><i>' + line_str + '</i></blockquote>\n' | 
					
						
							|  |  |  |         result += line_str | 
					
						
							|  |  |  |         prev_quote_line = line_str | 
					
						
							| 
									
										
										
										
											2021-06-25 14:38:31 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     if '</blockquote>\n' in result: | 
					
						
							|  |  |  |         result = result.replace('</blockquote>\n', '</blockquote>') | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if result.endswith('\n') and \ | 
					
						
							|  |  |  |        not markdown.endswith('\n'): | 
					
						
							|  |  |  |         result = result[:len(result) - 1] | 
					
						
							|  |  |  |     return result | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-12-29 21:55:09 +00:00
										 |  |  | def _markdown_replace_links(markdown: str, images: bool = False) -> str: | 
					
						
							| 
									
										
										
										
											2021-06-25 14:38:31 +00:00
										 |  |  |     """Replaces markdown links with html
 | 
					
						
							|  |  |  |     Optionally replace image links | 
					
						
							|  |  |  |     """
 | 
					
						
							| 
									
										
										
										
											2022-01-02 22:35:39 +00:00
										 |  |  |     replace_links = {} | 
					
						
							| 
									
										
										
										
											2021-06-25 14:38:31 +00:00
										 |  |  |     text = markdown | 
					
						
							| 
									
										
										
										
											2022-01-02 22:35:39 +00:00
										 |  |  |     start_chars = '[' | 
					
						
							| 
									
										
										
										
											2021-06-25 14:38:31 +00:00
										 |  |  |     if images: | 
					
						
							| 
									
										
										
										
											2022-01-02 22:35:39 +00:00
										 |  |  |         start_chars = '![' | 
					
						
							|  |  |  |     while start_chars in text: | 
					
						
							| 
									
										
										
										
											2021-06-25 14:38:31 +00:00
										 |  |  |         if ')' not in text: | 
					
						
							|  |  |  |             break | 
					
						
							| 
									
										
										
										
											2022-01-02 22:35:39 +00:00
										 |  |  |         text = text.split(start_chars, 1)[1] | 
					
						
							|  |  |  |         markdown_link = start_chars + text.split(')')[0] + ')' | 
					
						
							|  |  |  |         if ']' not in markdown_link or \ | 
					
						
							|  |  |  |            '(' not in markdown_link: | 
					
						
							| 
									
										
										
										
											2021-06-25 14:38:31 +00:00
										 |  |  |             text = text.split(')', 1)[1] | 
					
						
							|  |  |  |             continue | 
					
						
							|  |  |  |         if not images: | 
					
						
							| 
									
										
										
										
											2022-01-02 22:35:39 +00:00
										 |  |  |             replace_links[markdown_link] = \ | 
					
						
							| 
									
										
										
										
											2021-06-25 14:38:31 +00:00
										 |  |  |                 '<a href="' + \ | 
					
						
							| 
									
										
										
										
											2022-01-02 22:35:39 +00:00
										 |  |  |                 markdown_link.split('(')[1].split(')')[0] + \ | 
					
						
							| 
									
										
										
										
											2021-06-25 14:38:31 +00:00
										 |  |  |                 '" target="_blank" rel="nofollow noopener noreferrer">' + \ | 
					
						
							| 
									
										
										
										
											2022-01-02 22:35:39 +00:00
										 |  |  |                 markdown_link.split(start_chars)[1].split(']')[0] + \ | 
					
						
							| 
									
										
										
										
											2021-06-25 14:38:31 +00:00
										 |  |  |                 '</a>' | 
					
						
							|  |  |  |         else: | 
					
						
							| 
									
										
										
										
											2022-01-02 22:35:39 +00:00
										 |  |  |             replace_links[markdown_link] = \ | 
					
						
							| 
									
										
										
										
											2021-06-25 14:38:31 +00:00
										 |  |  |                 '<img class="markdownImage" src="' + \ | 
					
						
							| 
									
										
										
										
											2022-01-02 22:35:39 +00:00
										 |  |  |                 markdown_link.split('(')[1].split(')')[0] + \ | 
					
						
							| 
									
										
										
										
											2021-06-25 14:38:31 +00:00
										 |  |  |                 '" alt="' + \ | 
					
						
							| 
									
										
										
										
											2022-01-02 22:35:39 +00:00
										 |  |  |                 markdown_link.split(start_chars)[1].split(']')[0] + \ | 
					
						
							| 
									
										
										
										
											2021-06-25 14:38:31 +00:00
										 |  |  |                 '" />' | 
					
						
							|  |  |  |         text = text.split(')', 1)[1] | 
					
						
							| 
									
										
										
										
											2022-01-02 22:35:39 +00:00
										 |  |  |     for md_link, html_link in replace_links.items(): | 
					
						
							|  |  |  |         markdown = markdown.replace(md_link, html_link) | 
					
						
							| 
									
										
										
										
											2021-06-25 14:38:31 +00:00
										 |  |  |     return markdown | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-12-29 21:55:09 +00:00
										 |  |  | def markdown_to_html(markdown: str) -> str: | 
					
						
							| 
									
										
										
										
											2021-06-25 14:38:31 +00:00
										 |  |  |     """Converts markdown formatted text to html
 | 
					
						
							|  |  |  |     """
 | 
					
						
							| 
									
										
										
										
											2021-12-29 21:55:09 +00:00
										 |  |  |     markdown = _markdown_replace_quotes(markdown) | 
					
						
							|  |  |  |     markdown = _markdown_emphasis_html(markdown) | 
					
						
							|  |  |  |     markdown = _markdown_replace_links(markdown, True) | 
					
						
							|  |  |  |     markdown = _markdown_replace_links(markdown) | 
					
						
							| 
									
										
										
										
											2021-06-25 14:38:31 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     # replace headers | 
					
						
							| 
									
										
										
										
											2022-01-02 22:35:39 +00:00
										 |  |  |     lines_list = markdown.split('\n') | 
					
						
							|  |  |  |     html_str = '' | 
					
						
							| 
									
										
										
										
											2021-06-25 14:38:31 +00:00
										 |  |  |     ctr = 0 | 
					
						
							| 
									
										
										
										
											2021-07-03 20:15:34 +00:00
										 |  |  |     titles = { | 
					
						
							|  |  |  |         "h5": '#####', | 
					
						
							|  |  |  |         "h4": '####', | 
					
						
							|  |  |  |         "h3": '###', | 
					
						
							|  |  |  |         "h2": '##', | 
					
						
							|  |  |  |         "h1": '#' | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2022-01-02 22:35:39 +00:00
										 |  |  |     for line in lines_list: | 
					
						
							| 
									
										
										
										
											2021-06-25 14:38:31 +00:00
										 |  |  |         if ctr > 0: | 
					
						
							| 
									
										
										
										
											2022-01-02 22:35:39 +00:00
										 |  |  |             html_str += '<br>' | 
					
						
							|  |  |  |         for hsh, hashes in titles.items(): | 
					
						
							| 
									
										
										
										
											2021-07-03 20:15:34 +00:00
										 |  |  |             if line.startswith(hashes): | 
					
						
							|  |  |  |                 line = line.replace(hashes, '').strip() | 
					
						
							| 
									
										
										
										
											2022-01-02 22:35:39 +00:00
										 |  |  |                 line = '<' + hsh + '>' + line + '</' + hsh + '>' | 
					
						
							| 
									
										
										
										
											2021-07-03 20:15:34 +00:00
										 |  |  |                 ctr = -1 | 
					
						
							|  |  |  |                 break | 
					
						
							| 
									
										
										
										
											2022-01-02 22:35:39 +00:00
										 |  |  |         html_str += line | 
					
						
							| 
									
										
										
										
											2021-06-25 14:38:31 +00:00
										 |  |  |         ctr += 1 | 
					
						
							| 
									
										
										
										
											2022-01-02 22:35:39 +00:00
										 |  |  |     return html_str |