# This file is part of Tryton. The COPYRIGHT file at the top level of
# this repository contains the full copyright notices and license terms.
import base64
import html
import mimetypes
import os
import posixpath
import re
from pathlib import Path
from urllib.parse import unquote
from trytond.model import ModelSingleton, ModelSQL, ModelView, fields
from trytond.transaction import Transaction
DOCUMENTATION_ROOT = Path(__file__).with_name('process_documentation')
MARKDOWN_LINK = re.compile(r'(!?)\[([^\]]*)\]\(([^)]+)\)')
HTML_IMAGE_SOURCE = re.compile(
r'(]*\bsrc=)(["\'])([^"\']+)(\2)', re.IGNORECASE)
class ProcessDocumentation(ModelSingleton, ModelSQL, ModelView):
"Process Documentation"
__name__ = 'purchase_trade.process.documentation'
content = fields.Function(fields.Text('Content'), 'get_content')
@staticmethod
def default_content():
return _html_content()
def get_content(self, name):
return _html_content()
def _html_content():
html_file = Transaction().context.get('process_documentation_html')
if html_file:
return _html_file_content(html_file)
document = HTML_TEMPLATE % {
'content': _documentation_content(),
}
return IFRAME_TEMPLATE % {
'document': html.escape(document, quote=True),
}
def _html_file_content(filename):
path = (DOCUMENTATION_ROOT / Path(*filename.split('/'))).resolve()
root = DOCUMENTATION_ROOT.resolve()
common_path = os.path.commonpath([str(root), str(path)])
if path != root and common_path == str(root):
if path.exists() and path.is_file() and path.suffix.lower() == '.html':
document = path.read_text(encoding='utf-8')
document = _inline_html_assets(document, _relative_name(path))
return IFRAME_TEMPLATE % {
'document': html.escape(document, quote=True),
}
return _html_error('Documentation page not found: %s' % filename)
def _inline_html_assets(document, current_file):
def replace(match):
prefix, quote, source, suffix = match.groups()
resolved = _resolve_link(current_file, source)
if resolved.startswith(('http://', 'https://', 'data:', 'mailto:')):
return match.group(0)
data_uri = _asset_data_uri(resolved)
if not data_uri:
return match.group(0)
return '%s%s%s%s' % (prefix, quote, html.escape(data_uri, quote=True),
suffix)
return HTML_IMAGE_SOURCE.sub(replace, document)
def _html_error(message):
document = HTML_TEMPLATE % {
'content': '
%s
' % html.escape(message), } return IFRAME_TEMPLATE % { 'document': html.escape(document, quote=True), } def _documentation_content(): sections = [] index = DOCUMENTATION_ROOT / 'README.md' if index.exists(): sections.append(_render_page(index)) for path in sorted(DOCUMENTATION_ROOT.rglob('*.md')): if path == index: continue sections.append(_render_page(path)) return '\n'.join(sections) def _render_page(path): relative_name = _relative_name(path) markdown = path.read_text(encoding='utf-8') return '%s
' % _inline(stripped, current_file)) flush_table() close_list() if current_file != 'README.md': body.insert( 0, '%s
' % _internal_link('doc-README-md', 'Back to index')) return '\n'.join(body) def _internal_link(anchor, label): anchor = html.escape(anchor, quote=True) return '%s' % ( anchor, anchor, label) def _render_table(lines, current_file): rows = [ [cell.strip() for cell in line.strip('|').split('|')] for line in lines ] header = rows[0] if rows else [] data = rows[1:] if data and all(set(cell) <= {'-', ':'} for cell in data[0]): data = data[1:] output = ['| %s | ' % _inline(cell, current_file)) output.append('
|---|
| %s | ' % _inline(cell, current_file)) output.append('