Change pre code css in markdown

This commit is contained in:
cuom1999 2024-01-18 19:46:41 -06:00
parent d75a498d18
commit aef795b40c
24 changed files with 200 additions and 397 deletions

View file

@ -130,7 +130,6 @@ USE_SELENIUM = False
SELENIUM_CUSTOM_CHROME_PATH = None SELENIUM_CUSTOM_CHROME_PATH = None
SELENIUM_CHROMEDRIVER_PATH = "chromedriver" SELENIUM_CHROMEDRIVER_PATH = "chromedriver"
PYGMENT_THEME = "pygment-github.css"
INLINE_JQUERY = True INLINE_JQUERY = True
INLINE_FONTAWESOME = True INLINE_FONTAWESOME = True
JQUERY_JS = "//ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js" JQUERY_JS = "//ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js"

View file

@ -1,44 +1,13 @@
from django.utils.html import escape, mark_safe from django.utils.html import escape, mark_safe
from judge.markdown import markdown
__all__ = ["highlight_code"] __all__ = ["highlight_code"]
def _make_pre_code(code): def highlight_code(code, language, linenos=True, title=None):
return mark_safe("<pre>" + escape(code) + "</pre>") linenos_option = 'linenums="1"' if linenos else ""
title_option = f'title="{title}"' if title else ""
options = f"{{.{language} {linenos_option} {title_option}}}"
value = f"```{options}\n{code}\n```\n"
try: return mark_safe(markdown(value))
import pygments
import pygments.lexers
import pygments.formatters
import pygments.util
except ImportError:
def highlight_code(code, language, cssclass=None):
return _make_pre_code(code)
else:
def highlight_code(code, language, cssclass="codehilite", linenos=True):
try:
lexer = pygments.lexers.get_lexer_by_name(language)
except pygments.util.ClassNotFound:
return _make_pre_code(code)
if linenos:
return mark_safe(
pygments.highlight(
code,
lexer,
pygments.formatters.HtmlFormatter(
cssclass=cssclass, linenos="table", wrapcode=True
),
)
)
return mark_safe(
pygments.highlight(
code,
lexer,
pygments.formatters.HtmlFormatter(cssclass=cssclass, wrapcode=True),
)
)

View file

@ -1,112 +1,7 @@
from .. import registry from .. import registry
import markdown as _markdown from judge.markdown import markdown as _markdown
import bleach
from django.utils.html import escape
from bs4 import BeautifulSoup
from pymdownx import superfences
EXTENSIONS = [
"pymdownx.arithmatex",
"pymdownx.magiclink",
"pymdownx.betterem",
"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",
"nl2br",
"mdx_breakless_lists",
]
EXTENSION_CONFIGS = {
"pymdownx.superfences": {
"custom_fences": [
{
"name": "sample",
"class": "no-border",
"format": superfences.fence_code_format,
}
]
},
}
ALLOWED_TAGS = list(bleach.sanitizer.ALLOWED_TAGS) + [
"img",
"center",
"iframe",
"div",
"span",
"table",
"tr",
"td",
"th",
"tr",
"pre",
"code",
"p",
"hr",
"h1",
"h2",
"h3",
"h4",
"h5",
"h6",
"thead",
"tbody",
"sup",
"dl",
"dt",
"dd",
"br",
"details",
"summary",
]
ALLOWED_ATTRS = ["src", "width", "height", "href", "class", "open"]
@registry.filter @registry.filter
def markdown(value, lazy_load=False): def markdown(value, lazy_load=False):
extensions = EXTENSIONS return _markdown(value, lazy_load)
html = _markdown.markdown(
value, extensions=extensions, extension_configs=EXTENSION_CONFIGS
)
# Don't clean mathjax
hash_script_tag = {}
soup = BeautifulSoup(html, "html.parser")
for script_tag in soup.find_all("script"):
allow_math_types = ["math/tex", "math/tex; mode=display"]
if script_tag.attrs.get("type", False) in allow_math_types:
hash_script_tag[str(hash(str(script_tag)))] = str(script_tag)
for hashed_tag in hash_script_tag:
tag = hash_script_tag[hashed_tag]
html = html.replace(tag, hashed_tag)
html = bleach.clean(html, tags=ALLOWED_TAGS, attributes=ALLOWED_ATTRS)
for hashed_tag in hash_script_tag:
tag = hash_script_tag[hashed_tag]
html = html.replace(hashed_tag, tag)
if not html:
html = escape(value)
if lazy_load:
soup = BeautifulSoup(html, features="html.parser")
for img in soup.findAll("img"):
if img.get("src"):
img["data-src"] = img["src"]
img["src"] = ""
for img in soup.findAll("iframe"):
if img.get("src"):
img["data-src"] = img["src"]
img["src"] = ""
html = str(soup)
return '<div class="md-typeset">%s</div>' % html

