diff --git a/django_ace/static/django_ace/widget.js b/django_ace/static/django_ace/widget.js
index d097b06..8980ed7 100644
--- a/django_ace/static/django_ace/widget.js
+++ b/django_ace/static/django_ace/widget.js
@@ -77,15 +77,17 @@
mode = widget.getAttribute('data-mode'),
theme = widget.getAttribute('data-theme'),
wordwrap = widget.getAttribute('data-wordwrap'),
- toolbar = prev(widget),
- main_block = toolbar.parentNode;
+ toolbar = prev(widget);
+ var main_block = div.parentNode.parentNode;
- // Toolbar maximize/minimize button
- var min_max = toolbar.getElementsByClassName('django-ace-max_min');
- min_max[0].onclick = function () {
- minimizeMaximize(widget, main_block, editor);
- return false;
- };
+ if (toolbar != null) {
+ // Toolbar maximize/minimize button
+ var min_max = toolbar.getElementsByClassName('django-ace-max_min');
+ min_max[0].onclick = function () {
+ minimizeMaximize(widget, main_block, editor);
+ return false;
+ };
+ }
editor.getSession().setValue(textarea.value);
diff --git a/django_ace/widgets.py b/django_ace/widgets.py
index 38fcd75..ec4df40 100644
--- a/django_ace/widgets.py
+++ b/django_ace/widgets.py
@@ -19,6 +19,7 @@ class AceWidget(forms.Textarea):
width="100%",
height="300px",
no_ace_media=False,
+ toolbar=True,
*args,
**kwargs
):
@@ -28,6 +29,7 @@ class AceWidget(forms.Textarea):
self.width = width
self.height = height
self.ace_media = not no_ace_media
+ self.toolbar = toolbar
super(AceWidget, self).__init__(*args, **kwargs)
@property
@@ -61,10 +63,14 @@ class AceWidget(forms.Textarea):
html = "
%s" % (flatatt(ace_attrs), textarea)
- # add toolbar
- html = (
- ''
- ) % html
+ if self.toolbar:
+ toolbar = (
+ '"
+ ).format(self.width)
+ html = toolbar + html
+
+ html = '{}
'.format(html)
return mark_safe(html)
diff --git a/judge/admin/problem.py b/judge/admin/problem.py
index 88d260b..f178023 100644
--- a/judge/admin/problem.py
+++ b/judge/admin/problem.py
@@ -9,12 +9,15 @@ from django.forms import ModelForm
from django.urls import reverse_lazy
from django.utils.html import format_html
from django.utils.translation import gettext, gettext_lazy as _, ungettext
+from django_ace import AceWidget
+
from reversion.admin import VersionAdmin
from reversion_compare.admin import CompareVersionAdmin
from judge.models import (
LanguageLimit,
+ LanguageTemplate,
Problem,
ProblemClarification,
ProblemTranslation,
@@ -100,6 +103,20 @@ class LanguageLimitInline(admin.TabularInline):
form = LanguageLimitInlineForm
+class LanguageTemplateInlineForm(ModelForm):
+ class Meta:
+ widgets = {
+ "language": AdminSelect2Widget,
+ "source": AceWidget(width="600px", height="200px", toolbar=False),
+ }
+
+
+class LanguageTemplateInline(admin.TabularInline):
+ model = LanguageTemplate
+ fields = ("language", "source")
+ form = LanguageTemplateInlineForm
+
+
class ProblemClarificationForm(ModelForm):
class Meta:
if HeavyPreviewPageDownWidget is not None:
@@ -208,6 +225,7 @@ class ProblemAdmin(CompareVersionAdmin):
)
inlines = [
LanguageLimitInline,
+ LanguageTemplateInline,
ProblemClarificationInline,
ProblemSolutionInline,
ProblemTranslationInline,
diff --git a/judge/migrations/0126_languagetemplate.py b/judge/migrations/0126_languagetemplate.py
new file mode 100644
index 0000000..7555905
--- /dev/null
+++ b/judge/migrations/0126_languagetemplate.py
@@ -0,0 +1,54 @@
+# Generated by Django 2.2.25 on 2022-06-12 06:59
+
+from django.db import migrations, models
+import django.db.models.deletion
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ("judge", "0125_auto_20220602_1216"),
+ ]
+
+ operations = [
+ migrations.CreateModel(
+ name="LanguageTemplate",
+ fields=[
+ (
+ "id",
+ models.AutoField(
+ auto_created=True,
+ primary_key=True,
+ serialize=False,
+ verbose_name="ID",
+ ),
+ ),
+ (
+ "source",
+ models.TextField(max_length=65536, verbose_name="source code"),
+ ),
+ (
+ "language",
+ models.ForeignKey(
+ on_delete=django.db.models.deletion.CASCADE,
+ to="judge.Language",
+ verbose_name="language",
+ ),
+ ),
+ (
+ "problem",
+ models.ForeignKey(
+ on_delete=django.db.models.deletion.CASCADE,
+ related_name="language_templates",
+ to="judge.Problem",
+ verbose_name="problem",
+ ),
+ ),
+ ],
+ options={
+ "verbose_name": "language-specific template",
+ "verbose_name_plural": "language-specific templates",
+ "unique_together": {("problem", "language")},
+ },
+ ),
+ ]
diff --git a/judge/models/__init__.py b/judge/models/__init__.py
index a8232da..dcb7a1e 100644
--- a/judge/models/__init__.py
+++ b/judge/models/__init__.py
@@ -20,6 +20,7 @@ from judge.models.interface import BlogPost, MiscConfig, NavigationBar, validate
from judge.models.message import PrivateMessage, PrivateMessageThread
from judge.models.problem import (
LanguageLimit,
+ LanguageTemplate,
License,
Problem,
ProblemClarification,
diff --git a/judge/models/problem.py b/judge/models/problem.py
index 4562775..6ef8bdc 100644
--- a/judge/models/problem.py
+++ b/judge/models/problem.py
@@ -639,6 +639,24 @@ class LanguageLimit(models.Model):
verbose_name_plural = _("language-specific resource limits")
+class LanguageTemplate(models.Model):
+ problem = models.ForeignKey(
+ Problem,
+ verbose_name=_("problem"),
+ related_name="language_templates",
+ on_delete=CASCADE,
+ )
+ language = models.ForeignKey(
+ Language, verbose_name=_("language"), on_delete=CASCADE
+ )
+ source = models.TextField(verbose_name=_("source code"), max_length=65536)
+
+ class Meta:
+ unique_together = ("problem", "language")
+ verbose_name = _("language-specific template")
+ verbose_name_plural = _("language-specific templates")
+
+
class Solution(models.Model):
problem = models.OneToOneField(
Problem,
diff --git a/judge/views/problem.py b/judge/views/problem.py
index aa913d8..a05936f 100644
--- a/judge/views/problem.py
+++ b/judge/views/problem.py
@@ -53,6 +53,7 @@ from judge.models import (
Organization,
VolunteerProblemVote,
Profile,
+ LanguageTemplate,
)
from judge.pdf_problems import DefaultPdfMaker, HAS_PDF
from judge.utils.diggpaginator import DiggPaginator
@@ -895,10 +896,21 @@ class ProblemFeed(ProblemList):
class LanguageTemplateAjax(View):
def get(self, request, *args, **kwargs):
try:
- language = get_object_or_404(Language, id=int(request.GET.get("id", 0)))
+ problem = request.GET.get("problem", None)
+ lang_id = int(request.GET.get("id", 0))
+ res = None
+ if problem:
+ try:
+ res = LanguageTemplate.objects.get(
+ language__id=lang_id, problem__id=problem
+ ).source
+ except ObjectDoesNotExist:
+ pass
+ if not res:
+ res = get_object_or_404(Language, id=lang_id).template
except ValueError:
raise Http404()
- return HttpResponse(language.template, content_type="text/plain")
+ return HttpResponse(res, content_type="text/plain")
class RandomProblem(ProblemList):
@@ -1107,6 +1119,7 @@ def problem_submit(request, problem, submission=None):
"submissions_left": submissions_left,
"ACE_URL": settings.ACE_URL,
"default_lang": default_lang,
+ "problem_id": problem.id,
},
)
diff --git a/templates/problem/submit.html b/templates/problem/submit.html
index f940e23..a10c667 100644
--- a/templates/problem/submit.html
+++ b/templates/problem/submit.html
@@ -30,7 +30,8 @@
update_submit_area(code);
} else {
$.get('{{ url('language_template_ajax') }}', {
- id: lang_id
+ id: lang_id,
+ problem: {{problem_id}}
}).done(function (template) {
update_submit_area(template);
});