Migrate mistune to markdown
This commit is contained in:
parent
412945626b
commit
77aaae6735
46 changed files with 5112 additions and 420 deletions
|
@ -362,42 +362,6 @@ LANGUAGES = [
|
||||||
("vi", _("Vietnamese")),
|
("vi", _("Vietnamese")),
|
||||||
]
|
]
|
||||||
|
|
||||||
MARKDOWN_ADMIN_EDITABLE_STYLE = {
|
|
||||||
"safe_mode": False,
|
|
||||||
"use_camo": True,
|
|
||||||
"texoid": True,
|
|
||||||
"math": True,
|
|
||||||
}
|
|
||||||
|
|
||||||
MARKDOWN_DEFAULT_STYLE = {
|
|
||||||
"safe_mode": True,
|
|
||||||
"nofollow": True,
|
|
||||||
"use_camo": True,
|
|
||||||
"math": True,
|
|
||||||
}
|
|
||||||
|
|
||||||
MARKDOWN_USER_LARGE_STYLE = {
|
|
||||||
"safe_mode": True,
|
|
||||||
"nofollow": True,
|
|
||||||
"use_camo": True,
|
|
||||||
"math": True,
|
|
||||||
}
|
|
||||||
|
|
||||||
MARKDOWN_STYLES = {
|
|
||||||
"comment": MARKDOWN_ADMIN_EDITABLE_STYLE,
|
|
||||||
"self-description": MARKDOWN_ADMIN_EDITABLE_STYLE,
|
|
||||||
"problem": MARKDOWN_ADMIN_EDITABLE_STYLE,
|
|
||||||
"contest": MARKDOWN_ADMIN_EDITABLE_STYLE,
|
|
||||||
"language": MARKDOWN_ADMIN_EDITABLE_STYLE,
|
|
||||||
"license": MARKDOWN_ADMIN_EDITABLE_STYLE,
|
|
||||||
"judge": MARKDOWN_ADMIN_EDITABLE_STYLE,
|
|
||||||
"blog": MARKDOWN_ADMIN_EDITABLE_STYLE,
|
|
||||||
"solution": MARKDOWN_ADMIN_EDITABLE_STYLE,
|
|
||||||
"contest_tag": MARKDOWN_ADMIN_EDITABLE_STYLE,
|
|
||||||
"organization-about": MARKDOWN_ADMIN_EDITABLE_STYLE,
|
|
||||||
"ticket": MARKDOWN_ADMIN_EDITABLE_STYLE,
|
|
||||||
}
|
|
||||||
|
|
||||||
# Database
|
# Database
|
||||||
# https://docs.djangoproject.com/en/1.11/ref/settings/#databases
|
# https://docs.djangoproject.com/en/1.11/ref/settings/#databases
|
||||||
|
|
||||||
|
|
|
@ -1,183 +1,36 @@
|
||||||
import logging
|
|
||||||
import re
|
|
||||||
from html.parser import HTMLParser
|
|
||||||
from urllib.parse import urlparse
|
|
||||||
|
|
||||||
import mistune
|
|
||||||
from django.conf import settings
|
|
||||||
from markupsafe import Markup
|
|
||||||
from lxml import html
|
|
||||||
from lxml.etree import ParserError, XMLSyntaxError
|
|
||||||
|
|
||||||
from judge.highlight_code import highlight_code
|
|
||||||
from judge.jinja2.markdown.lazy_load import lazy_load as lazy_load_processor
|
|
||||||
from judge.jinja2.markdown.math import MathInlineGrammar, MathInlineLexer, MathRenderer
|
|
||||||
from judge.utils.camo import client as camo_client
|
|
||||||
from judge.utils.texoid import TEXOID_ENABLED, TexoidRenderer
|
|
||||||
from .. import registry
|
from .. import registry
|
||||||
|
import markdown as _markdown
|
||||||
logger = logging.getLogger("judge.html")
|
import bleach
|
||||||
|
from django.utils.html import escape
|
||||||
NOFOLLOW_WHITELIST = settings.NOFOLLOW_EXCLUDED
|
|
||||||
|
|
||||||
|
|
||||||
class CodeSafeInlineGrammar(mistune.InlineGrammar):
|
EXTENSIONS = [
|
||||||
double_emphasis = re.compile(r"^\*{2}([\s\S]+?)()\*{2}(?!\*)") # **word**
|
"pymdownx.magiclink",
|
||||||
emphasis = re.compile(r"^\*((?:\*\*|[^\*])+?)()\*(?!\*)") # *word*
|
"pymdownx.details",
|
||||||
|
"pymdownx.emoji",
|
||||||
|
"pymdownx.inlinehilite",
|
||||||
|
"pymdownx.superfences",
|
||||||
|
"pymdownx.tasklist",
|
||||||
|
"markdown.extensions.footnotes",
|
||||||
|
"markdown.extensions.attr_list",
|
||||||
|
"markdown.extensions.def_list",
|
||||||
|
"markdown.extensions.tables",
|
||||||
|
"markdown.extensions.admonition",
|
||||||
|
"pymdownx.arithmatex",
|
||||||
|
]
|
||||||
|
|
||||||
|
ALLOWED_TAGS = bleach.sanitizer.ALLOWED_TAGS + ["img", "center", "iframe"]
|
||||||
|
|
||||||
class AwesomeInlineGrammar(MathInlineGrammar, CodeSafeInlineGrammar):
|
ALLOWED_ATTRS = ["src", "width", "height", "href"]
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
class AwesomeInlineLexer(MathInlineLexer, mistune.InlineLexer):
|
|
||||||
grammar_class = AwesomeInlineGrammar
|
|
||||||
|
|
||||||
|
|
||||||
class AwesomeRenderer(MathRenderer, mistune.Renderer):
|
|
||||||
def __init__(self, *args, **kwargs):
|
|
||||||
self.nofollow = kwargs.pop("nofollow", True)
|
|
||||||
self.texoid = TexoidRenderer() if kwargs.pop("texoid", False) else None
|
|
||||||
self.parser = HTMLParser()
|
|
||||||
super(AwesomeRenderer, self).__init__(*args, **kwargs)
|
|
||||||
|
|
||||||
def _link_rel(self, href):
|
|
||||||
if href:
|
|
||||||
try:
|
|
||||||
url = urlparse(href)
|
|
||||||
except ValueError:
|
|
||||||
return ' rel="nofollow"'
|
|
||||||
else:
|
|
||||||
if url.netloc and url.netloc not in NOFOLLOW_WHITELIST:
|
|
||||||
return ' rel="nofollow"'
|
|
||||||
return ""
|
|
||||||
|
|
||||||
def autolink(self, link, is_email=False):
|
|
||||||
text = link = mistune.escape(link)
|
|
||||||
if is_email:
|
|
||||||
link = "mailto:%s" % link
|
|
||||||
return '<a href="%s"%s>%s</a>' % (link, self._link_rel(link), text)
|
|
||||||
|
|
||||||
def table(self, header, body):
|
|
||||||
return (
|
|
||||||
'<table class="table">\n<thead>%s</thead>\n'
|
|
||||||
"<tbody>\n%s</tbody>\n</table>\n"
|
|
||||||
) % (header, body)
|
|
||||||
|
|
||||||
def link(self, link, title, text):
|
|
||||||
link = mistune.escape_link(link)
|
|
||||||
if not title:
|
|
||||||
return '<a href="%s"%s>%s</a>' % (link, self._link_rel(link), text)
|
|
||||||
title = mistune.escape(title, quote=True)
|
|
||||||
return '<a href="%s" title="%s"%s>%s</a>' % (
|
|
||||||
link,
|
|
||||||
title,
|
|
||||||
self._link_rel(link),
|
|
||||||
text,
|
|
||||||
)
|
|
||||||
|
|
||||||
def block_code(self, code, lang=None):
|
|
||||||
if not lang:
|
|
||||||
return "\n<pre><code>%s</code></pre>\n" % mistune.escape(code).rstrip()
|
|
||||||
return highlight_code(code, lang)
|
|
||||||
|
|
||||||
def block_html(self, html):
|
|
||||||
if self.texoid and html.startswith("<latex"):
|
|
||||||
attr = html[6 : html.index(">")]
|
|
||||||
latex = html[html.index(">") + 1 : html.rindex("<")]
|
|
||||||
latex = self.parser.unescape(latex)
|
|
||||||
result = self.texoid.get_result(latex)
|
|
||||||
if not result:
|
|
||||||
return "<pre>%s</pre>" % mistune.escape(latex, smart_amp=False)
|
|
||||||
elif "error" not in result:
|
|
||||||
img = (
|
|
||||||
'''<img src="%(svg)s" onerror="this.src='%(png)s';this.onerror=null"'''
|
|
||||||
'width="%(width)s" height="%(height)s"%(tail)s>'
|
|
||||||
) % {
|
|
||||||
"svg": result["svg"],
|
|
||||||
"png": result["png"],
|
|
||||||
"width": result["meta"]["width"],
|
|
||||||
"height": result["meta"]["height"],
|
|
||||||
"tail": " /" if self.options.get("use_xhtml") else "",
|
|
||||||
}
|
|
||||||
style = [
|
|
||||||
"max-width: 100%",
|
|
||||||
"height: %s" % result["meta"]["height"],
|
|
||||||
"max-height: %s" % result["meta"]["height"],
|
|
||||||
"width: %s" % result["meta"]["height"],
|
|
||||||
]
|
|
||||||
if "inline" in attr:
|
|
||||||
tag = "span"
|
|
||||||
else:
|
|
||||||
tag = "div"
|
|
||||||
style += ["text-align: center"]
|
|
||||||
return '<%s style="%s">%s</%s>' % (tag, ";".join(style), img, tag)
|
|
||||||
else:
|
|
||||||
return "<pre>%s</pre>" % mistune.escape(
|
|
||||||
result["error"], smart_amp=False
|
|
||||||
)
|
|
||||||
return super(AwesomeRenderer, self).block_html(html)
|
|
||||||
|
|
||||||
def header(self, text, level, *args, **kwargs):
|
|
||||||
return super(AwesomeRenderer, self).header(text, level + 2, *args, **kwargs)
|
|
||||||
|
|
||||||
|
|
||||||
def create_spoiler(value, style):
|
|
||||||
respoiler = re.compile(r"(^\|\|(.+)\s+([\s\S]+?)\s*\|\|)", re.MULTILINE)
|
|
||||||
matches = re.findall(respoiler, value)
|
|
||||||
html = (
|
|
||||||
'<details><summary style="color: brown">'
|
|
||||||
+ '<span class="spoiler-summary">{summary}</span>'
|
|
||||||
+ "</summary>{detail}</details>"
|
|
||||||
)
|
|
||||||
|
|
||||||
for entire, summary, detail in matches:
|
|
||||||
detail = markdown(detail, style)
|
|
||||||
new_html = html.format(summary=summary, detail=detail)
|
|
||||||
value = value.replace(entire, new_html)
|
|
||||||
return value
|
|
||||||
|
|
||||||
|
|
||||||
@registry.filter
|
@registry.filter
|
||||||
def markdown(value, style, math_engine=None, lazy_load=False, hard_wrap=False):
|
def markdown(value, hard_wrap=False):
|
||||||
styles = settings.MARKDOWN_STYLES.get(style, settings.MARKDOWN_DEFAULT_STYLE)
|
extensions = EXTENSIONS
|
||||||
escape = styles.get("safe_mode", True)
|
if hard_wrap:
|
||||||
nofollow = styles.get("nofollow", True)
|
extensions = EXTENSIONS + ["nl2br"]
|
||||||
texoid = TEXOID_ENABLED and styles.get("texoid", False)
|
html = bleach.clean(value, tags=ALLOWED_TAGS, attributes=ALLOWED_ATTRS)
|
||||||
math = hasattr(settings, "MATHOID_URL") and styles.get("math", False)
|
html = _markdown.markdown(html, extensions=extensions)
|
||||||
|
if not html:
|
||||||
value = create_spoiler(value, style)
|
html = escape(value)
|
||||||
post_processors = []
|
return '<div class="md-typeset">%s</div>' % html
|
||||||
if styles.get("use_camo", False) and camo_client is not None:
|
|
||||||
post_processors.append(camo_client.update_tree)
|
|
||||||
if lazy_load:
|
|
||||||
post_processors.append(lazy_load_processor)
|
|
||||||
|
|
||||||
renderer = AwesomeRenderer(
|
|
||||||
escape=escape,
|
|
||||||
nofollow=nofollow,
|
|
||||||
texoid=texoid,
|
|
||||||
math=math and math_engine is not None,
|
|
||||||
math_engine=math_engine,
|
|
||||||
)
|
|
||||||
markdown = mistune.Markdown(
|
|
||||||
renderer=renderer,
|
|
||||||
inline=AwesomeInlineLexer,
|
|
||||||
parse_block_html=1,
|
|
||||||
parse_inline_html=1,
|
|
||||||
hard_wrap=hard_wrap,
|
|
||||||
)
|
|
||||||
result = markdown(value)
|
|
||||||
if post_processors:
|
|
||||||
try:
|
|
||||||
tree = html.fromstring(result, parser=html.HTMLParser(recover=True))
|
|
||||||
except (XMLSyntaxError, ParserError) as e:
|
|
||||||
if result and (
|
|
||||||
not isinstance(e, ParserError) or e.args[0] != "Document is empty"
|
|
||||||
):
|
|
||||||
logger.exception("Failed to parse HTML string")
|
|
||||||
tree = html.Element("div")
|
|
||||||
for processor in post_processors:
|
|
||||||
processor(tree)
|
|
||||||
result = html.tostring(tree, encoding="unicode")
|
|
||||||
return Markup(result)
|
|
||||||
|
|
|
@ -1,20 +0,0 @@
|
||||||
from copy import deepcopy
|
|
||||||
|
|
||||||
from django.templatetags.static import static
|
|
||||||
from lxml import html
|
|
||||||
|
|
||||||
|
|
||||||
def lazy_load(tree):
|
|
||||||
blank = static("blank.gif")
|
|
||||||
for img in tree.xpath(".//img"):
|
|
||||||
src = img.get("src", "")
|
|
||||||
if src.startswith("data") or "-math" in img.get("class", ""):
|
|
||||||
continue
|
|
||||||
noscript = html.Element("noscript")
|
|
||||||
copy = deepcopy(img)
|
|
||||||
copy.tail = ""
|
|
||||||
noscript.append(copy)
|
|
||||||
img.addprevious(noscript)
|
|
||||||
img.set("data-src", src)
|
|
||||||
img.set("src", blank)
|
|
||||||
img.set("class", img.get("class") + " unveil" if img.get("class") else "unveil")
|
|
|
@ -1,69 +0,0 @@
|
||||||
import re
|
|
||||||
|
|
||||||
import mistune
|
|
||||||
|
|
||||||
from django.conf import settings
|
|
||||||
|
|
||||||
from judge.utils.mathoid import MathoidMathParser
|
|
||||||
|
|
||||||
mistune._pre_tags.append("latex")
|
|
||||||
|
|
||||||
|
|
||||||
class MathInlineGrammar(mistune.InlineGrammar):
|
|
||||||
block_math = re.compile(r"^\$\$(.*?)\$\$|^\\\[(.*?)\\\]", re.DOTALL)
|
|
||||||
math = re.compile(r"^~(.*?)~|^\$(.*?)\$|^\\\((.*?)\\\)", re.DOTALL)
|
|
||||||
text = re.compile(r"^[\s\S]+?(?=[\\<!\[_*`$~]|\\[\[(]|https?://| {2,}\n|$)")
|
|
||||||
|
|
||||||
|
|
||||||
class MathInlineLexer(mistune.InlineLexer):
|
|
||||||
grammar_class = MathInlineGrammar
|
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
|
||||||
self.default_rules = self.default_rules[:]
|
|
||||||
self.inline_html_rules = self.default_rules
|
|
||||||
self.default_rules.insert(self.default_rules.index("strikethrough") + 1, "math")
|
|
||||||
self.default_rules.insert(
|
|
||||||
self.default_rules.index("strikethrough") + 1, "block_math"
|
|
||||||
)
|
|
||||||
super(MathInlineLexer, self).__init__(*args, **kwargs)
|
|
||||||
|
|
||||||
def output_block_math(self, m):
|
|
||||||
return self.renderer.block_math(m.group(1) or m.group(2))
|
|
||||||
|
|
||||||
def output_math(self, m):
|
|
||||||
return self.renderer.math(m.group(1) or m.group(2) or m.group(3))
|
|
||||||
|
|
||||||
def output_inline_html(self, m):
|
|
||||||
tag = m.group(1)
|
|
||||||
text = m.group(3)
|
|
||||||
if self._parse_inline_html and text:
|
|
||||||
if tag == "a":
|
|
||||||
self._in_link = True
|
|
||||||
text = self.output(text)
|
|
||||||
self._in_link = False
|
|
||||||
else:
|
|
||||||
text = self.output(text)
|
|
||||||
extra = m.group(2) or ""
|
|
||||||
html = "<%s%s>%s</%s>" % (tag, extra, text, tag)
|
|
||||||
else:
|
|
||||||
html = m.group(0)
|
|
||||||
return self.renderer.inline_html(html)
|
|
||||||
|
|
||||||
|
|
||||||
class MathRenderer(mistune.Renderer):
|
|
||||||
def __init__(self, *args, **kwargs):
|
|
||||||
if kwargs.pop("math", False) and settings.MATHOID_URL != False:
|
|
||||||
self.mathoid = MathoidMathParser(kwargs.pop("math_engine", None) or "svg")
|
|
||||||
else:
|
|
||||||
self.mathoid = None
|
|
||||||
super(MathRenderer, self).__init__(*args, **kwargs)
|
|
||||||
|
|
||||||
def block_math(self, math):
|
|
||||||
if self.mathoid is None or not math:
|
|
||||||
return r"$$%s$$" % mistune.escape(str(math))
|
|
||||||
return self.mathoid.display_math(math)
|
|
||||||
|
|
||||||
def math(self, math):
|
|
||||||
if self.mathoid is None or not math:
|
|
||||||
return r"~%s~" % mistune.escape(str(math))
|
|
||||||
return self.mathoid.inline_math(math)
|
|
|
@ -5,11 +5,11 @@ from judge.jinja2.markdown import markdown
|
||||||
from judge.jinja2.reference import reference
|
from judge.jinja2.reference import reference
|
||||||
|
|
||||||
|
|
||||||
def generate_opengraph(cache_key, data, style):
|
def generate_opengraph(cache_key, data):
|
||||||
metadata = cache.get(cache_key)
|
metadata = cache.get(cache_key)
|
||||||
if metadata is None:
|
if metadata is None:
|
||||||
description = None
|
description = None
|
||||||
tree = reference(markdown(data, style)).tree
|
tree = reference(markdown(data)).tree
|
||||||
for p in tree.iterfind(".//p"):
|
for p in tree.iterfind(".//p"):
|
||||||
text = p.text_content().strip()
|
text = p.text_content().strip()
|
||||||
if text:
|
if text:
|
||||||
|
|
|
@ -207,7 +207,7 @@ class PostView(TitleMixin, CommentedDetailView):
|
||||||
context["og_image"] = self.object.og_image
|
context["og_image"] = self.object.og_image
|
||||||
context["valid_user_to_show_edit"] = False
|
context["valid_user_to_show_edit"] = False
|
||||||
context["valid_org_to_show_edit"] = []
|
context["valid_org_to_show_edit"] = []
|
||||||
|
|
||||||
if self.request.profile in self.object.authors.all():
|
if self.request.profile in self.object.authors.all():
|
||||||
context["valid_user_to_show_edit"] = True
|
context["valid_user_to_show_edit"] = True
|
||||||
|
|
||||||
|
|
|
@ -308,7 +308,6 @@ class ProblemDetail(ProblemMixin, SolvedProblemMixin, CommentedDetailView):
|
||||||
metadata = generate_opengraph(
|
metadata = generate_opengraph(
|
||||||
"generated-meta-problem:%s:%d" % (context["language"], self.object.id),
|
"generated-meta-problem:%s:%d" % (context["language"], self.object.id),
|
||||||
context["description"],
|
context["description"],
|
||||||
"problem",
|
|
||||||
)
|
)
|
||||||
context["meta_description"] = self.object.summary or metadata[0]
|
context["meta_description"] = self.object.summary or metadata[0]
|
||||||
context["og_image"] = self.object.og_image or metadata[1]
|
context["og_image"] = self.object.og_image or metadata[1]
|
||||||
|
|
|
@ -41,6 +41,7 @@ else:
|
||||||
css = {
|
css = {
|
||||||
"all": [
|
"all": [
|
||||||
"pagedown_widget.css",
|
"pagedown_widget.css",
|
||||||
|
"markdown.css",
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -50,6 +51,7 @@ else:
|
||||||
"all": [
|
"all": [
|
||||||
"content-description.css",
|
"content-description.css",
|
||||||
"admin/css/pagedown.css",
|
"admin/css/pagedown.css",
|
||||||
|
"markdown.css",
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
js = ["admin/js/pagedown.js"]
|
js = ["admin/js/pagedown.js"]
|
||||||
|
@ -114,5 +116,6 @@ else:
|
||||||
"pygment-github.css",
|
"pygment-github.css",
|
||||||
"table.css",
|
"table.css",
|
||||||
"ranks.css",
|
"ranks.css",
|
||||||
|
"markdown.css",
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,7 +11,6 @@ django-impersonate
|
||||||
dmoj-wpadmin @ git+https://github.com/LQDJudge/dmoj-wpadmin.git
|
dmoj-wpadmin @ git+https://github.com/LQDJudge/dmoj-wpadmin.git
|
||||||
lxml
|
lxml
|
||||||
Pygments
|
Pygments
|
||||||
mistune<2
|
|
||||||
social-auth-app-django
|
social-auth-app-django
|
||||||
pytz
|
pytz
|
||||||
django-statici18n
|
django-statici18n
|
||||||
|
@ -38,4 +37,6 @@ lupa
|
||||||
websocket-client
|
websocket-client
|
||||||
python-memcached
|
python-memcached
|
||||||
numpy
|
numpy
|
||||||
pandas
|
pandas
|
||||||
|
markdown
|
||||||
|
bleach
|
||||||
|
|
|
@ -1,80 +1,15 @@
|
||||||
@import "vars";
|
@import "vars";
|
||||||
|
|
||||||
.content-description {
|
.content-description {
|
||||||
line-height: 1.5em;
|
line-height: 1.6em;
|
||||||
font-size: 1em;
|
font-size: 1em;
|
||||||
font-family: "Segoe UI", "Lucida Grande", Arial, sans-serif;
|
font-family: "Segoe UI", "Lucida Grande", Arial, sans-serif;
|
||||||
|
|
||||||
p {
|
|
||||||
margin: 1em 0 !important;
|
|
||||||
padding: 0 !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
img {
|
img {
|
||||||
max-width: 100%;
|
max-width: 100%;
|
||||||
height: auto;
|
height: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
h1, h2, h3, h4, h5, h6 {
|
|
||||||
font-weight: normal;
|
|
||||||
color: #111;
|
|
||||||
margin-bottom: 0.75em;
|
|
||||||
padding: 0;
|
|
||||||
background: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
h3, h4, h5, h6 {
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
||||||
|
|
||||||
h1 {
|
|
||||||
font-size: 2.5em;
|
|
||||||
}
|
|
||||||
|
|
||||||
h2 {
|
|
||||||
font-size: 2em;
|
|
||||||
}
|
|
||||||
|
|
||||||
h3 {
|
|
||||||
font-size: 1.6em;
|
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
h4 {
|
|
||||||
font-size: 1.4em;
|
|
||||||
border-bottom: 1px solid #EEE;
|
|
||||||
line-height: 1.225;
|
|
||||||
padding-bottom: 0.3em;
|
|
||||||
padding-top: 0.5em;
|
|
||||||
}
|
|
||||||
|
|
||||||
h5 {
|
|
||||||
font-size: 1.15em;
|
|
||||||
margin-top: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
h6 {
|
|
||||||
font-size: 0.9em;
|
|
||||||
}
|
|
||||||
|
|
||||||
blockquote {
|
|
||||||
color: #666;
|
|
||||||
margin: 0;
|
|
||||||
padding-left: 1.5em;
|
|
||||||
border-left: 0.5em #EEE solid;
|
|
||||||
}
|
|
||||||
|
|
||||||
hr {
|
|
||||||
display: block;
|
|
||||||
height: 0;
|
|
||||||
border: 0;
|
|
||||||
font-style: italic;
|
|
||||||
border-bottom: 1px solid $border_gray;
|
|
||||||
margin: 25px 0 20px 0;
|
|
||||||
padding: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
pre, code, kbd, samp, span.code {
|
pre, code, kbd, samp, span.code {
|
||||||
color: #000;
|
color: #000;
|
||||||
page-break-inside: avoid;
|
page-break-inside: avoid;
|
||||||
|
|
5032
resources/markdown.css
Normal file
5032
resources/markdown.css
Normal file
File diff suppressed because it is too large
Load diff
|
@ -26,14 +26,6 @@ td.user-name {
|
||||||
text-align: left;
|
text-align: left;
|
||||||
}
|
}
|
||||||
|
|
||||||
tr {
|
|
||||||
padding-bottom: 96px;
|
|
||||||
|
|
||||||
&:target {
|
|
||||||
background: #fff897;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
th.header.rank {
|
th.header.rank {
|
||||||
padding-left: 5px;
|
padding-left: 5px;
|
||||||
}
|
}
|
||||||
|
@ -84,6 +76,11 @@ th.header.rank {
|
||||||
&.highlight {
|
&.highlight {
|
||||||
background: #fff897;
|
background: #fff897;
|
||||||
}
|
}
|
||||||
|
padding-bottom: 96px;
|
||||||
|
|
||||||
|
&:target {
|
||||||
|
background: #fff897;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -57,6 +57,7 @@
|
||||||
<link rel="stylesheet" type="text/css" href="{{ static('libs/clipboard/tooltip.css') }}">
|
<link rel="stylesheet" type="text/css" href="{{ static('libs/clipboard/tooltip.css') }}">
|
||||||
<link rel="stylesheet" type="text/css" href="{{ static('libs/select2/select2.css') }}">
|
<link rel="stylesheet" type="text/css" href="{{ static('libs/select2/select2.css') }}">
|
||||||
<link rel="stylesheet" type="text/css" href="{{ static('icofont/icofont.min.css') }}">
|
<link rel="stylesheet" type="text/css" href="{{ static('icofont/icofont.min.css') }}">
|
||||||
|
<link rel="stylesheet" type="text/css" href="{{ static('markdown.css') }}">
|
||||||
{% endcompress %}
|
{% endcompress %}
|
||||||
<link rel="canonical"
|
<link rel="canonical"
|
||||||
href="{{ DMOJ_SCHEME }}://{{ DMOJ_CANONICAL|default(site.domain) }}{{ request.get_full_path() }}">
|
href="{{ DMOJ_SCHEME }}://{{ DMOJ_CANONICAL|default(site.domain) }}{{ request.get_full_path() }}">
|
||||||
|
|
|
@ -36,7 +36,7 @@
|
||||||
</div>
|
</div>
|
||||||
<div class="body content-description">
|
<div class="body content-description">
|
||||||
{% cache 86400 'post_content' post.id MATH_ENGINE %}
|
{% cache 86400 'post_content' post.id MATH_ENGINE %}
|
||||||
{{ post.content|markdown('blog', MATH_ENGINE)|reference|str|safe}}
|
{{ post.content|markdown|reference|str|safe}}
|
||||||
{% endcache %}
|
{% endcache %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -38,7 +38,7 @@
|
||||||
</h2>
|
</h2>
|
||||||
<div class="summary content-description blog-description">
|
<div class="summary content-description blog-description">
|
||||||
{% cache 86400 'post_summary' post.id %}
|
{% cache 86400 'post_summary' post.id %}
|
||||||
{{ post.summary|default(post.content, true)|markdown('blog', 'svg', lazy_load=True)|reference|str|safe }}
|
{{ post.summary|default(post.content, true)|markdown|reference|str|safe }}
|
||||||
{% endcache %}
|
{% endcache %}
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
|
@ -1 +1 @@
|
||||||
{{ preview_data|markdown('blog', MATH_ENGINE)|reference|str|safe }}
|
{{ preview_data|markdown|reference|str|safe }}
|
|
@ -66,7 +66,6 @@ let META_HEADER = [
|
||||||
}
|
}
|
||||||
|
|
||||||
$('.body-block').slice(0, window.messages_per_page).each(function() {
|
$('.body-block').slice(0, window.messages_per_page).each(function() {
|
||||||
resize_emoji($(this));
|
|
||||||
});
|
});
|
||||||
|
|
||||||
register_time($('.time-with-rel'));
|
register_time($('.time-with-rel'));
|
||||||
|
@ -153,7 +152,6 @@ let META_HEADER = [
|
||||||
|
|
||||||
function add_message(data) {
|
function add_message(data) {
|
||||||
var $data = $(data);
|
var $data = $(data);
|
||||||
resize_emoji($data.find('.body-block'));
|
|
||||||
|
|
||||||
$('#chat-log').append($data);
|
$('#chat-log').append($data);
|
||||||
$('#chat-box').scrollTop($('#chat-box')[0].scrollHeight);
|
$('#chat-box').scrollTop($('#chat-box')[0].scrollHeight);
|
||||||
|
@ -212,7 +210,6 @@ let META_HEADER = [
|
||||||
else {
|
else {
|
||||||
add_new_message(message, room, true);
|
add_new_message(message, room, true);
|
||||||
}
|
}
|
||||||
resize_emoji($body_block);
|
|
||||||
MathJax.Hub.Queue(["Typeset",MathJax.Hub]);
|
MathJax.Hub.Queue(["Typeset",MathJax.Hub]);
|
||||||
register_time($('.time-with-rel'));
|
register_time($('.time-with-rel'));
|
||||||
remove_unread_current_user();
|
remove_unread_current_user();
|
||||||
|
@ -439,10 +436,6 @@ let META_HEADER = [
|
||||||
});
|
});
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
$('.body-block').each(function() {
|
|
||||||
resize_emoji($(this));
|
|
||||||
});
|
|
||||||
|
|
||||||
$("#chat-log").show();
|
$("#chat-log").show();
|
||||||
$("#chat-log").change(function() {
|
$("#chat-log").change(function() {
|
||||||
$('#chat-log').scrollTop($('#chat-log')[0].scrollHeight);
|
$('#chat-log').scrollTop($('#chat-log')[0].scrollHeight);
|
||||||
|
|
|
@ -21,7 +21,7 @@
|
||||||
</a>
|
</a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<div class="message-text message-text-other">
|
<div class="message-text message-text-other">
|
||||||
{{message.body | markdown('comment', MATH_ENGINE, hard_wrap=True)|reference|str|safe }}
|
{{message.body|markdown(hard_wrap=True)|reference|str|safe }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</span>
|
</span>
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
{{ comment.body|markdown('comment', MATH_ENGINE)|reference|str|safe }}
|
{{ comment.body|markdown|reference|str|safe }}
|
|
@ -13,6 +13,6 @@
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endwith %}
|
{% endwith %}
|
||||||
<div class='blog-description content-description'>
|
<div class='blog-description content-description'>
|
||||||
{{ comment.body |markdown("comment", MATH_ENGINE)|reference|str|safe }}
|
{{ comment.body |markdown|reference|str|safe }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
|
@ -93,7 +93,7 @@
|
||||||
</div>
|
</div>
|
||||||
<div class="content content-description">
|
<div class="content content-description">
|
||||||
<div class="comment-body"{% if node.score <= vote_hide_threshold %} style="display:none"{% endif %}>
|
<div class="comment-body"{% if node.score <= vote_hide_threshold %} style="display:none"{% endif %}>
|
||||||
{{ node.body|markdown('comment', MATH_ENGINE, True)|reference|str|safe }}
|
{{ node.body|markdown|reference|str|safe }}
|
||||||
</div>
|
</div>
|
||||||
{% if node.score <= vote_hide_threshold %}
|
{% if node.score <= vote_hide_threshold %}
|
||||||
<div class="comment-body bad-comment-body">
|
<div class="comment-body bad-comment-body">
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
{{ preview_data|markdown('comment', MATH_ENGINE)|reference|str|safe }}
|
{{ preview_data|markdown|reference|str|safe }}
|
||||||
{% if REQUIRE_JAX %}
|
{% if REQUIRE_JAX %}
|
||||||
<div data-config="{{ static('mathjax_config.js') }}" class="require-mathjax-support"></div>
|
<div data-config="{{ static('mathjax_config.js') }}" class="require-mathjax-support"></div>
|
||||||
{% endif %}
|
{% endif %}
|
|
@ -1,3 +1,3 @@
|
||||||
{% with node=revision.field_dict %}
|
{% with node=revision.field_dict %}
|
||||||
<div class="comment-body">{{ node.body|markdown('comment', MATH_ENGINE)|reference|str|safe }}</div>
|
<div class="comment-body">{{ node.body|markdown|reference|str|safe }}</div>
|
||||||
{% endwith %}
|
{% endwith %}
|
||||||
|
|
|
@ -69,7 +69,7 @@
|
||||||
|
|
||||||
<div class="content-description">
|
<div class="content-description">
|
||||||
{% cache 3600 'contest_html' contest.id MATH_ENGINE %}
|
{% cache 3600 'contest_html' contest.id MATH_ENGINE %}
|
||||||
{{ contest.description|markdown('contest', MATH_ENGINE)|reference|str|safe }}
|
{{ contest.description|markdown|reference|str|safe }}
|
||||||
{% endcache %}
|
{% endcache %}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
{{ preview_data|markdown('contest', MATH_ENGINE)|reference|str|safe }}
|
{{ preview_data|markdown|reference|str|safe }}
|
||||||
{% if REQUIRE_JAX %}
|
{% if REQUIRE_JAX %}
|
||||||
<div data-config="{{ static('mathjax_config.js') }}" class="require-mathjax-support"></div>
|
<div data-config="{{ static('mathjax_config.js') }}" class="require-mathjax-support"></div>
|
||||||
{% endif %}
|
{% endif %}
|
|
@ -4,4 +4,4 @@
|
||||||
<hr>
|
<hr>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
{{ tag.description|markdown('contest_tag') }}
|
{{ tag.description|markdown }}
|
||||||
|
|
|
@ -10,5 +10,5 @@
|
||||||
{% block header %}{% include "flatpages/admin_link.html" %}{% endblock %}
|
{% block header %}{% include "flatpages/admin_link.html" %}{% endblock %}
|
||||||
|
|
||||||
{% block body %}
|
{% block body %}
|
||||||
<div class="content-description">{{ flatpage.content|markdown('solution')|reference|str|safe }}</div>
|
<div class="content-description">{{ flatpage.content|markdown|reference|str|safe }}</div>
|
||||||
{% endblock %}
|
{% endblock %}
|
|
@ -1,7 +1,7 @@
|
||||||
{% extends "flatpages/markdown.html" %}
|
{% extends "flatpages/markdown.html" %}
|
||||||
|
|
||||||
{% block body %}
|
{% block body %}
|
||||||
<div class="content-description">{{ flatpage.content|markdown('solution', MATH_ENGINE)|reference }}</div>
|
<div class="content-description">{{ flatpage.content|markdown|reference }}</div>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block bodyend %}
|
{% block bodyend %}
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
{{ preview_data|markdown('license') }}
|
{{ preview_data|markdown }}
|
|
@ -1,7 +1,7 @@
|
||||||
{% extends "common-content.html" %}
|
{% extends "common-content.html" %}
|
||||||
{% block description %}
|
{% block description %}
|
||||||
{% cache 3600 'license_html' license.id %}
|
{% cache 3600 'license_html' license.id %}
|
||||||
{{ license.text|markdown('license') }}
|
{{ license.text|markdown }}
|
||||||
{% endcache %}
|
{% endcache %}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
|
|
|
@ -39,7 +39,7 @@
|
||||||
<div class="sidebox-content">
|
<div class="sidebox-content">
|
||||||
<div style="margin: 0.3em;">
|
<div style="margin: 0.3em;">
|
||||||
{% cache 3600 'organization_html' organization.id MATH_ENGINE %}
|
{% cache 3600 'organization_html' organization.id MATH_ENGINE %}
|
||||||
{{ organization.about|markdown('organization-about', MATH_ENGINE)|reference|str|safe }}
|
{{ organization.about|markdown|reference|str|safe }}
|
||||||
{% endcache %}
|
{% endcache %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -71,7 +71,7 @@
|
||||||
<div class="sidebox-content">
|
<div class="sidebox-content">
|
||||||
<div style="margin: 0.3em;">
|
<div style="margin: 0.3em;">
|
||||||
{% cache 3600 'organization_html' organization.id MATH_ENGINE %}
|
{% cache 3600 'organization_html' organization.id MATH_ENGINE %}
|
||||||
{{ organization.about|markdown('organization-about', MATH_ENGINE)|reference|str|safe }}
|
{{ organization.about|markdown|reference|str|safe }}
|
||||||
{% endcache %}
|
{% endcache %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
{{ preview_data|markdown('organization-about', MATH_ENGINE)|reference|str|safe }}
|
{{ preview_data|markdown|reference|str|safe }}
|
||||||
{% if REQUIRE_JAX %}
|
{% if REQUIRE_JAX %}
|
||||||
<div data-config="{{ static('mathjax_config.js') }}" class="require-mathjax-support"></div>
|
<div data-config="{{ static('mathjax_config.js') }}" class="require-mathjax-support"></div>
|
||||||
{% endif %}
|
{% endif %}
|
|
@ -26,7 +26,7 @@
|
||||||
<p>Authors: {{ link_users(authors) }}</p>
|
<p>Authors: {{ link_users(authors) }}</p>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endwith %}
|
{% endwith %}
|
||||||
{{ solution.content|markdown('solution', MATH_ENGINE)|reference|str|safe }}
|
{{ solution.content|markdown|reference|str|safe }}
|
||||||
</div>
|
</div>
|
||||||
<hr>
|
<hr>
|
||||||
{% include "comments/list.html" %}
|
{% include "comments/list.html" %}
|
||||||
|
|
|
@ -54,7 +54,7 @@
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<div class='blog-description content-description'>
|
<div class='blog-description content-description'>
|
||||||
{% cache 86400 'problem_html' problem.id MATH_ENGINE LANGUAGE_CODE %}
|
{% cache 86400 'problem_html' problem.id MATH_ENGINE LANGUAGE_CODE %}
|
||||||
{{ problem.description|markdown("problem", MATH_ENGINE)|reference|str|safe }}
|
{{ problem.description|markdown|reference|str|safe }}
|
||||||
{% endcache %}
|
{% endcache %}
|
||||||
{% if problem.pdf_description %}
|
{% if problem.pdf_description %}
|
||||||
<embed src="{{url('problem_pdf_description', problem.code)}}" width="100%" height="500" type="application/pdf" style="margin-top: 0.5em">
|
<embed src="{{url('problem_pdf_description', problem.code)}}" width="100%" height="500" type="application/pdf" style="margin-top: 0.5em">
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
{{ preview_data|markdown('problem', MATH_ENGINE)|reference|str|safe }}
|
{{ preview_data|markdown|reference|str|safe }}
|
||||||
{% if REQUIRE_JAX %}
|
{% if REQUIRE_JAX %}
|
||||||
<div data-config="{{ static('mathjax_config.js') }}" class="require-mathjax-support"></div>
|
<div data-config="{{ static('mathjax_config.js') }}" class="require-mathjax-support"></div>
|
||||||
{% endif %}
|
{% endif %}
|
|
@ -376,7 +376,7 @@
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
{% cache 86400 'problem_html' problem.id MATH_ENGINE LANGUAGE_CODE %}
|
{% cache 86400 'problem_html' problem.id MATH_ENGINE LANGUAGE_CODE %}
|
||||||
{{ description|markdown("problem", MATH_ENGINE)|reference|str|safe }}
|
{{ description|markdown|reference|str|safe }}
|
||||||
{% endcache %}
|
{% endcache %}
|
||||||
|
|
||||||
{% if problem.pdf_description %}
|
{% if problem.pdf_description %}
|
||||||
|
@ -426,7 +426,7 @@
|
||||||
<div class="problem-clarification">
|
<div class="problem-clarification">
|
||||||
<div class="time">{{ relative_time(clarification.date) }}</div>
|
<div class="time">{{ relative_time(clarification.date) }}</div>
|
||||||
<span class="body">
|
<span class="body">
|
||||||
{{ clarification.description|markdown('problem', MATH_ENGINE)|reference }}
|
{{ clarification.description|markdown|reference }}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
|
|
@ -3,8 +3,11 @@
|
||||||
<head>
|
<head>
|
||||||
<link rel="stylesheet" href="{{ static('style.css') }}">
|
<link rel="stylesheet" href="{{ static('style.css') }}">
|
||||||
<link rel="stylesheet" href="{{ static('pygment-github.css') }}" type="text/css">
|
<link rel="stylesheet" href="{{ static('pygment-github.css') }}" type="text/css">
|
||||||
|
<link rel="stylesheet" href="{{ static('markdown.css') }}">
|
||||||
<link rel="stylesheet" href="style.css">
|
<link rel="stylesheet" href="style.css">
|
||||||
<link rel="stylesheet" href="pygment-github.css" type="text/css">
|
<link rel="stylesheet" href="pygment-github.css" type="text/css">
|
||||||
|
<link rel="stylesheet" href="markdown.css">
|
||||||
|
|
||||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
|
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
|
||||||
<style>
|
<style>
|
||||||
html {
|
html {
|
||||||
|
@ -87,7 +90,7 @@
|
||||||
</div>
|
</div>
|
||||||
<hr style="clear: both;">
|
<hr style="clear: both;">
|
||||||
<div class="content-description printing">
|
<div class="content-description printing">
|
||||||
{{ description|markdown('problem', 'tex')|reference|absolutify(url)|str|safe }}
|
{{ description|markdown|reference|absolutify(url)|str|safe }}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script type="text/javascript" src="{{ static('mathjax_config.js') }}"></script>
|
<script type="text/javascript" src="{{ static('mathjax_config.js') }}"></script>
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
{{ preview_data|markdown('solution', MATH_ENGINE)|reference|str|safe }}
|
{{ preview_data|markdown|reference|str|safe }}
|
||||||
{% if REQUIRE_JAX %}
|
{% if REQUIRE_JAX %}
|
||||||
<div data-config="{{ static('mathjax_config.js') }}" class="require-mathjax-support"></div>
|
<div data-config="{{ static('mathjax_config.js') }}" class="require-mathjax-support"></div>
|
||||||
{% endif %}
|
{% endif %}
|
|
@ -47,7 +47,7 @@
|
||||||
{% if language.description %}
|
{% if language.description %}
|
||||||
<div class="content-description">
|
<div class="content-description">
|
||||||
{% cache 86400 'language_html' language.id %}
|
{% cache 86400 'language_html' language.id %}
|
||||||
{{ language.description|markdown('language') }}
|
{{ language.description|markdown }}
|
||||||
{% endcache %}
|
{% endcache %}
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
|
@ -20,6 +20,6 @@
|
||||||
{{link_user(ticket.messages.last().user)}} {{_(' replied')}}
|
{{link_user(ticket.messages.last().user)}} {{_(' replied')}}
|
||||||
</div>
|
</div>
|
||||||
<div class='blog-description content-description'>
|
<div class='blog-description content-description'>
|
||||||
{{ ticket.messages.last().body |markdown("ticket", MATH_ENGINE)|reference|str|safe }}
|
{{ ticket.messages.last().body |markdown|reference|str|safe }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
|
@ -16,7 +16,7 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="content content-description">
|
<div class="content content-description">
|
||||||
{{ message.body|markdown('ticket', MATH_ENGINE)|reference|str|safe }}
|
{{ message.body|markdown|reference|str|safe }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
{{ preview_data|markdown('ticket', MATH_ENGINE)|reference|str|safe }}
|
{{ preview_data|markdown|reference|str|safe }}
|
||||||
{% if REQUIRE_JAX %}
|
{% if REQUIRE_JAX %}
|
||||||
<div data-config="{{ static('mathjax_config.js') }}" class="require-mathjax-support"></div>
|
<div data-config="{{ static('mathjax_config.js') }}" class="require-mathjax-support"></div>
|
||||||
{% endif %}
|
{% endif %}
|
|
@ -1,4 +1,4 @@
|
||||||
{{ preview_data|markdown('self-description', MATH_ENGINE)|reference|str|safe }}
|
{{ preview_data|markdown|reference|str|safe }}
|
||||||
{% if REQUIRE_JAX %}
|
{% if REQUIRE_JAX %}
|
||||||
<div data-config="{{ static('mathjax_config.js') }}" class="require-mathjax-support"></div>
|
<div data-config="{{ static('mathjax_config.js') }}" class="require-mathjax-support"></div>
|
||||||
{% endif %}
|
{% endif %}
|
|
@ -82,7 +82,7 @@
|
||||||
{% if user.about %}
|
{% if user.about %}
|
||||||
<h4>{{ _('About') }}</h4>
|
<h4>{{ _('About') }}</h4>
|
||||||
{% cache 86400 'user_about' user.id MATH_ENGINE %}
|
{% cache 86400 'user_about' user.id MATH_ENGINE %}
|
||||||
{{ user.about|markdown('self-description', MATH_ENGINE)|reference|str|safe }}
|
{{ user.about|markdown|reference|str|safe }}
|
||||||
{% endcache %}
|
{% endcache %}
|
||||||
{% else %}
|
{% else %}
|
||||||
<i>
|
<i>
|
||||||
|
|
|
@ -38,7 +38,7 @@
|
||||||
<div class="about-td">
|
<div class="about-td">
|
||||||
{% if user.about %}
|
{% if user.about %}
|
||||||
{% cache 86400 'user_about' user.id MATH_ENGINE %}
|
{% cache 86400 'user_about' user.id MATH_ENGINE %}
|
||||||
{{ user.about|markdown('self-description', MATH_ENGINE)|reference|str|safe }}
|
{{ user.about|markdown|reference|str|safe }}
|
||||||
{% endcache %}
|
{% endcache %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
|
|
Loading…
Reference in a new issue