View file

@ -96,7 +96,7 @@ class Command(BaseCommand):
.replace("'//", "'https://") .replace("'//", "'https://")
) )
maker.title = problem_name maker.title = problem_name
for file in ("style.css", "pygment-github.css", "mathjax3_config.js"): for file in ("style.css", "mathjax3_config.js"):
maker.load(file, os.path.join(settings.DMOJ_RESOURCES, file)) maker.load(file, os.path.join(settings.DMOJ_RESOURCES, file))
maker.make(debug=True) maker.make(debug=True)
if not maker.success: if not maker.success:

117
judge/markdown.py Normal file
View file

@ -0,0 +1,117 @@
import markdown as _markdown
import bleach
from django.utils.html import escape
from bs4 import BeautifulSoup
from pymdownx import superfences
EXTENSIONS = [
"pymdownx.arithmatex",
"pymdownx.magiclink",
"pymdownx.betterem",
"pymdownx.details",
"pymdownx.emoji",
"pymdownx.inlinehilite",
"pymdownx.superfences",
"pymdownx.highlight",
"pymdownx.tasklist",
"markdown.extensions.footnotes",
"markdown.extensions.attr_list",
"markdown.extensions.def_list",
"markdown.extensions.tables",
"markdown.extensions.admonition",
"nl2br",
"mdx_breakless_lists",
]
EXTENSION_CONFIGS = {
"pymdownx.superfences": {
"custom_fences": [
{
"name": "sample",
"class": "no-border",
"format": superfences.fence_code_format,
}
],
},
"pymdownx.highlight": {
"auto_title": True,
"auto_title_map": {
"Text Only": "",
},
},
}
ALLOWED_TAGS = list(bleach.sanitizer.ALLOWED_TAGS) + [
"img",
"center",
"iframe",
"div",
"span",
"table",
"tr",
"td",
"th",
"tr",
"pre",
"code",
"p",
"hr",
"h1",
"h2",
"h3",
"h4",
"h5",
"h6",
"thead",
"tbody",
"sup",
"dl",
"dt",
"dd",
"br",
"details",
"summary",
]
ALLOWED_ATTRS = ["src", "width", "height", "href", "class", "open"]
def markdown(value, lazy_load=False):
extensions = EXTENSIONS
html = _markdown.markdown(
value, extensions=extensions, extension_configs=EXTENSION_CONFIGS
)
# Don't clean mathjax
hash_script_tag = {}
soup = BeautifulSoup(html, "html.parser")
for script_tag in soup.find_all("script"):
allow_math_types = ["math/tex", "math/tex; mode=display"]
if script_tag.attrs.get("type", False) in allow_math_types:
hash_script_tag[str(hash(str(script_tag)))] = str(script_tag)
for hashed_tag in hash_script_tag:
tag = hash_script_tag[hashed_tag]
html = html.replace(tag, hashed_tag)
html = bleach.clean(html, tags=ALLOWED_TAGS, attributes=ALLOWED_ATTRS)
for hashed_tag in hash_script_tag:
tag = hash_script_tag[hashed_tag]
html = html.replace(hashed_tag, tag)
if not html:
html = escape(value)
if lazy_load:
soup = BeautifulSoup(html, features="html.parser")
for img in soup.findAll("img"):
if img.get("src"):
img["data-src"] = img["src"]
img["src"] = ""
for img in soup.findAll("iframe"):
if img.get("src"):
img["data-src"] = img["src"]
img["src"] = ""
html = str(soup)
return '<div class="md-typeset content-description">%s</div>' % html

