Skip to content

Commit a225fd5

Browse files
committed
Make new branch from trunk and merge facelift-r9007 into it, fixing a few merge conflicts
- Legacy-Id: 9072
2 parents f3b22aa + ab74e5c commit a225fd5

804 files changed

Lines changed: 20353 additions & 18639 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

README

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
This is the "facelift" datatracker branch that uses Twitter Bootstrap for
2+
the UI.
3+
4+
You need to install a few new django extensions:
5+
https://pypi.python.org/pypi/django-widget-tweaks
6+
https://pypi.python.org/pypi/django-bootstrap3
7+
https://pypi.python.org/pypi/django-typogrify
8+
9+
The meta goal of this effort is: *** NO CHANGES TO THE PYTHON CODE ***
10+
11+
Whenever changes to the python code are made, they can only fix HTML bugs,
12+
or add comments (tagged with "FACELIFT") about functionality that can be
13+
removed once the facelift templates become default. Or they need to add
14+
functionality that is only called from the new facelift templates.
15+
16+
Javascript that is only used on one template goes into that template.
17+
Javascript that is used by more than one template goes into ietf.js.
18+
19+
CSS that is only used on one template goes into that template.
20+
CSS that is used by more than one template goes into ietf.css. No CSS in the
21+
templates or - god forbid - style tags! (And no CSS or HTML styling in
22+
python code!!)
23+
24+
Templates that use jquery or bootstrap plugins include the css in the pagehead
25+
block, and the js in the js block.

TODO

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
Major pieces not facelifted: milestone editing, liaison editing, WG workflow customization
2+
3+
Use affix for navigation on active_wgs.html
4+
5+
Figure out why {% if debug %} does not work in the text templates under ietf/templates_facelift/community/public.
6+
7+
Make django generate HTML5 date inputs or use a js-based datepicker.
8+
9+
Deferring ballots does not work. (Seems to be an upstream bug.)
10+
11+
Make tables that are too wide to usefully work on small screens responsive. See http://getbootstrap.com/css/#tables-responsive

bootstrap3/__init__.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# -*- coding: utf-8 -*-
2+
3+
__version__ = '5.1.1'

bootstrap3/bootstrap.py

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
# -*- coding: utf-8 -*-
2+
from __future__ import unicode_literals
3+
4+
from django.conf import settings
5+
from django.utils.importlib import import_module
6+
7+
8+
# Default settings
9+
BOOTSTRAP3_DEFAULTS = {
10+
'jquery_url': '//code.jquery.com/jquery.min.js',
11+
'base_url': '//netdna.bootstrapcdn.com/bootstrap/3.3.2/',
12+
'css_url': None,
13+
'theme_url': None,
14+
'javascript_url': None,
15+
'javascript_in_head': False,
16+
'include_jquery': False,
17+
'horizontal_label_class': 'col-md-2',
18+
'horizontal_field_class': 'col-md-4',
19+
'set_required': True,
20+
'set_placeholder': True,
21+
'required_css_class': '',
22+
'error_css_class': 'has-error',
23+
'success_css_class': 'has-success',
24+
'formset_renderers': {
25+
'default': 'bootstrap3.renderers.FormsetRenderer',
26+
},
27+
'form_renderers': {
28+
'default': 'bootstrap3.renderers.FormRenderer',
29+
},
30+
'field_renderers': {
31+
'default': 'bootstrap3.renderers.FieldRenderer',
32+
'inline': 'bootstrap3.renderers.InlineFieldRenderer',
33+
},
34+
}
35+
36+
# Start with a copy of default settings
37+
BOOTSTRAP3 = BOOTSTRAP3_DEFAULTS.copy()
38+
39+
# Override with user settings from settings.py
40+
BOOTSTRAP3.update(getattr(settings, 'BOOTSTRAP3', {}))
41+
42+
43+
def get_bootstrap_setting(setting, default=None):
44+
"""
45+
Read a setting
46+
"""
47+
return BOOTSTRAP3.get(setting, default)
48+
49+
50+
def bootstrap_url(postfix):
51+
"""
52+
Prefix a relative url with the bootstrap base url
53+
"""
54+
return get_bootstrap_setting('base_url') + postfix
55+
56+
57+
def jquery_url():
58+
"""
59+
Return the full url to jQuery file to use
60+
"""
61+
return get_bootstrap_setting('jquery_url')
62+
63+
64+
def javascript_url():
65+
"""
66+
Return the full url to the Bootstrap JavaScript file
67+
"""
68+
return get_bootstrap_setting('javascript_url') or \
69+
bootstrap_url('js/bootstrap.min.js')
70+
71+
72+
def css_url():
73+
"""
74+
Return the full url to the Bootstrap CSS file
75+
"""
76+
return get_bootstrap_setting('css_url') or \
77+
bootstrap_url('css/bootstrap.min.css')
78+
79+
80+
def theme_url():
81+
"""
82+
Return the full url to the theme CSS file
83+
"""
84+
return get_bootstrap_setting('theme_url')
85+
86+
87+
def get_renderer(renderers, **kwargs):
88+
layout = kwargs.get('layout', '')
89+
path = renderers.get(layout, renderers['default'])
90+
mod, cls = path.rsplit(".", 1)
91+
return getattr(import_module(mod), cls)
92+
93+
94+
def get_formset_renderer(**kwargs):
95+
renderers = get_bootstrap_setting('formset_renderers')
96+
return get_renderer(renderers, **kwargs)
97+
98+
99+
def get_form_renderer(**kwargs):
100+
renderers = get_bootstrap_setting('form_renderers')
101+
return get_renderer(renderers, **kwargs)
102+
103+
104+
def get_field_renderer(**kwargs):
105+
renderers = get_bootstrap_setting('field_renderers')
106+
return get_renderer(renderers, **kwargs)

