Merge branch 'netbox-community:main' into 18936-import-cable-color-names

This commit is contained in:
Jonathan Ramstedt 2025-07-23 16:47:29 +03:00 committed by GitHub
commit b800445a1e
6 changed files with 33 additions and 11 deletions

View File

@ -302,13 +302,6 @@ Quit the server with CONTROL-C.
Next, connect to the name or IP of the server (as defined in `ALLOWED_HOSTS`) on port 8000; for example, <http://127.0.0.1:8000/>. You should be greeted with the NetBox home page. Try logging in using the username and password specified when creating a superuser.
!!! note
By default RHEL based distros will likely block your testing attempts with firewalld. The development server port can be opened with `firewall-cmd` (add `--permanent` if you want the rule to survive server restarts):
```no-highlight
firewall-cmd --zone=public --add-port=8000/tcp
```
!!! danger "Not for production use"
The development server is for development and testing purposes only. It is neither performant nor secure enough for production use. **Do not use it in production.**

View File

@ -3,6 +3,7 @@ import svgwrite
from svgwrite.container import Hyperlink
from svgwrite.image import Image
from svgwrite.gradients import LinearGradient
from svgwrite.masking import ClipPath
from svgwrite.shapes import Rect
from svgwrite.text import Text
@ -67,6 +68,20 @@ def get_device_description(device):
return description
def truncate_text(text, width, font_size=15):
"""
Truncate text to fit within the width of a rectangle.
:param text: The text to truncate
:param width: Width of rectangle
:param font_size: Font size (default is 15, ~0.875rem)
"""
char_width = font_size * 0.6 # 0.6 is an approximation of the average character width in pixels
max_char = int(width / char_width)
return text if len(text) <= max_char else text[:max_char] + '...'
class RackElevationSVG:
"""
Use this class to render a rack elevation as an SVG image.
@ -177,12 +192,26 @@ class RackElevationSVG:
link = Hyperlink(href=f'{self.base_url}{device.get_absolute_url()}', target="_parent")
link.set_desc(description)
# Create clipPath element
# This is necessary as fallback because the truncate_text method is an approximation
clip_id = f"clip-{device.id}"
clip_path = ClipPath(id=clip_id)
clip_path.add(Rect(coords, size))
self.drawing.defs.add(clip_path)
# Name to display
display_name = truncate_text(name, size[0])
# Add rect element to hyperlink
if color:
link.add(Rect(coords, size, style=f'fill: #{color}', class_=f'slot{css_extra}'))
else:
link.add(Rect(coords, size, class_=f'slot blocked{css_extra}'))
link.add(Text(name, insert=text_coords, fill=text_color, class_=f'label{css_extra}'))
link.add(
Text(display_name, insert=text_coords, fill=text_color, clip_path=f"url(#{clip_id})",
class_=f'label{css_extra}')
)
# Embed device type image if provided
if self.include_images and image:

Binary file not shown.

Binary file not shown.

View File

@ -35,7 +35,7 @@ function showRackElements(
selector: string,
elevation: HTMLObjectElement,
): void {
const elements = elevation.contentDocument?.querySelectorAll(selector) ?? [];
const elements = elevation.querySelectorAll(selector) ?? [];
for (const element of elements) {
element.classList.remove('hidden');
}
@ -45,7 +45,7 @@ function hideRackElements(
selector: string,
elevation: HTMLObjectElement,
): void {
const elements = elevation.contentDocument?.querySelectorAll(selector) ?? [];
const elements = elevation.querySelectorAll(selector) ?? [];
for (const element of elements) {
element.classList.add('hidden');
}

View File

@ -1,5 +1,5 @@
{% load i18n %}
<div style="margin-left: -30px">
<div style="margin-left: -30px" class="rack_elevation">
<div
hx-get="{% url 'dcim-api:rack-elevation' pk=object.pk %}?face={{ face }}&render=svg{% if extra_params %}&{{ extra_params }}{% endif %}"
hx-trigger="intersect"