Commit 373448cb authored by mrjoes's avatar mrjoes

Separated actions from model admin

parent 73256448
from flask import request, url_for, redirect
def action(name, text, confirmation=None):
"""
Use this decorator to expose mass-model actions
`name`
Action name
`text`
Action text.
Will be passed through gettext() before rendering.
`confirmation`
Confirmation text. If not provided, action will be executed
unconditionally.
Will be passed through gettext() before rendering.
"""
def wrap(f):
f._action = (name, text, confirmation)
return f
return wrap
class ActionsMixin(object):
def __init__(self):
self._actions = []
self._actions_data = {}
def init_actions(self):
self._actions = []
self._actions_data = {}
for p in dir(self):
attr = getattr(self, p)
if hasattr(attr, '_action'):
name, text, desc = attr._action
self._actions.append((name, text))
# TODO: Use namedtuple
self._actions_data[name] = (attr, text, desc)
def is_action_allowed(self, name):
return True
def get_actions_list(self):
actions = []
actions_confirmation = {}
for act in self._actions:
name, text = act
if self.is_action_allowed(name):
text = unicode(text)
actions.append((name, text))
actions_confirmation[name] = unicode(self._actions_data[name][2])
return actions, actions_confirmation
def handle_action(self, return_view=None):
action = request.form.get('action')
ids = request.form.getlist('rowid')
handler = self._actions_data.get(action)
if handler and self.is_action_allowed(action):
response = handler[0](ids)
if response is not None:
return response
if not return_view:
url = url_for('.' + self._default_view)
else:
url = url_for('.' + return_view)
return redirect(url)
def action(name, text, confirmation=None):
"""
Use this decorator to expose mass-model actions
`name`
Action name
`text`
Action text.
Will be passed through gettext() before rendering.
`confirmation`
Confirmation text. If not provided, action will be executed
unconditionally.
Will be passed through gettext() before rendering.
"""
def wrap(f):
f._action = (name, text, confirmation)
return f
return wrap
...@@ -5,9 +5,10 @@ from flask.ext.admin.babel import gettext ...@@ -5,9 +5,10 @@ from flask.ext.admin.babel import gettext
from flask.ext.admin.base import BaseView, expose from flask.ext.admin.base import BaseView, expose
from flask.ext.admin.tools import rec_getattr from flask.ext.admin.tools import rec_getattr
from flask.ext.admin.model import filters from flask.ext.admin.model import filters
from flask.ext.admin.actions import ActionsMixin
class BaseModelView(BaseView): class BaseModelView(BaseView, ActionsMixin):
""" """
Base model view. Base model view.
...@@ -230,7 +231,7 @@ class BaseModelView(BaseView): ...@@ -230,7 +231,7 @@ class BaseModelView(BaseView):
self.model = model self.model = model
# Actions # Actions
self._init_actions() self.init_actions()
# Scaffolding # Scaffolding
self._refresh_cache() self._refresh_cache()
...@@ -275,22 +276,6 @@ class BaseModelView(BaseView): ...@@ -275,22 +276,6 @@ class BaseModelView(BaseView):
self._filter_groups = None self._filter_groups = None
self._filter_types = None self._filter_types = None
# Actions
def _init_actions(self):
self._actions = []
self._action_data = dict()
for p in dir(self):
attr = getattr(self, p)
if hasattr(attr, '_action'):
name, text, desc = attr._action
self._actions.append((name, text))
# TODO: Use namedtuple
self._action_data[name] = (attr, text, desc)
# Primary key # Primary key
def get_pk_value(self, model): def get_pk_value(self, model):
""" """
...@@ -715,18 +700,8 @@ class BaseModelView(BaseView): ...@@ -715,18 +700,8 @@ class BaseModelView(BaseView):
return self._get_url('.index_view', page, column, desc, return self._get_url('.index_view', page, column, desc,
search, filters) search, filters)
# Actions. # Actions
actions = [] actions, actions_confirmation = self.get_actions_list()
actions_confirmation = {}
for act in self._actions:
name, text = act
if self.is_action_allowed(name):
text = unicode(text)
actions.append((name, text))
actions_confirmation[name] = unicode(self._action_data[name][2])
return self.render(self.list_template, return self.render(self.list_template,
data=data, data=data,
...@@ -851,15 +826,4 @@ class BaseModelView(BaseView): ...@@ -851,15 +826,4 @@ class BaseModelView(BaseView):
""" """
Mass-model action view. Mass-model action view.
""" """
action = request.form.get('action') return self.handle_action()
ids = request.form.getlist('rowid')
handler = self._action_data.get(action)
if handler and self.is_action_allowed(action):
response = handler[0](ids)
if response is not None:
return response
return redirect(url_for('.index_view'))
var AdminModelActions = function(actionErrorMessage, actionConfirmations) {
// Actions helpers. TODO: Move to separate file
this.execute = function(name) {
var selected = $('input.action-checkbox:checked').size();
if (selected === 0) {
alert(actionErrorMessage);
return false;
}
var msg = actionConfirmations[name];
if (!!msg)
if (!confirm(msg))
return false;
// Update hidden form and submit it
var form = $('#action_form');
$('#action', form).val(name);
$('input.action-checkbox', form).remove();
$('input.action-checkbox:checked').each(function() {
form.append($(this).clone());
});
form.submit();
return false;
};
$(function() {
$('#rowtoggle').change(function() {
$('td input[type=checkbox]').attr('checked', this.checked);
});
});
};
{% macro dropdown(actions) -%}
{% if actions %}
<li class="dropdown">
<a class="dropdown-toggle" data-toggle="dropdown" href="#">
{{ _gettext('With selected')}}<b class="caret"></b>
</a>
<ul id="action_list" class="dropdown-menu">
{% for p in actions %}
<li>
<a href="#" onclick="return modelActions.execute('{{ p[0] }}');">{{ _gettext(p[1]) }}</a>
</li>
{% endfor %}
</ul>
</li>
{% endif %}
{% endmacro %}
{% macro form(actions, view) %}
{% if actions %}
<form id="action_form" action="{{ url_for(view) }}" method="POST" style="display: none">
<input type="hidden" id="action" name="action" />
</form>
{% endif %}
{% endmacro %}
{% macro script(message, actions, actions_confirmation) %}
{% if actions %}
<script src="{{ url_for('admin.static', filename='js/actions.js') }}"></script>
<script language="javascript">
var modelActions = new AdminModelActions({{ message|tojson|safe }}, {{ actions_confirmation|tojson|safe }});
</script>
{% endif %}
{% endmacro %}
\ No newline at end of file
{% extends 'admin/master.html' %} {% extends 'admin/master.html' %}
{% import 'admin/lib.html' as lib with context %} {% import 'admin/lib.html' as lib with context %}
{% import 'admin/actions.html' as actionlib with context %}
{% block head %} {% block head %}
<link href="{{ url_for('admin.static', filename='chosen/chosen.css') }}" rel="stylesheet"> <link href="{{ url_for('admin.static', filename='chosen/chosen.css') }}" rel="stylesheet">
...@@ -32,20 +33,7 @@ ...@@ -32,20 +33,7 @@
</li> </li>
{% endif %} {% endif %}
{% if actions %} {{ actionlib.dropdown(actions) }}
<li class="dropdown">
<a class="dropdown-toggle" data-toggle="dropdown" href="#">
{{ _gettext('With selected')}}<b class="caret"></b>
</a>
<ul id="action_list" class="dropdown-menu">
{% for p in actions %}
<li>
<a href="#" onclick="return modelActions.execute('{{ p[0] }}');">{{ _gettext(p[1]) }}</a>
</li>
{% endfor %}
</ul>
</li>
{% endif %}
{% if search_supported %} {% if search_supported %}
<li> <li>
...@@ -107,7 +95,7 @@ ...@@ -107,7 +95,7 @@
<tr> <tr>
{% if actions %} {% if actions %}
<th class="span1"> <th class="span1">
<input type="checkbox" name="rowtoggle" id="rowtoggle" /> <input type="checkbox" name="rowtoggle" class="action-rowtoggle" />
</th> </th>
{% endif %} {% endif %}
<th class="span1">&nbsp;</th> <th class="span1">&nbsp;</th>
...@@ -166,24 +154,18 @@ ...@@ -166,24 +154,18 @@
</table> </table>
{{ lib.pager(page, num_pages, pager_url) }} {{ lib.pager(page, num_pages, pager_url) }}
{% if actions %} {{ actionlib.form(actions, '.action_view') }}
<form id="action_form" action="{{ url_for('.action_view') }}" method="POST" style="display: none">
<input type="hidden" id="action" name="action" />
</form>
{% endif %}
{% endblock %} {% endblock %}
{% block tail %} {% block tail %}
<script src="{{ url_for('admin.static', filename='js/bootstrap-datepicker.js') }}"></script> <script src="{{ url_for('admin.static', filename='js/bootstrap-datepicker.js') }}"></script>
<script src="{{ url_for('admin.static', filename='js/form.js') }}"></script> <script src="{{ url_for('admin.static', filename='js/form.js') }}"></script>
<script src="{{ url_for('admin.static', filename='js/filters.js') }}"></script> <script src="{{ url_for('admin.static', filename='js/filters.js') }}"></script>
{% if actions %}
<script src="{{ url_for('admin.static', filename='js/actions.js') }}"></script> {{ actionlib.script(_gettext('Please select at least one model.'),
<script language="javascript"> actions,
var modelActions = new AdminModelActions('{{ _gettext("Please select at least one model.") }}', actions_confirmation) }}
{{ actions_confirmation|tojson|safe }});
</script>
{% endif %}
{% if filter_groups is not none and filter_data is not none %} {% if filter_groups is not none and filter_data is not none %}
<script language="javascript"> <script language="javascript">
var form = new AdminForm(); var form = new AdminForm();
......
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