Ver Fonte

Introduce generic object template

Jeremy Stretch há 5 anos atrás
pai
commit
51120ccf31

+ 5 - 54
netbox/templates/circuits/circuit.html

@@ -1,60 +1,11 @@
-{% extends 'base.html' %}
-{% load buttons %}
-{% load custom_links %}
+{% extends 'generic/object.html' %}
 {% load helpers %}
 {% load plugins %}
 
-{% block title %}{{ object }}{% endblock %}
-
-{% block header %}
-    <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>
-                <li><a href="{% url 'circuits:circuit_list' %}?provider={{ object.provider.slug }}">{{ object.provider }}</a></li>
-                <li>{{ object.cid }}</li>
-            </ol>
-        </div>
-        <div class="col-sm-4 col-md-3">
-            <form action="{% url 'circuits:circuit_list' %}" method="get">
-                <div class="input-group">
-                    <input type="text" name="q" class="form-control" />
-                    <span class="input-group-btn">
-                        <button type="submit" class="btn btn-primary">
-                            <span class="mdi mdi-magnify" aria-hidden="true"></span>
-                        </button>
-                    </span>
-                </div>
-            </form>
-        </div>
-    </div>
-    <div class="pull-right noprint">
-        {% plugin_buttons object %}
-        {% if perms.circuits.add_circuit %}
-            {% clone_button object %}
-        {% endif %}
-        {% if perms.circuits.change_circuit %}
-            {% edit_button object %}
-        {% endif %}
-        {% if perms.circuits.delete_circuit %}
-            {% delete_button object %}
-        {% endif %}
-    </div>
-    <h1>{{ object }}</h1>
-    {% include 'inc/created_updated.html' %}
-    <div class="pull-right noprint">
-        {% custom_links object %}
-    </div>
-    <ul class="nav nav-tabs">
-        <li role="presentation"{% if not active_tab %} class="active"{% endif %}>
-            <a href="{{ object.get_absolute_url }}">Circuit</a>
-        </li>
-        {% if perms.extras.view_objectchange %}
-            <li role="presentation"{% if active_tab == 'changelog' %} class="active"{% endif %}>
-                <a href="{% url 'circuits:circuit_changelog' pk=object.pk %}">Change Log</a>
-            </li>
-        {% endif %}
-    </ul>
+{% block breadcrumbs %}
+  <li><a href="{% url 'circuits:circuit_list' %}">Circuits</a></li>
+  <li><a href="{% url 'circuits:circuit_list' %}?provider={{ object.provider.slug }}">{{ object.provider }}</a></li>
+  <li>{{ object.cid }}</li>
 {% endblock %}
 
 {% block content %}

+ 4 - 53
netbox/templates/circuits/provider.html

@@ -1,60 +1,11 @@
-{% extends 'base.html' %}
-{% load buttons %}
+{% extends 'generic/object.html' %}
 {% load static %}
-{% load custom_links %}
 {% load helpers %}
 {% load plugins %}
 
-{% block title %}{{ object }}{% endblock %}
-
-{% block header %}
-    <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>
-                <li>{{ object }}</li>
-            </ol>
-        </div>
-        <div class="col-sm-4 col-md-3">
-            <form action="{% url 'circuits:provider_list' %}" method="get">
-                <div class="input-group">
-                    <input type="text" name="q" class="form-control" />
-                    <span class="input-group-btn">
-                        <button type="submit" class="btn btn-primary">
-                            <span class="mdi mdi-magnify" aria-hidden="true"></span>
-                        </button>
-                    </span>
-                </div>
-            </form>
-        </div>
-    </div>
-    <div class="pull-right noprint">
-        {% plugin_buttons object %}
-        {% if perms.circuits.add_provider %}
-            {% clone_button object %}
-        {% endif %}
-        {% if perms.circuits.change_provider %}
-            {% edit_button object %}
-        {% endif %}
-        {% if perms.circuits.delete_provider %}
-            {% delete_button object %}
-        {% endif %}
-    </div>
-    <h1>{{ object }}</h1>
-    {% include 'inc/created_updated.html' %}
-    <div class="pull-right noprint">
-        {% custom_links object %}
-    </div>
-    <ul class="nav nav-tabs">
-        <li role="presentation"{% if not active_tab %} class="active"{% endif %}>
-            <a href="{{ object.get_absolute_url }}">Provider</a>
-        </li>
-        {% if perms.extras.view_objectchange %}
-            <li role="presentation"{% if active_tab == 'changelog' %} class="active"{% endif %}>
-                <a href="{% url 'circuits:provider_changelog' pk=object.pk %}">Change Log</a>
-            </li>
-        {% endif %}
-    </ul>
+{% block breadcrumbs %}
+  <li><a href="{% url 'circuits:provider_list' %}">Providers</a></li>
+  <li>{{ object }}</li>
 {% endblock %}
 
 {% block content %}

+ 14 - 35
netbox/templates/dcim/cable.html

@@ -1,42 +1,21 @@
-{% extends 'base.html' %}
+{% extends 'generic/object.html' %}
 {% load buttons %}
-{% load custom_links %}
 {% load helpers %}
+{% load perms %}
 {% load plugins %}
 
-{% block header %}
-    <div class="row noprint">
-        <div class="col-md-12">
-            <ol class="breadcrumb">
-                <li><a href="{% url 'dcim:cable_list' %}">Cables</a></li>
-                <li>{{ object }}</li>
-            </ol>
-        </div>
-    </div>
-    <div class="pull-right noprint">
-        {% plugin_buttons object %}
-        {% if perms.dcim.change_cable %}
-            {% edit_button object %}
-        {% endif %}
-        {% if perms.dcim.delete_cable %}
-            {% delete_button object %}
-        {% endif %}
-    </div>
-    <h1>{% block title %}Cable {{ object }}{% endblock %}</h1>
-    {% include 'inc/created_updated.html' %}
-    <div class="pull-right noprint">
-        {% custom_links object %}
-    </div>
-    <ul class="nav nav-tabs">
-        <li role="presentation"{% if not active_tab %} class="active"{% endif %}>
-            <a href="{{ object.get_absolute_url }}">Cable</a>
-        </li>
-        {% if perms.extras.view_objectchange %}
-            <li role="presentation"{% if active_tab == 'changelog' %} class="active"{% endif %}>
-                <a href="{% url 'dcim:cable_changelog' pk=object.pk %}">Change Log</a>
-            </li>
-        {% endif %}
-    </ul>
+{% block breadcrumbs %}
+  <li><a href="{% url 'dcim:cable_list' %}">Cables</a></li>
+  <li>{{ object }}</li>
+{% endblock %}
+
+{% block buttons %}
+  {% if request.user|can_change:object %}
+    {% edit_button object %}
+  {% endif %}
+  {% if request.user|can_delete:object %}
+    {% delete_button object %}
+  {% endif %}
 {% endblock %}
 
 {% block content %}

+ 54 - 76
netbox/templates/dcim/device/base.html

@@ -1,91 +1,69 @@
-{% extends 'base.html' %}
+{% extends 'generic/object.html' %}
 {% load buttons %}
 {% load static %}
 {% load helpers %}
 {% load custom_links %}
 {% load plugins %}
 
-{% block title %}{{ object }}{% endblock %}
+{% block breadcrumbs %}
+  <li><a href="{% url 'dcim:device_list' %}">Devices</a></li>
+  <li><a href="{% url 'dcim:device_list' %}?site={{ object.site.slug }}">{{ object.site }}</a></li>
+  {% if object.parent_bay %}
+    <li><a href="{% url 'dcim:device' pk=object.parent_bay.device.pk %}">{{ object.parent_bay.device }}</a></li>
+    <li>{{ object.parent_bay }}</li>
+  {% endif %}
+  <li>{{ object }}</li>
+{% endblock %}
 
