Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Sign in
Toggle navigation
F
flask-admin
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
JIRA
JIRA
Merge Requests
0
Merge Requests
0
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Commits
Issue Boards
Open sidebar
Python-Dev
flask-admin
Commits
1b36345c
Commit
1b36345c
authored
Mar 01, 2015
by
Serge S. Koval
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #800 from pawl/improvefileadmin
FileAdmin Improvements
parents
3c2b7122
8d690ae9
Changes
7
Hide whitespace changes
Inline
Side-by-side
Showing
7 changed files
with
461 additions
and
215 deletions
+461
-215
admin.pot
babel/admin.pot
+99
-98
fileadmin.py
flask_admin/contrib/fileadmin.py
+231
-98
helpers.py
flask_admin/helpers.py
+9
-4
base.py
flask_admin/model/base.py
+5
-7
list.html
flask_admin/templates/bootstrap2/admin/file/list.html
+4
-2
list.html
flask_admin/templates/bootstrap3/admin/file/list.html
+4
-2
test_fileadmin.py
flask_admin/tests/fileadmin/test_fileadmin.py
+109
-4
No files found.
babel/admin.pot
View file @
1b36345c
...
@@ -9,7 +9,7 @@ msgid ""
...
@@ -9,7 +9,7 @@ msgid ""
msgstr ""
msgstr ""
"Project-Id-Version: Flask-Admin VERSION\n"
"Project-Id-Version: Flask-Admin VERSION\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2015-0
1-16 12:12
-0600\n"
"POT-Creation-Date: 2015-0
2-28 21:53
-0600\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
...
@@ -18,153 +18,159 @@ msgstr ""
...
@@ -18,153 +18,159 @@ msgstr ""
"Content-Transfer-Encoding: 8bit\n"
"Content-Transfer-Encoding: 8bit\n"
"Generated-By: Babel 1.3\n"
"Generated-By: Babel 1.3\n"
#: ../flask_admin/base.py:4
19
#: ../flask_admin/base.py:4
26
msgid "Home"
msgid "Home"
msgstr ""
msgstr ""
#: ../flask_admin/contrib/fileadmin.py:34
#: ../flask_admin/contrib/fileadmin.py:220
msgid "Invalid directory name"
msgstr ""
#: ../flask_admin/contrib/fileadmin.py:42
msgid "File to upload"
msgid "File to upload"
msgstr ""
msgstr ""
#: ../flask_admin/contrib/fileadmin.py:
51
#: ../flask_admin/contrib/fileadmin.py:
228
msgid "File required."
msgid "File required."
msgstr ""
msgstr ""
#: ../flask_admin/contrib/fileadmin.py:
56
#: ../flask_admin/contrib/fileadmin.py:
233
msgid "Invalid file type."
msgid "Invalid file type."
msgstr ""
msgstr ""
#: ../flask_admin/contrib/fileadmin.py:
60
#: ../flask_admin/contrib/fileadmin.py:
244
msgid "Content"
msgid "Content"
msgstr ""
msgstr ""
#: ../flask_admin/contrib/fileadmin.py:427
#: ../flask_admin/contrib/fileadmin.py:258
msgid "Invalid name"
msgstr ""
#: ../flask_admin/contrib/fileadmin.py:266
#: ../flask_admin/templates/bootstrap2/admin/file/list.html:35
#: ../flask_admin/templates/bootstrap3/admin/file/list.html:35
msgid "Name"
msgstr ""
#: ../flask_admin/contrib/fileadmin.py:544
#, python-format
#, python-format
msgid "File \"%(name)s\" already exists."
msgid "File \"%(name)s\" already exists."
msgstr ""
msgstr ""
#: ../flask_admin/contrib/fileadmin.py:
446
#: ../flask_admin/contrib/fileadmin.py:
568
#: ../flask_admin/contrib/fileadmin.py:
512
#: ../flask_admin/contrib/fileadmin.py:
635
#: ../flask_admin/contrib/fileadmin.py:
565
#: ../flask_admin/contrib/fileadmin.py:
688
#: ../flask_admin/contrib/fileadmin.py:
602
#: ../flask_admin/contrib/fileadmin.py:
729
#: ../flask_admin/contrib/fileadmin.py:
64
5
#: ../flask_admin/contrib/fileadmin.py:
77
5
#: ../flask_admin/contrib/fileadmin.py:
693
#: ../flask_admin/contrib/fileadmin.py:
824
msgid "Permission denied."
msgid "Permission denied."
msgstr ""
msgstr ""
#: ../flask_admin/contrib/fileadmin.py:
508
#: ../flask_admin/contrib/fileadmin.py:
631
msgid "File uploading is disabled."
msgid "File uploading is disabled."
msgstr ""
msgstr ""
#: ../flask_admin/contrib/fileadmin.py:
521
#: ../flask_admin/contrib/fileadmin.py:
644
#, python-format
#, python-format
msgid "Failed to save file: %(error)s"
msgid "Failed to save file: %(error)s"
msgstr ""
msgstr ""
#: ../flask_admin/contrib/fileadmin.py:
561
#: ../flask_admin/contrib/fileadmin.py:
684
msgid "Directory creation is disabled."
msgid "Directory creation is disabled."
msgstr ""
msgstr ""
#: ../flask_admin/contrib/fileadmin.py:
576
#: ../flask_admin/contrib/fileadmin.py:
699
#, python-format
#, python-format
msgid "Failed to create directory: %(error)s"
msgid "Failed to create directory: %(error)s"
msgstr ""
msgstr ""
#: ../flask_admin/contrib/fileadmin.py:
598
#: ../flask_admin/contrib/fileadmin.py:
725
msgid "Deletion is disabled."
msgid "Deletion is disabled."
msgstr ""
msgstr ""
#: ../flask_admin/contrib/fileadmin.py:
607
#: ../flask_admin/contrib/fileadmin.py:
734
msgid "Directory deletion is disabled."
msgid "Directory deletion is disabled."
msgstr ""
msgstr ""
#: ../flask_admin/contrib/fileadmin.py:
613
#: ../flask_admin/contrib/fileadmin.py:
740
#, python-format
#, python-format
msgid "Directory \"%(path)s\" was successfully deleted."
msgid "Directory \"%(path)s\" was successfully deleted."
msgstr ""
msgstr ""
#: ../flask_admin/contrib/fileadmin.py:
615
#: ../flask_admin/contrib/fileadmin.py:
742
#, python-format
#, python-format
msgid "Failed to delete directory: %(error)s"
msgid "Failed to delete directory: %(error)s"
msgstr ""
msgstr ""
#: ../flask_admin/contrib/fileadmin.py:
620
#: ../flask_admin/contrib/fileadmin.py:
747
#: ../flask_admin/contrib/fileadmin.py:
759
#: ../flask_admin/contrib/fileadmin.py:
892
#, python-format
#, python-format
msgid "File \"%(name)s\" was successfully deleted."
msgid "File \"%(name)s\" was successfully deleted."
msgstr ""
msgstr ""
#: ../flask_admin/contrib/fileadmin.py:
622
#: ../flask_admin/contrib/fileadmin.py:
749
#: ../flask_admin/contrib/fileadmin.py:
761
#: ../flask_admin/contrib/fileadmin.py:
894
#, python-format
#, python-format
msgid "Failed to delete file: %(name)s"
msgid "Failed to delete file: %(name)s"
msgstr ""
msgstr ""
#: ../flask_admin/contrib/fileadmin.py:
64
1
#: ../flask_admin/contrib/fileadmin.py:
77
1
msgid "Renaming is disabled."
msgid "Renaming is disabled."
msgstr ""
msgstr ""
#: ../flask_admin/contrib/fileadmin.py:
64
9
#: ../flask_admin/contrib/fileadmin.py:
77
9
msgid "Path does not exist."
msgid "Path does not exist."
msgstr ""
msgstr ""
#: ../flask_admin/contrib/fileadmin.py:
661
#: ../flask_admin/contrib/fileadmin.py:
790
#, python-format
#, python-format
msgid "Successfully renamed \"%(src)s\" to \"%(dst)s\""
msgid "Successfully renamed \"%(src)s\" to \"%(dst)s\""
msgstr ""
msgstr ""
#: ../flask_admin/contrib/fileadmin.py:
664
#: ../flask_admin/contrib/fileadmin.py:
793
#, python-format
#, python-format
msgid "Failed to rename: %(error)s"
msgid "Failed to rename: %(error)s"
msgstr ""
msgstr ""
#: ../flask_admin/contrib/fileadmin.py:
709
#: ../flask_admin/contrib/fileadmin.py:
840
#, python-format
#, python-format
msgid "Error saving changes to %(name)s."
msgid "Error saving changes to %(name)s."
msgstr ""
msgstr ""
#: ../flask_admin/contrib/fileadmin.py:
713
#: ../flask_admin/contrib/fileadmin.py:
844
#, python-format
#, python-format
msgid "Changes to %(name)s saved successfully."
msgid "Changes to %(name)s saved successfully."
msgstr ""
msgstr ""
#: ../flask_admin/contrib/fileadmin.py:
720
#: ../flask_admin/contrib/fileadmin.py:
853
#, python-format
#, python-format
msgid "Error reading %(name)s."
msgid "Error reading %(name)s."
msgstr ""
msgstr ""
#: ../flask_admin/contrib/fileadmin.py:
723
#: ../flask_admin/contrib/fileadmin.py:
856
#: ../flask_admin/contrib/fileadmin.py:
732
#: ../flask_admin/contrib/fileadmin.py:
865
#, python-format
#, python-format
msgid "Unexpected error while reading from %(name)s"
msgid "Unexpected error while reading from %(name)s"
msgstr ""
msgstr ""
#: ../flask_admin/contrib/fileadmin.py:
729
#: ../flask_admin/contrib/fileadmin.py:
862
#, python-format
#, python-format
msgid "Cannot edit %(name)s."
msgid "Cannot edit %(name)s."
msgstr ""
msgstr ""
#: ../flask_admin/contrib/fileadmin.py:
746
#: ../flask_admin/contrib/fileadmin.py:
879
#: ../flask_admin/contrib/mongoengine/view.py:6
12
#: ../flask_admin/contrib/mongoengine/view.py:6
25
#: ../flask_admin/contrib/peewee/view.py:4
18
#: ../flask_admin/contrib/peewee/view.py:4
29
#: ../flask_admin/contrib/pymongo/view.py:34
3
#: ../flask_admin/contrib/pymongo/view.py:34
8
#: ../flask_admin/contrib/sqla/view.py:9
45
#: ../flask_admin/contrib/sqla/view.py:9
74
msgid "Delete"
msgid "Delete"
msgstr ""
msgstr ""
#: ../flask_admin/contrib/fileadmin.py:
747
#: ../flask_admin/contrib/fileadmin.py:
880
msgid "Are you sure you want to delete these files?"
msgid "Are you sure you want to delete these files?"
msgstr ""
msgstr ""
#: ../flask_admin/contrib/fileadmin.py:
750
#: ../flask_admin/contrib/fileadmin.py:
883
msgid "File deletion is disabled."
msgid "File deletion is disabled."
msgstr ""
msgstr ""
#: ../flask_admin/contrib/fileadmin.py:
763
#: ../flask_admin/contrib/fileadmin.py:
896
msgid "Edit"
msgid "Edit"
msgstr ""
msgstr ""
...
@@ -246,57 +252,57 @@ msgstr ""
...
@@ -246,57 +252,57 @@ msgstr ""
msgid "not between"
msgid "not between"
msgstr ""
msgstr ""
#: ../flask_admin/contrib/mongoengine/view.py:5
06
#: ../flask_admin/contrib/mongoengine/view.py:5
19
#, python-format
#, python-format
msgid "Failed to get model. %(error)s"
msgid "Failed to get model. %(error)s"
msgstr ""
msgstr ""
#: ../flask_admin/contrib/mongoengine/view.py:5
25
#: ../flask_admin/contrib/mongoengine/view.py:5
38
#: ../flask_admin/contrib/peewee/view.py:3
69
#: ../flask_admin/contrib/peewee/view.py:3
80
#: ../flask_admin/contrib/pymongo/view.py:2
78
#: ../flask_admin/contrib/pymongo/view.py:2
83
#: ../flask_admin/contrib/sqla/view.py:
877
#: ../flask_admin/contrib/sqla/view.py:
906
#, python-format
#, python-format
msgid "Failed to create record. %(error)s"
msgid "Failed to create record. %(error)s"
msgstr ""
msgstr ""
#: ../flask_admin/contrib/mongoengine/view.py:5
51
#: ../flask_admin/contrib/mongoengine/view.py:5
64
#: ../flask_admin/contrib/peewee/view.py:3
88
#: ../flask_admin/contrib/peewee/view.py:3
99
#: ../flask_admin/contrib/pymongo/view.py:30
3
#: ../flask_admin/contrib/pymongo/view.py:30
8
#: ../flask_admin/contrib/sqla/view.py:9
03 ../flask_admin/model/base.py:156
3
#: ../flask_admin/contrib/sqla/view.py:9
32 ../flask_admin/model/base.py:161
3
#: ../flask_admin/model/base.py:1
57
2
#: ../flask_admin/model/base.py:1
62
2
#, python-format
#, python-format
msgid "Failed to update record. %(error)s"
msgid "Failed to update record. %(error)s"
msgstr ""
msgstr ""
#: ../flask_admin/contrib/mongoengine/view.py:5
75
#: ../flask_admin/contrib/mongoengine/view.py:5
88
#: ../flask_admin/contrib/peewee/view.py:4
04
#: ../flask_admin/contrib/peewee/view.py:4
15
#: ../flask_admin/contrib/pymongo/view.py:3
29
#: ../flask_admin/contrib/pymongo/view.py:3
34
#: ../flask_admin/contrib/sqla/view.py:9
29 ../flask_admin/model/base.py:1513
#: ../flask_admin/contrib/sqla/view.py:9
58
#, python-format
#, python-format
msgid "Failed to delete record. %(error)s"
msgid "Failed to delete record. %(error)s"
msgstr ""
msgstr ""
#: ../flask_admin/contrib/mongoengine/view.py:6
13
#: ../flask_admin/contrib/mongoengine/view.py:6
26
#: ../flask_admin/contrib/peewee/view.py:4
19
#: ../flask_admin/contrib/peewee/view.py:4
30
#: ../flask_admin/contrib/pymongo/view.py:34
4
#: ../flask_admin/contrib/pymongo/view.py:34
9
#: ../flask_admin/contrib/sqla/view.py:9
46
#: ../flask_admin/contrib/sqla/view.py:9
75
msgid "Are you sure you want to delete selected records?"
msgid "Are you sure you want to delete selected records?"
msgstr ""
msgstr ""
#: ../flask_admin/contrib/mongoengine/view.py:6
22
#: ../flask_admin/contrib/mongoengine/view.py:6
35
#: ../flask_admin/contrib/peewee/view.py:4
35
#: ../flask_admin/contrib/peewee/view.py:4
46
#: ../flask_admin/contrib/pymongo/view.py:35
4
#: ../flask_admin/contrib/pymongo/view.py:35
9
#: ../flask_admin/contrib/sqla/view.py:9
62 ../flask_admin/model/base.py:1506
#: ../flask_admin/contrib/sqla/view.py:9
91 ../flask_admin/model/base.py:1561
#, python-format
#, python-format
msgid "Record was successfully deleted."
msgid "Record was successfully deleted."
msgid_plural "%(count)s records were successfully deleted."
msgid_plural "%(count)s records were successfully deleted."
msgstr[0] ""
msgstr[0] ""
msgstr[1] ""
msgstr[1] ""
#: ../flask_admin/contrib/mongoengine/view.py:6
28
#: ../flask_admin/contrib/mongoengine/view.py:6
41
#: ../flask_admin/contrib/peewee/view.py:4
41
#: ../flask_admin/contrib/peewee/view.py:4
52
#: ../flask_admin/contrib/pymongo/view.py:3
59
#: ../flask_admin/contrib/pymongo/view.py:3
64
#: ../flask_admin/contrib/sqla/view.py:9
70
#: ../flask_admin/contrib/sqla/view.py:9
99
#, python-format
#, python-format
msgid "Failed to delete records. %(error)s"
msgid "Failed to delete records. %(error)s"
msgstr ""
msgstr ""
...
@@ -319,7 +325,7 @@ msgid_plural "At least %d items are required"
...
@@ -319,7 +325,7 @@ msgid_plural "At least %d items are required"
msgstr[0] ""
msgstr[0] ""
msgstr[1] ""
msgstr[1] ""
#: ../flask_admin/contrib/sqla/view.py:8
56
#: ../flask_admin/contrib/sqla/view.py:8
85
#, python-format
#, python-format
msgid "Integrity error. %(message)s"
msgid "Integrity error. %(message)s"
msgstr ""
msgstr ""
...
@@ -336,20 +342,20 @@ msgstr ""
...
@@ -336,20 +342,20 @@ msgstr ""
msgid "Invalid file extension"
msgid "Invalid file extension"
msgstr ""
msgstr ""
#: ../flask_admin/model/base.py:1
173
#: ../flask_admin/model/base.py:1
227
msgid "There are no items in the table."
msgid "There are no items in the table."
msgstr ""
msgstr ""
#: ../flask_admin/model/base.py:1
197
#: ../flask_admin/model/base.py:1
251
#, python-format
#, python-format
msgid "Invalid Filter Value: %(value)s"
msgid "Invalid Filter Value: %(value)s"
msgstr ""
msgstr ""
#: ../flask_admin/model/base.py:14
30
#: ../flask_admin/model/base.py:14
84
msgid "Record was successfully created."
msgid "Record was successfully created."
msgstr ""
msgstr ""
#: ../flask_admin/model/base.py:1
467 ../flask_admin/model/base.py:156
8
#: ../flask_admin/model/base.py:1
521 ../flask_admin/model/base.py:161
8
msgid "Record was successfully saved."
msgid "Record was successfully saved."
msgstr ""
msgstr ""
...
@@ -392,40 +398,35 @@ msgstr ""
...
@@ -392,40 +398,35 @@ msgstr ""
msgid "Root"
msgid "Root"
msgstr ""
msgstr ""
#: ../flask_admin/templates/bootstrap2/admin/file/list.html:35
#: ../flask_admin/templates/bootstrap3/admin/file/list.html:35
msgid "Name"
msgstr ""
#: ../flask_admin/templates/bootstrap2/admin/file/list.html:36
#: ../flask_admin/templates/bootstrap2/admin/file/list.html:36
#: ../flask_admin/templates/bootstrap3/admin/file/list.html:36
#: ../flask_admin/templates/bootstrap3/admin/file/list.html:36
msgid "Size"
msgid "Size"
msgstr ""
msgstr ""
#: ../flask_admin/templates/bootstrap2/admin/file/list.html:6
2
#: ../flask_admin/templates/bootstrap2/admin/file/list.html:6
3
#: ../flask_admin/templates/bootstrap3/admin/file/list.html:6
2
#: ../flask_admin/templates/bootstrap3/admin/file/list.html:6
3
#, python-format
#, python-format
msgid "Are you sure you want to delete \\'%(name)s\\' recursively?"
msgid "Are you sure you want to delete \\'%(name)s\\' recursively?"
msgstr ""
msgstr ""
#: ../flask_admin/templates/bootstrap2/admin/file/list.html:7
0
#: ../flask_admin/templates/bootstrap2/admin/file/list.html:7
2
#: ../flask_admin/templates/bootstrap3/admin/file/list.html:7
0
#: ../flask_admin/templates/bootstrap3/admin/file/list.html:7
2
#, python-format
#, python-format
msgid "Are you sure you want to delete \\'%(name)s\\'?"
msgid "Are you sure you want to delete \\'%(name)s\\'?"
msgstr ""
msgstr ""
#: ../flask_admin/templates/bootstrap2/admin/file/list.html:10
5
#: ../flask_admin/templates/bootstrap2/admin/file/list.html:10
7
#: ../flask_admin/templates/bootstrap3/admin/file/list.html:10
5
#: ../flask_admin/templates/bootstrap3/admin/file/list.html:10
7
msgid "Upload File"
msgid "Upload File"
msgstr ""
msgstr ""
#: ../flask_admin/templates/bootstrap2/admin/file/list.html:11
0
#: ../flask_admin/templates/bootstrap2/admin/file/list.html:11
2
#: ../flask_admin/templates/bootstrap3/admin/file/list.html:11
0
#: ../flask_admin/templates/bootstrap3/admin/file/list.html:11
2
msgid "Create Directory"
msgid "Create Directory"
msgstr ""
msgstr ""
#: ../flask_admin/templates/bootstrap2/admin/file/list.html:12
7
#: ../flask_admin/templates/bootstrap2/admin/file/list.html:12
9
#: ../flask_admin/templates/bootstrap3/admin/file/list.html:12
7
#: ../flask_admin/templates/bootstrap3/admin/file/list.html:12
9
msgid "Please select at least one file."
msgid "Please select at least one file."
msgstr ""
msgstr ""
...
@@ -519,17 +520,17 @@ msgstr ""
...
@@ -519,17 +520,17 @@ msgstr ""
msgid "Edit record"
msgid "Edit record"
msgstr ""
msgstr ""
#: ../flask_admin/templates/bootstrap2/admin/model/list.html:11
6
#: ../flask_admin/templates/bootstrap2/admin/model/list.html:11
4
#: ../flask_admin/templates/bootstrap3/admin/model/list.html:11
6
#: ../flask_admin/templates/bootstrap3/admin/model/list.html:11
4
msgid "Are you sure you want to delete this record?"
msgid "Are you sure you want to delete this record?"
msgstr ""
msgstr ""
#: ../flask_admin/templates/bootstrap2/admin/model/list.html:11
6
#: ../flask_admin/templates/bootstrap2/admin/model/list.html:11
4
msgid "Delete record"
msgid "Delete record"
msgstr ""
msgstr ""
#: ../flask_admin/templates/bootstrap2/admin/model/list.html:1
61
#: ../flask_admin/templates/bootstrap2/admin/model/list.html:1
59
#: ../flask_admin/templates/bootstrap3/admin/model/list.html:1
60
#: ../flask_admin/templates/bootstrap3/admin/model/list.html:1
58
msgid "Please select at least one record."
msgid "Please select at least one record."
msgstr ""
msgstr ""
flask_admin/contrib/fileadmin.py
View file @
1b36345c
...
@@ -19,48 +19,6 @@ from flask.ext.admin.actions import action, ActionsMixin
...
@@ -19,48 +19,6 @@ from flask.ext.admin.actions import action, ActionsMixin
from
flask.ext.admin.babel
import
gettext
,
lazy_gettext
from
flask.ext.admin.babel
import
gettext
,
lazy_gettext
class
NameForm
(
form
.
BaseForm
):
"""
Form with a filename input field.
Validates if provided name is valid for *nix and Windows systems.
"""
name
=
fields
.
StringField
()
regexp
=
re
.
compile
(
r'^(?!^(PRN|AUX|CLOCK\$|NUL|CON|COM\d|LPT\d|\..*)(\..+)?$)[^\x00-\x1f\\?*:\";|/]+$'
)
def
validate_name
(
self
,
field
):
if
not
self
.
regexp
.
match
(
field
.
data
):
raise
validators
.
ValidationError
(
gettext
(
'Invalid directory name'
))
class
UploadForm
(
form
.
BaseForm
):
"""
File upload form. Works with FileAdmin instance to check if it is allowed
to upload file with given extension.
"""
upload
=
fields
.
FileField
(
lazy_gettext
(
'File to upload'
))
def
__init__
(
self
,
admin
):
self
.
admin
=
admin
super
(
UploadForm
,
self
)
.
__init__
(
helpers
.
get_form_data
())
def
validate_upload
(
self
,
field
):
if
not
self
.
upload
.
data
:
raise
validators
.
ValidationError
(
gettext
(
'File required.'
))
filename
=
self
.
upload
.
data
.
filename
if
not
self
.
admin
.
is_file_allowed
(
filename
):
raise
validators
.
ValidationError
(
gettext
(
'Invalid file type.'
))
class
EditForm
(
form
.
BaseForm
):
content
=
fields
.
TextAreaField
(
lazy_gettext
(
'Content'
),
(
validators
.
required
(),))
class
FileAdmin
(
BaseView
,
ActionsMixin
):
class
FileAdmin
(
BaseView
,
ActionsMixin
):
"""
"""
Simple file-management interface.
Simple file-management interface.
...
@@ -74,11 +32,15 @@ class FileAdmin(BaseView, ActionsMixin):
...
@@ -74,11 +32,15 @@ class FileAdmin(BaseView, ActionsMixin):
Sample usage::
Sample usage::
import os.path as op
from flask.ext.admin import Admin
from flask.ext.admin.contrib.fileadmin import FileAdmin
admin = Admin()
admin = Admin()
path = op.join(op.dirname(__file__), 'static')
path = op.join(op.dirname(__file__), 'static')
admin.add_view(FileAdmin(path, '/static/', name='Static Files'))
admin.add_view(FileAdmin(path, '/static/', name='Static Files'))
admin.setup_app(app)
"""
"""
can_upload
=
True
can_upload
=
True
...
@@ -156,9 +118,22 @@ class FileAdmin(BaseView, ActionsMixin):
...
@@ -156,9 +118,22 @@ class FileAdmin(BaseView, ActionsMixin):
Edit template
Edit template
"""
"""
upload_form
=
Upload
Form
form_base_class
=
form
.
Base
Form
"""
"""
Upload form class
Base form class. Will be used to create the upload, rename, edit, and delete form.
Allows enabling CSRF validation and useful if you want to have custom
contructor or override some fields.
Example::
class MyBaseForm(Form):
def do_something(self):
pass
class MyAdmin(FileAdmin):
form_base_class = MyBaseForm
"""
"""
def
__init__
(
self
,
base_path
,
base_url
=
None
,
def
__init__
(
self
,
base_path
,
base_url
=
None
,
...
@@ -231,6 +206,139 @@ class FileAdmin(BaseView, ActionsMixin):
...
@@ -231,6 +206,139 @@ class FileAdmin(BaseView, ActionsMixin):
"""
"""
return
self
.
base_url
return
self
.
base_url
def
get_upload_form
(
self
):
"""
Upload form class for file upload view.
Override to implement customized behavior.
"""
class
UploadForm
(
self
.
form_base_class
):
"""
File upload form. Works with FileAdmin instance to check if it
is allowed to upload file with given extension.
"""
upload
=
fields
.
FileField
(
lazy_gettext
(
'File to upload'
))
def
__init__
(
self
,
*
args
,
**
kwargs
):
super
(
UploadForm
,
self
)
.
__init__
(
*
args
,
**
kwargs
)
self
.
admin
=
kwargs
[
'admin'
]
def
validate_upload
(
self
,
field
):
if
not
self
.
upload
.
data
:
raise
validators
.
ValidationError
(
gettext
(
'File required.'
))
filename
=
self
.
upload
.
data
.
filename
if
not
self
.
admin
.
is_file_allowed
(
filename
):
raise
validators
.
ValidationError
(
gettext
(
'Invalid file type.'
))
return
UploadForm
def
get_edit_form
(
self
):
"""
Create form class for file editing view.
Override to implement customized behavior.
"""
class
EditForm
(
self
.
form_base_class
):
content
=
fields
.
TextAreaField
(
lazy_gettext
(
'Content'
),
(
validators
.
required
(),))
return
EditForm
def
get_name_form
(
self
):
"""
Create form class for renaming and mkdir views.
Override to implement customized behavior.
"""
def
validate_name
(
self
,
field
):
regexp
=
re
.
compile
(
r'^(?!^(PRN|AUX|CLOCK\$|NUL|CON|COM\d|LPT\d|\..*)(\..+)?$)[^\x00-\x1f\\?*:\";|/]+$'
)
if
not
regexp
.
match
(
field
.
data
):
raise
validators
.
ValidationError
(
gettext
(
'Invalid name'
))
class
NameForm
(
self
.
form_base_class
):
"""
Form with a filename input field.
Validates if provided name is valid for *nix and Windows systems.
"""
name
=
fields
.
StringField
(
lazy_gettext
(
'Name'
),
validators
=
[
validators
.
Required
(),
validate_name
])
path
=
fields
.
HiddenField
()
return
NameForm
def
get_delete_form
(
self
):
"""
Create form class for model delete view.
Override to implement customized behavior.
"""
class
DeleteForm
(
self
.
form_base_class
):
path
=
fields
.
HiddenField
(
validators
=
[
validators
.
Required
()])
return
DeleteForm
def
upload_form
(
self
):
"""
Instantiate file upload form and return it.
Override to implement custom behavior.
"""
upload_form_class
=
self
.
get_upload_form
()
if
request
.
form
:
# Workaround for allowing both CSRF token + FileField to be submitted
# https://bitbucket.org/danjac/flask-wtf/issue/12/fieldlist-filefield-does-not-follow
formdata
=
request
.
form
.
copy
()
# as request.form is immutable
formdata
.
update
(
request
.
files
)
# admin=self allows the form to use self.is_file_allowed
return
upload_form_class
(
formdata
,
admin
=
self
)
elif
request
.
files
:
return
upload_form_class
(
request
.
files
,
admin
=
self
)
else
:
return
upload_form_class
(
admin
=
self
)
def
name_form
(
self
):
"""
Instantiate form used in rename and mkdir then return it.
Override to implement custom behavior.
"""
name_form_class
=
self
.
get_name_form
()
if
request
.
form
:
return
name_form_class
(
request
.
form
)
elif
request
.
args
:
return
name_form_class
(
request
.
args
)
else
:
return
name_form_class
()
def
edit_form
(
self
):
"""
Instantiate file editing form and return it.
Override to implement custom behavior.
"""
edit_form_class
=
self
.
get_edit_form
()
if
request
.
form
:
return
edit_form_class
(
request
.
form
)
else
:
return
edit_form_class
()
def
delete_form
(
self
):
"""
Instantiate file delete form and return it.
Override to implement custom behavior.
"""
delete_form_class
=
self
.
get_delete_form
()
if
request
.
form
:
return
delete_form_class
(
request
.
form
)
else
:
return
delete_form_class
()
def
is_file_allowed
(
self
,
filename
):
def
is_file_allowed
(
self
,
filename
):
"""
"""
Verify if file can be uploaded.
Verify if file can be uploaded.
...
@@ -291,6 +399,15 @@ class FileAdmin(BaseView, ActionsMixin):
...
@@ -291,6 +399,15 @@ class FileAdmin(BaseView, ActionsMixin):
"""
"""
file_data
.
save
(
path
)
file_data
.
save
(
path
)
def
validate_form
(
self
,
form
):
"""
Validate the form on submit.
:param form:
Form to validate
"""
return
helpers
.
validate_form_on_submit
(
form
)
def
_get_dir_url
(
self
,
endpoint
,
path
=
None
,
**
kwargs
):
def
_get_dir_url
(
self
,
endpoint
,
path
=
None
,
**
kwargs
):
"""
"""
Return prettified URL
Return prettified URL
...
@@ -439,11 +556,16 @@ class FileAdmin(BaseView, ActionsMixin):
...
@@ -439,11 +556,16 @@ class FileAdmin(BaseView, ActionsMixin):
:param path:
:param path:
Optional directory path. If not provided, will use the base directory
Optional directory path. If not provided, will use the base directory
"""
"""
if
self
.
can_delete
:
delete_form
=
self
.
delete_form
()
else
:
delete_form
=
None
# Get path and verify if it is valid
# Get path and verify if it is valid
base_path
,
directory
,
path
=
self
.
_normalize_path
(
path
)
base_path
,
directory
,
path
=
self
.
_normalize_path
(
path
)
if
not
self
.
is_accessible_path
(
path
):
if
not
self
.
is_accessible_path
(
path
):
flash
(
gettext
(
'Permission denied.'
))
flash
(
gettext
(
'Permission denied.'
)
,
'error'
)
return
redirect
(
self
.
_get_dir_url
(
'.index'
))
return
redirect
(
self
.
_get_dir_url
(
'.index'
))
# Get directory listing
# Get directory listing
...
@@ -490,7 +612,8 @@ class FileAdmin(BaseView, ActionsMixin):
...
@@ -490,7 +612,8 @@ class FileAdmin(BaseView, ActionsMixin):
get_file_url
=
self
.
_get_file_url
,
get_file_url
=
self
.
_get_file_url
,
items
=
items
,
items
=
items
,
actions
=
actions
,
actions
=
actions
,
actions_confirmation
=
actions_confirmation
)
actions_confirmation
=
actions_confirmation
,
delete_form
=
delete_form
)
@
expose
(
'/upload/'
,
methods
=
(
'GET'
,
'POST'
))
@
expose
(
'/upload/'
,
methods
=
(
'GET'
,
'POST'
))
@
expose
(
'/upload/<path:path>'
,
methods
=
(
'GET'
,
'POST'
))
@
expose
(
'/upload/<path:path>'
,
methods
=
(
'GET'
,
'POST'
))
...
@@ -509,16 +632,16 @@ class FileAdmin(BaseView, ActionsMixin):
...
@@ -509,16 +632,16 @@ class FileAdmin(BaseView, ActionsMixin):
return
redirect
(
self
.
_get_dir_url
(
'.index'
,
path
))
return
redirect
(
self
.
_get_dir_url
(
'.index'
,
path
))
if
not
self
.
is_accessible_path
(
path
):
if
not
self
.
is_accessible_path
(
path
):
flash
(
gettext
(
'Permission denied.'
))
flash
(
gettext
(
'Permission denied.'
)
,
'error'
)
return
redirect
(
self
.
_get_dir_url
(
'.index'
))
return
redirect
(
self
.
_get_dir_url
(
'.index'
))
form
=
self
.
upload_form
(
self
)
form
=
self
.
upload_form
()
if
helpers
.
validate_form_on_submit
(
form
):
if
self
.
validate_form
(
form
):
try
:
try
:
self
.
_save_form_files
(
directory
,
path
,
form
)
self
.
_save_form_files
(
directory
,
path
,
form
)
return
redirect
(
self
.
_get_dir_url
(
'.index'
,
path
))
return
redirect
(
self
.
_get_dir_url
(
'.index'
,
path
))
except
Exception
as
ex
:
except
Exception
as
ex
:
flash
(
gettext
(
'Failed to save file:
%(error)
s'
,
error
=
ex
))
flash
(
gettext
(
'Failed to save file:
%(error)
s'
,
error
=
ex
)
,
'error'
)
return
self
.
render
(
self
.
upload_template
,
form
=
form
)
return
self
.
render
(
self
.
upload_template
,
form
=
form
)
...
@@ -562,18 +685,20 @@ class FileAdmin(BaseView, ActionsMixin):
...
@@ -562,18 +685,20 @@ class FileAdmin(BaseView, ActionsMixin):
return
redirect
(
dir_url
)
return
redirect
(
dir_url
)
if
not
self
.
is_accessible_path
(
path
):
if
not
self
.
is_accessible_path
(
path
):
flash
(
gettext
(
'Permission denied.'
))
flash
(
gettext
(
'Permission denied.'
)
,
'error'
)
return
redirect
(
self
.
_get_dir_url
(
'.index'
))
return
redirect
(
self
.
_get_dir_url
(
'.index'
))
form
=
NameForm
(
helpers
.
get_form_data
()
)
form
=
self
.
name_form
(
)
if
helpers
.
validate_form_on_submit
(
form
):
if
self
.
validate_form
(
form
):
try
:
try
:
os
.
mkdir
(
op
.
join
(
directory
,
form
.
name
.
data
))
os
.
mkdir
(
op
.
join
(
directory
,
form
.
name
.
data
))
self
.
on_mkdir
(
directory
,
form
.
name
.
data
)
self
.
on_mkdir
(
directory
,
form
.
name
.
data
)
return
redirect
(
dir_url
)
return
redirect
(
dir_url
)
except
Exception
as
ex
:
except
Exception
as
ex
:
flash
(
gettext
(
'Failed to create directory:
%(error)
s'
,
error
=
ex
),
'error'
)
flash
(
gettext
(
'Failed to create directory:
%(error)
s'
,
error
=
ex
),
'error'
)
else
:
helpers
.
flash_errors
(
form
,
message
=
'Failed to create directory:
%(error)
s'
)
return
self
.
render
(
self
.
mkdir_template
,
return
self
.
render
(
self
.
mkdir_template
,
form
=
form
,
form
=
form
,
...
@@ -584,42 +709,46 @@ class FileAdmin(BaseView, ActionsMixin):
...
@@ -584,42 +709,46 @@ class FileAdmin(BaseView, ActionsMixin):
"""
"""
Delete view method
Delete view method
"""
"""
path
=
request
.
form
.
get
(
'path'
)
form
=
self
.
delete_form
()
if
not
path
:
return
redirect
(
self
.
get_url
(
'.index'
))
# Get path and verify if it is valid
path
=
form
.
path
.
data
base_path
,
full_path
,
path
=
self
.
_normalize_path
(
path
)
if
path
:
return_url
=
self
.
_get_dir_url
(
'.index'
,
op
.
dirname
(
path
))
else
:
return_url
=
self
.
get_url
(
'.index'
)
return_url
=
self
.
_get_dir_url
(
'.index'
,
op
.
dirname
(
path
))
if
self
.
validate_form
(
form
):
# Get path and verify if it is valid
base_path
,
full_path
,
path
=
self
.
_normalize_path
(
path
)
if
not
self
.
can_delete
:
if
not
self
.
can_delete
:
flash
(
gettext
(
'Deletion is disabled.'
)
)
flash
(
gettext
(
'Deletion is disabled.'
),
'error'
)
return
redirect
(
return_url
)
return
redirect
(
return_url
)
if
not
self
.
is_accessible_path
(
path
):
if
not
self
.
is_accessible_path
(
path
):
flash
(
gettext
(
'Permission denied.'
)
)
flash
(
gettext
(
'Permission denied.'
),
'error'
)
return
redirect
(
self
.
_get_dir_url
(
'.index'
))
return
redirect
(
self
.
_get_dir_url
(
'.index'
))
if
op
.
isdir
(
full_path
):
if
op
.
isdir
(
full_path
):
if
not
self
.
can_delete_dirs
:
if
not
self
.
can_delete_dirs
:
flash
(
gettext
(
'Directory deletion is disabled.'
)
)
flash
(
gettext
(
'Directory deletion is disabled.'
),
'error'
)
return
redirect
(
return_url
)
return
redirect
(
return_url
)
try
:
try
:
shutil
.
rmtree
(
full_path
)
shutil
.
rmtree
(
full_path
)
self
.
on_directory_delete
(
full_path
,
path
)
self
.
on_directory_delete
(
full_path
,
path
)
flash
(
gettext
(
'Directory "
%(path)
s" was successfully deleted.'
,
path
=
path
))
flash
(
gettext
(
'Directory "
%(path)
s" was successfully deleted.'
,
path
=
path
))
except
Exception
as
ex
:
except
Exception
as
ex
:
flash
(
gettext
(
'Failed to delete directory:
%(error)
s'
,
error
=
ex
),
'error'
)
flash
(
gettext
(
'Failed to delete directory:
%(error)
s'
,
error
=
ex
),
'error'
)
else
:
try
:
os
.
remove
(
full_path
)
self
.
on_file_delete
(
full_path
,
path
)
flash
(
gettext
(
'File "
%(name)
s" was successfully deleted.'
,
name
=
path
))
except
Exception
as
ex
:
flash
(
gettext
(
'Failed to delete file:
%(name)
s'
,
name
=
ex
),
'error'
)
else
:
else
:
try
:
helpers
.
flash_errors
(
form
,
message
=
'Failed to delete file.
%(error)
s'
)
os
.
remove
(
full_path
)
self
.
on_file_delete
(
full_path
,
path
)
flash
(
gettext
(
'File "
%(name)
s" was successfully deleted.'
,
name
=
path
))
except
Exception
as
ex
:
flash
(
gettext
(
'Failed to delete file:
%(name)
s'
,
name
=
ex
),
'error'
)
return
redirect
(
return_url
)
return
redirect
(
return_url
)
...
@@ -628,29 +757,29 @@ class FileAdmin(BaseView, ActionsMixin):
...
@@ -628,29 +757,29 @@ class FileAdmin(BaseView, ActionsMixin):
"""
"""
Rename view method
Rename view method
"""
"""
path
=
request
.
args
.
get
(
'path'
)
form
=
self
.
name_form
()
if
not
path
:
return
redirect
(
self
.
get_url
(
'.index'
))
base_path
,
full_path
,
path
=
self
.
_normalize_path
(
path
)
path
=
form
.
path
.
data
if
path
:
base_path
,
full_path
,
path
=
self
.
_normalize_path
(
path
)
return_url
=
self
.
_get_dir_url
(
'.index'
,
op
.
dirname
(
path
))
return_url
=
self
.
_get_dir_url
(
'.index'
,
op
.
dirname
(
path
))
else
:
return
redirect
(
self
.
get_url
(
'.index'
))
if
not
self
.
can_rename
:
if
not
self
.
can_rename
:
flash
(
gettext
(
'Renaming is disabled.'
))
flash
(
gettext
(
'Renaming is disabled.'
)
,
'error'
)
return
redirect
(
return_url
)
return
redirect
(
return_url
)
if
not
self
.
is_accessible_path
(
path
):
if
not
self
.
is_accessible_path
(
path
):
flash
(
gettext
(
'Permission denied.'
))
flash
(
gettext
(
'Permission denied.'
)
,
'error'
)
return
redirect
(
self
.
_get_dir_url
(
'.index'
))
return
redirect
(
self
.
_get_dir_url
(
'.index'
))
if
not
op
.
exists
(
full_path
):
if
not
op
.
exists
(
full_path
):
flash
(
gettext
(
'Path does not exist.'
))
flash
(
gettext
(
'Path does not exist.'
)
,
'error'
)
return
redirect
(
return_url
)
return
redirect
(
return_url
)
form
=
NameForm
(
helpers
.
get_form_data
(),
name
=
op
.
basename
(
path
))
if
self
.
validate_form
(
form
):
if
helpers
.
validate_form_on_submit
(
form
):
try
:
try
:
dir_base
=
op
.
dirname
(
full_path
)
dir_base
=
op
.
dirname
(
full_path
)
filename
=
secure_filename
(
form
.
name
.
data
)
filename
=
secure_filename
(
form
.
name
.
data
)
...
@@ -664,6 +793,8 @@ class FileAdmin(BaseView, ActionsMixin):
...
@@ -664,6 +793,8 @@ class FileAdmin(BaseView, ActionsMixin):
flash
(
gettext
(
'Failed to rename:
%(error)
s'
,
error
=
ex
),
'error'
)
flash
(
gettext
(
'Failed to rename:
%(error)
s'
,
error
=
ex
),
'error'
)
return
redirect
(
return_url
)
return
redirect
(
return_url
)
else
:
helpers
.
flash_errors
(
form
,
message
=
'Failed to rename:
%(error)
s'
)
return
self
.
render
(
self
.
rename_template
,
return
self
.
render
(
self
.
rename_template
,
form
=
form
,
form
=
form
,
...
@@ -690,16 +821,16 @@ class FileAdmin(BaseView, ActionsMixin):
...
@@ -690,16 +821,16 @@ class FileAdmin(BaseView, ActionsMixin):
base_path
,
full_path
,
path
=
self
.
_normalize_path
(
path
)
base_path
,
full_path
,
path
=
self
.
_normalize_path
(
path
)
if
not
self
.
is_accessible_path
(
path
)
or
not
self
.
is_file_editable
(
path
):
if
not
self
.
is_accessible_path
(
path
)
or
not
self
.
is_file_editable
(
path
):
flash
(
gettext
(
'Permission denied.'
))
flash
(
gettext
(
'Permission denied.'
)
,
'error'
)
return
redirect
(
self
.
_get_dir_url
(
'.index'
))
return
redirect
(
self
.
_get_dir_url
(
'.index'
))
dir_url
=
self
.
_get_dir_url
(
'.index'
,
os
.
path
.
dirname
(
path
))
dir_url
=
self
.
_get_dir_url
(
'.index'
,
os
.
path
.
dirname
(
path
))
next_url
=
next_url
or
dir_url
next_url
=
next_url
or
dir_url
form
=
EditForm
(
helpers
.
get_form_data
()
)
form
=
self
.
edit_form
(
)
error
=
False
error
=
False
if
helpers
.
validate_form_on_submit
(
form
):
if
self
.
validate_form
(
form
):
form
.
process
(
request
.
form
,
content
=
''
)
form
.
process
(
request
.
form
,
content
=
''
)
if
form
.
validate
():
if
form
.
validate
():
try
:
try
:
...
@@ -713,8 +844,10 @@ class FileAdmin(BaseView, ActionsMixin):
...
@@ -713,8 +844,10 @@ class FileAdmin(BaseView, ActionsMixin):
flash
(
gettext
(
"Changes to
%(name)
s saved successfully."
,
name
=
path
))
flash
(
gettext
(
"Changes to
%(name)
s saved successfully."
,
name
=
path
))
return
redirect
(
next_url
)
return
redirect
(
next_url
)
else
:
else
:
helpers
.
flash_errors
(
form
,
message
=
'Failed to edit file.
%(error)
s'
)
try
:
try
:
with
open
(
full_path
,
'r'
)
as
f
:
with
open
(
full_path
,
'r
b
'
)
as
f
:
content
=
f
.
read
()
content
=
f
.
read
()
except
IOError
:
except
IOError
:
flash
(
gettext
(
"Error reading
%(name)
s."
,
name
=
path
),
'error'
)
flash
(
gettext
(
"Error reading
%(name)
s."
,
name
=
path
),
'error'
)
...
...
flask_admin/helpers.py
View file @
1b36345c
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
):
...
...
flask_admin/model/base.py
View file @
1b36345c
...
@@ -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
)
...
...
flask_admin/templates/bootstrap2/admin/file/list.html
View file @
1b36345c
...
@@ -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>
...
...
flask_admin/templates/bootstrap3/admin/file/list.html
View file @
1b36345c
...
@@ -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>
...
...
flask_admin/tests/fileadmin/test_fileadmin.py
View file @
1b36345c
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
=
My
FileAdmin
(
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'
))
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment