mirror of
https://github.com/netbox-community/netbox.git
synced 2025-07-22 20:12:00 -06:00
Fixes #672: Expanded color selection for rack and device roles
This commit is contained in:
parent
300ee820fa
commit
eb4cd0e723
@ -5,7 +5,7 @@
|
||||
"fields": {
|
||||
"name": "Console Server",
|
||||
"slug": "console-server",
|
||||
"color": "teal"
|
||||
"color": "009688"
|
||||
}
|
||||
},
|
||||
{
|
||||
@ -14,7 +14,7 @@
|
||||
"fields": {
|
||||
"name": "Core Switch",
|
||||
"slug": "core-switch",
|
||||
"color": "blue"
|
||||
"color": "2196f3"
|
||||
}
|
||||
},
|
||||
{
|
||||
@ -23,7 +23,7 @@
|
||||
"fields": {
|
||||
"name": "Distribution Switch",
|
||||
"slug": "distribution-switch",
|
||||
"color": "blue"
|
||||
"color": "2196f3"
|
||||
}
|
||||
},
|
||||
{
|
||||
@ -32,7 +32,7 @@
|
||||
"fields": {
|
||||
"name": "Access Switch",
|
||||
"slug": "access-switch",
|
||||
"color": "blue"
|
||||
"color": "2196f3"
|
||||
}
|
||||
},
|
||||
{
|
||||
@ -41,7 +41,7 @@
|
||||
"fields": {
|
||||
"name": "Management Switch",
|
||||
"slug": "management-switch",
|
||||
"color": "orange"
|
||||
"color": "ff9800"
|
||||
}
|
||||
},
|
||||
{
|
||||
@ -50,7 +50,7 @@
|
||||
"fields": {
|
||||
"name": "Firewall",
|
||||
"slug": "firewall",
|
||||
"color": "red"
|
||||
"color": "f44336"
|
||||
}
|
||||
},
|
||||
{
|
||||
@ -59,7 +59,7 @@
|
||||
"fields": {
|
||||
"name": "Router",
|
||||
"slug": "router",
|
||||
"color": "purple"
|
||||
"color": "9c27b0"
|
||||
}
|
||||
},
|
||||
{
|
||||
@ -68,7 +68,7 @@
|
||||
"fields": {
|
||||
"name": "Server",
|
||||
"slug": "server",
|
||||
"color": "medium_gray"
|
||||
"color": "9e9e9e"
|
||||
}
|
||||
},
|
||||
{
|
||||
@ -77,7 +77,7 @@
|
||||
"fields": {
|
||||
"name": "PDU",
|
||||
"slug": "pdu",
|
||||
"color": "dark_gray"
|
||||
"color": "607d8b"
|
||||
}
|
||||
},
|
||||
{
|
||||
|
57
netbox/dcim/migrations/0022_color_names_to_rgb.py
Normal file
57
netbox/dcim/migrations/0022_color_names_to_rgb.py
Normal file
@ -0,0 +1,57 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.10 on 2016-12-06 16:35
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations
|
||||
import utilities.fields
|
||||
|
||||
|
||||
COLOR_CONVERSION = {
|
||||
'teal': '009688',
|
||||
'green': '4caf50',
|
||||
'blue': '2196f3',
|
||||
'purple': '9c27b0',
|
||||
'yellow': 'ffeb3b',
|
||||
'orange': 'ff9800',
|
||||
'red': 'f44336',
|
||||
'light_gray': 'c0c0c0',
|
||||
'medium_gray': '9e9e9e',
|
||||
'dark_gray': '607d8b',
|
||||
}
|
||||
|
||||
|
||||
def color_names_to_rgb(apps, schema_editor):
|
||||
RackRole = apps.get_model('dcim', 'RackRole')
|
||||
DeviceRole = apps.get_model('dcim', 'DeviceRole')
|
||||
for color_name, color_rgb in COLOR_CONVERSION.items():
|
||||
RackRole.objects.filter(color=color_name).update(color=color_rgb)
|
||||
DeviceRole.objects.filter(color=color_name).update(color=color_rgb)
|
||||
|
||||
|
||||
def color_rgb_to_name(apps, schema_editor):
|
||||
RackRole = apps.get_model('dcim', 'RackRole')
|
||||
DeviceRole = apps.get_model('dcim', 'DeviceRole')
|
||||
for color_name, color_rgb in COLOR_CONVERSION.items():
|
||||
RackRole.objects.filter(color=color_rgb).update(color=color_name)
|
||||
DeviceRole.objects.filter(color=color_rgb).update(color=color_name)
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('dcim', '0021_add_ff_flexstack'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RunPython(color_names_to_rgb, color_rgb_to_name),
|
||||
migrations.AlterField(
|
||||
model_name='devicerole',
|
||||
name='color',
|
||||
field=utilities.fields.ColorField(max_length=6),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='rackrole',
|
||||
name='color',
|
||||
field=utilities.fields.ColorField(max_length=6),
|
||||
),
|
||||
]
|
@ -3,7 +3,7 @@ from collections import OrderedDict
|
||||
from django.conf import settings
|
||||
from django.contrib.contenttypes.models import ContentType
|
||||
from django.contrib.contenttypes.fields import GenericRelation
|
||||
from django.core.exceptions import MultipleObjectsReturned, ValidationError
|
||||
from django.core.exceptions import ValidationError
|
||||
from django.core.urlresolvers import reverse
|
||||
from django.core.validators import MaxValueValidator, MinValueValidator
|
||||
from django.db import models
|
||||
@ -12,7 +12,7 @@ from django.db.models import Count, Q, ObjectDoesNotExist
|
||||
from extras.models import CustomFieldModel, CustomField, CustomFieldValue
|
||||
from extras.rpc import RPC_CLIENTS
|
||||
from tenancy.models import Tenant
|
||||
from utilities.fields import NullableCharField
|
||||
from utilities.fields import ColorField, NullableCharField
|
||||
from utilities.managers import NaturalOrderByManager
|
||||
from utilities.models import CreatedUpdatedModel
|
||||
|
||||
@ -54,29 +54,6 @@ SUBDEVICE_ROLE_CHOICES = (
|
||||
(SUBDEVICE_ROLE_CHILD, 'Child'),
|
||||
)
|
||||
|
||||
COLOR_TEAL = 'teal'
|
||||
COLOR_GREEN = 'green'
|
||||
COLOR_BLUE = 'blue'
|
||||
COLOR_PURPLE = 'purple'
|
||||
COLOR_YELLOW = 'yellow'
|
||||
COLOR_ORANGE = 'orange'
|
||||
COLOR_RED = 'red'
|
||||
COLOR_GRAY1 = 'light_gray'
|
||||
COLOR_GRAY2 = 'medium_gray'
|
||||
COLOR_GRAY3 = 'dark_gray'
|
||||
ROLE_COLOR_CHOICES = [
|
||||
[COLOR_TEAL, 'Teal'],
|
||||
[COLOR_GREEN, 'Green'],
|
||||
[COLOR_BLUE, 'Blue'],
|
||||
[COLOR_PURPLE, 'Purple'],
|
||||
[COLOR_YELLOW, 'Yellow'],
|
||||
[COLOR_ORANGE, 'Orange'],
|
||||
[COLOR_RED, 'Red'],
|
||||
[COLOR_GRAY1, 'Light Gray'],
|
||||
[COLOR_GRAY2, 'Medium Gray'],
|
||||
[COLOR_GRAY3, 'Dark Gray'],
|
||||
]
|
||||
|
||||
# Virtual
|
||||
IFACE_FF_VIRTUAL = 0
|
||||
# Ethernet
|
||||
@ -345,7 +322,7 @@ class RackRole(models.Model):
|
||||
"""
|
||||
name = models.CharField(max_length=50, unique=True)
|
||||
slug = models.SlugField(unique=True)
|
||||
color = models.CharField(max_length=30, choices=ROLE_COLOR_CHOICES)
|
||||
color = ColorField()
|
||||
|
||||
class Meta:
|
||||
ordering = ['name']
|
||||
@ -761,7 +738,7 @@ class DeviceRole(models.Model):
|
||||
"""
|
||||
name = models.CharField(max_length=50, unique=True)
|
||||
slug = models.SlugField(unique=True)
|
||||
color = models.CharField(max_length=30, choices=ROLE_COLOR_CHOICES)
|
||||
color = ColorField()
|
||||
|
||||
class Meta:
|
||||
ordering = ['name']
|
||||
|
@ -11,7 +11,7 @@ from .models import (
|
||||
|
||||
|
||||
COLOR_LABEL = """
|
||||
<label class="label {{ record.color }}">{{ record }}</label>
|
||||
<label class="label" style="background-color: #{{ record.color }}">{{ record }}</label>
|
||||
"""
|
||||
|
||||
DEVICE_LINK = """
|
||||
@ -34,7 +34,7 @@ RACKROLE_ACTIONS = """
|
||||
|
||||
RACK_ROLE = """
|
||||
{% if record.role %}
|
||||
<label class="label {{ record.role.color }}">{{ value }}</label>
|
||||
<label class="label" style="background-color: #{{ record.role.color }}">{{ value }}</label>
|
||||
{% else %}
|
||||
—
|
||||
{% endif %}
|
||||
@ -59,7 +59,7 @@ PLATFORM_ACTIONS = """
|
||||
"""
|
||||
|
||||
DEVICE_ROLE = """
|
||||
<label class="label {{ record.device_role.color }}">{{ value }}</label>
|
||||
<label class="label" style="background-color: #{{ record.device_role.color }}">{{ value }}</label>
|
||||
"""
|
||||
|
||||
STATUS_ICON = """
|
||||
|
@ -296,18 +296,6 @@ li.occupied + li.available {
|
||||
border-top: 1px solid #474747;
|
||||
}
|
||||
|
||||
/* Colors (from http://flatuicolors.com) */
|
||||
.teal { background-color: #1abc9c; }
|
||||
.green { background-color: #2ecc71; }
|
||||
.blue { background-color: #3498db; }
|
||||
.purple { background-color: #9b59b6; }
|
||||
.yellow { background-color: #f1c40f; }
|
||||
.orange { background-color: #e67e22; }
|
||||
.red { background-color: #e74c3c; }
|
||||
.light_gray { background-color: #dce2e3; }
|
||||
.medium_gray { background-color: #95a5a6; }
|
||||
.dark_gray { background-color: #34495e; }
|
||||
|
||||
/* Misc */
|
||||
.banner-bottom {
|
||||
margin-bottom: 50px;
|
||||
|
@ -1,5 +1,11 @@
|
||||
from django.core.validators import RegexValidator
|
||||
from django.db import models
|
||||
|
||||
from .forms import ColorSelect
|
||||
|
||||
|
||||
validate_color = RegexValidator('^[0-9a-f]{6}$', 'Enter a valid hexadecimal RGB color code.', 'invalid')
|
||||
|
||||
|
||||
class NullableCharField(models.CharField):
|
||||
description = "Stores empty values as NULL rather than ''"
|
||||
@ -11,3 +17,16 @@ class NullableCharField(models.CharField):
|
||||
|
||||
def get_prep_value(self, value):
|
||||
return value or None
|
||||
|
||||
|
||||
class ColorField(models.CharField):
|
||||
default_validators = [validate_color]
|
||||
description = "A hexadecimal RGB color code"
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
kwargs['max_length'] = 6
|
||||
super(ColorField, self).__init__(*args, **kwargs)
|
||||
|
||||
def formfield(self, **kwargs):
|
||||
kwargs['widget'] = ColorSelect
|
||||
return super(ColorField, self).formfield(**kwargs)
|
||||
|
@ -11,6 +11,32 @@ from django.utils.html import format_html
|
||||
from django.utils.safestring import mark_safe
|
||||
|
||||
|
||||
COLOR_CHOICES = (
|
||||
('aa1409', 'Dark red'),
|
||||
('f44336', 'Red'),
|
||||
('e91e63', 'Pink'),
|
||||
('ff66ff', 'Fuschia'),
|
||||
('9c27b0', 'Purple'),
|
||||
('673ab7', 'Dark purple'),
|
||||
('3f51b5', 'Indigo'),
|
||||
('2196f3', 'Blue'),
|
||||
('03a9f4', 'Light blue'),
|
||||
('00bcd4', 'Cyan'),
|
||||
('009688', 'Teal'),
|
||||
('2f6a31', 'Dark green'),
|
||||
('4caf50', 'Green'),
|
||||
('8bc34a', 'Light green'),
|
||||
('cddc39', 'Lime'),
|
||||
('ffeb3b', 'Yellow'),
|
||||
('ffc107', 'Amber'),
|
||||
('ff9800', 'Orange'),
|
||||
('ff5722', 'Dark orange'),
|
||||
('795548', 'Brown'),
|
||||
('c0c0c0', 'Light grey'),
|
||||
('9e9e9e', 'Grey'),
|
||||
('607d8b', 'Dark grey'),
|
||||
('111111', 'Black'),
|
||||
)
|
||||
NUMERIC_EXPANSION_PATTERN = '\[(\d+-\d+)\]'
|
||||
IP4_EXPANSION_PATTERN = '\[([0-9]{1,3}-[0-9]{1,3})\]'
|
||||
IP6_EXPANSION_PATTERN = '\[([0-9a-f]{1,4}-[0-9a-f]{1,4})\]'
|
||||
@ -71,6 +97,27 @@ class SmallTextarea(forms.Textarea):
|
||||
pass
|
||||
|
||||
|
||||
class ColorSelect(forms.Select):
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
kwargs['choices'] = COLOR_CHOICES
|
||||
super(ColorSelect, self).__init__(*args, **kwargs)
|
||||
|
||||
def render_option(self, selected_choices, option_value, option_label):
|
||||
if option_value is None:
|
||||
option_value = ''
|
||||
option_value = force_text(option_value)
|
||||
if option_value in selected_choices:
|
||||
selected_html = mark_safe(' selected')
|
||||
if not self.allow_multiple_selected:
|
||||
# Only allow for a single selection.
|
||||
selected_choices.remove(option_value)
|
||||
else:
|
||||
selected_html = ''
|
||||
return format_html('<option value="{}"{} style="background-color: #{}">{}</option>',
|
||||
option_value, selected_html, option_value, force_text(option_label))
|
||||
|
||||
|
||||
class SelectWithDisabled(forms.Select):
|
||||
"""
|
||||
Modified the stock Select widget to accept choices using a dict() for a label. The dict for each option must include
|
||||
|
Loading…
Reference in New Issue
Block a user