Refactor HTML formatting to use mark_safe for safe rendering in various tables and models
This commit is contained in:
+3
-3
@@ -191,9 +191,9 @@ class AnatomyQuestion(QuestionBase):
|
|||||||
if not unmarked_answers:
|
if not unmarked_answers:
|
||||||
return "No answers to mark"
|
return "No answers to mark"
|
||||||
return format_html(
|
return format_html(
|
||||||
"<span class='warn'>{} answer unmarked:</span> {}".format(
|
"<span class='warn'>{} answer unmarked:</span> {}",
|
||||||
len(unmarked_answers), ", ".join(unmarked_answers)
|
len(unmarked_answers),
|
||||||
)
|
", ".join(unmarked_answers),
|
||||||
)
|
)
|
||||||
|
|
||||||
def get_unmarked_user_answers(self, exam_pk: int|None = None):
|
def get_unmarked_user_answers(self, exam_pk: int|None = None):
|
||||||
|
|||||||
+20
-16
@@ -19,6 +19,8 @@ from .models import (
|
|||||||
|
|
||||||
from django.utils.html import format_html
|
from django.utils.html import format_html
|
||||||
|
|
||||||
|
from django.utils.safestring import mark_safe
|
||||||
|
|
||||||
from easy_thumbnails.files import get_thumbnailer
|
from easy_thumbnails.files import get_thumbnailer
|
||||||
|
|
||||||
from easy_thumbnails.exceptions import InvalidImageFormatError
|
from easy_thumbnails.exceptions import InvalidImageFormatError
|
||||||
@@ -83,12 +85,14 @@ class CaseTable(SelectionTable):
|
|||||||
|
|
||||||
def get_view_cell(record):
|
def get_view_cell(record):
|
||||||
if record.open_access:
|
if record.open_access:
|
||||||
return format_html(f"""<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-opencollective" viewBox="0 0 16 16">
|
return mark_safe(
|
||||||
<path fill-opacity=".4" d="M12.995 8.195c0 .937-.312 1.912-.78 2.693l1.99 1.99c.976-1.327 1.6-2.966 1.6-4.683 0-1.795-.624-3.434-1.561-4.76l-2.068 2.028c.468.781.78 1.679.78 2.732h.04Z"/>
|
'<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-opencollective" viewBox="0 0 16 16">'
|
||||||
<path d="M8 13.151a4.995 4.995 0 1 1 0-9.99c1.015 0 1.951.273 2.732.82l1.95-2.03a7.805 7.805 0 1 0 .04 12.449l-1.951-2.03a5.072 5.072 0 0 1-2.732.781z"/>
|
'<path fill-opacity=".4" d="M12.995 8.195c0 .937-.312 1.912-.78 2.693l1.99 1.99c.976-1.327 1.6-2.966 1.6-4.683 0-1.795-.624-3.434-1.561-4.76l-2.068 2.028c.468.781.78 1.679.78 2.732h.04Z"/>'
|
||||||
</svg> View""")
|
'<path d="M8 13.151a4.995 4.995 0 1 1 0-9.99c1.015 0 1.951.273 2.732.82l1.95-2.03a7.805 7.805 0 1 0 .04 12.449l-1.951-2.03a5.072 5.072 0 0 1-2.732.781z"/>'
|
||||||
|
'</svg> View'
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
return f'View'
|
return 'View'
|
||||||
|
|
||||||
def render_author(self, value):
|
def render_author(self, value):
|
||||||
max_length = 15 # Set the maximum length for truncating individual author names
|
max_length = 15 # Set the maximum length for truncating individual author names
|
||||||
@@ -108,7 +112,7 @@ class CaseTable(SelectionTable):
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
# Join all authors with commas
|
# Join all authors with commas
|
||||||
return format_html(", ".join(authors))
|
return mark_safe(", ".join(authors))
|
||||||
|
|
||||||
def render_associated_cases(self, record):
|
def render_associated_cases(self, record):
|
||||||
links = []
|
links = []
|
||||||
@@ -130,7 +134,7 @@ class CaseTable(SelectionTable):
|
|||||||
except Case.DoesNotExist:
|
except Case.DoesNotExist:
|
||||||
pass
|
pass
|
||||||
if links:
|
if links:
|
||||||
return format_html(", ".join(links))
|
return mark_safe(", ".join(links))
|
||||||
return "No associated cases"
|
return "No associated cases"
|
||||||
|
|
||||||
def render_collections(self, record):
|
def render_collections(self, record):
|
||||||
@@ -151,7 +155,7 @@ class CaseTable(SelectionTable):
|
|||||||
)
|
)
|
||||||
for collection in collections
|
for collection in collections
|
||||||
]
|
]
|
||||||
return format_html(", ".join(links))
|
return mark_safe(", ".join(links))
|
||||||
return "No collections"
|
return "No collections"
|
||||||
|
|
||||||
associated_cases = tables.Column(
|
associated_cases = tables.Column(
|
||||||
@@ -166,7 +170,7 @@ class CaseTable(SelectionTable):
|
|||||||
"atlas:case_update", text="Edit", args=[A("pk")], orderable=False
|
"atlas:case_update", text="Edit", args=[A("pk")], orderable=False
|
||||||
)
|
)
|
||||||
view = tables.LinkColumn(
|
view = tables.LinkColumn(
|
||||||
"atlas:case_detail", text=get_view_cell, args=[A("pk")], orderable=False
|
"atlas:case_detail", text=get_view_cell, args=[A("pk")], accessor="pk", orderable=False
|
||||||
)
|
)
|
||||||
clone = tables.LinkColumn(
|
clone = tables.LinkColumn(
|
||||||
"atlas:case_clone", text="Clone", args=[A("pk")], orderable=False
|
"atlas:case_clone", text="Clone", args=[A("pk")], orderable=False
|
||||||
@@ -199,7 +203,7 @@ class PopupLinkColumn(tables.Column):
|
|||||||
obj = value.first()
|
obj = value.first()
|
||||||
|
|
||||||
if obj is None:
|
if obj is None:
|
||||||
return format_html("<span>No image<span>")
|
return mark_safe("<span>No image</span>")
|
||||||
|
|
||||||
return obj.get_thumbnail()[0]
|
return obj.get_thumbnail()[0]
|
||||||
|
|
||||||
@@ -209,7 +213,7 @@ class SeriesTable(SelectionTable):
|
|||||||
"atlas:series_update", text="Edit", args=[A("pk")], orderable=False
|
"atlas:series_update", text="Edit", args=[A("pk")], orderable=False
|
||||||
)
|
)
|
||||||
view = tables.LinkColumn(
|
view = tables.LinkColumn(
|
||||||
"atlas:series_detail", text="View", args=[A("pk")], orderable=False
|
"atlas:series_detail", text="View", args=[A("pk")], accessor="pk", orderable=False
|
||||||
)
|
)
|
||||||
popup = tables.Column(empty_values=(), orderable=False)
|
popup = tables.Column(empty_values=(), orderable=False)
|
||||||
|
|
||||||
@@ -290,7 +294,7 @@ class ConditionTable(SelectionTable):
|
|||||||
)
|
)
|
||||||
|
|
||||||
def render_synonym(self, value, record):
|
def render_synonym(self, value, record):
|
||||||
return format_html(record.get_synonym_link())
|
return mark_safe(record.get_synonym_link())
|
||||||
return f"{record.get_synonym_link()}"
|
return f"{record.get_synonym_link()}"
|
||||||
|
|
||||||
|
|
||||||
@@ -317,7 +321,7 @@ class FindingTable(SelectionTable):
|
|||||||
|
|
||||||
|
|
||||||
def render_synonym(self, value, record):
|
def render_synonym(self, value, record):
|
||||||
return format_html(record.get_synonym_link())
|
return mark_safe(record.get_synonym_link())
|
||||||
|
|
||||||
|
|
||||||
class StructureTable(SelectionTable):
|
class StructureTable(SelectionTable):
|
||||||
@@ -344,7 +348,7 @@ class StructureTable(SelectionTable):
|
|||||||
|
|
||||||
|
|
||||||
def render_synonym(self, value, record):
|
def render_synonym(self, value, record):
|
||||||
return format_html(record.get_synonym_link())
|
return mark_safe(record.get_synonym_link())
|
||||||
|
|
||||||
|
|
||||||
class PresentationTable(SelectionTable):
|
class PresentationTable(SelectionTable):
|
||||||
@@ -412,7 +416,7 @@ class SubspecialtyTable(SelectionTable):
|
|||||||
|
|
||||||
|
|
||||||
def render_synonym(self, value, record):
|
def render_synonym(self, value, record):
|
||||||
return format_html(record.get_synonym_link())
|
return mark_safe(record.get_synonym_link())
|
||||||
return f"{record.get_synonym_link()}"
|
return f"{record.get_synonym_link()}"
|
||||||
|
|
||||||
|
|
||||||
@@ -432,7 +436,7 @@ class CaseCollectionTable(SelectionTable):
|
|||||||
"atlas:exam_update", text="Edit", args=[A("pk")], orderable=False
|
"atlas:exam_update", text="Edit", args=[A("pk")], orderable=False
|
||||||
)
|
)
|
||||||
view = tables.LinkColumn(
|
view = tables.LinkColumn(
|
||||||
"atlas:collection_detail", text="View", args=[A("pk")], orderable=False
|
"atlas:collection_detail", text="View", args=[A("pk")], accessor="pk", orderable=False
|
||||||
)
|
)
|
||||||
|
|
||||||
created = tables.DateTimeColumn(verbose_name="Created", accessor="created", orderable=True)
|
created = tables.DateTimeColumn(verbose_name="Created", accessor="created", orderable=True)
|
||||||
|
|||||||
+7
-8
@@ -3,6 +3,7 @@ import django_tables2 as tables
|
|||||||
from django_tables2.utils import A
|
from django_tables2.utils import A
|
||||||
|
|
||||||
from django.utils.html import format_html
|
from django.utils.html import format_html
|
||||||
|
from django.utils.safestring import mark_safe
|
||||||
|
|
||||||
from easy_thumbnails.files import get_thumbnailer
|
from easy_thumbnails.files import get_thumbnailer
|
||||||
|
|
||||||
@@ -85,7 +86,7 @@ class SelectionTable(tables.Table):
|
|||||||
|
|
||||||
# Also include a small data attribute so client scripts can locate the
|
# Also include a small data attribute so client scripts can locate the
|
||||||
# controls by selection name if they prefer.
|
# controls by selection name if they prefer.
|
||||||
return format_html(html)
|
return mark_safe(html)
|
||||||
|
|
||||||
class SingleImageColumn(tables.Column):
|
class SingleImageColumn(tables.Column):
|
||||||
def render(self, value):
|
def render(self, value):
|
||||||
@@ -100,7 +101,7 @@ class BlockImageColumn(tables.Column):
|
|||||||
blocks = []
|
blocks = []
|
||||||
for obj in value.all():
|
for obj in value.all():
|
||||||
blocks.append(obj.get_block())
|
blocks.append(obj.get_block())
|
||||||
return format_html(
|
return mark_safe(
|
||||||
"""<span class='multi-image-block'>
|
"""<span class='multi-image-block'>
|
||||||
{}
|
{}
|
||||||
</span>""".format("".join(blocks))
|
</span>""".format("".join(blocks))
|
||||||
@@ -112,9 +113,7 @@ class SeriesImageColumn(tables.Column):
|
|||||||
images = value.all()
|
images = value.all()
|
||||||
|
|
||||||
if not images:
|
if not images:
|
||||||
return format_html(
|
return mark_safe('<span>None</span><br/>')
|
||||||
'<span>None<span><br/>',
|
|
||||||
)
|
|
||||||
|
|
||||||
obj = findMiddle(images)
|
obj = findMiddle(images)
|
||||||
|
|
||||||
@@ -141,7 +140,7 @@ class FirstImageColumn(tables.Column):
|
|||||||
obj = value.all()
|
obj = value.all()
|
||||||
|
|
||||||
if not obj:
|
if not obj:
|
||||||
return format_html('<span>No image<span>')
|
return mark_safe('<span>No image</span>')
|
||||||
|
|
||||||
image_object = obj[0].image
|
image_object = obj[0].image
|
||||||
try:
|
try:
|
||||||
@@ -196,7 +195,7 @@ class CidUserTable(SelectionTable):
|
|||||||
if record.supervisor is not None:
|
if record.supervisor is not None:
|
||||||
return format_html("""{}\n[{}]""", record.email, record.supervisor.email)
|
return format_html("""{}\n[{}]""", record.email, record.supervisor.email)
|
||||||
else:
|
else:
|
||||||
return format_html(record.email)
|
return record.email
|
||||||
|
|
||||||
|
|
||||||
class CidUserExamTable(SelectionTable):
|
class CidUserExamTable(SelectionTable):
|
||||||
@@ -249,7 +248,7 @@ class CidUserExamTable(SelectionTable):
|
|||||||
if record.supervisor is not None:
|
if record.supervisor is not None:
|
||||||
return format_html("""{}\n[{}]""", record.email, record.supervisor.email)
|
return format_html("""{}\n[{}]""", record.email, record.supervisor.email)
|
||||||
else:
|
else:
|
||||||
return format_html(record.email)
|
return record.email
|
||||||
|
|
||||||
|
|
||||||
# def render_cid(self, value, record):
|
# def render_cid(self, value, record):
|
||||||
|
|||||||
+7
-6
@@ -16,6 +16,7 @@ from django.urls import reverse_lazy
|
|||||||
from django.urls.base import reverse
|
from django.urls.base import reverse
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
from django.utils.html import format_html
|
from django.utils.html import format_html
|
||||||
|
from django.utils.safestring import mark_safe
|
||||||
from django.views.decorators.csrf import csrf_exempt
|
from django.views.decorators.csrf import csrf_exempt
|
||||||
from django.core.exceptions import PermissionDenied, FieldError
|
from django.core.exceptions import PermissionDenied, FieldError
|
||||||
|
|
||||||
@@ -850,7 +851,7 @@ def generic_exam_set_open_access(request):
|
|||||||
# For now require the user to have edit access (ExamViews.check_user_edit_access equivalent)
|
# For now require the user to have edit access (ExamViews.check_user_edit_access equivalent)
|
||||||
# We'll do a lightweight check: if user is superuser OR user in exam.get_author_objects()
|
# We'll do a lightweight check: if user is superuser OR user in exam.get_author_objects()
|
||||||
if not (request.user.is_superuser or request.user in exam.get_author_objects()):
|
if not (request.user.is_superuser or request.user in exam.get_author_objects()):
|
||||||
return HttpResponse(format_html('<div class="alert alert-danger">Permission denied</div>'))
|
return HttpResponse(mark_safe('<div class="alert alert-danger">Permission denied</div>'))
|
||||||
|
|
||||||
set_val = True if request.POST.get("set_open_access") == "true" else False
|
set_val = True if request.POST.get("set_open_access") == "true" else False
|
||||||
|
|
||||||
@@ -1147,7 +1148,7 @@ class ExamViews(View, LoginRequiredMixin):
|
|||||||
if request.method == "POST":
|
if request.method == "POST":
|
||||||
if not self.check_user_edit_access(request.user, exam_id=pk):
|
if not self.check_user_edit_access(request.user, exam_id=pk):
|
||||||
return HttpResponse(
|
return HttpResponse(
|
||||||
format_html(
|
mark_safe(
|
||||||
'<div class="alert alert-danger" role="alert">Error toggling results published.</div>'
|
'<div class="alert alert-danger" role="alert">Error toggling results published.</div>'
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
@@ -1163,7 +1164,7 @@ class ExamViews(View, LoginRequiredMixin):
|
|||||||
})
|
})
|
||||||
else:
|
else:
|
||||||
HttpResponse(
|
HttpResponse(
|
||||||
format_html(
|
mark_safe(
|
||||||
'<div class="alert alert-danger" role="alert">Error toggling results published.</div>'
|
'<div class="alert alert-danger" role="alert">Error toggling results published.</div>'
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
@@ -1173,7 +1174,7 @@ class ExamViews(View, LoginRequiredMixin):
|
|||||||
if request.method == "POST":
|
if request.method == "POST":
|
||||||
if not self.check_user_edit_access(request.user, exam_id=pk):
|
if not self.check_user_edit_access(request.user, exam_id=pk):
|
||||||
return HttpResponse(
|
return HttpResponse(
|
||||||
format_html(
|
mark_safe(
|
||||||
'<div class="alert alert-danger" role="alert">Error toggling exam active.</div>'
|
'<div class="alert alert-danger" role="alert">Error toggling exam active.</div>'
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
@@ -1189,7 +1190,7 @@ class ExamViews(View, LoginRequiredMixin):
|
|||||||
})
|
})
|
||||||
else:
|
else:
|
||||||
HttpResponse(
|
HttpResponse(
|
||||||
format_html(
|
mark_safe(
|
||||||
'<div class="alert alert-danger" role="alert">Error toggling exam active.</div>'
|
'<div class="alert alert-danger" role="alert">Error toggling exam active.</div>'
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
@@ -4722,7 +4723,7 @@ def user_group_add_by_email_user(request, group_id):
|
|||||||
res += f"<br/><div class='alert alert-warning'>Invalid emails [{','.join(invalid_emails)}]</div>"
|
res += f"<br/><div class='alert alert-warning'>Invalid emails [{','.join(invalid_emails)}]</div>"
|
||||||
|
|
||||||
|
|
||||||
return HttpResponse(format_html(res), content_type="text/html")
|
return HttpResponse(res, content_type="text/html")
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
+2
-1
@@ -6,6 +6,7 @@ from generic.tables import BlockImageColumn, SeriesImageColumn
|
|||||||
from .models import Long, LongSeries, UserAnswer
|
from .models import Long, LongSeries, UserAnswer
|
||||||
|
|
||||||
from django.utils.html import format_html
|
from django.utils.html import format_html
|
||||||
|
from django.utils.safestring import mark_safe
|
||||||
from django.db.models import Prefetch
|
from django.db.models import Prefetch
|
||||||
|
|
||||||
from easy_thumbnails.files import get_thumbnailer
|
from easy_thumbnails.files import get_thumbnailer
|
||||||
@@ -61,7 +62,7 @@ class PopupLinkColumn(tables.Column):
|
|||||||
obj = value.first()
|
obj = value.first()
|
||||||
|
|
||||||
if obj is None:
|
if obj is None:
|
||||||
return format_html("<span>No image<span>")
|
return mark_safe("<span>No image</span>")
|
||||||
|
|
||||||
return obj.get_thumbnail()[0]
|
return obj.get_thumbnail()[0]
|
||||||
|
|
||||||
|
|||||||
+2
-1
@@ -23,6 +23,7 @@ from django.core.exceptions import ValidationError
|
|||||||
from django.core.validators import validate_email
|
from django.core.validators import validate_email
|
||||||
|
|
||||||
from django.utils.html import format_html
|
from django.utils.html import format_html
|
||||||
|
from django.utils.safestring import mark_safe
|
||||||
|
|
||||||
# from django.contrib.auth.models import User
|
# from django.contrib.auth.models import User
|
||||||
from django.contrib.auth.decorators import login_required, user_passes_test
|
from django.contrib.auth.decorators import login_required, user_passes_test
|
||||||
@@ -1107,7 +1108,7 @@ def accounts_bulk_create(request):
|
|||||||
except Exception as error:
|
except Exception as error:
|
||||||
error_text = error_text + f"<p>Error creating users.<br/>{error}</p>"
|
error_text = error_text + f"<p>Error creating users.<br/>{error}</p>"
|
||||||
|
|
||||||
context["error"] = format_html(error_text)
|
context["error"] = mark_safe(error_text)
|
||||||
context["created_users"] = created_users
|
context["created_users"] = created_users
|
||||||
|
|
||||||
if existing_users:
|
if existing_users:
|
||||||
|
|||||||
+3
-3
@@ -316,9 +316,9 @@ class Rapid(QuestionBase):
|
|||||||
if not unmarked_answers:
|
if not unmarked_answers:
|
||||||
return "No answers to mark"
|
return "No answers to mark"
|
||||||
return format_html(
|
return format_html(
|
||||||
"<span class='warn'>{} answer unmarked:</span> {}".format(
|
"<span class='warn'>{} answer unmarked:</span> {}",
|
||||||
len(unmarked_answers), ", ".join(unmarked_answers)
|
len(unmarked_answers),
|
||||||
)
|
", ".join(unmarked_answers),
|
||||||
)
|
)
|
||||||
|
|
||||||
def get_unmarked_user_answers(self, exam_pk: int | None = None, marker=None):
|
def get_unmarked_user_answers(self, exam_pk: int | None = None, marker=None):
|
||||||
|
|||||||
+5
-2
@@ -11,11 +11,14 @@ from generic.tables import SelectionTable
|
|||||||
|
|
||||||
class QuestionTable(SelectionTable):
|
class QuestionTable(SelectionTable):
|
||||||
def get_view_cell(record):
|
def get_view_cell(record):
|
||||||
|
|
||||||
if record.open_access:
|
if record.open_access:
|
||||||
return format_html("""<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-opencollective" viewBox="0 0 16 16">
|
return mark_safe(
|
||||||
|
"""<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-opencollective" viewBox="0 0 16 16">
|
||||||
<path fill-opacity=".4" d="M12.995 8.195c0 .937-.312 1.912-.78 2.693l1.99 1.99c.976-1.327 1.6-2.966 1.6-4.683 0-1.795-.624-3.434-1.561-4.76l-2.068 2.028c.468.781.78 1.679.78 2.732h.04Z"/>
|
<path fill-opacity=".4" d="M12.995 8.195c0 .937-.312 1.912-.78 2.693l1.99 1.99c.976-1.327 1.6-2.966 1.6-4.683 0-1.795-.624-3.434-1.561-4.76l-2.068 2.028c.468.781.78 1.679.78 2.732h.04Z"/>
|
||||||
<path d="M8 13.151a4.995 4.995 0 1 1 0-9.99c1.015 0 1.951.273 2.732.82l1.95-2.03a7.805 7.805 0 1 0 .04 12.449l-1.951-2.03a5.072 5.072 0 0 1-2.732.781z"/>
|
<path d="M8 13.151a4.995 4.995 0 1 1 0-9.99c1.015 0 1.951.273 2.732.82l1.95-2.03a7.805 7.805 0 1 0 .04 12.449l-1.951-2.03a5.072 5.072 0 0 1-2.732.781z"/>
|
||||||
</svg> View""")
|
</svg> View"""
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
return 'View'
|
return 'View'
|
||||||
#edit = tables.LinkColumn(
|
#edit = tables.LinkColumn(
|
||||||
|
|||||||
+3
-3
@@ -185,9 +185,9 @@ class Question(QuestionBase):
|
|||||||
if not unmarked_answers:
|
if not unmarked_answers:
|
||||||
return "No answers to mark"
|
return "No answers to mark"
|
||||||
return format_html(
|
return format_html(
|
||||||
"<span class='warn'>{} answer unmarked:</span> {}".format(
|
"<span class='warn'>{} answer unmarked:</span> {}",
|
||||||
len(unmarked_answers), ", ".join(unmarked_answers)
|
len(unmarked_answers),
|
||||||
)
|
", ".join(unmarked_answers),
|
||||||
)
|
)
|
||||||
|
|
||||||
def get_unmarked_user_answers(self, exam_pk: int | None = None, marker=None):
|
def get_unmarked_user_answers(self, exam_pk: int | None = None, marker=None):
|
||||||
|
|||||||
Reference in New Issue
Block a user