Przeglądaj źródła

Merge pull request #2990 from digitalocean/develop

Release v2.5.8
Jeremy Stretch 7 lat temu
rodzic
commit
d112b6027a
100 zmienionych plików z 316 dodań i 231 usunięć
  1. 32 0
      CHANGELOG.md
  2. 1 1
      netbox/circuits/forms.py
  3. 1 1
      netbox/circuits/tables.py
  4. 1 1
      netbox/dcim/api/serializers.py
  5. 3 3
      netbox/dcim/api/views.py
  6. 17 34
      netbox/dcim/filters.py
  7. 1 1
      netbox/dcim/managers.py
  8. 8 9
      netbox/dcim/models.py
  9. 8 8
      netbox/dcim/tables.py
  10. 3 1
      netbox/dcim/views.py
  11. 5 1
      netbox/extras/middleware.py
  12. 18 0
      netbox/extras/migrations/0017_exporttemplate_mime_type_length.py
  13. 1 1
      netbox/extras/models.py
  14. 1 1
      netbox/extras/tables.py
  15. 6 6
      netbox/ipam/forms.py
  16. 24 2
      netbox/ipam/models.py
  17. 4 4
      netbox/ipam/tables.py
  18. 4 3
      netbox/netbox/settings.py
  19. 1 0
      netbox/netbox/views.py
  20. 14 1
      netbox/project-static/css/base.css
  21. 4 0
      netbox/project-static/js/forms.js
  22. 1 1
      netbox/secrets/tables.py
  23. 1 1
      netbox/templates/_base.html
  24. 2 2
      netbox/templates/circuits/circuit.html
  25. 2 2
      netbox/templates/circuits/circuit_list.html
  26. 1 1
      netbox/templates/circuits/circuittype_list.html
  27. 3 3
      netbox/templates/circuits/provider.html
  28. 2 2
      netbox/templates/circuits/provider_list.html
  29. 2 2
      netbox/templates/dcim/cable.html
  30. 2 2
      netbox/templates/dcim/cable_list.html
  31. 2 2
      netbox/templates/dcim/console_connections_list.html
  32. 15 15
      netbox/templates/dcim/device.html
  33. 1 1
      netbox/templates/dcim/device_inventory.html
  34. 2 2
      netbox/templates/dcim/device_list.html
  35. 1 1
      netbox/templates/dcim/devicerole_list.html
  36. 2 2
      netbox/templates/dcim/devicetype.html
  37. 2 2
      netbox/templates/dcim/devicetype_list.html
  38. 1 1
      netbox/templates/dcim/inc/consoleport.html
  39. 1 1
      netbox/templates/dcim/inc/consoleserverport.html
  40. 1 1
      netbox/templates/dcim/inc/devicebay.html
  41. 1 1
      netbox/templates/dcim/inc/devicetype_component_table.html
  42. 1 1
      netbox/templates/dcim/inc/frontport.html
  43. 2 2
      netbox/templates/dcim/inc/interface.html
  44. 1 1
      netbox/templates/dcim/inc/inventoryitem.html
  45. 1 1
      netbox/templates/dcim/inc/poweroutlet.html
  46. 1 1
      netbox/templates/dcim/inc/powerport.html
  47. 1 1
      netbox/templates/dcim/inc/rearport.html
  48. 2 2
      netbox/templates/dcim/interface.html
  49. 2 2
      netbox/templates/dcim/interface_connections_list.html
  50. 2 2
      netbox/templates/dcim/inventoryitem_list.html
  51. 1 1
      netbox/templates/dcim/manufacturer_list.html
  52. 1 1
      netbox/templates/dcim/platform_list.html
  53. 2 2
      netbox/templates/dcim/power_connections_list.html
  54. 6 6
      netbox/templates/dcim/rack.html
  55. 2 2
      netbox/templates/dcim/rack_elevation_list.html
  56. 2 2
      netbox/templates/dcim/rack_list.html
  57. 2 2
      netbox/templates/dcim/rackgroup_list.html
  58. 1 1
      netbox/templates/dcim/rackreservation_list.html
  59. 1 1
      netbox/templates/dcim/rackrole_list.html
  60. 2 2
      netbox/templates/dcim/region_list.html
  61. 6 6
      netbox/templates/dcim/site.html
  62. 2 2
      netbox/templates/dcim/site_list.html
  63. 2 2
      netbox/templates/dcim/virtualchassis_list.html
  64. 2 2
      netbox/templates/extras/configcontext.html
  65. 2 2
      netbox/templates/extras/configcontext_list.html
  66. 2 2
      netbox/templates/extras/objectchange.html
  67. 2 2
      netbox/templates/extras/objectchange_list.html
  68. 2 2
      netbox/templates/extras/report.html
  69. 6 0
      netbox/templates/extras/tag.html
  70. 1 1
      netbox/templates/inc/image_attachments.html
  71. 1 1
      netbox/templates/inc/search_panel.html
  72. 2 2
      netbox/templates/ipam/aggregate.html
  73. 2 2
      netbox/templates/ipam/aggregate_list.html
  74. 1 1
      netbox/templates/ipam/inc/service.html
  75. 3 3
      netbox/templates/ipam/ipaddress.html
  76. 2 2
      netbox/templates/ipam/ipaddress_list.html
  77. 2 2
      netbox/templates/ipam/prefix.html
  78. 2 2
      netbox/templates/ipam/prefix_list.html
  79. 2 2
      netbox/templates/ipam/rir_list.html
  80. 1 1
      netbox/templates/ipam/role_list.html
  81. 1 1
      netbox/templates/ipam/service.html
  82. 2 2
      netbox/templates/ipam/service_list.html
  83. 3 3
      netbox/templates/ipam/vlan.html
  84. 2 2
      netbox/templates/ipam/vlan_list.html
  85. 2 2
      netbox/templates/ipam/vlangroup_list.html
  86. 1 1
      netbox/templates/ipam/vlangroup_vlans.html
  87. 2 2
      netbox/templates/ipam/vrf.html
  88. 2 2
      netbox/templates/ipam/vrf_list.html
  89. 1 1
      netbox/templates/secrets/inc/private_key_modal.html
  90. 1 1
      netbox/templates/secrets/inc/secret_tr.html
  91. 3 3
      netbox/templates/secrets/secret.html
  92. 2 2
      netbox/templates/secrets/secret_list.html
  93. 1 1
      netbox/templates/secrets/secretrole_list.html
  94. 2 2
      netbox/templates/tenancy/tenant.html
  95. 2 2
      netbox/templates/tenancy/tenant_list.html
  96. 1 1
      netbox/templates/tenancy/tenantgroup_list.html
  97. 1 1
      netbox/templates/users/api_tokens.html
  98. 2 2
      netbox/templates/users/userkey.html
  99. 2 2
      netbox/templates/utilities/obj_table.html
  100. 3 3
      netbox/templates/virtualization/cluster.html

+ 32 - 0
CHANGELOG.md

