Horizon selects are now themable: Project Panels

Based on Diana's work.
Complete for Admin Panels: https://review.openstack.org/#/c/290204/

Horizon used a standard select input. Unfortunately this type of
input is only customizable to a small extend. Note: not all
ChoiceFields were converted because they have unexpected
impact.

Change-Id: Id9405c6f0127d6e86db09dfaf772f1275a7d7547
Partially-implements: blueprint horizon-theme-css-reorg
This commit is contained in:
Cindy Lu 2016-06-23 11:59:40 -07:00
parent f9ba1ecc2f
commit 163489b5e9
19 changed files with 131 additions and 117 deletions

View File

@ -28,7 +28,7 @@ from openstack_dashboard.usage import quotas
class FloatingIpAllocate(forms.SelfHandlingForm):
pool = forms.ChoiceField(label=_("Pool"))
pool = forms.ThemableChoiceField(label=_("Pool"))
def __init__(self, *args, **kwargs):
super(FloatingIpAllocate, self).__init__(*args, **kwargs)

View File

@ -34,7 +34,7 @@ class AssociateIPAction(workflows.Action):
coerce=filters.get_int_or_uuid,
empty_value=None,
add_item_link=ALLOCATE_URL)
instance_id = forms.ChoiceField(label=_("Instance"))
instance_id = forms.ThemableChoiceField(label=_("Instance"))
class Meta(object):
name = _("IP Address")

View File

@ -53,8 +53,8 @@ class CreateContainer(forms.SelfHandlingForm):
name = forms.CharField(max_length=255,
label=_("Container Name"),
validators=[no_slash_validator])
access = forms.ChoiceField(label=_("Container Access"),
choices=ACCESS_CHOICES)
access = forms.ThemableChoiceField(label=_("Container Access"),
choices=ACCESS_CHOICES)
def handle(self, request, data):
try:

View File

