Просмотр исходного кода

Merge branch 'develop' into develop-2.6

Jeremy Stretch 7 лет назад
Родитель
Сommit
0b95016e00
100 измененных файлов с 297 добавлено и 207 удалено
  1. 14 1
      CHANGELOG.md
  2. 1 1
      netbox/circuits/tables.py
  3. 7 8
      netbox/dcim/models.py
  4. 8 8
      netbox/dcim/tables.py
  5. 3 1
      netbox/dcim/views.py
  6. 1 1
      netbox/extras/tables.py
  7. 6 6
      netbox/ipam/forms.py
  8. 24 2
      netbox/ipam/models.py
  9. 4 4
      netbox/ipam/tables.py
  10. 1 0
      netbox/netbox/settings.py
  11. 14 1
      netbox/project-static/css/base.css
  12. 4 0
      netbox/project-static/js/forms.js
  13. 1 1
      netbox/secrets/tables.py
  14. 1 1
      netbox/templates/_base.html
  15. 2 2
      netbox/templates/circuits/circuit.html
  16. 2 2
      netbox/templates/circuits/circuit_list.html
  17. 1 1
      netbox/templates/circuits/circuittype_list.html
  18. 3 3
      netbox/templates/circuits/provider.html
  19. 2 2
      netbox/templates/circuits/provider_list.html
  20. 2 2
      netbox/templates/dcim/cable.html
  21. 2 2
      netbox/templates/dcim/cable_list.html
  22. 2 2
      netbox/templates/dcim/console_connections_list.html
  23. 15 15
      netbox/templates/dcim/device.html
  24. 1 1
      netbox/templates/dcim/device_inventory.html
  25. 2 2
      netbox/templates/dcim/device_list.html
  26. 1 1
      netbox/templates/dcim/devicerole_list.html
  27. 2 2
      netbox/templates/dcim/devicetype.html
  28. 2 2
      netbox/templates/dcim/devicetype_list.html
  29. 1 1
      netbox/templates/dcim/inc/consoleport.html
  30. 1 1
      netbox/templates/dcim/inc/consoleserverport.html
  31. 1 2
      netbox/templates/dcim/inc/devicebay.html
  32. 1 1
      netbox/templates/dcim/inc/devicetype_component_table.html
  33. 1 1
      netbox/templates/dcim/inc/frontport.html
  34. 2 2
      netbox/templates/dcim/inc/interface.html
  35. 1 1
      netbox/templates/dcim/inc/inventoryitem.html
  36. 1 1
      netbox/templates/dcim/inc/poweroutlet.html
  37. 1 1
      netbox/templates/dcim/inc/powerport.html
  38. 1 1
      netbox/templates/dcim/inc/rearport.html
  39. 2 2
      netbox/templates/dcim/interface.html
  40. 2 2
      netbox/templates/dcim/interface_connections_list.html
  41. 2 2
      netbox/templates/dcim/inventoryitem_list.html
  42. 1 1
      netbox/templates/dcim/manufacturer_list.html
  43. 1 1
      netbox/templates/dcim/platform_list.html
  44. 2 2
      netbox/templates/dcim/power_connections_list.html
  45. 6 6
      netbox/templates/dcim/rack.html
  46. 2 2
      netbox/templates/dcim/rack_elevation_list.html
  47. 2 2
      netbox/templates/dcim/rack_list.html
  48. 2 2
      netbox/templates/dcim/rackgroup_list.html
  49. 1 1
      netbox/templates/dcim/rackreservation_list.html
  50. 1 1
      netbox/templates/dcim/rackrole_list.html
  51. 2 2
      netbox/templates/dcim/region_list.html
  52. 6 6
      netbox/templates/dcim/site.html
  53. 2 2
      netbox/templates/dcim/site_list.html
  54. 2 2
      netbox/templates/dcim/virtualchassis_list.html
  55. 2 2
      netbox/templates/extras/configcontext.html
  56. 2 2
      netbox/templates/extras/configcontext_list.html
  57. 2 2
      netbox/templates/extras/objectchange.html
  58. 2 2
      netbox/templates/extras/objectchange_list.html
  59. 2 2
      netbox/templates/extras/report.html
  60. 6 0
      netbox/templates/extras/tag.html
  61. 1 1
      netbox/templates/inc/image_attachments.html
  62. 1 1
      netbox/templates/inc/search_panel.html
  63. 2 2
      netbox/templates/ipam/aggregate.html
  64. 2 2
      netbox/templates/ipam/aggregate_list.html
  65. 1 1
      netbox/templates/ipam/inc/service.html
  66. 3 3
      netbox/templates/ipam/ipaddress.html
  67. 2 2
      netbox/templates/ipam/ipaddress_list.html
  68. 2 2
      netbox/templates/ipam/prefix.html
  69. 2 2
      netbox/templates/ipam/prefix_list.html
  70. 2 2
      netbox/templates/ipam/rir_list.html
  71. 1 1
      netbox/templates/ipam/role_list.html
  72. 1 1
      netbox/templates/ipam/service.html
  73. 2 2
      netbox/templates/ipam/service_list.html
  74. 3 3
      netbox/templates/ipam/vlan.html
  75. 2 2
      netbox/templates/ipam/vlan_list.html
  76. 2 2
      netbox/templates/ipam/vlangroup_list.html
  77. 1 1
      netbox/templates/ipam/vlangroup_vlans.html
  78. 2 2
      netbox/templates/ipam/vrf.html
  79. 2 2
      netbox/templates/ipam/vrf_list.html
  80. 1 1
      netbox/templates/secrets/inc/private_key_modal.html
  81. 1 1
      netbox/templates/secrets/inc/secret_tr.html
  82. 3 3
      netbox/templates/secrets/secret.html
  83. 2 2
      netbox/templates/secrets/secret_list.html
  84. 1 1
      netbox/templates/secrets/secretrole_list.html
  85. 2 2
      netbox/templates/tenancy/tenant.html
  86. 2 2
      netbox/templates/tenancy/tenant_list.html
  87. 1 1
      netbox/templates/tenancy/tenantgroup_list.html
  88. 1 1
      netbox/templates/users/api_tokens.html
  89. 2 2
      netbox/templates/users/userkey.html
  90. 2 2
      netbox/templates/utilities/obj_table.html
  91. 3 3
      netbox/templates/virtualization/cluster.html
  92. 1 1
      netbox/templates/virtualization/cluster_add_devices.html
  93. 2 2
      netbox/templates/virtualization/cluster_list.html
  94. 1 1
      netbox/templates/virtualization/clustergroup_list.html
  95. 1 1
      netbox/templates/virtualization/clustertype_list.html
  96. 4 4
      netbox/templates/virtualization/virtualmachine.html
  97. 2 2
      netbox/templates/virtualization/virtualmachine_list.html
  98. 1 1
      netbox/tenancy/tables.py
  99. 18 7
      netbox/utilities/api.py
  100. 21 1
      netbox/utilities/custom_inspectors.py