@@ -1,3 +1,33 @@
+v2.5.8 (2019-03-11)
+
+## Enhancements
+
+* [#2435](https://github.com/digitalocean/netbox/issues/2435) - Printer friendly CSS
+
+## Bug Fixes
+
+* [#2065](https://github.com/digitalocean/netbox/issues/2065) - Correct documentation for VM interface serializer
+* [#2705](https://github.com/digitalocean/netbox/issues/2705) - Fix endpoint grouping in API docs
+* [#2781](https://github.com/digitalocean/netbox/issues/2781) - Fix filtering of sites/devices/VMs by multiple regions
+* [#2923](https://github.com/digitalocean/netbox/issues/2923) - Provider filter form's site field should be blank by default
+* [#2938](https://github.com/digitalocean/netbox/issues/2938) - Enforce deterministic ordering of device components returned by API
+* [#2939](https://github.com/digitalocean/netbox/issues/2939) - Exclude circuit terminations from API interface connections endpoint
+* [#2940](https://github.com/digitalocean/netbox/issues/2940) - Allow CSV import of prefixes/IPs to VRF without an RD assigned
+* [#2944](https://github.com/digitalocean/netbox/issues/2944) - Record the deletion of an IP address in the changelog of its parent interface (if any)
+* [#2952](https://github.com/digitalocean/netbox/issues/2952) - Added the `slug` field to the Tenant filter for use in the API and search function
+* [#2954](https://github.com/digitalocean/netbox/issues/2954) - Remove trailing slashes to fix root/template paths on Windows
+* [#2961](https://github.com/digitalocean/netbox/issues/2961) - Prevent exception when exporting inventory items belonging to unnamed devices
+* [#2962](https://github.com/digitalocean/netbox/issues/2962) - Increase ExportTemplate `mime_type` field length
+* [#2966](https://github.com/digitalocean/netbox/issues/2966) - Accept `null` cable length_unit via API
+* [#2972](https://github.com/digitalocean/netbox/issues/2972) - Improve ContentTypeField serializer to elegantly handle invalid data
+* [#2976](https://github.com/digitalocean/netbox/issues/2976) - Add delete button to tag view
+* [#2980](https://github.com/digitalocean/netbox/issues/2980) - Improve rendering time for API docs
+* [#2982](https://github.com/digitalocean/netbox/issues/2982) - Correct CSS class assignment on color picker
+* [#2984](https://github.com/digitalocean/netbox/issues/2984) - Fix logging of unlabeled cable ID on cable deletion
+* [#2985](https://github.com/digitalocean/netbox/issues/2985) - Fix pagination page length for rack elevations
+
+---
+
 v2.5.7 (2019-02-21)
 
 ## Enhancements
@@ -24,6 +54,8 @@ v2.5.7 (2019-02-21)
 * [#2914](https://github.com/digitalocean/netbox/issues/2914) - Fix empty connected circuit link on device interfaces list
 * [#2915](https://github.com/digitalocean/netbox/issues/2915) - Fix bulk editing of pass-through ports
 
+---
+
 v2.5.6 (2019-02-13)
 
 ## Enhancements

+ 1 - 1
netbox/circuits/forms.py

@@ -107,7 +107,7 @@ class ProviderFilterForm(BootstrapMixin, CustomFieldFilterForm):
     site = FilterChoiceField(
         queryset=Site.objects.all(),
         to_field_name='slug',
-        widget=APISelect(
+        widget=APISelectMultiple(
             api_url="/api/dcim/sites/",
             value_field="slug",
         )

+ 1 - 1
netbox/circuits/tables.py

@@ -59,7 +59,7 @@ class CircuitTypeTable(BaseTable):
     name = tables.LinkColumn()
     circuit_count = tables.Column(verbose_name='Circuits')
     actions = tables.TemplateColumn(
-        template_code=CIRCUITTYPE_ACTIONS, attrs={'td': {'class': 'text-right'}}, verbose_name=''
+        template_code=CIRCUITTYPE_ACTIONS, attrs={'td': {'class': 'text-right noprint'}}, verbose_name=''
     )
 
     class Meta(BaseTable.Meta):

+ 1 - 1
netbox/dcim/api/serializers.py

@@ -507,7 +507,7 @@ class CableSerializer(ValidatedModelSerializer):
     termination_a = serializers.SerializerMethodField(read_only=True)
     termination_b = serializers.SerializerMethodField(read_only=True)
     status = ChoiceField(choices=CONNECTION_STATUS_CHOICES, required=False)
-    length_unit = ChoiceField(choices=CABLE_LENGTH_UNIT_CHOICES, required=False)
+    length_unit = ChoiceField(choices=CABLE_LENGTH_UNIT_CHOICES, required=False, allow_null=True)
 
     class Meta:
         model = Cable

+ 3 - 3
netbox/dcim/api/views.py

@@ -496,11 +496,11 @@ class PowerConnectionViewSet(ListModelMixin, GenericViewSet):
 
 class InterfaceConnectionViewSet(ListModelMixin, GenericViewSet):
     queryset = Interface.objects.select_related(
-        'device', '_connected_interface', '_connected_circuittermination'
+        'device', '_connected_interface__device'
     ).filter(
         # Avoid duplicate connections by only selecting the lower PK in a connected pair
-        Q(_connected_interface__isnull=False, pk__lt=F('_connected_interface')) |
-        Q(_connected_circuittermination__isnull=False)
+        _connected_interface__isnull=False,
+        pk__lt=F('_connected_interface')
     )
     serializer_class = serializers.InterfaceConnectionSerializer
     filterset_class = filters.InterfaceConnectionFilter

+ 17 - 34
netbox/dcim/filters.py

@@ -1,6 +1,5 @@
 import django_filters
 from django.contrib.auth.models import User
-from django.core.exceptions import ObjectDoesNotExist
 from django.db.models import Q
 from netaddr import EUI
 from netaddr.core import AddrFormatError
@@ -8,7 +7,9 @@ from netaddr.core import AddrFormatError
 from extras.filters import CustomFieldFilterSet
 from tenancy.models import Tenant
 from utilities.constants import COLOR_CHOICES
-from utilities.filters import NameSlugSearchFilterSet, NullableCharFieldFilter, NumericInFilter, TagFilter
+from utilities.filters import (
+    NameSlugSearchFilterSet, NullableCharFieldFilter, NumericInFilter, TagFilter, TreeNodeMultipleChoiceFilter
+)
 from virtualization.models import Cluster
 from .constants import *
 from .models import (
@@ -49,14 +50,15 @@ class SiteFilter(CustomFieldFilterSet, django_filters.FilterSet):
         choices=SITE_STATUS_CHOICES,
         null_value=None
     )
-    region_id = django_filters.NumberFilter(
-        method='filter_region',
-        field_name='pk',
+    region_id = TreeNodeMultipleChoiceFilter(
+        queryset=Region.objects.all(),
+        field_name='region__in',
         label='Region (ID)',
     )
-    region = django_filters.CharFilter(
-        method='filter_region',
-        field_name='slug',
+    region = TreeNodeMultipleChoiceFilter(
+        queryset=Region.objects.all(),
+        field_name='region__in',
+        to_field_name='slug',
         label='Region (slug)',
     )
     tenant_id = django_filters.ModelMultipleChoiceFilter(
@@ -95,16 +97,6 @@ class SiteFilter(CustomFieldFilterSet, django_filters.FilterSet):
             pass
         return queryset.filter(qs_filter)
 
-    def filter_region(self, queryset, name, value):
-        try:
-            region = Region.objects.get(**{name: value})
-        except ObjectDoesNotExist:
-            return queryset.none()
-        return queryset.filter(
-            Q(region=region) |
-            Q(region__in=region.get_descendants())
-        )
-
 
 class RackGroupFilter(NameSlugSearchFilterSet):
     site_id = django_filters.ModelMultipleChoiceFilter(
@@ -513,14 +505,15 @@ class DeviceFilter(CustomFieldFilterSet):
     )
     name = NullableCharFieldFilter()
     asset_tag = NullableCharFieldFilter()
-    region_id = django_filters.NumberFilter(
-        method='filter_region',
-        field_name='pk',
+    region_id = TreeNodeMultipleChoiceFilter(
+        queryset=Region.objects.all(),
+        field_name='site__region__in',
         label='Region (ID)',
     )
-    region = django_filters.CharFilter(
-        method='filter_region',
-        field_name='slug',
+    region = TreeNodeMultipleChoiceFilter(
+        queryset=Region.objects.all(),
+        field_name='site__region__in',
+        to_field_name='slug',
         label='Region (slug)',
     )
     site_id = django_filters.ModelMultipleChoiceFilter(
@@ -619,16 +612,6 @@ class DeviceFilter(CustomFieldFilterSet):
             Q(comments__icontains=value)
         ).distinct()
 
-    def filter_region(self, queryset, name, value):
-        try:
-            region = Region.objects.get(**{name: value})
-        except ObjectDoesNotExist:
-            return queryset.none()
-        return queryset.filter(
-            Q(site__region=region) |
-            Q(site__region__in=region.get_descendants())
-        )
-
     def _mac_address(self, queryset, name, value):
         value = value.strip()
         if not value:

+ 1 - 1
netbox/dcim/managers.py

@@ -27,7 +27,7 @@ class DeviceComponentManager(Manager):
             select={
                 'name_padded': sql.format(table_name, table_name),
             }
-        ).order_by('name_padded')
+        ).order_by('name_padded', 'pk')
 
 
 class InterfaceQuerySet(QuerySet):

+ 8 - 9
netbox/dcim/models.py

@@ -2423,7 +2423,7 @@ class InventoryItem(ComponentModel):
 
     def to_csv(self):
         return (
-            self.device.name or '{' + self.device.pk + '}',
+            self.device.name or '{{{}}}'.format(self.device.pk),
             self.name,
             self.manufacturer.name if self.manufacturer else None,
             self.part_id,
@@ -2557,16 +2557,15 @@ class Cable(ChangeLoggedModel):
             ('termination_b_type', 'termination_b_id'),
         )
 
-    def __init__(self, *args, **kwargs):
-
-        super().__init__(*args, **kwargs)
+    def __str__(self):
+        if self.label:
+            return self.label
 
-        # Create an ID string for use by __str__(). We have to save a copy of pk since it's nullified after .delete()
-        # is called.
-        self.id_string = '#{}'.format(self.pk)
+        # Save a copy of the PK on the instance since it's nullified if .delete() is called
+        if not hasattr(self, 'id_string'):
+            self.id_string = '#{}'.format(self.pk)
 
-    def __str__(self):
-        return self.label or self.id_string
+        return self.id_string
 
     def get_absolute_url(self):
         return reverse('dcim:cable', args=[self.pk])

+ 8 - 8
netbox/dcim/tables.py

@@ -196,7 +196,7 @@ class RegionTable(BaseTable):
     slug = tables.Column(verbose_name='Slug')
     actions = tables.TemplateColumn(
         template_code=REGION_ACTIONS,
-        attrs={'td': {'class': 'text-right'}},
+        attrs={'td': {'class': 'text-right noprint'}},
         verbose_name=''
     )
 
@@ -239,7 +239,7 @@ class RackGroupTable(BaseTable):
     slug = tables.Column()
     actions = tables.TemplateColumn(
         template_code=RACKGROUP_ACTIONS,
-        attrs={'td': {'class': 'text-right'}},
+        attrs={'td': {'class': 'text-right noprint'}},
         verbose_name=''
     )
 
@@ -258,7 +258,7 @@ class RackRoleTable(BaseTable):
     rack_count = tables.Column(verbose_name='Racks')
     color = tables.TemplateColumn(COLOR_LABEL, verbose_name='Color')
     slug = tables.Column(verbose_name='Slug')
-    actions = tables.TemplateColumn(template_code=RACKROLE_ACTIONS, attrs={'td': {'class': 'text-right'}},
+    actions = tables.TemplateColumn(template_code=RACKROLE_ACTIONS, attrs={'td': {'class': 'text-right noprint'}},
                                     verbose_name='')
 
     class Meta(BaseTable.Meta):
@@ -309,7 +309,7 @@ class RackReservationTable(BaseTable):
     rack = tables.LinkColumn('dcim:rack', args=[Accessor('rack.pk')])
     unit_list = tables.Column(orderable=False, verbose_name='Units')
     actions = tables.TemplateColumn(
-        template_code=RACKRESERVATION_ACTIONS, attrs={'td': {'class': 'text-right'}}, verbose_name=''
+        template_code=RACKRESERVATION_ACTIONS, attrs={'td': {'class': 'text-right noprint'}}, verbose_name=''
     )
 
     class Meta(BaseTable.Meta):
@@ -327,7 +327,7 @@ class ManufacturerTable(BaseTable):
     devicetype_count = tables.Column(verbose_name='Device Types')
     platform_count = tables.Column(verbose_name='Platforms')
     slug = tables.Column(verbose_name='Slug')
-    actions = tables.TemplateColumn(template_code=MANUFACTURER_ACTIONS, attrs={'td': {'class': 'text-right'}},
+    actions = tables.TemplateColumn(template_code=MANUFACTURER_ACTIONS, attrs={'td': {'class': 'text-right noprint'}},
                                     verbose_name='')
 
     class Meta(BaseTable.Meta):
@@ -463,7 +463,7 @@ class DeviceRoleTable(BaseTable):
     slug = tables.Column(verbose_name='Slug')
     actions = tables.TemplateColumn(
         template_code=DEVICEROLE_ACTIONS,
-        attrs={'td': {'class': 'text-right'}},
+        attrs={'td': {'class': 'text-right noprint'}},
         verbose_name=''
     )
 
@@ -492,7 +492,7 @@ class PlatformTable(BaseTable):
     )
     actions = tables.TemplateColumn(
         template_code=PLATFORM_ACTIONS,
-        attrs={'td': {'class': 'text-right'}},
+        attrs={'td': {'class': 'text-right noprint'}},
         verbose_name=''
     )
 
@@ -779,7 +779,7 @@ class VirtualChassisTable(BaseTable):
     member_count = tables.Column(verbose_name='Members')
     actions = tables.TemplateColumn(
         template_code=VIRTUALCHASSIS_ACTIONS,
-        attrs={'td': {'class': 'text-right'}},
+        attrs={'td': {'class': 'text-right noprint'}},
         verbose_name=''
     )
 

+ 3 - 1
netbox/dcim/views.py

@@ -1,5 +1,6 @@
 import re
 
+from django.conf import settings
 from django.contrib import messages
 from django.contrib.auth.mixins import PermissionRequiredMixin
 from django.core.paginator import EmptyPage, PageNotAnInteger
@@ -353,8 +354,9 @@ class RackElevationListView(View):
         total_count = racks.count()
 
         # Pagination
-        paginator = EnhancedPaginator(racks, 25)
+        per_page = request.GET.get('per_page', settings.PAGINATE_COUNT)
         page_number = request.GET.get('page', 1)
+        paginator = EnhancedPaginator(racks, per_page)
         try:
             page = paginator.page(page_number)
         except PageNotAnInteger:

+ 5 - 1
netbox/extras/middleware.py

@@ -29,7 +29,11 @@ def cache_changed_object(instance, **kwargs):
 
 def _record_object_deleted(request, instance, **kwargs):
 
-    # Record that the object was deleted.
+    # Force resolution of request.user in case it's still a SimpleLazyObject. This seems to happen
+    # occasionally during tests, but haven't been able to determine why.
+    assert request.user.is_authenticated
+
+    # Record that the object was deleted
     if hasattr(instance, 'log_change'):
         instance.log_change(request.user, request.id, OBJECTCHANGE_ACTION_DELETE)
 

+ 18 - 0
netbox/extras/migrations/0017_exporttemplate_mime_type_length.py

@@ -0,0 +1,18 @@
+# Generated by Django 2.1.7 on 2019-03-05 18:07
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('extras', '0016_exporttemplate_add_cable'),
+    ]
+
+    operations = [
+        migrations.AlterField(
+            model_name='exporttemplate',
+            name='mime_type',
+            field=models.CharField(blank=True, max_length=50),
+        ),
+    ]

+ 1 - 1
netbox/extras/models.py

@@ -357,7 +357,7 @@ class ExportTemplate(models.Model):
     )
     template_code = models.TextField()
     mime_type = models.CharField(
-        max_length=15,
+        max_length=50,
         blank=True
     )
     file_extension = models.CharField(

+ 1 - 1
netbox/extras/tables.py

@@ -68,7 +68,7 @@ class TagTable(BaseTable):
     )
     actions = tables.TemplateColumn(
         template_code=TAG_ACTIONS,
-        attrs={'td': {'class': 'text-right'}},
+        attrs={'td': {'class': 'text-right noprint'}},
         verbose_name=''
     )
 

+ 6 - 6
netbox/ipam/forms.py

@@ -349,11 +349,11 @@ class PrefixForm(BootstrapMixin, TenancyForm, CustomFieldForm):
 
 
 class PrefixCSVForm(forms.ModelForm):
-    vrf = forms.ModelChoiceField(
+    vrf = FlexibleModelChoiceField(
         queryset=VRF.objects.all(),
-        required=False,
         to_field_name='rd',
-        help_text='Route distinguisher of parent VRF',
+        required=False,
+        help_text='Route distinguisher of parent VRF (or {ID})',
         error_messages={
             'invalid_choice': 'VRF not found.',
         }
@@ -764,11 +764,11 @@ class IPAddressBulkAddForm(BootstrapMixin, TenancyForm, CustomFieldForm):
 
 
 class IPAddressCSVForm(forms.ModelForm):
-    vrf = forms.ModelChoiceField(
+    vrf = FlexibleModelChoiceField(
         queryset=VRF.objects.all(),
-        required=False,
         to_field_name='rd',
-        help_text='Route distinguisher of the assigned VRF',
+        required=False,
+        help_text='Route distinguisher of parent VRF (or {ID})',
         error_messages={
             'invalid_choice': 'VRF not found.',
         }

+ 24 - 2
netbox/ipam/models.py

@@ -1,7 +1,7 @@
 import netaddr
 from django.conf import settings
 from django.contrib.contenttypes.fields import GenericRelation
-from django.core.exceptions import ValidationError
+from django.core.exceptions import ValidationError, ObjectDoesNotExist
 from django.core.validators import MaxValueValidator, MinValueValidator
 from django.db import models
 from django.db.models import Q
@@ -10,8 +10,9 @@ from django.urls import reverse
 from taggit.managers import TaggableManager
 
 from dcim.models import Interface
-from extras.models import CustomFieldModel
+from extras.models import CustomFieldModel, ObjectChange
 from utilities.models import ChangeLoggedModel
+from utilities.utils import serialize_object
 from .constants import *
 from .fields import IPNetworkField, IPAddressField
 from .querysets import PrefixQuerySet
@@ -629,6 +630,27 @@ class IPAddress(ChangeLoggedModel, CustomFieldModel):
             self.family = self.address.version
         super().save(*args, **kwargs)
 
+    def log_change(self, user, request_id, action):
+        """
+        Include the connected Interface (if any).
+        """
+
+        # It's possible that an IPAddress can be deleted _after_ its parent Interface, in which case trying to resolve
+        # the interface will raise DoesNotExist.
+        try:
+            parent_obj = self.interface
+        except ObjectDoesNotExist:
+            parent_obj = None
+
+        ObjectChange(
+            user=user,
+            request_id=request_id,
+            changed_object=self,
+            related_object=parent_obj,
+            action=action,
+            object_data=serialize_object(self)
+        ).save()
+
     def to_csv(self):
 
         # Determine if this IP is primary for a Device

+ 4 - 4
netbox/ipam/tables.py

@@ -203,7 +203,7 @@ class RIRTable(BaseTable):
     name = tables.LinkColumn(verbose_name='Name')
     is_private = BooleanColumn(verbose_name='Private')
     aggregate_count = tables.Column(verbose_name='Aggregates')
-    actions = tables.TemplateColumn(template_code=RIR_ACTIONS, attrs={'td': {'class': 'text-right'}}, verbose_name='')
+    actions = tables.TemplateColumn(template_code=RIR_ACTIONS, attrs={'td': {'class': 'text-right noprint'}}, verbose_name='')
 
     class Meta(BaseTable.Meta):
         model = RIR
@@ -288,7 +288,7 @@ class RoleTable(BaseTable):
         orderable=False,
         verbose_name='VLANs'
     )
-    actions = tables.TemplateColumn(template_code=ROLE_ACTIONS, attrs={'td': {'class': 'text-right'}}, verbose_name='')
+    actions = tables.TemplateColumn(template_code=ROLE_ACTIONS, attrs={'td': {'class': 'text-right noprint'}}, verbose_name='')
 
     class Meta(BaseTable.Meta):
         model = Role
@@ -392,7 +392,7 @@ class VLANGroupTable(BaseTable):
     site = tables.LinkColumn('dcim:site', args=[Accessor('site.slug')], verbose_name='Site')
     vlan_count = tables.Column(verbose_name='VLANs')
     slug = tables.Column(verbose_name='Slug')
-    actions = tables.TemplateColumn(template_code=VLANGROUP_ACTIONS, attrs={'td': {'class': 'text-right'}},
+    actions = tables.TemplateColumn(template_code=VLANGROUP_ACTIONS, attrs={'td': {'class': 'text-right noprint'}},
                                     verbose_name='')
 
     class Meta(BaseTable.Meta):
@@ -437,7 +437,7 @@ class VLANMemberTable(BaseTable):
     )
     actions = tables.TemplateColumn(
         template_code=VLAN_MEMBER_ACTIONS,
-        attrs={'td': {'class': 'text-right'}},
+        attrs={'td': {'class': 'text-right noprint'}},
         verbose_name=''
     )
 

+ 4 - 3
netbox/netbox/settings.py

@@ -22,7 +22,7 @@ except ImportError:
     )
 
 
-VERSION = '2.5.7'
+VERSION = '2.5.8'
 
 BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
 
@@ -197,7 +197,7 @@ ROOT_URLCONF = 'netbox.urls'
 TEMPLATES = [
     {
         'BACKEND': 'django.template.backends.django.DjangoTemplates',
-        'DIRS': [BASE_DIR + '/templates/'],
+        'DIRS': [BASE_DIR + '/templates'],
         'APP_DIRS': True,
         'OPTIONS': {
             'context_processors': [
@@ -223,7 +223,7 @@ USE_I18N = True
 USE_TZ = True
 
 # Static files (CSS, JavaScript, Images)
-STATIC_ROOT = BASE_DIR + '/static/'
+STATIC_ROOT = BASE_DIR + '/static'
 STATIC_URL = '/{}static/'.format(BASE_PATH)
 STATICFILES_DIRS = (
     os.path.join(BASE_DIR, "project-static"),
@@ -315,6 +315,7 @@ SWAGGER_SETTINGS = {
         'utilities.custom_inspectors.IdInFilterInspector',
         'drf_yasg.inspectors.CoreAPICompatInspector',
     ],
+    'DEFAULT_MODEL_DEPTH': 1,
     'DEFAULT_PAGINATOR_INSPECTORS': [
         'utilities.custom_inspectors.NullablePaginatorInspector',
         'drf_yasg.inspectors.DjangoRestResponsePagination',

+ 1 - 0
netbox/netbox/views.py

@@ -267,6 +267,7 @@ class SearchView(View):
 class APIRootView(APIView):
     _ignore_model_permissions = True
     exclude_from_schema = True
+    swagger_schema = None
 
     def get_view_name(self):
         return "API Root"

+ 14 - 1
netbox/project-static/css/base.css

@@ -49,6 +49,19 @@ footer p {
     }
 }
 
+/* Printer friendly CSS class and various fixes for printing. */
+@media print {
+    body {
+        padding-top: 0px;
+    }
+    a[href]:after {
+        content: none !important;
+    }
+    .noprint {
+        display: none !important;
+    }
+}
+
 /* Collapse the nav menu on displays less than 960px wide */
 @media (max-width: 959px) {
     .navbar-header {
@@ -575,4 +588,4 @@ td .progress {
 }
 textarea {
     font-family: Consolas, Lucida Console, monospace;
-}
+}

+ 4 - 0
netbox/project-static/js/forms.js

@@ -90,6 +90,10 @@ $(document).ready(function() {
     // Assign color picker selection classes
     function colorPickerClassCopy(data, container) {
         if (data.element) {
+            // Remove any existing color-selection classes
+            $(container).attr('class', function(i, c) {
+                return c.replace(/(^|\s)color-selection-\S+/g, '');
+            });
             $(container).addClass($(data.element).attr("class"));
         }
         return data.text;

+ 1 - 1
netbox/secrets/tables.py

@@ -23,7 +23,7 @@ class SecretRoleTable(BaseTable):
     secret_count = tables.Column(verbose_name='Secrets')
     slug = tables.Column(verbose_name='Slug')
     actions = tables.TemplateColumn(
-        template_code=SECRETROLE_ACTIONS, attrs={'td': {'class': 'text-right'}}, verbose_name=''
+        template_code=SECRETROLE_ACTIONS, attrs={'td': {'class': 'text-right noprint'}}, verbose_name=''
     )
 
     class Meta(BaseTable.Meta):

+ 1 - 1
netbox/templates/_base.html

@@ -54,7 +54,7 @@
                 <div class="col-xs-4 text-center">
                     <p class="text-muted">{% now 'Y-m-d H:i:s T' %}</p>
                 </div>
-                <div class="col-xs-4 text-right">
+                <div class="col-xs-4 text-right noprint">
                     <p class="text-muted">
                         <i class="fa fa-fw fa-book text-primary"></i> <a href="http://netbox.readthedocs.io/">Docs</a> &middot;
                         <i class="fa fa-fw fa-cloud text-primary"></i> <a href="{% url 'api_docs' %}">API</a> &middot;

+ 2 - 2
netbox/templates/circuits/circuit.html

@@ -4,7 +4,7 @@
 {% block title %}{{ circuit }}{% endblock %}
 
 {% block header %}
-    <div class="row">
+    <div class="row noprint">
         <div class="col-sm-8 col-md-9">
             <ol class="breadcrumb">
                 <li><a href="{% url 'circuits:circuit_list' %}">Circuits</a></li>
@@ -25,7 +25,7 @@
             </form>
         </div>
     </div>
-    <div class="pull-right">
+    <div class="pull-right noprint">
         {% if perms.circuits.change_circuit %}
             <a href="{% url 'circuits:circuit_edit' pk=circuit.pk %}" class="btn btn-warning">
                 <span class="fa fa-pencil" aria-hidden="true"></span>

+ 2 - 2
netbox/templates/circuits/circuit_list.html

@@ -2,7 +2,7 @@
 {% load buttons %}
 
 {% block content %}
-<div class="pull-right">
+<div class="pull-right noprint">
     {% if perms.circuits.add_circuit %}
         {% add_button 'circuits:circuit_add' %}
         {% import_button 'circuits:circuit_import' %}
@@ -14,7 +14,7 @@
 	<div class="col-md-9">
         {% include 'utilities/obj_table.html' with bulk_edit_url='circuits:circuit_bulk_edit' bulk_delete_url='circuits:circuit_bulk_delete' %}
     </div>
-    <div class="col-md-3">
+    <div class="col-md-3 noprint">
 		{% include 'inc/search_panel.html' %}
 		{% include 'inc/tags_panel.html' %}
     </div>

+ 1 - 1
netbox/templates/circuits/circuittype_list.html

@@ -2,7 +2,7 @@
 {% load buttons %}
 
 {% block content %}
-<div class="pull-right">
+<div class="pull-right noprint">
     {% if perms.circuits.add_circuittype %}
         {% add_button 'circuits:circuittype_add' %}
         {% import_button 'circuits:circuittype_import' %}

+ 3 - 3
netbox/templates/circuits/provider.html

@@ -5,7 +5,7 @@
 {% block title %}{{ provider }}{% endblock %}
 
 {% block header %}
-    <div class="row">
+    <div class="row noprint">
         <div class="col-sm-8 col-md-9">
             <ol class="breadcrumb">
                 <li><a href="{% url 'circuits:provider_list' %}">Providers</a></li>
@@ -25,7 +25,7 @@
             </form>
         </div>
     </div>
-    <div class="pull-right">
+    <div class="pull-right noprint">
         {% if show_graphs %}
             <button type="button" class="btn btn-primary" data-toggle="modal" data-target="#graphs_modal" data-obj="{{ provider.name }}" data-url="{% url 'circuits-api:provider-graphs' pk=provider.pk %}" title="Show graphs">
                 <i class="fa fa-signal" aria-hidden="true"></i>
@@ -172,7 +172,7 @@
                 {% endfor %}
             </table>
             {% if perms.circuits.add_circuit %}
-                <div class="panel-footer text-right">
+                <div class="panel-footer text-right noprint">
                     <a href="{% url 'circuits:circuit_add' %}?provider={{ provider.pk }}" class="btn btn-xs btn-primary">
                         <span class="glyphicon glyphicon-plus" aria-hidden="true"></span> Add circuit
                     </a>

+ 2 - 2
netbox/templates/circuits/provider_list.html

@@ -2,7 +2,7 @@
 {% load buttons %}
 
 {% block content %}
-<div class="pull-right">
+<div class="pull-right noprint">
     {% if perms.circuits.add_provider %}
         {% add_button 'circuits:provider_add' %}
         {% import_button 'circuits:provider_import' %}
@@ -14,7 +14,7 @@
     <div class="col-md-9">
         {% include 'utilities/obj_table.html' with bulk_edit_url='circuits:provider_bulk_edit' bulk_delete_url='circuits:provider_bulk_delete' %}
     </div>
-    <div class="col-md-3">
+    <div class="col-md-3 noprint">
 		{% include 'inc/search_panel.html' %}
 		{% include 'inc/tags_panel.html' %}
     </div>

+ 2 - 2
netbox/templates/dcim/cable.html

@@ -2,7 +2,7 @@
 {% load helpers %}
 
 {% block header %}
-    <div class="row">
+    <div class="row noprint">
         <div class="col-md-12">
             <ol class="breadcrumb">
                 <li><a href="{% url 'dcim:cable_list' %}">Cables</a></li>
@@ -10,7 +10,7 @@
             </ol>
         </div>
     </div>
-    <div class="pull-right">
+    <div class="pull-right noprint">
         {% if perms.dcim.change_cable %}
             <a href="{% url 'dcim:cable_edit' pk=cable.pk %}" class="btn btn-warning">
                 <span class="fa fa-pencil" aria-hidden="true"></span> Edit this cable

+ 2 - 2
netbox/templates/dcim/cable_list.html

@@ -2,7 +2,7 @@
 {% load buttons %}
 
 {% block content %}
-<div class="pull-right">
+<div class="pull-right noprint">
     {% if perms.dcim.add_cable %}
         {% import_button 'dcim:cable_import' %}
     {% endif %}
@@ -13,7 +13,7 @@
 	<div class="col-md-9">
         {% include 'utilities/obj_table.html' with bulk_edit_url='dcim:cable_bulk_edit' bulk_delete_url='dcim:cable_bulk_delete' %}
     </div>
-    <div class="col-md-3">
+    <div class="col-md-3 noprint">
 		{% include 'inc/search_panel.html' %}
     </div>
 </div>

+ 2 - 2
netbox/templates/dcim/console_connections_list.html

@@ -2,7 +2,7 @@
 {% load buttons %}
 
 {% block content %}
-<div class="pull-right">
+<div class="pull-right noprint">
     {% export_button content_type %}
 </div>
 <h1>{% block title %}Console Connections{% endblock %}</h1>
@@ -11,7 +11,7 @@
         {% include 'responsive_table.html' %}
         {% include 'inc/paginator.html' with paginator=table.paginator page=table.page %}
     </div>
-    <div class="col-md-3">
+    <div class="col-md-3 noprint">
 		{% include 'inc/search_panel.html' %}
     </div>
 </div>

+ 15 - 15
netbox/templates/dcim/device.html

@@ -5,7 +5,7 @@
 {% block title %}{{ device }}{% endblock %}
 
 {% block header %}
-    <div class="row">
+    <div class="row noprint">
         <div class="col-sm-8 col-md-9">
         <ol class="breadcrumb">
             <li><a href="{% url 'dcim:site' slug=device.site.slug %}">{{ device.site }}</a></li>
@@ -33,7 +33,7 @@
             </form>
         </div>
     </div>
-    <div class="pull-right">
+    <div class="pull-right noprint">
         {% if perms.dcim.change_device %}
             <div class="btn-group">
                 <button type="button" class="btn btn-primary dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
@@ -199,7 +199,7 @@
                             </tr>
                         {% endfor %}
                     </table>
-                    <div class="panel-footer text-right">
+                    <div class="panel-footer text-right noprint">
                         {% if perms.dcim.change_virtualchassis %}
                             <a href="{% url 'dcim:virtualchassis_add_member' pk=device.virtual_chassis.pk %}?site={{ device.site.pk }}&rack={{ device.rack.pk }}&return_url={{ device.get_absolute_url }}" class="btn btn-primary btn-xs">
                                 <span class="glyphicon glyphicon-plus" aria-hidden="true"></span> Add Member
@@ -317,7 +317,7 @@
                         {% endfor %}
                     </table>
                     {% if perms.dcim.add_interface or perms.dcim.add_consoleport or perms.dcim.add_powerport %}
-                        <div class="panel-footer text-right">
+                        <div class="panel-footer text-right noprint">
                             {% if perms.dcim.add_consoleport %}
                                 <a href="{% url 'dcim:consoleport_add' pk=device.pk %}" class="btn btn-xs btn-primary">
                                     <span class="glyphicon glyphicon-plus" aria-hidden="true"></span> Add console port
@@ -352,7 +352,7 @@
                         <form id="secret_form">
                             {% csrf_token %}
                         </form>
-                        <div class="panel-footer text-right">
+                        <div class="panel-footer text-right noprint">
                             <a href="{% url 'dcim:device_addsecret' pk=device.pk %}" class="btn btn-xs btn-primary">
                                 <span class="glyphicon glyphicon-plus" aria-hidden="true"></span>
                                 Add secret
@@ -377,7 +377,7 @@
                     </div>
                 {% endif %}
                 {% if perms.ipam.add_service %}
-                    <div class="panel-footer text-right">
+                    <div class="panel-footer text-right noprint">
                         <a href="{% url 'dcim:device_service_assign' device=device.pk %}" class="btn btn-xs btn-primary">
                             <span class="glyphicon glyphicon-plus" aria-hidden="true"></span> Assign service
                         </a>
@@ -390,7 +390,7 @@
                 </div>
                 {% include 'inc/image_attachments.html' with images=device.images.all %}
                 {% if perms.extras.add_imageattachment %}
-                    <div class="panel-footer text-right">
+                    <div class="panel-footer text-right noprint">
                         <a href="{% url 'dcim:device_add_image' object_id=device.pk %}" class="btn btn-primary btn-xs">
                             <span class="glyphicon glyphicon-plus" aria-hidden="true"></span>
                             Attach an image
@@ -398,7 +398,7 @@
                     </div>
                 {% endif %}
             </div>
-            <div class="panel panel-default">
+            <div class="panel panel-default noprint">
                 <div class="panel-heading">
                     <strong>Related Devices</strong>
                 </div>
@@ -459,7 +459,7 @@
                             {% endfor %}
                         </tbody>
                     </table>
-                    <div class="panel-footer">
+                    <div class="panel-footer noprint">
                         {% if device_bays and perms.dcim.change_devicebay %}
                             <button type="submit" name="_rename" formaction="{% url 'dcim:devicebay_bulk_rename' %}?return_url={{ device.get_absolute_url }}" class="btn btn-warning btn-xs">
                                 <span class="glyphicon glyphicon-pencil" aria-hidden="true"></span> Rename
@@ -493,7 +493,7 @@
                 <div class="panel panel-default">
                     <div class="panel-heading">
                         <strong>Interfaces</strong>
-                        <div class="pull-right">
+                        <div class="pull-right noprint">
                             <button class="btn btn-default btn-xs toggle-ips" selected="selected">
                                 <span class="glyphicon glyphicon-check" aria-hidden="true"></span> Show IPs
                             </button>
@@ -521,7 +521,7 @@
                             {% endfor %}
                         </tbody>
                     </table>
-                    <div class="panel-footer">
+                    <div class="panel-footer noprint">
                         {% if interfaces and perms.dcim.change_interface %}
                             <button type="submit" name="_rename" formaction="{% url 'dcim:interface_bulk_rename' %}?return_url={{ device.get_absolute_url }}" class="btn btn-warning btn-xs">
                                 <span class="glyphicon glyphicon-pencil" aria-hidden="true"></span> Rename
@@ -581,7 +581,7 @@
                             {% endfor %}
                         </tbody>
                     </table>
-                    <div class="panel-footer">
+                    <div class="panel-footer noprint">
                         {% if consoleserverports and perms.dcim.change_consoleport %}
                             <button type="submit" name="_rename" formaction="{% url 'dcim:consoleserverport_bulk_rename' %}?return_url={{ device.get_absolute_url }}" class="btn btn-warning btn-xs">
                                 <span class="glyphicon glyphicon-pencil" aria-hidden="true"></span> Rename
@@ -636,7 +636,7 @@
                             {% endfor %}
                         </tbody>
                     </table>
-                    <div class="panel-footer">
+                    <div class="panel-footer noprint">
                         {% if poweroutlets and perms.dcim.change_powerport %}
                             <button type="submit" name="_rename" formaction="{% url 'dcim:poweroutlet_bulk_rename' %}?return_url={{ device.get_absolute_url }}" class="btn btn-warning btn-xs">
                                 <span class="glyphicon glyphicon-pencil" aria-hidden="true"></span> Rename
@@ -693,7 +693,7 @@
                                 {% endfor %}
                             </tbody>
                         </table>
-                        <div class="panel-footer">
+                        <div class="panel-footer noprint">
                             {% if front_ports and perms.dcim.change_frontport %}
                                 <button type="submit" name="_rename" formaction="{% url 'dcim:frontport_bulk_rename' %}?return_url={{ device.get_absolute_url }}" class="btn btn-warning btn-xs">
                                     <span class="glyphicon glyphicon-pencil" aria-hidden="true"></span> Rename
@@ -750,7 +750,7 @@
                                 {% endfor %}
                             </tbody>
                         </table>
-                        <div class="panel-footer">
+                        <div class="panel-footer noprint">
                             {% if rear_ports and perms.dcim.change_rearport %}
                                 <button type="submit" name="_rename" formaction="{% url 'dcim:rearport_bulk_rename' %}?return_url={{ device.get_absolute_url }}" class="btn btn-warning btn-xs">
                                     <span class="glyphicon glyphicon-pencil" aria-hidden="true"></span> Rename

+ 1 - 1
netbox/templates/dcim/device_inventory.html

@@ -53,7 +53,7 @@
                     </tbody>
                 </table>
                 {% if perms.dcim.add_inventoryitem %}
-                    <div class="panel-footer text-right">
+                    <div class="panel-footer text-right noprint">
                         <a href="{% url 'dcim:inventoryitem_add' device=device.pk %}" class="btn btn-primary btn-xs">
                             <span class="fa fa-plus" aria-hidden="true"></span> Add Inventory Item
                         </a>

+ 2 - 2
netbox/templates/dcim/device_list.html

@@ -2,7 +2,7 @@
 {% load buttons %}
 
 {% block content %}
-<div class="pull-right">
+<div class="pull-right noprint">
     {% if perms.dcim.add_device %}
         {% add_button 'dcim:device_add' %}
         {% import_button 'dcim:device_import' %}
@@ -14,7 +14,7 @@
 	<div class="col-md-9">
         {% include 'dcim/inc/device_table.html' with bulk_edit_url='dcim:device_bulk_edit' bulk_delete_url='dcim:device_bulk_delete' %}
     </div>
-    <div class="col-md-3">
+    <div class="col-md-3 noprint">
 		{% include 'inc/search_panel.html' %}
 		{% include 'inc/tags_panel.html' %}
     </div>

+ 1 - 1
netbox/templates/dcim/devicerole_list.html

@@ -2,7 +2,7 @@
 {% load buttons %}
 
 {% block content %}
-<div class="pull-right">
+<div class="pull-right noprint">
     {% if perms.dcim.add_devicerole %}
         {% add_button 'dcim:devicerole_add' %}
         {% import_button 'dcim:devicerole_import' %}

+ 2 - 2
netbox/templates/dcim/devicetype.html

@@ -4,7 +4,7 @@
 {% block title %}{{ devicetype.manufacturer }} {{ devicetype.model }}{% endblock %}
 
 {% block header %}
-    <div class="row">
+    <div class="row noprint">
         <div class="col-md-12">
             <ol class="breadcrumb">
                 <li><a href="{% url 'dcim:devicetype_list' %}">Device Types</a></li>
@@ -14,7 +14,7 @@
         </div>
     </div>
     {% if perms.dcim.change_devicetype or perms.dcim.delete_devicetype %}
-        <div class="pull-right">
+        <div class="pull-right noprint">
             {% if perms.dcim.change_devicetype %}
                 <div class="btn-group">
                     <button type="button" class="btn btn-primary dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">

+ 2 - 2
netbox/templates/dcim/devicetype_list.html

@@ -2,7 +2,7 @@
 {% load buttons %}
 
 {% block content %}
-<div class="pull-right">
+<div class="pull-right noprint">
     {% if perms.dcim.add_devicetype %}
         {% add_button 'dcim:devicetype_add' %}
         {% import_button 'dcim:devicetype_import' %}
@@ -14,7 +14,7 @@
 	<div class="col-md-9">
         {% include 'utilities/obj_table.html' with bulk_edit_url='dcim:devicetype_bulk_edit' bulk_delete_url='dcim:devicetype_bulk_delete' %}
     </div>
-    <div class="col-md-3">
+    <div class="col-md-3 noprint">
 		{% include 'inc/search_panel.html' %}
 		{% include 'inc/tags_panel.html' %}
     </div>

+ 1 - 1
netbox/templates/dcim/inc/consoleport.html

@@ -29,7 +29,7 @@
     {% endif %}
 
     {# Actions #}
-    <td class="text-right">
+    <td class="text-right noprint">
         {% if cp.cable %}
             {% include 'dcim/inc/cable_toggle_buttons.html' with cable=cp.cable %}
         {% elif perms.dcim.add_cable %}

+ 1 - 1
netbox/templates/dcim/inc/consoleserverport.html

@@ -36,7 +36,7 @@
     {% endif %}
 
     {# Actions #}
-    <td class="text-right">
+    <td class="text-right noprint">
         {% if csp.cable %}
             {% include 'dcim/inc/cable_toggle_buttons.html' with cable=csp.cable %}
         {% elif perms.dcim.add_cable %}

+ 1 - 1
netbox/templates/dcim/inc/devicebay.html

@@ -23,7 +23,7 @@
             <span class="text-muted">Vacant</span>
         </td>
     {% endif %}
-    <td class="text-right">
+    <td class="text-right noprint">
         {% if perms.dcim.change_devicebay %}
             {% if devicebay.installed_device %}
                 <a href="{% url 'dcim:devicebay_depopulate' pk=devicebay.pk %}" class="btn btn-danger btn-xs">

+ 1 - 1
netbox/templates/dcim/inc/devicetype_component_table.html

@@ -6,7 +6,7 @@
                 <strong>{{ title }}</strong>
             </div>
             {% include 'responsive_table.html' %}
-            <div class="panel-footer">
+            <div class="panel-footer noprint">
                 {% if table.rows %}
                     {% if edit_url %}
                         <button type="submit" name="_edit" formaction="{% url edit_url pk=devicetype.pk %}?return_url={{ devicetype.get_absolute_url }}" class="btn btn-xs btn-warning">

+ 1 - 1
netbox/templates/dcim/inc/frontport.html

@@ -54,7 +54,7 @@
     {% endif %}
 
     {# Actions #}
-    <td class="text-right">
+    <td class="text-right noprint">
         {% if frontport.cable %}
             {% include 'dcim/inc/cable_toggle_buttons.html' with cable=frontport.cable %}
         {% elif perms.dcim.add_cable %}

+ 2 - 2
netbox/templates/dcim/inc/interface.html

@@ -134,7 +134,7 @@
     {% endif %}
 
     {# Buttons #}
-    <td class="text-right text-nowrap">
+    <td class="text-right text-nowrap noprint">
         {% if show_graphs %}
             {% if iface.connected_endpoint %}
                 <button type="button" class="btn btn-primary btn-xs" data-toggle="modal" data-target="#graphs_modal" data-obj="{{ device.name }} - {{ iface.name }}" data-url="{% url 'dcim-api:interface-graphs' pk=iface.pk %}" title="Show graphs">
@@ -231,7 +231,7 @@
                             </td>
 
                             {# Buttons #}
-                            <td class="text-right text-nowrap">
+                            <td class="text-right text-nowrap noprint">
                                 {% if perms.ipam.change_ipaddress %}
                                     <a href="{% url 'ipam:ipaddress_edit' pk=ip.pk %}?return_url={{ device.get_absolute_url }}" class="btn btn-info btn-xs">
                                         <i class="glyphicon glyphicon-pencil" aria-hidden="true" title="Edit IP address"></i>

+ 1 - 1
netbox/templates/dcim/inc/inventoryitem.html

@@ -6,7 +6,7 @@
     <td>{{ item.serial }}</td>
     <td>{{ item.asset_tag|default:"" }}</td>
     <td>{{ item.description }}</td>
-    <td class="text-right">
+    <td class="text-right noprint">
         {% if perms.dcim.change_inventoryitem %}
             <a href="{% url 'dcim:inventoryitem_edit' pk=item.pk %}" class="btn btn-xs btn-warning"><span class="glyphicon glyphicon-pencil" aria-hidden="true"></span></a>
         {% endif %}

+ 1 - 1
netbox/templates/dcim/inc/poweroutlet.html

@@ -36,7 +36,7 @@
     {% endif %}
 
     {# Actions #}
-    <td class="text-right">
+    <td class="text-right noprint">
         {% if po.cable %}
             {% include 'dcim/inc/cable_toggle_buttons.html' with cable=po.cable %}
         {% elif perms.dcim.add_cable %}

+ 1 - 1
netbox/templates/dcim/inc/powerport.html

@@ -29,7 +29,7 @@
     {% endif %}
 
     {# Actions #}
-    <td class="text-right">
+    <td class="text-right noprint">
         {% if pp.cable %}
             {% include 'dcim/inc/cable_toggle_buttons.html' with cable=pp.cable %}
         {% elif perms.dcim.add_cable %}

+ 1 - 1
netbox/templates/dcim/inc/rearport.html

@@ -53,7 +53,7 @@
     {% endif %}
 
     {# Actions #}
-    <td class="text-right">
+    <td class="text-right noprint">
         {% if rearport.cable %}
             {% include 'dcim/inc/cable_toggle_buttons.html' with cable=rearport.cable %}
         {% elif perms.dcim.add_cable %}

+ 2 - 2
netbox/templates/dcim/interface.html

@@ -2,7 +2,7 @@
 {% load helpers %}
 
 {% block header %}
-    <div class="row">
+    <div class="row noprint">
         <div class="col-md-12">
             <ol class="breadcrumb">
                 {% if interface.device %}
@@ -15,7 +15,7 @@
             </ol>
         </div>
     </div>
-    <div class="pull-right">
+    <div class="pull-right noprint">
         {% if perms.dcim.change_interface %}
             <a href="{% if interface.device %}{% url 'dcim:interface_edit' pk=interface.pk %}{% else %}{% url 'virtualization:interface_edit' pk=interface.pk %}{% endif %}" class="btn btn-warning">
                 <span class="fa fa-pencil" aria-hidden="true"></span> Edit this interface

+ 2 - 2
netbox/templates/dcim/interface_connections_list.html

@@ -2,7 +2,7 @@
 {% load buttons %}
 
 {% block content %}
-<div class="pull-right">
+<div class="pull-right noprint">
     {% export_button content_type %}
 </div>
 <h1>{% block title %}Interface Connections{% endblock %}</h1>
@@ -11,7 +11,7 @@
         {% include 'responsive_table.html' %}
         {% include 'inc/paginator.html' with paginator=table.paginator page=table.page %}
     </div>
-    <div class="col-md-3">
+    <div class="col-md-3 noprint">
 		{% include 'inc/search_panel.html' %}
     </div>
 </div>

+ 2 - 2
netbox/templates/dcim/inventoryitem_list.html

@@ -3,7 +3,7 @@
 {% load helpers %}
 
 {% block content %}
-<div class="pull-right">
+<div class="pull-right noprint">
     {% if perms.dcim.add_devicetype %}
         {% import_button 'dcim:inventoryitem_import' %}
     {% endif %}
@@ -14,7 +14,7 @@
 	<div class="col-md-9">
         {% include 'utilities/obj_table.html' with bulk_edit_url='dcim:inventoryitem_bulk_edit' bulk_delete_url='dcim:inventoryitem_bulk_delete' %}
     </div>
-    <div class="col-md-3">
+    <div class="col-md-3 noprint">
 		{% include 'inc/search_panel.html' %}
     </div>
 </div>

+ 1 - 1
netbox/templates/dcim/manufacturer_list.html

@@ -2,7 +2,7 @@
 {% load buttons %}
 
 {% block content %}
-<div class="pull-right">
+<div class="pull-right noprint">
     {% if perms.dcim.add_manufacturer %}
         {% add_button 'dcim:manufacturer_add' %}
         {% import_button 'dcim:manufacturer_import' %}

+ 1 - 1
netbox/templates/dcim/platform_list.html

@@ -2,7 +2,7 @@
 {% load buttons %}
 
 {% block content %}
-<div class="pull-right">
+<div class="pull-right noprint">
     {% if perms.dcim.add_platform %}
         {% add_button 'dcim:platform_add' %}
         {% import_button 'dcim:platform_import' %}

+ 2 - 2
netbox/templates/dcim/power_connections_list.html

@@ -2,7 +2,7 @@
 {% load buttons %}
 
 {% block content %}
-<div class="pull-right">
+<div class="pull-right noprint">
     {% export_button content_type %}
 </div>
 <h1>{% block title %}Power Connections{% endblock %}</h1>
@@ -11,7 +11,7 @@
         {% include 'responsive_table.html' %}
         {% include 'inc/paginator.html' with paginator=table.paginator page=table.page %}
     </div>
-    <div class="col-md-3">
+    <div class="col-md-3 noprint">
 		{% include 'inc/search_panel.html' %}
     </div>
 </div>

+ 6 - 6
netbox/templates/dcim/rack.html

@@ -2,7 +2,7 @@
 {% load helpers %}
 
 {% block header %}
-    <div class="row">
+    <div class="row noprint">
         <div class="col-sm-8 col-md-9">
             <ol class="breadcrumb">
                 <li><a href="{% url 'dcim:rack_list' %}">Racks</a></li>
@@ -23,7 +23,7 @@
             </form>
         </div>
     </div>
-    <div class="pull-right">
+    <div class="pull-right noprint">
         <a {% if prev_rack %}href="{% url 'dcim:rack' pk=prev_rack.pk %}"{% else %}disabled="disabled"{% endif %} class="btn btn-primary">
             <span class="fa fa-chevron-left" aria-hidden="true"></span> Previous Rack
         </a>
@@ -223,7 +223,7 @@
                 <div class="panel-body text-muted">None</div>
             {% endif %}
             {% if perms.dcim.add_device %}
-                <div class="panel-footer text-right">
+                <div class="panel-footer text-right noprint">
                     <a href="{% url 'dcim:device_add' %}?site={{ rack.site.pk }}&rack={{ rack.pk }}" class="btn btn-primary btn-xs">
                         <span class="glyphicon glyphicon-plus" aria-hidden="true"></span>
                         Add a non-racked device
@@ -237,7 +237,7 @@
             </div>
             {% include 'inc/image_attachments.html' with images=rack.images.all %}
             {% if perms.extras.add_imageattachment %}
-                <div class="panel-footer text-right">
+                <div class="panel-footer text-right noprint">
                     <a href="{% url 'dcim:rack_add_image' object_id=rack.pk %}" class="btn btn-primary btn-xs">
                         <span class="glyphicon glyphicon-plus" aria-hidden="true"></span>
                         Attach an image
@@ -271,7 +271,7 @@
                                 {{ resv.description }}<br />
                                 <small>{{ resv.user }} &middot; {{ resv.created }}</small>
                             </td>
-                            <td class="text-right">
+                            <td class="text-right noprint">
                                 {% if perms.dcim.change_rackreservation %}
                                     <a href="{% url 'dcim:rackreservation_edit' pk=resv.pk %}" class="btn btn-warning btn-xs" title="Edit reservation">
                                         <i class="glyphicon glyphicon-pencil" aria-hidden="true"></i>
@@ -290,7 +290,7 @@
                 <div class="panel-body text-muted">None</div>
             {% endif %}
             {% if perms.dcim.add_rackreservation %}
-                <div class="panel-footer text-right">
+                <div class="panel-footer text-right noprint">
                     <a href="{% url 'dcim:rack_add_reservation' rack=rack.pk %}" class="btn btn-primary btn-xs">
                         <span class="glyphicon glyphicon-plus" aria-hidden="true"></span>
                         Add a reservation

+ 2 - 2
netbox/templates/dcim/rack_elevation_list.html

@@ -2,7 +2,7 @@
 {% load helpers %}
 
 {% block content %}
-<div class="btn-group pull-right" role="group">
+<div class="btn-group pull-right noprint" role="group">
     <a href="{% url 'dcim:rack_elevation_list' %}{% querystring request face=0 %}" class="btn btn-default{% if request.GET.face != '1' %} active{% endif %}">Front</a>
     <a href="{% url 'dcim:rack_elevation_list' %}{% querystring request face=1 %}" class="btn btn-default{% if request.GET.face == '1' %} active{% endif %}">Rear</a>
 </div>
@@ -38,7 +38,7 @@
             <p>No racks found</p>
         </div>
     {% endif %}
-    <div class="col-md-3">
+    <div class="col-md-3 noprint">
 		{% include 'inc/search_panel.html' %}
     </div>
 </div>

+ 2 - 2
netbox/templates/dcim/rack_list.html

@@ -2,7 +2,7 @@
 {% load buttons %}
 
 {% block content %}
-<div class="pull-right">
+<div class="pull-right noprint">
     {% if perms.dcim.add_rack %}
         {% add_button 'dcim:rack_add' %}
         {% import_button 'dcim:rack_import' %}
@@ -14,7 +14,7 @@
 	<div class="col-md-9">
         {% include 'utilities/obj_table.html' with bulk_edit_url='dcim:rack_bulk_edit' bulk_delete_url='dcim:rack_bulk_delete' %}
     </div>
-    <div class="col-md-3">
+    <div class="col-md-3 noprint">
 		{% include 'inc/search_panel.html' %}
 		{% include 'inc/tags_panel.html' %}
     </div>

+ 2 - 2
netbox/templates/dcim/rackgroup_list.html

@@ -2,7 +2,7 @@
 {% load buttons %}
 
 {% block content %}
-<div class="pull-right">
+<div class="pull-right noprint">
     {% if perms.dcim.add_rackgroup %}
         {% add_button 'dcim:rackgroup_add' %}
         {% import_button 'dcim:rackgroup_import' %}
@@ -14,7 +14,7 @@
 	<div class="col-md-9">
         {% include 'utilities/obj_table.html' with bulk_delete_url='dcim:rackgroup_bulk_delete' %}
     </div>
-    <div class="col-md-3">
+    <div class="col-md-3 noprint">
 		{% include 'inc/search_panel.html' %}
     </div>
 </div>

+ 1 - 1
netbox/templates/dcim/rackreservation_list.html

@@ -7,7 +7,7 @@
 	<div class="col-md-9">
         {% include 'utilities/obj_table.html' with bulk_edit_url='dcim:rackreservation_bulk_edit' bulk_delete_url='dcim:rackreservation_bulk_delete' %}
     </div>
-	<div class="col-md-3">
+	<div class="col-md-3 noprint">
 		{% include 'inc/search_panel.html' %}
 	</div>
 </div>

+ 1 - 1
netbox/templates/dcim/rackrole_list.html

@@ -2,7 +2,7 @@
 {% load buttons %}
 
 {% block content %}
-<div class="pull-right">
+<div class="pull-right noprint">
     {% if perms.dcim.add_rackrole %}
         {% add_button 'dcim:rackrole_add' %}
         {% import_button 'dcim:rackrole_import' %}

+ 2 - 2
netbox/templates/dcim/region_list.html

@@ -2,7 +2,7 @@
 {% load buttons %}
 
 {% block content %}
-<div class="pull-right">
+<div class="pull-right noprint">
     {% if perms.dcim.add_region %}
         {% add_button 'dcim:region_add' %}
         {% import_button 'dcim:region_import' %}
@@ -14,7 +14,7 @@
 	<div class="col-md-9">
         {% include 'utilities/obj_table.html' with bulk_delete_url='dcim:region_bulk_delete' %}
     </div>
-	<div class="col-md-3">
+	<div class="col-md-3 noprint">
 		{% include 'inc/search_panel.html' %}
 	</div>
 </div>

+ 6 - 6
netbox/templates/dcim/site.html

@@ -4,7 +4,7 @@
 {% load helpers %}
 
 {% block header %}
-    <div class="row">
+    <div class="row noprint">
         <div class="col-sm-8 col-md-9">
             <ol class="breadcrumb">
                 <li><a href="{% url 'dcim:site_list' %}">Sites</a></li>
@@ -30,7 +30,7 @@
             </form>
         </div>
     </div>
-    <div class="pull-right">
+    <div class="pull-right noprint">
         {% if show_graphs %}
             <button type="button" class="btn btn-primary" data-toggle="modal" data-target="#graphs_modal" data-obj="{{ site.name }}" data-url="{% url 'dcim-api:site-graphs' pk=site.pk %}" title="Show graphs">
                 <i class="fa fa-signal" aria-hidden="true"></i>
@@ -138,7 +138,7 @@
                     <td>Physical Address</td>
                     <td>
                         {% if site.physical_address %}
-                            <div class="pull-right">
+                            <div class="pull-right noprint">
                                 <a href="http://maps.google.com/?q={{ site.physical_address|oneline|urlencode }}" target="_blank" class="btn btn-primary btn-xs">
                                     <i class="glyphicon glyphicon-map-marker"></i> Map it
                                 </a>
@@ -157,7 +157,7 @@
                     <td>GPS Coordinates</td>
                     <td>
                         {% if site.latitude and site.longitude %}
-                            <div class="pull-right">
+                            <div class="pull-right noprint">
                                 <a href="http://maps.google.com/?q={{ site.latitude }},{{ site.longitude }}" target="_blank" class="btn btn-primary btn-xs">
                                     <i class="glyphicon glyphicon-map-marker"></i> Map it
                                 </a>
@@ -251,7 +251,7 @@
                         <tr>
                             <td><i class="fa fa-fw fa-folder-o"></i> <a href="{{ rg.get_absolute_url }}">{{ rg }}</a></td>
                             <td>{{ rg.rack_count }}</td>
-                            <td class="text-right">
+                            <td class="text-right noprint">
                                 <a href="{% url 'dcim:rack_elevation_list' %}?group_id={{ rg.pk }}" class="btn btn-xs btn-primary" title="View elevations">
                                     <i class="fa fa-eye"></i>
                                 </a>
@@ -271,7 +271,7 @@
             </div>
             {% include 'inc/image_attachments.html' with images=site.images.all %}
             {% if perms.extras.add_imageattachment %}
-                <div class="panel-footer text-right">
+                <div class="panel-footer text-right noprint">
                     <a href="{% url 'dcim:site_add_image' object_id=site.pk %}" class="btn btn-primary btn-xs">
                         <span class="glyphicon glyphicon-plus" aria-hidden="true"></span>
                         Attach an image

+ 2 - 2
netbox/templates/dcim/site_list.html

@@ -2,7 +2,7 @@
 {% load buttons %}
 
 {% block content %}
-<div class="pull-right">
+<div class="pull-right noprint">
     {% if perms.dcim.add_site %}
         {% add_button 'dcim:site_add' %}
         {% import_button 'dcim:site_import' %}
@@ -14,7 +14,7 @@
 	<div class="col-md-9">
         {% include 'utilities/obj_table.html' with bulk_edit_url='dcim:site_bulk_edit' %}
     </div>
-    <div class="col-md-3">
+    <div class="col-md-3 noprint">
 		{% include 'inc/search_panel.html' %}
 		{% include 'inc/tags_panel.html' %}
     </div>

+ 2 - 2
netbox/templates/dcim/virtualchassis_list.html

@@ -3,7 +3,7 @@
 {% load helpers %}
 
 {% block content %}
-<div class="pull-right">
+<div class="pull-right noprint">
     {% export_button content_type %}
 </div>
 <h1>{% block title %}Virtual Chassis{% endblock %}</h1>
@@ -11,7 +11,7 @@
 	<div class="col-md-9">
         {% include 'utilities/obj_table.html' %}
     </div>
-    <div class="col-md-3">
+    <div class="col-md-3 noprint">
 		{% include 'inc/search_panel.html' %}
 		{% include 'inc/tags_panel.html' %}
     </div>

+ 2 - 2
netbox/templates/extras/configcontext.html

@@ -2,7 +2,7 @@
 {% load helpers %}
 
 {% block header %}
-    <div class="row">
+    <div class="row noprint">
         <div class="col-sm-8 col-md-9">
             <ol class="breadcrumb">
                 <li><a href="{% url 'extras:configcontext_list' %}">Config Contexts</a></li>
@@ -22,7 +22,7 @@
             </form>
         </div>
     </div>
-    <div class="pull-right">
+    <div class="pull-right noprint">
         {% if perms.extras.change_configcontext %}
             <a href="{% url 'extras:configcontext_edit' pk=configcontext.pk %}" class="btn btn-warning">
                 <span class="glyphicon glyphicon-pencil" aria-hidden="true"></span>

+ 2 - 2
netbox/templates/extras/configcontext_list.html

@@ -2,7 +2,7 @@
 {% load buttons %}
 
 {% block content %}
-    <div class="pull-right">
+    <div class="pull-right noprint">
         {% if perms.extras.add_configcontext %}
             {% add_button 'extras:configcontext_add' %}
         {% endif %}
@@ -12,7 +12,7 @@
         <div class="col-md-9">
             {% include 'utilities/obj_table.html' with bulk_edit_url='extras:configcontext_bulk_edit' bulk_delete_url='extras:configcontext_bulk_delete' %}
         </div>
-        <div class="col-md-3">
+        <div class="col-md-3 noprint">
             {% include 'inc/search_panel.html' %}
         </div>
     </div>

+ 2 - 2
netbox/templates/extras/objectchange.html

@@ -4,7 +4,7 @@
 {% block title %}{{ objectchange }}{% endblock %}
 
 {% block header %}
-    <div class="row">
+    <div class="row noprint">
         <div class="col-sm-8 col-md-9">
             <ol class="breadcrumb">
                 <li><a href="{% url 'extras:objectchange_list' %}">Changelog</a></li>
@@ -97,7 +97,7 @@
     </div>
     <div class="row">
         <div class="col-md-12">
-            {% include 'panel_table.html' with table=related_changes_table heading='Related Changes' %}
+            {% include 'panel_table.html' with table=related_changes_table heading='Related Changes' panel_class='noprint' %}
             {% if related_changes_count > related_changes_table.rows|length %}
                 <div class="pull-right">
                     <a href="{% url 'extras:objectchange_list' %}?request_id={{ objectchange.request_id }}" class="btn btn-primary">See all {{ related_changes_count|add:"1" }} changes</a>

+ 2 - 2
netbox/templates/extras/objectchange_list.html

@@ -2,7 +2,7 @@
 {% load buttons %}
 
 {% block content %}
-<div class="pull-right">
+<div class="pull-right noprint">
     {% export_button content_type %}
 </div>
 <h1>{% block title %}Changelog{% endblock %}</h1>
@@ -10,7 +10,7 @@
 	<div class="col-md-9">
         {% include 'utilities/obj_table.html' %}
     </div>
-    <div class="col-md-3">
+    <div class="col-md-3 noprint">
 		{% include 'inc/search_panel.html' %}
     </div>
 </div>

+ 2 - 2
netbox/templates/extras/report.html

@@ -4,7 +4,7 @@
 {% block title %}{{ report.name }}{% endblock %}
 
 {% block content %}
-    <div class="row">
+    <div class="row noprint">
         <div class="col-md-12">
             <ol class="breadcrumb">
                 <li><a href="{% url 'extras:report_list' %}">Reports</a></li>
@@ -14,7 +14,7 @@
         </div>
     </div>
     {% if perms.extras.add_reportresult %}
-        <div class="pull-right">
+        <div class="pull-right noprint">
             <form action="{% url 'extras:report_run' name=report.full_name %}" method="post">
                 {% csrf_token %}
                 {{ run_form }}

+ 6 - 0
netbox/templates/extras/tag.html

@@ -29,6 +29,12 @@
                 Edit this tag
             </a>
         {% endif %}
+        {% if perms.taggit.delete_tag %}
+            <a href="{% url 'extras:tag_delete' slug=tag.slug %}" class="btn btn-danger">
+                <span class="glyphicon glyphicon-trash" aria-hidden="true"></span>
+                Delete this tag
+            </a>
+        {% endif %}
     </div>
     <h1>{% block title %}Tag: {{ tag }}{% endblock %}</h1>
 {% endblock %}

+ 1 - 1
netbox/templates/inc/image_attachments.html

@@ -14,7 +14,7 @@
                 </td>
                 <td>{{ attachment.size|filesizeformat }}</td>
                 <td>{{ attachment.created }}</td>
-                <td class="text-right">
+                <td class="text-right noprint">
                     {% if perms.extras.change_imageattachment %}
                         <a href="{% url 'extras:imageattachment_edit' pk=attachment.pk %}" class="btn btn-warning btn-xs" title="Edit image">
                             <i class="glyphicon glyphicon-pencil" aria-hidden="true"></i>

+ 1 - 1
netbox/templates/inc/search_panel.html

@@ -26,7 +26,7 @@
                         {% endif %}
                     </div>
                 {% endfor %}
-                <div class="text-right">
+                <div class="text-right noprint">
                     <button type="submit" class="btn btn-primary">
                         <span class="fa fa-search" aria-hidden="true"></span> Apply
                     </button>

+ 2 - 2
netbox/templates/ipam/aggregate.html

@@ -2,7 +2,7 @@
 {% load helpers %}
 
 {% block header %}
-    <div class="row">
+    <div class="row noprint">
         <div class="col-sm-8 col-md-9">
             <ol class="breadcrumb">
                 <li><a href="{% url 'ipam:aggregate_list' %}">Aggregates</a></li>
@@ -23,7 +23,7 @@
             </form>
         </div>
     </div>
-    <div class="pull-right">
+    <div class="pull-right noprint">
         {% if perms.ipam.change_aggregate %}
             <a href="{% url 'ipam:aggregate_edit' pk=aggregate.pk %}" class="btn btn-warning">
                 <span class="fa fa-pencil" aria-hidden="true"></span>

+ 2 - 2
netbox/templates/ipam/aggregate_list.html

@@ -3,7 +3,7 @@
 {% load humanize %}
 
 {% block content %}
-<div class="pull-right">
+<div class="pull-right noprint">
     {% if perms.ipam.add_aggregate %}
         {% add_button 'ipam:aggregate_add' %}
         {% import_button 'ipam:aggregate_import' %}
@@ -15,7 +15,7 @@
 	<div class="col-md-9">
         {% include 'utilities/obj_table.html' with bulk_edit_url='ipam:aggregate_bulk_edit' bulk_delete_url='ipam:aggregate_bulk_delete' %}
 	</div>
-	<div class="col-md-3">
+	<div class="col-md-3 noprint">
 		{% include 'inc/search_panel.html' %}
 		{% include 'inc/tags_panel.html' %}
         <div class="panel panel-default">

+ 1 - 1
netbox/templates/ipam/inc/service.html

@@ -13,7 +13,7 @@
         {% endfor %}
     </td>
     <td>{{ service.description }}</td>
-    <td class="text-right">
+    <td class="text-right noprint">
         <a href="{% url 'ipam:service_changelog' pk=service.pk %}" class="btn btn-default btn-xs" title="Changelog">
             <i class="fa fa-history"></i>
         </a>

+ 3 - 3
netbox/templates/ipam/ipaddress.html

@@ -2,7 +2,7 @@
 {% load helpers %}
 
 {% block header %}
-    <div class="row">
+    <div class="row noprint">
         <div class="col-sm-8 col-md-9">
             <ol class="breadcrumb">
                 <li><a href="{% url 'ipam:ipaddress_list' %}">IP Addresses</a></li>
@@ -25,7 +25,7 @@
             </form>
         </div>
     </div>
-    <div class="pull-right">
+    <div class="pull-right noprint">
         {% if perms.ipam.change_ipaddress %}
             <a href="{% url 'ipam:ipaddress_edit' pk=ipaddress.pk %}" class="btn btn-warning">
                 <span class="fa fa-pencil" aria-hidden="true"></span>
@@ -150,7 +150,7 @@
         {% if duplicate_ips_table.rows %}
             {% include 'panel_table.html' with table=duplicate_ips_table heading='Duplicate IP Addresses' panel_class='danger' %}
         {% endif %}
-        {% include 'panel_table.html' with table=related_ips_table heading='Related IP Addresses' panel_class='default' %}
+        {% include 'panel_table.html' with table=related_ips_table heading='Related IP Addresses' panel_class='default noprint' %}
 	</div>
 </div>
 {% endblock %}

+ 2 - 2
netbox/templates/ipam/ipaddress_list.html

@@ -2,7 +2,7 @@
 {% load buttons %}
 
 {% block content %}
-<div class="pull-right">
+<div class="pull-right noprint">
     {% if perms.ipam.add_ipaddress %}
         {% add_button 'ipam:ipaddress_add' %}
         {% import_button 'ipam:ipaddress_import' %}
@@ -14,7 +14,7 @@
 	<div class="col-md-9">
         {% include 'utilities/obj_table.html' with bulk_edit_url='ipam:ipaddress_bulk_edit' bulk_delete_url='ipam:ipaddress_bulk_delete' %}
 	</div>
-	<div class="col-md-3">
+	<div class="col-md-3 noprint">
 		{% include 'inc/search_panel.html' %}
 		{% include 'inc/tags_panel.html' %}
 	</div>

+ 2 - 2
netbox/templates/ipam/prefix.html

@@ -2,7 +2,7 @@
 {% load helpers %}
 
 {% block header %}
-    <div class="row">
+    <div class="row noprint">
         <div class="col-sm-8 col-md-9">
             <ol class="breadcrumb">
                 <li><a href="{% url 'ipam:prefix_list' %}">Prefixes</a></li>
@@ -25,7 +25,7 @@
             </form>
         </div>
     </div>
-    <div class="pull-right">
+    <div class="pull-right noprint">
         {% if perms.ipam.add_prefix and active_tab == 'prefixes' and first_available_prefix %}
             <a href="{% url 'ipam:prefix_add' %}?prefix={{ first_available_prefix }}&vrf={{ prefix.vrf.pk }}&site={{ prefix.site.pk }}&tenant_group={{ prefix.tenant.group.pk }}&tenant={{ prefix.tenant.pk }}" class="btn btn-success">
                 <i class="fa fa-plus" aria-hidden="true"></i> Add Child Prefix

+ 2 - 2
netbox/templates/ipam/prefix_list.html

@@ -3,7 +3,7 @@
 {% load helpers %}
 
 {% block content %}
-<div class="pull-right">
+<div class="pull-right noprint">
     <div class="btn-group" role="group">
         <a href="{% url 'ipam:prefix_list' %}{% querystring request expand=None page=1 %}" class="btn btn-default{% if not request.GET.expand %} active{% endif %}">Collapse</a>
         <a href="{% url 'ipam:prefix_list' %}{% querystring request expand='on' page=1 %}" class="btn btn-default{% if request.GET.expand %} active{% endif %}">Expand</a>
@@ -19,7 +19,7 @@
 	<div class="col-md-9">
         {% include 'utilities/obj_table.html' with bulk_edit_url='ipam:prefix_bulk_edit' bulk_delete_url='ipam:prefix_bulk_delete' %}
 	</div>
-	<div class="col-md-3">
+	<div class="col-md-3 noprint">
 		{% include 'inc/search_panel.html' %}
 		{% include 'inc/tags_panel.html' %}
 	</div>

+ 2 - 2
netbox/templates/ipam/rir_list.html

@@ -3,7 +3,7 @@
 {% load humanize %}
 
 {% block content %}
-<div class="pull-right">
+<div class="pull-right noprint">
     {% if request.GET.family == '6' %}
         <a href="{% url 'ipam:rir_list' %}" class="btn btn-default">
             <span class="fa fa-table" aria-hidden="true"></span>
@@ -29,7 +29,7 @@
             <div class="alert alert-info pull-right"><strong>Note:</strong> Numbers shown indicate /64 prefixes.</div>
         {% endif %}
     </div>
-	<div class="col-md-3">
+	<div class="col-md-3 noprint">
 		{% include 'inc/search_panel.html' %}
 	</div>
 </div>

+ 1 - 1
netbox/templates/ipam/role_list.html

@@ -2,7 +2,7 @@
 {% load buttons %}
 
 {% block content %}
-<div class="pull-right">
+<div class="pull-right noprint">
     {% if perms.ipam.add_role %}
         {% add_button 'ipam:role_add' %}
         {% import_button 'ipam:role_import' %}

+ 1 - 1
netbox/templates/ipam/service.html

@@ -2,7 +2,7 @@
 {% load helpers %}
 
 {% block content %}
-<div class="row">
+<div class="row noprint">
     <div class="col-sm-8 col-md-9">
         <ol class="breadcrumb">
             <li><a href="{% url 'ipam:service_list' %}">Services</a></li>

+ 2 - 2
netbox/templates/ipam/service_list.html

@@ -2,7 +2,7 @@
 {% load buttons %}
 
 {% block content %}
-<div class="pull-right">
+<div class="pull-right noprint">
     {% export_button content_type %}
 </div>
 <h1>{% block title %}Services{% endblock %}</h1>
@@ -10,7 +10,7 @@
 	<div class="col-md-9">
         {% include 'utilities/obj_table.html' with bulk_edit_url='ipam:service_bulk_edit' bulk_delete_url='ipam:service_bulk_delete' %}
 	</div>
-	<div class="col-md-3">
+	<div class="col-md-3 noprint">
 		{% include 'inc/search_panel.html' %}
 		{% include 'inc/tags_panel.html' %}
 	</div>

+ 3 - 3
netbox/templates/ipam/vlan.html

@@ -2,7 +2,7 @@
 {% load helpers %}
 
 {% block header %}
-    <div class="row">
+    <div class="row noprint">
         <div class="col-sm-8 col-md-9">
             <ol class="breadcrumb">
                 <li><a href="{% url 'ipam:vlan_list' %}">VLANs</a></li>
@@ -28,7 +28,7 @@
         </form>
         </div>
     </div>
-    <div class="pull-right">
+    <div class="pull-right noprint">
         {% if perms.ipam.change_vlan %}
             <a href="{% url 'ipam:vlan_edit' pk=vlan.pk %}" class="btn btn-warning">
                 <span class="fa fa-pencil" aria-hidden="true"></span>
@@ -143,7 +143,7 @@
                 </div>
                 {% include 'responsive_table.html' with table=prefix_table %}
                 {% if perms.ipam.add_prefix %}
-                    <div class="panel-footer text-right">
+                    <div class="panel-footer text-right noprint">
                         <a href="{% url 'ipam:prefix_add' %}?{% if vlan.tenant %}tenant={{ vlan.tenant.pk }}&{% endif %}site={{ vlan.site.pk }}&vlan={{ vlan.pk }}" class="btn btn-primary btn-xs">
                             <span class="glyphicon glyphicon-plus" aria-hidden="true"></span>
                             Add a prefix

+ 2 - 2
netbox/templates/ipam/vlan_list.html

@@ -2,7 +2,7 @@
 {% load buttons %}
 
 {% block content %}
-<div class="pull-right">
+<div class="pull-right noprint">
     {% if perms.ipam.add_vlan %}
         {% add_button 'ipam:vlan_add' %}
         {% import_button 'ipam:vlan_import' %}
@@ -14,7 +14,7 @@
 	<div class="col-md-9">
         {% include 'utilities/obj_table.html' with bulk_edit_url='ipam:vlan_bulk_edit' bulk_delete_url='ipam:vlan_bulk_delete' %}
 	</div>
-	<div class="col-md-3">
+	<div class="col-md-3 noprint">
 		{% include 'inc/search_panel.html' %}
 		{% include 'inc/tags_panel.html' %}
 	</div>

+ 2 - 2
netbox/templates/ipam/vlangroup_list.html

@@ -2,7 +2,7 @@
 {% load buttons %}
 
 {% block content %}
-<div class="pull-right">
+<div class="pull-right noprint">
     {% if perms.ipam.add_vlangroup %}
         {% add_button 'ipam:vlangroup_add' %}
         {% import_button 'ipam:vlangroup_import' %}
@@ -14,7 +14,7 @@
 	<div class="col-md-9">
         {% include 'utilities/obj_table.html' with bulk_delete_url='ipam:vlangroup_bulk_delete' %}
     </div>
-    <div class="col-md-3">
+    <div class="col-md-3 noprint">
 		{% include 'inc/search_panel.html' %}
     </div>
 </div>

+ 1 - 1
netbox/templates/ipam/vlangroup_vlans.html

@@ -3,7 +3,7 @@
 {% block title %}{{ vlan_group }} - VLANs{% endblock %}
 
 {% block content %}
-<div class="row">
+<div class="row noprint">
     <div class="col-sm-12 col-md-12">
         <ol class="breadcrumb">
             <li><a href="{% url 'ipam:vlangroup_list' %}">VLAN Groups</a></li>

+ 2 - 2
netbox/templates/ipam/vrf.html

@@ -2,7 +2,7 @@
 {% load helpers %}
 
 {% block header %}
-    <div class="row">
+    <div class="row noprint">
         <div class="col-sm-8 col-md-9">
             <ol class="breadcrumb">
                 <li><a href="{% url 'ipam:vrf_list' %}">VRFs</a></li>
@@ -22,7 +22,7 @@
             </form>
         </div>
     </div>
-    <div class="pull-right">
+    <div class="pull-right noprint">
         {% if perms.ipam.change_vrf %}
             <a href="{% url 'ipam:vrf_edit' pk=vrf.pk %}" class="btn btn-warning">
                 <span class="fa fa-pencil" aria-hidden="true"></span>

+ 2 - 2
netbox/templates/ipam/vrf_list.html

@@ -2,7 +2,7 @@
 {% load buttons %}
 
 {% block content %}
-<div class="pull-right">
+<div class="pull-right noprint">
     {% if perms.ipam.add_vrf %}
         {% add_button 'ipam:vrf_add' %}
         {% import_button 'ipam:vrf_import' %}
@@ -14,7 +14,7 @@
 	<div class="col-md-9">
         {% include 'utilities/obj_table.html' with bulk_edit_url='ipam:vrf_bulk_edit' bulk_delete_url='ipam:vrf_bulk_delete' %}
 	</div>
-	<div class="col-md-3">
+	<div class="col-md-3 noprint">
 		{% include 'inc/search_panel.html' %}
 		{% include 'inc/tags_panel.html' %}
 	</div>

+ 1 - 1
netbox/templates/secrets/inc/private_key_modal.html

@@ -16,7 +16,7 @@
                 <div class="form-group">
                     <textarea class="form-control" id="user_privkey" style="height: 300px;"></textarea>
                 </div>
-                <div class="form-group text-right">
+                <div class="form-group text-right noprint">
                     <button id="request_session_key" class="btn btn-primary" data-dismiss="modal">
                         Request session key
                     </button>

+ 1 - 1
netbox/templates/secrets/inc/secret_tr.html

@@ -3,7 +3,7 @@
     <td><a href="{% url 'secrets:secret' pk=secret.pk %}">{{ secret.role }}</a></td>
     <td>{{ secret.name }}</td>
     <td id="secret_{{ secret.pk }}">********</td>
-    <td class="text-right">
+    <td class="text-right noprint">
         {% if secret|decryptable_by:request.user %}
             <button class="btn btn-xs btn-success unlock-secret" secret-id="{{ secret.pk }}">
                 <i class="fa fa-lock"></i> Unlock

+ 3 - 3
netbox/templates/secrets/secret.html

@@ -4,7 +4,7 @@
 {% load secret_helpers %}
 
 {% block header %}
-    <div class="row">
+    <div class="row noprint">
         <div class="col-md-12">
             <ol class="breadcrumb">
                 <li><a href="{% url 'secrets:secret_list' %}">Secrets</a></li>
@@ -13,7 +13,7 @@
             </ol>
         </div>
     </div>
-    <div class="pull-right">
+    <div class="pull-right noprint">
         {% if perms.secrets.change_secret %}
             <a href="{% url 'secrets:secret_edit' pk=secret.pk %}" class="btn btn-warning">
                 <span class="fa fa-pencil" aria-hidden="true"></span>
@@ -78,7 +78,7 @@
                     <div class="row">
                         <div class="col-md-2">Secret</div>
                         <div class="col-md-6" id="secret_{{ secret.pk }}">********</div>
-                        <div class="col-md-4 text-right">
+                        <div class="col-md-4 text-right noprint">
                             <button class="btn btn-xs btn-success unlock-secret" secret-id="{{ secret.pk }}">
                                 <i class="fa fa-lock"></i> Unlock
                             </button>

+ 2 - 2
netbox/templates/secrets/secret_list.html

@@ -2,7 +2,7 @@
 {% load buttons %}
 
 {% block content %}
-<div class="pull-right">
+<div class="pull-right noprint">
     {% if perms.secrets.add_secret %}
         {% import_button 'secrets:secret_import' %}
     {% endif %}
@@ -13,7 +13,7 @@
     <div class="col-md-9">
         {% include 'utilities/obj_table.html' with bulk_edit_url='secrets:secret_bulk_edit' bulk_delete_url='secrets:secret_bulk_delete' %}
     </div>
-    <div class="col-md-3">
+    <div class="col-md-3 noprint">
 		{% include 'inc/search_panel.html' %}
 		{% include 'inc/tags_panel.html' %}
     </div>

+ 1 - 1
netbox/templates/secrets/secretrole_list.html

@@ -2,7 +2,7 @@
 {% load buttons %}
 
 {% block content %}
-<div class="pull-right">
+<div class="pull-right noprint">
     {% if perms.secrets.add_secretrole %}
         {% add_button 'secrets:secretrole_add' %}
         {% import_button 'secrets:secretrole_import' %}

+ 2 - 2
netbox/templates/tenancy/tenant.html

@@ -2,7 +2,7 @@
 {% load helpers %}
 
 {% block header %}
-    <div class="row">
+    <div class="row noprint">
         <div class="col-sm-8 col-md-9">
             <ol class="breadcrumb">
                 <li><a href="{% url 'tenancy:tenant_list' %}">Tenants</a></li>
@@ -25,7 +25,7 @@
             </form>
         </div>
     </div>
-    <div class="pull-right">
+    <div class="pull-right noprint">
         {% if perms.tenancy.change_tenant %}
             <a href="{% url 'tenancy:tenant_edit' slug=tenant.slug %}" class="btn btn-warning">
                 <span class="fa fa-pencil" aria-hidden="true"></span>

+ 2 - 2
netbox/templates/tenancy/tenant_list.html

@@ -2,7 +2,7 @@
 {% load buttons %}
 
 {% block content %}
-<div class="pull-right">
+<div class="pull-right noprint">
     {% if perms.tenancy.add_tenant %}
         {% add_button 'tenancy:tenant_add' %}
         {% import_button 'tenancy:tenant_import' %}
@@ -14,7 +14,7 @@
 	<div class="col-md-9">
         {% include 'utilities/obj_table.html' with bulk_edit_url='tenancy:tenant_bulk_edit' bulk_delete_url='tenancy:tenant_bulk_delete' %}
     </div>
-    <div class="col-md-3">
+    <div class="col-md-3 noprint">
 		{% include 'inc/search_panel.html' %}
 		{% include 'inc/tags_panel.html' %}
     </div>

+ 1 - 1
netbox/templates/tenancy/tenantgroup_list.html

@@ -2,7 +2,7 @@
 {% load buttons %}
 
 {% block content %}
-<div class="pull-right">
+<div class="pull-right noprint">
     {% if perms.tenancy.add_tenantgroup %}
         {% add_button 'tenancy:tenantgroup_add' %}
         {% import_button 'tenancy:tenantgroup_import' %}

+ 1 - 1
netbox/templates/users/api_tokens.html

@@ -9,7 +9,7 @@
             {% for token in tokens %}
                 <div class="panel panel-{% if token.is_expired %}danger{% else %}default{% endif %}">
                     <div class="panel-heading">
-                        <div class="pull-right">
+                        <div class="pull-right noprint">
                             {% if perms.users.change_token %}
                                 <a href="{% url 'user:token_edit' pk=token.pk %}" class="btn btn-xs btn-warning">Edit</a>
                             {% endif %}

+ 2 - 2
netbox/templates/users/userkey.html

@@ -4,7 +4,7 @@
 
 {% block usercontent %}
     {% if userkey %}
-        <div class="pull-right">
+        <div class="pull-right noprint">
             <a href="{% url 'user:userkey_edit' %}" class="btn btn-warning">
                 <span class="fa fa-pencil" aria-hidden="true"></span>
                 Edit user key
@@ -28,7 +28,7 @@
         <pre>{{ userkey.public_key }}</pre>
         <hr />
         {% if userkey.session_key %}
-            <div class="pull-right">
+            <div class="pull-right noprint">
                 <a href="{% url 'user:sessionkey_delete' %}" class="btn btn-danger">
                     <span class="fa fa-trash" aria-hidden="true"></span>
                     Delete session key

+ 2 - 2
netbox/templates/utilities/obj_table.html

@@ -4,7 +4,7 @@
         {% csrf_token %}
         <input type="hidden" name="return_url" value="{% if return_url %}{{ return_url }}{% else %}{{ request.path }}{% if request.GET %}?{{ request.GET.urlencode }}{% endif %}{% endif %}" />
         {% if table.paginator.num_pages > 1 %}
-            <div id="select_all_box" class="hidden panel panel-default">
+            <div id="select_all_box" class="hidden panel panel-default noprint">
                 <div class="panel-body">
                     <div class="checkbox-inline">
                         <label for="select_all">
@@ -28,7 +28,7 @@
             </div>
         {% endif %}
         {% include table_template|default:'responsive_table.html' %}
-        <div class="pull-left">
+        <div class="pull-left noprint">
             {% block extra_actions %}{% endblock %}
             {% if bulk_edit_url and permissions.change %}
                 <button type="submit" name="_edit" formaction="{% url bulk_edit_url %}{% if request.GET %}?{{ request.GET.urlencode }}{% endif %}" class="btn btn-warning btn-sm">

+ 3 - 3
netbox/templates/virtualization/cluster.html

@@ -2,7 +2,7 @@
 {% load helpers %}
 
 {% block header %}
-    <div class="row" xmlns="http://www.w3.org/1999/html">
+    <div class="row noprint" xmlns="http://www.w3.org/1999/html">
         <div class="col-sm-8 col-md-9">
             <ol class="breadcrumb">
                 <li><a href="{{ cluster.type.get_absolute_url }}">{{ cluster.type }}</a></li>
@@ -25,7 +25,7 @@
             </form>
         </div>
     </div>
-    <div class="pull-right">
+    <div class="pull-right noprint">
         {% if perms.virtualization.change_cluster %}
             <a href="{% url 'virtualization:cluster_edit' pk=cluster.pk %}" class="btn btn-warning">
                 <span class="fa fa-pencil" aria-hidden="true"></span>
@@ -119,7 +119,7 @@
             {% endif %}
             {% include 'responsive_table.html' with table=device_table %}
             {% if perms.virtualization.change_cluster %}
-                <div class="panel-footer">
+                <div class="panel-footer noprint">
                     <div class="pull-right">
                         <a href="{% url 'virtualization:cluster_add_devices' pk=cluster.pk %}" class="btn btn-primary btn-xs">
                             <span class="glyphicon glyphicon-plus" aria-hidden="true"></span>

Niektóre pliki nie zostały wyświetlone z powodu dużej ilości zmienionych plików