Service configuration read only form

This replaces current service configuration view with form view
that is consistent with wireframes and editable form view which is
to be implemented.

Change-Id: Ic4ea20fb6c2b3dc261af30c0d23e695d546ccae8
This commit is contained in:
Jiri Tomasek 2015-02-03 15:46:04 +01:00
parent fc4946b816
commit 1897d28726
6 changed files with 181 additions and 195 deletions

View File

@ -22,14 +22,15 @@ import netaddr
SEPARATOR_RE = re.compile('[\s,;|]+', re.UNICODE)
def fieldset(self, *args, **kwargs):
def fieldset(form, *args, **kwargs):
"""A helper function for grouping fields based on their names."""
prefix = kwargs.pop('prefix', None)
names = args or self.fields.keys()
prefix = kwargs.pop('prefix', '.*')
names = args or form.fields.keys()
for name in names:
if prefix is None or name.startswith(prefix):
yield forms.forms.BoundField(self, self.fields[name], name)
if prefix is not None and re.match(prefix, name):
yield forms.forms.BoundField(form, form.fields[name], name)
class MACDialect(netaddr.mac_eui48):
@ -133,3 +134,24 @@ class LabelWidget(forms.Widget):
if value:
return html.escape(value)
return ''
class StaticTextWidget(forms.Widget):
def render(self, name, value, attrs=None):
if value is None:
value = ''
return html.format_html('<p class="form-control-static">{0}</p>',
value)
class StaticTextPasswordWidget(forms.Widget):
def render(self, name, value, attrs=None):
if value is None or value == '':
return html.format_html(u'<p class="form-control-static"></p>')
else:
return html.format_html(
u'<p class="form-control-static">'
u'<a href="" class="btn btn-default btn-xs password-button"'
u' data-content="{0}"><i class="fa fa-eye"></i>&nbsp;{1}</a>'
u'</p>', value, _(u"Reveal")
)

View File

