.
This commit is contained in:
@@ -0,0 +1,20 @@
|
|||||||
|
# Generated by Django 5.0.2 on 2024-10-09 20:39
|
||||||
|
|
||||||
|
from django.conf import settings
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('anatomy', '0021_alter_anatomyquestion_answer_help_and_more'),
|
||||||
|
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='exam',
|
||||||
|
name='markers',
|
||||||
|
field=models.ManyToManyField(blank=True, help_text='Authorised markers for the exam', related_name='anatomy_exam_markers', to=settings.AUTH_USER_MODEL),
|
||||||
|
),
|
||||||
|
]
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
# Generated by Django 5.0.2 on 2024-10-09 20:39
|
||||||
|
|
||||||
|
from django.conf import settings
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('atlas', '0061_alter_seriesfinding_current_image_id_index'),
|
||||||
|
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='casecollection',
|
||||||
|
name='markers',
|
||||||
|
field=models.ManyToManyField(blank=True, help_text='Authorised markers for the exam', related_name='casecollection_markers', to=settings.AUTH_USER_MODEL),
|
||||||
|
),
|
||||||
|
]
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
# Generated by Django 5.0.2 on 2024-10-09 20:39
|
||||||
|
|
||||||
|
from django.conf import settings
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('longs', '0028_alter_exam_double_mark'),
|
||||||
|
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='exam',
|
||||||
|
name='markers',
|
||||||
|
field=models.ManyToManyField(blank=True, help_text='Authorised markers for the exam', related_name='long_exam_markers', to=settings.AUTH_USER_MODEL),
|
||||||
|
),
|
||||||
|
]
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
# Generated by Django 5.0.2 on 2024-10-09 20:39
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('oef', '0003_alter_formats_format'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='Specialty',
|
||||||
|
fields=[
|
||||||
|
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('name', models.CharField(max_length=200)),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='formats',
|
||||||
|
name='specialties',
|
||||||
|
field=models.ManyToManyField(blank=True, to='oef.specialty'),
|
||||||
|
),
|
||||||
|
]
|
||||||
+9
-1
@@ -37,6 +37,8 @@ class Formats(models.Model):
|
|||||||
|
|
||||||
inherits = models.ForeignKey('self', on_delete=models.CASCADE, null=True, blank=True)
|
inherits = models.ForeignKey('self', on_delete=models.CASCADE, null=True, blank=True)
|
||||||
|
|
||||||
|
specialties = models.ManyToManyField('Specialty', blank=True)
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return self.name
|
return self.name
|
||||||
|
|
||||||
@@ -58,4 +60,10 @@ class Formats(models.Model):
|
|||||||
formats.append(self.format)
|
formats.append(self.format)
|
||||||
|
|
||||||
formats.reverse()
|
formats.reverse()
|
||||||
return "\n".join(formats)
|
return "\n".join(formats)
|
||||||
|
|
||||||
|
class Specialty(models.Model):
|
||||||
|
name = models.CharField(max_length=200)
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return self.name
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
|
||||||
|
{% for entry in entries %}
|
||||||
|
<li>{{entry.exam_code}}</li>
|
||||||
|
{% endfor %}
|
||||||
|
|
||||||
@@ -1,113 +1,141 @@
|
|||||||
{% extends 'oef/base.html' %}
|
{% extends 'oef/base.html' %}
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<h2>Questions</h2>
|
<h2>Questions</h2>
|
||||||
There are {{ questions|length }} question sets.
|
There are {{ questions|length }} question sets.
|
||||||
|
|
||||||
<div>Filter by:
|
<div>Filter by:
|
||||||
<textarea type="text" id="search-text" name="search" placeholder="Search text.." rows=5 cols=50 ></textarea>
|
<textarea type="text" id="search-text" name="search" placeholder="Search text.." rows=5 cols=50 ></textarea>
|
||||||
|
|
||||||
<button
|
<button
|
||||||
|
|
||||||
_="on click repeat for el in <li.question/>
|
|
||||||
set p to the first <pre/> in el then
|
|
||||||
if p.title does not contain value of #search-text then hide el">Filter</button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="offcanvas offcanvas-end bg-dark" tabindex="-1" id="offcanvasExample" data-bs-scroll="true" data-bs-backdrop="false" aria-labelledby="offcanvasExampleLabel">
|
_="on click repeat for el in <li.question/>
|
||||||
<div class="offcanvas-header">
|
set p to the first <pre/> in el then
|
||||||
<h5 class="offcanvas-title" id="offcanvasExampleLabel">Comparisons</h5>
|
if p.title does not contain value of #search-text then hide el">Filter</button>
|
||||||
<button type="button" class="btn-close text-reset" data-bs-dismiss="offcanvas" aria-label="Close"></button>
|
|
||||||
</div>
|
|
||||||
<div class="offcanvas-body">
|
|
||||||
<div>
|
|
||||||
<ul id="sidebar-list">
|
|
||||||
</ul>
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
<button _="on click remove <li.sidebar-item/>">Clear</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<ul>
|
<div class="offcanvas offcanvas-end bg-dark" tabindex="-1" id="offcanvasExample" data-bs-scroll="true" data-bs-backdrop="false" aria-labelledby="offcanvasExampleLabel">
|
||||||
{% for question in questions %}
|
<div class="offcanvas-header">
|
||||||
<li class="question">
|
<h5 class="offcanvas-title" id="offcanvasExampleLabel">Comparisons</h5>
|
||||||
|
<button type="button" class="btn-close text-reset" data-bs-dismiss="offcanvas" aria-label="Close"></button>
|
||||||
|
</div>
|
||||||
|
<div class="offcanvas-body">
|
||||||
|
<div>
|
||||||
|
<ul id="sidebar-list">
|
||||||
|
</ul>
|
||||||
|
<ul id="sidebar-codes">
|
||||||
|
</li>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<button _="on click remove <li.sidebar-item/>">Clear</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<ul>
|
||||||
|
{% for question, format in questions_and_formats %}
|
||||||
|
<li class="question">
|
||||||
|
<span class="format-name">
|
||||||
|
{% if format %}
|
||||||
|
<a href='{% url "oef:format_update" format.pk %}'>{{format.name}}</a>
|
||||||
|
{% endif %}
|
||||||
|
</span>
|
||||||
<pre class="pre-question" title="{{ question }}">{{ question }}</pre>
|
<pre class="pre-question" title="{{ question }}">{{ question }}</pre>
|
||||||
<form>
|
<form>
|
||||||
<input type="hidden" name="question" value="{{ question }}">
|
<input type="hidden" name="question" value="{{ question }}">
|
||||||
<button
|
<button
|
||||||
hx-post="{% url 'oef:entries_by_question' %}"
|
hx-post="{% url 'oef:entries_by_question' %}"
|
||||||
hx-target="next .result"
|
hx-target="next .result"
|
||||||
_="on click toggle @disabled until htmx:afterOnLoad then remove me"
|
_="on click toggle @disabled until htmx:afterOnLoad then remove me"
|
||||||
>View exams</button>
|
>View exams</button>
|
||||||
<span class="result"></span>
|
<span class="result"></span>
|
||||||
<button
|
<button
|
||||||
hx-post="{% url 'oef:questions_sym' %}"
|
hx-post="{% url 'oef:questions_sym' %}"
|
||||||
hx-target="next .results-sym"
|
hx-target="next .results-sym"
|
||||||
{% comment %} _="on click toggle @disabled until htmx:afterOnLoad then remove me" {% endcomment %}
|
{% comment %} _="on click toggle @disabled until htmx:afterOnLoad then remove me" {% endcomment %}
|
||||||
>View similar</button>
|
>View similar</button>
|
||||||
<span class="results-sym"></span>
|
<span class="results-sym"></span>
|
||||||
|
|
||||||
</form>
|
<button class="btn btn-sm" type="button" _="on click set #question1.value to the title of the previous <pre/>">
|
||||||
<button class="btn btn-sm" _="on click set #question1.value to the title of the previous <pre/>">
|
Diff1
|
||||||
Diff1
|
</button>
|
||||||
</button>
|
<button class="btn btn-sm" type="button" _="on click set #question2.value to the title of the previous <pre/> then go to #question2 ">
|
||||||
<button class="btn btn-sm" _="on click set #question2.value to the title of the previous <pre/> then go to #question2 ">
|
Diff2
|
||||||
Diff2
|
</button>
|
||||||
</button>
|
<button class="btn btn-sm" type="button"
|
||||||
<button class="btn btn-sm" type="button"
|
_="on click set q to the title of the previous <pre/> then log q then set el to `<li class='sidebar-item'><pre>${q}</pre></li>` then log el then put el at end of #sidebar-list then
|
||||||
_="on click set q to the title of the previous <pre/> then log q then set el to `<li class='sidebar-item'><pre>${q}</pre></li>` then log el then put el at end of #sidebar-list then
|
js
|
||||||
js
|
new bootstrap.Offcanvas(document.getElementById('offcanvasExample')).show();
|
||||||
new bootstrap.Offcanvas(document.getElementById('offcanvasExample')).show();
|
end
|
||||||
end
|
">
|
||||||
">
|
Add to sidebar
|
||||||
Add to sidebar
|
</button>
|
||||||
</button>
|
<button
|
||||||
</li>
|
class="btn btn-sm" type="button"
|
||||||
{% endfor %}
|
hx-post="{% url 'oef:entries_by_question_codes' %}"
|
||||||
</ul>
|
hx-target="#sidebar-codes"
|
||||||
|
hx-swap="beforeend"
|
||||||
|
_="on click toggle @disabled until htmx:afterOnLoad then remove me then
|
||||||
|
js
|
||||||
|
new bootstrap.Offcanvas(document.getElementById('offcanvasExample')).show();
|
||||||
|
end
|
||||||
|
"
|
||||||
|
>Add codes to sidebar</button>
|
||||||
|
<input type="text" class="format-name" placeholder="format name" style="display:none">
|
||||||
|
<button
|
||||||
|
class="btn btn-sm save-format-button" type="button" style="display:none"
|
||||||
|
hx-post="{% url 'oef:formats_save' %}"
|
||||||
|
>Save format</button>
|
||||||
|
<button class="btn btn-sm" type="button"
|
||||||
|
_="on click show previous .save-format-button then show previous .format-name then hide me">Save format</button>
|
||||||
|
</form>
|
||||||
|
</li>
|
||||||
|
{% endfor %}
|
||||||
|
</ul>
|
||||||
|
|
||||||
<div>Diff:</div>
|
<div>Diff:</div>
|
||||||
<form>
|
<form>
|
||||||
<textarea id="question1" name="question1" cols=40 rows=10></textarea>
|
<textarea id="question1" name="question1" cols=40 rows=10></textarea>
|
||||||
<textarea id="question2" name="question2" cols=40 rows=10></textarea>
|
<textarea id="question2" name="question2" cols=40 rows=10></textarea>
|
||||||
<button
|
<button
|
||||||
hx-post='{% url "oef:questions_diff" %}'
|
hx-post='{% url "oef:questions_diff" %}'
|
||||||
hx-target="#diff-results">Diff</button>
|
hx-target="#diff-results">Diff</button>
|
||||||
<br/>
|
<br/>
|
||||||
<button type="button" style="font:inherit;font-size:.8em;background:#3465a4;color:white;border:none;padding: 0 .4em; border-radius: .4em" _="on click
|
<button type="button" style="font:inherit;font-size:.8em;background:#3465a4;color:white;border:none;padding: 0 .4em; border-radius: .4em" _="on click
|
||||||
writeText(#question1.value) on navigator.clipboard
|
writeText(#question1.value) on navigator.clipboard
|
||||||
put 'copied!' into me
|
put 'copied!' into me
|
||||||
wait 1s
|
wait 1s
|
||||||
put 'copy1' into me">copy1</button>
|
put 'copy1' into me">copy1</button>
|
||||||
<button type="button" style="font:inherit;font-size:.8em;background:#3465a4;color:white;border:none;padding: 0 .4em; border-radius: .4em" _="on click
|
<button type="button" style="font:inherit;font-size:.8em;background:#3465a4;color:white;border:none;padding: 0 .4em; border-radius: .4em" _="on click
|
||||||
writeText(#question2.value) on navigator.clipboard
|
writeText(#question2.value) on navigator.clipboard
|
||||||
put 'copied!' into me
|
put 'copied!' into me
|
||||||
wait 1s
|
wait 1s
|
||||||
put 'copy2' into me">copy2</button>
|
put 'copy2' into me">copy2</button>
|
||||||
<button type="button" style="font:inherit;font-size:.8em;background:#3465a4;color:white;border:none;padding: 0 .4em; border-radius: .4em" _="on click
|
<button type="button" style="font:inherit;font-size:.8em;background:#3465a4;color:white;border:none;padding: 0 .4em; border-radius: .4em" _="on click
|
||||||
repeat for pre in .pre-question
|
repeat for pre in .pre-question
|
||||||
if #question1.value is pre.title
|
if #question1.value is pre.title
|
||||||
then go to pre then break
|
then go to pre then break
|
||||||
">go1</button>
|
">go1</button>
|
||||||
<button type="button" style="font:inherit;font-size:.8em;background:#3465a4;color:white;border:none;padding: 0 .4em; border-radius: .4em" _="on click
|
<button type="button" style="font:inherit;font-size:.8em;background:#3465a4;color:white;border:none;padding: 0 .4em; border-radius: .4em" _="on click
|
||||||
repeat for pre in .pre-question
|
repeat for pre in .pre-question
|
||||||
if #question2.value is pre.title
|
if #question2.value is pre.title
|
||||||
then go to pre then break
|
then go to pre then break
|
||||||
">go2</button>
|
">go2</button>
|
||||||
<br/>results
|
<br/>results
|
||||||
<span id="diff-results"></span>
|
<span id="diff-results"></span>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
|
|
||||||
{% endblock content %}
|
{% endblock content %}
|
||||||
|
|
||||||
|
|
||||||
{% block css %}
|
{% block css %}
|
||||||
<style>
|
<style>
|
||||||
li.question {
|
li.question {
|
||||||
border: 1px solid white;
|
border: 1px solid white;
|
||||||
}
|
}
|
||||||
</style>
|
.format-name {
|
||||||
|
color: blue;
|
||||||
|
float: right;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
{% endblock css %}
|
{% endblock css %}
|
||||||
|
|
||||||
@@ -36,6 +36,9 @@ urlpatterns = [
|
|||||||
path(
|
path(
|
||||||
"entries/entries_by_question", views.entries_by_question, name="entries_by_question"
|
"entries/entries_by_question", views.entries_by_question, name="entries_by_question"
|
||||||
),
|
),
|
||||||
|
path(
|
||||||
|
"entries/entries_by_question/codes", views.entries_by_question_codes, name="entries_by_question_codes"
|
||||||
|
),
|
||||||
path(
|
path(
|
||||||
"entries/<int:pk>/update", views.EntryUpdateView.as_view(), name="entry_update"
|
"entries/<int:pk>/update", views.EntryUpdateView.as_view(), name="entry_update"
|
||||||
),
|
),
|
||||||
@@ -51,5 +54,8 @@ urlpatterns = [
|
|||||||
path(
|
path(
|
||||||
"formats/<int:pk>/apply", views.formats_apply, name="formats_apply"
|
"formats/<int:pk>/apply", views.formats_apply, name="formats_apply"
|
||||||
),
|
),
|
||||||
|
path(
|
||||||
|
"formats/save", views.formats_save, name="formats_save"
|
||||||
|
),
|
||||||
|
|
||||||
]
|
]
|
||||||
+44
-2
@@ -29,7 +29,7 @@ class FormatsCreateView(CreateView):
|
|||||||
class FormatsUpdateView(UpdateView):
|
class FormatsUpdateView(UpdateView):
|
||||||
model = Formats
|
model = Formats
|
||||||
template_name_suffix = "_update_form"
|
template_name_suffix = "_update_form"
|
||||||
fields = ['name', 'format', 'inherits']
|
fields = ['name', 'format', 'specialties', 'inherits']
|
||||||
|
|
||||||
class EntryTableView(SingleTableMixin, FilterView):
|
class EntryTableView(SingleTableMixin, FilterView):
|
||||||
model = Entry
|
model = Entry
|
||||||
@@ -96,7 +96,16 @@ def questions(request):
|
|||||||
|
|
||||||
questions = sorted(questions, key=lambda x: len(x))
|
questions = sorted(questions, key=lambda x: len(x))
|
||||||
|
|
||||||
return render(request, "oef/questions.html", {"questions": questions})
|
questions_and_formats = []
|
||||||
|
|
||||||
|
for question in questions:
|
||||||
|
try:
|
||||||
|
format = Formats.objects.get(format=question)
|
||||||
|
except Formats.DoesNotExist:
|
||||||
|
format = None
|
||||||
|
questions_and_formats.append((question, format))
|
||||||
|
|
||||||
|
return render(request, "oef/questions.html", {"questions": questions, "questions_and_formats": questions_and_formats})
|
||||||
|
|
||||||
def entries_by_question(request):
|
def entries_by_question(request):
|
||||||
|
|
||||||
@@ -108,6 +117,16 @@ def entries_by_question(request):
|
|||||||
|
|
||||||
return HttpResponse("Please provide a question")
|
return HttpResponse("Please provide a question")
|
||||||
|
|
||||||
|
def entries_by_question_codes(request):
|
||||||
|
|
||||||
|
if request.method == "POST":
|
||||||
|
question = request.POST.get("question")
|
||||||
|
entries = Entry.objects.filter(questions=question)
|
||||||
|
|
||||||
|
return render(request, "oef/entries_by_question_codes.html", {"entries": entries})
|
||||||
|
|
||||||
|
return HttpResponse("Please provide a question")
|
||||||
|
|
||||||
def questions_diff(request):
|
def questions_diff(request):
|
||||||
if request.method == "POST":
|
if request.method == "POST":
|
||||||
question1 = request.POST.get("question1", False)
|
question1 = request.POST.get("question1", False)
|
||||||
@@ -143,3 +162,26 @@ def questions_sym(request):
|
|||||||
|
|
||||||
|
|
||||||
return HttpResponse("Please provide a question set")
|
return HttpResponse("Please provide a question set")
|
||||||
|
|
||||||
|
|
||||||
|
def formats_save(request):
|
||||||
|
|
||||||
|
if request.method == "POST":
|
||||||
|
question = request.POST.get("question")
|
||||||
|
format_name = request.POST.get("format_name")
|
||||||
|
|
||||||
|
if not question:
|
||||||
|
return HttpResponse("Please provide a question")
|
||||||
|
|
||||||
|
if Formats.objects.filter(name=format_name).exists():
|
||||||
|
return HttpResponse("Format name already exists")
|
||||||
|
|
||||||
|
if Formats.objects.filter(format=question).exists():
|
||||||
|
return HttpResponse("Format questions already exists")
|
||||||
|
|
||||||
|
Formats.objects.create(name="format_name", format=question)
|
||||||
|
|
||||||
|
return HttpResponse("Format saved")
|
||||||
|
|
||||||
|
|
||||||
|
return HttpResponse("Please provide a question")
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
# Generated by Django 5.0.2 on 2024-10-09 20:39
|
||||||
|
|
||||||
|
from django.conf import settings
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('physics', '0014_rename_rapid_examquestiondetail_question'),
|
||||||
|
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='exam',
|
||||||
|
name='markers',
|
||||||
|
field=models.ManyToManyField(blank=True, help_text='Authorised markers for the exam', related_name='physics_exam_markers', to=settings.AUTH_USER_MODEL),
|
||||||
|
),
|
||||||
|
]
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
# Generated by Django 5.0.2 on 2024-10-09 20:39
|
||||||
|
|
||||||
|
from django.conf import settings
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('rapids', '0012_alter_exam_markers'),
|
||||||
|
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='exam',
|
||||||
|
name='markers',
|
||||||
|
field=models.ManyToManyField(blank=True, help_text='Authorised markers for the exam', related_name='rapid_exam_markers', to=settings.AUTH_USER_MODEL),
|
||||||
|
),
|
||||||
|
]
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
# Generated by Django 5.0.2 on 2024-10-09 20:39
|
||||||
|
|
||||||
|
from django.conf import settings
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('sbas', '0014_rename_rapid_examquestiondetail_question'),
|
||||||
|
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='exam',
|
||||||
|
name='markers',
|
||||||
|
field=models.ManyToManyField(blank=True, help_text='Authorised markers for the exam', related_name='sba_exam_markers', to=settings.AUTH_USER_MODEL),
|
||||||
|
),
|
||||||
|
]
|
||||||
Reference in New Issue
Block a user