fix: change pattern detail view
This commit is contained in:
@@ -2,7 +2,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: crochet\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2026-07-21 12:09+0000\n"
|
||||
"POT-Creation-Date: 2026-07-21 12:32+0000\n"
|
||||
"Language: en\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
@@ -46,9 +46,10 @@ msgstr "More information"
|
||||
|
||||
#: crochet/templates/crochet/_cookie_banner.html:11
|
||||
msgid "Entendido"
|
||||
msgstr "Got it"
|
||||
msgstr "Agree"
|
||||
|
||||
#: crochet/templates/crochet/account_home.html:4
|
||||
#: crochet/templates/crochet/pattern_detail.html:43
|
||||
msgid "Mis patrones"
|
||||
msgstr "My patterns"
|
||||
|
||||
@@ -110,6 +111,7 @@ msgstr "Log out"
|
||||
#: crochet/templates/crochet/base.html:62
|
||||
#: crochet/templates/crochet/home.html:24
|
||||
#: crochet/templates/crochet/password_reset_complete.html:18
|
||||
#: crochet/templates/crochet/pattern_detail.html:45
|
||||
#: crochet/templates/registration/login.html:4
|
||||
#: crochet/templates/registration/login.html:14
|
||||
msgid "Iniciar sesión"
|
||||
@@ -634,7 +636,7 @@ msgstr ""
|
||||
msgid "Añade una sección para ver aquí el resultado."
|
||||
msgstr "Add a section to see the result here."
|
||||
|
||||
#: crochet/templates/crochet/pattern_detail.html:26
|
||||
#: crochet/templates/crochet/pattern_detail.html:31
|
||||
msgid "Descargar PDF"
|
||||
msgstr "Download PDF"
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: crochet\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2026-07-21 12:09+0000\n"
|
||||
"POT-Creation-Date: 2026-07-21 12:32+0000\n"
|
||||
"Language: es\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
@@ -47,6 +47,7 @@ msgid "Entendido"
|
||||
msgstr ""
|
||||
|
||||
#: crochet/templates/crochet/account_home.html:4
|
||||
#: crochet/templates/crochet/pattern_detail.html:43
|
||||
msgid "Mis patrones"
|
||||
msgstr ""
|
||||
|
||||
@@ -108,6 +109,7 @@ msgstr ""
|
||||
#: crochet/templates/crochet/base.html:62
|
||||
#: crochet/templates/crochet/home.html:24
|
||||
#: crochet/templates/crochet/password_reset_complete.html:18
|
||||
#: crochet/templates/crochet/pattern_detail.html:45
|
||||
#: crochet/templates/registration/login.html:4
|
||||
#: crochet/templates/registration/login.html:14
|
||||
msgid "Iniciar sesión"
|
||||
@@ -604,7 +606,7 @@ msgstr ""
|
||||
msgid "Añade una sección para ver aquí el resultado."
|
||||
msgstr ""
|
||||
|
||||
#: crochet/templates/crochet/pattern_detail.html:26
|
||||
#: crochet/templates/crochet/pattern_detail.html:31
|
||||
msgid "Descargar PDF"
|
||||
msgstr ""
|
||||
|
||||
|
||||
@@ -59,7 +59,13 @@ class Command(BaseCommand):
|
||||
|
||||
INPUT_CSS.parent.mkdir(parents=True, exist_ok=True)
|
||||
sources = '\n'.join(f'@source "{path}";' for path in SOURCES)
|
||||
INPUT_CSS.write_text(f'@import "tailwindcss";\n{sources}\n')
|
||||
# source(none): sin esto, Tailwind v4 escanea IGUALMENTE todo el
|
||||
# proyecto en busca de clases por su cuenta (detección automática de
|
||||
# contenido), además de los @source de abajo; el resultado deja de
|
||||
# estar acotado a SOURCES y arrastra clases de cualquier otra
|
||||
# plantilla (pattern.html, home.html...), justo lo que este comando
|
||||
# existe para evitar.
|
||||
INPUT_CSS.write_text(f'@import "tailwindcss" source(none);\n{sources}\n')
|
||||
|
||||
self.stdout.write(f'Compilando {OUTPUT_CSS.relative_to(settings.BASE_DIR)}...')
|
||||
result = subprocess.run(
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -1,6 +1,6 @@
|
||||
{% load static django_htmx %}
|
||||
{# Un comentario Django de una sola línea (su lexer no cruza saltos de línea al buscar el cierre, y tampoco admite el propio delimitador de comentario dentro del texto). #}
|
||||
{# Incluido por cada plantilla con look de "plataforma" (editor, cuenta): daisyUI + Tailwind vía CDN + el tema propio "crochet" (ver theme.css) + htmx. pattern_detail.html NO lo incluye a propósito (ver ese archivo). #}
|
||||
{# Incluido por cada plantilla con look de "plataforma" (editor, cuenta, y también pattern_detail.html para tener el mismo navbar/cabecera): daisyUI + Tailwind vía CDN + el tema propio "crochet" (ver theme.css) + htmx. #}
|
||||
<link href="https://cdn.jsdelivr.net/npm/daisyui@5" rel="stylesheet" type="text/css" />
|
||||
<script src="https://cdn.jsdelivr.net/npm/@tailwindcss/browser@4"></script>
|
||||
<link rel="stylesheet" href="{% static 'css/theme.css' %}">
|
||||
|
||||
@@ -4,6 +4,13 @@
|
||||
{% block title %}{{ page_settings.title|default:"Crochet" }}{% endblock %}
|
||||
|
||||
{% block extra_css %}
|
||||
<!-- El navbar real (ver base.html) usa clases de daisyUI, así que aquí
|
||||
hace falta cargarlo igual que en el resto del sitio (ver
|
||||
_platform_assets.html), aunque esta vista se genere también como
|
||||
HTML de entrada para el PDF: al ser no-print, no se imprime, pero
|
||||
WeasyPrint sí llega a pedir por red su CSS (daisyUI, Google Fonts)
|
||||
para construirlo, aunque el resultado no se vea nunca en el PDF. -->
|
||||
{% include 'crochet/_platform_assets.html' %}
|
||||
<link rel="stylesheet" href="{% static 'css/pattern-detail.css' %}">
|
||||
<style>
|
||||
:root {
|
||||
@@ -14,26 +21,12 @@
|
||||
</style>
|
||||
{% endblock %}
|
||||
|
||||
<!-- Sin daisyUI aquí (ver arriba), así que el navbar de base.html no tiene
|
||||
ningún estilo que aplicarle: se deja vacío a propósito. Lo mismo con
|
||||
el aviso de cookies: es una vista de solo lectura sin JS propio. -->
|
||||
{% block navbar %}{% endblock %}
|
||||
{% block cookie_banner %}{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<main class="p-4">
|
||||
<div class="flex justify-end gap-2 mb-4 no-print">
|
||||
<a href="{% url 'crochet:pattern_pdf' pattern.uuid %}" class="detail-btn">{% trans 'Descargar PDF' %}</a>
|
||||
{% if lang != 'es' %}
|
||||
{% language 'es' %}
|
||||
<a href="{% url 'crochet:pattern_detail' pattern.uuid %}" class="detail-btn">Español</a>
|
||||
{% endlanguage %}
|
||||
{% endif %}
|
||||
{% if lang != 'en' %}
|
||||
{% language 'en' %}
|
||||
<a href="{% url 'crochet:pattern_detail' pattern.uuid %}" class="detail-btn">English</a>
|
||||
{% endlanguage %}
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<div id="panel-output" class="border border-gray-300 rounded-2xl p-4 max-w-3xl mx-auto"
|
||||
|
||||
@@ -12,6 +12,7 @@ from crochet.models import Pattern, PatternImage, StitchType
|
||||
from crochet.tests.test_models import make_test_image_file
|
||||
|
||||
NO_HTML_COMMENTS_RE = re.compile(r'<!--.*?-->', re.S)
|
||||
NAV_RE = re.compile(r'<nav[ >].*?</nav>', re.S)
|
||||
|
||||
|
||||
class HomeViewTests(TestCase):
|
||||
@@ -286,15 +287,23 @@ class PatternDetailViewTests(TestCase):
|
||||
self.assertLess(body.index('Ampharos'), body.index(self.pattern.cover_image_large.url))
|
||||
|
||||
def test_page_has_no_editable_elements(self):
|
||||
# El navbar compartido (ver base.html) sí trae sus propios <button>
|
||||
# (desplegable de idioma/cuenta): no editan el patrón, así que se
|
||||
# descarta antes de comprobar esto. La regla real es que el PATRÓN
|
||||
# en sí -lo que hay fuera del navbar- no debe traer ningún control
|
||||
# de edición, ni siquiera oculto con CSS: cualquiera podría
|
||||
# revelarlo desde las herramientas de desarrollador del navegador y
|
||||
# guardar cambios reales (esta vista comparte uuid con la de editar).
|
||||
with translation.override('es'):
|
||||
url = reverse('crochet:pattern_detail', args=[self.pattern.uuid])
|
||||
response = self.client.get(url)
|
||||
body_without_comments = NO_HTML_COMMENTS_RE.sub('', response.content.decode())
|
||||
body_without_nav = NAV_RE.sub('', body_without_comments)
|
||||
|
||||
self.assertNotRegex(body_without_comments, r'<input[ >]')
|
||||
self.assertNotRegex(body_without_comments, r'<button[ >]')
|
||||
self.assertNotRegex(body_without_comments, r'<select[ >]')
|
||||
self.assertNotIn('csrf-token', body_without_comments)
|
||||
self.assertNotRegex(body_without_nav, r'<input[ >]')
|
||||
self.assertNotRegex(body_without_nav, r'<button[ >]')
|
||||
self.assertNotRegex(body_without_nav, r'<select[ >]')
|
||||
self.assertNotIn('csrf-token', body_without_nav)
|
||||
|
||||
def test_malicious_text_is_escaped(self):
|
||||
pattern = Pattern.objects.create(sections=[
|
||||
|
||||
Reference in New Issue
Block a user