fix: preserve reorder drag width and images
This commit is contained in:
@@ -138,6 +138,12 @@ GridStack-11-Stylesheet von NetBox 4.6.7, durch die Gerätekacheln mit einer
|
||||
berechneten Breite von `0px` unsichtbar waren. Topology-Breiten werden außerdem
|
||||
nach dem vollständigen Rendern zentral auf die HTTP-Antwort angewendet und
|
||||
zusätzlich direkt in die vorhandenen Geräte-Styles geschrieben.
|
||||
Ab Version `0.9.8` behalten Geräte beim Ziehen in Reorder ihre tatsächliche
|
||||
Rackbreite. Die 12-Spalten-Regeln lassen dazu GridStacks temporären
|
||||
Pixelkoordinaten während des Ziehens Vorrang, statt eine Prozentbreite auf das
|
||||
Browserfenster anzuwenden. Die serverseitig aufgebauten Reorder-Kacheln zeigen
|
||||
außerdem wieder die Front- und Rückseitenbilder des Gerätetyps entsprechend
|
||||
der gewählten Ansicht an.
|
||||
Bereits vorhandene Geräte, die dieselbe HE und Rackseite belegen, aber noch
|
||||
keine Plugin-Platzierungszeile besitzen, werden in Rack-SVG, Reorder und der
|
||||
Topology-Rack-Ansicht ohne Datenbankänderung gleichmäßig nebeneinander
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
from netbox.plugins import PluginConfig, get_plugin_config
|
||||
|
||||
__version__ = "0.9.7"
|
||||
__version__ = "0.9.8"
|
||||
|
||||
|
||||
class NetBoxUtilitiesConfig(PluginConfig):
|
||||
|
||||
@@ -41,35 +41,38 @@
|
||||
* provides GridStack 11's base stylesheet. GridStack 11 calculates item
|
||||
* widths through a CSS variable which the older runtime never sets. Without
|
||||
* these explicit rules every 12-column widget therefore has width: 0px.
|
||||
* Do not mark these declarations important: while dragging, GridStack writes
|
||||
* pixel coordinates inline on a fixed-position helper. Those inline values
|
||||
* must take precedence or the helper is sized relative to the viewport.
|
||||
*/
|
||||
.netbox-utilities-reorder-grid[gs-column="12"] > .grid-stack-item {
|
||||
width: calc(100% / 12) !important;
|
||||
width: calc(100% / 12);
|
||||
}
|
||||
|
||||
.netbox-utilities-reorder-grid[gs-column="12"] > .grid-stack-item[gs-w="2"] { width: calc(200% / 12) !important; }
|
||||
.netbox-utilities-reorder-grid[gs-column="12"] > .grid-stack-item[gs-w="3"] { width: 25% !important; }
|
||||
.netbox-utilities-reorder-grid[gs-column="12"] > .grid-stack-item[gs-w="4"] { width: calc(400% / 12) !important; }
|
||||
.netbox-utilities-reorder-grid[gs-column="12"] > .grid-stack-item[gs-w="5"] { width: calc(500% / 12) !important; }
|
||||
.netbox-utilities-reorder-grid[gs-column="12"] > .grid-stack-item[gs-w="6"] { width: 50% !important; }
|
||||
.netbox-utilities-reorder-grid[gs-column="12"] > .grid-stack-item[gs-w="7"] { width: calc(700% / 12) !important; }
|
||||
.netbox-utilities-reorder-grid[gs-column="12"] > .grid-stack-item[gs-w="8"] { width: calc(800% / 12) !important; }
|
||||
.netbox-utilities-reorder-grid[gs-column="12"] > .grid-stack-item[gs-w="9"] { width: 75% !important; }
|
||||
.netbox-utilities-reorder-grid[gs-column="12"] > .grid-stack-item[gs-w="10"] { width: calc(1000% / 12) !important; }
|
||||
.netbox-utilities-reorder-grid[gs-column="12"] > .grid-stack-item[gs-w="11"] { width: calc(1100% / 12) !important; }
|
||||
.netbox-utilities-reorder-grid[gs-column="12"] > .grid-stack-item[gs-w="12"] { width: 100% !important; }
|
||||
.netbox-utilities-reorder-grid[gs-column="12"] > .grid-stack-item[gs-w="2"] { width: calc(200% / 12); }
|
||||
.netbox-utilities-reorder-grid[gs-column="12"] > .grid-stack-item[gs-w="3"] { width: 25%; }
|
||||
.netbox-utilities-reorder-grid[gs-column="12"] > .grid-stack-item[gs-w="4"] { width: calc(400% / 12); }
|
||||
.netbox-utilities-reorder-grid[gs-column="12"] > .grid-stack-item[gs-w="5"] { width: calc(500% / 12); }
|
||||
.netbox-utilities-reorder-grid[gs-column="12"] > .grid-stack-item[gs-w="6"] { width: 50%; }
|
||||
.netbox-utilities-reorder-grid[gs-column="12"] > .grid-stack-item[gs-w="7"] { width: calc(700% / 12); }
|
||||
.netbox-utilities-reorder-grid[gs-column="12"] > .grid-stack-item[gs-w="8"] { width: calc(800% / 12); }
|
||||
.netbox-utilities-reorder-grid[gs-column="12"] > .grid-stack-item[gs-w="9"] { width: 75%; }
|
||||
.netbox-utilities-reorder-grid[gs-column="12"] > .grid-stack-item[gs-w="10"] { width: calc(1000% / 12); }
|
||||
.netbox-utilities-reorder-grid[gs-column="12"] > .grid-stack-item[gs-w="11"] { width: calc(1100% / 12); }
|
||||
.netbox-utilities-reorder-grid[gs-column="12"] > .grid-stack-item[gs-w="12"] { width: 100%; }
|
||||
|
||||
.netbox-utilities-reorder-grid[gs-column="12"] > .grid-stack-item[gs-x="0"] { left: 0 !important; }
|
||||
.netbox-utilities-reorder-grid[gs-column="12"] > .grid-stack-item[gs-x="1"] { left: calc(100% / 12) !important; }
|
||||
.netbox-utilities-reorder-grid[gs-column="12"] > .grid-stack-item[gs-x="2"] { left: calc(200% / 12) !important; }
|
||||
.netbox-utilities-reorder-grid[gs-column="12"] > .grid-stack-item[gs-x="3"] { left: 25% !important; }
|
||||
.netbox-utilities-reorder-grid[gs-column="12"] > .grid-stack-item[gs-x="4"] { left: calc(400% / 12) !important; }
|
||||
.netbox-utilities-reorder-grid[gs-column="12"] > .grid-stack-item[gs-x="5"] { left: calc(500% / 12) !important; }
|
||||
.netbox-utilities-reorder-grid[gs-column="12"] > .grid-stack-item[gs-x="6"] { left: 50% !important; }
|
||||
.netbox-utilities-reorder-grid[gs-column="12"] > .grid-stack-item[gs-x="7"] { left: calc(700% / 12) !important; }
|
||||
.netbox-utilities-reorder-grid[gs-column="12"] > .grid-stack-item[gs-x="8"] { left: calc(800% / 12) !important; }
|
||||
.netbox-utilities-reorder-grid[gs-column="12"] > .grid-stack-item[gs-x="9"] { left: 75% !important; }
|
||||
.netbox-utilities-reorder-grid[gs-column="12"] > .grid-stack-item[gs-x="10"] { left: calc(1000% / 12) !important; }
|
||||
.netbox-utilities-reorder-grid[gs-column="12"] > .grid-stack-item[gs-x="11"] { left: calc(1100% / 12) !important; }
|
||||
.netbox-utilities-reorder-grid[gs-column="12"] > .grid-stack-item[gs-x="0"] { left: 0; }
|
||||
.netbox-utilities-reorder-grid[gs-column="12"] > .grid-stack-item[gs-x="1"] { left: calc(100% / 12); }
|
||||
.netbox-utilities-reorder-grid[gs-column="12"] > .grid-stack-item[gs-x="2"] { left: calc(200% / 12); }
|
||||
.netbox-utilities-reorder-grid[gs-column="12"] > .grid-stack-item[gs-x="3"] { left: 25%; }
|
||||
.netbox-utilities-reorder-grid[gs-column="12"] > .grid-stack-item[gs-x="4"] { left: calc(400% / 12); }
|
||||
.netbox-utilities-reorder-grid[gs-column="12"] > .grid-stack-item[gs-x="5"] { left: calc(500% / 12); }
|
||||
.netbox-utilities-reorder-grid[gs-column="12"] > .grid-stack-item[gs-x="6"] { left: 50%; }
|
||||
.netbox-utilities-reorder-grid[gs-column="12"] > .grid-stack-item[gs-x="7"] { left: calc(700% / 12); }
|
||||
.netbox-utilities-reorder-grid[gs-column="12"] > .grid-stack-item[gs-x="8"] { left: calc(800% / 12); }
|
||||
.netbox-utilities-reorder-grid[gs-column="12"] > .grid-stack-item[gs-x="9"] { left: 75%; }
|
||||
.netbox-utilities-reorder-grid[gs-column="12"] > .grid-stack-item[gs-x="10"] { left: calc(1000% / 12); }
|
||||
.netbox-utilities-reorder-grid[gs-column="12"] > .grid-stack-item[gs-x="11"] { left: calc(1100% / 12); }
|
||||
|
||||
.netbox-utilities-sidebar-resizer {
|
||||
display: none;
|
||||
|
||||
@@ -45,8 +45,8 @@
|
||||
gs-locked="{% if not request.user|can_change:device %}true{% else %}false{% endif %}"
|
||||
gs-no-move="{% if not request.user|can_change:device %}true{% else %}false{% endif %}">
|
||||
<div class="grid-stack-item-content"
|
||||
style="background-color: #{{ device.role.color|default:'1685fc' }}; color: #{% if device.role.color %}{{ device.role.color|text_color }}{% else %}000000{% endif %}">
|
||||
{{ device.name|default:device.device_type }}
|
||||
style="{% if images and device.device_type.front_image %}background-image: url('{{ device.device_type.front_image.url }}'); background-size: {{ unit_width }}px;{% else %}background-color: #{{ device.role.color|default:'1685fc' }};{% endif %} color: #{% if device.role.color %}{{ device.role.color|text_color }}{% else %}000000{% endif %}">
|
||||
{% if images and device.device_type.front_image %}{% if labels %}{{ device.name|default:device.device_type }}{% endif %}{% else %}{{ device.name|default:device.device_type }}{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
|
||||
@@ -20,10 +20,17 @@
|
||||
data-item-face="{% if device.face == face %}{{ face }}{% else %}back{% endif %}"
|
||||
data-rack-width="{{ device.width }}"
|
||||
data-horizontal-position="{{ device.horizontal_position }}">
|
||||
<div class="grid-stack-item-content{% if device.face != face %} device_rear{% endif %}"
|
||||
style="{% if device.face == face %}background-color: #{{ device.color }}; color: #{{ device.text_color }}{% else %}color: #000000{% endif %}">
|
||||
{{ device.label }}
|
||||
{% if device.face == face %}
|
||||
<div class="grid-stack-item-content"
|
||||
style="{% if images and device.front_image %}background-image: url('{{ device.front_image }}'); background-size: {{ unit_width }}px; color: #{{ device.text_color }}{% else %}background-color: #{{ device.color }}; color: #{{ device.text_color }}{% endif %}">
|
||||
{% if images and device.front_image %}{% if labels %}{{ device.label }}{% endif %}{% else %}{{ device.label }}{% endif %}
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="grid-stack-item-content device_rear"
|
||||
style="{% if images and device.rear_image %}background-image: url('{{ device.rear_image }}'); background-size: {{ unit_width }}px; color: #{{ device.text_color }}{% else %}color: #000000{% endif %}">
|
||||
{% if images and device.rear_image %}{% if labels %}{{ device.label }}{% endif %}{% else %}{{ device.label }}{% endif %}
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
|
||||
@@ -348,9 +348,44 @@ class ReorderRackFrontendTest(SimpleTestCase):
|
||||
encoding="utf-8"
|
||||
)
|
||||
self.assertIn('.netbox-utilities-reorder-grid[gs-column="12"]', stylesheet)
|
||||
self.assertIn('.grid-stack-item[gs-w="6"] { width: 50% !important; }', stylesheet)
|
||||
self.assertIn('.grid-stack-item[gs-w="12"] { width: 100% !important; }', stylesheet)
|
||||
self.assertIn('.grid-stack-item[gs-x="6"] { left: 50% !important; }', stylesheet)
|
||||
self.assertIn('.grid-stack-item[gs-w="6"] { width: 50%; }', stylesheet)
|
||||
self.assertIn('.grid-stack-item[gs-w="12"] { width: 100%; }', stylesheet)
|
||||
self.assertIn('.grid-stack-item[gs-x="6"] { left: 50%; }', stylesheet)
|
||||
self.assertNotIn('.grid-stack-item[gs-w="6"] { width: 50% !important; }', stylesheet)
|
||||
|
||||
rendered_image_grid = render_to_string(
|
||||
"netbox_utilities/reorder_rack_grid.html",
|
||||
{
|
||||
"object": SimpleNamespace(units=[1], u_height=42),
|
||||
"face": "front",
|
||||
"grid_id": 0,
|
||||
"images": True,
|
||||
"labels": False,
|
||||
"unit_width": 220,
|
||||
"reorder_devices": [
|
||||
{
|
||||
"id": 10,
|
||||
"label": "LEO-Fritzbox",
|
||||
"face": "front",
|
||||
"full_depth": False,
|
||||
"grid_x": 0,
|
||||
"grid_y": 62,
|
||||
"grid_width": 6,
|
||||
"grid_height": 2,
|
||||
"width": 2,
|
||||
"horizontal_position": 1,
|
||||
"color": "f0a000",
|
||||
"text_color": "000000",
|
||||
"locked": False,
|
||||
"front_image": "/media/device-type-images/fritzbox.png",
|
||||
"rear_image": None,
|
||||
}
|
||||
],
|
||||
},
|
||||
)
|
||||
self.assertIn("background-image: url('/media/device-type-images/fritzbox.png')", rendered_image_grid)
|
||||
self.assertIn("background-size: 220px", rendered_image_grid)
|
||||
self.assertNotIn(">LEO-Fritzbox<", rendered_image_grid.replace("\n", ""))
|
||||
|
||||
def test_resolved_reorder_page_uses_server_side_width_view(self):
|
||||
view_class = resolve(reverse("dcim:rack_reorder", kwargs={"pk": 3})).func.view_class
|
||||
@@ -414,7 +449,7 @@ class ReorderRackFrontendTest(SimpleTestCase):
|
||||
self.assertEqual(template_name, "netbox_utilities/reorder_rack.html")
|
||||
self.assertEqual(context["reorder_devices"][0]["label"], "LEO-Fritzbox")
|
||||
self.assertEqual(context["reorder_devices"][0]["grid_width"], 6)
|
||||
self.assertEqual(context["asset_version"], "0.9.7")
|
||||
self.assertEqual(context["asset_version"], "0.9.8")
|
||||
self.assertIs(context["reorder_rack_width_data"], get_width_data.return_value)
|
||||
get_width_data.assert_called_once()
|
||||
self.assertIs(get_width_data.call_args.kwargs["rack"], rack)
|
||||
|
||||
@@ -246,7 +246,7 @@ class TopologyViewsRackWidthTest(SimpleTestCase):
|
||||
self.assertIn('id="netbox-utilities-topology-rack-width-styles"', html)
|
||||
self.assertIn('.rack-device[href="/dcim/devices/334/"]', html)
|
||||
self.assertIn("left: calc(50% + 3px) !important", html)
|
||||
self.assertIn("netbox_utilities/topology-rack-width.js?v=0.9.7", html)
|
||||
self.assertIn("netbox_utilities/topology-rack-width.js?v=0.9.8", html)
|
||||
self.assertIn("left:calc(0% + 3px)!important", html)
|
||||
self.assertIn("left:calc(50% + 3px)!important", html)
|
||||
self.assertIn("width:calc(50% - 6px)!important", html)
|
||||
|
||||
+1
-1
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
||||
|
||||
[project]
|
||||
name = "netbox-utilities"
|
||||
version = "0.9.7"
|
||||
version = "0.9.8"
|
||||
description = "Navigation, tenant utilities, partial-width rack devices, bulk uploads, and rack reordering for NetBox 4.6"
|
||||
readme = "README.md"
|
||||
requires-python = ">=3.12"
|
||||
|
||||
Reference in New Issue
Block a user