View file

@ -24,7 +24,6 @@ def get_resource(request):
scheme = "http" scheme = "http"
return { return {
"PYGMENT_THEME": settings.PYGMENT_THEME,
"INLINE_JQUERY": settings.INLINE_JQUERY, "INLINE_JQUERY": settings.INLINE_JQUERY,
"INLINE_FONTAWESOME": settings.INLINE_FONTAWESOME, "INLINE_FONTAWESOME": settings.INLINE_FONTAWESOME,
"JQUERY_JS": settings.JQUERY_JS, "JQUERY_JS": settings.JQUERY_JS,

View file

@ -406,7 +406,7 @@ class ProblemPdfView(ProblemMixin, SingleObjectMixin, View):
.replace("'//", "'https://") .replace("'//", "'https://")
) )
maker.title = problem_name maker.title = problem_name
assets = ["style.css", "pygment-github.css"] assets = ["style.css"]
if maker.math_engine == "jax": if maker.math_engine == "jax":
assets.append("mathjax3_config.js") assets.append("mathjax3_config.js")
for file in assets: for file in assets:

View file

@ -344,7 +344,7 @@ def problem_init_view(request, problem):
"problem/yaml.html", "problem/yaml.html",
{ {
"raw_source": data, "raw_source": data,
"highlighted_source": highlight_code(data, "yaml", linenos=False), "highlighted_source": highlight_code(data, "yaml", linenos=True),
"title": _("Generated init.yml for %s") % problem.name, "title": _("Generated init.yml for %s") % problem.name,
"content_title": mark_safe( "content_title": mark_safe(
escape(_("Generated init.yml for %s")) escape(_("Generated init.yml for %s"))

View file

@ -113,22 +113,6 @@ class SubmissionDetailBase(LoginRequiredMixin, TitleMixin, SubmissionMixin, Deta
) )
class SubmissionSource(SubmissionDetailBase):
template_name = "submission/source.html"
def get_queryset(self):
return super().get_queryset().select_related("source")
def get_context_data(self, **kwargs):
context = super(SubmissionSource, self).get_context_data(**kwargs)
submission = self.object
context["raw_source"] = submission.source.source.rstrip("\n")
context["highlighted_source"] = highlight_code(
submission.source.source, submission.language.pygments, linenos=False
)
return context
def get_hidden_subtasks(request, submission): def get_hidden_subtasks(request, submission):
contest = submission.contest_object contest = submission.contest_object
if contest and contest.is_editable_by(request.user): if contest and contest.is_editable_by(request.user):
@ -227,9 +211,11 @@ class SubmissionStatus(SubmissionDetailBase):
) )
context["time_limit"] = submission.problem.time_limit context["time_limit"] = submission.problem.time_limit
context["can_see_testcases"] = False context["can_see_testcases"] = False
context["raw_source"] = submission.source.source.rstrip("\n")
context["highlighted_source"] = highlight_code( context["highlighted_source"] = highlight_code(
submission.source.source, submission.language.pygments, linenos=False submission.source.source,
submission.language.pygments,
linenos=True,
title=submission.language,
) )
contest = submission.contest_or_none contest = submission.contest_or_none
@ -265,7 +251,7 @@ class SubmissionTestCaseQuery(SubmissionStatus):
return super(SubmissionTestCaseQuery, self).get(request, *args, **kwargs) return super(SubmissionTestCaseQuery, self).get(request, *args, **kwargs)
class SubmissionSourceRaw(SubmissionSource): class SubmissionSourceRaw(SubmissionDetailBase):
def get(self, request, *args, **kwargs): def get(self, request, *args, **kwargs):
submission = self.get_object() submission = self.get_object()
return HttpResponse(submission.source.source, content_type="text/plain") return HttpResponse(submission.source.source, content_type="text/plain")

View file

@ -117,7 +117,6 @@ else:
class Media: class Media:
css = { css = {
"all": [ "all": [
"pygment-github.css",
"table.css", "table.css",
"ranks.css", "ranks.css",
"dmmd-preview.css", "dmmd-preview.css",

View file

@ -277,6 +277,34 @@ function registerPopper($trigger, $dropdown) {
}) })
} }
function populateCopyButton() {
var copyButton;
$('pre code').each(function () {
$(this).before($('<div>', {'class': 'copy-clipboard'})
.append(copyButton = $('<span>', {
'class': 'btn-clipboard',
'data-clipboard-text': $(this).text(),
'title': 'Click to copy'
}).append('<i class="fa fa-copy"></i><span style="margin-left: 2px">Copy</span>')));
$(copyButton.get(0)).mouseleave(function () {
$(this).attr('class', 'btn-clipboard');
$(this).removeAttr('aria-label');
});
var curClipboard = new Clipboard(copyButton.get(0));
curClipboard.on('success', function (e) {
e.clearSelection();
showTooltip(e.trigger, 'Copied!');
});
curClipboard.on('error', function (e) {
showTooltip(e.trigger, fallbackMessage(e.action));
});
});
}
function onWindowReady() { function onWindowReady() {
// http://stackoverflow.com/a/1060034/1090657 // http://stackoverflow.com/a/1060034/1090657
var hidden = 'hidden'; var hidden = 'hidden';
@ -370,31 +398,8 @@ function onWindowReady() {
}) })
$('#logout').on('click', () => $('#logout-form').submit()); $('#logout').on('click', () => $('#logout-form').submit());
var copyButton; populateCopyButton();
$('pre code').each(function () {
$(this).parent().before($('<div>', {'class': 'copy-clipboard'})
.append(copyButton = $('<span>', {
'class': 'btn-clipboard',
'data-clipboard-text': $(this).text(),
'title': 'Click to copy'
}).text('Copy')));
$(copyButton.get(0)).mouseleave(function () {
$(this).attr('class', 'btn-clipboard');
$(this).removeAttr('aria-label');
});
var curClipboard = new Clipboard(copyButton.get(0));
curClipboard.on('success', function (e) {
e.clearSelection();
showTooltip(e.trigger, 'Copied!');
});
curClipboard.on('error', function (e) {
showTooltip(e.trigger, fallbackMessage(e.action));
});
});
$('a').click(function() { $('a').click(function() {
var href = $(this).attr('href'); var href = $(this).attr('href');
if (!href || href === '#' || href.startsWith("javascript")) { if (!href || href === '#' || href.startsWith("javascript")) {

View file

@ -21,11 +21,9 @@
font-family: $monospace-fonts !important; font-family: $monospace-fonts !important;
margin: 0 2px; margin: 0 2px;
padding: 0 5px; padding: 0 5px;
border: 1px solid $border_gray; background-color: var(--md-code-bg-color);
background-color: #f8f8f8;
border-radius: $widget_border_radius; border-radius: $widget_border_radius;
font-size: 0.95em; color: var(--md-code-fg-color);
color: #444;
} }
pre { pre {
@ -36,17 +34,16 @@
padding: 0; padding: 0;
background: transparent; background: transparent;
font-size: 1em; font-size: 1em;
color: black; color: var(--md-code-fg-color);
} }
white-space: pre-wrap; white-space: pre-wrap;
word-wrap: break-word; word-wrap: break-word;
padding: 1em; padding: 0.5em 1em;
border: 1px solid $border_gray; background-color: var(--md-code-bg-color);
background-color: #f8f8f8; color: var(--md-code-fg-color);
color: black; border-radius: 3px;
border-radius: $widget_border_radius;
} }
pre.no-border { pre.no-border {
@ -57,6 +54,10 @@
border-radius: none; border-radius: none;
} }
.linenos pre {
padding-right: 0;
}
b, strong { b, strong {
font-weight: bold; font-weight: bold;
} }
@ -170,7 +171,10 @@ pre {
} }
@media (min-width: 700px) { @media (min-width: 800px) {
.content-description pre:has(code) {
min-width: 20em;
}
#common-content { #common-content {
display: flex; display: flex;
flex-direction: row-reverse; flex-direction: row-reverse;
@ -206,7 +210,7 @@ pre {
} }
} }
@media not all and (min-width: 700px) { @media not all and (min-width: 800px) {
#content-right .info-float { #content-right .info-float {
float: none; float: none;
width: 100% !important; width: 100% !important;

View file

@ -4295,7 +4295,7 @@ html .md-typeset .footnote-ref{
padding:0 1.1764705882em padding:0 1.1764705882em
} }
.highlight span.filename{ .highlight span.filename{
background-color:var(--md-code-bg-color); background-color:var(--md-default-fg-color--lighter);
border-bottom:.05rem solid var(--md-default-fg-color--lightest); border-bottom:.05rem solid var(--md-default-fg-color--lightest);
border-top-left-radius:.1rem; border-top-left-radius:.1rem;
border-top-right-radius:.1rem; border-top-right-radius:.1rem;
@ -4374,8 +4374,6 @@ html .md-typeset .footnote-ref{
background-color:var(--md-code-bg-color); background-color:var(--md-code-bg-color);
border-bottom-left-radius:.1rem; border-bottom-left-radius:.1rem;
border-top-left-radius:.1rem; border-top-left-radius:.1rem;
font-size:.85em;
padding:.7720588235em 0 .7720588235em 1.1764705882em;
-webkit-user-select:none; -webkit-user-select:none;
-moz-user-select:none; -moz-user-select:none;
-ms-user-select:none; -ms-user-select:none;

View file

@ -155,48 +155,6 @@ label[for="language"], label[for="status"] {
color: #555; color: #555;
} }
.source-ln {
color: gray;
border-right: 1px solid gray;
padding-right: 5px;
text-align: right;
a {
color: gray;
display: block;
&:hover {
text-decoration: underline;
}
&::before {
display: block;
content: " ";
margin-top: -50px;
height: 50px;
visibility: hidden;
}
}
}
.source-code pre, .source-ln pre {
margin: 0;
padding: 0;
white-space: pre;
}
.source-code {
padding-left: 15px;
width: 100%;
}
.source-wrap {
overflow-x: auto;
padding: 1em;
border-radius: 10px;
border: double 4px darkgray;
}
.statistics-table { .statistics-table {
.status { .status {
font-weight: bold; font-weight: bold;

View file

@ -200,30 +200,26 @@ input {
// Bootstrap-y copy button // Bootstrap-y copy button
.btn-clipboard { .btn-clipboard {
top: 0; top: -5px;
right: 0; right: -8px;
display: block; display: block;
font-size: 12px; font-size: 12px;
color: #767676;
cursor: pointer; cursor: pointer;
background-color: #FFF;
border: 1px solid #E1E1E8;
border-radius: 0 $widget_border_radius;
position: absolute; position: absolute;
padding: 5px 8px; padding: 5px 8px;
font-family: system-ui, "Noto Sans";
&:hover {
border-radius: $widget_border_radius;
border: 1px solid #E1E1E8;
background-color: #FFF;
}
} }
.copy-clipboard { .copy-clipboard {
position: relative; position: relative;
} }
.md-typeset .admonition .btn-clipboard,
.md-typeset details .btn-clipboard {
right: -0.6rem;
border-radius: 0 0 0 4px;
}
// Bootstrap-y tabs // Bootstrap-y tabs
.ul_tab_a_active { .ul_tab_a_active {
color: $theme_color; color: $theme_color;

View file

@ -50,9 +50,7 @@
<link rel="stylesheet" type="text/css" href="{{ static('markdown.css') }}"> <link rel="stylesheet" type="text/css" href="{{ static('markdown.css') }}">
{% compress css %} {% compress css %}
<link rel="stylesheet" href="{{ static('style.css') }}"> <link rel="stylesheet" href="{{ static('style.css') }}">
{% if PYGMENT_THEME %} {% if INLINE_FONTAWESOME %}
<link rel="stylesheet" href="{{ static(PYGMENT_THEME) }}">
{% endif %}{% if INLINE_FONTAWESOME %}
<link rel="stylesheet" href="{{ static('libs/fontawesome/font-awesome.css') }}">{% endif %} <link rel="stylesheet" href="{{ static('libs/fontawesome/font-awesome.css') }}">{% endif %}
<link rel="stylesheet" type="text/css" href="{{ static('libs/featherlight/featherlight.min.css') }}"> <link rel="stylesheet" type="text/css" href="{{ static('libs/featherlight/featherlight.min.css') }}">
<link rel="stylesheet" type="text/css" href="{{ static('libs/clipboard/tooltip.css') }}"> <link rel="stylesheet" type="text/css" href="{{ static('libs/clipboard/tooltip.css') }}">

View file

@ -113,6 +113,7 @@
$('#chat-box').scrollTop($('#chat-box')[0].scrollHeight); $('#chat-box').scrollTop($('#chat-box')[0].scrollHeight);
register_time($('.time-with-rel')); register_time($('.time-with-rel'));
MathJax.typeset(); MathJax.typeset();
populateCopyButton();
merge_authors(); merge_authors();
} }
@ -167,6 +168,7 @@
add_new_message(message, room, true); add_new_message(message, room, true);
} }
MathJax.typeset(); MathJax.typeset();
populateCopyButton();
register_time($('.time-with-rel')); register_time($('.time-with-rel'));
remove_unread_current_user(); remove_unread_current_user();
merge_authors(); merge_authors();

View file

@ -21,34 +21,6 @@
info_float.width(container.width()); info_float.width(container.width());
} }
} }
// TODO: remove this
var copyButton;
$('pre code').each(function () {
$(this).parent().before($('<div>', {'class': 'copy-clipboard'})
.append(copyButton = $('<span>', {
'class': 'btn-clipboard',
'data-clipboard-text': $(this).text(),
'title': 'Click to copy'
}).text('Copy')));
$(copyButton.get(0)).mouseleave(function () {
$(this).attr('class', 'btn-clipboard');
$(this).removeAttr('aria-label');
});
var curClipboard = new Clipboard(copyButton.get(0));
curClipboard.on('success', function (e) {
e.clearSelection();
showTooltip(e.trigger, 'Copied!');
});
curClipboard.on('error', function (e) {
showTooltip(e.trigger, fallbackMessage(e.action));
});
});
}); });
</script> </script>
{% endcompress %} {% endcompress %}

