Mercurial > wikked
changeset 437:3985b3d72353
web: Add basic ability to upload files.
author | Ludovic Chabant <ludovic@chabant.com> |
---|---|
date | Fri, 14 Apr 2017 21:27:46 -0700 |
parents | 8b777637da75 |
children | b259b440a9bd |
files | wikked/templates/error-unauthorized-upload.html wikked/templates/upload-file.html wikked/views/__init__.py wikked/views/edit.py wikked/views/history.py wikked/views/read.py wikked/web.py wikked/webimpl/edit.py |
diffstat | 8 files changed, 160 insertions(+), 9 deletions(-) [+] |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/wikked/templates/error-unauthorized-upload.html Fri Apr 14 21:27:46 2017 -0700 @@ -0,0 +1,13 @@ +{% extends 'index.html' %} +{% block content %} +<article> + <header> + <h1>You're not authorized to upload files</h1> + </header> + <section> + <p>It seems you can't upload files. + Please <a href="/login">log into an account</a> that has upload permissions.</p> + </section> +</article> +{% endblock %} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/wikked/templates/upload-file.html Fri Apr 14 21:27:46 2017 -0700 @@ -0,0 +1,54 @@ +{% extends 'index.html' %} +{% block content %} +<article> + <header> + <h1>Upload File</h1> + </header> + <section> + {% if success %} + <h2>File Uploaded!</h2> + <section> + <p>The file is accessible with the following syntax: <code>{%raw%}{{file: {%endraw%}{{success.example}}{%raw%}}}{%endraw%}</code></p> + {% if success.is_page_specific %} + <p><strong>Note</strong>: this syntax is valid only on <a href="{{get_read_url(for_page)}}">the current page</a>.</p> + {% endif %} + </section> + {% else %} + <form id="file-upload" class="pure-form" action="{{post_back}}" method="POST" enctype="multipart/form-data"> + <fieldset> + {%if error%} + <section class="editing-error alert alert-danger"> + <p><strong>Error:</strong> <span class="upload-error-message">{{error}}</span></p> + </section> + {%endif%} + <section class="editing-input"> + <div class="pure-control-group"> + <label for="file">Choose a file:</label> + <input type="file" name="file" /> + </div> + <div class="pure-control-group pure-control-addon"> + <label for="title" class="">Rename File</label> + <input type="text" name="path" placeholder="Name..." value="{{path}}"></input> + </div> + <div class="pure-form-help"> + <span for="title">You can specify a different name and/or path: <code>Folder/Filename</code>.</span> + </div> + {% if for_page %} + <div class="pure-control-group"> + <label for="is_page_specific">Page-specific file</label> + <input type="checkbox" name="is_page_specific" value="true"></input> + </div> + <div class="pure-form-help"> + <span for="title">Check this to upload this file under: <code>{{for_page}}</code></span> + </div> + {% endif %} + <div class="pure-control-group"> + <button name="do-upload" type="submit" class="pure-button pure-button-primary"><span class="fa fa-upload"></span> Upload</button> + </div> + </section> + </fieldset> + </form> + {% endif %} + </section> +</article> +{% endblock %}
--- a/wikked/views/__init__.py Thu Mar 30 20:14:59 2017 -0700 +++ b/wikked/views/__init__.py Fri Apr 14 21:27:46 2017 -0700 @@ -82,7 +82,7 @@ def add_navigation_data( url, data, *, home=True, new_page=True, - read=False, edit=False, history=False, inlinks=False, + read=False, edit=False, history=False, inlinks=False, upload=False, raw_url=None, extras=None, footers=None): if url is not None: url = url.lstrip('/') @@ -112,6 +112,13 @@ 'icon': 'link' }) + if upload: + nav['extras'].append({ + 'title': "Upload File", + 'url': url_for('upload_file', p=url), + 'icon': 'upload' + }) + if raw_url: nav['footers'].append({ 'title': "RAW",
--- a/wikked/views/edit.py Thu Mar 30 20:14:59 2017 -0700 +++ b/wikked/views/edit.py Fri Apr 14 21:27:46 2017 -0700 @@ -6,7 +6,7 @@ from wikked.web import app, get_wiki from wikked.webimpl import url_from_viewarg from wikked.webimpl.edit import ( - get_edit_page, do_edit_page, preview_edited_page) + get_edit_page, do_edit_page, preview_edited_page, do_upload_file) @app.route('/create/') @@ -63,7 +63,7 @@ add_auth_data(data) add_navigation_data( url, data, - read=True, history=True, inlinks=True, + read=True, history=True, inlinks=True, upload=True, raw_url=url_for('api_read_page', url=url.lstrip('/'))) return render_template('edit-page.html', **data) @@ -89,3 +89,37 @@ else: abort(400) + + +@app.route('/upload', methods=['GET', 'POST']) +@errorhandling_ui2('error-unauthorized-upload.html') +def upload_file(): + p = request.args.get('p') + data = { + 'post_back': url_for('upload_file', p=p), + 'for_page': p + } + add_auth_data(data) + add_navigation_data(p, data) + + if request.method == 'GET': + return render_template('upload-file.html', **data) + + if request.method == 'POST': + wiki = get_wiki() + user = current_user.get_id() or request.remote_addr + + for_url = None + is_page_specific = (request.form.get('is_page_specific') == 'true') + if is_page_specific: + for_url = p + + res = do_upload_file( + wiki, user, request.files.get('file'), + for_url=for_url) + + data['success'] = { + 'example': res['example'], + 'is_page_specific': is_page_specific} + + return render_template('upload-file.html', **data)
--- a/wikked/views/history.py Thu Mar 30 20:14:59 2017 -0700 +++ b/wikked/views/history.py Fri Apr 14 21:27:46 2017 -0700 @@ -38,7 +38,7 @@ add_auth_data(data) add_navigation_data( url, data, - read=True, edit=True, inlinks=True, + read=True, edit=True, inlinks=True, upload=True, raw_url='/api/history/' + url.lstrip('/')) return render_template('history-page.html', **data) @@ -104,4 +104,3 @@ add_navigation_data( '', data) return render_template('diff-rev.html', **data) -
--- a/wikked/views/read.py Thu Mar 30 20:14:59 2017 -0700 +++ b/wikked/views/read.py Fri Apr 14 21:27:46 2017 -0700 @@ -56,7 +56,7 @@ add_auth_data(data) add_navigation_data( url, data, - edit=True, history=True, inlinks=True, + edit=True, history=True, inlinks=True, upload=True, raw_url='/api/raw/' + url.lstrip('/')) return render_template(tpl_name, **data) @@ -94,7 +94,6 @@ add_auth_data(data) add_navigation_data( url, data, - read=True, edit=True, history=True, + read=True, edit=True, history=True, upload=True, raw_url='/api/inlinks/' + url.lstrip('/')) return render_template('inlinks-page.html', **data) -
--- a/wikked/web.py Thu Mar 30 20:14:59 2017 -0700 +++ b/wikked/web.py Fri Apr 14 21:27:46 2017 -0700 @@ -57,10 +57,12 @@ # Make the app serve static content and wiki assets in DEBUG mode. +app.config['WIKI_ROOT'] = wiki_root +app.config['WIKI_FILES_DIR'] = os.path.join(wiki_root, '_files') if app.config['WIKI_DEV_ASSETS'] or app.config['WIKI_SERVE_FILES']: app.wsgi_app = SharedDataMiddleware( app.wsgi_app, - {'/files': os.path.join(wiki_root, '_files')}) + {'/files': app.config['WIKI_FILES_DIR']}) # In DEBUG mode, also serve raw assets instead of static ones.
--- a/wikked/webimpl/edit.py Thu Mar 30 20:14:59 2017 -0700 +++ b/wikked/webimpl/edit.py Fri Apr 14 21:27:46 2017 -0700 @@ -1,5 +1,7 @@ +import os.path import logging import urllib.parse +from werkzeug.utils import secure_filename from wikked.page import Page, PageData from wikked.formatter import PageFormatter, FormattingContext from wikked.resolver import PageResolver @@ -102,3 +104,44 @@ resolver = PageResolver(dummy) dummy._setExtendedData(resolver.run()) return dummy.text + + +def do_upload_file(wiki, user, reqfile, for_url=None, submit=True): + if not reqfile: + raise Exception("No file was specified.") + if not reqfile.filename: + raise Exception("No file name was specified.") + + # TODO: check permissions for the user. + + filename = secure_filename(reqfile.filename) + + files_dir = os.path.join(wiki.root, '_files') + upload_dir = files_dir + if for_url: + upload_dir = os.path.join(wiki.root, for_url) + + path = os.path.join(upload_dir, filename) + path = os.path.normpath(path) + if not path.startswith(wiki.root): + raise Exception("Don't try anything weird, please.") + + # Save to disk. + os.makedirs(os.path.dirname(path), exist_ok=True) + reqfile.save(path) + + # Commit the file to the source-control. + if submit: + commit_meta = { + 'author': user, + 'message': "Uploaded '%s'." % filename} + wiki.scm.commit([path], commit_meta) + + if for_url: + example = './%s' % filename + else: + example = os.path.relpath(path, files_dir) + result = { + 'example': example + } + return result