# HG changeset patch # User Ludovic Chabant # Date 1492230466 25200 # Node ID 3985b3d723539ff9f8da382ed875876dc599cade # Parent 8b777637da75847b0b6603dfb59205ef8f4354b9 web: Add basic ability to upload files. diff -r 8b777637da75 -r 3985b3d72353 wikked/templates/error-unauthorized-upload.html --- /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 %} +
+
+

You're not authorized to upload files

+
+
+

It seems you can't upload files. + Please log into an account that has upload permissions.

+
+
+{% endblock %} + diff -r 8b777637da75 -r 3985b3d72353 wikked/templates/upload-file.html --- /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 %} +
+
+

Upload File

+
+
+ {% if success %} +

File Uploaded!

+
+

The file is accessible with the following syntax: {%raw%}{{file: {%endraw%}{{success.example}}{%raw%}}}{%endraw%}

+ {% if success.is_page_specific %} +

Note: this syntax is valid only on the current page.

+ {% endif %} +
+ {% else %} +
+
+ {%if error%} +
+

Error: {{error}}

+
+ {%endif%} +
+
+ + +
+
+ + +
+
+ You can specify a different name and/or path: Folder/Filename. +
+ {% if for_page %} +
+ + +
+
+ Check this to upload this file under: {{for_page}} +
+ {% endif %} +
+ +
+
+
+
+ {% endif %} +
+
+{% endblock %} diff -r 8b777637da75 -r 3985b3d72353 wikked/views/__init__.py --- 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", diff -r 8b777637da75 -r 3985b3d72353 wikked/views/edit.py --- 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) diff -r 8b777637da75 -r 3985b3d72353 wikked/views/history.py --- 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) - diff -r 8b777637da75 -r 3985b3d72353 wikked/views/read.py --- 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) - diff -r 8b777637da75 -r 3985b3d72353 wikked/web.py --- 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. diff -r 8b777637da75 -r 3985b3d72353 wikked/webimpl/edit.py --- 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