bootstrap3/components.py

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
# -*- coding: utf-8 -*-
2+
from __future__ import unicode_literals
3+
4+
from django.forms.widgets import flatatt
5+
6+
from .text import text_value
7+
8+
9+
def render_icon(icon, title=''):
10+
"""
11+
Render a Bootstrap glyphicon icon
12+
"""
13+
attrs = {
14+
'class': 'glyphicon glyphicon-{icon}'.format(icon=icon),
15+
}
16+
if title:
17+
attrs['title'] = title
18+
return '<span{attrs}></span>'.format(attrs=flatatt(attrs))
19+
20+
21+
def render_alert(content, alert_type=None, dismissable=True):
22+
"""
23+
Render a Bootstrap alert
24+
"""
25+
button = ''
26+
if not alert_type:
27+
alert_type = 'info'
28+
css_classes = ['alert', 'alert-' + text_value(alert_type)]
29+
if dismissable:
30+
css_classes.append('alert-dismissable')
31+
button = '<button type="button" class="close" ' + \
32+
'data-dismiss="alert" aria-hidden="true">&times;</button>'
33+
return '<div class="{css_classes}">{button}{content}</div>'.format(
34+
css_classes=' '.join(css_classes),
35+
button=button,
36+
content=text_value(content),
37+
)

bootstrap3/exceptions.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
# -*- coding: utf-8 -*-
2+
from __future__ import unicode_literals
3+
4+
5+
class BootstrapException(Exception):
6+
"""
7+
Any exception from this package
8+
"""
9+
pass
10+
11+
12+
class BootstrapError(BootstrapException):
13+
"""
14+
Any exception that is an error
15+
"""
16+
pass

bootstrap3/forms.py

