Commit fba873d5 authored by Sergey Markelov's avatar Sergey Markelov

Merge remote-tracking branch 'upstream/master'

parents d3dddbf6 19d6dbd7
......@@ -5,6 +5,7 @@ from wtforms import form, fields, validators
from flask.ext import admin, login
from flask.ext.admin.contrib import sqla
from flask.ext.admin import helpers, expose
from werkzeug.security import generate_password_hash, check_password_hash
# Create Flask application
......@@ -59,7 +60,10 @@ class LoginForm(form.Form):
if user is None:
raise validators.ValidationError('Invalid user')
if user.password != self.password.data:
# we're comparing the plaintext pw with the the hash from the db
if not check_password_hash(user.password, self.password.data):
# to compare plain text passwords use
# if user.password != self.password.data:
raise validators.ValidationError('Invalid password')
def get_user(self):
......@@ -125,6 +129,9 @@ class MyAdminIndexView(admin.AdminIndexView):
user = User()
form.populate_obj(user)
# we hash the users password to avoid saving it as plaintext in the db,
# remove to use plain text:
user.password = generate_password_hash(form.password.data)
db.session.add(user)
db.session.commit()
......@@ -188,7 +195,9 @@ def build_sample_db():
user.last_name = last_names[i]
user.login = user.first_name.lower()
user.email = user.login + "@example.com"
user.password = ''.join(random.choice(string.ascii_lowercase + string.digits) for i in range(10))
user.password = generate_password_hash(''.join(random.choice(string.ascii_lowercase + string.digits) for i in range(10)))
# passwords are hashed, to use plaintext passwords use:
# user.password = ''.join(random.choice(string.ascii_lowercase + string.digits) for i in range(10))
db.session.add(user)
db.session.commit()
......
......@@ -10,7 +10,7 @@
Authentication
</p>
<p>
This example shows how you can use Flask-Login for authentication. It is only intended as a basic demonstration, so please don't freak out when you see passwords being stored as plain text.
This example shows how you can use Flask-Login for authentication. It is only intended as a basic demonstration.
</p>
{% else %}
<form method="POST" action="">
......
__version__ = '1.0.8'
__version__ = '1.0.9.dev0'
__author__ = 'Serge S. Koval'
__email__ = 'serge.koval+github@gmail.com'
......
......@@ -148,7 +148,8 @@ class BaseView(with_metaclass(AdminViewMeta, BaseViewClass)):
return args
def __init__(self, name=None, category=None, endpoint=None, url=None,
static_folder=None, static_url_path=None):
static_folder=None, static_url_path=None,
menu_class_name=None, menu_icon_type=None, menu_icon_value=None):
"""
Constructor.
......@@ -168,9 +169,16 @@ class BaseView(with_metaclass(AdminViewMeta, BaseViewClass)):
and '/admin/' prefix won't be applied.
:param static_url_path:
Static URL Path. If provided, this specifies the path to the static url directory.
:param debug:
Optional debug flag. If set to `True`, will rethrow exceptions in some cases, so Werkzeug
debugger can catch them.
:param menu_class_name:
Optional class name for the menu item.
:param menu_icon_type:
Optional icon. Possible icon types:
- `flask.ext.admin.consts.ICON_TYPE_GLYPH` - Bootstrap glyph icon
- `flask.ext.admin.consts.ICON_TYPE_IMAGE` - Image relative to Flask static directory
- `flask.ext.admin.consts.ICON_TYPE_IMAGE_URL` - Image with full URL
:param menu_icon_value:
Icon glyph name or URL, depending on `menu_icon_type` setting
"""
self.name = name
self.category = category
......@@ -178,6 +186,11 @@ class BaseView(with_metaclass(AdminViewMeta, BaseViewClass)):
self.url = url
self.static_folder = static_folder
self.static_url_path = static_url_path
self.menu = None
self.menu_class_name = menu_class_name
self.menu_icon_type = menu_icon_type
self.menu_icon_value = menu_icon_value
# Initialized from create_blueprint
self.admin = None
......@@ -483,6 +496,9 @@ class Admin(object):
def _add_view_to_menu(self, view):
self._add_menu_item(MenuView(view.name, view), view.category)
def get_category_menu_item(self, name):
return self._menu_categories.get(name)
def init_app(self, app):
"""
Register all views with the Flask application.
......
# bootstrap glyph icon
ICON_TYPE_GLYPH = 'glyph'
# image relative to Flask static folder
ICON_TYPE_IMAGE = 'image'
# external image
ICON_TYPE_IMAGE_URL = 'image-url'
......@@ -199,7 +199,8 @@ class ModelView(BaseModelView):
"""
def __init__(self, model, name=None,
category=None, endpoint=None, url=None):
category=None, endpoint=None, url=None,
menu_class_name=None, menu_icon_type=None, menu_icon_value=None):
"""
Constructor
......@@ -213,10 +214,24 @@ class ModelView(BaseModelView):
Endpoint
:param url:
Custom URL
:param menu_class_name:
Optional class name for the menu item.
:param menu_icon_type:
Optional icon. Possible icon types:
- `flask.ext.admin.consts.ICON_TYPE_GLYPH` - Bootstrap glyph icon
- `flask.ext.admin.consts.ICON_TYPE_IMAGE` - Image relative to Flask static directory
- `flask.ext.admin.consts.ICON_TYPE_IMAGE_URL` - Image with full URL
:param menu_icon_value:
Icon glyph name or URL, depending on `menu_icon_type` setting
"""
self._search_fields = []
super(ModelView, self).__init__(model, name, category, endpoint, url)
super(ModelView, self).__init__(model, name, category, endpoint, url,
menu_class_name=menu_class_name,
menu_icon_type=menu_icon_type,
menu_icon_value=menu_icon_value)
self._primary_key = self.scaffold_pk()
......@@ -493,12 +508,11 @@ class ModelView(BaseModelView):
model.save()
except Exception as ex:
if not self.handle_view_exception(ex):
raise
flash(gettext('Failed to create model. %(error)s',
error=format_error(ex)),
'error')
log.exception('Failed to create model')
flash(gettext('Failed to create model. %(error)s',
error=format_error(ex)),
'error')
log.exception('Failed to create model')
return False
else:
self.after_model_change(form, model, True)
......@@ -520,12 +534,11 @@ class ModelView(BaseModelView):
model.save()
except Exception as ex:
if not self.handle_view_exception(ex):
raise
flash(gettext('Failed to update model. %(error)s',
error=format_error(ex)),
'error')
log.exception('Failed to update model')
flash(gettext('Failed to update model. %(error)s',
error=format_error(ex)),
'error')
log.exception('Failed to update model')
return False
else:
self.after_model_change(form, model, False)
......@@ -545,12 +558,11 @@ class ModelView(BaseModelView):
return True
except Exception as ex:
if not self.handle_view_exception(ex):
raise
flash(gettext('Failed to delete model. %(error)s',
error=format_error(ex)),
'error')
log.exception('Failed to delete model')
flash(gettext('Failed to delete model. %(error)s',
error=format_error(ex)),
'error')
log.exception('Failed to delete model')
return False
# FileField access API
......@@ -600,7 +612,5 @@ class ModelView(BaseModelView):
count=count))
except Exception as ex:
if not self.handle_view_exception(ex):
raise
flash(gettext('Failed to delete models. %(error)s', error=str(ex)),
'error')
flash(gettext('Failed to delete models. %(error)s', error=str(ex)),
'error')
......@@ -132,10 +132,14 @@ class ModelView(BaseModelView):
"""
def __init__(self, model, name=None,
category=None, endpoint=None, url=None):
category=None, endpoint=None, url=None,
menu_class_name=None, menu_icon_type=None, menu_icon_value=None):
self._search_fields = []
super(ModelView, self).__init__(model, name, category, endpoint, url)
super(ModelView, self).__init__(model, name, category, endpoint, url,
menu_class_name=menu_class_name,
menu_icon_type=menu_icon_type,
menu_icon_value=menu_icon_value)
self._primary_key = self.scaffold_pk()
......@@ -350,10 +354,9 @@ class ModelView(BaseModelView):
save_inline(form, model)
except Exception as ex:
if not self.handle_view_exception(ex):
raise
flash(gettext('Failed to create model. %(error)s', error=str(ex)), 'error')
log.exception('Failed to create model')
flash(gettext('Failed to create model. %(error)s', error=str(ex)), 'error')
log.exception('Failed to create model')
return False
else:
self.after_model_change(form, model, True)
......@@ -370,10 +373,9 @@ class ModelView(BaseModelView):
save_inline(form, model)
except Exception as ex:
if not self.handle_view_exception(ex):
raise
flash(gettext('Failed to update model. %(error)s', error=str(ex)), 'error')
log.exception('Failed to update model')
flash(gettext('Failed to update model. %(error)s', error=str(ex)), 'error')
log.exception('Failed to update model')
return False
else:
self.after_model_change(form, model, False)
......@@ -387,10 +389,9 @@ class ModelView(BaseModelView):
return True
except Exception as ex:
if not self.handle_view_exception(ex):
raise
flash(gettext('Failed to delete model. %(error)s', error=str(ex)), 'error')
log.exception('Failed to delete model')
flash(gettext('Failed to delete model. %(error)s', error=str(ex)), 'error')
log.exception('Failed to delete model')
return False
# Default model actions
......@@ -425,6 +426,4 @@ class ModelView(BaseModelView):
count=count))
except Exception as ex:
if not self.handle_view_exception(ex):
raise
flash(gettext('Failed to delete models. %(error)s', error=str(ex)), 'error')
flash(gettext('Failed to delete models. %(error)s', error=str(ex)), 'error')
......@@ -39,7 +39,8 @@ class ModelView(BaseModelView):
"""
def __init__(self, coll,
name=None, category=None, endpoint=None, url=None):
name=None, category=None, endpoint=None, url=None,
menu_class_name=None, menu_icon_type=None, menu_icon_value=None):
"""
Constructor
......@@ -53,6 +54,16 @@ class ModelView(BaseModelView):
Endpoint
:param url:
Custom URL
:param menu_class_name:
Optional class name for the menu item.
:param menu_icon_type:
Optional icon. Possible icon types:
- `flask.ext.admin.consts.ICON_TYPE_GLYPH` - Bootstrap glyph icon
- `flask.ext.admin.consts.ICON_TYPE_IMAGE` - Image relative to Flask static directory
- `flask.ext.admin.consts.ICON_TYPE_IMAGE_URL` - Image with full URL
:param menu_icon_value:
Icon glyph name or URL, depending on `menu_icon_type` setting
"""
self._search_fields = []
......@@ -62,7 +73,10 @@ class ModelView(BaseModelView):
if endpoint is None:
endpoint = ('%sview' % coll.name).lower()
super(ModelView, self).__init__(None, name, category, endpoint, url)
super(ModelView, self).__init__(None, name, category, endpoint, url,
menu_class_name=menu_class_name,
menu_icon_type=menu_icon_type,
menu_icon_value=menu_icon_value)
self.coll = coll
......
......@@ -242,7 +242,8 @@ class ModelView(BaseModelView):
"""
def __init__(self, model, session,
name=None, category=None, endpoint=None, url=None):
name=None, category=None, endpoint=None, url=None,
menu_class_name=None, menu_icon_type=None, menu_icon_value=None):
"""
Constructor.
......@@ -258,6 +259,16 @@ class ModelView(BaseModelView):
Endpoint name. If not set, defaults to the model name
:param url:
Base URL. If not set, defaults to '/admin/' + endpoint
:param menu_class_name:
Optional class name for the menu item.
:param menu_icon_type:
Optional icon. Possible icon types:
- `flask.ext.admin.consts.ICON_TYPE_GLYPH` - Bootstrap glyph icon
- `flask.ext.admin.consts.ICON_TYPE_IMAGE` - Image relative to Flask static directory
- `flask.ext.admin.consts.ICON_TYPE_IMAGE_URL` - Image with full URL
:param menu_icon_value:
Icon glyph name or URL, depending on `menu_icon_type` setting
"""
self.session = session
......@@ -271,7 +282,10 @@ class ModelView(BaseModelView):
if self.form_choices is None:
self.form_choices = {}
super(ModelView, self).__init__(model, name, category, endpoint, url)
super(ModelView, self).__init__(model, name, category, endpoint, url,
menu_class_name=menu_class_name,
menu_icon_type=menu_icon_type,
menu_icon_value=menu_icon_value)
# Primary key
self._primary_key = self.scaffold_pk()
......@@ -831,11 +845,11 @@ class ModelView(BaseModelView):
self.session.commit()
except Exception as ex:
if not self.handle_view_exception(ex):
raise
flash(gettext('Failed to create model. %(error)s', error=str(ex)), 'error')
log.exception('Failed to create model')
flash(gettext('Failed to create model. %(error)s', error=str(ex)), 'error')
log.exception('Failed to create model')
self.session.rollback()
return False
else:
self.after_model_change(form, model, True)
......@@ -857,10 +871,9 @@ class ModelView(BaseModelView):
self.session.commit()
except Exception as ex:
if not self.handle_view_exception(ex):
raise
flash(gettext('Failed to update model. %(error)s', error=str(ex)), 'error')
log.exception('Failed to update model')
flash(gettext('Failed to update model. %(error)s', error=str(ex)), 'error')
log.exception('Failed to update model')
self.session.rollback()
return False
......@@ -884,11 +897,11 @@ class ModelView(BaseModelView):
return True
except Exception as ex:
if not self.handle_view_exception(ex):
raise
flash(gettext('Failed to delete model. %(error)s', error=str(ex)), 'error')
log.exception('Failed to delete model')
flash(gettext('Failed to delete model. %(error)s', error=str(ex)), 'error')
log.exception('Failed to delete model')
self.session.rollback()
return False
# Default model actions
......
......@@ -5,6 +5,12 @@ from flask.ext.admin import helpers as h
__all__ = ['Select2Widget', 'DatePickerWidget', 'DateTimePickerWidget', 'RenderTemplateWidget', 'Select2TagsWidget', ]
def _is_bootstrap3():
view = h.get_current_view()
return view and view.admin.template_mode == 'bootstrap3'
class Select2Widget(widgets.Select):
"""
`Select2 <https://github.com/ivaynberg/select2>`_ styled select widget.
......@@ -14,7 +20,7 @@ class Select2Widget(widgets.Select):
"""
def __call__(self, field, **kwargs):
kwargs.setdefault('data-role', u'select2')
allow_blank = getattr(field, 'allow_blank', False)
if allow_blank and not self.multiple:
kwargs['data-allow-blank'] = u'1'
......@@ -41,7 +47,12 @@ class DatePickerWidget(widgets.TextInput):
"""
def __call__(self, field, **kwargs):
kwargs.setdefault('data-role', u'datepicker')
kwargs.setdefault('data-date-format', u'yyyy-mm-dd')
if _is_bootstrap3():
kwargs.setdefault('data-date-format', u'YYYY-MM-DD')
else:
kwargs.setdefault('data-date-format', u'yyyy-mm-dd')
kwargs.setdefault('data-date-autoclose', u'true')
self.date_format = kwargs['data-date-format']
return super(DatePickerWidget, self).__call__(field, **kwargs)
......@@ -55,10 +66,15 @@ class DateTimePickerWidget(widgets.TextInput):
"""
def __call__(self, field, **kwargs):
kwargs.setdefault('data-role', u'datetimepicker')
kwargs.setdefault('data-date-format', u'yyyy-mm-dd hh:ii:ss')
if _is_bootstrap3():
kwargs.setdefault('data-date-format', u'YYYY-MM-DD hh:mm:ss')
else:
kwargs.setdefault('data-date-format', u'yyyy-mm-dd hh:ii:ss')
kwargs.setdefault('data-date-today-btn', u'linked')
kwargs.setdefault('data-date-today-highlight', u'true')
kwargs.setdefault('data-date-autoclose', u'true')
kwargs.setdefault('data-date-today-btn', u'linked')
kwargs.setdefault('data-date-today-highlight', u'true')
return super(DateTimePickerWidget, self).__call__(field, **kwargs)
......@@ -70,7 +86,12 @@ class TimePickerWidget(widgets.TextInput):
"""
def __call__(self, field, **kwargs):
kwargs.setdefault('data-role', u'timepicker')
kwargs.setdefault('data-date-format', u'hh:ii:ss')
if _is_bootstrap3():
kwargs.setdefault('data-date-format', u'hh:mm:ss')
else:
kwargs.setdefault('data-date-format', u'hh:ii:ss')
kwargs.setdefault('data-date-autoclose', u'true')
return super(TimePickerWidget, self).__call__(field, **kwargs)
......
......@@ -5,11 +5,18 @@ class BaseMenu(object):
"""
Base menu item
"""
def __init__(self, name):
def __init__(self, name, class_name=None, icon_type=None, icon_value=None):
self.name = name
self.class_name = class_name
self.icon_type = icon_type
self.icon_value = icon_value
self.parent = None
self._children = []
def add_child(self, menu):
# TODO: Check if menu item is already assigned to some parent
menu.parent = self
self._children.append(menu)
def get_url(self):
......@@ -25,6 +32,15 @@ class BaseMenu(object):
return False
def get_class_name(self):
return self.class_name
def get_icon_type(self):
return self.icon_type
def get_icon_value(self):
return self.icon_value
def is_visible(self):
return True
......@@ -65,11 +81,16 @@ class MenuView(BaseMenu):
Admin view menu item
"""
def __init__(self, name, view=None):
super(MenuView, self).__init__(name)
super(MenuView, self).__init__(name,
class_name=view.menu_class_name,
icon_type=view.menu_icon_type,
icon_value=view.menu_icon_value)
self._view = view
self._cached_url = None
view.menu = self
def get_url(self):
if self._view is None:
return None
......@@ -103,8 +124,8 @@ class MenuLink(BaseMenu):
"""
Link item
"""
def __init__(self, name, url=None, endpoint=None, category=None):
super(MenuLink, self).__init__(name)
def __init__(self, name, url=None, endpoint=None, category=None, class_name=None, icon_type=None, icon_value=None):
super(MenuLink, self).__init__(name, class_name, icon_type, icon_value)
self.category = category
......
......@@ -365,7 +365,7 @@ class BaseModelView(BaseView, ActionsMixin):
'style': 'color: black'
}
}
Note, changing the format of a DateTimeField will require changes to both form_widget_args and form_args::
form_args = dict(
......@@ -484,7 +484,8 @@ class BaseModelView(BaseView, ActionsMixin):
"""
def __init__(self, model,
name=None, category=None, endpoint=None, url=None):
name=None, category=None, endpoint=None, url=None,
menu_class_name=None, menu_icon_type=None, menu_icon_value=None):
"""
Constructor.
......@@ -500,9 +501,16 @@ class BaseModelView(BaseView, ActionsMixin):
'userview'
:param url:
Base URL. If not provided, will use endpoint as a URL.
:param debug:
Enable debugging mode. Won't catch exceptions on model
save failures.
:param menu_class_name:
Optional class name for the menu item.
:param menu_icon_type:
Optional icon. Possible icon types:
- `flask.ext.admin.consts.ICON_TYPE_GLYPH` - Bootstrap glyph icon
- `flask.ext.admin.consts.ICON_TYPE_IMAGE` - Image relative to Flask static directory
- `flask.ext.admin.consts.ICON_TYPE_IMAGE_URL` - Image with full URL
:param menu_icon_value:
Icon glyph name or URL, depending on `menu_icon_type` setting
"""
# If name not provided, it is model name
......@@ -513,7 +521,10 @@ class BaseModelView(BaseView, ActionsMixin):
if endpoint is None:
endpoint = model.__name__.lower()
super(BaseModelView, self).__init__(name, category, endpoint, url)
super(BaseModelView, self).__init__(name, category, endpoint, url,
menu_class_name=menu_class_name,
menu_icon_type=menu_icon_type,
menu_icon_value=menu_icon_value)
self.model = model
......@@ -890,9 +901,9 @@ class BaseModelView(BaseView, ActionsMixin):
# Exception handler
def handle_view_exception(self, exc):
if self._debug:
return False
raise
return True
return False
# Model event handlers
def on_model_change(self, form, model, is_created):
......@@ -954,6 +965,29 @@ class BaseModelView(BaseView, ActionsMixin):
By default do nothing.
"""
pass
def on_form_prefill (self, form, id):
"""
Perform additional actions to pre-fill the edit form.
Called from edit_view, if the current action is rendering
the form rather than receiving client side input, after
default pre-filling has been performed.
By default does nothing.
You only need to override this if you have added custom
fields that depend on the database contents in a way that
Flask-admin can't figure out by itself. Fields that were
added by name of a normal column or relationship should
work out of the box.
:param form:
Form instance
:param id:
id of the object that is going to be edited
"""
pass
def create_model(self, form):
"""
......@@ -1296,6 +1330,9 @@ class BaseModelView(BaseView, ActionsMixin):
else:
return redirect(return_url)
if request.method == 'GET':
self.on_form_prefill(form, id)
form_opts = FormOpts(widget_args=self.form_widget_args,
form_rules=self._form_edit_rules)
......
......@@ -20,6 +20,7 @@ form.icon button {
a.icon, button span.glyphicon {
text-decoration: none;
margin-left: 10px;
color: #333;
}
/* Model search form */
......
......@@ -32,6 +32,7 @@ var AdminFilters = function(element, filtersElement, filterGroups) {
if($('.filters tr').length == 0) {
$('button', $root).hide();
$('a[class=btn]', $root).hide();
$('.filters tbody').remove();
} else {
$('button', $root).show();
}
......@@ -92,6 +93,9 @@ var AdminFilters = function(element, filtersElement, filterGroups) {
if (filter.type) {
$field.attr('data-role', filter.type);
if (filter.type == "datepicker") {
$field.attr('data-date-format', "YYYY-MM-DD");
}
faForm.applyStyle($field, filter.type);
}
}
......
......@@ -109,17 +109,25 @@
return true;
case 'datepicker':
$el.datetimepicker({
minView: 'month'
// TODO: Have separate converters for bs2 and bs3
// Bootstrap 2 option
minView: 'month',
// Bootstrap 3 option
pickTime: false
});
return true;
case 'datetimepicker':
$el.datetimepicker();
$el.datetimepicker({
});
return true;
case 'timepicker':
$el.datetimepicker({
// Bootstrap 2 option
startView: 'day',
maxView: 'day',
formatViewType: 'time'
formatViewType: 'time',
// Bootstrap 3 option
pickDate: false
});
return true;
}
......
/*!
* Datetimepicker for Bootstrap v3
* https://github.com/Eonasdan/bootstrap-datetimepicker/
*/
.bootstrap-datetimepicker-widget{top:0;left:0;width:250px;padding:4px;margin-top:1px;z-index:99999!important;border-radius:4px}.bootstrap-datetimepicker-widget.timepicker-sbs{width:600px}.bootstrap-datetimepicker-widget.bottom:before{content:'';display:inline-block;border-left:7px solid transparent;border-right:7px solid transparent;border-bottom:7px solid #ccc;border-bottom-color:rgba(0,0,0,.2);position:absolute;top:-7px;left:7px}.bootstrap-datetimepicker-widget.bottom:after{content:'';display:inline-block;border-left:6px solid transparent;border-right:6px solid transparent;border-bottom:6px solid #fff;position:absolute;top:-6px;left:8px}.bootstrap-datetimepicker-widget.top:before{content:'';display:inline-block;border-left:7px solid transparent;border-right:7px solid transparent;border-top:7px solid #ccc;border-top-color:rgba(0,0,0,.2);position:absolute;bottom:-7px;left:6px}.bootstrap-datetimepicker-widget.top:after{content:'';display:inline-block;border-left:6px solid transparent;border-right:6px solid transparent;border-top:6px solid #fff;position:absolute;bottom:-6px;left:7px}.bootstrap-datetimepicker-widget .dow{width:14.2857%}.bootstrap-datetimepicker-widget.pull-right:before{left:auto;right:6px}.bootstrap-datetimepicker-widget.pull-right:after{left:auto;right:7px}.bootstrap-datetimepicker-widget>ul{list-style-type:none;margin:0}.bootstrap-datetimepicker-widget .timepicker-hour,.bootstrap-datetimepicker-widget .timepicker-minute,.bootstrap-datetimepicker-widget .timepicker-second{width:100%;font-weight:bold;font-size:1.2em}.bootstrap-datetimepicker-widget table[data-hour-format="12"] .separator{width:4px;padding:0;margin:0}.bootstrap-datetimepicker-widget .datepicker>div{display:none}.bootstrap-datetimepicker-widget .picker-switch{text-align:center}.bootstrap-datetimepicker-widget table{width:100%;margin:0}.bootstrap-datetimepicker-widget td,.bootstrap-datetimepicker-widget th{text-align:center;width:20px;height:20px;border-radius:4px}.bootstrap-datetimepicker-widget td.day:hover,.bootstrap-datetimepicker-widget td.hour:hover,.bootstrap-datetimepicker-widget td.minute:hover,.bootstrap-datetimepicker-widget td.second:hover{background:#eee;cursor:pointer}.bootstrap-datetimepicker-widget td.old,.bootstrap-datetimepicker-widget td.new{color:#999}.bootstrap-datetimepicker-widget td.today{position:relative}.bootstrap-datetimepicker-widget td.today:before{content:'';display:inline-block;border-left:7px solid transparent;border-bottom:7px solid #428bca;border-top-color:rgba(0,0,0,.2);position:absolute;bottom:4px;right:4px}.bootstrap-datetimepicker-widget td.active,.bootstrap-datetimepicker-widget td.active:hover{background-color:#428bca;color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,.25)}.bootstrap-datetimepicker-widget td.active.today:before{border-bottom-color:#fff}.bootstrap-datetimepicker-widget td.disabled,.bootstrap-datetimepicker-widget td.disabled:hover{background:none;color:#999;cursor:not-allowed}.bootstrap-datetimepicker-widget td span{display:block;width:47px;height:54px;line-height:54px;float:left;margin:2px;cursor:pointer;border-radius:4px}.bootstrap-datetimepicker-widget td span:hover{background:#eee}.bootstrap-datetimepicker-widget td span.active{background-color:#428bca;color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,.25)}.bootstrap-datetimepicker-widget td span.old{color:#999}.bootstrap-datetimepicker-widget td span.disabled,.bootstrap-datetimepicker-widget td span.disabled:hover{background:none;color:#999;cursor:not-allowed}.bootstrap-datetimepicker-widget th.switch{width:145px}.bootstrap-datetimepicker-widget th.next,.bootstrap-datetimepicker-widget th.prev{font-size:21px}.bootstrap-datetimepicker-widget th.disabled,.bootstrap-datetimepicker-widget th.disabled:hover{background:none;color:#999;cursor:not-allowed}.bootstrap-datetimepicker-widget thead tr:first-child th{cursor:pointer}.bootstrap-datetimepicker-widget thead tr:first-child th:hover{background:#eee}.input-group.date .input-group-addon span{display:block;cursor:pointer;width:16px;height:16px}.bootstrap-datetimepicker-widget.left-oriented:before{left:auto;right:6px}.bootstrap-datetimepicker-widget.left-oriented:after{left:auto;right:7px}.bootstrap-datetimepicker-widget ul.list-unstyled li div.timepicker div.timepicker-picker table.table-condensed tbody>tr>td{padding:0!important}
\ No newline at end of file
This diff is collapsed.
......@@ -12,7 +12,6 @@
{% block head_css %}
<link href="{{ admin_static.url(filename='bootstrap/bootstrap2/css/bootstrap.css') }}" rel="stylesheet">
<link href="{{ admin_static.url(filename='bootstrap/bootstrap2/css/bootstrap-responsive.css') }}" rel="stylesheet">
<link href="{{ admin_static.url(filename='datetimepicker/bootstrap-datetimepicker.css') }}" rel="stylesheet">
<link href="{{ admin_static.url(filename='admin/css/bootstrap2/admin.css') }}" rel="stylesheet">
{% endblock %}
{% block head %}
......@@ -56,7 +55,8 @@
{% block tail_js %}
<script src="{{ admin_static.url(filename='vendor/jquery-2.0.3.min.js') }}" type="text/javascript"></script>
<script src="{{ admin_static.url(filename='bootstrap/bootstrap2/js/bootstrap.min.js') }}" type="text/javascript"></script>
<script src="{{ admin_static.url(filename='select2/select2.min.js') }}" type="text/javascript"></script>
<script src="{{ admin_static.url(filename='vendor/moment-2.7.0.min.js') }}" type="text/javascript"></script>
<script src="{{ admin_static.url(filename='vendor/select2/select2.min.js') }}" type="text/javascript"></script>
{% endblock %}
{% block tail %}
......
{% macro menu_icon(item) -%}
{% set icon_type = item.get_icon_type() %}
{% if icon_type %}
{% set icon_value = item.get_icon_value() %}
{% if icon_type == 'glyph' %}
<i class="{{ icon_value }}"></i>
{% elif icon_type == 'image' %}
<img src="{{ url_for('static', filename=icon_value) }}" alt="menu image"></img>
{% elif icon_type == 'image-url' %}
<img src="item.icon_value" alt="menu image"></img>
{% endif %}
{% endif %}
{%- endmacro %}
{% macro menu() %}
{% for item in admin_view.admin.menu() %}
{% if item.is_category() %}
{% set children = item.get_children() %}
{% if children %}
{% if item.is_active(admin_view) %}<li class="active dropdown">{% else %}<li class="dropdown">{% endif %}
<a class="dropdown-toggle" data-toggle="dropdown" href="javascript:void(0)">{{ item.name }}<b class="caret"></b></a>
{% set class_name = item.get_class_name() %}
{% if item.is_active(admin_view) %}
<li class="active dropdown{% if class_name %} {{class_name}}{% endif %}">
{% else %}
<li class="dropdown{% if class_name %} {{class_name}}{% endif %}">
{% endif %}
<a class="dropdown-toggle" data-toggle="dropdown" href="javascript:void(0)">
{{ menu_icon(item) }}{{ item.name }}<b class="caret"></b>
</a>
<ul class="dropdown-menu">
{% for child in children %}
{% if child.is_active(admin_view) %}<li class="active">{% else %}<li>{% endif %}
<a href="{{ child.get_url() }}">{{ child.name }}</a>
{% set class_name = child.get_class_name() %}
{% if child.is_active(admin_view) %}
<li class="active{% if class_name %} {{class_name}}{% endif %}">
{% else %}
<li{% if class_name %} class="{{class_name}}"{% endif %}>
{% endif %}
<a href="{{ child.get_url() }}">{{ menu_icon(item) }}{{ child.name }}</a>
</li>
{% endfor %}
</ul>
......@@ -16,8 +42,13 @@
{% endif %}
{% else %}
{% if item.is_accessible() and item.is_visible() %}
{% if item.is_active(admin_view) %}<li class="active">{% else %}<li>{% endif %}
<a href="{{ item.get_url() }}">{{ item.name }}</a>
{% set class_name = item.get_class_name() %}
{% if item.is_active(admin_view) %}
<li class="active{% if class_name %} {{class_name}}{% endif %}">
{% else %}
<li{% if class_name %} class="{{class_name}}"{% endif %}>
{% endif %}
<a href="{{ item.get_url() }}">{{ menu_icon(item) }}{{ item.name }}</a>
</li>
{% endif %}
{% endif %}
......@@ -28,7 +59,7 @@
{% for item in admin_view.admin.menu_links() %}
{% if item.is_accessible() and item.is_visible() %}
<li>
<a href="{{ item.get_url() }}">{{ item.name }}</a>
<a href="{{ item.get_url() }}">{{ menu_icon(item) }}{{ item.name }}</a>
</li>
{% endif %}
{% endfor %}
......
{% import 'admin/static.html' as admin_static with context %}
{# ---------------------- Pager -------------------------- #}
{% macro pager(page, pages, generator) -%}
{% if pages > 1 %}
......@@ -75,7 +77,7 @@
{%- endmacro %}
{# ---------------------- Forms -------------------------- #}
{% macro render_field(form, field, kwargs={}) %}
{% macro render_field(form, field, kwargs={}, caller=None) %}
{% set direct_error = h.is_field_error(field.errors) %}
<div class="control-group{{ ' error' if direct_error else '' }}">
<div class="control-label">
......@@ -102,6 +104,9 @@
</ul>
{% endif %}
</div>
{% if caller %}
{{ caller(form, field, direct_error, kwargs) }}
{% endif %}
</div>
{% endmacro %}
......@@ -165,3 +170,13 @@
{{ render_form_buttons(cancel_url, extra) }}
{% endcall %}
{% endmacro %}
{% macro form_css() %}
<link href="{{ admin_static.url(filename='vendor/select2/select2.css') }}" rel="stylesheet">
<link href="{{ admin_static.url(filename='vendor/datetimepicker-bs2/bootstrap-datetimepicker.css') }}" rel="stylesheet">
{% endmacro %}
{% macro form_js() %}
<script src="{{ admin_static.url(filename='vendor/datetimepicker-bs2/bootstrap-datetimepicker.js') }}"></script>
<script src="{{ admin_static.url(filename='admin/js/form.js') }}"></script>
{% endmacro %}
{% extends 'admin/master.html' %}
{% import 'admin/lib.html' as lib with context %}
{% import 'admin/static.html' as admin_static with context %}
{% macro extra() %}
<input name="_add_another" type="submit" class="btn btn-large" value="{{ _gettext('Save and Add') }}" />
......@@ -8,8 +7,7 @@
{% block head %}
{{ super() }}
<link href="{{ admin_static.url(filename='select2/select2.css') }}" rel="stylesheet">
<link href="{{ admin_static.url(filename='datetimepicker/bootstrap-datetimepicker.css') }}" rel="stylesheet">
{{ lib.form_css() }}
{% endblock %}
{% block body %}
......@@ -30,6 +28,5 @@
{% block tail %}
{{ super() }}
<script src="{{ admin_static.url(filename='datetimepicker/bootstrap-datetimepicker.js') }}"></script>
<script src="{{ admin_static.url(filename='admin/js/form.js') }}"></script>
{{ lib.form_js() }}
{% endblock %}
{% extends 'admin/master.html' %}
{% import 'admin/lib.html' as lib with context %}
{% import 'admin/static.html' as admin_static with context %}
{% macro extra() %}
<input name="_continue_editing" type="submit" class="btn btn-large" value="{{ _gettext('Save and Continue') }}" />
......@@ -8,8 +7,7 @@
{% block head %}
{{ super() }}
<link href="{{ admin_static.url(filename='select2/select2.css') }}" rel="stylesheet">
<link href="{{ admin_static.url(filename='datetimepicker/bootstrap-datetimepicker.css') }}" rel="stylesheet">
{{ lib.form_css() }}
{% endblock %}
{% block body %}
......@@ -21,6 +19,5 @@
{% block tail %}
{{ super() }}
<script src="{{ admin_static.url(filename='datetimepicker/bootstrap-datetimepicker.js') }}"></script>
<script src="{{ admin_static.url(filename='admin/js/form.js') }}"></script>
{{ lib.form_js() }}
{% endblock %}
......@@ -6,8 +6,7 @@
{% block head %}
{{ super() }}
<link href="{{ admin_static.url(filename='select2/select2.css') }}" rel="stylesheet">
<link href="{{ admin_static.url(filename='datetimepicker/bootstrap-datetimepicker.css') }}" rel="stylesheet">
{{ lib.form_css() }}
{% endblock %}
{% block body %}
......@@ -103,7 +102,7 @@
<td>
{% block list_row_actions scoped %}
{%- if admin_view.can_edit -%}
<a class="icon" href="{{ url_for('.edit_view', id=get_pk_value(row), url=return_url) }}" title="Edit record">
<a class="icon" href="{{ url_for('.edit_view', id=get_pk_value(row), url=return_url) }}" title="{{ _gettext('Edit record') }}">
<i class="icon-pencil"></i>
</a>
{%- endif -%}
......@@ -112,7 +111,7 @@
{% if csrf_token %}
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}"/>
{% endif %}
<button onclick="return confirm('{{ _gettext('You sure you want to delete this item?') }}');" title="Delete record">
<button onclick="return confirm('{{ _gettext('You sure you want to delete this item?') }}');" title="{{ _gettext('Delete record') }}">
<i class="icon-trash"></i>
</button>
</form>
......@@ -145,8 +144,7 @@
{% block tail %}
{{ super() }}
<script src="{{ admin_static.url(filename='datetimepicker/bootstrap-datetimepicker.js') }}"></script>
<script src="{{ admin_static.url(filename='admin/js/form.js') }}"></script>
{{ lib.form_js() }}
<script src="{{ admin_static.url(filename='admin/js/filters.js') }}"></script>
{{ actionlib.script(_gettext('Please select at least one model.'),
......
......@@ -66,7 +66,8 @@
{% block tail_js %}
<script src="{{ admin_static.url(filename='vendor/jquery-2.0.3.min.js') }}" type="text/javascript"></script>
<script src="{{ admin_static.url(filename='bootstrap/bootstrap3/js/bootstrap.min.js') }}" type="text/javascript"></script>
<script src="{{ admin_static.url(filename='select2/select2.min.js') }}" type="text/javascript"></script>
<script src="{{ admin_static.url(filename='vendor/moment-2.7.0.min.js') }}" type="text/javascript"></script>
<script src="{{ admin_static.url(filename='vendor/select2/select2.min.js') }}" type="text/javascript"></script>
{% endblock %}
{% block tail %}
......
{% macro menu_icon(item) -%}
{% set icon_type = item.get_icon_type() %}
{% if icon_type %}
{% set icon_value = item.get_icon_value() %}
{% if icon_type == 'glyph' %}
<i class="glyphicon {{ icon_value }}"></i>
{% elif icon_type == 'image' %}
<img src="{{ url_for('static', filename=icon_value) }}" alt="menu image"></img>
{% elif icon_type == 'image-url' %}
<img src="item.icon_value" alt="menu image"></img>
{% endif %}
{% endif %}
{%- endmacro %}
{% macro menu() %}
{% for item in admin_view.admin.menu() %}
{% if item.is_category() %}
{% set children = item.get_children() %}
{% if children %}
{% if item.is_active(admin_view) %}<li class="active dropdown">{% else %}<li class="dropdown">{% endif %}
<a class="dropdown-toggle" data-toggle="dropdown" href="javascript:void(0)">{{ item.name }}<b class="caret"></b></a>
{% if item.is_active(admin_view) %}
<li class="active dropdown{% if class_name %} {{class_name}}{% endif %}">
{% else %}
<li class="dropdown{% if class_name %} {{class_name}}{% endif %}">
{% endif %}
<a class="dropdown-toggle" data-toggle="dropdown" href="javascript:void(0)">{{ menu_icon(item) }}{{ item.name }}<b class="caret"></b></a>
<ul class="dropdown-menu">
{% for child in children %}
{% if child.is_active(admin_view) %}<li class="active">{% else %}<li>{% endif %}
<a href="{{ child.get_url() }}">{{ child.name }}</a>
{% set class_name = child.get_class_name() %}
{% if child.is_active(admin_view) %}
<li class="active"{% if class_name %} {{class_name}}{% endif %}>
{% else %}
<li{% if class_name %} class="{{class_name}}"{% endif %}>
{% endif %}
<a href="{{ child.get_url() }}">{{ menu_icon(item) }}{{ child.name }}</a>
</li>
{% endfor %}
</ul>
......@@ -16,8 +39,13 @@
{% endif %}
{% else %}
{% if item.is_accessible() and item.is_visible() %}
{% if item.is_active(admin_view) %}<li class="active">{% else %}<li>{% endif %}
<a href="{{ item.get_url() }}">{{ item.name }}</a>
{% set class_name = item.get_class_name() %}
{% if item.is_active(admin_view) %}
<li class="active"{% if class_name %} {{class_name}}{% endif %}>
{% else %}
<li{% if class_name %} class="{{class_name}}"{% endif %}>
{% endif %}
<a href="{{ item.get_url() }}">{{ menu_icon(item) }}{{ item.name }}</a>
</li>
{% endif %}
{% endif %}
......@@ -28,7 +56,7 @@
{% for item in admin_view.admin.menu_links() %}
{% if item.is_accessible() and item.is_visible() %}
<li>
<a href="{{ item.get_url() }}">{{ item.name }}</a>
<a href="{{ item.get_url() }}">{{ menu_icon(item) }}{{ item.name }}</a>
</li>
{% endif %}
{% endfor %}
......
{% import 'admin/static.html' as admin_static with context %}
{# ---------------------- Pager -------------------------- #}
{% macro pager(page, pages, generator) -%}
{% if pages > 1 %}
......@@ -73,7 +75,7 @@
{%- endmacro %}
{# ---------------------- Forms -------------------------- #}
{% macro render_field(form, field, kwargs={}) %}
{% macro render_field(form, field, kwargs={}, caller=None) %}
{% set direct_error = h.is_field_error(field.errors) %}
<div class="form-group{{ ' error' if direct_error else '' }}">
<label for="{{ field.id }}" class="col-md-2 control-label">{{ field.label.text }}
......@@ -83,7 +85,7 @@
&nbsp;
{%- endif %}
</label>
<div class="{{ kwargs.get('column_class', 'col-md-4') }}">
<div class="{{ kwargs.get('column_class', 'col-md-10') }}">
{% set _dummy = kwargs.setdefault('class', 'form-control') %}
{{ field(**kwargs)|safe }}
</div>
......@@ -97,6 +99,9 @@
{% endfor %}
</ul>
{% endif %}
{% if caller %}
{{ caller(form, field, direct_error, kwargs) }}
{% endif %}
</div>
{% endmacro %}
......@@ -158,3 +163,13 @@
{{ render_form_buttons(cancel_url, extra) }}
{% endcall %}
{% endmacro %}
{% macro form_css() %}
<link href="{{ admin_static.url(filename='vendor/select2/select2.css') }}" rel="stylesheet">
<link href="{{ admin_static.url(filename='vendor/datetimepicker-bs3/bootstrap-datetimepicker.min.css') }}" rel="stylesheet">
{% endmacro %}
{% macro form_js() %}
<script src="{{ admin_static.url(filename='vendor/datetimepicker-bs3/bootstrap-datetimepicker.min.js') }}"></script>
<script src="{{ admin_static.url(filename='admin/js/form.js') }}"></script>
{% endmacro %}
{% extends 'admin/master.html' %}
{% import 'admin/lib.html' as lib with context %}
{% import 'admin/static.html' as admin_static with context %}
{% macro extra() %}
<input name="_add_another" type="submit" class="btn btn-large" value="{{ _gettext('Save and Add') }}" />
......@@ -8,8 +7,7 @@
{% block head %}
{{ super() }}
<link href="{{ admin_static.url(filename='select2/select2.css') }}" rel="stylesheet">
<link href="{{ admin_static.url(filename='datetimepicker/bootstrap-datetimepicker.css') }}" rel="stylesheet">
{{ lib.form_css() }}
{% endblock %}
{% block body %}
......@@ -32,6 +30,5 @@
{% block tail %}
{{ super() }}
<script src="{{ admin_static.url(filename='datetimepicker/bootstrap-datetimepicker.js') }}"></script>
<script src="{{ admin_static.url(filename='admin/js/form.js') }}"></script>
{{ lib.form_js() }}
{% endblock %}
{% extends 'admin/master.html' %}
{% import 'admin/lib.html' as lib with context %}
{% import 'admin/static.html' as admin_static with context %}
{% macro extra() %}
<input name="_continue_editing" type="submit" class="btn" value="{{ _gettext('Save and Continue') }}" />
......@@ -8,8 +7,7 @@
{% block head %}
{{ super() }}
<link href="{{ admin_static.url(filename='select2/select2.css') }}" rel="stylesheet">
<link href="{{ admin_static.url(filename='datetimepicker/bootstrap-datetimepicker.css') }}" rel="stylesheet">
{{ lib.form_css() }}
{% endblock %}
{% block body %}
......@@ -21,6 +19,5 @@
{% block tail %}
{{ super() }}
<script src="{{ admin_static.url(filename='datetimepicker/bootstrap-datetimepicker.js') }}"></script>
<script src="{{ admin_static.url(filename='admin/js/form.js') }}"></script>
{{ lib.form_js() }}
{% endblock %}
......@@ -46,7 +46,7 @@
{%- endfor %}
</select>
{%- else -%}
<input name="flt{{n}}_{{ filter_arg }}" type="text" value="{{ value or '' }}" class="filter-val form-control"{% if filter.data_type %} data-role="{{ filter.data_type }}"{% endif %}></input>
<input name="flt{{n}}_{{ filter_arg }}" type="text" value="{{ value or '' }}" class="filter-val form-control"{% if filter.data_type %} data-role="{{ filter.data_type }}"{% endif %} {% if filter.data_type == "datepicker" %}data-date-format="YYYY-MM-DD"{% endif %}></input>
{%- endif -%}
</td>
</tr>
......
......@@ -6,8 +6,7 @@
{% block head %}
{{ super() }}
<link href="{{ admin_static.url(filename='select2/select2.css') }}" rel="stylesheet">
<link href="{{ admin_static.url(filename='datetimepicker/bootstrap-datetimepicker.css') }}" rel="stylesheet">
{{ lib.form_css() }}
{% endblock %}
{% block body %}
......@@ -103,7 +102,7 @@
<td>
{% block list_row_actions scoped %}
{%- if admin_view.can_edit -%}
<a class="icon" href="{{ url_for('.edit_view', id=get_pk_value(row), url=return_url) }}" title="Edit record">
<a class="icon" href="{{ url_for('.edit_view', id=get_pk_value(row), url=return_url) }}" title="{{ _gettext('Edit record') }}">
<span class="glyphicon glyphicon-pencil"></span>
</a>
{%- endif -%}
......@@ -145,9 +144,8 @@
{% block tail %}
{{ super() }}
<script src="{{ admin_static.url(filename='datetimepicker/bootstrap-datetimepicker.js') }}"></script>
<script src="{{ admin_static.url(filename='admin/js/form.js') }}"></script>
<script src="{{ admin_static.url(filename='admin/js/filters.js') }}"></script>
{{ lib.form_js() }}
{{ actionlib.script(_gettext('Please select at least one model.'),
actions,
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment