diff --git a/incorporealcms/pages.py b/incorporealcms/pages.py index 911df60..6191852 100644 --- a/incorporealcms/pages.py +++ b/incorporealcms/pages.py @@ -6,7 +6,7 @@ import re from flask import Blueprint, Markup, abort from flask import current_app as app -from flask import redirect, request +from flask import redirect, request, send_from_directory from tzlocal import get_localzone from incorporealcms.lib import get_meta_str, init_md, render @@ -21,7 +21,7 @@ bp = Blueprint('pages', __name__, url_prefix='/') def display_page(path): """Get the file contents of the requested path and render the file.""" try: - resolved_path = request_path_to_instance_resource_path(path) + resolved_path, render_md = request_path_to_instance_resource_path(path) logger.debug("received request for path '%s', resolved to '%s'", path, resolved_path) except PermissionError: abort(400) @@ -30,6 +30,9 @@ def display_page(path): except FileNotFoundError: abort(404) + if not render_md: + return send_from_directory(app.instance_path, resolved_path) + try: with app.open_instance_resource(resolved_path, 'r') as entry_file: mtime = datetime.datetime.fromtimestamp(os.path.getmtime(entry_file.name), get_localzone()) @@ -76,21 +79,23 @@ def request_path_to_instance_resource_path(path): logger.info("client requested a path '%s' that is actually a directory", path) raise IsADirectoryError - # derive the proper markdown file depending on if this is a dir or file + # derive the proper markdown or actual file depending on if this is a dir or file if os.path.isdir(resolved_path): absolute_resource = os.path.join(resolved_path, 'index.md') + elif os.path.exists(resolved_path): + logger.info("final DIRECT path = '%s' for request '%s'", resolved_path, path) + return resolved_path.replace(f'{app.instance_path}{os.path.sep}', ''), False else: absolute_resource = f'{resolved_path}.md' - logger.info("final path = '%s' for request '%s'", absolute_resource, path) - # does the final file actually exist? if not os.path.exists(absolute_resource): logger.warning("requested final path '%s' does not exist!", absolute_resource) raise FileNotFoundError + logger.info("final path = '%s' for request '%s'", absolute_resource, path) # we checked that the file exists via absolute path, but now we need to give the path relative to instance dir - return absolute_resource.replace(f'{app.instance_path}{os.path.sep}', '') + return absolute_resource.replace(f'{app.instance_path}{os.path.sep}', ''), True def instance_resource_path_to_request_path(path): diff --git a/tests/functional_tests.py b/tests/functional_tests.py index bd3a892..e83ca73 100644 --- a/tests/functional_tests.py +++ b/tests/functional_tests.py @@ -9,6 +9,13 @@ def test_page_that_exists(client): assert b'

test index

