summaryrefslogtreecommitdiff
path: root/tuskar_ui/forms.py
blob: e5aab204389ce35e4df7daae0bc084730165fffc (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
#
#    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.

import re

from django import forms
from django.utils import html
from django.utils.translation import ugettext_lazy as _
import netaddr


SEPARATOR_RE = re.compile('[\s,;|]+', re.UNICODE)


def label_with_tooltip(label, tooltip=None, title=None):
    if not tooltip:
        return label
    return html.format_html(
        u'{0}&nbsp;<a class="help-icon fa fa-question-circle" '
        u'data-content="{1}" tabindex="0" href="#" '
        u'data-title="{2}"></a>',
        html.escape(label),
        html.escape(tooltip),
        html.escape(title or label)
    )


def fieldset(form, *args, **kwargs):
    """A helper function for grouping fields based on their names."""

    prefix = kwargs.pop('prefix', '.*')
    names = args or form.fields.keys()

    for name in names:
        if prefix is not None and re.match(prefix, name):
            yield forms.forms.BoundField(form, form.fields[name], name)


class MACDialect(netaddr.mac_eui48):
    """For validating MAC addresses. Same validation as Nova uses."""
    word_fmt = '%.02x'
    word_sep = ':'


def normalize_MAC(value):
    try:
        return str(netaddr.EUI(
            value.strip(), version=48, dialect=MACDialect)).upper()
    except (netaddr.AddrFormatError, TypeError):
        raise ValueError('Invalid MAC address')


class NumberInput(forms.widgets.TextInput):
    """A form input for numbers."""
    input_type = 'number'


class NumberPickerInput(forms.widgets.TextInput):
    """A form input that is rendered as a big number picker."""

    def __init__(self, attrs=None):
        default_attrs = {'class': 'number-picker'}
        if attrs:
            default_attrs.update(attrs)
        super(NumberPickerInput, self).__init__(default_attrs)


class MACField(forms.fields.Field):
    """A form field for entering a single MAC address."""

    def clean(self, value):
        value = super(MACField, self).clean(value)
        try:
            return normalize_MAC(value)
        except ValueError:
            raise forms.ValidationError(_(u'Enter a valid MAC address.'))


class MultiMACField(forms.fields.Field):
    """A form field for entering multiple MAC addresses.

       The individual MAC addresses can be separated by any whitespace,
       commas, semicolons or pipe characters.

       Gives a string of normalized MAC addresses separated by spaces.
    """

    def clean(self, value):
        value = super(MultiMACField, self).clean(value)

        macs = []
        for mac in SEPARATOR_RE.split(value):
            if mac:
                try:
                    normalized_mac = normalize_MAC(mac)
                except ValueError:
                    raise forms.ValidationError(
                        _(u'%r is not a valid MAC address.') % mac)
                else:
                    macs.append(normalized_mac)

        return ' '.join(sorted(set(macs)))


class NetworkField(forms.fields.Field):
    """A form field for entering a network specification with a mask."""

    def clean(self, value):
        value = super(NetworkField, self).clean(value)
        try:
            return str(netaddr.IPNetwork(value, version=4))
        except netaddr.AddrFormatError:
            raise forms.ValidationError(_("Enter valid IPv4 network address."))


class SelfHandlingFormset(forms.formsets.BaseFormSet):
    def __init__(self, *args, **kwargs):
        self.request = kwargs.pop('request', None)
        super(SelfHandlingFormset, self).__init__(*args, **kwargs)

    def handle(self, request, data):
        success = True
        for form in self:
            form_success = form.handle(request, form.cleaned_data)
            if not form_success:
                success = False
            else:
                pass
        return success


class LabelWidget(forms.Widget):
    """A widget for displaying information.

    This is a custom widget to show context information just as text,
    as readonly inputs are confusing.
    Note that the field also must be required=False, as no input
    is rendered, and it must be ignored in the handle() method.
    """
    def render(self, name, value, attrs=None):
        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")
            )