Explorar o código

Add connector images for front & rear port types

Include generated drawings for front/rear port types and display them in the
front/rear port forms, detail panels, and cable termination panels. Also make
the type field on the console, console server, power port, power outlet, and
front/rear port bulk edit forms image-aware.
Jeremy Stretch hai 8 horas
pai
achega
bdfa850f0c
Modificáronse 68 ficheiros con 651 adicións e 79 borrados
  1. 61 61
      netbox/dcim/choices.py
  2. 6 1
      netbox/dcim/forms/bulk_create.py
  3. 30 0
      netbox/dcim/forms/bulk_edit.py
  4. 4 4
      netbox/dcim/forms/filtersets.py
  5. 6 0
      netbox/dcim/models/device_components.py
  6. 2 0
      netbox/dcim/tests/test_models.py
  7. 4 0
      netbox/project-static/img/connectors/port/110-punch.svg
  8. 4 0
      netbox/project-static/img/connectors/port/4p2c.svg
  9. 4 0
      netbox/project-static/img/connectors/port/4p4c.svg
  10. 4 0
      netbox/project-static/img/connectors/port/6p2c.svg
  11. 4 0
      netbox/project-static/img/connectors/port/6p4c.svg
  12. 4 0
      netbox/project-static/img/connectors/port/6p6c.svg
  13. 4 0
      netbox/project-static/img/connectors/port/8p2c.svg
  14. 4 0
      netbox/project-static/img/connectors/port/8p4c.svg
  15. 4 0
      netbox/project-static/img/connectors/port/8p6c.svg
  16. 4 0
      netbox/project-static/img/connectors/port/8p8c.svg
  17. 5 0
      netbox/project-static/img/connectors/port/bnc.svg
  18. 4 0
      netbox/project-static/img/connectors/port/cs.svg
  19. 5 0
      netbox/project-static/img/connectors/port/f.svg
  20. 5 0
      netbox/project-static/img/connectors/port/fc-apc.svg
  21. 5 0
      netbox/project-static/img/connectors/port/fc-pc.svg
  22. 5 0
      netbox/project-static/img/connectors/port/fc-upc.svg
  23. 5 0
      netbox/project-static/img/connectors/port/fc.svg
  24. 4 0
      netbox/project-static/img/connectors/port/gg45.svg
  25. 4 0
      netbox/project-static/img/connectors/port/lc-apc.svg
  26. 4 0
      netbox/project-static/img/connectors/port/lc-pc.svg
  27. 4 0
      netbox/project-static/img/connectors/port/lc-upc.svg
  28. 4 0
      netbox/project-static/img/connectors/port/lc.svg
  29. 5 0
      netbox/project-static/img/connectors/port/lsh-apc.svg
  30. 5 0
      netbox/project-static/img/connectors/port/lsh-pc.svg
  31. 5 0
      netbox/project-static/img/connectors/port/lsh-upc.svg
  32. 5 0
      netbox/project-static/img/connectors/port/lsh.svg
  33. 4 0
      netbox/project-static/img/connectors/port/lx5-apc.svg
  34. 4 0
      netbox/project-static/img/connectors/port/lx5-pc.svg
  35. 4 0
      netbox/project-static/img/connectors/port/lx5-upc.svg
  36. 4 0
      netbox/project-static/img/connectors/port/lx5.svg
  37. 5 0
      netbox/project-static/img/connectors/port/mdc.svg
  38. 5 0
      netbox/project-static/img/connectors/port/mpo.svg
  39. 5 0
      netbox/project-static/img/connectors/port/mrj21.svg
  40. 5 0
      netbox/project-static/img/connectors/port/mtrj.svg
  41. 4 0
      netbox/project-static/img/connectors/port/mu-apc.svg
  42. 4 0
      netbox/project-static/img/connectors/port/mu-pc.svg
  43. 4 0
      netbox/project-static/img/connectors/port/mu-upc.svg
  44. 4 0
      netbox/project-static/img/connectors/port/mu.svg
  45. 5 0
      netbox/project-static/img/connectors/port/n.svg
  46. 4 0
      netbox/project-static/img/connectors/port/sc-apc.svg
  47. 4 0
      netbox/project-static/img/connectors/port/sc-pc.svg
  48. 4 0
      netbox/project-static/img/connectors/port/sc-upc.svg
  49. 4 0
      netbox/project-static/img/connectors/port/sc.svg
  50. 5 0
      netbox/project-static/img/connectors/port/sma-905.svg
  51. 5 0
      netbox/project-static/img/connectors/port/sma-906.svg
  52. 4 0
      netbox/project-static/img/connectors/port/sn.svg
  53. 5 0
      netbox/project-static/img/connectors/port/st.svg
  54. 5 0
      netbox/project-static/img/connectors/port/tera-1p.svg
  55. 5 0
      netbox/project-static/img/connectors/port/tera-2p.svg
  56. 5 0
      netbox/project-static/img/connectors/port/tera-4p.svg
  57. 5 0
      netbox/project-static/img/connectors/port/urm-p2.svg
  58. 5 0
      netbox/project-static/img/connectors/port/urm-p4.svg
  59. 5 0
      netbox/project-static/img/connectors/port/urm-p8.svg
  60. 4 0
      netbox/project-static/img/connectors/port/usb-a.svg
  61. 4 0
      netbox/project-static/img/connectors/port/usb-b.svg
  62. 4 0
      netbox/project-static/img/connectors/port/usb-c.svg
  63. 4 0
      netbox/project-static/img/connectors/port/usb-micro-a.svg
  64. 4 0
      netbox/project-static/img/connectors/port/usb-micro-ab.svg
  65. 4 0
      netbox/project-static/img/connectors/port/usb-micro-b.svg
  66. 4 0
      netbox/project-static/img/connectors/port/usb-mini-a.svg
  67. 4 0
      netbox/project-static/img/connectors/port/usb-mini-b.svg
  68. 274 13
      scripts/generate_connector_images.py

+ 61 - 61
netbox/dcim/choices.py