' in response.data +def test_direct_file_that_exists(client): + """Test that the app can serve a basic file at the index.""" + response = client.get('/foo.txt') + assert response.status_code == 200 + assert b'test file' in response.data + + def test_page_that_doesnt_exist(client): """Test that the app returns 404 for nonsense requests and they use my error page.""" response = client.get('/ohuesthaoeusth') diff --git a/tests/instance/pages/bss-square-no-bg.png b/tests/instance/pages/bss-square-no-bg.png new file mode 100644 index 0000000..c592d25 Binary files /dev/null and b/tests/instance/pages/bss-square-no-bg.png differ diff --git a/tests/instance/pages/foo.txt b/tests/instance/pages/foo.txt new file mode 100644 index 0000000..16b14f5 --- /dev/null +++ b/tests/instance/pages/foo.txt @@ -0,0 +1 @@ +test file diff --git a/tests/test_pages.py b/tests/test_pages.py index a61c535..f438540 100644 --- a/tests/test_pages.py +++ b/tests/test_pages.py @@ -60,44 +60,45 @@ def test_render_with_no_user_theme(app): def test_request_path_to_instance_resource_path(app): """Test a normal URL request is transformed into the file path.""" with app.test_request_context(): - assert request_path_to_instance_resource_path('index') == 'pages/index.md' + assert request_path_to_instance_resource_path('index') == ('pages/index.md', True) def test_request_path_to_instance_resource_path_direct_file(app): """Test a normal URL request is transformed into the file path.""" with app.test_request_context(): - assert request_path_to_instance_resource_path('no-title') == 'pages/no-title.md' + assert request_path_to_instance_resource_path('no-title') == ('pages/no-title.md', True) def test_request_path_to_instance_resource_path_in_subdir(app): """Test a normal URL request is transformed into the file path.""" with app.test_request_context(): - assert request_path_to_instance_resource_path('subdir/page') == 'pages/subdir/page.md' + assert request_path_to_instance_resource_path('subdir/page') == ('pages/subdir/page.md', True) def test_request_path_to_instance_resource_path_subdir_index(app): """Test a normal URL request is transformed into the file path.""" with app.test_request_context(): - assert request_path_to_instance_resource_path('subdir/') == 'pages/subdir/index.md' + assert request_path_to_instance_resource_path('subdir/') == ('pages/subdir/index.md', True) def test_request_path_to_instance_resource_path_relatives_walked(app): """Test a normal URL request is transformed into the file path.""" with app.test_request_context(): assert (request_path_to_instance_resource_path('subdir/more-subdir/../../more-metadata') == - 'pages/more-metadata.md') + ('pages/more-metadata.md', True)) def test_request_path_to_instance_resource_path_relatives_walked_indexes_work_too(app): """Test a normal URL request is transformed into the file path.""" with app.test_request_context(): - assert request_path_to_instance_resource_path('subdir/more-subdir/../../') == 'pages/index.md' + assert request_path_to_instance_resource_path('subdir/more-subdir/../../') == ('pages/index.md', True) def test_request_path_to_instance_resource_path_relatives_walked_into_subdirs_also_fine(app): """Test a normal URL request is transformed into the file path.""" with app.test_request_context(): - assert request_path_to_instance_resource_path('subdir/more-subdir/../../subdir/page') == 'pages/subdir/page.md' + assert (request_path_to_instance_resource_path('subdir/more-subdir/../../subdir/page') == + ('pages/subdir/page.md', True)) def test_request_path_to_instance_resource_path_permission_error_on_ref_above_pages(app): @@ -114,6 +115,13 @@ def test_request_path_to_instance_resource_path_isadirectory_on_file_like_req_fo assert request_path_to_instance_resource_path('subdir') +def test_request_path_to_instance_resource_path_actual_file(app): + """Test that a request for e.g. '/foo.png' when foo.png is a real file works.""" + with app.test_request_context(): + assert (request_path_to_instance_resource_path('bss-square-no-bg.png') == + ('pages/bss-square-no-bg.png', False)) + + def test_request_path_to_instance_resource_path_nonexistant_file_errors(app): """Test that a request for something not on disk errors.""" with app.test_request_context(): @@ -155,13 +163,15 @@ def test_instance_resource_path_to_request_path_on_subdir_and_page(app): def test_request_resource_request_root(app): """Test that a request can resolve to a resource and back to a request.""" with app.test_request_context(): - instance_resource_path_to_request_path(request_path_to_instance_resource_path('index')) == '' + instance_path, _ = request_path_to_instance_resource_path('index') + instance_resource_path_to_request_path(instance_path) == '' def test_request_resource_request_page(app): """Test that a request can resolve to a resource and back to a request.""" with app.test_request_context(): - instance_resource_path_to_request_path(request_path_to_instance_resource_path('no-title')) == 'no-title' + instance_path, _ = request_path_to_instance_resource_path('no-title') + instance_resource_path_to_request_path(instance_path) == 'no-title' def test_request_path_to_breadcrumb_display_patterns():