+ 14 - 1
CHANGELOG.md

@@ -30,20 +30,33 @@ to now use "Extras | Tag."
 
 ---
 
-v2.5.8 (FUTURE)
+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
 
 ---
 

+ 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):

+ 7 - 8
netbox/dcim/models.py

@@ -2550,16 +2550,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:

+ 1 - 1
netbox/extras/tables.py

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

+ 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, TaggedItem
+from extras.models import CustomFieldModel, ObjectChange, TaggedItem
 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=''
     )
 

+ 1 - 0
netbox/netbox/settings.py

@@ -316,6 +316,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',

+ 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>
@@ -460,7 +460,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
@@ -494,7 +494,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>
@@ -522,7 +522,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
@@ -583,7 +583,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
@@ -639,7 +639,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
@@ -696,7 +696,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
@@ -753,7 +753,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

@@ -34,7 +34,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

@@ -43,7 +43,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 - 2
netbox/templates/dcim/inc/devicebay.html

@@ -40,8 +40,7 @@
         <td colspan="2"></td>
     {% endif %}
 
-    {# Actions #}
-    <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

@@ -43,7 +43,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

@@ -34,7 +34,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>
     {% include 'inc/created_updated.html' with obj=tag %}

+ 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>

+ 1 - 1
netbox/templates/virtualization/cluster_add_devices.html

@@ -47,7 +47,7 @@
             </div>
         </div>
         <div class="row">
-            <div class="col-md-6 col-md-offset-3 text-right">
+            <div class="col-md-6 col-md-offset-3 text-right noprint">
                 <button type="submit" name="_add" class="btn btn-primary">Add Devices</button>
                 <a href="{{ return_url }}" class="btn btn-default">Cancel</a>
             </div>

+ 2 - 2
netbox/templates/virtualization/cluster_list.html

@@ -2,7 +2,7 @@
 {% load buttons %}
 
 {% block content %}
-<div class="pull-right">
+<div class="pull-right noprint">
     {% if perms.virtualization.add_cluster %}
         {% add_button 'virtualization:cluster_add' %}
         {% import_button 'virtualization:cluster_import' %}
@@ -14,7 +14,7 @@
 	<div class="col-md-9">
         {% include 'utilities/obj_table.html' with bulk_edit_url='virtualization:cluster_bulk_edit' bulk_delete_url='virtualization:cluster_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/virtualization/clustergroup_list.html

@@ -2,7 +2,7 @@
 {% load buttons %}
 
 {% block content %}
-<div class="pull-right">
+<div class="pull-right noprint">
     {% if perms.virtualization.add_clustergroup %}
         {% add_button 'virtualization:clustergroup_add' %}
         {% import_button 'virtualization:clustergroup_import' %}

+ 1 - 1
netbox/templates/virtualization/clustertype_list.html

@@ -2,7 +2,7 @@
 {% load buttons %}
 
 {% block content %}
-<div class="pull-right">
+<div class="pull-right noprint">
     {% if perms.virtualization.add_clustertype %}
         {% add_button 'virtualization:clustertype_add' %}
         {% import_button 'virtualization:clustertype_import' %}

+ 4 - 4
netbox/templates/virtualization/virtualmachine.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">
                 {% if virtualmachine.cluster %}
@@ -24,7 +24,7 @@
             </form>
         </div>
     </div>
-    <div class="pull-right">
+    <div class="pull-right noprint">
         {% if perms.virtualization.change_virtualmachine %}
             <a href="{% url 'virtualization:virtualmachine_edit' pk=virtualmachine.pk %}" class="btn btn-warning">
                 <span class="fa fa-pencil"></span>
@@ -221,7 +221,7 @@
                 </div>
             {% endif %}
             {% if perms.ipam.add_service %}
-                <div class="panel-footer text-right">
+                <div class="panel-footer text-right noprint">
                     <a href="{% url 'virtualization:virtualmachine_service_assign' virtualmachine=virtualmachine.pk %}" class="btn btn-xs btn-primary">
                         <span class="glyphicon glyphicon-plus" aria-hidden="true"></span> Assign service
                     </a>
@@ -271,7 +271,7 @@
                 </tbody>
             </table>
             {% if perms.dcim.add_interface or perms.dcim.delete_interface %}
-                <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={{ virtualmachine.get_absolute_url }}" class="btn btn-warning btn-xs">
                             <span class="glyphicon glyphicon-pencil" aria-hidden="true"></span> Rename

+ 2 - 2
netbox/templates/virtualization/virtualmachine_list.html

@@ -2,7 +2,7 @@
 {% load buttons %}
 
 {% block content %}
-<div class="pull-right">
+<div class="pull-right noprint">
     {% if perms.virtualization.add_virtualmachine %}
         {% add_button 'virtualization:virtualmachine_add' %}
         {% import_button 'virtualization:virtualmachine_import' %}
@@ -14,7 +14,7 @@
 	<div class="col-md-9">
         {% include 'virtualization/inc/virtualmachine_table.html' with bulk_edit_url='virtualization:virtualmachine_bulk_edit' bulk_delete_url='virtualization:virtualmachine_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/tenancy/tables.py

@@ -31,7 +31,7 @@ class TenantGroupTable(BaseTable):
     tenant_count = tables.Column(verbose_name='Tenants')
     slug = tables.Column(verbose_name='Slug')
     actions = tables.TemplateColumn(
-        template_code=TENANTGROUP_ACTIONS, attrs={'td': {'class': 'text-right'}}, verbose_name=''
+        template_code=TENANTGROUP_ACTIONS, attrs={'td': {'class': 'text-right noprint'}}, verbose_name=''
     )
 
     class Meta(BaseTable.Meta):

+ 18 - 7
netbox/utilities/api.py

@@ -8,7 +8,7 @@ from django.db.models import ManyToManyField
 from django.http import Http404
 from rest_framework.exceptions import APIException
 from rest_framework.permissions import BasePermission
-from rest_framework.relations import PrimaryKeyRelatedField
+from rest_framework.relations import PrimaryKeyRelatedField, RelatedField
 from rest_framework.response import Response
 from rest_framework.serializers import Field, ModelSerializer, ValidationError
 from rest_framework.viewsets import ModelViewSet as _ModelViewSet, ViewSet
@@ -101,19 +101,30 @@ class ChoiceField(Field):
         return data
 
 
-class ContentTypeField(Field):
+class ContentTypeField(RelatedField):
     """
     Represent a ContentType as '<app_label>.<model>'
     """
-    def to_representation(self, obj):
-        return "{}.{}".format(obj.app_label, obj.model)
+    default_error_messages = {
+        "does_not_exist": "Invalid content type: {content_type}",
+        "invalid": "Invalid value. Specify a content type as '<app_label>.<model_name>'.",
+    }
+
+    # Can't set this as an attribute because it raises an exception when the field is read-only
+    def get_queryset(self):
+        return ContentType.objects.all()
 
     def to_internal_value(self, data):
-        app_label, model = data.split('.')
         try:
+            app_label, model = data.split('.')
             return ContentType.objects.get_by_natural_key(app_label=app_label, model=model)
-        except ContentType.DoesNotExist:
-            raise ValidationError("Invalid content type")
+        except ObjectDoesNotExist:
+            self.fail('does_not_exist', content_type=data)
+        except (TypeError, ValueError):
+            self.fail('invalid')
+
+    def to_representation(self, obj):
+        return "{}.{}".format(obj.app_label, obj.model)
 
 
 class TimeZoneField(Field):

+ 21 - 1
netbox/utilities/custom_inspectors.py

@@ -1,14 +1,24 @@
 from drf_yasg import openapi
 from drf_yasg.inspectors import FieldInspector, NotHandled, PaginatorInspector, FilterInspector, SwaggerAutoSchema
+from drf_yasg.utils import get_serializer_ref_name
 from rest_framework.fields import ChoiceField
 from rest_framework.relations import ManyRelatedField
 from taggit_serializer.serializers import TagListSerializerField
 
+from dcim.api.serializers import InterfaceSerializer as DeviceInterfaceSerializer
+from virtualization.api.serializers import InterfaceSerializer as VirtualMachineInterfaceSerializer
 from extras.api.customfields import CustomFieldsSerializer
 from utilities.api import ChoiceField, SerializedPKRelatedField, WritableNestedSerializer
 
 
+# this might be ugly, but it limits drf_yasg-specific code to this file
+DeviceInterfaceSerializer.Meta.ref_name = 'DeviceInterface'
+VirtualMachineInterfaceSerializer.Meta.ref_name = 'VirtualMachineInterface'
+
+
 class NetBoxSwaggerAutoSchema(SwaggerAutoSchema):
+    writable_serializers = {}
+
     def get_request_serializer(self):
         serializer = super().get_request_serializer()
 
@@ -21,7 +31,17 @@ class NetBoxSwaggerAutoSchema(SwaggerAutoSchema):
                     properties[child_name] = None
 
             if properties:
-                writable_class = type('Writable' + type(serializer).__name__, (type(serializer),), properties)
+                if type(serializer) not in self.writable_serializers:
+                    writable_name = 'Writable' + type(serializer).__name__
+                    meta_class = getattr(type(serializer), 'Meta', None)
+                    if meta_class:
+                        ref_name = 'Writable' + get_serializer_ref_name(serializer)
+                        writable_meta = type('Meta', (meta_class,), {'ref_name': ref_name})
+                        properties['Meta'] = writable_meta
+
+                    self.writable_serializers[type(serializer)] = type(writable_name, (type(serializer),), properties)
+
+                writable_class = self.writable_serializers[type(serializer)]
                 serializer = writable_class()
 
         return serializer

Некоторые файлы не были показаны из-за большого количества измененных файлов