View file

@ -4,4 +4,4 @@
<hr> <hr>
{% endif %} {% endif %}
{{ tag.description|markdown }} {{ tag.description }}

View file

@ -44,15 +44,13 @@
$.ajax({ $.ajax({
url: "{{url('blog_preview')}}", url: "{{url('blog_preview')}}",
type: 'POST', type: 'POST',
headers: {
'X-CSRFToken': csrfToken, // Include the CSRF token in the headers
},
data: { data: {
preview: $(this).val() preview: $(this).val()
}, },
success: function(data) { success: function(data) {
$('#display').html(data); $('#display').html(data);
MathJax.typeset(); MathJax.typeset();
populateCopyButton();
}, },
error: function(error) { error: function(error) {
alert(error); alert(error);

View file

@ -1,20 +1,5 @@
{% extends "base.html" %} {% extends "base.html" %}
{% block body %} {% block body %}
<div class="source-wrap"> {{ highlighted_source }}</td>
<table>
<tr>
<td class="source-ln">
<div>
{% for line in raw_source.split('\n') %}
<a href="#line-{{ loop.index }}" name="line-{{ loop.index }}">
<pre>{{ loop.index }}</pre>
</a>
{% endfor %}
</div>
</td>
<td class="source-code">{{ highlighted_source }}</td>
</tr>
</table>
</div>
{% endblock %} {% endblock %}

View file

@ -13,6 +13,6 @@
{% if request.profile.id in submission.problem.editor_ids or perms.judge.edit_all_problem %} {% if request.profile.id in submission.problem.editor_ids or perms.judge.edit_all_problem %}
<hr style="float:left;width:30%"><br> <hr style="float:left;width:30%"><br>
<h4>{{ _('Error information') }}</h4> <h4>{{ _('Error information') }}</h4>
<code>{{ submission.error|highlight('pytb', linenos=False) }}</code> <code>{{ submission.error|highlight('pytb', linenos=True) }}</code>
{% endif %} {% endif %}
{% endif %} {% endif %}

View file

@ -1,64 +0,0 @@
{% extends "submission/info-base.html" %}
{% block media %}
<style>
.line {
position: relative;
}
.highlighter {
position: absolute;
width: 9999px;
top: 0;
bottom: 0;
left: 0;
right: 0;
}
a:active .line .highlighter {
background: rgba(255, 212, 0, 0.48);
}
.copy-clipboard {
margin-top: 0;
}
</style>
{% endblock %}
{% block body %}
<div style="clear: both"></div>
<br>
<div><a href="{{ url('submission_status', submission.id) }}">{{ _('View status') }}</a></div>
<div><a href="{{ url('submission_source_raw', submission.id) }}">{{ _('View raw source') }}</a></div>
{% if request.user == submission.user.user or perms.judge.resubmit_other %}
<div><a href="{{ url('problem_submit', submission.problem.code, submission.id) }}">{{ _('Resubmit') }}</a></div>
{% endif %}
{% if perms.judge.rejudge_submission %}
<div>
<form action="{{ url('submission_rejudge') }}" method="post">
{% csrf_token %}
<a href="#" onclick="parentNode.submit()">{{ _('Rejudge') }}</a>
<input type="hidden" name="id" value="{{ submission.id }}">
<input type="hidden" name="path" value="{{ url('submission_status', submission.id) }}">
</form>
</div>
{% endif %}
<br>
<hr>
<br>
<div class="source-wrap">
<table style="width: 100%">
<tr>
<td class="source-ln" style="width: 0">
<div>
{% for line in raw_source.split('\n') %}
<a href="#line-{{ loop.index }}" name="line-{{ loop.index }}">
<pre class="line">{{ loop.index }}</pre>
</a>
{% endfor %}
</div>
</td>
<td class="source-code">{{ highlighted_source }}</td>
</tr>
</table>
</div>
{% endblock %}

View file

@ -135,7 +135,7 @@
{% block body %} {% block body %}
<div style="clear: both"></div> <div style="clear: both"></div>
<br> <br>
{% if request.user == submission.user.user or perms.judge.resubmit_other %} {% if request.profile == submission.user or perms.judge.resubmit_other %}
<div><a href="{{ url('problem_submit', submission.problem.code, submission.id) }}">{{ _('Resubmit') }}</a></div> <div><a href="{{ url('problem_submit', submission.problem.code, submission.id) }}">{{ _('Resubmit') }}</a></div>
{% endif %} {% endif %}
{% if perms.judge.rejudge_submission %} {% if perms.judge.rejudge_submission %}
@ -151,21 +151,8 @@
<br> <br>
<h3 id="source-header" class="toggle closed"><i class="fa fa-chevron-right fa-fw"></i>{{_('Source code')}}</h3> <h3 id="source-header" class="toggle closed"><i class="fa fa-chevron-right fa-fw"></i>{{_('Source code')}}</h3>
<div class="source-wrap toggled" style="display: none; margin-bottom: 1em"> <div class="toggled" style="display: none; margin-bottom: 1em">
<table style="width: 100%"> {{ highlighted_source }}
<tr>
<td class="source-ln" style="width: 0">
<div>
{% for line in raw_source.split('\n') %}
<a href="#line-{{ loop.index }}" name="line-{{ loop.index }}">
<pre class="line">{{ loop.index }}</pre>
</a>
{% endfor %}
</div>
</td>
<td class="source-code">{{ highlighted_source }}</td>
</tr>
</table>
</div> </div>
<div id="test-cases">{% include "submission/status-testcases.html" %}</div> <div id="test-cases">{% include "submission/status-testcases.html" %}</div>