Mercurial > piecrust2
changeset 935:7ecb946bfafd
admin: Lots of fixes for running the admin panel in a WSGI server.
- Use new source APIs in the dashboard to open WIP files.
- Fixed broken/outdated code in some views.
- Fixed cases when Flask is not running at the root URL by using the
`SCRIPT_NAME` variable somewhat more properly.
author | Ludovic Chabant <ludovic@chabant.com> |
---|---|
date | Wed, 04 Oct 2017 09:15:16 -0700 |
parents | 98430e7143d2 |
children | abc52a6262a1 |
files | piecrust/admin/blueprint.py piecrust/admin/settings.py piecrust/admin/siteinfo.py piecrust/admin/views/__init__.py piecrust/admin/views/create.py piecrust/admin/views/dashboard.py piecrust/admin/views/edit.py piecrust/admin/views/preview.py piecrust/admin/views/sources.py piecrust/admin/web.py piecrust/serving/wrappers.py piecrust/wsgiutil/__init__.py |
diffstat | 12 files changed, 183 insertions(+), 123 deletions(-) [+] |
line wrap: on
line diff
--- a/piecrust/admin/blueprint.py Wed Oct 04 09:11:58 2017 -0700 +++ b/piecrust/admin/blueprint.py Wed Oct 04 09:15:16 2017 -0700 @@ -18,22 +18,24 @@ def load_user(user_id): - admin_id = g.config.get('security/username') + admin_id = current_app.config.get('USERNAME') if admin_id == user_id: - admin_pwd = g.config.get('security/password') + admin_pwd = current_app.config.get('PASSWORD') return User(admin_id, admin_pwd) return None -login_manager = LoginManager() -login_manager.login_view = 'FoodTruck.login' -login_manager.user_loader(load_user) +def record_login_manager(state): + login_manager = LoginManager() + login_manager.login_view = 'FoodTruck.login' + login_manager.user_loader(load_user) - -def record_login_manager(state): if state.app.config['SECRET_KEY'] == 'temp-key': def _handler(): - raise Exception("No secret key has been set!") + from flask import render_template + return render_template( + 'error.html', + error="No secret key has been set!") logger.debug("No secret key found, disabling website login.") login_manager.unauthorized_handler(_handler) @@ -91,9 +93,8 @@ @foodtruck_bp.before_request def _setup_foodtruck_globals(): def _get_site(): - root_dir = current_app.config['FOODTRUCK_ROOT'] - url_prefix = current_app.config['FOODTRUCK_URL_PREFIX'] - return SiteInfo(root_dir, url_prefix, debug=current_app.debug) + root_dir = current_app.config['FOODTRUCK_ROOT_DIR'] + return SiteInfo(root_dir, debug=current_app.debug) g.site = LazySomething(_get_site) @@ -131,5 +132,3 @@ import piecrust.admin.views.preview # NOQA import piecrust.admin.views.publish # NOQA import piecrust.admin.views.sources # NOQA - -
--- a/piecrust/admin/settings.py Wed Oct 04 09:11:58 2017 -0700 +++ b/piecrust/admin/settings.py Wed Oct 04 09:15:16 2017 -0700 @@ -1,2 +1,1 @@ -FOODTRUCK_URL_PREFIX = '/pc-admin' - +FOODTRUCK_ROOT_URL = ''
--- a/piecrust/admin/siteinfo.py Wed Oct 04 09:11:58 2017 -0700 +++ b/piecrust/admin/siteinfo.py Wed Oct 04 09:15:16 2017 -0700 @@ -5,6 +5,7 @@ import logging import threading import subprocess +from flask import request from piecrust.app import PieCrustFactory @@ -20,15 +21,21 @@ class SiteInfo: - def __init__(self, root_dir, url_prefix, *, debug=False): + def __init__(self, root_dir, *, debug=False): self.root_dir = root_dir - self.url_prefix = url_prefix self.debug = debug self._piecrust_factory = None self._piecrust_app = None self._scm = None @property + def url_prefix(self): + return request.script_root + + def make_url(self, rel_url): + return self.url_prefix + rel_url + + @property def piecrust_factory(self): if self._piecrust_factory is None: self._piecrust_factory = PieCrustFactory( @@ -36,10 +43,10 @@ cache_key='admin', debug=self.debug, config_values=[ - ('site/root', '%s/preview/' % self.url_prefix), - ('site/asset_url_format', - self.url_prefix + '/preview/_asset/%path%') - ]) + ('site/root', self.make_url('/preview/')), + ('site/asset_url_format', self.make_url( + '/preview/_asset/%path%'))] + ) return self._piecrust_factory @property
--- a/piecrust/admin/views/__init__.py Wed Oct 04 09:11:58 2017 -0700 +++ b/piecrust/admin/views/__init__.py Wed Oct 04 09:15:16 2017 -0700 @@ -1,4 +1,4 @@ -from flask import render_template, current_app +from flask import current_app, render_template, request from flask.views import View from .menu import get_menu_context @@ -31,4 +31,7 @@ def with_base_data(context=None): if context is None: context = {} - context['base_url'] = current_app.config['FOODTRUCK_URL_PREFIX'] + + script_root = request.script_root or '' + root_url = current_app.config.get('FOODTRUCK_ROOT_URL') or '' + context['base_url'] = script_root + root_url
--- a/piecrust/admin/views/create.py Wed Oct 04 09:11:58 2017 -0700 +++ b/piecrust/admin/views/create.py Wed Oct 04 09:15:16 2017 -0700 @@ -4,6 +4,7 @@ from flask.ext.login import login_required from piecrust.page import Page from piecrust.sources.interfaces import IInteractiveSource +from piecrust.uriutil import split_uri from ..blueprint import foodtruck_bp from ..views import with_menu_context @@ -72,5 +73,6 @@ page = Page(source, content_item) uri = page.getUri() logger.debug("Redirecting to: %s" % uri) - return redirect(url_for('.edit_page', uri=uri)) + _, rel_url = split_uri(page.app, uri) + return redirect(url_for('.edit_page', url=rel_url))
--- a/piecrust/admin/views/dashboard.py Wed Oct 04 09:11:58 2017 -0700 +++ b/piecrust/admin/views/dashboard.py Wed Oct 04 09:15:16 2017 -0700 @@ -10,7 +10,7 @@ from piecrust.uriutil import split_uri from ..textutil import text_preview from ..blueprint import foodtruck_bp, load_user -from ..views import with_menu_context +from ..views import with_menu_context, with_base_data logger = logging.getLogger(__name__) @@ -87,7 +87,8 @@ if source is None: return None - content_item = source.findContentFromPath(path) + full_path = os.path.join(pcapp.root_dir, path) + content_item = source.findContentFromPath(full_path) if content_item is None: return None @@ -103,7 +104,7 @@ return { 'title': page.config.get('title'), 'slug': slug, - 'url': url_for('.edit_page', uri=slug), + 'url': url_for('.edit_page', url=slug), 'text': extract } @@ -126,6 +127,7 @@ "User '%s' doesn't exist or password is incorrect." % username) + with_base_data(data) return render_template('login.html', **data)
--- a/piecrust/admin/views/edit.py Wed Oct 04 09:11:58 2017 -0700 +++ b/piecrust/admin/views/edit.py Wed Oct 04 09:15:16 2017 -0700 @@ -13,85 +13,63 @@ logger = logging.getLogger(__name__) -@foodtruck_bp.route('/edit/', defaults={'uri': ''}, methods=['GET', 'POST']) -@foodtruck_bp.route('/edit/<path:uri>', methods=['GET', 'POST']) +@foodtruck_bp.route('/edit/', defaults={'slug': ''}, methods=['GET', 'POST']) +@foodtruck_bp.route('/edit/<path:slug>', methods=['GET', 'POST']) @login_required -def edit_page(uri): +def edit_page(slug): site = g.site - pcapp = site.piecrust_app - rp = get_requested_page(pcapp, '%s/preview/%s' % (site.url_prefix, uri)) - page = rp.page + site_app = site.piecrust_app + rp = get_requested_page(site_app, + '/site/%s/%s' % (g.sites.current_site, slug)) + page = rp.qualified_page if page is None: abort(404) if request.method == 'POST': - return _submit_page_form(page, uri) + page_text = request.form['page_text'] + if request.form['is_dos_nl'] == '0': + page_text = page_text.replace('\r\n', '\n') + + if 'do_save' in request.form or 'do_save_and_commit' in request.form: + logger.debug("Writing page: %s" % page.path) + with open(page.path, 'w', encoding='utf8', newline='') as fp: + fp.write(page_text) + flash("%s was saved." % os.path.relpath( + page.path, site_app.root_dir)) - return _edit_page_form(page, uri) + if 'do_save_and_commit' in request.form: + message = request.form.get('commit_msg') + if not message: + message = "Edit %s" % os.path.relpath( + page.path, site_app.root_dir) + if site.scm: + commit_paths = [page.path] + assets_dir = os.path.splitext(page.path)[0] + '-assets' + if os.path.isdir(assets_dir): + commit_paths += list(os.listdir(assets_dir)) + site.scm.commit(commit_paths, message) + + if 'do_save' in request.form or 'do_save_and_commit' in request.form: + return _edit_page_form(page, slug, site.name) + + abort(400) + + return _edit_page_form(page, slug, site.name) -def _edit_page_form(page, uri): - data = {} - data['is_new_page'] = False - data['url_postback'] = url_for('.edit_page', uri=uri) - data['url_upload_asset'] = url_for('.upload_page_asset', uri=uri) - data['url_preview'] = page.getUri() - data['url_cancel'] = url_for( - '.list_source', source_name=page.source.name) - - with page.source.openItem(page.content_item, 'r') as fp: - data['page_text'] = fp.read() - data['is_dos_nl'] = "1" if '\r\n' in data['page_text'] else "0" - - assetor = Assetor(page) - assets_data = [] - for n in assetor._getAssetNames(): - assets_data.append({'name': n, 'url': assetor[n]}) - data['assets'] = assets_data - - data['has_scm'] = (g.site.scm is not None) - - with_menu_context(data) - return render_template('edit_page.html', **data) - - -def _submit_page_form(page, uri): - page_text = request.form['page_text'] - if request.form['is_dos_nl'] == '0': - page_text = page_text.replace('\r\n', '\n') - - if 'do_save' in request.form or 'do_save_and_commit' in request.form: - logger.debug("Writing page: %s" % page.content_spec) - with page.source.openItem(page.content_item, 'w') as fp: - fp.write(page_text) - flash("%s was saved." % page.content_spec) - - scm = g.site.scm - if 'do_save_and_commit' in request.form and scm is not None: - message = request.form.get('commit_msg') - if not message: - message = "Edit %s" % page.content_spec - scm.commit([page.content_spec], message) - - if 'do_save' in request.form or 'do_save_and_commit' in request.form: - return _edit_page_form(page, uri) - - abort(400) - - -@foodtruck_bp.route('/upload/<path:uri>', methods=['POST']) -def upload_page_asset(uri): +@foodtruck_bp.route('/upload/<path:slug>', methods=['POST']) +def upload_page_asset(slug): if 'ft-asset-file' not in request.files: - return redirect(url_for('.edit_page', uri=uri)) + return redirect(url_for('.edit_page', slug=slug)) asset_file = request.files['ft-asset-file'] if asset_file.filename == '': - return redirect(url_for('.edit_page', uri=uri)) + return redirect(url_for('.edit_page', slug=slug)) site = g.site - pcapp = site.piecrust_app - rp = get_requested_page(pcapp, - '/site/%s/%s' % (g.sites.current_site, uri)) + site_app = site.piecrust_app + rp = get_requested_page(site_app, + '/site/%s/%s' % (g.sites.current_site, slug)) page = rp.qualified_page if page is None: abort(404) @@ -109,4 +87,29 @@ asset_path = os.path.join(dirname, filename) logger.info("Uploading file to: %s" % asset_path) asset_file.save(asset_path) - return redirect(url_for('.edit_page', uri=uri)) + return redirect(url_for('.edit_page', slug=slug)) + + +def _edit_page_form(page, slug, sitename): + data = {} + data['is_new_page'] = False + data['url_postback'] = url_for('.edit_page', slug=slug) + data['url_upload_asset'] = url_for('.upload_page_asset', slug=slug) + data['url_preview'] = page.getUri() + data['url_cancel'] = url_for( + '.list_source', source_name=page.source.name) + with open(page.path, 'r', encoding='utf8', newline='') as fp: + data['page_text'] = fp.read() + data['is_dos_nl'] = "1" if '\r\n' in data['page_text'] else "0" + + page.app.env.base_asset_url_format = \ + page.app.config.get('site/root') + '_asset/%path%' + assetor = Assetor(page, 'blah') + assets_data = [] + for n in assetor.allNames(): + assets_data.append({'name': n, 'url': assetor[n]}) + data['assets'] = assets_data + + with_menu_context(data) + return render_template('edit_page.html', **data) +
--- a/piecrust/admin/views/preview.py Wed Oct 04 09:11:58 2017 -0700 +++ b/piecrust/admin/views/preview.py Wed Oct 04 09:15:16 2017 -0700 @@ -1,4 +1,4 @@ -from flask import current_app, g, make_response +from flask import g, make_response from flask.ext.login import login_required from piecrust.serving.server import PieCrustServer from ..blueprint import foodtruck_bp @@ -14,8 +14,6 @@ @login_required def preview_page(url): pcappfac = g.site.piecrust_factory - url_prefix = current_app.config['FOODTRUCK_URL_PREFIX'] - server = PieCrustServer(pcappfac, - root_url='%s/preview/' % url_prefix) + server = PieCrustServer(pcappfac, root_url=g.site.make_url('/preview/')) return make_response(server)
--- a/piecrust/admin/views/sources.py Wed Oct 04 09:11:58 2017 -0700 +++ b/piecrust/admin/views/sources.py Wed Oct 04 09:15:16 2017 -0700 @@ -27,7 +27,7 @@ 'tags': p.get('tags', []), 'category': p.get('category'), 'source': source_name, - 'url': url_for('.edit_page', uri=p['slug']) + 'url': url_for('.edit_page', url=p['rel_url']) } data['pages'].append(page_data)
--- a/piecrust/admin/web.py Wed Oct 04 09:11:58 2017 -0700 +++ b/piecrust/admin/web.py Wed Oct 04 09:15:16 2017 -0700 @@ -7,7 +7,7 @@ logger = logging.getLogger(__name__) -def create_foodtruck_app(extra_settings=None): +def create_foodtruck_app(extra_settings=None, url_prefix=None): from .blueprint import foodtruck_bp app = Flask(__name__.split('.')[0], static_folder=None) @@ -15,17 +15,12 @@ if extra_settings: app.config.update(extra_settings) - root_dir = app.config.setdefault('FOODTRUCK_ROOT', os.getcwd()) + root_dir = app.config.setdefault('FOODTRUCK_ROOT_DIR', os.getcwd()) app.config.from_pyfile(os.path.join(root_dir, 'admin_app.cfg'), silent=True) app.config.from_envvar('FOODTRUCK_SETTINGS', silent=True) - # Add a special route for the `.well-known` directory. - app.wsgi_app = SharedDataMiddleware( - app.wsgi_app, - {'/.well-known': os.path.join(root_dir, '.well-known')}) - # Setup logging/error handling. if app.config['DEBUG']: l = logging.getLogger() @@ -37,8 +32,32 @@ app.config['SECRET_KEY'] = 'temp-key' # Register extensions and blueprints. - bp_prefix = app.config['FOODTRUCK_URL_PREFIX'] - app.register_blueprint(foodtruck_bp, url_prefix=bp_prefix) + app.register_blueprint(foodtruck_bp, url_prefix=url_prefix) + + # --------------- + # Debugging stuff + @app.errorhandler(404) + def page_not_found(e): + from flask import request, url_for + output = [] + for rule in app.url_map.iter_rules(): + options = {} + for arg in rule.arguments: + options[arg] = "[{0}]".format(arg) + methods = ','.join(rule.methods) + try: + url = url_for(rule.endpoint, **options) + except: + url = '???' + line = ("{:50s} {:20s} {}".format(rule.endpoint, methods, url)) + output.append(line) + + resp = request.path + '<br/>\n' + resp += str(request.environ) + '<br/>\n' + resp += str(app.config['FOODTRUCK_ROOT_URL']) + '<br/>\n' + resp += '<br/>\n'.join(sorted(output)) + return resp, 404 + # --------------- logger.debug("Created FoodTruck app with admin root: %s" % root_dir)
--- a/piecrust/serving/wrappers.py Wed Oct 04 09:11:58 2017 -0700 +++ b/piecrust/serving/wrappers.py Wed Oct 04 09:15:16 2017 -0700 @@ -142,10 +142,27 @@ app_wrapper.run() +def get_piecrust_server(root_dir, *, + debug=False, + cache_key=None, + serve_site=True, + serve_admin=False, + is_cmdline_mode=False): + from piecrust.app import PieCrustFactory + appfactory = PieCrustFactory(root_dir, + debug=debug, + cache_key=cache_key) + return _get_piecrust_server(appfactory, + serve_site=serve_site, + serve_admin=serve_admin, + is_cmdline_mode=is_cmdline_mode) + + def _get_piecrust_server(appfactory, *, serve_site=True, serve_admin=False, is_cmdline_mode=False, + admin_root_url=None, run_sse_check=None): app = None @@ -164,11 +181,10 @@ if serve_admin: from piecrust.admin.web import create_foodtruck_app - admin_root_url = '/pc-admin' es = { 'FOODTRUCK_CMDLINE_MODE': is_cmdline_mode, - 'FOODTRUCK_ROOT': appfactory.root_dir, - 'FOODTRUCK_URL_PREFIX': admin_root_url, + 'FOODTRUCK_ROOT_DIR': appfactory.root_dir, + 'FOODTRUCK_ROOT_URL': admin_root_url, 'DEBUG': appfactory.debug} if is_cmdline_mode: es.update({ @@ -179,9 +195,11 @@ # Disable PIN protection with Werkzeug's debugger. os.environ['WERKZEUG_DEBUG_PIN'] = 'off' - admin_app = create_foodtruck_app(es) - admin_app.wsgi_app = _PieCrustSiteOrAdminMiddleware( - app, admin_app.wsgi_app, admin_root_url) + admin_app = create_foodtruck_app(es, url_prefix=admin_root_url) + if app is not None: + admin_app.wsgi_app = _PieCrustSiteOrAdminMiddleware( + app, admin_app.wsgi_app, admin_root_url) + app = admin_app return app @@ -203,3 +221,13 @@ if path_info.startswith(self.admin_root_url): return self.admin_app(environ, start_response) return self.main_app(environ, start_response) + + +class _PieCrustAdminScriptNamePatcherMiddleware: + def __init__(self, admin_app, admin_root_url): + self.admin_app = admin_app + self.admin_root_url = '/%s' % admin_root_url.strip('/') + + def __call__(self, environ, start_response): + environ['SCRIPT_NAME'] = self.admin_root_url + return self.admin_app(environ, start_response)
--- a/piecrust/wsgiutil/__init__.py Wed Oct 04 09:11:58 2017 -0700 +++ b/piecrust/wsgiutil/__init__.py Wed Oct 04 09:15:16 2017 -0700 @@ -1,5 +1,5 @@ import logging -from piecrust.serving.server import WsgiServer +from piecrust.serving.wrappers import get_piecrust_server def _setup_logging(log_file, log_level, max_log_bytes, log_backup_count): @@ -13,29 +13,29 @@ def get_app(root_dir, *, cache_key='prod', - enable_debug_info=False, + serve_admin=False, log_file=None, log_level=logging.INFO, log_backup_count=0, max_log_bytes=4096): _setup_logging(log_file, log_level, max_log_bytes, log_backup_count) - app = WsgiServer(root_dir, - cache_key=cache_key, - enable_debug_info=enable_debug_info) + app = get_piecrust_server(root_dir, + serve_site=True, + serve_admin=serve_admin, + cache_key=cache_key) return app def get_admin_app(root_dir, *, - url_prefix='pc-admin', + cache_key='prod', log_file=None, log_level=logging.INFO, log_backup_count=0, max_log_bytes=4096): _setup_logging(log_file, log_level, max_log_bytes, log_backup_count) - es = { - 'FOODTRUCK_ROOT': root_dir, - 'FOODTRUCK_URL_PREFIX': url_prefix} - from piecrust.admin.web import create_foodtruck_app - app = create_foodtruck_app(es) + app = get_piecrust_server(root_dir, + serve_site=False, + serve_admin=True, + cache_key=cache_key) return app