-{% block header %}
-    <div class="row noprint">
-        <div class="col-sm-8 col-md-9">
-        <ol class="breadcrumb">
-            <li><a href="{% url 'dcim:device_list' %}">Devices</a></li>
-            <li><a href="{% url 'dcim:device_list' %}?site={{ object.site.slug }}">{{ object.site }}</a></li>
-            {% if object.parent_bay %}
-                <li><a href="{% url 'dcim:device' pk=object.parent_bay.device.pk %}">{{ object.parent_bay.device }}</a></li>
-                <li>{{ object.parent_bay }}</li>
-            {% endif %}
-            <li>{{ object }}</li>
-        </ol>
-        </div>
-        <div class="col-sm-4 col-md-3">
-            <form action="{% url 'dcim:device_list' %}" method="get">
-                <div class="input-group">
-                    <input type="text" name="q" class="form-control" placeholder="Search devices" />
-                    <span class="input-group-btn">
-                        <button type="submit" class="btn btn-primary">
-                            <span class="mdi mdi-magnify" aria-hidden="true"></span>
-                        </button>
-                    </span>
-                </div>
-            </form>
-        </div>
-    </div>
-    <div class="pull-right noprint">
-        {% plugin_buttons object %}
-        {% 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">
-                    <span class="mdi mdi-plus-thick" aria-hidden="true"></span> Add Components <span class="caret"></span>
-                </button>
-                <ul class="dropdown-menu">
-                    {% if perms.dcim.add_consoleport %}
-                        <li><a href="{% url 'dcim:consoleport_add' %}?device={{ object.pk }}&return_url={% url 'dcim:device_consoleports' pk=object.pk %}">Console Ports</a></li>
-                    {% endif %}
-                    {% if perms.dcim.add_consoleserverport %}
-                        <li><a href="{% url 'dcim:consoleserverport_add' %}?device={{ object.pk }}&return_url={% url 'dcim:device_consoleserverports' pk=object.pk %}">Console Server Ports</a></li>
-                    {% endif %}
-                    {% if perms.dcim.add_powerport %}
-                        <li><a href="{% url 'dcim:powerport_add' %}?device={{ object.pk }}&return_url={% url 'dcim:device_powerports' pk=object.pk %}">Power Ports</a></li>
-                    {% endif %}
-                    {% if perms.dcim.add_poweroutlet %}
-                        <li><a href="{% url 'dcim:poweroutlet_add' %}?device={{ object.pk }}&return_url={% url 'dcim:device_poweroutlets' pk=object.pk %}">Power Outlets</a></li>
-                    {% endif %}
-                    {% if perms.dcim.add_interface %}
-                        <li><a href="{% url 'dcim:interface_add' %}?device={{ object.pk }}&return_url={% url 'dcim:device_interfaces' pk=object.pk %}">Interfaces</a></li>
-                    {% endif %}
-                    {% if perms.dcim.add_frontport %}
-                        <li><a href="{% url 'dcim:frontport_add' %}?device={{ object.pk }}&return_url={% url 'dcim:device_frontports' pk=object.pk %}">Front Ports</a></li>
-                    {% endif %}
-                    {% if perms.dcim.add_rearport %}
-                        <li><a href="{% url 'dcim:rearport_add' %}?device={{ object.pk }}&return_url={% url 'dcim:device_rearports' pk=object.pk %}">Rear Ports</a></li>
-                    {% endif %}
-                    {% if perms.dcim.add_devicebay %}
-                        <li><a href="{% url 'dcim:devicebay_add' %}?device={{ object.pk }}&return_url={% url 'dcim:device_devicebays' pk=object.pk %}">Device Bays</a></li>
-                    {% endif %}
-                    {% if perms.dcim.add_inventoryitem %}
-                        <li><a href="{% url 'dcim:inventoryitem_add' %}?device={{ object.pk }}&return_url={% url 'dcim:device_inventory' pk=object.pk %}">Inventory Items</a></li>
-                    {% endif %}
-                </ul>
-            </div>
+{% block buttons %}
+  {% 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">
+        <span class="mdi mdi-plus-thick" aria-hidden="true"></span> Add Components <span class="caret"></span>
+      </button>
+      <ul class="dropdown-menu">
+        {% if perms.dcim.add_consoleport %}
+          <li><a href="{% url 'dcim:consoleport_add' %}?device={{ object.pk }}&return_url={% url 'dcim:device_consoleports' pk=object.pk %}">Console Ports</a></li>
         {% endif %}
-        {% if perms.dcim.add_device %}
-            {% clone_button object %}
+        {% if perms.dcim.add_consoleserverport %}
+          <li><a href="{% url 'dcim:consoleserverport_add' %}?device={{ object.pk }}&return_url={% url 'dcim:device_consoleserverports' pk=object.pk %}">Console Server Ports</a></li>
         {% endif %}
-        {% if perms.dcim.change_device %}
-            {% edit_button object %}
+        {% if perms.dcim.add_powerport %}
+          <li><a href="{% url 'dcim:powerport_add' %}?device={{ object.pk }}&return_url={% url 'dcim:device_powerports' pk=object.pk %}">Power Ports</a></li>
         {% endif %}
-        {% if perms.dcim.delete_device %}
-            {% delete_button object %}
+        {% if perms.dcim.add_poweroutlet %}
+          <li><a href="{% url 'dcim:poweroutlet_add' %}?device={{ object.pk }}&return_url={% url 'dcim:device_poweroutlets' pk=object.pk %}">Power Outlets</a></li>
         {% endif %}
+        {% if perms.dcim.add_interface %}
+          <li><a href="{% url 'dcim:interface_add' %}?device={{ object.pk }}&return_url={% url 'dcim:device_interfaces' pk=object.pk %}">Interfaces</a></li>
+        {% endif %}
+        {% if perms.dcim.add_frontport %}
+          <li><a href="{% url 'dcim:frontport_add' %}?device={{ object.pk }}&return_url={% url 'dcim:device_frontports' pk=object.pk %}">Front Ports</a></li>
+        {% endif %}
+        {% if perms.dcim.add_rearport %}
+          <li><a href="{% url 'dcim:rearport_add' %}?device={{ object.pk }}&return_url={% url 'dcim:device_rearports' pk=object.pk %}">Rear Ports</a></li>
+        {% endif %}
+        {% if perms.dcim.add_devicebay %}
+          <li><a href="{% url 'dcim:devicebay_add' %}?device={{ object.pk }}&return_url={% url 'dcim:device_devicebays' pk=object.pk %}">Device Bays</a></li>
+        {% endif %}
+        {% if perms.dcim.add_inventoryitem %}
+          <li><a href="{% url 'dcim:inventoryitem_add' %}?device={{ object.pk }}&return_url={% url 'dcim:device_inventory' pk=object.pk %}">Inventory Items</a></li>
+        {% endif %}
+      </ul>
     </div>
-    <h1>{{ object }}</h1>
-    {% include 'inc/created_updated.html' %}
-    <div class="pull-right noprint">
-        {% custom_links object %}
-    </div>
+  {% endif %}
+  {% if perms.dcim.add_device %}
+      {% clone_button object %}
+  {% endif %}
+  {% if perms.dcim.change_device %}
+      {% edit_button object %}
+  {% endif %}
+  {% if perms.dcim.delete_device %}
+      {% delete_button object %}
+  {% endif %}
+{% endblock %}
+
+{% block tabs %}
     <ul class="nav nav-tabs">
         <li role="presentation" {% if active_tab == 'device' %} class="active"{% endif %}>
             <a href="{% url 'dcim:device' pk=object.pk %}">Device</a>

+ 10 - 39
netbox/templates/dcim/device_component.html

@@ -1,44 +1,15 @@
-{% extends 'base.html' %}
+{% extends 'generic/object.html' %}
 {% load helpers %}
 {% load perms %}
 {% load plugins %}
 
-{% block header %}
-    <div class="row noprint">
-        <div class="col-md-12">
-            <ol class="breadcrumb">
-                <li><a href="{% url 'dcim:device_list' %}">Devices</a></li>
-                <li><a href="{{ object.device.get_absolute_url }}">{{ object.device }}</a></li>
-                {% if breadcrumb_url %}
-                    <li><a href="{% url breadcrumb_url pk=object.device.pk %}">{{ object|meta:"verbose_name_plural"|bettertitle }}</a></li>
-                {% endif %}
-                <li>{{ object }}</li>
-            </ol>
-        </div>
-    </div>
-    <div class="pull-right noprint">
-        {% plugin_buttons object %}
-        {% if request.user|can_change:object %}
-            <a href="{% url object|viewname:"edit" pk=object.pk %}" class="btn btn-warning">
-                <span class="mdi mdi-pencil" aria-hidden="true"></span> Edit
-            </a>
-        {% endif %}
-        {% if request.user|can_delete:object %}
-            <a href="{% url object|viewname:"delete" pk=object.pk %}" class="btn btn-danger">
-                <span class="mdi mdi-trash-can-outline" aria-hidden="true"></span> Delete
-            </a>
-        {% endif %}
-    </div>
-    <h1>{% block title %}{{ object.device }} / {{ object }}{% endblock %}</h1>
-    {% include 'inc/created_updated.html' %}
-    <ul class="nav nav-tabs">
-        <li role="presentation"{% if not active_tab %} class="active"{% endif %}>
-            <a href="{{ object.get_absolute_url }}">{{ object|meta:"verbose_name"|bettertitle }}</a>
-        </li>
-        {% if perms.extras.view_objectchange %}
-            <li role="presentation"{% if active_tab == 'changelog' %} class="active"{% endif %}>
-                <a href="{% url object|viewname:"changelog" pk=object.pk %}">Change Log</a>
-            </li>
-        {% endif %}
-    </ul>
+{% block title %}{{ object.device }} / {{ object }}{% endblock %}
+
+{% block breadcrumbs %}
+  <li><a href="{% url 'dcim:device_list' %}">Devices</a></li>
+  <li><a href="{{ object.device.get_absolute_url }}">{{ object.device }}</a></li>
+  {% if breadcrumb_url %}
+    <li><a href="{% url breadcrumb_url pk=object.device.pk %}">{{ object|meta:"verbose_name_plural"|bettertitle }}</a></li>
+  {% endif %}
+  <li>{{ object }}</li>
 {% endblock %}

+ 45 - 67
netbox/templates/dcim/devicetype.html

@@ -1,81 +1,59 @@
-{% extends 'base.html' %}
+{% extends 'generic/object.html' %}
 {% load buttons %}
-{% load custom_links %}
 {% load helpers %}
 {% load plugins %}
 
 {% block title %}{{ object.manufacturer }} {{ object.model }}{% endblock %}
 
-{% block header %}
-    <div class="row noprint">
-        <div class="col-md-12">
-            <ol class="breadcrumb">
-                <li><a href="{% url 'dcim:devicetype_list' %}">Device Types</a></li>
-                <li><a href="{% url 'dcim:devicetype_list' %}?manufacturer={{ object.manufacturer.slug }}">{{ object.manufacturer }}</a></li>
-                <li>{{ object.model }}</li>
-            </ol>
-        </div>
-    </div>
-    <div class="pull-right noprint">
-        {% plugin_buttons object %}
-        {% 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">
-                    <span class="mdi mdi-plus-thick" aria-hidden="true"></span> Add Components <span class="caret"></span>
-                </button>
-                <ul class="dropdown-menu">
-                    {% if perms.dcim.add_consoleporttemplate %}
-                        <li><a href="{% url 'dcim:consoleporttemplate_add' %}?device_type={{ object.pk }}&return_url={{ object.get_absolute_url }}%23tab_consoleports">Console Ports</a></li>
-                    {% endif %}
-                    {% if perms.dcim.add_consoleserverporttemplate %}
-                        <li><a href="{% url 'dcim:consoleserverporttemplate_add' %}?device_type={{ object.pk }}&return_url={{ object.get_absolute_url }}%23tab_consoleserverports">Console Server Ports</a></li>
-                    {% endif %}
-                    {% if perms.dcim.add_powerporttemplate %}
-                        <li><a href="{% url 'dcim:powerporttemplate_add' %}?device_type={{ object.pk }}&return_url={{ object.get_absolute_url }}%23tab_powerports">Power Ports</a></li>
-                    {% endif %}
-                    {% if perms.dcim.add_poweroutlettemplate %}
-                        <li><a href="{% url 'dcim:poweroutlettemplate_add' %}?device_type={{ object.pk }}&return_url={{ object.get_absolute_url }}%23tab_poweroutlets">Power Outlets</a></li>
-                    {% endif %}
-                    {% if perms.dcim.add_interfacetemplate %}
-                        <li><a href="{% url 'dcim:interfacetemplate_add' %}?device_type={{ object.pk }}&return_url={{ object.get_absolute_url }}%23tab_interfaces">Interfaces</a></li>
-                    {% endif %}
-                    {% if perms.dcim.add_frontporttemplate %}
-                        <li><a href="{% url 'dcim:frontporttemplate_add' %}?device_type={{ object.pk }}&return_url={{ object.get_absolute_url }}%23tab_frontports">Front Ports</a></li>
-                    {% endif %}
-                    {% if perms.dcim.add_rearporttemplate %}
-                        <li><a href="{% url 'dcim:rearporttemplate_add' %}?device_type={{ object.pk }}&return_url={{ object.get_absolute_url }}%23tab_rearports">Rear Ports</a></li>
-                    {% endif %}
-                    {% if perms.dcim.add_devicebaytemplate %}
-                        <li><a href="{% url 'dcim:devicebaytemplate_add' %}?device_type={{ object.pk }}&return_url={{ object.get_absolute_url }}%23tab_devicebays">Device Bays</a></li>
-                    {% endif %}
-                </ul>
-            </div>
+{% block breadcrumbs %}
+  <li><a href="{% url 'dcim:devicetype_list' %}">Device Types</a></li>
+  <li><a href="{% url 'dcim:devicetype_list' %}?manufacturer={{ object.manufacturer.slug }}">{{ object.manufacturer }}</a></li>
+  <li>{{ object.model }}</li>
+{% endblock %}
+
+{% block buttons %}
+  {% 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">
+        <span class="mdi mdi-plus-thick" aria-hidden="true"></span> Add Components <span class="caret"></span>
+      </button>
+      <ul class="dropdown-menu">
+        {% if perms.dcim.add_consoleporttemplate %}
+          <li><a href="{% url 'dcim:consoleporttemplate_add' %}?device_type={{ object.pk }}&return_url={{ object.get_absolute_url }}%23tab_consoleports">Console Ports</a></li>
         {% endif %}
-        {% if perms.dcim.add_devicetype %}
-            {% clone_button object %}
+        {% if perms.dcim.add_consoleserverporttemplate %}
+          <li><a href="{% url 'dcim:consoleserverporttemplate_add' %}?device_type={{ object.pk }}&return_url={{ object.get_absolute_url }}%23tab_consoleserverports">Console Server Ports</a></li>
         {% endif %}
-        {% if perms.dcim.change_devicetype %}
-            {% edit_button object %}
+        {% if perms.dcim.add_powerporttemplate %}
+          <li><a href="{% url 'dcim:powerporttemplate_add' %}?device_type={{ object.pk }}&return_url={{ object.get_absolute_url }}%23tab_powerports">Power Ports</a></li>
         {% endif %}
-        {% if perms.dcim.delete_devicetype %}
-            {% delete_button object %}
+        {% if perms.dcim.add_poweroutlettemplate %}
+          <li><a href="{% url 'dcim:poweroutlettemplate_add' %}?device_type={{ object.pk }}&return_url={{ object.get_absolute_url }}%23tab_poweroutlets">Power Outlets</a></li>
         {% endif %}
-    </div>
-    <h1>{{ object.manufacturer }} {{ object.model }}</h1>
-    {% include 'inc/created_updated.html' %}
-    <div class="pull-right noprint">
-        {% custom_links object %}
-    </div>
-    <ul class="nav nav-tabs">
-        <li role="presentation"{% if not active_tab %} class="active"{% endif %}>
-            <a href="{{ object.get_absolute_url }}">Device Type</a>
-        </li>
-        {% if perms.extras.view_objectchange %}
-            <li role="presentation"{% if active_tab == 'changelog' %} class="active"{% endif %}>
-                <a href="{% url 'dcim:devicetype_changelog' pk=object.pk %}">Change Log</a>
-            </li>
+        {% if perms.dcim.add_interfacetemplate %}
+          <li><a href="{% url 'dcim:interfacetemplate_add' %}?device_type={{ object.pk }}&return_url={{ object.get_absolute_url }}%23tab_interfaces">Interfaces</a></li>
+        {% endif %}
+        {% if perms.dcim.add_frontporttemplate %}
+          <li><a href="{% url 'dcim:frontporttemplate_add' %}?device_type={{ object.pk }}&return_url={{ object.get_absolute_url }}%23tab_frontports">Front Ports</a></li>
+        {% endif %}
+        {% if perms.dcim.add_rearporttemplate %}
+          <li><a href="{% url 'dcim:rearporttemplate_add' %}?device_type={{ object.pk }}&return_url={{ object.get_absolute_url }}%23tab_rearports">Rear Ports</a></li>
         {% endif %}
-    </ul>
+        {% if perms.dcim.add_devicebaytemplate %}
+          <li><a href="{% url 'dcim:devicebaytemplate_add' %}?device_type={{ object.pk }}&return_url={{ object.get_absolute_url }}%23tab_devicebays">Device Bays</a></li>
+        {% endif %}
+      </ul>
+    </div>
+  {% endif %}
+  {% if perms.dcim.add_devicetype %}
+    {% clone_button object %}
+  {% endif %}
+  {% if perms.dcim.change_devicetype %}
+    {% edit_button object %}
+  {% endif %}
+  {% if perms.dcim.delete_devicetype %}
+    {% delete_button object %}
+  {% endif %}
 {% endblock %}
 
 {% block content %}

+ 9 - 54
netbox/templates/dcim/powerfeed.html

@@ -1,63 +1,18 @@
-{% extends 'base.html' %}
+{% extends 'generic/object.html' %}
 {% load buttons %}
 {% load static %}
 {% load custom_links %}
 {% load helpers %}
 {% load plugins %}
 
-{% block header %}
-    <div class="row noprint">
-        <div class="col-sm-8 col-md-9">
-            <ol class="breadcrumb">
-                <li><a href="{% url 'dcim:powerfeed_list' %}">Power Feeds</a></li>
-                <li><a href="{{ object.power_panel.site.get_absolute_url }}">{{ object.power_panel.site }}</a></li>
-                <li><a href="{{ object.power_panel.get_absolute_url }}">{{ object.power_panel }}</a></li>
-                {% if object.rack %}
-                    <li><a href="{{ object.rack.get_absolute_url }}">{{ object.rack }}</a></li>
-                {% endif %}
-                <li>{{ object }}</li>
-            </ol>
-        </div>
-        <div class="col-sm-4 col-md-3">
-            <form action="{% url 'dcim:powerfeed_list' %}" method="get">
-                <div class="input-group">
-                    <input type="text" name="q" class="form-control" placeholder="Search power feeds" />
-                    <span class="input-group-btn">
-                        <button type="submit" class="btn btn-primary">
-                            <span class="mdi mdi-magnify" aria-hidden="true"></span>
-                        </button>
-                    </span>
-                </div>
-            </form>
-        </div>
-    </div>
-    <div class="pull-right noprint">
-        {% plugin_buttons object %}
-        {% if perms.dcim.add_powerfeed %}
-            {% clone_button object %}
-        {% endif %}
-        {% if perms.dcim.change_powerfeed %}
-            {% edit_button object %}
-        {% endif %}
-        {% if perms.dcim.delete_powerfeed %}
-            {% delete_button object %}
-        {% endif %}
-    </div>
-    <h1>{% block title %}{{ object }}{% endblock %}</h1>
-    {% include 'inc/created_updated.html' %}
-    <div class="pull-right noprint">
-        {% custom_links object %}
-    </div>
-    <ul class="nav nav-tabs">
-        <li role="presentation"{% if not active_tab %} class="active"{% endif %}>
-            <a href="{{ object.get_absolute_url }}">Cable</a>
-        </li>
-        {% if perms.extras.view_objectchange %}
-            <li role="presentation"{% if active_tab == 'changelog' %} class="active"{% endif %}>
-                <a href="{% url 'dcim:powerfeed_changelog' pk=object.pk %}">Change Log</a>
-            </li>
-        {% endif %}
-    </ul>
+{% block breadcrumbs %}
+  <li><a href="{% url 'dcim:powerfeed_list' %}">Power Feeds</a></li>
+  <li><a href="{{ object.power_panel.site.get_absolute_url }}">{{ object.power_panel.site }}</a></li>
+  <li><a href="{{ object.power_panel.get_absolute_url }}">{{ object.power_panel }}</a></li>
+  {% if object.rack %}
+    <li><a href="{{ object.rack.get_absolute_url }}">{{ object.rack }}</a></li>
+  {% endif %}
+  <li>{{ object }}</li>
 {% endblock %}
 
 {% block content %}

+ 8 - 53
netbox/templates/dcim/powerpanel.html

@@ -1,59 +1,14 @@
-{% extends 'base.html' %}
-{% load buttons %}
-{% load custom_links %}
+{% extends 'generic/object.html' %}
 {% load helpers %}
-{% load static %}
 {% load plugins %}
 
-{% block header %}
-    <div class="row noprint">
-        <div class="col-sm-8 col-md-9">
-            <ol class="breadcrumb">
-                <li><a href="{% url 'dcim:powerpanel_list' %}">Power Panels</a></li>
-                <li><a href="{{ object.site.get_absolute_url }}">{{ object.site }}</a></li>
-                {% if object.rack_group %}
-                    <li><a href="{{ object.rack_group.get_absolute_url }}">{{ object.rack_group }}</a></li>
-                {% endif %}
-                <li>{{ object }}</li>
-            </ol>
-        </div>
-        <div class="col-sm-4 col-md-3">
-            <form action="{% url 'dcim:powerpanel_list' %}" method="get">
-                <div class="input-group">
-                    <input type="text" name="q" class="form-control" placeholder="Search power panels" />
-                    <span class="input-group-btn">
-                        <button type="submit" class="btn btn-primary">
-                            <span class="mdi mdi-magnify" aria-hidden="true"></span>
-                        </button>
-                    </span>
-                </div>
-            </form>
-        </div>
-    </div>
-    <div class="pull-right noprint">
-        {% plugin_buttons object %}
-        {% if perms.dcim.change_powerpanel %}
-            {% edit_button object %}
-        {% endif %}
-        {% if perms.dcim.delete_powerpanel %}
-            {% delete_button object %}
-        {% endif %}
-    </div>
-    <h1>{% block title %}{{ object }}{% endblock %}</h1>
-    {% include 'inc/created_updated.html' %}
-    <div class="pull-right noprint">
-        {% custom_links object %}
-    </div>
-    <ul class="nav nav-tabs">
-        <li role="presentation"{% if not active_tab %} class="active"{% endif %}>
-            <a href="{{ object.get_absolute_url }}">Cable</a>
-        </li>
-        {% if perms.extras.view_objectchange %}
-            <li role="presentation"{% if active_tab == 'changelog' %} class="active"{% endif %}>
-                <a href="{% url 'dcim:powerpanel_changelog' pk=object.pk %}">Change Log</a>
-            </li>
-        {% endif %}
-    </ul>
+{% block breadcrumbs %}
+  <li><a href="{% url 'dcim:powerpanel_list' %}">Power Panels</a></li>
+  <li><a href="{{ object.site.get_absolute_url }}">{{ object.site }}</a></li>
+  {% if object.rack_group %}
+    <li><a href="{{ object.rack_group.get_absolute_url }}">{{ object.rack_group }}</a></li>
+  {% endif %}
+  <li>{{ object }}</li>
 {% endblock %}
 
 {% block content %}

+ 38 - 66
netbox/templates/dcim/rack.html

@@ -1,74 +1,46 @@
-{% extends 'base.html' %}
+{% extends 'generic/object.html' %}
 {% load buttons %}
-{% load custom_links %}
 {% load helpers %}
 {% load static %}
 {% load plugins %}
 
-{% block header %}
-    <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>
-                <li><a href="{% url 'dcim:rack_list' %}?site={{ object.site.slug }}">{{ object.site }}</a></li>
-                {% if object.group %}
-                    {% for group in object.group.get_ancestors %}
-                        <li><a href="{{ group.get_absolute_url }}">{{ group }}</a></li>
-                    {% endfor %}
-                    <li><a href="{{ object.group.get_absolute_url }}">{{ object.group }}</a></li>
-                {% endif %}
-                <li>{{ object }}</li>
-            </ol>
-        </div>
-        <div class="col-sm-4 col-md-3">
-            <form action="{% url 'dcim:rack_list' %}" method="get">
-                <div class="input-group">
-                    <input type="text" name="q" class="form-control" placeholder="Search racks" />
-                    <span class="input-group-btn">
-                        <button type="submit" class="btn btn-primary">
-                            <span class="mdi mdi-magnify" aria-hidden="true"></span>
-                        </button>
-                    </span>
-                </div>
-            </form>
-        </div>
-    </div>
-    <div class="pull-right noprint">
-        {% plugin_buttons object %}
-        <a {% if prev_rack %}href="{% url 'dcim:rack' pk=prev_rack.pk %}"{% else %}disabled="disabled"{% endif %} class="btn btn-primary">
-            <span class="mdi mdi-chevron-left" aria-hidden="true"></span> Previous Rack
-        </a>
-        <a {% if next_rack %}href="{% url 'dcim:rack' pk=next_rack.pk %}"{% else %}disabled="disabled"{% endif %} class="btn btn-primary">
-            <span class="mdi mdi-chevron-right" aria-hidden="true"></span> Next Rack
-        </a>
-        {% if perms.dcim.add_rack %}
-            {% clone_button object %}
-        {% endif %}
-        {% if perms.dcim.change_rack %}
-            {% edit_button object %}
-        {% endif %}
-        {% if perms.dcim.delete_rack %}
-            {% delete_button object %}
-        {% endif %}
-    </div>
-    <h1>{% block title %}Rack {{ object }}{% endblock %}</h1>
-    {% include 'inc/created_updated.html' %}
-    <div class="pull-right noprint">
-        <button class="btn btn-sm btn-default toggle-images" selected="selected">
-            <span class="mdi mdi-checkbox-marked-circle-outline" aria-hidden="true"></span> Show Images
-        </button>
-        {% custom_links object %}
-    </div>
-    <ul class="nav nav-tabs">
-        <li role="presentation"{% if not active_tab %} class="active"{% endif %}>
-            <a href="{{ object.get_absolute_url }}">Rack</a>
-        </li>
-        {% if perms.extras.view_objectchange %}
-            <li role="presentation"{% if active_tab == 'changelog' %} class="active"{% endif %}>
-                <a href="{% url 'dcim:rack_changelog' pk=object.pk %}">Change Log</a>
-            </li>
-        {% endif %}
-    </ul>
+{% block title %}Rack {{ object }}{% endblock %}
+
+{% block breadcrumbs %}
+  <li><a href="{% url 'dcim:rack_list' %}">Racks</a></li>
+  <li><a href="{% url 'dcim:rack_list' %}?site={{ object.site.slug }}">{{ object.site }}</a></li>
+  {% if object.group %}
+    {% for group in object.group.get_ancestors %}
+      <li><a href="{{ group.get_absolute_url }}">{{ group }}</a></li>
+    {% endfor %}
+    <li><a href="{{ object.group.get_absolute_url }}">{{ object.group }}</a></li>
+  {% endif %}
+  <li>{{ object }}</li>
+{% endblock %}
+
+{% block buttons %}
+  <a {% if prev_rack %}href="{% url 'dcim:rack' pk=prev_rack.pk %}"{% else %}disabled="disabled"{% endif %} class="btn btn-primary">
+    <span class="mdi mdi-chevron-left" aria-hidden="true"></span> Previous Rack
+  </a>
+  <a {% if next_rack %}href="{% url 'dcim:rack' pk=next_rack.pk %}"{% else %}disabled="disabled"{% endif %} class="btn btn-primary">
+    <span class="mdi mdi-chevron-right" aria-hidden="true"></span> Next Rack
+  </a>
+  {% if perms.dcim.add_rack %}
+    {% clone_button object %}
+  {% endif %}
+  {% if perms.dcim.change_rack %}
+    {% edit_button object %}
+  {% endif %}
+  {% if perms.dcim.delete_rack %}
+    {% delete_button object %}
+  {% endif %}
+{% endblock %}
+
+{% block tab_buttons %}
+  <button class="btn btn-sm btn-default toggle-images" selected="selected">
+    <span class="mdi mdi-checkbox-marked-circle-outline" aria-hidden="true"></span> Show Images
+  </button>
+  {{ block.super }}
 {% endblock %}
 
 {% block content %}

+ 14 - 47
netbox/templates/dcim/rackreservation.html

@@ -1,56 +1,23 @@
-{% extends 'base.html' %}
+{% extends 'generic/object.html' %}
 {% load buttons %}
 {% load custom_links %}
 {% load helpers %}
 {% load static %}
 {% load plugins %}
 
-{% block header %}
-    <div class="row noprint">
-        <div class="col-sm-8 col-md-9">
-            <ol class="breadcrumb">
-                <li><a href="{% url 'dcim:rackreservation_list' %}">Rack Reservations</a></li>
-                <li><a href="{{ object.rack.get_absolute_url }}">{{ object.rack }}</a></li>
-                <li>Units {{ object.unit_list }}</li>
-            </ol>
-        </div>
-        <div class="col-sm-4 col-md-3">
-            <form action="{% url 'dcim:rackreservation_list' %}" method="get">
-                <div class="input-group">
-                    <input type="text" name="q" class="form-control" placeholder="Search racks" />
-                    <span class="input-group-btn">
-                        <button type="submit" class="btn btn-primary">
-                            <span class="mdi mdi-magnify" aria-hidden="true"></span>
-                        </button>
-                    </span>
-                </div>
-            </form>
-        </div>
-    </div>
-    <div class="pull-right noprint">
-        {% plugin_buttons object %}
-        {% if perms.dcim.change_rackreservation %}
-            {% edit_button object %}
-        {% endif %}
-        {% if perms.dcim.delete_rackreservation %}
-            {% delete_button object %}
-        {% endif %}
-    </div>
-    <h1>{% block title %}{{ object }}{% endblock %}</h1>
-    {% include 'inc/created_updated.html' %}
-    <div class="pull-right noprint">
-        {% custom_links object %}
-    </div>
-    <ul class="nav nav-tabs">
-        <li role="presentation"{% if not active_tab %} class="active"{% endif %}>
-            <a href="{{ object.get_absolute_url }}">Rack</a>
-        </li>
-        {% if perms.extras.view_objectchange %}
-            <li role="presentation"{% if active_tab == 'changelog' %} class="active"{% endif %}>
-                <a href="{% url 'dcim:rackreservation_changelog' pk=object.pk %}">Change Log</a>
-            </li>
-        {% endif %}
-    </ul>
+{% block breadcrumbs %}
+  <li><a href="{% url 'dcim:rackreservation_list' %}">Rack Reservations</a></li>
+  <li><a href="{{ object.rack.get_absolute_url }}">{{ object.rack }}</a></li>
+  <li>Units {{ object.unit_list }}</li>
+{% endblock %}
+
+{% block buttons %}
+  {% if perms.dcim.change_rackreservation %}
+    {% edit_button object %}
+  {% endif %}
+  {% if perms.dcim.delete_rackreservation %}
+    {% delete_button object %}
+  {% endif %}
 {% endblock %}
 
 {% block content %}

+ 10 - 58
netbox/templates/dcim/site.html

@@ -1,65 +1,17 @@
-{% extends 'base.html' %}
-{% load buttons %}
-{% load custom_links %}
+{% extends 'generic/object.html' %}
 {% load helpers %}
 {% load plugins %}
-{% load static %}
 {% load tz %}
 
-{% block header %}
-    <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>
-                {% if object.region %}
-                    {% for region in object.region.get_ancestors %}
-                        <li><a href="{{ region.get_absolute_url }}">{{ region }}</a></li>
-                    {% endfor %}
-                    <li><a href="{{ object.region.get_absolute_url }}">{{ object.region }}</a></li>
-                {% endif %}
-                <li>{{ object }}</li>
-            </ol>
-        </div>
-        <div class="col-sm-4 col-md-3">
-            <form action="{% url 'dcim:site_list' %}" method="get">
-                <div class="input-group">
-                    <input type="text" name="q" class="form-control" placeholder="Search sites" />
-                    <span class="input-group-btn">
-                        <button type="submit" class="btn btn-primary">
-                            <span class="mdi mdi-magnify" aria-hidden="true"></span>
-                        </button>
-                    </span>
-                </div>
-            </form>
-        </div>
-    </div>
-    <div class="pull-right noprint">
-        {% plugin_buttons object %}
-        {% if perms.dcim.add_site %}
-            {% clone_button object %}
-        {% endif %}
-        {% if perms.dcim.change_site %}
-            {% edit_button object %}
-        {% endif %}
-        {% if perms.dcim.delete_site %}
-            {% delete_button object %}
-        {% endif %}
-    </div>
-    <h1>{% block title %}{{ object }}{% endblock %}</h1>
-    {% include 'inc/created_updated.html' %}
-    <div class="pull-right noprint">
-        {% custom_links object %}
-    </div>
-    <ul class="nav nav-tabs">
-        <li role="presentation"{% if not active_tab %} class="active"{% endif %}>
-            <a href="{{ object.get_absolute_url }}">Site</a>
-        </li>
-        {% if perms.extras.view_objectchange %}
-            <li role="presentation"{% if active_tab == 'changelog' %} class="active"{% endif %}>
-                <a href="{% url 'dcim:site_changelog' pk=object.pk %}">Change Log</a>
-            </li>
-        {% endif %}
-    </ul>
+{% block breadcrumbs %}
+  <li><a href="{% url 'dcim:site_list' %}">Sites</a></li>
+  {% if object.region %}
+    {% for region in object.region.get_ancestors %}
+      <li><a href="{{ region.get_absolute_url }}">{{ region }}</a></li>
+    {% endfor %}
+    <li><a href="{{ object.region.get_absolute_url }}">{{ object.region }}</a></li>
+  {% endif %}
+  <li>{{ object }}</li>
 {% endblock %}
 
 {% block content %}

+ 16 - 49
netbox/templates/dcim/virtualchassis.html

@@ -1,57 +1,24 @@
-{% extends 'base.html' %}
+{% extends 'generic/object.html' %}
 {% load buttons %}
 {% load custom_links %}
 {% load helpers %}
 {% load plugins %}
 
-{% block header %}
-    <div class="row noprint">
-        <div class="col-sm-8 col-md-9">
-            <ol class="breadcrumb">
-                <li><a href="{% url 'dcim:virtualchassis_list' %}">Virtual Chassis</a></li>
-                {% if object.master %}
-                    <li><a href="{% url 'dcim:virtualchassis_list' %}?site={{ object.master.site.slug }}">{{ object.master.site }}</a></li>
-                {% endif %}
-                <li>{{ object }}</li>
-            </ol>
-        </div>
-        <div class="col-sm-4 col-md-3">
-            <form action="{% url 'dcim:virtualchassis_list' %}" method="get">
-                <div class="input-group">
-                    <input type="text" name="q" class="form-control" placeholder="Search virtual chassis" />
-                    <span class="input-group-btn">
-                        <button type="submit" class="btn btn-primary">
-                            <span class="mdi mdi-magnify" aria-hidden="true"></span>
-                        </button>
-                    </span>
-                </div>
-            </form>
-        </div>
-    </div>
-    <div class="pull-right noprint">
-        {% plugin_buttons object %}
-        {% if perms.dcim.change_virtualchassis %}
-            {% edit_button object %}
-        {% endif %}
-        {% if perms.dcim.delete_virtualchassis %}
-            {% delete_button object %}
-        {% endif %}
-    </div>
-    <h1>{% block title %}{{ object }}{% endblock %}</h1>
-    {% include 'inc/created_updated.html' %}
-    <div class="pull-right noprint">
-        {% custom_links object %}
-    </div>
-    <ul class="nav nav-tabs">
-        <li role="presentation"{% if not active_tab %} class="active"{% endif %}>
-            <a href="{{ object.get_absolute_url }}">Virtual Chassis</a>
-        </li>
-        {% if perms.extras.view_objectchange %}
-            <li role="presentation"{% if active_tab == 'changelog' %} class="active"{% endif %}>
-                <a href="{% url 'dcim:virtualchassis_changelog' pk=object.pk %}">Change Log</a>
-            </li>
-        {% endif %}
-    </ul>
+{% block breadcrumbs %}
+  <li><a href="{% url 'dcim:virtualchassis_list' %}">Virtual Chassis</a></li>
+  {% if object.master %}
+    <li><a href="{% url 'dcim:virtualchassis_list' %}?site={{ object.master.site.slug }}">{{ object.master.site }}</a></li>
+  {% endif %}
+  <li>{{ object }}</li>
+{% endblock %}
+
+{% block buttons %}
+  {% if perms.dcim.change_virtualchassis %}
+    {% edit_button object %}
+  {% endif %}
+  {% if perms.dcim.delete_virtualchassis %}
+    {% delete_button object %}
+  {% endif %}
 {% endblock %}
 
 {% block content %}

+ 4 - 41
netbox/templates/extras/configcontext.html

@@ -1,47 +1,10 @@
-{% extends 'base.html' %}
+{% extends 'generic/object.html' %}
 {% load helpers %}
 {% load static %}
 
-{% block header %}
-    <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>
-                <li>{{ object }}</li>
-            </ol>
-        </div>
-        <div class="col-sm-4 col-md-3">
-            <form action="{% url 'extras:configcontext_list' %}" method="get">
-                <div class="input-group">
-                    <input type="text" name="q" class="form-control" />
-                    <span class="input-group-btn">
-                        <button type="submit" class="btn btn-primary">
-                            <span class="mdi mdi-magnify" aria-hidden="true"></span>
-                        </button>
-                    </span>
-                </div>
-            </form>
-        </div>
-    </div>
-    <div class="pull-right noprint">
-        {% if perms.extras.change_configcontext %}
-            <a href="{% url 'extras:configcontext_edit' pk=object.pk %}" class="btn btn-warning">
-                <span class="mdi mdi-pencil" aria-hidden="true"></span>
-                Edit this config context
-            </a>
-        {% endif %}
-    </div>
-    <ul class="nav nav-tabs">
-        <li role="presentation"{% if not active_tab %} class="active"{% endif %}>
-            <a href="{{ object.get_absolute_url }}">Config Context</a>
-        </li>
-        {% if perms.extras.view_objectchange %}
-            <li role="presentation"{% if active_tab == 'changelog' %} class="active"{% endif %}>
-                <a href="{% url 'extras:configcontext_changelog' pk=object.pk %}">Change Log</a>
-            </li>
-        {% endif %}
-    </ul>
-    <h1>{% block title %}{{ object }}{% endblock %}</h1>
+{% block breadcrumbs %}
+  <li><a href="{% url 'extras:configcontext_list' %}">Config Contexts</a></li>
+  <li>{{ object }}</li>
 {% endblock %}
 
 {% block content %}

+ 63 - 0
netbox/templates/generic/object.html

@@ -0,0 +1,63 @@
+{% extends 'base.html' %}
+{% load buttons %}
+{% load custom_links %}
+{% load helpers %}
+{% load perms %}
+{% load plugins %}
+
+{% block header %}
+  <div class="row noprint">
+    <div class="col-sm-8 col-md-9">
+      <ol class="breadcrumb">
+        {% block breadcrumbs %}{% endblock %}
+      </ol>
+    </div>
+    <div class="col-sm-4 col-md-3">
+      {# TODO: Provide absolute search URL #}
+      <form action="../" method="get">
+        <div class="input-group">
+          <input type="text" name="q" class="form-control" placeholder="Search" />
+          <span class="input-group-btn">
+            <button type="submit" class="btn btn-primary">
+              <span class="mdi mdi-magnify" aria-hidden="true"></span>
+            </button>
+          </span>
+        </div>
+      </form>
+    </div>
+  </div>
+  <div class="pull-right noprint">
+    {% plugin_buttons object %}
+    {% block buttons %}
+      {% if request.user|can_add:object %}
+        {% clone_button object %}
+      {% endif %}
+      {% if request.user|can_change:object %}
+        {% edit_button object %}
+      {% endif %}
+      {% if request.user|can_delete:object %}
+        {% delete_button object %}
+      {% endif %}
+    {% endblock %}
+  </div>
+  <h1>{% block title %}{{ object }}{% endblock %}</h1>
+  {% include 'inc/created_updated.html' %}
+  <div class="pull-right noprint">
+    {% block tab_buttons %}
+      {% custom_links object %}
+    {% endblock %}
+  </div>
+  {% block tabs %}
+    <ul class="nav nav-tabs">
+      <li role="presentation"{% if not active_tab %} class="active"{% endif %}>
+        <a href="{{ object.get_absolute_url }}">{{ object|meta:"verbose_name"|bettertitle }}</a>
+      </li>
+      {% if perms.extras.view_objectchange %}
+        <li role="presentation"{% if active_tab == 'changelog' %} class="active"{% endif %}>
+          {# TODO: Fix changelog URL resolution hack #}
+          <a href="{{ object.get_absolute_url }}changelog/">Change Log</a>
+        </li>
+      {% endif %}
+    </ul>
+  {% endblock %}
+{% endblock %}

+ 10 - 51
netbox/templates/ipam/aggregate.html

@@ -1,59 +1,18 @@
-{% extends 'base.html' %}
+{% extends 'generic/object.html' %}
 {% load buttons %}
 {% load custom_links %}
 {% load helpers %}
 {% load plugins %}
 
-{% block header %}
-    <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>
-                <li><a href="{% url 'ipam:aggregate_list' %}?rir={{ object.rir.slug }}">{{ object.rir }}</a></li>
-                <li>{{ object }}</li>
-            </ol>
-        </div>
-        <div class="col-sm-4 col-md-3">
-            <form action="{% url 'ipam:aggregate_list' %}" method="get">
-                <div class="input-group">
-                    <input type="text" name="q" class="form-control" placeholder="Search aggregates" />
-                    <span class="input-group-btn">
-                        <button type="submit" class="btn btn-primary">
-                            <span class="mdi mdi-magnify" aria-hidden="true"></span>
-                        </button>
-                    </span>
-                </div>
-            </form>
-        </div>
-    </div>
-    <div class="pull-right noprint">
-        {% plugin_buttons object %}
-        {% if perms.ipam.add_aggregate %}
-            {% clone_button object %}
-        {% endif %}
-        {% if perms.ipam.change_aggregate %}
-            {% edit_button object %}
-        {% endif %}
-        {% if perms.ipam.delete_aggregate %}
-            {% delete_button object %}
-        {% endif %}
-    </div>
-    <h1>{% block title %}{{ object }}{% endblock %}</h1>
-    {% include 'inc/created_updated.html' %}
-    {% include 'ipam/inc/toggle_available.html' %}
-    <div class="pull-right noprint">
-        {% custom_links object %}
-    </div>
-    <ul class="nav nav-tabs">
-        <li role="presentation"{% if not active_tab %} class="active"{% endif %}>
-            <a href="{{ object.get_absolute_url }}">Aggregate</a>
-        </li>
-        {% if perms.extras.view_objectchange %}
-            <li role="presentation"{% if active_tab == 'changelog' %} class="active"{% endif %}>
-                <a href="{% url 'ipam:aggregate_changelog' pk=object.pk %}">Change Log</a>
-            </li>
-        {% endif %}
-    </ul>
+{% block breadcrumbs %}
+  <li><a href="{% url 'ipam:aggregate_list' %}">Aggregates</a></li>
+  <li><a href="{% url 'ipam:aggregate_list' %}?rir={{ object.rir.slug }}">{{ object.rir }}</a></li>
+  <li>{{ object }}</li>
+{% endblock %}
+
+{% block buttons %}
+  {% include 'ipam/inc/toggle_available.html' %}
+  {{ block.super }}
 {% endblock %}
 
 {% block content %}

+ 8 - 10
netbox/templates/ipam/inc/toggle_available.html

@@ -1,13 +1,11 @@
 {% load helpers %}
 {% if show_available is not None %}
-    <div class="pull-right">
-        <div class="btn-group" role="group">
-            <a href="{{ request.path }}{% querystring request show_available='true' %}" class="btn btn-default{% if show_available %} active disabled{% endif %}">
-              <i class="mdi mdi-eye-outline"></i> Show available
-            </a>
-            <a href="{{ request.path }}{% querystring request show_available='false' %}" class="btn btn-default{% if not show_available %} active disabled{% endif %}">
-              <i class="mdi mdi-eye-off-outline"></i> Hide available
-            </a>
-        </div>
-    </div>
+  <div class="btn-group" role="group">
+    <a href="{{ request.path }}{% querystring request show_available='true' %}" class="btn btn-default{% if show_available %} active disabled{% endif %}">
+      <i class="mdi mdi-eye-outline"></i> Show available
+    </a>
+    <a href="{{ request.path }}{% querystring request show_available='false' %}" class="btn btn-default{% if not show_available %} active disabled{% endif %}">
+      <i class="mdi mdi-eye-off-outline"></i> Hide available
+    </a>
+  </div>
 {% endif %}

+ 7 - 54
netbox/templates/ipam/ipaddress.html

@@ -1,61 +1,14 @@
-{% extends 'base.html' %}
-{% load buttons %}
-{% load custom_links %}
+{% extends 'generic/object.html' %}
 {% load helpers %}
 {% load plugins %}
 {% load render_table from django_tables2 %}
 
-{% block header %}
-    <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>
-                {% if object.vrf %}
-                    <li><a href="{% url 'ipam:vrf' pk=object.vrf.pk %}">{{ object.vrf }}</a></li>
-                {% endif %}
-                <li>{{ object }}</li>
-            </ol>
-        </div>
-        <div class="col-sm-4 col-md-3">
-            <form action="{% url 'ipam:ipaddress_list' %}" method="get">
-                <div class="input-group">
-                    <input type="text" name="q" class="form-control" placeholder="Search IPs" />
-                    <span class="input-group-btn">
-                        <button type="submit" class="btn btn-primary">
-                            <span class="mdi mdi-magnify" aria-hidden="true"></span>
-                        </button>
-                    </span>
-                </div>
-            </form>
-        </div>
-    </div>
-    <div class="pull-right noprint">
-        {% plugin_buttons object %}
-        {% if perms.ipam.add_ipaddress %}
-            {% clone_button object %}
-        {% endif %}
-        {% if perms.ipam.change_ipaddress %}
-            {%edit_button object %}
-        {% endif %}
-        {% if perms.ipam.delete_ipaddress %}
-            {% delete_button object %}
-        {% endif %}
-    </div>
-    <h1>{% block title %}{{ object }}{% endblock %}</h1>
-    {% include 'inc/created_updated.html' %}
-    <div class="pull-right noprint">
-        {% custom_links object %}
-    </div>
-    <ul class="nav nav-tabs">
-        <li role="presentation"{% if not active_tab %} class="active"{% endif %}>
-            <a href="{{ object.get_absolute_url }}">IP Address</a>
-        </li>
-        {% if perms.extras.view_objectchange %}
-            <li role="presentation"{% if active_tab == 'changelog' %} class="active"{% endif %}>
-                <a href="{% url 'ipam:ipaddress_changelog' pk=object.pk %}">Change Log</a>
-            </li>
-        {% endif %}
-    </ul>
+{% block breadcrumbs %}
+  <li><a href="{% url 'ipam:ipaddress_list' %}">IP Addresses</a></li>
+  {% if object.vrf %}
+    <li><a href="{% url 'ipam:vrf' pk=object.vrf.pk %}">{{ object.vrf }}</a></li>
+  {% endif %}
+  <li>{{ object }}</li>
 {% endblock %}
 
 {% block content %}

+ 40 - 70
netbox/templates/ipam/prefix.html

@@ -1,79 +1,49 @@
-{% extends 'base.html' %}
-{% load buttons %}
-{% load custom_links %}
+{% extends 'generic/object.html' %}
 {% load helpers %}
 {% load plugins %}
 
-{% block header %}
-    <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>
-                {% if object.vrf %}
-                    <li><a href="{% url 'ipam:vrf' pk=object.vrf.pk %}">{{ object.vrf }}</a></li>
-                {% endif %}
-                <li>{{ object }}</li>
-            </ol>
-        </div>
-        <div class="col-sm-4 col-md-3">
-            <form action="{% url 'ipam:prefix_list' %}" method="get">
-                <div class="input-group">
-                    <input type="text" name="q" class="form-control" placeholder="Search prefixes" />
-                    <span class="input-group-btn">
-                        <button type="submit" class="btn btn-primary">
-                            <span class="mdi mdi-magnify" aria-hidden="true"></span>
-                        </button>
-                    </span>
-                </div>
-            </form>
-        </div>
-    </div>
-    <div class="pull-right noprint">
-        {% plugin_buttons object %}
-        {% if perms.ipam.add_prefix and active_tab == 'prefixes' and first_available_prefix %}
-            <a href="{% url 'ipam:prefix_add' %}?prefix={{ first_available_prefix }}&vrf={{ object.vrf.pk }}&site={{ object.site.pk }}&tenant_group={{ object.tenant.group.pk }}&tenant={{ object.tenant.pk }}" class="btn btn-success">
-                <i class="mdi mdi-plus-thick" aria-hidden="true"></i> Add Child Prefix
-            </a>
-        {% endif %}
-        {% if perms.ipam.add_ipaddress and active_tab == 'ip-addresses' and first_available_ip %}
-            <a href="{% url 'ipam:ipaddress_add' %}?address={{ first_available_ip }}&vrf={{ object.vrf.pk }}&tenant_group={{ object.tenant.group.pk }}&tenant={{ object.tenant.pk }}" class="btn btn-success">
-                <span class="mdi mdi-plus-thick" aria-hidden="true"></span>
-                Add an IP Address
-            </a>
-        {% endif %}
-        {% if perms.ipam.add_prefix %}
-            {% clone_button object %}
-        {% endif %}
-        {% if perms.ipam.change_prefix %}
-            {% edit_button object %}
-        {% endif %}
-        {% if perms.ipam.delete_prefix %}
-            {% delete_button object %}
-        {% endif %}
-    </div>
-    <h1>{% block title %}{{ object }}{% endblock %}</h1>
-    {% include 'inc/created_updated.html' %}
-    {% include 'ipam/inc/toggle_available.html' %}
-    <div class="pull-right noprint">
-        {% custom_links object %}
-    </div>
+{% block breadcrumbs %}
+  <li><a href="{% url 'ipam:prefix_list' %}">Prefixes</a></li>
+  {% if object.vrf %}
+    <li><a href="{% url 'ipam:vrf' pk=object.vrf.pk %}">{{ object.vrf }}</a></li>
+  {% endif %}
+  <li>{{ object }}</li>
+{% endblock %}
+
+{% block buttons %}
+  {% include 'ipam/inc/toggle_available.html' %}
+  {% if perms.ipam.add_prefix and active_tab == 'prefixes' and first_available_prefix %}
+    <a href="{% url 'ipam:prefix_add' %}?prefix={{ first_available_prefix }}&vrf={{ object.vrf.pk }}&site={{ object.site.pk }}&tenant_group={{ object.tenant.group.pk }}&tenant={{ object.tenant.pk }}" class="btn btn-success">
+      <i class="mdi mdi-plus-thick" aria-hidden="true"></i> Add Child Prefix
+    </a>
+  {% endif %}
+  {% if perms.ipam.add_ipaddress and active_tab == 'ip-addresses' and first_available_ip %}
+    <a href="{% url 'ipam:ipaddress_add' %}?address={{ first_available_ip }}&vrf={{ object.vrf.pk }}&tenant_group={{ object.tenant.group.pk }}&tenant={{ object.tenant.pk }}" class="btn btn-success">
+      <span class="mdi mdi-plus-thick" aria-hidden="true"></span>
+      Add an IP Address
+    </a>
+  {% endif %}
+  {{ block.super }}
+{% endblock %}
+
+{% block tabs %}
     <ul class="nav nav-tabs" style="margin-bottom: 20px">
-        <li role="presentation"{% if not active_tab %} class="active"{% endif %}>
-            <a href="{% url 'ipam:prefix' pk=object.pk %}">Prefix</a>
+      <li role="presentation"{% if not active_tab %} class="active"{% endif %}>
+        <a href="{% url 'ipam:prefix' pk=object.pk %}">Prefix</a>
+      </li>
+      <li role="presentation"{% if active_tab == 'prefixes' %} class="active"{% endif %}>
+        <a href="{% url 'ipam:prefix_prefixes' pk=object.pk %}">Child Prefixes <span class="badge">{{ object.get_child_prefixes.count }}</span></a>
+      </li>
+      {% if perms.ipam.view_ipaddress and object.status != 'container' %}
+        <li role="presentation"{% if active_tab == 'ip-addresses' %} class="active"{% endif %}>
+          <a href="{% url 'ipam:prefix_ipaddresses' pk=object.pk %}">IP Addresses <span class="badge">{{ object.get_child_ips.count }}</span></a>
         </li>
-        <li role="presentation"{% if active_tab == 'prefixes' %} class="active"{% endif %}>
-            <a href="{% url 'ipam:prefix_prefixes' pk=object.pk %}">Child Prefixes <span class="badge">{{ object.get_child_prefixes.count }}</span></a>
+      {% endif %}
+      {% if perms.extras.view_objectchange %}
+        <li role="presentation"{% if active_tab == 'changelog' %} class="active"{% endif %}>
+          <a href="{% url 'ipam:prefix_changelog' pk=object.pk %}">Change Log</a>
         </li>
-        {% if perms.ipam.view_ipaddress and object.status != 'container' %}
-            <li role="presentation"{% if active_tab == 'ip-addresses' %} class="active"{% endif %}>
-                <a href="{% url 'ipam:prefix_ipaddresses' pk=object.pk %}">IP Addresses <span class="badge">{{ object.get_child_ips.count }}</span></a>
-            </li>
-        {% endif %}
-        {% if perms.extras.view_objectchange %}
-            <li role="presentation"{% if active_tab == 'changelog' %} class="active"{% endif %}>
-                <a href="{% url 'ipam:prefix_changelog' pk=object.pk %}">Change Log</a>
-            </li>
-        {% endif %}
+      {% endif %}
     </ul>
 {% endblock %}
 

+ 4 - 51
netbox/templates/ipam/routetarget.html

@@ -1,57 +1,10 @@
-{% extends 'base.html' %}
-{% load buttons %}
-{% load custom_links %}
+{% extends 'generic/object.html' %}
 {% load helpers %}
 {% load plugins %}
 
-{% block header %}
-    <div class="row noprint">
-        <div class="col-sm-8 col-md-9">
-            <ol class="breadcrumb">
-                <li><a href="{% url 'ipam:routetarget_list' %}">Route Targets</a></li>
-                <li>{{ object }}</li>
-            </ol>
-        </div>
-        <div class="col-sm-4 col-md-3">
-            <form action="{% url 'ipam:routetarget_list' %}" method="get">
-                <div class="input-group">
-                    <input type="text" name="q" class="form-control" placeholder="Search roue targets" />
-                    <span class="input-group-btn">
-                        <button type="submit" class="btn btn-primary">
-                            <span class="mdi mdi-magnify" aria-hidden="true"></span>
-                        </button>
-                    </span>
-                </div>
-            </form>
-        </div>
-    </div>
-    <div class="pull-right noprint">
-        {% plugin_buttons object %}
-        {% if perms.ipam.add_routetarget %}
-            {% clone_button object %}
-        {% endif %}
-        {% if perms.ipam.change_routetarget %}
-            {% edit_button object %}
-        {% endif %}
-        {% if perms.ipam.delete_routetarget %}
-            {% delete_button object %}
-        {% endif %}
-    </div>
-    <h1>{% block title %}Route target {{ object }}{% endblock %}</h1>
-    {% include 'inc/created_updated.html' %}
-    <div class="pull-right noprint">
-        {% custom_links object %}
-    </div>
-    <ul class="nav nav-tabs">
-        <li role="presentation"{% if not active_tab %} class="active"{% endif %}>
-            <a href="{{ object.get_absolute_url }}">Route Target</a>
-        </li>
-        {% if perms.extras.view_objectchange %}
-            <li role="presentation"{% if active_tab == 'changelog' %} class="active"{% endif %}>
-                <a href="{% url 'ipam:routetarget_changelog' pk=object.pk %}">Change Log</a>
-            </li>
-        {% endif %}
-    </ul>
+{% block breadcrumbs %}
+  <li><a href="{% url 'ipam:routetarget_list' %}">Route Targets</a></li>
+  <li>{{ object }}</li>
 {% endblock %}
 
 {% block content %}

+ 17 - 37
netbox/templates/ipam/service.html

@@ -1,45 +1,25 @@
-{% extends 'base.html' %}
+{% extends 'generic/object.html' %}
 {% load buttons %}
-{% load custom_links %}
 {% load helpers %}
+{% load perms %}
 {% load plugins %}
 
+{% block breadcrumbs %}
+  <li><a href="{% url 'ipam:service_list' %}">Services</a></li>
+  <li><a href="{{ object.parent.get_absolute_url }}">{{ object.parent }}</a></li>
+  <li>{{ object }}</li>
+{% endblock %}
+
+{% block buttons %}
+  {% if request.user|can_change:object %}
+    {% edit_button object %}
+  {% endif %}
+  {% if request.user|can_delete:object %}
+    {% delete_button object %}
+  {% endif %}
+{% endblock %}
+
 {% block content %}
-<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>
-            <li><a href="{{ object.parent.get_absolute_url }}">{{ object.parent }}</a></li>
-            <li>{{ object }}</li>
-        </ol>
-    </div>
-    <div class="col-sm-4 col-md-3">
-        <form action="{% url 'ipam:service_list' %}" method="get">
-            <div class="input-group">
-                <input type="text" name="q" class="form-control" placeholder="Search Services" />
-                <span class="input-group-btn">
-                    <button type="submit" class="btn btn-primary">
-                        <span class="mdi mdi-magnify" aria-hidden="true"></span>
-                    </button>
-                </span>
-            </div>
-        </form>
-    </div>
-</div>
-<div class="pull-right">
-    {% plugin_buttons object %}
-    {% if perms.dcim.change_service %}
-        {% edit_button object %}
-    {% endif %}
-    {% if perms.dcim.delete_service %}
-        {% delete_button object %}
-    {% endif %}
-</div>
-<h1>{% block title %}{{ object }}{% endblock %}</h1>
-{% include 'inc/created_updated.html' %}
-<div class="pull-right noprint">
-    {% custom_links object %}
-</div>
 <div class="row">
 	<div class="col-md-6">
         <div class="panel panel-default">

+ 31 - 63
netbox/templates/ipam/vlan.html

@@ -1,69 +1,37 @@
-{% extends 'base.html' %}
-{% load buttons %}
-{% load custom_links %}
+{% extends 'generic/object.html' %}
 {% load helpers %}
 {% load plugins %}
 
-{% block header %}
-    <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>
-                {% if object.site %}
-                    <li><a href="{% url 'ipam:vlan_list' %}?site={{ object.site.slug }}">{{ object.site }}</a></li>
-                {% endif %}
-                {% if object.group %}
-                    <li><a href="{% url 'ipam:vlan_list' %}?group={{ object.group.slug }}">{{ object.group }}</a></li>
-                {% endif %}
-                <li>{{ object }}</li>
-            </ol>
-        </div>
-        <div class="col-sm-4 col-md-3">
-        <form action="{% url 'ipam:vlan_list' %}" method="get">
-            <div class="input-group">
-                <input type="text" name="q" class="form-control" placeholder="Search VLANs" />
-                <span class="input-group-btn">
-                    <button type="submit" class="btn btn-primary">
-                        <span class="mdi mdi-magnify" aria-hidden="true"></span>
-                    </button>
-                </span>
-            </div>
-        </form>
-        </div>
-    </div>
-    <div class="pull-right noprint">
-        {% plugin_buttons object %}
-        {% if perms.ipam.add_vlan %}
-            {% clone_button object %}
-        {% endif %}
-        {% if perms.ipam.change_vlan %}
-            {% edit_button object %}
-        {% endif %}
-        {% if perms.ipam.delete_vlan %}
-            {% delete_button object %}
-        {% endif %}
-    </div>
-    <h1>{% block title %}VLAN {{ object.display_name }}{% endblock %}</h1>
-    {% include 'inc/created_updated.html' %}
-    <div class="pull-right noprint">
-        {% custom_links object %}
-    </div>
-    <ul class="nav nav-tabs" style="margin-bottom: 20px">
-        <li role="presentation"{% if not active_tab %} class="active"{% endif %}>
-            <a href="{% url 'ipam:vlan' pk=object.pk %}">VLAN</a>
-        </li>
-        <li role="presentation"{% if active_tab == 'interfaces' %} class="active"{% endif %}>
-            <a href="{% url 'ipam:vlan_interfaces' pk=object.pk %}">Device Interfaces <span class="badge">{{ object.get_interfaces.count }}</span></a>
-        </li>
-        <li role="presentation"{% if active_tab == 'vminterfaces' %} class="active"{% endif %}>
-            <a href="{% url 'ipam:vlan_vminterfaces' pk=object.pk %}">VM Interfaces <span class="badge">{{ object.get_vminterfaces.count }}</span></a>
-        </li>
-        {% if perms.extras.view_objectchange %}
-            <li role="presentation"{% if active_tab == 'changelog' %} class="active"{% endif %}>
-                <a href="{% url 'ipam:vlan_changelog' pk=object.pk %}">Change Log</a>
-            </li>
-        {% endif %}
-    </ul>
+{% block title %}VLAN {{ object.display_name }}{% endblock %}
+
+{% block breadcrumbs %}
+  <li><a href="{% url 'ipam:vlan_list' %}">VLANs</a></li>
+  {% if object.site %}
+    <li><a href="{% url 'ipam:vlan_list' %}?site={{ object.site.slug }}">{{ object.site }}</a></li>
+  {% endif %}
+  {% if object.group %}
+    <li><a href="{% url 'ipam:vlan_list' %}?group={{ object.group.slug }}">{{ object.group }}</a></li>
+  {% endif %}
+  <li>{{ object }}</li>
+{% endblock %}
+
+{% block tabs %}
+  <ul class="nav nav-tabs" style="margin-bottom: 20px">
+    <li role="presentation"{% if not active_tab %} class="active"{% endif %}>
+      <a href="{% url 'ipam:vlan' pk=object.pk %}">VLAN</a>
+    </li>
+    <li role="presentation"{% if active_tab == 'interfaces' %} class="active"{% endif %}>
+      <a href="{% url 'ipam:vlan_interfaces' pk=object.pk %}">Device Interfaces <span class="badge">{{ object.get_interfaces.count }}</span></a>
+    </li>
+    <li role="presentation"{% if active_tab == 'vminterfaces' %} class="active"{% endif %}>
+      <a href="{% url 'ipam:vlan_vminterfaces' pk=object.pk %}">VM Interfaces <span class="badge">{{ object.get_vminterfaces.count }}</span></a>
+    </li>
+    {% if perms.extras.view_objectchange %}
+      <li role="presentation"{% if active_tab == 'changelog' %} class="active"{% endif %}>
+        <a href="{% url 'ipam:vlan_changelog' pk=object.pk %}">Change Log</a>
+      </li>
+    {% endif %}
+  </ul>
 {% endblock %}
 
 {% block content %}

+ 6 - 49
netbox/templates/ipam/vrf.html

@@ -1,57 +1,14 @@
-{% extends 'base.html' %}
+{% extends 'generic/object.html' %}
 {% load buttons %}
 {% load custom_links %}
 {% load helpers %}
 {% load plugins %}
 
-{% block header %}
-    <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>
-                <li>{{ object }}</li>
-            </ol>
-        </div>
-        <div class="col-sm-4 col-md-3">
-            <form action="{% url 'ipam:vrf_list' %}" method="get">
-                <div class="input-group">
-                    <input type="text" name="q" class="form-control" placeholder="Search VRFs" />
-                    <span class="input-group-btn">
-                        <button type="submit" class="btn btn-primary">
-                            <span class="mdi mdi-magnify" aria-hidden="true"></span>
-                        </button>
-                    </span>
-                </div>
-            </form>
-        </div>
-    </div>
-    <div class="pull-right noprint">
-        {% plugin_buttons object %}
-        {% if perms.ipam.add_vrf %}
-            {% clone_button object %}
-        {% endif %}
-        {% if perms.ipam.change_vrf %}
-            {% edit_button object %}
-        {% endif %}
-        {% if perms.ipam.delete_vrf %}
-            {% delete_button object %}
-        {% endif %}
-    </div>
-    <h1>{% block title %}VRF {{ object }}{% endblock %}</h1>
-    {% include 'inc/created_updated.html' %}
-    <div class="pull-right noprint">
-        {% custom_links object %}
-    </div>
-    <ul class="nav nav-tabs">
-        <li role="presentation"{% if not active_tab %} class="active"{% endif %}>
-            <a href="{{ object.get_absolute_url }}">VRF</a>
-        </li>
-        {% if perms.extras.view_objectchange %}
-            <li role="presentation"{% if active_tab == 'changelog' %} class="active"{% endif %}>
-                <a href="{% url 'ipam:vrf_changelog' pk=object.pk %}">Change Log</a>
-            </li>
-        {% endif %}
-    </ul>
+{% block title %}VRF {{ object }}{% endblock %}
+
+{% block breadcrumbs %}
+  <li><a href="{% url 'ipam:vrf_list' %}">VRFs</a></li>
+  <li>{{ object }}</li>
 {% endblock %}
 
 {% block content %}

+ 15 - 37
netbox/templates/secrets/secret.html

@@ -1,45 +1,23 @@
-{% extends 'base.html' %}
+{% extends 'generic/object.html' %}
 {% load buttons %}
-{% load custom_links %}
 {% load helpers %}
 {% load static %}
 {% load plugins %}
 
-{% block header %}
-    <div class="row noprint">
-        <div class="col-md-12">
-            <ol class="breadcrumb">
-                <li><a href="{% url 'secrets:secret_list' %}">Secrets</a></li>
-                <li><a href="{% url 'secrets:secret_list' %}?role={{ object.role.slug }}">{{ object.role }}</a></li>
-                <li><a href="{{ object.assigned_object.get_absolute_url }}">{{ object.assigned_object }}</a></li>
-                <li>{{ object }}</li>
-            </ol>
-        </div>
-    </div>
-    <div class="pull-right noprint">
-        {% plugin_buttons object %}
-        {% if perms.secrets.change_secret %}
-            {% edit_button object %}
-        {% endif %}
-        {% if perms.secrets.delete_secret %}
-            {% delete_button object %}
-        {% endif %}
-    </div>
-    <h1>{% block title %}{{ object }}{% endblock %}</h1>
-    {% include 'inc/created_updated.html' %}
-    <div class="pull-right noprint">
-        {% custom_links object %}
-    </div>
-    <ul class="nav nav-tabs">
-        <li role="presentation"{% if not active_tab %} class="active"{% endif %}>
-            <a href="{{ object.get_absolute_url }}">Secret</a>
-        </li>
-        {% if perms.extras.view_objectchange %}
-            <li role="presentation"{% if active_tab == 'changelog' %} class="active"{% endif %}>
-                <a href="{% url 'secrets:secret_changelog' pk=object.pk %}">Change Log</a>
-            </li>
-        {% endif %}
-    </ul>
+{% block breadcrumbs %}
+  <li><a href="{% url 'secrets:secret_list' %}">Secrets</a></li>
+  <li><a href="{% url 'secrets:secret_list' %}?role={{ object.role.slug }}">{{ object.role }}</a></li>
+  <li><a href="{{ object.assigned_object.get_absolute_url }}">{{ object.assigned_object }}</a></li>
+  <li>{{ object }}</li>
+{% endblock %}
+
+{% block buttons %}
+  {% if perms.secrets.change_secret %}
+    {% edit_button object %}
+  {% endif %}
+  {% if perms.secrets.delete_secret %}
+    {% delete_button object %}
+  {% endif %}
 {% endblock %}
 
 {% block content %}

+ 7 - 54
netbox/templates/tenancy/tenant.html

@@ -1,60 +1,13 @@
-{% extends 'base.html' %}
-{% load buttons %}
-{% load custom_links %}
+{% extends 'generic/object.html' %}
 {% load helpers %}
 {% load plugins %}
 
-{% block header %}
-    <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>
-                {% if object.group %}
-                    <li><a href="{% url 'tenancy:tenant_list' %}?group={{ object.group.slug }}">{{ object.group }}</a></li>
-                {% endif %}
-                <li>{{ object }}</li>
-            </ol>
-        </div>
-        <div class="col-sm-4 col-md-3">
-            <form action="{% url 'tenancy:tenant_list' %}" method="get">
-                <div class="input-group">
-                    <input type="text" name="q" class="form-control" placeholder="Name" />
-                    <span class="input-group-btn">
-                        <button type="submit" class="btn btn-primary">
-                            <span class="mdi mdi-magnify" aria-hidden="true"></span>
-                        </button>
-                    </span>
-                </div>
-            </form>
-        </div>
-    </div>
-    <div class="pull-right noprint">
-        {% plugin_buttons object %}
-        {% if perms.tenancy.add_tenant %}
-            {% clone_button object %}
-        {% endif %}
-        {% if perms.tenancy.change_tenant %}
-            {% edit_button object %}
-        {% endif %}
-        {% if perms.tenancy.delete_tenant %}
-            {% delete_button object %}
-        {% endif %}
-    </div>
-    <h1>{% block title %}{{ object }}{% endblock %}</h1>
-    {% include 'inc/created_updated.html' %}
-    <div class="pull-right noprint">
-        {% custom_links object %}
-    </div>
-    <ul class="nav nav-tabs">
-        <li role="presentation"{% if not active_tab %} class="active"{% endif %}>
-            <a href="{{ object.get_absolute_url }}">Tenant</a>
-        </li>
-        {% if perms.extras.view_objectchange %}
-            <li role="presentation"{% if active_tab == 'changelog' %} class="active"{% endif %}>
-                <a href="{% url 'tenancy:tenant_changelog' pk=object.pk %}">Change Log</a>
-            </li>
-        {% endif %}
-    </ul>
+{% block breadcrumbs %}
+  <li><a href="{% url 'tenancy:tenant_list' %}">Tenants</a></li>
+  {% if object.group %}
+    <li><a href="{% url 'tenancy:tenant_list' %}?group={{ object.group.slug }}">{{ object.group }}</a></li>
+  {% endif %}
+  <li>{{ object }}</li>
 {% endblock %}
 
 {% block content %}

+ 7 - 52
netbox/templates/virtualization/cluster.html

@@ -1,60 +1,15 @@
-{% extends 'base.html' %}
+{% extends 'generic/object.html' %}
 {% load buttons %}
 {% load custom_links %}
 {% load helpers %}
 {% load plugins %}
 
-{% block header %}
-    <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="{{ object.type.get_absolute_url }}">{{ object.type }}</a></li>
-                {% if object.group %}
-                    <li><a href="{{ object.group.get_absolute_url }}">{{ object.group }}</a></li>
-                {% endif %}
-                <li>{{ object }}</li>
-            </ol>
-        </div>
-        <div class="col-sm-4 col-md-3">
-            <form action="{% url 'virtualization:cluster_list' %}" method="get">
-                <div class="input-group">
-                    <input type="text" name="q" class="form-control" placeholder="Search clusters" />
-                    <span class="input-group-btn">
-                        <button type="submit" class="btn btn-primary">
-                            <span class="mdi mdi-magnify" aria-hidden="true"></span>
-                        </button>
-                    </span>
-                </div>
-            </form>
-        </div>
-    </div>
-    <div class="pull-right noprint">
-        {% plugin_buttons object %}
-        {% if perms.virtualization.add_cluster %}
-            {% clone_button object %}
-        {% endif %}
-        {% if perms.virtualization.change_cluster %}
-            {% edit_button object %}
-        {% endif %}
-        {% if perms.virtualization.delete_cluster %}
-            {% delete_button object %}
-        {% endif %}
-    </div>
-    <h1>{% block title %}{{ object }}{% endblock %}</h1>
-    {% include 'inc/created_updated.html' %}
-    <div class="pull-right noprint">
-        {% custom_links object %}
-    </div>
-    <ul class="nav nav-tabs">
-        <li role="presentation"{% if not active_tab %} class="active"{% endif %}>
-            <a href="{{ object.get_absolute_url }}">Cluster</a>
-        </li>
-        {% if perms.extras.view_objectchange %}
-            <li role="presentation"{% if active_tab == 'changelog' %} class="active"{% endif %}>
-                <a href="{% url 'virtualization:cluster_changelog' pk=object.pk %}">Change Log</a>
-            </li>
-        {% endif %}
-    </ul>
+{% block breadcrumbs %}
+  <li><a href="{{ object.type.get_absolute_url }}">{{ object.type }}</a></li>
+  {% if object.group %}
+    <li><a href="{{ object.group.get_absolute_url }}">{{ object.group }}</a></li>
+  {% endif %}
+  <li>{{ object }}</li>
 {% endblock %}
 
 {% block content %}

+ 33 - 61
netbox/templates/virtualization/virtualmachine.html

@@ -1,70 +1,42 @@
-{% extends 'base.html' %}
+{% extends 'generic/object.html' %}
 {% load buttons %}
 {% load custom_links %}
 {% load static %}
 {% load helpers %}
 {% load plugins %}
 
-{% block header %}
-    <div class="row noprint">
-        <div class="col-sm-8 col-md-9">
-            <ol class="breadcrumb">
-                {% if object.cluster %}
-                    <li><a href="{{ object.cluster.get_absolute_url }}">{{ object.cluster }}</a></li>
-                {% endif %}
-                <li>{{ object }}</li>
-            </ol>
-        </div>
-        <div class="col-sm-4 col-md-3">
-            <form action="{% url 'virtualization:virtualmachine_list' %}" method="get">
-                <div class="input-group">
-                    <input type="text" name="q" class="form-control" placeholder="Search virtual machines" />
-                    <span class="input-group-btn">
-                        <button type="submit" class="btn btn-primary">
-                            <span class="mdi mdi-magnify"></span>
-                        </button>
-                    </span>
-                </div>
-            </form>
-        </div>
-    </div>
-    <div class="pull-right noprint">
-        {% if perms.virtualization.add_vminterface %}
-            <a href="{% url 'virtualization:vminterface_add' %}?virtual_machine={{ object.pk }}&return_url={{ object.get_absolute_url }}" class="btn btn-primary">
-                <span class="mdi mdi-plus-thick" aria-hidden="true"></span> Add Interfaces
-            </a>
-        {% endif %}
-        {% plugin_buttons object %}
-        {% if perms.virtualization.add_virtualmachine %}
-            {% clone_button object %}
-        {% endif %}
-        {% if perms.virtualization.change_virtualmachine %}
-            {% edit_button object %}
-        {% endif %}
-        {% if perms.virtualization.delete_virtualmachine %}
-            {% delete_button object %}
-        {% endif %}
-    </div>
-    <h1>{% block title %}{{ object }}{% endblock %}</h1>
-    {% include 'inc/created_updated.html' %}
-    <div class="pull-right noprint">
-        {% custom_links object %}
-    </div>
-    <ul class="nav nav-tabs">
-        <li role="presentation"{% if not active_tab %} class="active"{% endif %}>
-            <a href="{{ object.get_absolute_url }}">Virtual Machine</a>
-        </li>
-        {% if perms.extras.view_configcontext %}
-            <li role="presentation"{% if active_tab == 'config-context' %} class="active"{% endif %}>
-                <a href="{% url 'virtualization:virtualmachine_configcontext' pk=object.pk %}">Config Context</a>
-            </li>
-        {% endif %}
-        {% if perms.extras.view_objectchange %}
-            <li role="presentation"{% if active_tab == 'changelog' %} class="active"{% endif %}>
-                <a href="{% url 'virtualization:virtualmachine_changelog' pk=object.pk %}">Change Log</a>
-            </li>
-        {% endif %}
-    </ul>
+{% block breadcrumbs %}
+  {% if object.cluster %}
+    <li><a href="{{ object.cluster.get_absolute_url }}">{{ object.cluster }}</a></li>
+  {% endif %}
+  <li>{{ object }}</li>
+{% endblock %}
+
+{% block buttons %}
+  {% if perms.virtualization.add_vminterface %}
+    <a href="{% url 'virtualization:vminterface_add' %}?virtual_machine={{ object.pk }}&return_url={{ object.get_absolute_url }}" class="btn btn-primary">
+      <span class="mdi mdi-plus-thick" aria-hidden="true"></span> Add Interfaces
+    </a>
+  {% endif %}
+  {{ block.super }}
+{% endblock %}
+
+{% block tabs %}
+  <ul class="nav nav-tabs">
+    <li role="presentation"{% if not active_tab %} class="active"{% endif %}>
+      <a href="{{ object.get_absolute_url }}">Virtual Machine</a>
+    </li>
+    {% if perms.extras.view_configcontext %}
+      <li role="presentation"{% if active_tab == 'config-context' %} class="active"{% endif %}>
+        <a href="{% url 'virtualization:virtualmachine_configcontext' pk=object.pk %}">Config Context</a>
+      </li>
+    {% endif %}
+    {% if perms.extras.view_objectchange %}
+      <li role="presentation"{% if active_tab == 'changelog' %} class="active"{% endif %}>
+        <a href="{% url 'virtualization:virtualmachine_changelog' pk=object.pk %}">Change Log</a>
+      </li>
+    {% endif %}
+  </ul>
 {% endblock %}
 
 {% block content %}

+ 7 - 35
netbox/templates/virtualization/vminterface.html

@@ -1,41 +1,13 @@
-{% extends 'base.html' %}
+{% extends 'generic/object.html' %}
 {% load helpers %}
 {% load plugins %}
 
-{% block header %}
-    <div class="row noprint">
-        <div class="col-md-12">
-            <ol class="breadcrumb">
-                <li><a href="{% url 'virtualization:virtualmachine_list' %}">Virtual Machines</a></li>
-                <li><a href="{{ object.virtual_machine.get_absolute_url }}">{{ object.virtual_machine }}</a></li>
-                <li>{{ object }}</li>
-            </ol>
-        </div>
-    </div>
-    <div class="pull-right noprint">
-        {% plugin_buttons object %}
-        {% if perms.virtualization.change_vminterface %}
-            <a href="{% url 'virtualization:vminterface_edit' pk=object.pk %}" class="btn btn-warning">
-                <span class="mdi mdi-pencil" aria-hidden="true"></span> Edit
-            </a>
-        {% endif %}
-        {% if perms.virtualization.delete_vminterface %}
-            <a href="{% url 'virtualization:vminterface_delete' pk=object.pk %}" class="btn btn-danger">
-                <span class="mdi mdi-trash-can-outline" aria-hidden="true"></span> Delete
-            </a>
-        {% endif %}
-    </div>
-    <h1>{% block title %}{{ object.virtual_machine }} / {{ object.name }}{% endblock %}</h1>
-    <ul class="nav nav-tabs">
-        <li role="presentation"{% if not active_tab %} class="active"{% endif %}>
-            <a href="{{ object.get_absolute_url }}">Interface</a>
-        </li>
-        {% if perms.extras.view_objectchange %}
-            <li role="presentation"{% if active_tab == 'changelog' %} class="active"{% endif %}>
-                <a href="{% url 'virtualization:vminterface_changelog' pk=object.pk %}">Change Log</a>
-            </li>
-        {% endif %}
-    </ul>
+{% block title %}{{ object.virtual_machine }} / {{ object.name }}{% endblock %}
+
+{% block breadcrumbs %}
+  <li><a href="{% url 'virtualization:virtualmachine_list' %}">Virtual Machines</a></li>
+  <li><a href="{{ object.virtual_machine.get_absolute_url }}">{{ object.virtual_machine }}</a></li>
+  <li>{{ object }}</li>
 {% endblock %}
 
 {% block content %}