Lines changed: 173 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,173 @@
1+
# -*- coding: utf-8 -*-
2+
from __future__ import unicode_literals
3+
4+
from django.contrib.admin.widgets import AdminFileWidget
5+
from django.forms import (
6+
HiddenInput, FileInput, CheckboxSelectMultiple, Textarea, TextInput
7+
)
8+
9+
from .bootstrap import (
10+
get_bootstrap_setting, get_form_renderer, get_field_renderer,
11+
get_formset_renderer
12+
)
13+
from .text import text_concat, text_value
14+
from .exceptions import BootstrapError
15+
from .utils import add_css_class, render_tag
16+
from .components import render_icon
17+
18+
19+
FORM_GROUP_CLASS = 'form-group'
20+
21+
22+
def render_formset(formset, **kwargs):
23+
"""
24+
Render a formset to a Bootstrap layout
25+
"""
26+
renderer_cls = get_formset_renderer(**kwargs)
27+
return renderer_cls(formset, **kwargs).render()
28+
29+
30+
def render_formset_errors(form, **kwargs):
31+
"""
32+
Render formset errors to a Bootstrap layout
33+
"""
34+
renderer_cls = get_formset_renderer(**kwargs)
35+
return renderer_cls(form, **kwargs).render_errors()
36+
37+
38+
def render_form(form, **kwargs):
39+
"""
40+
Render a formset to a Bootstrap layout
41+
"""
42+
renderer_cls = get_form_renderer(**kwargs)
43+
return renderer_cls(form, **kwargs).render()
44+
45+
46+
def render_form_errors(form, type='all', **kwargs):
47+
"""
48+
Render form errors to a Bootstrap layout
49+
"""
50+
renderer_cls = get_form_renderer(**kwargs)
51+
return renderer_cls(form, **kwargs).render_errors(type)
52+
53+
54+
def render_field(field, **kwargs):
55+
"""
56+
Render a formset to a Bootstrap layout
57+
"""
58+
renderer_cls = get_field_renderer(**kwargs)
59+
return renderer_cls(field, **kwargs).render()
60+
61+
62+
def render_label(content, label_for=None, label_class=None, label_title=''):
63+
"""
64+
Render a label with content
65+
"""
66+
attrs = {}
67+
if label_for:
68+
attrs['for'] = label_for
69+
if label_class:
70+
attrs['class'] = label_class
71+
if label_title:
72+
attrs['title'] = label_title
73+
return render_tag('label', attrs=attrs, content=content)
74+
75+
76+
def render_button(
77+
content, button_type=None, icon=None, button_class='', size='',
78+
href=''):
79+
"""
80+
Render a button with content
81+
"""
82+
attrs = {}
83+
classes = add_css_class('btn', button_class)
84+
size = text_value(size).lower().strip()
85+
if size == 'xs':
86+
classes = add_css_class(classes, 'btn-xs')
87+
elif size == 'sm' or size == 'small':
88+
classes = add_css_class(classes, 'btn-sm')
89+
elif size == 'lg' or size == 'large':
90+
classes = add_css_class(classes, 'btn-lg')
91+
elif size == 'md' or size == 'medium':
92+
pass
93+
elif size:
94+
raise BootstrapError(
95+
'Parameter "size" should be "xs", "sm", "lg" or ' +
96+
'empty ("{}" given).'.format(size))
97+
if button_type:
98+
if button_type == 'submit':
99+
classes = add_css_class(classes, 'btn-primary')
100+
elif button_type not in ('reset', 'button', 'link'):
101+
raise BootstrapError(
102+
'Parameter "button_type" should be "submit", "reset", ' +
103+
'"button", "link" or empty ("{}" given).'.format(button_type))
104+
attrs['type'] = button_type
105+
attrs['class'] = classes
106+
icon_content = render_icon(icon) if icon else ''
107+
if href:
108+
attrs['href'] = href
109+
tag = 'a'
110+
else:
111+
tag = 'button'
112+
return render_tag(
113+
tag, attrs=attrs, content=text_concat(
114+
icon_content, content, separator=' '))
115+
116+
117+
def render_field_and_label(
118+
field, label, field_class='', label_for=None, label_class='',
119+
layout='', **kwargs):
120+
"""
121+
Render a field with its label
122+
"""
123+
if layout == 'horizontal':
124+
if not label_class:
125+
label_class = get_bootstrap_setting('horizontal_label_class')
126+
if not field_class:
127+
field_class = get_bootstrap_setting('horizontal_field_class')
128+
if not label:
129+
label = '&#160;'
130+
label_class = add_css_class(label_class, 'control-label')
131+
html = field
132+
if field_class:
133+
html = '<div class="{klass}">{html}</div>'.format(
134+
klass=field_class, html=html)
135+
if label:
136+
html = render_label(
137+
label, label_for=label_for, label_class=label_class) + html
138+
return html
139+
140+
141+
def render_form_group(content, css_class=FORM_GROUP_CLASS):
142+
"""
143+
Render a Bootstrap form group
144+
"""
145+
return '<div class="{klass}">{content}</div>'.format(
146+
klass=css_class,
147+
content=content,
148+
)
149+
150+
151+
def is_widget_required_attribute(widget):
152+
"""
153+
Is this widget required?
154+
"""
155+
if not get_bootstrap_setting('set_required'):
156+
return False
157+
if not widget.is_required:
158+
return False
159+
if isinstance(
160+
widget, (
161+
AdminFileWidget, HiddenInput, FileInput,
162+
CheckboxSelectMultiple)):
163+
return False
164+
return True
165+
166+
167+
def is_widget_with_placeholder(widget):
168+
"""
169+
Is this a widget that should have a placeholder?
170+
Only text, search, url, tel, e-mail, password, number have placeholders
171+
These are all derived form TextInput, except for Textarea
172+
"""
173+
return isinstance(widget, (TextInput, Textarea))

bootstrap3/models.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# -*- coding: utf-8 -*-
2+
3+
# Empty models.py, required file for Django tests

0 commit comments

Comments
 (0)