Commit 1b36345c authored by Serge S. Koval's avatar Serge S. Koval

Merge pull request #800 from pawl/improvefileadmin

FileAdmin Improvements
parents 3c2b7122 8d690ae9
This diff is collapsed.
This diff is collapsed.
from re import sub from re import sub
from jinja2 import contextfunction from jinja2 import contextfunction
from flask import g, request, url_for from flask import g, request, url_for, flash
from wtforms.validators import DataRequired, InputRequired from wtforms.validators import DataRequired, InputRequired
from flask.ext.admin._compat import urljoin, urlparse from flask.ext.admin._compat import urljoin, urlparse, iteritems
from flask.ext.admin.babel import gettext
from ._compat import string_types from ._compat import string_types
...@@ -93,7 +93,12 @@ def is_field_error(errors): ...@@ -93,7 +93,12 @@ def is_field_error(errors):
return True return True
return False return False
def flash_errors(form, message):
for field_name, errors in iteritems(form.errors):
errors = form[field_name].label.text + u": " + u", ".join(errors)
flash(gettext(message, error=str(errors)), 'error')
@contextfunction @contextfunction
def resolve_ctx(context): def resolve_ctx(context):
......
...@@ -14,7 +14,7 @@ from flask.ext.admin.form import BaseForm, FormOpts, rules ...@@ -14,7 +14,7 @@ from flask.ext.admin.form import BaseForm, FormOpts, rules
from flask.ext.admin.model import filters, typefmt from flask.ext.admin.model import filters, typefmt
from flask.ext.admin.actions import ActionsMixin from flask.ext.admin.actions import ActionsMixin
from flask.ext.admin.helpers import (get_form_data, validate_form_on_submit, from flask.ext.admin.helpers import (get_form_data, validate_form_on_submit,
get_redirect_target) get_redirect_target, flash_errors)
from flask.ext.admin.tools import rec_getattr from flask.ext.admin.tools import rec_getattr
from flask.ext.admin._backwards import ObsoleteAttr from flask.ext.admin._backwards import ObsoleteAttr
from flask.ext.admin._compat import iteritems, OrderedDict, as_unicode from flask.ext.admin._compat import iteritems, OrderedDict, as_unicode
...@@ -972,6 +972,9 @@ class BaseModelView(BaseView, ActionsMixin): ...@@ -972,6 +972,9 @@ class BaseModelView(BaseView, ActionsMixin):
Instantiate model delete form and return it. Instantiate model delete form and return it.
Override to implement custom behavior. Override to implement custom behavior.
The delete form originally used a GET request, so delete_form
accepts both GET and POST request for backwards compatibility.
""" """
if request.form: if request.form:
return self._delete_form_class(request.form) return self._delete_form_class(request.form)
...@@ -1558,12 +1561,7 @@ class BaseModelView(BaseView, ActionsMixin): ...@@ -1558,12 +1561,7 @@ class BaseModelView(BaseView, ActionsMixin):
flash(gettext('Record was successfully deleted.')) flash(gettext('Record was successfully deleted.'))
return redirect(return_url) return redirect(return_url)
else: else:
# flash validation errors flash_errors(form, message='Failed to delete record. %(error)s')
for field_name, errors in iteritems(form.errors):
errors = field_name + u": " + u", ".join(errors)
flash(gettext('Failed to delete record. %(error)s',
error=str(errors)),
'error')
return redirect(return_url) return redirect(return_url)
......
...@@ -58,7 +58,8 @@ ...@@ -58,7 +58,8 @@
{% if is_dir %} {% if is_dir %}
{% if name != '..' and admin_view.can_delete_dirs %} {% if name != '..' and admin_view.can_delete_dirs %}
<form class="icon" method="POST" action="{{ get_url('.delete') }}"> <form class="icon" method="POST" action="{{ get_url('.delete') }}">
<input type="hidden" name="path" value="{{ path }}"></input> {{ delete_form.path(value=path) }}
{{ delete_form.csrf_token }}
<button onclick="return confirm('{{ _gettext('Are you sure you want to delete \\\'%(name)s\\\' recursively?', name=name) }}')"> <button onclick="return confirm('{{ _gettext('Are you sure you want to delete \\\'%(name)s\\\' recursively?', name=name) }}')">
<i class="icon-remove"></i> <i class="icon-remove"></i>
</button> </button>
...@@ -66,7 +67,8 @@ ...@@ -66,7 +67,8 @@
{% endif %} {% endif %}
{% else %} {% else %}
<form class="icon" method="POST" action="{{ get_url('.delete') }}"> <form class="icon" method="POST" action="{{ get_url('.delete') }}">
<input type="hidden" name="path" value="{{ path }}"></input> {{ delete_form.path(value=path) }}
{{ delete_form.csrf_token }}
<button onclick="return confirm('{{ _gettext('Are you sure you want to delete \\\'%(name)s\\\'?', name=name) }}')"> <button onclick="return confirm('{{ _gettext('Are you sure you want to delete \\\'%(name)s\\\'?', name=name) }}')">
<i class="icon-remove"></i> <i class="icon-remove"></i>
</button> </button>
......
...@@ -58,7 +58,8 @@ ...@@ -58,7 +58,8 @@
{% if is_dir %} {% if is_dir %}
{% if name != '..' and admin_view.can_delete_dirs %} {% if name != '..' and admin_view.can_delete_dirs %}
<form class="icon" method="POST" action="{{ get_url('.delete') }}"> <form class="icon" method="POST" action="{{ get_url('.delete') }}">
<input type="hidden" name="path" value="{{ path }}"></input> {{ delete_form.path(value=path) }}
{{ delete_form.csrf_token }}
<button onclick="return confirm('{{ _gettext('Are you sure you want to delete \\\'%(name)s\\\' recursively?', name=name) }}')"> <button onclick="return confirm('{{ _gettext('Are you sure you want to delete \\\'%(name)s\\\' recursively?', name=name) }}')">
<i class="glyphicon glyphicon-remove"></i> <i class="glyphicon glyphicon-remove"></i>
</button> </button>
...@@ -66,7 +67,8 @@ ...@@ -66,7 +67,8 @@
{% endif %} {% endif %}
{% else %} {% else %}
<form class="icon" method="POST" action="{{ get_url('.delete') }}"> <form class="icon" method="POST" action="{{ get_url('.delete') }}">
<input type="hidden" name="path" value="{{ path }}"></input> {{ delete_form.path(value=path) }}
{{ delete_form.csrf_token }}
<button onclick="return confirm('{{ _gettext('Are you sure you want to delete \\\'%(name)s\\\'?', name=name) }}')"> <button onclick="return confirm('{{ _gettext('Are you sure you want to delete \\\'%(name)s\\\'?', name=name) }}')">
<i class="glyphicon glyphicon-trash"></i> <i class="glyphicon glyphicon-trash"></i>
</button> </button>
......
from nose.tools import eq_, ok_
import os.path as op import os.path as op
from nose.tools import eq_, ok_
from flask.ext.admin.contrib import fileadmin from flask.ext.admin.contrib import fileadmin
from . import setup from . import setup
try:
from StringIO import StringIO
except ImportError:
from io import StringIO
def create_view(): def create_view():
app, admin = setup() app, admin = setup()
class MyFileAdmin(fileadmin.FileAdmin):
editable_extensions = ('txt',)
path = op.join(op.dirname(__file__), 'files') path = op.join(op.dirname(__file__), 'files')
view = fileadmin.FileAdmin(path, '/files/', name='Files') view = MyFileAdmin(path, '/files/', name='Files')
admin.add_view(view) admin.add_view(view)
return app, admin, view return app, admin, view
...@@ -21,8 +30,104 @@ def test_file_admin(): ...@@ -21,8 +30,104 @@ def test_file_admin():
client = app.test_client() client = app.test_client()
rv = client.get('/admin/fileadmin/') # index
rv = client.get('/admin/myfileadmin/')
eq_(rv.status_code, 200)
ok_('path=dummy.txt' in rv.data.decode('utf-8'))
# edit
rv = client.get('/admin/myfileadmin/edit/?path=dummy.txt')
eq_(rv.status_code, 200) eq_(rv.status_code, 200)
ok_('dummy.txt' in rv.data.decode('utf-8')) ok_('dummy.txt' in rv.data.decode('utf-8'))
# TODO: Check actions, etc rv = client.post('/admin/myfileadmin/edit/?path=dummy.txt', data=dict(
content='new_string'
))
eq_(rv.status_code, 302)
rv = client.get('/admin/myfileadmin/edit/?path=dummy.txt')
eq_(rv.status_code, 200)
ok_('dummy.txt' in rv.data.decode('utf-8'))
ok_('new_string' in rv.data.decode('utf-8'))
# rename
rv = client.get('/admin/myfileadmin/rename/?path=dummy.txt')
eq_(rv.status_code, 200)
ok_('dummy.txt' in rv.data.decode('utf-8'))
rv = client.post('/admin/myfileadmin/rename/?path=dummy.txt', data=dict(
name='dummy_renamed.txt',
path='dummy.txt'
))
eq_(rv.status_code, 302)
rv = client.get('/admin/myfileadmin/')
eq_(rv.status_code, 200)
ok_('path=dummy_renamed.txt' in rv.data.decode('utf-8'))
ok_('path=dummy.txt' not in rv.data.decode('utf-8'))
# upload
rv = client.get('/admin/myfileadmin/upload/')
eq_(rv.status_code, 200)
rv = client.post('/admin/myfileadmin/upload/', data=dict(
upload=(StringIO(""), 'dummy.txt'),
))
eq_(rv.status_code, 302)
rv = client.get('/admin/myfileadmin/')
eq_(rv.status_code, 200)
ok_('path=dummy.txt' in rv.data.decode('utf-8'))
ok_('path=dummy_renamed.txt' in rv.data.decode('utf-8'))
# delete
rv = client.post('/admin/myfileadmin/delete/', data=dict(
path='dummy_renamed.txt'
))
eq_(rv.status_code, 302)
rv = client.get('/admin/myfileadmin/')
eq_(rv.status_code, 200)
ok_('path=dummy_renamed.txt' not in rv.data.decode('utf-8'))
ok_('path=dummy.txt' in rv.data.decode('utf-8'))
# mkdir
rv = client.get('/admin/myfileadmin/mkdir/')
eq_(rv.status_code, 200)
rv = client.post('/admin/myfileadmin/mkdir/', data=dict(
name='dummy_dir'
))
eq_(rv.status_code, 302)
rv = client.get('/admin/myfileadmin/')
eq_(rv.status_code, 200)
ok_('path=dummy.txt' in rv.data.decode('utf-8'))
ok_('path=dummy_dir' in rv.data.decode('utf-8'))
# rename - directory
rv = client.get('/admin/myfileadmin/rename/?path=dummy_dir')
eq_(rv.status_code, 200)
ok_('dummy_dir' in rv.data.decode('utf-8'))
rv = client.post('/admin/myfileadmin/rename/?path=dummy_dir', data=dict(
name='dummy_renamed_dir',
path='dummy_dir'
))
eq_(rv.status_code, 302)
rv = client.get('/admin/myfileadmin/')
eq_(rv.status_code, 200)
ok_('path=dummy_renamed_dir' in rv.data.decode('utf-8'))
ok_('path=dummy_dir' not in rv.data.decode('utf-8'))
# delete - directory
rv = client.post('/admin/myfileadmin/delete/', data=dict(
path='dummy_renamed_dir'
))
eq_(rv.status_code, 302)
rv = client.get('/admin/myfileadmin/')
eq_(rv.status_code, 200)
ok_('path=dummy_renamed_dir' not in rv.data.decode('utf-8'))
ok_('path=dummy.txt' in rv.data.decode('utf-8'))
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