Commit 19066d73 authored by jadkik's avatar jadkik

Added file editor

parent c19ace83
...@@ -55,6 +55,11 @@ class UploadForm(form.BaseForm): ...@@ -55,6 +55,11 @@ class UploadForm(form.BaseForm):
raise wtf.ValidationError(gettext('Invalid file type.')) raise wtf.ValidationError(gettext('Invalid file type.'))
class EditForm(form.BaseForm):
content = wtf.TextAreaField(lazy_gettext('Content'),
[wtf.validators.required()])
class FileAdmin(BaseView, ActionsMixin): class FileAdmin(BaseView, ActionsMixin):
""" """
Simple file-management interface. Simple file-management interface.
...@@ -111,6 +116,16 @@ class FileAdmin(BaseView, ActionsMixin): ...@@ -111,6 +116,16 @@ class FileAdmin(BaseView, ActionsMixin):
allowed_extensions = ('swf', 'jpg', 'gif', 'png') allowed_extensions = ('swf', 'jpg', 'gif', 'png')
""" """
editable_extensions = None
"""
List of editable extensions, in lower case.
Example::
class MyAdmin(FileAdmin):
editable_extensions = ('md', 'html', 'txt')
"""
list_template = 'admin/file/list.html' list_template = 'admin/file/list.html'
""" """
File list template File list template
...@@ -131,6 +146,11 @@ class FileAdmin(BaseView, ActionsMixin): ...@@ -131,6 +146,11 @@ class FileAdmin(BaseView, ActionsMixin):
Rename template Rename template
""" """
edit_template = 'admin/file/edit.html'
"""
Edit template
"""
def __init__(self, base_path, base_url, def __init__(self, base_path, base_url,
name=None, category=None, endpoint=None, url=None, name=None, category=None, endpoint=None, url=None,
verify_path=True): verify_path=True):
...@@ -165,6 +185,11 @@ class FileAdmin(BaseView, ActionsMixin): ...@@ -165,6 +185,11 @@ class FileAdmin(BaseView, ActionsMixin):
and not isinstance(self.allowed_extensions, set)): and not isinstance(self.allowed_extensions, set)):
self.allowed_extensions = set(self.allowed_extensions) self.allowed_extensions = set(self.allowed_extensions)
# Convert editable_extensions to set for quick validation
if (self.editable_extensions
and not isinstance(self.editable_extensions, set)):
self.editable_extensions = set(self.editable_extensions)
# Check if path exists # Check if path exists
if not op.exists(base_path): if not op.exists(base_path):
raise IOError('FileAdmin path "%s" does not exist or is not accessible' % base_path) raise IOError('FileAdmin path "%s" does not exist or is not accessible' % base_path)
...@@ -215,6 +240,25 @@ class FileAdmin(BaseView, ActionsMixin): ...@@ -215,6 +240,25 @@ class FileAdmin(BaseView, ActionsMixin):
return True return True
def is_file_editable(self, filename):
"""
Verify if file can be edited.
Override to customize behavior.
:param filename:
Source file name
"""
ext = op.splitext(filename)[1].lower()
if ext.startswith('.'):
ext = ext[1:]
if self.editable_extensions and ext not in self.editable_extensions:
return False
return True
def is_in_folder(self, base_path, directory): def is_in_folder(self, base_path, directory):
""" """
Verify if `directory` is in `base_path` folder Verify if `directory` is in `base_path` folder
...@@ -265,8 +309,11 @@ class FileAdmin(BaseView, ActionsMixin): ...@@ -265,8 +309,11 @@ class FileAdmin(BaseView, ActionsMixin):
:param path: :param path:
Static file path Static file path
""" """
base_url = self.get_base_url() if self.is_file_editable(path):
return urlparse.urljoin(base_url, path) return url_for(".edit", path=path)
else:
base_url = self.get_base_url()
return urlparse.urljoin(base_url, path)
def _normalize_path(self, path): def _normalize_path(self, path):
""" """
...@@ -494,6 +541,44 @@ class FileAdmin(BaseView, ActionsMixin): ...@@ -494,6 +541,44 @@ class FileAdmin(BaseView, ActionsMixin):
name=op.basename(path), name=op.basename(path),
dir_url=return_url) dir_url=return_url)
@expose('/edit/', methods=('GET', 'POST'))
def edit(self):
"""
Edit view method
"""
path = request.args.get('path')
next_url = None
if not path:
return redirect(url_for('.index'))
path = path.split(':')
if len(path) > 1:
next_url = url_for('.edit', path=':'.join(path[1:]))
path = path[0]
base_path, full_path, path = self._normalize_path(path)
dir_url = self._get_dir_url('.index', os.path.dirname(path))
next_url = next_url or dir_url
form = EditForm()
if request.method == 'POST':
form.process(request.form, content='')
if form.validate():
try:
with open(full_path, 'w') as f:
f.write(request.form['content'])
except IOError:
flash(lazy_gettext("Error saving changes to file!"),
'error')
else:
flash(lazy_gettext("Changes saved successfully!"))
return redirect(next_url)
else:
with open(full_path, 'r') as f:
form.content.data = f.read()
return self.render(self.edit_template, dir_url=dir_url, form=form,
path=path)
@expose('/action/', methods=('POST',)) @expose('/action/', methods=('POST',))
def action_view(self): def action_view(self):
return self.handle_action() return self.handle_action()
...@@ -511,3 +596,7 @@ class FileAdmin(BaseView, ActionsMixin): ...@@ -511,3 +596,7 @@ class FileAdmin(BaseView, ActionsMixin):
flash(gettext('File "%(name)s" was successfully deleted.', name=path)) flash(gettext('File "%(name)s" was successfully deleted.', name=path))
except Exception, ex: except Exception, ex:
flash(gettext('Failed to delete file: %(name)s', name=ex), 'error') flash(gettext('Failed to delete file: %(name)s', name=ex), 'error')
@action('edit', lazy_gettext('Edit'))
def action_edit(self, items):
return redirect(url_for('.edit', path=':'.join(items)))
{% extends 'admin/master.html' %}
{% import 'admin/lib.html' as lib with context %}
{% block body %}
<h3>{{ _gettext('You are editing %(path)s', path=path) }}</h3>
{{ lib.render_form(form, dir_url) }}
{% endblock %}
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