@@ -1780,78 +1780,78 @@ class PortTypeChoices(ChoiceSet):
         (
             _('Copper'),
             (
-                Choice(TYPE_8P8C, '8P8C'),
-                Choice(TYPE_8P6C, '8P6C'),
-                Choice(TYPE_8P4C, '8P4C'),
-                Choice(TYPE_8P2C, '8P2C'),
-                Choice(TYPE_6P6C, '6P6C'),
-                Choice(TYPE_6P4C, '6P4C'),
-                Choice(TYPE_6P2C, '6P2C'),
-                Choice(TYPE_4P4C, '4P4C'),
-                Choice(TYPE_4P2C, '4P2C'),
-                Choice(TYPE_GG45, 'GG45'),
-                Choice(TYPE_TERA4P, 'TERA 4P'),
-                Choice(TYPE_TERA2P, 'TERA 2P'),
-                Choice(TYPE_TERA1P, 'TERA 1P'),
-                Choice(TYPE_110_PUNCH, '110 Punch'),
-                Choice(TYPE_BNC, 'BNC'),
-                Choice(TYPE_F, 'F Connector'),
-                Choice(TYPE_N, 'N Connector'),
-                Choice(TYPE_MRJ21, 'MRJ21'),
+                Choice(TYPE_8P8C, '8P8C', image='connectors/port/8p8c.svg'),
+                Choice(TYPE_8P6C, '8P6C', image='connectors/port/8p6c.svg'),
+                Choice(TYPE_8P4C, '8P4C', image='connectors/port/8p4c.svg'),
+                Choice(TYPE_8P2C, '8P2C', image='connectors/port/8p2c.svg'),
+                Choice(TYPE_6P6C, '6P6C', image='connectors/port/6p6c.svg'),
+                Choice(TYPE_6P4C, '6P4C', image='connectors/port/6p4c.svg'),
+                Choice(TYPE_6P2C, '6P2C', image='connectors/port/6p2c.svg'),
+                Choice(TYPE_4P4C, '4P4C', image='connectors/port/4p4c.svg'),
+                Choice(TYPE_4P2C, '4P2C', image='connectors/port/4p2c.svg'),
+                Choice(TYPE_GG45, 'GG45', image='connectors/port/gg45.svg'),
+                Choice(TYPE_TERA4P, 'TERA 4P', image='connectors/port/tera-4p.svg'),
+                Choice(TYPE_TERA2P, 'TERA 2P', image='connectors/port/tera-2p.svg'),
+                Choice(TYPE_TERA1P, 'TERA 1P', image='connectors/port/tera-1p.svg'),
+                Choice(TYPE_110_PUNCH, '110 Punch', image='connectors/port/110-punch.svg'),
+                Choice(TYPE_BNC, 'BNC', image='connectors/port/bnc.svg'),
+                Choice(TYPE_F, 'F Connector', image='connectors/port/f.svg'),
+                Choice(TYPE_N, 'N Connector', image='connectors/port/n.svg'),
+                Choice(TYPE_MRJ21, 'MRJ21', image='connectors/port/mrj21.svg'),
             ),
         ),
         (
             _('Fiber Optic'),
             (
-                Choice(TYPE_FC, 'FC'),
-                Choice(TYPE_FC_PC, 'FC/PC'),
-                Choice(TYPE_FC_UPC, 'FC/UPC'),
-                Choice(TYPE_FC_APC, 'FC/APC'),
-                Choice(TYPE_LC, 'LC'),
-                Choice(TYPE_LC_PC, 'LC/PC'),
-                Choice(TYPE_LC_UPC, 'LC/UPC'),
-                Choice(TYPE_LC_APC, 'LC/APC'),
-                Choice(TYPE_MU, 'MU'),
-                Choice(TYPE_MU_PC, 'MU/PC'),
-                Choice(TYPE_MU_UPC, 'MU/UPC'),
-                Choice(TYPE_MU_APC, 'MU/APC'),
-                Choice(TYPE_LSH, 'LSH'),
-                Choice(TYPE_LSH_PC, 'LSH/PC'),
-                Choice(TYPE_LSH_UPC, 'LSH/UPC'),
-                Choice(TYPE_LSH_APC, 'LSH/APC'),
-                Choice(TYPE_LX5, 'LX.5'),
-                Choice(TYPE_LX5_PC, 'LX.5/PC'),
-                Choice(TYPE_LX5_UPC, 'LX.5/UPC'),
-                Choice(TYPE_LX5_APC, 'LX.5/APC'),
-                Choice(TYPE_MPO, 'MPO'),
-                Choice(TYPE_MTRJ, 'MTRJ'),
-                Choice(TYPE_SC, 'SC'),
-                Choice(TYPE_SC_PC, 'SC/PC'),
-                Choice(TYPE_SC_UPC, 'SC/UPC'),
-                Choice(TYPE_SC_APC, 'SC/APC'),
-                Choice(TYPE_ST, 'ST'),
-                Choice(TYPE_CS, 'CS'),
-                Choice(TYPE_SN, 'SN'),
-                Choice(TYPE_MDC, 'MDC'),
-                Choice(TYPE_SMA_905, 'SMA 905'),
-                Choice(TYPE_SMA_906, 'SMA 906'),
-                Choice(TYPE_URM_P2, 'URM-P2'),
-                Choice(TYPE_URM_P4, 'URM-P4'),
-                Choice(TYPE_URM_P8, 'URM-P8'),
+                Choice(TYPE_FC, 'FC', image='connectors/port/fc.svg'),
+                Choice(TYPE_FC_PC, 'FC/PC', image='connectors/port/fc-pc.svg'),
+                Choice(TYPE_FC_UPC, 'FC/UPC', image='connectors/port/fc-upc.svg'),
+                Choice(TYPE_FC_APC, 'FC/APC', image='connectors/port/fc-apc.svg'),
+                Choice(TYPE_LC, 'LC', image='connectors/port/lc.svg'),
+                Choice(TYPE_LC_PC, 'LC/PC', image='connectors/port/lc-pc.svg'),
+                Choice(TYPE_LC_UPC, 'LC/UPC', image='connectors/port/lc-upc.svg'),
+                Choice(TYPE_LC_APC, 'LC/APC', image='connectors/port/lc-apc.svg'),
+                Choice(TYPE_MU, 'MU', image='connectors/port/mu.svg'),
+                Choice(TYPE_MU_PC, 'MU/PC', image='connectors/port/mu-pc.svg'),
+                Choice(TYPE_MU_UPC, 'MU/UPC', image='connectors/port/mu-upc.svg'),
+                Choice(TYPE_MU_APC, 'MU/APC', image='connectors/port/mu-apc.svg'),
+                Choice(TYPE_LSH, 'LSH', image='connectors/port/lsh.svg'),
+                Choice(TYPE_LSH_PC, 'LSH/PC', image='connectors/port/lsh-pc.svg'),
+                Choice(TYPE_LSH_UPC, 'LSH/UPC', image='connectors/port/lsh-upc.svg'),
+                Choice(TYPE_LSH_APC, 'LSH/APC', image='connectors/port/lsh-apc.svg'),
+                Choice(TYPE_LX5, 'LX.5', image='connectors/port/lx5.svg'),
+                Choice(TYPE_LX5_PC, 'LX.5/PC', image='connectors/port/lx5-pc.svg'),
+                Choice(TYPE_LX5_UPC, 'LX.5/UPC', image='connectors/port/lx5-upc.svg'),
+                Choice(TYPE_LX5_APC, 'LX.5/APC', image='connectors/port/lx5-apc.svg'),
+                Choice(TYPE_MPO, 'MPO', image='connectors/port/mpo.svg'),
+                Choice(TYPE_MTRJ, 'MTRJ', image='connectors/port/mtrj.svg'),
+                Choice(TYPE_SC, 'SC', image='connectors/port/sc.svg'),
+                Choice(TYPE_SC_PC, 'SC/PC', image='connectors/port/sc-pc.svg'),
+                Choice(TYPE_SC_UPC, 'SC/UPC', image='connectors/port/sc-upc.svg'),
+                Choice(TYPE_SC_APC, 'SC/APC', image='connectors/port/sc-apc.svg'),
+                Choice(TYPE_ST, 'ST', image='connectors/port/st.svg'),
+                Choice(TYPE_CS, 'CS', image='connectors/port/cs.svg'),
+                Choice(TYPE_SN, 'SN', image='connectors/port/sn.svg'),
+                Choice(TYPE_MDC, 'MDC', image='connectors/port/mdc.svg'),
+                Choice(TYPE_SMA_905, 'SMA 905', image='connectors/port/sma-905.svg'),
+                Choice(TYPE_SMA_906, 'SMA 906', image='connectors/port/sma-906.svg'),
+                Choice(TYPE_URM_P2, 'URM-P2', image='connectors/port/urm-p2.svg'),
+                Choice(TYPE_URM_P4, 'URM-P4', image='connectors/port/urm-p4.svg'),
+                Choice(TYPE_URM_P8, 'URM-P8', image='connectors/port/urm-p8.svg'),
                 Choice(TYPE_SPLICE, 'Splice'),
             ),
         ),
         (
             _('USB'),
             (
-                Choice(TYPE_USB_A, 'USB Type A'),
-                Choice(TYPE_USB_B, 'USB Type B'),
-                Choice(TYPE_USB_C, 'USB Type C'),
-                Choice(TYPE_USB_MINI_A, 'USB Mini A'),
-                Choice(TYPE_USB_MINI_B, 'USB Mini B'),
-                Choice(TYPE_USB_MICRO_A, 'USB Micro A'),
-                Choice(TYPE_USB_MICRO_B, 'USB Micro B'),
-                Choice(TYPE_USB_MICRO_AB, 'USB Micro AB'),
+                Choice(TYPE_USB_A, 'USB Type A', image='connectors/port/usb-a.svg'),
+                Choice(TYPE_USB_B, 'USB Type B', image='connectors/port/usb-b.svg'),
+                Choice(TYPE_USB_C, 'USB Type C', image='connectors/port/usb-c.svg'),
+                Choice(TYPE_USB_MINI_A, 'USB Mini A', image='connectors/port/usb-mini-a.svg'),
+                Choice(TYPE_USB_MINI_B, 'USB Mini B', image='connectors/port/usb-mini-b.svg'),
+                Choice(TYPE_USB_MICRO_A, 'USB Micro A', image='connectors/port/usb-micro-a.svg'),
+                Choice(TYPE_USB_MICRO_B, 'USB Micro B', image='connectors/port/usb-micro-b.svg'),
+                Choice(TYPE_USB_MICRO_AB, 'USB Micro AB', image='connectors/port/usb-micro-ab.svg'),
             ),
         ),
         (

+ 6 - 1
netbox/dcim/forms/bulk_create.py

@@ -1,7 +1,7 @@
 from django import forms
 from django.utils.translation import gettext_lazy as _
 
-from dcim.choices import ConsolePortTypeChoices, PowerOutletTypeChoices, PowerPortTypeChoices
+from dcim.choices import ConsolePortTypeChoices, PortTypeChoices, PowerOutletTypeChoices, PowerPortTypeChoices
 from dcim.models import *
 from extras.models import Tag
 from netbox.forms.mixins import ChangelogMessageMixin, CustomFieldsMixin
@@ -156,6 +156,11 @@ class RearPortBulkCreateForm(
     DeviceBulkAddComponentForm
 ):
     model = RearPort
+    type = TypedChoiceField(
+        label=_('Type'),
+        choices=add_blank_choice(PortTypeChoices),
+        required=False,
+    )
     field_order = ('name', 'label', 'type', 'positions', 'mark_connected', 'description', 'tags')
 
 

+ 30 - 0
netbox/dcim/forms/bulk_edit.py

@@ -1723,6 +1723,11 @@ class ConsolePortBulkEditForm(
     ComponentBulkEditForm,
     form_from_model(ConsolePort, ['label', 'type', 'speed', 'mark_connected', 'description'])
 ):
+    type = ChoiceField(
+        label=_('Type'),
+        choices=add_blank_choice(ConsolePortTypeChoices),
+        required=False
+    )
     mark_connected = forms.NullBooleanField(
         label=_('Mark connected'),
         required=False,
@@ -1740,6 +1745,11 @@ class ConsoleServerPortBulkEditForm(
     ComponentBulkEditForm,
     form_from_model(ConsoleServerPort, ['label', 'type', 'speed', 'mark_connected', 'description'])
 ):
+    type = ChoiceField(
+        label=_('Type'),
+        choices=add_blank_choice(ConsolePortTypeChoices),
+        required=False
+    )
     mark_connected = forms.NullBooleanField(
         label=_('Mark connected'),
         required=False,
@@ -1757,6 +1767,11 @@ class PowerPortBulkEditForm(
     ComponentBulkEditForm,
     form_from_model(PowerPort, ['label', 'type', 'maximum_draw', 'allocated_draw', 'mark_connected', 'description'])
 ):
+    type = ChoiceField(
+        label=_('Type'),
+        choices=add_blank_choice(PowerPortTypeChoices),
+        required=False
+    )
     mark_connected = forms.NullBooleanField(
         label=_('Mark connected'),
         required=False,
@@ -1778,6 +1793,11 @@ class PowerOutletBulkEditForm(
         ['label', 'type', 'status', 'color', 'feed_leg', 'power_port', 'mark_connected', 'description']
     )
 ):
+    type = ChoiceField(
+        label=_('Type'),
+        choices=add_blank_choice(PowerOutletTypeChoices),
+        required=False
+    )
     mark_connected = forms.NullBooleanField(
         label=_('Mark connected'),
         required=False,
@@ -2110,6 +2130,11 @@ class FrontPortBulkEditForm(
     ComponentBulkEditForm,
     form_from_model(FrontPort, ['label', 'type', 'color', 'mark_connected', 'description'])
 ):
+    type = ChoiceField(
+        label=_('Type'),
+        choices=add_blank_choice(PortTypeChoices),
+        required=False
+    )
     mark_connected = forms.NullBooleanField(
         label=_('Mark connected'),
         required=False,
@@ -2127,6 +2152,11 @@ class RearPortBulkEditForm(
     ComponentBulkEditForm,
     form_from_model(RearPort, ['label', 'type', 'color', 'mark_connected', 'description'])
 ):
+    type = ChoiceField(
+        label=_('Type'),
+        choices=add_blank_choice(PortTypeChoices),
+        required=False
+    )
     mark_connected = forms.NullBooleanField(
         label=_('Mark connected'),
         required=False,

+ 4 - 4
netbox/dcim/forms/filtersets.py

@@ -2206,7 +2206,7 @@ class FrontPortFilterForm(CabledFilterForm, DeviceComponentFilterForm):
         FieldSet('owner_group_id', 'owner_id', name=_('Ownership')),
     )
     model = FrontPort
-    type = forms.MultipleChoiceField(
+    type = MultipleChoiceField(
         label=_('Type'),
         choices=PortTypeChoices,
         required=False
@@ -2225,7 +2225,7 @@ class FrontPortTemplateFilterForm(ModularDeviceComponentTemplateFilterForm):
         FieldSet('name', 'label', 'type', 'color', name=_('Attributes')),
         FieldSet('device_type_id', 'module_type_id', name=_('Device')),
     )
-    type = forms.MultipleChoiceField(
+    type = MultipleChoiceField(
         label=_('Type'),
         choices=PortTypeChoices,
         required=False
@@ -2249,7 +2249,7 @@ class RearPortFilterForm(CabledFilterForm, DeviceComponentFilterForm):
         FieldSet('cabled', 'occupied', name=_('Cable')),
         FieldSet('owner_group_id', 'owner_id', name=_('Ownership')),
     )
-    type = forms.MultipleChoiceField(
+    type = MultipleChoiceField(
         label=_('Type'),
         choices=PortTypeChoices,
         required=False
@@ -2268,7 +2268,7 @@ class RearPortTemplateFilterForm(ModularDeviceComponentTemplateFilterForm):
         FieldSet('name', 'label', 'type', 'color', name=_('Attributes')),
         FieldSet('device_type_id', 'module_type_id', name=_('Device')),
     )
-    type = forms.MultipleChoiceField(
+    type = MultipleChoiceField(
         label=_('Type'),
         choices=PortTypeChoices,
         required=False

+ 6 - 0
netbox/dcim/models/device_components.py

@@ -1586,6 +1586,9 @@ class FrontPort(ModularComponentModel, CabledObjectModel, TrackingModelMixin):
         verbose_name = _('front port')
         verbose_name_plural = _('front ports')
 
+    def get_type_image(self):
+        return PortTypeChoices.images.get(self.type)
+
     def clean(self):
         super().clean()
 
@@ -1628,6 +1631,9 @@ class RearPort(ModularComponentModel, CabledObjectModel, TrackingModelMixin):
         verbose_name = _('rear port')
         verbose_name_plural = _('rear ports')
 
+    def get_type_image(self):
+        return PortTypeChoices.images.get(self.type)
+
     def clean(self):
         super().clean()
 

+ 2 - 0
netbox/dcim/tests/test_models.py

@@ -4138,6 +4138,8 @@ class ComponentTypeImageTestCase(TestCase):
         for model, choices, value in (
             (ConsolePort, ConsolePortTypeChoices, ConsolePortTypeChoices.TYPE_DE9),
             (ConsoleServerPort, ConsolePortTypeChoices, ConsolePortTypeChoices.TYPE_RJ45),
+            (FrontPort, PortTypeChoices, PortTypeChoices.TYPE_8P8C),
+            (RearPort, PortTypeChoices, PortTypeChoices.TYPE_LC),
             (PowerPort, PowerPortTypeChoices, PowerPortTypeChoices.TYPE_IEC_C14),
             (PowerOutlet, PowerOutletTypeChoices, PowerOutletTypeChoices.TYPE_IEC_C13),
         ):

+ 4 - 0
netbox/project-static/img/connectors/port/110-punch.svg

@@ -0,0 +1,4 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="64" height="64" viewBox="0 0 64 64">
+<g fill="none" stroke="#000" stroke-width="3" stroke-linejoin="round"><path d="M4 53.59L4 10.41L7.5 20.06L11 10.41L14.5 20.06L18 10.41L21.5 20.06L25 10.41L28.5 20.06L32 10.41L35.5 20.06L39 10.41L42.5 20.06L46 10.41L49.5 20.06L53 10.41L56.5 20.06L60 10.41L60 53.59Z"/></g>
+<g fill="#000"><path d="M6.77 20.06L8.23 20.06L8.23 31.08L6.77 31.08Z"/><path d="M13.77 20.06L15.23 20.06L15.23 31.08L13.77 31.08Z"/><path d="M20.77 20.06L22.23 20.06L22.23 31.08L20.77 31.08Z"/><path d="M27.77 20.06L29.23 20.06L29.23 31.08L27.77 31.08Z"/><path d="M34.77 20.06L36.23 20.06L36.23 31.08L34.77 31.08Z"/><path d="M41.77 20.06L43.23 20.06L43.23 31.08L41.77 31.08Z"/><path d="M48.77 20.06L50.23 20.06L50.23 31.08L48.77 31.08Z"/><path d="M55.77 20.06L57.23 20.06L57.23 31.08L55.77 31.08Z"/></g>
+</svg>

+ 4 - 0
netbox/project-static/img/connectors/port/4p2c.svg

@@ -0,0 +1,4 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="64" height="64" viewBox="0 0 64 64">
+<g fill="none" stroke="#000" stroke-width="3" stroke-linejoin="round"><path d="M11.11 4L52.89 4L52.89 40.41L46.9 40.41L46.9 48.82L40.52 48.82L40.52 60L23.48 60L23.48 48.82L17.1 48.82L17.1 40.41L11.11 40.41Z"/></g>
+<g fill="#000"><path d="M27.83 4.11L30.76 4.11L30.76 16.88L27.83 16.88Z"/><path d="M33.24 4.11L36.17 4.11L36.17 16.88L33.24 16.88Z"/></g>
+</svg>

+ 4 - 0
netbox/project-static/img/connectors/port/4p4c.svg

@@ -0,0 +1,4 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="64" height="64" viewBox="0 0 64 64">
+<g fill="none" stroke="#000" stroke-width="3" stroke-linejoin="round"><path d="M11.11 4L52.89 4L52.89 40.41L46.9 40.41L46.9 48.82L40.52 48.82L40.52 60L23.48 60L23.48 48.82L17.1 48.82L17.1 40.41L11.11 40.41Z"/></g>
+<g fill="#000"><path d="M22.42 4.11L25.35 4.11L25.35 16.88L22.42 16.88Z"/><path d="M27.83 4.11L30.76 4.11L30.76 16.88L27.83 16.88Z"/><path d="M33.24 4.11L36.17 4.11L36.17 16.88L33.24 16.88Z"/><path d="M38.65 4.11L41.58 4.11L41.58 16.88L38.65 16.88Z"/></g>
+</svg>

+ 4 - 0
netbox/project-static/img/connectors/port/6p2c.svg

@@ -0,0 +1,4 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="64" height="64" viewBox="0 0 64 64">
+<g fill="none" stroke="#000" stroke-width="3" stroke-linejoin="round"><path d="M4 5.45L60 5.45L60 44.21L50.7 44.21L50.7 53.74L43.51 53.74L43.51 58.55L20.49 58.55L20.49 53.74L13.3 53.74L13.3 44.21L4 44.21Z"/></g>
+<g fill="#000"><path d="M27.56 5.56L30.68 5.56L30.68 19.16L27.56 19.16Z"/><path d="M33.32 5.56L36.44 5.56L36.44 19.16L33.32 19.16Z"/></g>
+</svg>

+ 4 - 0
netbox/project-static/img/connectors/port/6p4c.svg

@@ -0,0 +1,4 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="64" height="64" viewBox="0 0 64 64">
+<g fill="none" stroke="#000" stroke-width="3" stroke-linejoin="round"><path d="M4 5.45L60 5.45L60 44.21L50.7 44.21L50.7 53.74L43.51 53.74L43.51 58.55L20.49 58.55L20.49 53.74L13.3 53.74L13.3 44.21L4 44.21Z"/></g>
+<g fill="#000"><path d="M21.8 5.56L24.92 5.56L24.92 19.16L21.8 19.16Z"/><path d="M27.56 5.56L30.68 5.56L30.68 19.16L27.56 19.16Z"/><path d="M33.32 5.56L36.44 5.56L36.44 19.16L33.32 19.16Z"/><path d="M39.08 5.56L42.2 5.56L42.2 19.16L39.08 19.16Z"/></g>
+</svg>

+ 4 - 0
netbox/project-static/img/connectors/port/6p6c.svg

@@ -0,0 +1,4 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="64" height="64" viewBox="0 0 64 64">
+<g fill="none" stroke="#000" stroke-width="3" stroke-linejoin="round"><path d="M4 5.45L60 5.45L60 44.21L50.7 44.21L50.7 53.74L43.51 53.74L43.51 58.55L20.49 58.55L20.49 53.74L13.3 53.74L13.3 44.21L4 44.21Z"/></g>
+<g fill="#000"><path d="M16.04 5.56L19.16 5.56L19.16 19.16L16.04 19.16Z"/><path d="M21.8 5.56L24.92 5.56L24.92 19.16L21.8 19.16Z"/><path d="M27.56 5.56L30.68 5.56L30.68 19.16L27.56 19.16Z"/><path d="M33.32 5.56L36.44 5.56L36.44 19.16L33.32 19.16Z"/><path d="M39.08 5.56L42.2 5.56L42.2 19.16L39.08 19.16Z"/><path d="M44.84 5.56L47.96 5.56L47.96 19.16L44.84 19.16Z"/></g>
+</svg>

+ 4 - 0
netbox/project-static/img/connectors/port/8p2c.svg

@@ -0,0 +1,4 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="64" height="64" viewBox="0 0 64 64">
+<g fill="none" stroke="#000" stroke-width="3" stroke-linejoin="round"><path d="M4 9.97L60 9.97L60 42.13L46.81 42.13L46.81 50.03L41.54 50.03L41.54 54.03L22.46 54.03L22.46 50.03L17.19 50.03L17.19 42.13L4 42.13Z"/></g>
+<g fill="#000"><path d="M28.32 10.07L30.9 10.07L30.9 21.35L28.32 21.35Z"/><path d="M33.1 10.07L35.68 10.07L35.68 21.35L33.1 21.35Z"/></g>
+</svg>

+ 4 - 0
netbox/project-static/img/connectors/port/8p4c.svg

@@ -0,0 +1,4 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="64" height="64" viewBox="0 0 64 64">
+<g fill="none" stroke="#000" stroke-width="3" stroke-linejoin="round"><path d="M4 9.97L60 9.97L60 42.13L46.81 42.13L46.81 50.03L41.54 50.03L41.54 54.03L22.46 54.03L22.46 50.03L17.19 50.03L17.19 42.13L4 42.13Z"/></g>
+<g fill="#000"><path d="M23.54 10.07L26.13 10.07L26.13 21.35L23.54 21.35Z"/><path d="M28.32 10.07L30.9 10.07L30.9 21.35L28.32 21.35Z"/><path d="M33.1 10.07L35.68 10.07L35.68 21.35L33.1 21.35Z"/><path d="M37.87 10.07L40.46 10.07L40.46 21.35L37.87 21.35Z"/></g>
+</svg>

+ 4 - 0
netbox/project-static/img/connectors/port/8p6c.svg

@@ -0,0 +1,4 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="64" height="64" viewBox="0 0 64 64">
+<g fill="none" stroke="#000" stroke-width="3" stroke-linejoin="round"><path d="M4 9.97L60 9.97L60 42.13L46.81 42.13L46.81 50.03L41.54 50.03L41.54 54.03L22.46 54.03L22.46 50.03L17.19 50.03L17.19 42.13L4 42.13Z"/></g>
+<g fill="#000"><path d="M18.76 10.07L21.35 10.07L21.35 21.35L18.76 21.35Z"/><path d="M23.54 10.07L26.13 10.07L26.13 21.35L23.54 21.35Z"/><path d="M28.32 10.07L30.9 10.07L30.9 21.35L28.32 21.35Z"/><path d="M33.1 10.07L35.68 10.07L35.68 21.35L33.1 21.35Z"/><path d="M37.87 10.07L40.46 10.07L40.46 21.35L37.87 21.35Z"/><path d="M42.65 10.07L45.24 10.07L45.24 21.35L42.65 21.35Z"/></g>
+</svg>

+ 4 - 0
netbox/project-static/img/connectors/port/8p8c.svg

@@ -0,0 +1,4 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="64" height="64" viewBox="0 0 64 64">
+<g fill="none" stroke="#000" stroke-width="3" stroke-linejoin="round"><path d="M4 9.97L60 9.97L60 42.13L46.81 42.13L46.81 50.03L41.54 50.03L41.54 54.03L22.46 54.03L22.46 50.03L17.19 50.03L17.19 42.13L4 42.13Z"/></g>
+<g fill="#000"><path d="M13.99 10.07L16.57 10.07L16.57 21.35L13.99 21.35Z"/><path d="M18.76 10.07L21.35 10.07L21.35 21.35L18.76 21.35Z"/><path d="M23.54 10.07L26.13 10.07L26.13 21.35L23.54 21.35Z"/><path d="M28.32 10.07L30.9 10.07L30.9 21.35L28.32 21.35Z"/><path d="M33.1 10.07L35.68 10.07L35.68 21.35L33.1 21.35Z"/><path d="M37.87 10.07L40.46 10.07L40.46 21.35L37.87 21.35Z"/><path d="M42.65 10.07L45.24 10.07L45.24 21.35L42.65 21.35Z"/><path d="M47.43 10.07L50.01 10.07L50.01 21.35L47.43 21.35Z"/></g>
+</svg>

+ 5 - 0
netbox/project-static/img/connectors/port/bnc.svg

@@ -0,0 +1,5 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="64" height="64" viewBox="0 0 64 64">
+<g fill="none" stroke="#000" stroke-width="3" stroke-linejoin="round"><circle cx="32" cy="32" r="24.56"/></g>
+<g fill="none" stroke="#000" stroke-width="2" stroke-linejoin="round"><circle cx="32" cy="32" r="20.62"/><circle cx="32" cy="32" r="11.71"/></g>
+<g fill="#000"><circle cx="9.09" cy="32" r="5.09"/><circle cx="54.91" cy="32" r="5.09"/><circle cx="32" cy="32" r="5.35"/></g>
+</svg>

+ 4 - 0
netbox/project-static/img/connectors/port/cs.svg

@@ -0,0 +1,4 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="64" height="64" viewBox="0 0 64 64">
+<g fill="none" stroke="#000" stroke-width="3" stroke-linejoin="round"><path d="M4 19.16L23.44 19.16L23.44 7.03L40.56 7.03L40.56 19.16L60 19.16L60 56.97L4 56.97Z"/></g>
+<g fill="#000"><circle cx="18.45" cy="38.06" r="4.46"/><circle cx="45.55" cy="38.06" r="4.46"/></g>
+</svg>

+ 5 - 0
netbox/project-static/img/connectors/port/f.svg

@@ -0,0 +1,5 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="64" height="64" viewBox="0 0 64 64">
+<g fill="none" stroke="#000" stroke-width="3" stroke-linejoin="round"><path d="M32 4L56.25 18L56.25 46L32 60L7.75 46L7.75 18Z"/></g>
+<g fill="none" stroke="#000" stroke-width="2" stroke-linejoin="round"><circle cx="32" cy="32" r="20.99"/><circle cx="32" cy="32" r="11.46"/></g>
+<g fill="#000"><circle cx="32" cy="32" r="2.87"/></g>
+</svg>

+ 5 - 0
netbox/project-static/img/connectors/port/fc-apc.svg

@@ -0,0 +1,5 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="64" height="64" viewBox="0 0 64 64">
+<g fill="none" stroke="#000" stroke-width="3" stroke-linejoin="round"><path d="M9.6 4L54.4 4A5.6 5.6 0 0 1 60 9.6L60 54.4A5.6 5.6 0 0 1 54.4 60L9.6 60A5.6 5.6 0 0 1 4 54.4L4 9.6A5.6 5.6 0 0 1 9.6 4Z"/></g>
+<g fill="none" stroke="#000" stroke-width="2" stroke-linejoin="round"><path d="M36.01 17.62A14.93 14.93 0 1 1 27.99 17.62L27.99 24.41L36.01 24.41Z"/><circle cx="32" cy="32" r="8.59"/><circle cx="49.7" cy="14.3" r="4.11"/><circle cx="14.3" cy="49.7" r="4.11"/></g>
+<g fill="#000"><circle cx="32" cy="32" r="4.67"/></g>
+</svg>

+ 5 - 0
netbox/project-static/img/connectors/port/fc-pc.svg

@@ -0,0 +1,5 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="64" height="64" viewBox="0 0 64 64">
+<g fill="none" stroke="#000" stroke-width="3" stroke-linejoin="round"><path d="M9.6 4L54.4 4A5.6 5.6 0 0 1 60 9.6L60 54.4A5.6 5.6 0 0 1 54.4 60L9.6 60A5.6 5.6 0 0 1 4 54.4L4 9.6A5.6 5.6 0 0 1 9.6 4Z"/></g>
+<g fill="none" stroke="#000" stroke-width="2" stroke-linejoin="round"><path d="M36.01 17.62A14.93 14.93 0 1 1 27.99 17.62L27.99 24.41L36.01 24.41Z"/><circle cx="32" cy="32" r="8.59"/><circle cx="49.7" cy="14.3" r="4.11"/><circle cx="14.3" cy="49.7" r="4.11"/></g>
+<g fill="#000"><circle cx="32" cy="32" r="4.67"/></g>
+</svg>

+ 5 - 0
netbox/project-static/img/connectors/port/fc-upc.svg

@@ -0,0 +1,5 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="64" height="64" viewBox="0 0 64 64">
+<g fill="none" stroke="#000" stroke-width="3" stroke-linejoin="round"><path d="M9.6 4L54.4 4A5.6 5.6 0 0 1 60 9.6L60 54.4A5.6 5.6 0 0 1 54.4 60L9.6 60A5.6 5.6 0 0 1 4 54.4L4 9.6A5.6 5.6 0 0 1 9.6 4Z"/></g>
+<g fill="none" stroke="#000" stroke-width="2" stroke-linejoin="round"><path d="M36.01 17.62A14.93 14.93 0 1 1 27.99 17.62L27.99 24.41L36.01 24.41Z"/><circle cx="32" cy="32" r="8.59"/><circle cx="49.7" cy="14.3" r="4.11"/><circle cx="14.3" cy="49.7" r="4.11"/></g>
+<g fill="#000"><circle cx="32" cy="32" r="4.67"/></g>
+</svg>

+ 5 - 0
netbox/project-static/img/connectors/port/fc.svg

@@ -0,0 +1,5 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="64" height="64" viewBox="0 0 64 64">
+<g fill="none" stroke="#000" stroke-width="3" stroke-linejoin="round"><path d="M9.6 4L54.4 4A5.6 5.6 0 0 1 60 9.6L60 54.4A5.6 5.6 0 0 1 54.4 60L9.6 60A5.6 5.6 0 0 1 4 54.4L4 9.6A5.6 5.6 0 0 1 9.6 4Z"/></g>
+<g fill="none" stroke="#000" stroke-width="2" stroke-linejoin="round"><path d="M36.01 17.62A14.93 14.93 0 1 1 27.99 17.62L27.99 24.41L36.01 24.41Z"/><circle cx="32" cy="32" r="8.59"/><circle cx="49.7" cy="14.3" r="4.11"/><circle cx="14.3" cy="49.7" r="4.11"/></g>
+<g fill="#000"><circle cx="32" cy="32" r="4.67"/></g>
+</svg>

+ 4 - 0
netbox/project-static/img/connectors/port/gg45.svg

@@ -0,0 +1,4 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="64" height="64" viewBox="0 0 64 64">
+<g fill="none" stroke="#000" stroke-width="3" stroke-linejoin="round"><path d="M4 9.97L60 9.97L60 42.13L46.81 42.13L46.81 50.03L41.54 50.03L41.54 54.03L22.46 54.03L22.46 50.03L17.19 50.03L17.19 42.13L4 42.13Z"/></g>
+<g fill="#000"><path d="M13.99 10.07L16.57 10.07L16.57 21.35L13.99 21.35Z"/><path d="M18.76 10.07L21.35 10.07L21.35 21.35L18.76 21.35Z"/><path d="M23.54 10.07L26.13 10.07L26.13 21.35L23.54 21.35Z"/><path d="M28.32 10.07L30.9 10.07L30.9 21.35L28.32 21.35Z"/><path d="M33.1 10.07L35.68 10.07L35.68 21.35L33.1 21.35Z"/><path d="M37.87 10.07L40.46 10.07L40.46 21.35L37.87 21.35Z"/><path d="M42.65 10.07L45.24 10.07L45.24 21.35L42.65 21.35Z"/><path d="M47.43 10.07L50.01 10.07L50.01 21.35L47.43 21.35Z"/><path d="M7.88 27.93L10.51 27.93L10.51 42.04L7.88 42.04Z"/><path d="M12.68 27.93L15.31 27.93L15.31 42.04L12.68 42.04Z"/><path d="M48.69 27.93L51.32 27.93L51.32 42.04L48.69 42.04Z"/><path d="M53.49 27.93L56.12 27.93L56.12 42.04L53.49 42.04Z"/></g>
+</svg>

+ 4 - 0
netbox/project-static/img/connectors/port/lc-apc.svg

@@ -0,0 +1,4 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="64" height="64" viewBox="0 0 64 64">
+<g fill="none" stroke="#000" stroke-width="3" stroke-linejoin="round"><path d="M16.1 60L16.1 28.2L19.56 28.2L19.56 22.67L15.41 22.67L15.41 14.37L22.32 14.37L22.32 4L41.68 4L41.68 14.37L48.59 14.37L48.59 22.67L44.44 22.67L44.44 28.2L47.9 28.2L47.9 60Z"/></g>
+<g fill="#000"><circle cx="32" cy="44.1" r="4.32"/></g>
+</svg>

+ 4 - 0
netbox/project-static/img/connectors/port/lc-pc.svg

@@ -0,0 +1,4 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="64" height="64" viewBox="0 0 64 64">
+<g fill="none" stroke="#000" stroke-width="3" stroke-linejoin="round"><path d="M16.1 60L16.1 28.2L19.56 28.2L19.56 22.67L15.41 22.67L15.41 14.37L22.32 14.37L22.32 4L41.68 4L41.68 14.37L48.59 14.37L48.59 22.67L44.44 22.67L44.44 28.2L47.9 28.2L47.9 60Z"/></g>
+<g fill="#000"><circle cx="32" cy="44.1" r="4.32"/></g>
+</svg>

+ 4 - 0
netbox/project-static/img/connectors/port/lc-upc.svg

@@ -0,0 +1,4 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="64" height="64" viewBox="0 0 64 64">
+<g fill="none" stroke="#000" stroke-width="3" stroke-linejoin="round"><path d="M16.1 60L16.1 28.2L19.56 28.2L19.56 22.67L15.41 22.67L15.41 14.37L22.32 14.37L22.32 4L41.68 4L41.68 14.37L48.59 14.37L48.59 22.67L44.44 22.67L44.44 28.2L47.9 28.2L47.9 60Z"/></g>
+<g fill="#000"><circle cx="32" cy="44.1" r="4.32"/></g>
+</svg>

+ 4 - 0
netbox/project-static/img/connectors/port/lc.svg

@@ -0,0 +1,4 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="64" height="64" viewBox="0 0 64 64">
+<g fill="none" stroke="#000" stroke-width="3" stroke-linejoin="round"><path d="M16.1 60L16.1 28.2L19.56 28.2L19.56 22.67L15.41 22.67L15.41 14.37L22.32 14.37L22.32 4L41.68 4L41.68 14.37L48.59 14.37L48.59 22.67L44.44 22.67L44.44 28.2L47.9 28.2L47.9 60Z"/></g>
+<g fill="#000"><circle cx="32" cy="44.1" r="4.32"/></g>
+</svg>

+ 5 - 0
netbox/project-static/img/connectors/port/lsh-apc.svg

@@ -0,0 +1,5 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="64" height="64" viewBox="0 0 64 64">
+<g fill="none" stroke="#000" stroke-width="3" stroke-linejoin="round"><path d="M14.18 4L49.82 4A2.83 2.83 0 0 1 52.65 6.83L52.65 57.17A2.83 2.83 0 0 1 49.82 60L14.18 60A2.83 2.83 0 0 1 11.35 57.17L11.35 6.83A2.83 2.83 0 0 1 14.18 4Z"/></g>
+<g fill="none" stroke="#000" stroke-width="2" stroke-linejoin="round"><path d="M18.42 18.99L45.58 18.99A2.26 2.26 0 0 1 47.84 21.25L47.84 54.06A2.26 2.26 0 0 1 45.58 56.32L18.42 56.32A2.26 2.26 0 0 1 16.16 54.06L16.16 21.25A2.26 2.26 0 0 1 18.42 18.99Z"/></g>
+<g fill="#000"><path d="M23.52 6.55L40.48 6.55L40.48 13.33L23.52 13.33Z"/></g>
+</svg>

+ 5 - 0
netbox/project-static/img/connectors/port/lsh-pc.svg

@@ -0,0 +1,5 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="64" height="64" viewBox="0 0 64 64">
+<g fill="none" stroke="#000" stroke-width="3" stroke-linejoin="round"><path d="M14.18 4L49.82 4A2.83 2.83 0 0 1 52.65 6.83L52.65 57.17A2.83 2.83 0 0 1 49.82 60L14.18 60A2.83 2.83 0 0 1 11.35 57.17L11.35 6.83A2.83 2.83 0 0 1 14.18 4Z"/></g>
+<g fill="none" stroke="#000" stroke-width="2" stroke-linejoin="round"><path d="M18.42 18.99L45.58 18.99A2.26 2.26 0 0 1 47.84 21.25L47.84 54.06A2.26 2.26 0 0 1 45.58 56.32L18.42 56.32A2.26 2.26 0 0 1 16.16 54.06L16.16 21.25A2.26 2.26 0 0 1 18.42 18.99Z"/></g>
+<g fill="#000"><path d="M23.52 6.55L40.48 6.55L40.48 13.33L23.52 13.33Z"/></g>
+</svg>

+ 5 - 0
netbox/project-static/img/connectors/port/lsh-upc.svg

@@ -0,0 +1,5 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="64" height="64" viewBox="0 0 64 64">
+<g fill="none" stroke="#000" stroke-width="3" stroke-linejoin="round"><path d="M14.18 4L49.82 4A2.83 2.83 0 0 1 52.65 6.83L52.65 57.17A2.83 2.83 0 0 1 49.82 60L14.18 60A2.83 2.83 0 0 1 11.35 57.17L11.35 6.83A2.83 2.83 0 0 1 14.18 4Z"/></g>
+<g fill="none" stroke="#000" stroke-width="2" stroke-linejoin="round"><path d="M18.42 18.99L45.58 18.99A2.26 2.26 0 0 1 47.84 21.25L47.84 54.06A2.26 2.26 0 0 1 45.58 56.32L18.42 56.32A2.26 2.26 0 0 1 16.16 54.06L16.16 21.25A2.26 2.26 0 0 1 18.42 18.99Z"/></g>
+<g fill="#000"><path d="M23.52 6.55L40.48 6.55L40.48 13.33L23.52 13.33Z"/></g>
+</svg>

+ 5 - 0
netbox/project-static/img/connectors/port/lsh.svg

@@ -0,0 +1,5 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="64" height="64" viewBox="0 0 64 64">
+<g fill="none" stroke="#000" stroke-width="3" stroke-linejoin="round"><path d="M14.18 4L49.82 4A2.83 2.83 0 0 1 52.65 6.83L52.65 57.17A2.83 2.83 0 0 1 49.82 60L14.18 60A2.83 2.83 0 0 1 11.35 57.17L11.35 6.83A2.83 2.83 0 0 1 14.18 4Z"/></g>
+<g fill="none" stroke="#000" stroke-width="2" stroke-linejoin="round"><path d="M18.42 18.99L45.58 18.99A2.26 2.26 0 0 1 47.84 21.25L47.84 54.06A2.26 2.26 0 0 1 45.58 56.32L18.42 56.32A2.26 2.26 0 0 1 16.16 54.06L16.16 21.25A2.26 2.26 0 0 1 18.42 18.99Z"/></g>
+<g fill="#000"><path d="M23.52 6.55L40.48 6.55L40.48 13.33L23.52 13.33Z"/></g>
+</svg>

+ 4 - 0
netbox/project-static/img/connectors/port/lx5-apc.svg

@@ -0,0 +1,4 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="64" height="64" viewBox="0 0 64 64">
+<g fill="none" stroke="#000" stroke-width="3" stroke-linejoin="round"><path d="M17.51 4L46.49 4A2.64 2.64 0 0 1 49.13 6.64L49.13 57.36A2.64 2.64 0 0 1 46.49 60L17.51 60A2.64 2.64 0 0 1 14.87 57.36L14.87 6.64A2.64 2.64 0 0 1 17.51 4Z"/></g>
+<g fill="#000"><path d="M25.41 6.64L38.59 6.64L38.59 12.56L25.41 12.56Z"/><circle cx="32" cy="43.2" r="4.12"/></g>
+</svg>

+ 4 - 0
netbox/project-static/img/connectors/port/lx5-pc.svg

@@ -0,0 +1,4 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="64" height="64" viewBox="0 0 64 64">
+<g fill="none" stroke="#000" stroke-width="3" stroke-linejoin="round"><path d="M17.51 4L46.49 4A2.64 2.64 0 0 1 49.13 6.64L49.13 57.36A2.64 2.64 0 0 1 46.49 60L17.51 60A2.64 2.64 0 0 1 14.87 57.36L14.87 6.64A2.64 2.64 0 0 1 17.51 4Z"/></g>
+<g fill="#000"><path d="M25.41 6.64L38.59 6.64L38.59 12.56L25.41 12.56Z"/><circle cx="32" cy="43.2" r="4.12"/></g>
+</svg>

+ 4 - 0
netbox/project-static/img/connectors/port/lx5-upc.svg

@@ -0,0 +1,4 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="64" height="64" viewBox="0 0 64 64">
+<g fill="none" stroke="#000" stroke-width="3" stroke-linejoin="round"><path d="M17.51 4L46.49 4A2.64 2.64 0 0 1 49.13 6.64L49.13 57.36A2.64 2.64 0 0 1 46.49 60L17.51 60A2.64 2.64 0 0 1 14.87 57.36L14.87 6.64A2.64 2.64 0 0 1 17.51 4Z"/></g>
+<g fill="#000"><path d="M25.41 6.64L38.59 6.64L38.59 12.56L25.41 12.56Z"/><circle cx="32" cy="43.2" r="4.12"/></g>
+</svg>

+ 4 - 0
netbox/project-static/img/connectors/port/lx5.svg

@@ -0,0 +1,4 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="64" height="64" viewBox="0 0 64 64">
+<g fill="none" stroke="#000" stroke-width="3" stroke-linejoin="round"><path d="M17.51 4L46.49 4A2.64 2.64 0 0 1 49.13 6.64L49.13 57.36A2.64 2.64 0 0 1 46.49 60L17.51 60A2.64 2.64 0 0 1 14.87 57.36L14.87 6.64A2.64 2.64 0 0 1 17.51 4Z"/></g>
+<g fill="#000"><path d="M25.41 6.64L38.59 6.64L38.59 12.56L25.41 12.56Z"/><circle cx="32" cy="43.2" r="4.12"/></g>
+</svg>

+ 5 - 0
netbox/project-static/img/connectors/port/mdc.svg

@@ -0,0 +1,5 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="64" height="64" viewBox="0 0 64 64">
+<g fill="none" stroke="#000" stroke-width="3" stroke-linejoin="round"><path d="M21.85 4L42.15 4A1.85 1.85 0 0 1 44 5.85L44 58.15A1.85 1.85 0 0 1 42.15 60L21.85 60A1.85 1.85 0 0 1 20 58.15L20 5.85A1.85 1.85 0 0 1 21.85 4Z"/></g>
+<g fill="none" stroke="#000" stroke-width="2" stroke-linejoin="round"><path d="M32 16L32 16A8.98 8.98 0 0 1 40.98 24.98L40.98 43.94A8.98 8.98 0 0 1 32 52.92L32 52.92A8.98 8.98 0 0 1 23.02 43.94L23.02 24.98A8.98 8.98 0 0 1 32 16Z"/></g>
+<g fill="#000"><path d="M26.77 5.85L37.23 5.85L37.23 11.38L26.77 11.38Z"/><circle cx="32" cy="24.92" r="3.85"/><circle cx="32" cy="44" r="3.85"/></g>
+</svg>

+ 5 - 0
netbox/project-static/img/connectors/port/mpo.svg

@@ -0,0 +1,5 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="64" height="64" viewBox="0 0 64 64">
+<g fill="none" stroke="#000" stroke-width="3" stroke-linejoin="round"><path d="M9.42 13.48L25.23 13.48L25.23 9.87L38.77 9.87L38.77 13.48L54.58 13.48A34.32 34.32 0 0 1 54.58 50.52L9.42 50.52A34.32 34.32 0 0 1 9.42 13.48Z"/></g>
+<g fill="none" stroke="#000" stroke-width="2" stroke-linejoin="round"><path d="M18.45 26.47L45.55 26.47A0.9 0.9 0 0 1 46.45 27.37L46.45 36.63A0.9 0.9 0 0 1 45.55 37.53L18.45 37.53A0.9 0.9 0 0 1 17.55 36.63L17.55 27.37A0.9 0.9 0 0 1 18.45 26.47Z"/></g>
+<g fill="#000"><circle cx="21.61" cy="32" r="1.81"/><circle cx="42.39" cy="32" r="1.81"/><path d="M25.45 31.32L38.55 31.32L38.55 32.68L25.45 32.68Z"/></g>
+</svg>

+ 5 - 0
netbox/project-static/img/connectors/port/mrj21.svg

@@ -0,0 +1,5 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="64" height="64" viewBox="0 0 64 64">
+<g fill="none" stroke="#000" stroke-width="3" stroke-linejoin="round"><path d="M5.55 19.17L58.45 19.17A1.55 1.55 0 0 1 60 20.72L60 43.28A1.55 1.55 0 0 1 58.45 44.83L5.55 44.83A1.55 1.55 0 0 1 4 43.28L4 20.72A1.55 1.55 0 0 1 5.55 19.17Z"/></g>
+<g fill="none" stroke="#000" stroke-width="2" stroke-linejoin="round"><path d="M17.77 22.12L47.18 22.12L52.1 27.02L52.1 29.27L46.35 39.49L42.5 41.86L15.91 41.86L12.93 38.89L12.93 32.76A10.97 10.97 0 0 0 17.77 22.12Z"/><circle cx="9.85" cy="25.03" r="2.32"/><circle cx="54.15" cy="38.97" r="2.32"/></g>
+<g fill="#000"><path d="M19.51 24.54L49.33 24.54L49.33 30.08L19.51 30.08Z"/><path d="M14.67 33.92L44.49 33.92L44.49 39.46L14.67 39.46Z"/></g>
+</svg>

+ 5 - 0
netbox/project-static/img/connectors/port/mtrj.svg

@@ -0,0 +1,5 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="64" height="64" viewBox="0 0 64 64">
+<g fill="none" stroke="#000" stroke-width="3" stroke-linejoin="round"><path d="M4 17.61L16.44 17.61L16.44 9.83L47.56 9.83L47.56 17.61L60 17.61L60 54.17L4 54.17Z"/></g>
+<g fill="none" stroke="#000" stroke-width="2" stroke-linejoin="round"><path d="M16.44 26.36L47.56 26.36A1.56 1.56 0 0 1 49.11 27.92L49.11 43.86A1.56 1.56 0 0 1 47.56 45.42L16.44 45.42A1.56 1.56 0 0 1 14.89 43.86L14.89 27.92A1.56 1.56 0 0 1 16.44 26.36Z"/></g>
+<g fill="#000"><circle cx="21.89" cy="35.89" r="2.72"/><circle cx="42.11" cy="35.89" r="2.72"/><circle cx="29.08" cy="35.89" r="1.56"/><circle cx="34.92" cy="35.89" r="1.56"/></g>
+</svg>

+ 4 - 0
netbox/project-static/img/connectors/port/mu-apc.svg

@@ -0,0 +1,4 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="64" height="64" viewBox="0 0 64 64">
+<g fill="none" stroke="#000" stroke-width="3" stroke-linejoin="round"><path d="M12 4L52 4L52 13.5L56.5 20L56.5 44L52 50.5L52 60L12 60Z"/></g>
+<g fill="#000"><circle cx="32" cy="32" r="6.25"/></g>
+</svg>

+ 4 - 0
netbox/project-static/img/connectors/port/mu-pc.svg

@@ -0,0 +1,4 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="64" height="64" viewBox="0 0 64 64">
+<g fill="none" stroke="#000" stroke-width="3" stroke-linejoin="round"><path d="M12 4L52 4L52 13.5L56.5 20L56.5 44L52 50.5L52 60L12 60Z"/></g>
+<g fill="#000"><circle cx="32" cy="32" r="6.25"/></g>
+</svg>

+ 4 - 0
netbox/project-static/img/connectors/port/mu-upc.svg

@@ -0,0 +1,4 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="64" height="64" viewBox="0 0 64 64">
+<g fill="none" stroke="#000" stroke-width="3" stroke-linejoin="round"><path d="M12 4L52 4L52 13.5L56.5 20L56.5 44L52 50.5L52 60L12 60Z"/></g>
+<g fill="#000"><circle cx="32" cy="32" r="6.25"/></g>
+</svg>

+ 4 - 0
netbox/project-static/img/connectors/port/mu.svg

@@ -0,0 +1,4 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="64" height="64" viewBox="0 0 64 64">
+<g fill="none" stroke="#000" stroke-width="3" stroke-linejoin="round"><path d="M12 4L52 4L52 13.5L56.5 20L56.5 44L52 50.5L52 60L12 60Z"/></g>
+<g fill="#000"><circle cx="32" cy="32" r="6.25"/></g>
+</svg>

+ 5 - 0
netbox/project-static/img/connectors/port/n.svg

@@ -0,0 +1,5 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="64" height="64" viewBox="0 0 64 64">
+<g fill="none" stroke="#000" stroke-width="3" stroke-linejoin="round"><path d="M7.31 4L56.69 4A3.31 3.31 0 0 1 60 7.31L60 56.69A3.31 3.31 0 0 1 56.69 60L7.31 60A3.31 3.31 0 0 1 4 56.69L4 7.31A3.31 3.31 0 0 1 7.31 4Z"/></g>
+<g fill="none" stroke="#000" stroke-width="2" stroke-linejoin="round"><circle cx="11.83" cy="11.83" r="3.53"/><circle cx="11.83" cy="52.17" r="3.53"/><circle cx="52.17" cy="11.83" r="3.53"/><circle cx="52.17" cy="52.17" r="3.53"/><circle cx="32" cy="32" r="17.42"/><circle cx="32" cy="32" r="9.48"/></g>
+<g fill="#000"><circle cx="32" cy="32" r="3.31"/></g>
+</svg>

+ 4 - 0
netbox/project-static/img/connectors/port/sc-apc.svg

@@ -0,0 +1,4 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="64" height="64" viewBox="0 0 64 64">
+<g fill="none" stroke="#000" stroke-width="3" stroke-linejoin="round"><path d="M4 11.78L25.78 11.78L25.78 6.18L38.22 6.18L38.22 11.78L60 11.78L60 57.82L4 57.82Z"/></g>
+<g fill="#000"><circle cx="32" cy="34.8" r="7.78"/></g>
+</svg>

+ 4 - 0
netbox/project-static/img/connectors/port/sc-pc.svg

@@ -0,0 +1,4 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="64" height="64" viewBox="0 0 64 64">
+<g fill="none" stroke="#000" stroke-width="3" stroke-linejoin="round"><path d="M4 11.78L25.78 11.78L25.78 6.18L38.22 6.18L38.22 11.78L60 11.78L60 57.82L4 57.82Z"/></g>
+<g fill="#000"><circle cx="32" cy="34.8" r="7.78"/></g>
+</svg>

+ 4 - 0
netbox/project-static/img/connectors/port/sc-upc.svg

@@ -0,0 +1,4 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="64" height="64" viewBox="0 0 64 64">
+<g fill="none" stroke="#000" stroke-width="3" stroke-linejoin="round"><path d="M4 11.78L25.78 11.78L25.78 6.18L38.22 6.18L38.22 11.78L60 11.78L60 57.82L4 57.82Z"/></g>
+<g fill="#000"><circle cx="32" cy="34.8" r="7.78"/></g>
+</svg>

+ 4 - 0
netbox/project-static/img/connectors/port/sc.svg

@@ -0,0 +1,4 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="64" height="64" viewBox="0 0 64 64">
+<g fill="none" stroke="#000" stroke-width="3" stroke-linejoin="round"><path d="M4 11.78L25.78 11.78L25.78 6.18L38.22 6.18L38.22 11.78L60 11.78L60 57.82L4 57.82Z"/></g>
+<g fill="#000"><circle cx="32" cy="34.8" r="7.78"/></g>
+</svg>

+ 5 - 0
netbox/project-static/img/connectors/port/sma-905.svg

@@ -0,0 +1,5 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="64" height="64" viewBox="0 0 64 64">
+<g fill="none" stroke="#000" stroke-width="3" stroke-linejoin="round"><circle cx="32" cy="32" r="28"/></g>
+<g fill="none" stroke="#000" stroke-width="2" stroke-linejoin="round"><circle cx="32" cy="32" r="22.05"/></g>
+<g fill="#000"><circle cx="32" cy="32" r="14.02"/></g>
+</svg>

+ 5 - 0
netbox/project-static/img/connectors/port/sma-906.svg

@@ -0,0 +1,5 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="64" height="64" viewBox="0 0 64 64">
+<g fill="none" stroke="#000" stroke-width="3" stroke-linejoin="round"><circle cx="32" cy="32" r="28"/></g>
+<g fill="none" stroke="#000" stroke-width="2" stroke-linejoin="round"><circle cx="32" cy="32" r="22.05"/></g>
+<g fill="#000"><circle cx="32" cy="32" r="14.02"/></g>
+</svg>

+ 4 - 0
netbox/project-static/img/connectors/port/sn.svg

@@ -0,0 +1,4 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="64" height="64" viewBox="0 0 64 64">
+<g fill="none" stroke="#000" stroke-width="3" stroke-linejoin="round"><path d="M20.6 4L27.26 4L27.26 8.74L36.74 8.74L36.74 4L43.4 4L43.4 60L36.74 60L36.74 55.26L27.26 55.26L27.26 60L20.6 60Z"/></g>
+<g fill="#000"><circle cx="32" cy="22.82" r="3.7"/><circle cx="32" cy="41.18" r="3.7"/></g>
+</svg>

+ 5 - 0
netbox/project-static/img/connectors/port/st.svg

@@ -0,0 +1,5 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="64" height="64" viewBox="0 0 64 64">
+<g fill="none" stroke="#000" stroke-width="3" stroke-linejoin="round"><path d="M36.23 9.47A22.92 22.92 0 1 1 27.77 9.47L27.77 14.63L36.23 14.63Z"/></g>
+<g fill="none" stroke="#000" stroke-width="2" stroke-linejoin="round"><circle cx="32" cy="32" r="17.87"/></g>
+<g fill="#000"><circle cx="8.43" cy="32" r="4.23"/><circle cx="55.57" cy="32" r="4.23"/><circle cx="32" cy="32" r="8.14"/></g>
+</svg>

+ 5 - 0
netbox/project-static/img/connectors/port/tera-1p.svg

@@ -0,0 +1,5 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="64" height="64" viewBox="0 0 64 64">
+<g fill="none" stroke="#000" stroke-width="3" stroke-linejoin="round"><path d="M16.85 4L47.15 4A2.42 2.42 0 0 1 49.58 6.42L49.58 57.58A2.42 2.42 0 0 1 47.15 60L16.85 60A2.42 2.42 0 0 1 14.42 57.58L14.42 6.42A2.42 2.42 0 0 1 16.85 4Z"/></g>
+<g fill="none" stroke="#000" stroke-width="2" stroke-linejoin="round"><path d="M18.45 24.61L28.34 24.61A1.45 1.45 0 0 1 29.79 26.06L29.79 36.17A1.45 1.45 0 0 1 28.34 37.62L18.45 37.62A1.45 1.45 0 0 1 16.99 36.17L16.99 26.06A1.45 1.45 0 0 1 18.45 24.61Z"/><path d="M18.45 39.08L28.34 39.08A1.45 1.45 0 0 1 29.79 40.53L29.79 51.81A1.45 1.45 0 0 1 28.34 53.26L18.45 53.26A1.45 1.45 0 0 1 16.99 51.81L16.99 40.53A1.45 1.45 0 0 1 18.45 39.08Z"/><path d="M35.66 24.61L45.55 24.61A1.45 1.45 0 0 1 47.01 26.06L47.01 36.17A1.45 1.45 0 0 1 45.55 37.62L35.66 37.62A1.45 1.45 0 0 1 34.21 36.17L34.21 26.06A1.45 1.45 0 0 1 35.66 24.61Z"/><path d="M35.66 39.08L45.55 39.08A1.45 1.45 0 0 1 47.01 40.53L47.01 51.81A1.45 1.45 0 0 1 45.55 53.26L35.66 53.26A1.45 1.45 0 0 1 34.21 51.81L34.21 40.53A1.45 1.45 0 0 1 35.66 39.08Z"/></g>
+<g fill="#000"><path d="M27.33 8.48L36.67 8.48A1.7 1.7 0 0 1 38.36 10.18L38.36 10.18A1.7 1.7 0 0 1 36.67 11.88L27.33 11.88A1.7 1.7 0 0 1 25.64 10.18L25.64 10.18A1.7 1.7 0 0 1 27.33 8.48Z"/><path d="M24.17 26.91L26.84 26.91L26.84 30.79L24.17 30.79Z"/><path d="M19.93 26.91L22.59 26.91L22.59 30.79L19.93 30.79Z"/></g>
+</svg>

+ 5 - 0
netbox/project-static/img/connectors/port/tera-2p.svg

@@ -0,0 +1,5 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="64" height="64" viewBox="0 0 64 64">
+<g fill="none" stroke="#000" stroke-width="3" stroke-linejoin="round"><path d="M16.85 4L47.15 4A2.42 2.42 0 0 1 49.58 6.42L49.58 57.58A2.42 2.42 0 0 1 47.15 60L16.85 60A2.42 2.42 0 0 1 14.42 57.58L14.42 6.42A2.42 2.42 0 0 1 16.85 4Z"/></g>
+<g fill="none" stroke="#000" stroke-width="2" stroke-linejoin="round"><path d="M18.45 24.61L28.34 24.61A1.45 1.45 0 0 1 29.79 26.06L29.79 36.17A1.45 1.45 0 0 1 28.34 37.62L18.45 37.62A1.45 1.45 0 0 1 16.99 36.17L16.99 26.06A1.45 1.45 0 0 1 18.45 24.61Z"/><path d="M18.45 39.08L28.34 39.08A1.45 1.45 0 0 1 29.79 40.53L29.79 51.81A1.45 1.45 0 0 1 28.34 53.26L18.45 53.26A1.45 1.45 0 0 1 16.99 51.81L16.99 40.53A1.45 1.45 0 0 1 18.45 39.08Z"/><path d="M35.66 24.61L45.55 24.61A1.45 1.45 0 0 1 47.01 26.06L47.01 36.17A1.45 1.45 0 0 1 45.55 37.62L35.66 37.62A1.45 1.45 0 0 1 34.21 36.17L34.21 26.06A1.45 1.45 0 0 1 35.66 24.61Z"/><path d="M35.66 39.08L45.55 39.08A1.45 1.45 0 0 1 47.01 40.53L47.01 51.81A1.45 1.45 0 0 1 45.55 53.26L35.66 53.26A1.45 1.45 0 0 1 34.21 51.81L34.21 40.53A1.45 1.45 0 0 1 35.66 39.08Z"/></g>
+<g fill="#000"><path d="M27.33 8.48L36.67 8.48A1.7 1.7 0 0 1 38.36 10.18L38.36 10.18A1.7 1.7 0 0 1 36.67 11.88L27.33 11.88A1.7 1.7 0 0 1 25.64 10.18L25.64 10.18A1.7 1.7 0 0 1 27.33 8.48Z"/><path d="M24.17 26.91L26.84 26.91L26.84 30.79L24.17 30.79Z"/><path d="M19.93 26.91L22.59 26.91L22.59 30.79L19.93 30.79Z"/><path d="M24.17 46.79L26.84 46.79L26.84 50.67L24.17 50.67Z"/><path d="M19.93 46.79L22.59 46.79L22.59 50.67L19.93 50.67Z"/></g>
+</svg>

+ 5 - 0
netbox/project-static/img/connectors/port/tera-4p.svg

@@ -0,0 +1,5 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="64" height="64" viewBox="0 0 64 64">
+<g fill="none" stroke="#000" stroke-width="3" stroke-linejoin="round"><path d="M16.85 4L47.15 4A2.42 2.42 0 0 1 49.58 6.42L49.58 57.58A2.42 2.42 0 0 1 47.15 60L16.85 60A2.42 2.42 0 0 1 14.42 57.58L14.42 6.42A2.42 2.42 0 0 1 16.85 4Z"/></g>
+<g fill="none" stroke="#000" stroke-width="2" stroke-linejoin="round"><path d="M18.45 24.61L28.34 24.61A1.45 1.45 0 0 1 29.79 26.06L29.79 36.17A1.45 1.45 0 0 1 28.34 37.62L18.45 37.62A1.45 1.45 0 0 1 16.99 36.17L16.99 26.06A1.45 1.45 0 0 1 18.45 24.61Z"/><path d="M18.45 39.08L28.34 39.08A1.45 1.45 0 0 1 29.79 40.53L29.79 51.81A1.45 1.45 0 0 1 28.34 53.26L18.45 53.26A1.45 1.45 0 0 1 16.99 51.81L16.99 40.53A1.45 1.45 0 0 1 18.45 39.08Z"/><path d="M35.66 24.61L45.55 24.61A1.45 1.45 0 0 1 47.01 26.06L47.01 36.17A1.45 1.45 0 0 1 45.55 37.62L35.66 37.62A1.45 1.45 0 0 1 34.21 36.17L34.21 26.06A1.45 1.45 0 0 1 35.66 24.61Z"/><path d="M35.66 39.08L45.55 39.08A1.45 1.45 0 0 1 47.01 40.53L47.01 51.81A1.45 1.45 0 0 1 45.55 53.26L35.66 53.26A1.45 1.45 0 0 1 34.21 51.81L34.21 40.53A1.45 1.45 0 0 1 35.66 39.08Z"/></g>
+<g fill="#000"><path d="M27.33 8.48L36.67 8.48A1.7 1.7 0 0 1 38.36 10.18L38.36 10.18A1.7 1.7 0 0 1 36.67 11.88L27.33 11.88A1.7 1.7 0 0 1 25.64 10.18L25.64 10.18A1.7 1.7 0 0 1 27.33 8.48Z"/><path d="M24.17 26.91L26.84 26.91L26.84 30.79L24.17 30.79Z"/><path d="M19.93 26.91L22.59 26.91L22.59 30.79L19.93 30.79Z"/><path d="M24.17 46.79L26.84 46.79L26.84 50.67L24.17 50.67Z"/><path d="M19.93 46.79L22.59 46.79L22.59 50.67L19.93 50.67Z"/><path d="M37.16 26.91L39.83 26.91L39.83 30.79L37.16 30.79Z"/><path d="M41.41 26.91L44.07 26.91L44.07 30.79L41.41 30.79Z"/><path d="M37.16 46.79L39.83 46.79L39.83 50.67L37.16 50.67Z"/><path d="M41.41 46.79L44.07 46.79L44.07 50.67L41.41 50.67Z"/></g>
+</svg>

+ 5 - 0
netbox/project-static/img/connectors/port/urm-p2.svg

@@ -0,0 +1,5 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="64" height="64" viewBox="0 0 64 64">
+<g fill="none" stroke="#000" stroke-width="3" stroke-linejoin="round"><path d="M22.81 4L41.19 4A4.18 4.18 0 0 1 45.37 8.18L45.37 55.82A4.18 4.18 0 0 1 41.19 60L22.81 60A4.18 4.18 0 0 1 18.63 55.82L18.63 8.18A4.18 4.18 0 0 1 22.81 4Z"/></g>
+<g fill="none" stroke="#000" stroke-width="2" stroke-linejoin="round"><path d="M24.06 11.1L39.94 11.1A2.09 2.09 0 0 1 42.03 13.19L42.03 50.81A2.09 2.09 0 0 1 39.94 52.9L24.06 52.9A2.09 2.09 0 0 1 21.97 50.81L21.97 13.19A2.09 2.09 0 0 1 24.06 11.1Z"/></g>
+<g fill="#000"><path d="M28.66 11.94L35.34 11.94L35.34 15.28L28.66 15.28Z"/><path d="M28.66 48.72L35.34 48.72L35.34 52.06L28.66 52.06Z"/><circle cx="32" cy="26.15" r="2.61"/><circle cx="32" cy="37.85" r="2.61"/></g>
+</svg>

+ 5 - 0
netbox/project-static/img/connectors/port/urm-p4.svg

@@ -0,0 +1,5 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="64" height="64" viewBox="0 0 64 64">
+<g fill="none" stroke="#000" stroke-width="3" stroke-linejoin="round"><path d="M9.43 4L54.57 4A4.18 4.18 0 0 1 58.75 8.18L58.75 55.82A4.18 4.18 0 0 1 54.57 60L9.43 60A4.18 4.18 0 0 1 5.25 55.82L5.25 8.18A4.18 4.18 0 0 1 9.43 4Z"/></g>
+<g fill="none" stroke="#000" stroke-width="2" stroke-linejoin="round"><path d="M10.69 11.1L26.57 11.1A2.09 2.09 0 0 1 28.66 13.19L28.66 50.81A2.09 2.09 0 0 1 26.57 52.9L10.69 52.9A2.09 2.09 0 0 1 8.6 50.81L8.6 13.19A2.09 2.09 0 0 1 10.69 11.1Z"/><path d="M37.43 11.1L53.31 11.1A2.09 2.09 0 0 1 55.4 13.19L55.4 50.81A2.09 2.09 0 0 1 53.31 52.9L37.43 52.9A2.09 2.09 0 0 1 35.34 50.81L35.34 13.19A2.09 2.09 0 0 1 37.43 11.1Z"/></g>
+<g fill="#000"><path d="M15.28 11.94L21.97 11.94L21.97 15.28L15.28 15.28Z"/><path d="M15.28 48.72L21.97 48.72L21.97 52.06L15.28 52.06Z"/><circle cx="18.63" cy="26.15" r="2.61"/><circle cx="18.63" cy="37.85" r="2.61"/><path d="M42.03 11.94L48.72 11.94L48.72 15.28L42.03 15.28Z"/><path d="M42.03 48.72L48.72 48.72L48.72 52.06L42.03 52.06Z"/><circle cx="45.37" cy="26.15" r="2.61"/><circle cx="45.37" cy="37.85" r="2.61"/></g>
+</svg>

+ 5 - 0
netbox/project-static/img/connectors/port/urm-p8.svg

@@ -0,0 +1,5 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="64" height="64" viewBox="0 0 64 64">
+<g fill="none" stroke="#000" stroke-width="3" stroke-linejoin="round"><path d="M6.19 17.34L57.81 17.34A2.19 2.19 0 0 1 60 19.53L60 44.47A2.19 2.19 0 0 1 57.81 46.66L6.19 46.66A2.19 2.19 0 0 1 4 44.47L4 19.53A2.19 2.19 0 0 1 6.19 17.34Z"/></g>
+<g fill="none" stroke="#000" stroke-width="2" stroke-linejoin="round"><path d="M6.84 21.06L15.16 21.06A1.09 1.09 0 0 1 16.25 22.16L16.25 41.84A1.09 1.09 0 0 1 15.16 42.94L6.84 42.94A1.09 1.09 0 0 1 5.75 41.84L5.75 22.16A1.09 1.09 0 0 1 6.84 21.06Z"/><path d="M20.84 21.06L29.16 21.06A1.09 1.09 0 0 1 30.25 22.16L30.25 41.84A1.09 1.09 0 0 1 29.16 42.94L20.84 42.94A1.09 1.09 0 0 1 19.75 41.84L19.75 22.16A1.09 1.09 0 0 1 20.84 21.06Z"/><path d="M34.84 21.06L43.16 21.06A1.09 1.09 0 0 1 44.25 22.16L44.25 41.84A1.09 1.09 0 0 1 43.16 42.94L34.84 42.94A1.09 1.09 0 0 1 33.75 41.84L33.75 22.16A1.09 1.09 0 0 1 34.84 21.06Z"/><path d="M48.84 21.06L57.16 21.06A1.09 1.09 0 0 1 58.25 22.16L58.25 41.84A1.09 1.09 0 0 1 57.16 42.94L48.84 42.94A1.09 1.09 0 0 1 47.75 41.84L47.75 22.16A1.09 1.09 0 0 1 48.84 21.06Z"/></g>
+<g fill="#000"><path d="M9.25 21.5L12.75 21.5L12.75 23.25L9.25 23.25Z"/><path d="M9.25 40.75L12.75 40.75L12.75 42.5L9.25 42.5Z"/><circle cx="11" cy="28.94" r="1.37"/><circle cx="11" cy="35.06" r="1.37"/><path d="M23.25 21.5L26.75 21.5L26.75 23.25L23.25 23.25Z"/><path d="M23.25 40.75L26.75 40.75L26.75 42.5L23.25 42.5Z"/><circle cx="25" cy="28.94" r="1.37"/><circle cx="25" cy="35.06" r="1.37"/><path d="M37.25 21.5L40.75 21.5L40.75 23.25L37.25 23.25Z"/><path d="M37.25 40.75L40.75 40.75L40.75 42.5L37.25 42.5Z"/><circle cx="39" cy="28.94" r="1.37"/><circle cx="39" cy="35.06" r="1.37"/><path d="M51.25 21.5L54.75 21.5L54.75 23.25L51.25 23.25Z"/><path d="M51.25 40.75L54.75 40.75L54.75 42.5L51.25 42.5Z"/><circle cx="53" cy="28.94" r="1.37"/><circle cx="53" cy="35.06" r="1.37"/></g>
+</svg>

+ 4 - 0
netbox/project-static/img/connectors/port/usb-a.svg

@@ -0,0 +1,4 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="64" height="64" viewBox="0 0 64 64">
+<g fill="none" stroke="#000" stroke-width="3" stroke-linejoin="round"><path d="M5.4 21.5L58.6 21.5A1.4 1.4 0 0 1 60 22.9L60 41.1A1.4 1.4 0 0 1 58.6 42.5L5.4 42.5A1.4 1.4 0 0 1 4 41.1L4 22.9A1.4 1.4 0 0 1 5.4 21.5Z"/></g>
+<g fill="#000"><path d="M8.67 23.6L55.33 23.6L55.33 32.47L8.67 32.47Z"/></g>
+</svg>

+ 4 - 0
netbox/project-static/img/connectors/port/usb-b.svg

@@ -0,0 +1,4 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="64" height="64" viewBox="0 0 64 64">
+<g fill="none" stroke="#000" stroke-width="3" stroke-linejoin="round"><path d="M13.22 6.31L50.78 6.31L60 15.53L60 57.69L4 57.69L4 15.53Z"/></g>
+<g fill="#000"><path d="M14.87 24.09L49.13 24.09L49.13 45.18L14.87 45.18Z"/></g>
+</svg>

+ 4 - 0
netbox/project-static/img/connectors/port/usb-c.svg

@@ -0,0 +1,4 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="64" height="64" viewBox="0 0 64 64">
+<g fill="none" stroke="#000" stroke-width="3" stroke-linejoin="round"><path d="M12.67 23.33L51.33 23.33A8.67 8.67 0 0 1 60 32L60 32A8.67 8.67 0 0 1 51.33 40.67L12.67 40.67A8.67 8.67 0 0 1 4 32L4 32A8.67 8.67 0 0 1 12.67 23.33Z"/></g>
+<g fill="#000"><path d="M11 29.67L53 29.67A1 1 0 0 1 54 30.67L54 33.33A1 1 0 0 1 53 34.33L11 34.33A1 1 0 0 1 10 33.33L10 30.67A1 1 0 0 1 11 29.67Z"/></g>
+</svg>

+ 4 - 0
netbox/project-static/img/connectors/port/usb-micro-a.svg

@@ -0,0 +1,4 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="64" height="64" viewBox="0 0 64 64">
+<g fill="none" stroke="#000" stroke-width="3" stroke-linejoin="round"><path d="M5.62 24.49L58.38 24.49A1.62 1.62 0 0 1 60 26.12L60 37.88A1.62 1.62 0 0 1 58.38 39.51L5.62 39.51A1.62 1.62 0 0 1 4 37.88L4 26.12A1.62 1.62 0 0 1 5.62 24.49Z"/></g>
+<g fill="#000"><path d="M11.71 26.52L52.29 26.52L52.29 30.17L11.71 30.17Z"/></g>
+</svg>

+ 4 - 0
netbox/project-static/img/connectors/port/usb-micro-ab.svg

@@ -0,0 +1,4 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="64" height="64" viewBox="0 0 64 64">
+<g fill="none" stroke="#000" stroke-width="3" stroke-linejoin="round"><path d="M5.62 24.49L58.38 24.49A1.62 1.62 0 0 1 60 26.12L60 37.88A1.62 1.62 0 0 1 58.38 39.51L5.62 39.51A1.62 1.62 0 0 1 4 37.88L4 26.12A1.62 1.62 0 0 1 5.62 24.49Z"/></g>
+<g fill="#000"><path d="M11.71 26.52L52.29 26.52L52.29 30.17L11.71 30.17Z"/></g>
+</svg>

+ 4 - 0
netbox/project-static/img/connectors/port/usb-micro-b.svg

@@ -0,0 +1,4 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="64" height="64" viewBox="0 0 64 64">
+<g fill="none" stroke="#000" stroke-width="3" stroke-linejoin="round"><path d="M60 24.49L60 33.83L54.32 39.51L9.68 39.51L4 33.83L4 24.49Z"/></g>
+<g fill="#000"><path d="M11.71 26.52L52.29 26.52L52.29 30.17L11.71 30.17Z"/></g>
+</svg>

+ 4 - 0
netbox/project-static/img/connectors/port/usb-mini-a.svg

@@ -0,0 +1,4 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="64" height="64" viewBox="0 0 64 64">
+<g fill="none" stroke="#000" stroke-width="3" stroke-linejoin="round"><path d="M4 20L60 20L60 34.4A9.6 9.6 0 0 1 50.4 44L13.6 44A9.6 9.6 0 0 1 4 34.4Z"/></g>
+<g fill="#000"><path d="M12 24.8L52 24.8L52 29.6L12 29.6Z"/></g>
+</svg>

+ 4 - 0
netbox/project-static/img/connectors/port/usb-mini-b.svg

@@ -0,0 +1,4 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="64" height="64" viewBox="0 0 64 64">
+<g fill="none" stroke="#000" stroke-width="3" stroke-linejoin="round"><path d="M4 20L60 20L60 33.6L55.2 33.6L50.4 44L13.6 44L8.8 33.6L4 33.6Z"/></g>
+<g fill="#000"><path d="M12 24.8L52 24.8L52 29.6L12 29.6Z"/></g>
+</svg>

+ 274 - 13
scripts/generate_connector_images.py

@@ -1,6 +1,6 @@
 #!/usr/bin/env python3
 """
-Generate the connector drawings displayed for power port, power outlet, and console port types.
+Generate the connector drawings displayed for power port, power outlet, console port, and front/rear port types.
 
 Each drawing depicts the mating face of a connector as a 64x64 SVG: the face is outlined and its contacts are
 filled solid black, on a transparent background (the UI inverts the drawings in dark mode). Receptacles are drawn
@@ -729,24 +729,103 @@ def d_sub(contacts, width, pitch):
     return drawing
 
 
-def modular_jack(width, keyway, contacts, positions):
+# Modular jack sizes (positions): opening width, and the latch keyway's wide step (width, bottom) and narrow step
+# (width, bottom). The opening spans y -3.42 to +3.42 above the keyway.
+MODULAR_JACKS = {
+    4: (7.85, (5.6, 5.0), (3.2, 7.1)),
+    6: (9.88, (6.6, 5.1), (4.06, 5.95)),
+    8: (11.91, (6.3, 5.1), (4.06, 5.95)),
+}
+
+
+def modular_jack(positions, contacts):
     """
     A modular (RJ) jack, contacts along the top and the latch keyway at the bottom. contacts lists the populated
     positions, numbered from 1 at the left.
     """
-    # The opening spans y -3.42 to +5.95 (including the keyway); centre it vertically
-    dy = -1.265
-    x, kw = width / 2, keyway / 2
+    width, (wide, step), (narrow, bottom) = MODULAR_JACKS[positions]
+    dy = -(bottom - 3.42) / 2  # Centre the opening (including the keyway) vertically
+    x, w, n = width / 2, wide / 2, narrow / 2
     outline = polygon([(px, py + dy) for px, py in (
-        (-x, -3.42), (x, -3.42), (x, 3.42), (kw, 3.42), (kw, 5.1), (2.03, 5.1), (2.03, 5.95), (-2.03, 5.95),
-        (-2.03, 5.1), (-kw, 5.1), (-kw, 3.42), (-x, 3.42),
+        (-x, -3.42), (x, -3.42), (x, 3.42), (w, 3.42), (w, step), (n, step), (n, bottom), (-n, bottom),
+        (-n, step), (-w, step), (-w, 3.42), (-x, 3.42),
     )])
-    drawing = Drawing(width, 9.37).outline(outline)
-    for n in contacts:
-        drawing.fill(rect((n - (positions + 1) / 2) * 1.016, -2.2 + dy, 0.55, 2.4))
+    drawing = Drawing(width, bottom + 3.42).outline(outline)
+    for c in contacts:
+        drawing.fill(rect((c - (positions + 1) / 2) * 1.016, -2.2 + dy, 0.55, 2.4))
+    return drawing
+
+
+def gg45():
+    """An 8P jack with an additional pair of contacts standing in each bottom corner (IEC 60603-7-7)."""
+    drawing = modular_jack(8, range(1, 9))
+    dy = -(5.95 - 3.42) / 2
+    return drawing.fill(*(rect(x, 1.9 + dy, 0.56, 3) for x in (-4.85, -3.83, 3.83, 4.85)))
+
+
+def tera(pairs):
+    """
+    A Siemon TERA outlet: four pair cavities beneath a latch. TERA outlets accept 1-, 2- and 4-pair plugs;
+    contacts are shown only in the cavities a plug with the given number of pairs occupies.
+    """
+    drawing = Drawing(14.5, 23.1).outline(rect(0, 0, 14.5, 23.1, 1)).fill(rect(0, -9, 5.25, 1.4, 0.7))
+    # Pair cavities in plug order: 1 (top left), 2 (bottom left), 3 (top right), 4 (bottom right)
+    cavities = [(-1, -0.365, 5.37, -1.3), (-1, 5.845, 5.85, 6.9), (1, -0.365, 5.37, -1.3), (1, 5.845, 5.85, 6.9)]
+    for i, (side, cy, h, contact_y) in enumerate(cavities):
+        drawing.detail(rect(side * 3.55, cy, 5.28, h, 0.6))
+        if i < pairs:
+            drawing.fill(rect(side * 2.68, contact_y, 1.1, 1.6), rect(side * 4.43, contact_y, 1.1, 1.6))
+    return drawing
+
+
+def punch_110():
+    """A 110 connecting block (side view): nine teeth separating eight wire slots."""
+    points = [(-15.24, 11.75)]
+    for i in range(9):
+        x = -15.24 + i * 3.81
+        points.append((x, -11.75))
+        if i < 8:
+            points.append((x + 1.905, -6.5))
+    points.append((15.24, 11.75))
+    drawing = Drawing(30.48, 23.5).outline(polygon(points))
+    for i in range(8):
+        drawing.fill(rect(-13.335 + i * 3.81, -3.5, 0.8, 6))
     return drawing
 
 
+def bnc():
+    """A BNC jack: a smooth barrel with two bayonet studs."""
+    return Drawing(11, 11).outline(Circle(0, 0, 4.825)).detail(Circle(0, 0, 4.05), Circle(0, 0, 2.3)).fill(
+        Circle(-4.5, 0, 1), Circle(4.5, 0, 1), Circle(0, 0, 1.05),
+    )
+
+
+def f_connector():
+    """An F jack: a threaded barrel on a hex body, with a solid dielectric face and a central pinhole."""
+    hexagon = polygon([polar(0, 0, 6.35, angle) for angle in range(0, 360, 60)])
+    return Drawing(11, 12.7).outline(hexagon).detail(Circle(0, 0, 4.76), Circle(0, 0, 2.6)).fill(Circle(0, 0, 0.65))
+
+
+def n_connector():
+    """An N jack on a square four-hole flange."""
+    holes = (Circle(x, y, 1.6) for x in (-9.15, 9.15) for y in (-9.15, 9.15))
+    return Drawing(25.4, 25.4).outline(rect(0, 0, 25.4, 25.4, 1.5)).detail(
+        *holes, Circle(0, 0, 7.9), Circle(0, 0, 4.3),
+    ).fill(Circle(0, 0, 1.5))
+
+
+def mrj21():
+    """An MRJ21 receptacle: a skewed opening holding two offset rows of contacts."""
+    opening = Path([
+        ('M', -7.35, -5.1), ('L', 7.84, -5.1), ('L', 10.38, -2.57), ('L', 10.38, -1.41), ('L', 7.41, 3.87),
+        ('L', 5.42, 5.09), ('L', -8.31, 5.09), ('L', -9.85, 3.56), ('L', -9.85, 0.395),
+        arc_to(-12.96, -4.34, 5.665, (-9.85, 0.395), (-7.35, -5.1), clockwise=False), ('Z',),
+    ])
+    return Drawing(28.92, 13.25).outline(rect(0, 0, 28.92, 13.25, 0.8)).detail(
+        opening, Circle(-11.44, -3.6, 1.2), Circle(11.44, 3.6, 1.2),
+    ).fill(rect(1.25, -2.42, 15.4, 2.86), rect(-1.25, 2.42, 15.4, 2.86))
+
+
 def mini_din_8():
     """A Mini-DIN 8 receptacle: eight contacts in an insert with a notch at the top and shoulders at the bottom."""
     r = 3.95
@@ -764,6 +843,139 @@ def mini_din_8():
     return drawing
 
 
+#
+# Fiber optic adapters (as seen on a patch panel, key or latch at the top). Polish variants (PC/UPC/APC) are
+# mechanically identical and share a drawing.
+#
+
+def fiber_sc():
+    """SC (IEC 61754-4): a rectangular opening with a keyway at the top."""
+    outline = polygon([
+        (-4.5, -3.25), (-1, -3.25), (-1, -4.15), (1, -4.15), (1, -3.25), (4.5, -3.25), (4.5, 4.15), (-4.5, 4.15),
+    ])
+    return Drawing(9, 8.3).outline(outline).fill(Circle(0, 0.45, 1.25))
+
+
+def fiber_lc():
+    """LC (IEC 61754-20): a square port beneath a stepped latch cavity."""
+    outline = polygon([(x, y + 1.75) for x, y in (
+        (-2.3, 2.3), (-2.3, -2.3), (-1.8, -2.3), (-1.8, -3.1), (-2.4, -3.1), (-2.4, -4.3), (-1.4, -4.3),
+        (-1.4, -5.8), (1.4, -5.8), (1.4, -4.3), (2.4, -4.3), (2.4, -3.1), (1.8, -3.1), (1.8, -2.3), (2.3, -2.3),
+        (2.3, 2.3),
+    )])
+    return Drawing(4.8, 8.1).outline(outline).fill(Circle(0, 1.75, 0.625))
+
+
+def fiber_mu():
+    """MU (IEC 61754-6): a small upright rectangle with a key on one side."""
+    outline = polygon([
+        (-2, -2.8), (2, -2.8), (2, -1.85), (2.45, -1.2), (2.45, 1.2), (2, 1.85), (2, 2.8), (-2, 2.8),
+    ])
+    return Drawing(4.9, 5.6).outline(outline).fill(Circle(0, 0, 0.625))
+
+
+def fiber_fc():
+    """FC (IEC 61754-13): a threaded barrel with a key slot at the top, on a square two-hole flange."""
+    slot = 1.075
+    outer, inner = math.sqrt(4 ** 2 - slot ** 2), math.sqrt(2.3 ** 2 - slot ** 2)
+    barrel = Path([
+        ('M', slot, -outer), arc_to(0, 0, 4, (slot, -outer), (-slot, -outer)),
+        ('L', -slot, -inner), ('L', slot, -inner), ('Z',),
+    ])
+    return Drawing(15, 15).outline(rect(0, 0, 15, 15, 1.5)).detail(
+        barrel, Circle(0, 0, 2.3), Circle(4.74, -4.74, 1.1), Circle(-4.74, 4.74, 1.1),
+    ).fill(Circle(0, 0, 1.25))
+
+
+def fiber_st():
+    """ST (IEC 61754-2): a bayonet barrel with two pins, keyed at the top."""
+    slot = 0.65
+    outer, inner = math.sqrt(3.52 ** 2 - slot ** 2), math.sqrt(2.745 ** 2 - slot ** 2)
+    barrel = Path([
+        ('M', slot, -outer), arc_to(0, 0, 3.52, (slot, -outer), (-slot, -outer)),
+        ('L', -slot, -inner), ('L', slot, -inner), ('Z',),
+    ])
+    return Drawing(8.6, 8.6).outline(barrel).detail(Circle(0, 0, 2.745)).fill(
+        Circle(-3.62, 0, 0.65), Circle(3.62, 0, 0.65), Circle(0, 0, 1.25),
+    )
+
+
+def fiber_e2000():
+    """LSH/E-2000 (IEC 61754-15): an upright opening with a latch window, its ferrule hidden by a shutter."""
+    return Drawing(7.3, 9.9).outline(rect(0, 0, 7.3, 9.9, 0.5)).detail(rect(0, 1, 5.6, 6.6, 0.4)).fill(
+        rect(0, -3.9, 3, 1.2),
+    )
+
+
+def fiber_lx5():
+    """LX.5 (IEC 61754-23): a narrow upright port with its ferrule offset away from the latch."""
+    return Drawing(5.2, 8.5).outline(rect(0, 0, 5.2, 8.5, 0.4)).fill(rect(0, -3.4, 2, 0.9), Circle(0, 1.7, 0.625))
+
+
+def fiber_mpo():
+    """MPO (IEC 61754-7): a wide opening keyed at the top, around an MT ferrule with a row of fibers."""
+    outline = Path([
+        ('M', -5, -4.1), ('L', -1.5, -4.1), ('L', -1.5, -4.9), ('L', 1.5, -4.9), ('L', 1.5, -4.1), ('L', 5, -4.1),
+        arc_to(-1.4, 0, 7.6, (5, -4.1), (5, 4.1)), ('L', -5, 4.1), arc_to(1.4, 0, 7.6, (-5, 4.1), (-5, -4.1)),
+        ('Z',),
+    ])
+    return Drawing(12.4, 9.8).outline(outline).detail(rect(0, 0, 6.4, 2.45, 0.2)).fill(
+        Circle(-2.3, 0, 0.4), Circle(2.3, 0, 0.4), rect(0, 0, 2.9, 0.3),
+    )
+
+
+def fiber_mtrj():
+    """MT-RJ (IEC 61754-18): a small RJ-style opening around a ferrule holding two fibers between guide pins."""
+    outline = polygon([
+        (-3.6, -1.85), (-2, -1.85), (-2, -2.85), (2, -2.85), (2, -1.85), (3.6, -1.85), (3.6, 2.85), (-3.6, 2.85),
+    ])
+    return Drawing(7.2, 5.7).outline(outline).detail(rect(0, 0.5, 4.4, 2.45, 0.2)).fill(
+        Circle(-1.3, 0.5, 0.35), Circle(1.3, 0.5, 0.35), Circle(-0.375, 0.5, 0.2), Circle(0.375, 0.5, 0.2),
+    )
+
+
+def fiber_cs():
+    """CS (Senko): two ferrules side by side beneath a push-pull tab channel."""
+    outline = polygon([
+        (-3.925, -1.8), (-1.2, -1.8), (-1.2, -3.5), (1.2, -3.5), (1.2, -1.8), (3.925, -1.8), (3.925, 3.5),
+        (-3.925, 3.5),
+    ])
+    return Drawing(7.85, 7).outline(outline).fill(Circle(-1.9, 0.85, 0.625), Circle(1.9, 0.85, 0.625))
+
+
+def fiber_sn():
+    """SN (Senko): a narrow upright port with two stacked ferrules and tabs at the top and bottom."""
+    outline = polygon([
+        (-1.925, -4.73), (-0.8, -4.73), (-0.8, -3.93), (0.8, -3.93), (0.8, -4.73), (1.925, -4.73),
+        (1.925, 4.73), (0.8, 4.73), (0.8, 3.93), (-0.8, 3.93), (-0.8, 4.73), (-1.925, 4.73),
+    ])
+    return Drawing(3.85, 9.46).outline(outline).fill(Circle(0, -1.55, 0.625), Circle(0, 1.55, 0.625))
+
+
+def fiber_mdc():
+    """MDC (US Conec): a narrow upright port with two stacked ferrules in a figure-8 boss."""
+    return Drawing(3.9, 9.1).outline(rect(0, 0, 3.9, 9.1, 0.3)).detail(rect(0, 0.4, 2.92, 6, 1.46)).fill(
+        rect(0, -3.8, 1.7, 0.9), Circle(0, -1.15, 0.625), Circle(0, 1.95, 0.625),
+    )
+
+
+def fiber_sma():
+    """SMA 905/906 (IEC 61754-22): a plain threaded barrel around a large bore; both types look alike."""
+    return Drawing(6.35, 6.35).outline(Circle(0, 0, 3.175)).detail(Circle(0, 0, 2.5)).fill(Circle(0, 0, 1.59))
+
+
+def fiber_urm(cells):
+    """URM (Euromicron, DIN SPEC 40032): a row of cells, each holding two stacked ferrules."""
+    width = cells * 6.4
+    drawing = Drawing(width, 13.4).outline(rect(0, 0, width, 13.4, 1))
+    for i in range(cells):
+        x = (i - (cells - 1) / 2) * 6.4
+        drawing.detail(rect(x, 0, 4.8, 10, 0.5)).fill(
+            rect(x, -4.4, 1.6, 0.8), rect(x, 4.4, 1.6, 0.8), Circle(x, -1.4, 0.625), Circle(x, 1.4, 0.625),
+        )
+    return drawing
+
+
 #
 # USB (receptacle openings)
 #
@@ -1069,9 +1281,9 @@ CONSOLE_PORTS = {
     'de-9': lambda: d_sub(9, 16.26, 2.74),
     'db-25': lambda: d_sub(25, 38.3, 2.77),
     # RJ-11 is defined with only its two centre positions populated (47 CFR 68.502)
-    'rj-11': lambda: modular_jack(9.88, 6.6, (3, 4), 6),
-    'rj-12': lambda: modular_jack(9.88, 6.6, range(1, 7), 6),
-    'rj-45': lambda: modular_jack(11.91, 6.3, range(1, 9), 8),
+    'rj-11': lambda: modular_jack(6, (3, 4)),
+    'rj-12': lambda: modular_jack(6, range(1, 7)),
+    'rj-45': lambda: modular_jack(8, range(1, 9)),
     'mini-din-8': mini_din_8,
     'usb-a': usb_a,
     'usb-b': usb_b,
@@ -1084,6 +1296,54 @@ CONSOLE_PORTS = {
 }
 
 
+# Front/rear port type: drawing function. Ports are drawn as the jack, adapter, or bulkhead receptacle.
+PORTS = {
+    '8p8c': lambda: modular_jack(8, range(1, 9)),
+    '8p6c': lambda: modular_jack(8, range(2, 8)),
+    '8p4c': lambda: modular_jack(8, range(3, 7)),
+    '8p2c': lambda: modular_jack(8, (4, 5)),
+    '6p6c': lambda: modular_jack(6, range(1, 7)),
+    '6p4c': lambda: modular_jack(6, range(2, 6)),
+    '6p2c': lambda: modular_jack(6, (3, 4)),
+    '4p4c': lambda: modular_jack(4, range(1, 5)),
+    '4p2c': lambda: modular_jack(4, (2, 3)),
+    'gg45': gg45,
+    'tera-4p': lambda: tera(4),
+    'tera-2p': lambda: tera(2),
+    'tera-1p': lambda: tera(1),
+    '110-punch': punch_110,
+    'bnc': bnc,
+    'f': f_connector,
+    'n': n_connector,
+    'mrj21': mrj21,
+    **{f'fc{polish}': fiber_fc for polish in ('', '-pc', '-upc', '-apc')},
+    **{f'lc{polish}': fiber_lc for polish in ('', '-pc', '-upc', '-apc')},
+    **{f'mu{polish}': fiber_mu for polish in ('', '-pc', '-upc', '-apc')},
+    **{f'lsh{polish}': fiber_e2000 for polish in ('', '-pc', '-upc', '-apc')},
+    **{f'lx5{polish}': fiber_lx5 for polish in ('', '-pc', '-upc', '-apc')},
+    **{f'sc{polish}': fiber_sc for polish in ('', '-pc', '-upc', '-apc')},
+    'mpo': fiber_mpo,
+    'mtrj': fiber_mtrj,
+    'st': fiber_st,
+    'cs': fiber_cs,
+    'sn': fiber_sn,
+    'mdc': fiber_mdc,
+    'sma-905': fiber_sma,
+    'sma-906': fiber_sma,
+    'urm-p2': lambda: fiber_urm(1),
+    'urm-p4': lambda: fiber_urm(2),
+    'urm-p8': lambda: fiber_urm(4),
+    'usb-a': usb_a,
+    'usb-b': usb_b,
+    'usb-c': usb_c,
+    'usb-mini-a': usb_mini_a,
+    'usb-mini-b': usb_mini_b,
+    'usb-micro-a': usb_micro_a,
+    'usb-micro-b': usb_micro_b,
+    'usb-micro-ab': usb_micro_a,
+}
+
+
 def write(directory, drawings):
     directory.mkdir(parents=True, exist_ok=True)
     expected = set()
@@ -1107,6 +1367,7 @@ def main():
     write(OUTPUT_DIR / 'power-outlet', POWER_OUTLETS)
     write(OUTPUT_DIR / 'power-port', POWER_PORTS)
     write(OUTPUT_DIR / 'console-port', CONSOLE_PORTS)
+    write(OUTPUT_DIR / 'port', PORTS)
 
 
 if __name__ == '__main__':