From 3420d848031352acad00af1f3afb500c95ef069d Mon Sep 17 00:00:00 2001 From: Louis Date: Wed, 16 Sep 2026 12:45:01 +0200 Subject: [PATCH] v0.2.0: fix leaked multi-line {# #} comments, add German translation - Fix template comments rendering as literal text: Django's {# #} tag regex (tag_re in django/template/base.py) has no re.DOTALL, so a comment spanning multiple lines is never recognized as a tag at all and leaks into the rendered output verbatim - exactly what showed up under "Elevation". Collapsed all three offending comments to single lines. This was also almost certainly the real cause of the "PNG could not be generated" report: two of the leaked comments sat inside the shared SVG partial, corrupting the exported SVG enough that the browser's image decoder rejected it (my own onerror handler then showed a misleading CORS-sounding message - the actual bug was malformed markup, not cross-origin images). - Add a German translation (locale/de/), built from every _() and {% trans %}/{% blocktrans %} string in the plugin (269 entries, cross-checked against the source for completeness). English needs no catalog of its own since it's the literal source text; both were already listed in NetBox's own language selector. - Add CHANGELOG.md and bump the version to 0.2.0, covering this and every unreleased change so far; will keep bumping per change from here on. Co-Authored-By: Claude Sonnet 5 --- CHANGELOG.md | 36 + README.md | 7 + netbox_rack_concept/__init__.py | 2 +- .../locale/de/LC_MESSAGES/django.mo | Bin 0 -> 21201 bytes .../locale/de/LC_MESSAGES/django.po | 928 ++++++++++++++++++ .../inc/elevation_svg_content.html | 6 +- .../netbox_rack_concept/rackconcept.html | 4 +- pyproject.toml | 4 +- 8 files changed, 978 insertions(+), 9 deletions(-) create mode 100644 CHANGELOG.md create mode 100644 netbox_rack_concept/locale/de/LC_MESSAGES/django.mo create mode 100644 netbox_rack_concept/locale/de/LC_MESSAGES/django.po diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..f84e0e9 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,36 @@ +# Changelog + +## 0.2.0 + +- Fix: several multi-line `{# ... #}` template comments rendered as literal text on + the page instead of being stripped, because Django's comment-tag regex doesn't match + across newlines. All comments are now single-line. (This was also the most likely + cause of a reported "PNG export failed" error: the leaked text made the exported SVG + invalid, so the browser couldn't decode it as an image.) +- Add German translation (`locale/de/`). English remains the source language and needs + no catalog; both are already listed in NetBox's own language selector. +- Center the "+ Add device" hint on empty rack units instead of right-aligning it. +- Add SVG/PNG/draw.io export for rack concepts. +- Fix drag-and-drop reorder: NetBox sets `CSRF_COOKIE_HTTPONLY`, so the reorder POST's + CSRF token now comes from a rendered `{% csrf_token %}` input instead of the + (JS-invisible) cookie. +- Add per-type component tabs (Interfaces, Front/Rear Ports, Console (Server) Ports, + Power Ports/Outlets) on the concept device page, mirroring real `dcim.Device` pages. +- Copying a real rack (or merging two real racks) into a concept now also copies + internal cabling as ConceptCables. +- Show the peer device/port in component connection columns. +- Fix `IntegrityError` when merging two real racks: `airflow`/`face` can be `NULL` on + a real `Rack`/`Device` but not on the corresponding concept fields. +- Add rack cabling: `ConceptComponent`, `ConceptCable`, device-type component sync, + patch panel front/rear pass-through mapping. +- Add click-to-add on empty rack units, dropping the separate "Add planned device" + button. +- Add images/labels view toggle and clean up device label styling on the elevation. +- Fix rack elevation overlap; add device images to elevation blocks. +- Add rack/concept merge (2 racks or 2 concepts → 1), and a size override when copying + a real rack into a concept. + +## 0.1.0 + +Initial release: rack concept planning, copy/clone/merge/deploy flows, drag-and-drop +elevation, partial-width devices, REST API. diff --git a/README.md b/README.md index 351eb0a..99e626c 100644 --- a/README.md +++ b/README.md @@ -268,6 +268,13 @@ Kabel, Interfaces jenseits der Device-Type-Templates, IP-Adressen, Seriennummern Asset-Tags. Geplante Geräte ohne Rolle, ohne Gerätetyp oder ohne Position werden übersprungen — welche das sind, zeigt die Bestätigungsseite vor dem Ausrollen an. +## Sprache + +Das Plugin ist auf Englisch (Quelltext) und Deutsch übersetzt. Beide Sprachen sind in +NetBox bereits als Auswahl hinterlegt; die Anzeigesprache stellt man wie gewohnt über +das Benutzerprofil (Preferences → Language) um — keine zusätzliche Konfiguration nötig. +Die Übersetzung liegt unter `netbox_rack_concept/locale/de/LC_MESSAGES/`. + ## Berechtigungen Es gelten die normalen NetBox-Objektberechtigungen: diff --git a/netbox_rack_concept/__init__.py b/netbox_rack_concept/__init__.py index a982016..a47956b 100644 --- a/netbox_rack_concept/__init__.py +++ b/netbox_rack_concept/__init__.py @@ -1,6 +1,6 @@ from netbox.plugins import PluginConfig -__version__ = '0.1.0' +__version__ = '0.2.0' class RackConceptConfig(PluginConfig): diff --git a/netbox_rack_concept/locale/de/LC_MESSAGES/django.mo b/netbox_rack_concept/locale/de/LC_MESSAGES/django.mo new file mode 100644 index 0000000000000000000000000000000000000000..1b59d062fd1cece0ec217c611ac7d8cd43a69a17 GIT binary patch literal 21201 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 {% if not unit.occupied and can_add_device %} @@ -58,8 +57,7 @@ - {# Absolute when we have a request (the export - a file that outlives the page - it came from needs a URL that still resolves once it's saved elsewhere). #} + {# Absolute when we have a request: an exported file that outlives the page it came from needs a URL that still resolves once saved elsewhere. #}
- {# CSRF_COOKIE_HTTPONLY is on in NetBox, so the elevation.js reorder POST can't - read the csrftoken cookie via document.cookie - it reads this hidden input's - value instead, the standard Django way to CSRF-protect a plain fetch(). #} + {# CSRF_COOKIE_HTTPONLY is on in NetBox, so elevation.js reads the token from this hidden input instead of the (JS-invisible) cookie. #} {% csrf_token %}