@ -16,13 +16,15 @@ import json
import logging
import django.forms
from django.utils import html
from django.utils.translation import ugettext_lazy as _
import horizon.exceptions
import horizon.forms
import horizon.messages
from tuskar_ui import api
import tuskar_ui.forms
from tuskar_ui.utils import utils
LOG = logging.getLogger(__name__)
@ -44,6 +46,58 @@ CINDER_ISCSI_HELPER_CHOICES = [
]
def name_with_tooltip(parameter):
humanized_name = utils.de_camel_case(parameter.stripped_name)
if not parameter.description:
return humanized_name
return html.format_html(
u'{0}&nbsp;<a class="help-icon fa fa-question-circle" '
u'data-content="{1}" tabindex="0" href="#" '
u'title="{2}"></a>',
html.escape(humanized_name),
html.escape(parameter.description),
html.escape(humanized_name)
)
class ServiceConfig(horizon.forms.SelfHandlingForm):
def __init__(self, *args, **kwargs):
super(ServiceConfig, self).__init__(*args, **kwargs)
plan = api.tuskar.Plan.get_the_plan(self.request)
parameters = plan.parameter_list(include_key_parameters=False)
for p in parameters:
if p.hidden:
self.fields[p.name] = django.forms.CharField(
required=False,
widget=tuskar_ui.forms.StaticTextPasswordWidget(),
label=name_with_tooltip(p))
else:
self.fields[p.name] = django.forms.CharField(
required=False,
widget=tuskar_ui.forms.StaticTextWidget(),
label=name_with_tooltip(p))
def global_fieldset(self):
return tuskar_ui.forms.fieldset(self, prefix='^(?!.*::)')
def controller_fieldset(self):
return tuskar_ui.forms.fieldset(self, prefix='controller-1')
def compute_fieldset(self):
return tuskar_ui.forms.fieldset(self, prefix='compute-1')
def block_storage_fieldset(self):
return tuskar_ui.forms.fieldset(self, prefix='cinder-storage-1')
def object_storage_fieldset(self):
return tuskar_ui.forms.fieldset(self, prefix='swift-storage-1')
def handle():
pass
class EditServiceConfig(horizon.forms.SelfHandlingForm):
virt_type = django.forms.ChoiceField(
label=_("Deployment Type"),

View File

@ -1,97 +0,0 @@
# -*- coding: utf8 -*-
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from django.utils.html import escape # noqa
from django.utils.safestring import mark_safe # noqa
from django.utils.translation import ugettext_lazy as _
from horizon import tables
def name_with_tooltip(parameter):
if not parameter.description:
return parameter.stripped_name
return mark_safe(
u'%s&nbsp;<a class="help-icon fa fa-question-circle" '
u'data-content="%s" tabindex="0" href="#" '
u'title="%s"></a>' % (
escape(parameter.stripped_name),
escape(parameter.description),
escape(parameter.stripped_name),
),
)
def value_or_hidden(parameter):
if parameter.hidden:
return mark_safe(
u'<span class="btn btn-xs btn-default password-button" '
u'data-content="%s"'
u'><i class="fa fa-eye"></i>&nbsp;%s</span>' % (
escape(parameter.value),
escape(_(u"Reveal")),
),
)
return parameter.value
class ParametersTable(tables.DataTable):
name = tables.Column(
name_with_tooltip,
verbose_name=_("Parameter Name"),
classes=['data-table-config-label'],
)
value = tables.Column(
value_or_hidden,
verbose_name=_("Value"),
classes=['data-table-config-value'],
)
def get_object_id(self, datum):
return datum.name
class Meta:
name = "parameters"
verbose_name = _("Service Configuration")
table_actions = ()
row_actions = ()
template = "horizon/common/_definition_list_data_table.html"
class GlobalParametersTable(ParametersTable):
class Meta(ParametersTable.Meta):
name = "global_parameters"
verbose_name = _("Global")
class ControllerParametersTable(ParametersTable):
class Meta(ParametersTable.Meta):
name = "controller_parameters"
verbose_name = _("Controller")
class ComputeParametersTable(ParametersTable):
class Meta(ParametersTable.Meta):
name = "compute_parameters"
verbose_name = _("Compute")
class BlockStorageParametersTable(ParametersTable):
class Meta(ParametersTable.Meta):
name = "block_storage_parameters"
verbose_name = _("Block Storage")
class ObjectStorageParametersTable(ParametersTable):
class Meta(ParametersTable.Meta):
name = "object_storage_parameters"
verbose_name = _("Object Storage")

View File

@ -1,56 +1,70 @@
{% extends 'infrastructure/base.html' %}
{% extends "infrastructure/base.html" %}
{% load i18n %}
{% load url from future %}
{% block title %}{% trans 'Service Configuration' %}{% endblock %}
{% block title %}{% trans "Service Configuration" %}{% endblock %}
{% block page_header %}
{% include 'horizon/common/_items_count_domain_page_header.html' with title=_('Service Configuration') %}
{% endblock page_header %}
{% endblock %}
{% block main %}
<div class="row">
<div class="col-xs-2">
<ul class="nav nav-pills nav-stacked nav-arrow" role="tablist">
<li class="active"><a href="#global" role="tab" data-toggle="tab">{% trans "Global" %}</a></li>
<li><a href="#controller" role="tab" data-toggle="tab">{% trans "Controller" %}</a></li>
<li><a href="#compute" role="tab" data-toggle="tab">{% trans "Compute" %}</a></li>
<li><a href="#block-storage" role="tab" data-toggle="tab">{% trans "Block Storage" %}</a></li>
<li><a href="#object-storage" role="tab" data-toggle="tab">{% trans "Object Storage" %}</a></li>
</ul>
<div class="row">
<form class="form-horizontal">
{% include 'horizon/common/_form_errors.html' with form=form %}
<div class="col-md-2">
<ul class="nav nav-pills nav-stacked nav-arrow" role="tablist">
<li class="active"><a href="#global" role="tab" data-toggle="tab">{% trans "Global" %}</a></li>
<li><a href="#controller" role="tab" data-toggle="tab">{% trans "Controller" %}</a></li>
<li><a href="#compute" role="tab" data-toggle="tab">{% trans "Compute" %}</a></li>
<li><a href="#block-storage" role="tab" data-toggle="tab">{% trans "Block Storage" %}</a></li>
<li><a href="#object-storage" role="tab" data-toggle="tab">{% trans "Object Storage" %}</a></li>
</ul>
</div>
<div class="col-md-10">
<div class="tab-content panel panel-default configuration-panel">
<div class="tab-pane active" id="global">
{% for field in form.global_fieldset %}
{% include 'horizon/common/_horizontal_field.html' with field=field %}
{% endfor %}
</div>
<div class="tab-pane" id="controller">
{% for field in form.controller_fieldset %}
{% include 'horizon/common/_horizontal_field.html' with field=field %}
{% endfor %}
</div>
<div class="tab-pane" id="compute">
{% for field in form.compute_fieldset %}
{% include 'horizon/common/_horizontal_field.html' with field=field %}
{% endfor %}
</div>
<div class="tab-pane" id="block-storage">
{% for field in form.block_storage_fieldset %}
{% include 'horizon/common/_horizontal_field.html' with field=field %}
{% endfor %}
</div>
<div class="tab-pane" id="object-storage">
{% for field in form.object_storage_fieldset %}
{% include 'horizon/common/_horizontal_field.html' with field=field %}
{% endfor %}
</div>
</div>
</div>
</form>
</div>
<div class="col-xs-10">
<div class="tab-content panel panel-default configuration-panel">
<div class="tab-pane active" id="global">
{{ global_parameters_table.render }}
</div>
<div class="tab-pane" id="controller">
{{ controller_parameters_table.render }}
</div>
<div class="tab-pane" id="compute">
{{ compute_parameters_table.render }}
</div>
<div class="tab-pane" id="block-storage">
{{ block_storage_parameters_table.render }}
</div>
<div class="tab-pane" id="object-storage">
{{ object_storage_parameters_table.render }}
</div>
</div>
</div>
</div>
<script type="text/javascript">
(window.$ || window.addHorizonLoadEvent)(function () {
$('a.help-icon').click(function () {
return false;
}).popover({
trigger: 'focus',
placement: 'right'
<script type="text/javascript">
(window.$ || window.addHorizonLoadEvent)(function () {
$(document).tooltip('hide'); // prevent horizon from adding tooltip
$('a.help-icon').click(function () {
return false;
}).popover({
trigger: 'focus',
placement: 'right'
});
$('a.password-button').popover({
trigger: 'click',
placement: 'right'
});
});
$('span.password-button').popover({
trigger: 'click',
placement: 'top'
});
});
</script>
</script>
{% endblock %}

View File

@ -20,7 +20,6 @@ import horizon.tables
from tuskar_ui import api
from tuskar_ui.infrastructure.parameters import forms
from tuskar_ui.infrastructure.parameters import tables
class ServiceConfigView(horizon.forms.ModalFormView):
@ -60,42 +59,15 @@ class ServiceConfigView(horizon.forms.ModalFormView):
'virt_type': virt_type}
class IndexView(horizon.tables.MultiTableView):
table_classes = (
tables.GlobalParametersTable,
tables.ControllerParametersTable,
tables.ComputeParametersTable,
tables.BlockStorageParametersTable,
tables.ObjectStorageParametersTable,
)
class IndexView(horizon.forms.ModalFormView):
form_class = forms.ServiceConfig
template_name = "infrastructure/parameters/index.html"
def get(self, request, *args, **kwargs):
self.plan = api.tuskar.Plan.get_the_plan(request)
def get_initial(self):
self.plan = api.tuskar.Plan.get_the_plan(self.request)
self.parameters = self.plan.parameter_list(
include_key_parameters=False)
return super(IndexView, self).get(request, *args, **kwargs)
def _get_parameters(self, role_name=None):
if not role_name:
return [p for p in self.parameters if p.role is None]
return [p for p in self.parameters
if p.role and p.role.name == role_name]
def get_global_parameters_data(self):
return self._get_parameters(None)
def get_controller_parameters_data(self):
return self._get_parameters('controller')
def get_compute_parameters_data(self):
return self._get_parameters('compute')
def get_block_storage_parameters_data(self):
return self._get_parameters('cinder-storage')
def get_object_storage_parameters_data(self):
return self._get_parameters('swift-storage')
return {p.name: p.value for p in self.parameters}
def get_context_data(self, **kwargs):
context = super(IndexView, self).get_context_data(**kwargs)

View File

@ -219,29 +219,50 @@ table.definition-list-table > thead {
table.definition-list-table > tbody > tr > td {
border: 0;
}
ul.nav-arrow {
padding-top: 30px;
& > li {
z-index: 1000;
position: relative;
margin-right: -32px;
}
& > li.active:after {
display: block;
content: '';
position: absolute;
top: 1px;
right: -7px;
border-top: 18px solid transparent;
border-bottom: 18px solid transparent;
border-left: 8px solid $link-color;
z-index: 1000;
position: relative;
margin-right: -30px;
& > a {
border-top-right-radius: 0;
border-bottom-right-radius: 0;
}
&.active {
margin-right: -31px;
&:after {
display: block;
content: '';
position: absolute;
top: 0px;
right: -12px;
border-top: 19px solid transparent;
border-bottom: 19px solid transparent;
border-left: 12px solid $link-color;
}
}
}
}
.configuration-panel {
min-height: 40em;
padding: 15px 15px 15px 30px !important;
border: none;
border-left: 1px solid $border-color;
border-radius: 0;
box-shadow: none;
}
.password-button + div.popover {
white-space: pre-wrap;
word-wrap: break-word;
}
.form-horizontal label .popover {
min-width: 250px;
.popover-content {
font-weight: normal;
}
}