fix: change pattern detail view
This commit is contained in:
@@ -2,7 +2,7 @@ msgid ""
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: crochet\n"
|
"Project-Id-Version: crochet\n"
|
||||||
"Report-Msgid-Bugs-To: \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"
|
"Language: en\n"
|
||||||
"MIME-Version: 1.0\n"
|
"MIME-Version: 1.0\n"
|
||||||
"Content-Type: text/plain; charset=UTF-8\n"
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
@@ -46,9 +46,10 @@ msgstr "More information"
|
|||||||
|
|
||||||
#: crochet/templates/crochet/_cookie_banner.html:11
|
#: crochet/templates/crochet/_cookie_banner.html:11
|
||||||
msgid "Entendido"
|
msgid "Entendido"
|
||||||
msgstr "Got it"
|
msgstr "Agree"
|
||||||
|
|
||||||
#: crochet/templates/crochet/account_home.html:4
|
#: crochet/templates/crochet/account_home.html:4
|
||||||
|
#: crochet/templates/crochet/pattern_detail.html:43
|
||||||
msgid "Mis patrones"
|
msgid "Mis patrones"
|
||||||
msgstr "My patterns"
|
msgstr "My patterns"
|
||||||
|
|
||||||
@@ -110,6 +111,7 @@ msgstr "Log out"
|
|||||||
#: crochet/templates/crochet/base.html:62
|
#: crochet/templates/crochet/base.html:62
|
||||||
#: crochet/templates/crochet/home.html:24
|
#: crochet/templates/crochet/home.html:24
|
||||||
#: crochet/templates/crochet/password_reset_complete.html:18
|
#: 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:4
|
||||||
#: crochet/templates/registration/login.html:14
|
#: crochet/templates/registration/login.html:14
|
||||||
msgid "Iniciar sesión"
|
msgid "Iniciar sesión"
|
||||||
@@ -634,7 +636,7 @@ msgstr ""
|
|||||||
msgid "Añade una sección para ver aquí el resultado."
|
msgid "Añade una sección para ver aquí el resultado."
|
||||||
msgstr "Add a section to see the result here."
|
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"
|
msgid "Descargar PDF"
|
||||||
msgstr "Download PDF"
|
msgstr "Download PDF"
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ msgid ""
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: crochet\n"
|
"Project-Id-Version: crochet\n"
|
||||||
"Report-Msgid-Bugs-To: \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"
|
"Language: es\n"
|
||||||
"MIME-Version: 1.0\n"
|
"MIME-Version: 1.0\n"
|
||||||
"Content-Type: text/plain; charset=UTF-8\n"
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
@@ -47,6 +47,7 @@ msgid "Entendido"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: crochet/templates/crochet/account_home.html:4
|
#: crochet/templates/crochet/account_home.html:4
|
||||||
|
#: crochet/templates/crochet/pattern_detail.html:43
|
||||||
msgid "Mis patrones"
|
msgid "Mis patrones"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@@ -108,6 +109,7 @@ msgstr ""
|
|||||||
#: crochet/templates/crochet/base.html:62
|
#: crochet/templates/crochet/base.html:62
|
||||||
#: crochet/templates/crochet/home.html:24
|
#: crochet/templates/crochet/home.html:24
|
||||||
#: crochet/templates/crochet/password_reset_complete.html:18
|
#: 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:4
|
||||||
#: crochet/templates/registration/login.html:14
|
#: crochet/templates/registration/login.html:14
|
||||||
msgid "Iniciar sesión"
|
msgid "Iniciar sesión"
|
||||||
@@ -604,7 +606,7 @@ msgstr ""
|
|||||||
msgid "Añade una sección para ver aquí el resultado."
|
msgid "Añade una sección para ver aquí el resultado."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: crochet/templates/crochet/pattern_detail.html:26
|
#: crochet/templates/crochet/pattern_detail.html:31
|
||||||
msgid "Descargar PDF"
|
msgid "Descargar PDF"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
|||||||
@@ -59,7 +59,13 @@ class Command(BaseCommand):
|
|||||||
|
|
||||||
INPUT_CSS.parent.mkdir(parents=True, exist_ok=True)
|
INPUT_CSS.parent.mkdir(parents=True, exist_ok=True)
|
||||||
sources = '\n'.join(f'@source "{path}";' for path in SOURCES)
|
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)}...')
|
self.stdout.write(f'Compilando {OUTPUT_CSS.relative_to(settings.BASE_DIR)}...')
|
||||||
result = subprocess.run(
|
result = subprocess.run(
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
@@ -1,6 +1,6 @@
|
|||||||
{% load static django_htmx %}
|
{% 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). #}
|
{# 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" />
|
<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>
|
<script src="https://cdn.jsdelivr.net/npm/@tailwindcss/browser@4"></script>
|
||||||
<link rel="stylesheet" href="{% static 'css/theme.css' %}">
|
<link rel="stylesheet" href="{% static 'css/theme.css' %}">
|
||||||
|
|||||||
@@ -4,6 +4,13 @@
|
|||||||
{% block title %}{{ page_settings.title|default:"Crochet" }}{% endblock %}
|
{% block title %}{{ page_settings.title|default:"Crochet" }}{% endblock %}
|
||||||
|
|
||||||
{% block extra_css %}
|
{% 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' %}">
|
<link rel="stylesheet" href="{% static 'css/pattern-detail.css' %}">
|
||||||
<style>
|
<style>
|
||||||
:root {
|
:root {
|
||||||
@@ -14,26 +21,12 @@
|
|||||||
</style>
|
</style>
|
||||||
{% endblock %}
|
{% 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 cookie_banner %}{% endblock %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<main class="p-4">
|
<main class="p-4">
|
||||||
<div class="flex justify-end gap-2 mb-4 no-print">
|
<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>
|
<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>
|
||||||
|
|
||||||
<div id="panel-output" class="border border-gray-300 rounded-2xl p-4 max-w-3xl mx-auto"
|
<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
|
from crochet.tests.test_models import make_test_image_file
|
||||||
|
|
||||||
NO_HTML_COMMENTS_RE = re.compile(r'<!--.*?-->', re.S)
|
NO_HTML_COMMENTS_RE = re.compile(r'<!--.*?-->', re.S)
|
||||||
|
NAV_RE = re.compile(r'<nav[ >].*?</nav>', re.S)
|
||||||
|
|
||||||
|
|
||||||
class HomeViewTests(TestCase):
|
class HomeViewTests(TestCase):
|
||||||
@@ -286,15 +287,23 @@ class PatternDetailViewTests(TestCase):
|
|||||||
self.assertLess(body.index('Ampharos'), body.index(self.pattern.cover_image_large.url))
|
self.assertLess(body.index('Ampharos'), body.index(self.pattern.cover_image_large.url))
|
||||||
|
|
||||||
def test_page_has_no_editable_elements(self):
|
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'):
|
with translation.override('es'):
|
||||||
url = reverse('crochet:pattern_detail', args=[self.pattern.uuid])
|
url = reverse('crochet:pattern_detail', args=[self.pattern.uuid])
|
||||||
response = self.client.get(url)
|
response = self.client.get(url)
|
||||||
body_without_comments = NO_HTML_COMMENTS_RE.sub('', response.content.decode())
|
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_nav, r'<input[ >]')
|
||||||
self.assertNotRegex(body_without_comments, r'<button[ >]')
|
self.assertNotRegex(body_without_nav, r'<button[ >]')
|
||||||
self.assertNotRegex(body_without_comments, r'<select[ >]')
|
self.assertNotRegex(body_without_nav, r'<select[ >]')
|
||||||
self.assertNotIn('csrf-token', body_without_comments)
|
self.assertNotIn('csrf-token', body_without_nav)
|
||||||
|
|
||||||
def test_malicious_text_is_escaped(self):
|
def test_malicious_text_is_escaped(self):
|
||||||
pattern = Pattern.objects.create(sections=[
|
pattern = Pattern.objects.create(sections=[
|
||||||
|
|||||||
Reference in New Issue
Block a user