@ -35,12 +35,12 @@ class UpdateRule(forms.SelfHandlingForm):
description = forms.CharField(
required=False,
max_length=80, label=_("Description"))
protocol = forms.ChoiceField(
protocol = forms.ThemableChoiceField(
label=_("Protocol"), required=False,
choices=[('TCP', _('TCP')), ('UDP', _('UDP')), ('ICMP', _('ICMP')),
('ANY', _('ANY'))],
help_text=_('Protocol for the firewall rule'))
action = forms.ChoiceField(
action = forms.ThemableChoiceField(
label=_("Action"), required=False,
choices=[('ALLOW', _('ALLOW')), ('DENY', _('DENY')),
('REJECT', _('REJECT'))],
@ -68,7 +68,7 @@ class UpdateRule(forms.SelfHandlingForm):
validators=[port_validator],
help_text=_('Destination port (integer in [1, 65535] or range'
' in a:b)'))
ip_version = forms.ChoiceField(
ip_version = forms.ThemableChoiceField(
label=_("IP Version"), required=False,
choices=[('4', '4'), ('6', '6')],
help_text=_('IP Version for Firewall Rule'))
@ -133,10 +133,10 @@ class UpdateFirewall(forms.SelfHandlingForm):
description = forms.CharField(max_length=80,
label=_("Description"),
required=False)
firewall_policy_id = forms.ChoiceField(label=_("Policy"))
admin_state_up = forms.ChoiceField(choices=[(True, _('UP')),
(False, _('DOWN'))],
label=_("Admin State"))
firewall_policy_id = forms.ThemableChoiceField(label=_("Policy"))
admin_state_up = forms.ThemableChoiceField(choices=[(True, _('UP')),
(False, _('DOWN'))],
label=_("Admin State"))
failure_url = 'horizon:project:firewalls:index'
@ -182,11 +182,11 @@ class UpdateFirewall(forms.SelfHandlingForm):
class InsertRuleToPolicy(forms.SelfHandlingForm):
firewall_rule_id = forms.ChoiceField(label=_("Insert Rule"))
insert_before = forms.ChoiceField(label=_("Before"),
required=False)
insert_after = forms.ChoiceField(label=_("After"),
required=False)
firewall_rule_id = forms.ThemableChoiceField(label=_("Insert Rule"))
insert_before = forms.ThemableChoiceField(label=_("Before"),
required=False)
insert_after = forms.ThemableChoiceField(label=_("After"),
required=False)
failure_url = 'horizon:project:firewalls:index'
@ -245,7 +245,7 @@ class InsertRuleToPolicy(forms.SelfHandlingForm):
class RemoveRuleFromPolicy(forms.SelfHandlingForm):
firewall_rule_id = forms.ChoiceField(label=_("Remove Rule"))
firewall_rule_id = forms.ThemableChoiceField(label=_("Remove Rule"))
failure_url = 'horizon:project:firewalls:index'

View File

@ -34,13 +34,13 @@ class AddRuleAction(workflows.Action):
max_length=80,
label=_("Description"),
required=False)
protocol = forms.ChoiceField(
protocol = forms.ThemableChoiceField(
label=_("Protocol"),
choices=[('tcp', _('TCP')),
('udp', _('UDP')),
('icmp', _('ICMP')),
('any', _('ANY'))],)
action = forms.ChoiceField(
action = forms.ThemableChoiceField(
label=_("Action"),
choices=[('allow', _('ALLOW')),
('deny', _('DENY')),
@ -63,7 +63,7 @@ class AddRuleAction(workflows.Action):
label=_("Destination Port/Port Range"),
required=False,
validators=[port_validator])
ip_version = forms.ChoiceField(
ip_version = forms.ThemableChoiceField(
label=_("IP Version"), required=False,
choices=[('4', '4'), ('6', '6')])
shared = forms.BooleanField(
@ -326,10 +326,10 @@ class AddFirewallAction(workflows.Action):
description = forms.CharField(max_length=80,
label=_("Description"),
required=False)
firewall_policy_id = forms.ChoiceField(label=_("Policy"))
admin_state_up = forms.ChoiceField(choices=[(True, _('UP')),
(False, _('DOWN'))],
label=_("Admin State"))
firewall_policy_id = forms.ThemableChoiceField(label=_("Policy"))
admin_state_up = forms.ThemableChoiceField(choices=[(True, _('UP')),
(False, _('DOWN'))],
label=_("Admin State"))
def __init__(self, request, *args, **kwargs):
super(AddFirewallAction, self).__init__(request, *args, **kwargs)

View File

@ -128,18 +128,18 @@ class CreateImageForm(forms.SelfHandlingForm):
kernel = forms.ChoiceField(
label=_('Kernel'),
required=False,
widget=forms.SelectWidget(
widget=forms.ThemableSelectWidget(
transform=lambda x: "%s (%s)" % (
x.name, defaultfilters.filesizeformat(x.size))))
ramdisk = forms.ChoiceField(
label=_('Ramdisk'),
required=False,
widget=forms.SelectWidget(
widget=forms.ThemableSelectWidget(
transform=lambda x: "%s (%s)" % (
x.name, defaultfilters.filesizeformat(x.size))))
disk_format = forms.ChoiceField(label=_('Format'),
choices=[],
widget=forms.Select(attrs={
widget=forms.ThemableSelectWidget(attrs={
'class': 'switchable',
'ng-model': 'ctrl.diskFormat'}))
architecture = forms.CharField(
@ -329,7 +329,7 @@ class UpdateImageForm(forms.SelfHandlingForm):
required=False,
widget=forms.TextInput(attrs={'readonly': 'readonly'}),
)
disk_format = forms.ChoiceField(
disk_format = forms.ThemableChoiceField(
label=_("Format"),
)
minimum_disk = forms.IntegerField(label=_("Minimum Disk (GB)"),

View File

@ -40,9 +40,10 @@ class RebuildInstanceForm(forms.SelfHandlingForm):
image = forms.ChoiceField(
label=_("Select Image"),
widget=forms.SelectWidget(attrs={'class': 'image-selector'},
data_attrs=('size', 'display-name'),
transform=_image_choice_title))
widget=forms.ThemableSelectWidget(
attrs={'class': 'image-selector'},
data_attrs=('size', 'display-name'),
transform=_image_choice_title))
password = forms.RegexField(
label=_("Rebuild Password"),
required=False,
@ -53,8 +54,8 @@ class RebuildInstanceForm(forms.SelfHandlingForm):
label=_("Confirm Rebuild Password"),
required=False,
widget=forms.PasswordInput(render_value=False))
disk_config = forms.ChoiceField(label=_("Disk Partition"),
required=False)
disk_config = forms.ThemableChoiceField(label=_("Disk Partition"),
required=False)
def __init__(self, request, *args, **kwargs):
super(RebuildInstanceForm, self).__init__(request, *args, **kwargs)
@ -284,7 +285,7 @@ class DetachVolume(forms.SelfHandlingForm):
class AttachInterface(forms.SelfHandlingForm):
instance_id = forms.CharField(widget=forms.HiddenInput())
network = forms.ChoiceField(label=_("Network"))
network = forms.ThemableChoiceField(label=_("Network"))
def __init__(self, request, *args, **kwargs):
super(AttachInterface, self).__init__(request, *args, **kwargs)
@ -308,7 +309,7 @@ class AttachInterface(forms.SelfHandlingForm):
class DetachInterface(forms.SelfHandlingForm):
instance_id = forms.CharField(widget=forms.HiddenInput())
port = forms.ChoiceField(label=_("Port"))
port = forms.ThemableChoiceField(label=_("Port"))
def __init__(self, request, *args, **kwargs):
super(DetachInterface, self).__init__(request, *args, **kwargs)

View File

@ -35,8 +35,9 @@ class SetFlavorChoiceAction(workflows.Action):
widget=forms.TextInput(attrs={'readonly': 'readonly'}),
required=False,
)
flavor = forms.ChoiceField(label=_("New Flavor"),
help_text=_("Choose the flavor to launch."))
flavor = forms.ThemableChoiceField(
label=_("New Flavor"),
help_text=_("Choose the flavor to launch."))
class Meta(object):
name = _("Flavor Choice")

View File

@ -34,10 +34,11 @@ class UpdatePool(forms.SelfHandlingForm):
attrs={'readonly': 'readonly'}))
description = forms.CharField(required=False,
max_length=80, label=_("Description"))
lb_method = forms.ChoiceField(label=_("Load Balancing Method"))
admin_state_up = forms.ChoiceField(choices=[(True, _('UP')),
(False, _('DOWN'))],
label=_("Admin State"))
lb_method = forms.ThemableChoiceField(label=_("Load Balancing Method"))
admin_state_up = forms.ThemableChoiceField(
choices=[(True, _('UP')),
(False, _('DOWN'))],
label=_("Admin State"))
failure_url = 'horizon:project:loadbalancers:index'
@ -76,8 +77,8 @@ class UpdateVip(forms.SelfHandlingForm):
attrs={'readonly': 'readonly'}))
description = forms.CharField(required=False,
max_length=80, label=_("Description"))
pool_id = forms.ChoiceField(label=_("Pool"))
session_persistence = forms.ChoiceField(
pool_id = forms.ThemableChoiceField(label=_("Pool"))
session_persistence = forms.ThemableChoiceField(
required=False, initial={}, label=_("Session Persistence"))
cookie_name = forms.CharField(
@ -90,9 +91,10 @@ class UpdateVip(forms.SelfHandlingForm):
min_value=-1, label=_("Connection Limit"),
help_text=_("Maximum number of connections allowed "
"for the VIP or '-1' if the limit is not set"))
admin_state_up = forms.ChoiceField(choices=[(True, _('UP')),
(False, _('DOWN'))],
label=_("Admin State"))
admin_state_up = forms.ThemableChoiceField(
choices=[(True, _('UP')),
(False, _('DOWN'))],
label=_("Admin State"))
failure_url = 'horizon:project:loadbalancers:index'
@ -169,13 +171,14 @@ class UpdateMember(forms.SelfHandlingForm):
member_id = forms.CharField(label=_("ID"),
widget=forms.TextInput(
attrs={'readonly': 'readonly'}))
pool_id = forms.ChoiceField(label=_("Pool"))
pool_id = forms.ThemableChoiceField(label=_("Pool"))
weight = forms.IntegerField(max_value=256, min_value=0, label=_("Weight"),
help_text=_("Relative part of requests this "
"pool member serves compared to others"))
admin_state_up = forms.ChoiceField(choices=[(True, _('UP')),
(False, _('DOWN'))],
label=_("Admin State"))
admin_state_up = forms.ThemableChoiceField(
choices=[(True, _('UP')),
(False, _('DOWN'))],
label=_("Admin State"))
failure_url = 'horizon:project:loadbalancers:index'
@ -236,9 +239,10 @@ class UpdateMonitor(forms.SelfHandlingForm):
label=_("Max Retries (1~10)"),
help_text=_("Number of permissible failures before changing "
"the status of member to inactive"))
admin_state_up = forms.ChoiceField(choices=[(True, _('UP')),
(False, _('DOWN'))],
label=_("Admin State"))
admin_state_up = forms.ThemableChoiceField(
choices=[(True, _('UP')),
(False, _('DOWN'))],
label=_("Admin State"))
failure_url = 'horizon:project:loadbalancers:index'

View File

@ -39,13 +39,14 @@ class AddPoolAction(workflows.Action):
max_length=80, label=_("Description"))
# provider is optional because some LBaaS implementation does
# not support service-type extension.
provider = forms.ChoiceField(label=_("Provider"), required=False)
subnet_id = forms.ChoiceField(label=_("Subnet"))
protocol = forms.ChoiceField(label=_("Protocol"))
lb_method = forms.ChoiceField(label=_("Load Balancing Method"))
admin_state_up = forms.ChoiceField(choices=[(True, _('UP')),
(False, _('DOWN'))],
label=_("Admin State"))
provider = forms.ThemableChoiceField(label=_("Provider"), required=False)
subnet_id = forms.ThemableChoiceField(label=_("Subnet"))
protocol = forms.ThemableChoiceField(label=_("Protocol"))
lb_method = forms.ThemableChoiceField(label=_("Load Balancing Method"))
admin_state_up = forms.ThemableChoiceField(
choices=[(True, _('UP')),
(False, _('DOWN'))],
label=_("Admin State"))
def __init__(self, request, *args, **kwargs):
super(AddPoolAction, self).__init__(request, *args, **kwargs)
@ -151,9 +152,9 @@ class AddVipAction(workflows.Action):
description = forms.CharField(
initial="", required=False,
max_length=80, label=_("Description"))
subnet_id = forms.ChoiceField(label=_("VIP Subnet"),
initial="",
required=False)
subnet_id = forms.ThemableChoiceField(label=_("VIP Subnet"),
initial="",
required=False)
address = forms.IPField(label=_("IP address"),
version=forms.IPv4 | forms.IPv6,
mask=False,
@ -163,10 +164,10 @@ class AddVipAction(workflows.Action):
help_text=_("Enter an integer value "
"between 1 and 65535."),
validators=[validators.validate_port_range])
protocol = forms.ChoiceField(label=_("Protocol"))
protocol = forms.ThemableChoiceField(label=_("Protocol"))
session_persistence = forms.ChoiceField(
required=False, initial={}, label=_("Session Persistence"),
widget=forms.Select(attrs={
widget=forms.ThemableSelectWidget(attrs={
'class': 'switchable',
'data-slug': 'persistence'
}))
@ -184,9 +185,10 @@ class AddVipAction(workflows.Action):
required=False, min_value=-1, label=_("Connection Limit"),
help_text=_("Maximum number of connections allowed "
"for the VIP or '-1' if the limit is not set"))
admin_state_up = forms.ChoiceField(choices=[(True, _('UP')),
(False, _('DOWN'))],
label=_("Admin State"))
admin_state_up = forms.ThemableChoiceField(
choices=[(True, _('UP')),
(False, _('DOWN'))],
label=_("Admin State"))
def __init__(self, request, *args, **kwargs):
super(AddVipAction, self).__init__(request, *args, **kwargs)
@ -288,7 +290,7 @@ class AddVip(workflows.Workflow):
class AddMemberAction(workflows.Action):
pool_id = forms.ChoiceField(label=_("Pool"))
pool_id = forms.ThemableChoiceField(label=_("Pool"))
member_type = forms.ChoiceField(
label=_("Member Source"),
choices=[('server_list', _("Select from active instances")),
@ -330,9 +332,10 @@ class AddMemberAction(workflows.Action):
"members and can be modified later."),
validators=[validators.validate_port_range]
)
admin_state_up = forms.ChoiceField(choices=[(True, _('UP')),
(False, _('DOWN'))],
label=_("Admin State"))
admin_state_up = forms.ThemableChoiceField(
choices=[(True, _('UP')),
(False, _('DOWN'))],
label=_("Admin State"))
def __init__(self, request, *args, **kwargs):
super(AddMemberAction, self).__init__(request, *args, **kwargs)
@ -537,9 +540,10 @@ class AddMonitorAction(workflows.Action):
'data-type-http': _('Expected HTTP Status Codes'),
'data-type-https': _('Expected HTTP Status Codes')
}))
admin_state_up = forms.ChoiceField(choices=[(True, _('UP')),
(False, _('DOWN'))],
label=_("Admin State"))
admin_state_up = forms.ThemableChoiceField(
choices=[(True, _('UP')),
(False, _('DOWN'))],
label=_("Admin State"))
def __init__(self, request, *args, **kwargs):
super(AddMonitorAction, self).__init__(request, *args, **kwargs)
@ -616,7 +620,7 @@ class AddMonitor(workflows.Workflow):
class AddPMAssociationAction(workflows.Action):
monitor_id = forms.ChoiceField(label=_("Monitor"))
monitor_id = forms.ThemableChoiceField(label=_("Monitor"))
def __init__(self, request, *args, **kwargs):
super(AddPMAssociationAction, self).__init__(request, *args, **kwargs)
@ -679,7 +683,7 @@ class AddPMAssociation(workflows.Workflow):
class DeletePMAssociationAction(workflows.Action):
monitor_id = forms.ChoiceField(label=_("Monitor"))
monitor_id = forms.ThemableChoiceField(label=_("Monitor"))
def __init__(self, request, *args, **kwargs):
super(DeletePMAssociationAction, self).__init__(

View File

@ -38,10 +38,11 @@ class UpdateNetwork(forms.SelfHandlingForm):
network_id = forms.CharField(label=_("ID"),
widget=forms.TextInput(
attrs={'readonly': 'readonly'}))
admin_state = forms.ChoiceField(choices=[(True, _('UP')),
(False, _('DOWN'))],
required=False,
label=_("Admin State"))
admin_state = forms.ThemableChoiceField(
choices=[(True, _('UP')),
(False, _('DOWN'))],
required=False,
label=_("Admin State"))
shared = forms.BooleanField(label=_("Shared"), required=False)
failure_url = 'horizon:project:networks:index'

View File

@ -38,9 +38,10 @@ class UpdatePort(forms.SelfHandlingForm):
name = forms.CharField(max_length=255,
label=_("Name"),
required=False)
admin_state = forms.ChoiceField(choices=[(True, _('UP')),
(False, _('DOWN'))],
label=_("Admin State"))
admin_state = forms.ThemableChoiceField(
choices=[(True, _('UP')),
(False, _('DOWN'))],
label=_("Admin State"))
failure_url = 'horizon:project:networks:detail'
def __init__(self, request, *args, **kwargs):

View File

@ -81,12 +81,12 @@ class CreateSubnet(network_workflows.CreateNetwork):
class UpdateSubnetInfoAction(CreateSubnetInfoAction):
address_source = forms.ChoiceField(widget=forms.HiddenInput(),
required=False)
subnetpool = forms.ChoiceField(widget=forms.HiddenInput(),
required=False)
prefixlen = forms.ChoiceField(widget=forms.HiddenInput(),
required=False)
address_source = forms.ThemableChoiceField(widget=forms.HiddenInput(),
required=False)
subnetpool = forms.ThemableChoiceField(widget=forms.HiddenInput(),
required=False)
prefixlen = forms.ThemableChoiceField(widget=forms.HiddenInput(),
required=False)
cidr = forms.IPField(label=_("Network Address"),
required=False,
initial="",
@ -104,9 +104,9 @@ class UpdateSubnetInfoAction(CreateSubnetInfoAction):
# when re-POST since the value of the ChoiceField is not set.
# Thus now I use HiddenInput for the ip_version ChoiceField as a work
# around.
ip_version = forms.ChoiceField(choices=[(4, 'IPv4'), (6, 'IPv6')],
widget=forms.HiddenInput(),
label=_("IP Version"))
ip_version = forms.ThemableChoiceField(choices=[(4, 'IPv4'), (6, 'IPv6')],
widget=forms.HiddenInput(),
label=_("IP Version"))
class Meta(object):
name = _("Subnet")

View File

@ -41,16 +41,17 @@ class CreateNetworkInfoAction(workflows.Action):
widget = None
else:
widget = forms.HiddenInput()
net_profile_id = forms.ChoiceField(label=_("Network Profile"),
required=False,
widget=widget)
net_profile_id = forms.ThemableChoiceField(label=_("Network Profile"),
required=False,
widget=widget)
admin_state = forms.ChoiceField(choices=[(True, _('UP')),
(False, _('DOWN'))],
label=_("Admin State"),
required=False,
help_text=_("The state to start"
" the network in."))
admin_state = forms.ThemableChoiceField(
choices=[(True, _('UP')),
(False, _('DOWN'))],
label=_("Admin State"),
required=False,
help_text=_("The state to start"
" the network in."))
shared = forms.BooleanField(label=_("Shared"), initial=False,
required=False)
with_subnet = forms.BooleanField(label=_("Create Subnet"),

View File

@ -34,12 +34,12 @@ LOG = logging.getLogger(__name__)
class CreateForm(forms.SelfHandlingForm):
name = forms.CharField(max_length=255, label=_("Router Name"),
required=False)
admin_state_up = forms.ChoiceField(label=_("Admin State"),
choices=[(True, _('UP')),
(False, _('DOWN'))],
required=False)
external_network = forms.ChoiceField(label=_("External Network"),
required=False)
admin_state_up = forms.ThemableChoiceField(label=_("Admin State"),
choices=[(True, _('UP')),
(False, _('DOWN'))],
required=False)
external_network = forms.ThemableChoiceField(label=_("External Network"),
required=False)
mode = forms.ChoiceField(label=_("Router Type"))
ha = forms.ChoiceField(label=_("High Availability Mode"))
failure_url = 'horizon:project:routers:index'
@ -116,13 +116,13 @@ class CreateForm(forms.SelfHandlingForm):
class UpdateForm(forms.SelfHandlingForm):
name = forms.CharField(label=_("Name"), required=False)
admin_state = forms.ChoiceField(choices=[(True, _('UP')),
(False, _('DOWN'))],
label=_("Admin State"))
admin_state = forms.ThemableChoiceField(choices=[(True, _('UP')),
(False, _('DOWN'))],
label=_("Admin State"))
router_id = forms.CharField(label=_("ID"),
widget=forms.TextInput(
attrs={'readonly': 'readonly'}))
mode = forms.ChoiceField(label=_("Router Type"))
mode = forms.ThemableChoiceField(label=_("Router Type"))
ha = forms.BooleanField(label=_("High Availability Mode"), required=False)
redirect_url = reverse_lazy('horizon:project:routers:index')

View File

@ -26,7 +26,7 @@ LOG = logging.getLogger(__name__)
class AddInterface(forms.SelfHandlingForm):
subnet_id = forms.ChoiceField(label=_("Subnet"))
subnet_id = forms.ThemableChoiceField(label=_("Subnet"))
ip_address = forms.IPField(
label=_("IP Address (optional)"), required=False, initial="",
help_text=_("Specify an IP address for the interface "
@ -151,7 +151,7 @@ class AddInterface(forms.SelfHandlingForm):
class SetGatewayForm(forms.SelfHandlingForm):
network_id = forms.ChoiceField(label=_("External Network"))
network_id = forms.ThemableChoiceField(label=_("External Network"))
router_name = forms.CharField(label=_("Router Name"),
widget=forms.TextInput(
attrs={'readonly': 'readonly'}))

View File

@ -65,7 +65,8 @@ class CreateBackupForm(forms.SelfHandlingForm):
class RestoreBackupForm(forms.SelfHandlingForm):
volume_id = forms.ChoiceField(label=_('Select Volume'), required=False)
volume_id = forms.ThemableChoiceField(label=_('Select Volume'),
required=False)
backup_id = forms.CharField(widget=forms.HiddenInput())
backup_name = forms.CharField(widget=forms.HiddenInput())

View File

@ -29,7 +29,7 @@ class CreateCGroupForm(forms.SelfHandlingForm):
required=False)
snapshot_source = forms.ChoiceField(
label=_("Use snapshot as a source"),
widget=forms.SelectWidget(
widget=forms.ThemableSelectWidget(
attrs={'class': 'snapshot-selector'},
data_attrs=('name'),
transform=lambda x: "%s" % (x.name)),

View File

@ -167,7 +167,7 @@ class CloneCGroupForm(forms.SelfHandlingForm):
required=False)
cgroup_source = forms.ChoiceField(
label=_("Use a consistency group as source"),
widget=forms.SelectWidget(
widget=forms.ThemableSelectWidget(
attrs={'class': 'image-selector'},
data_attrs=('name'),
transform=lambda x: "%s" % (x.name)),