Skip to content

Commit 47ae731

Browse files
committed
Refactor plain pages and add asset handling to it
1 parent bdb1a70 commit 47ae731

File tree

5 files changed

+119
-80
lines changed

5 files changed

+119
-80
lines changed

plain-pages/plain/pages/default_settings.py

-1
This file was deleted.

plain-pages/plain/pages/pages.py

+48-15
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,7 @@
99

1010

1111
class Page:
12-
def __init__(self, url_path, relative_path, absolute_path):
13-
self.url_path = url_path
12+
def __init__(self, relative_path, absolute_path):
1413
self.relative_path = relative_path
1514
self.absolute_path = absolute_path
1615

@@ -39,30 +38,64 @@ def content(self):
3938
# Strip the frontmatter again, since it was in the template file itself
4039
_, content = frontmatter.parse(content)
4140

42-
if self.content_type == "markdown":
41+
if self.is_markdown():
4342
content = render_markdown(content)
4443

4544
return content
4645

47-
@property
48-
def content_type(self):
46+
def is_markdown(self):
4947
extension = os.path.splitext(self.absolute_path)[1]
48+
return extension == ".md"
5049

51-
# Explicitly define the known content types that we intend to handle
52-
# (others will still pass through)
53-
if extension == ".md":
54-
return "markdown"
50+
def is_template(self):
51+
return ".template." in os.path.basename(self.absolute_path)
5552

56-
if extension == ".html":
57-
return "html"
53+
def is_asset(self):
54+
extension = os.path.splitext(self.absolute_path)[1]
55+
return extension.lower() in (
56+
".jpg",
57+
".jpeg",
58+
".png",
59+
".gif",
60+
".webp",
61+
".svg",
62+
".js",
63+
".css",
64+
".ico",
65+
)
66+
67+
def is_redirect(self):
68+
extension = os.path.splitext(self.absolute_path)[1]
69+
return extension == ".redirect"
70+
71+
def get_url_path(self) -> str | None:
72+
if self.is_template():
73+
return None
74+
75+
if self.is_asset():
76+
return self.relative_path
77+
78+
url_path = os.path.splitext(self.relative_path)[0]
5879

59-
if extension == ".redirect":
60-
return "redirect"
80+
# If it's an index.html or something, the url is the parent dir
81+
if os.path.basename(url_path) == "index":
82+
url_path = os.path.dirname(url_path)
6183

62-
return extension.lstrip(".")
84+
return url_path + "/" # With trailing slash
6385

6486
def get_template_name(self):
6587
if template_name := self.vars.get("template_name"):
6688
return template_name
6789

68-
return f"{self.content_type}.html"
90+
return ""
91+
92+
def get_view_class(self):
93+
from .views import PageAssetView, PageRedirectView, PageView
94+
95+
if self.is_redirect():
96+
return PageRedirectView
97+
98+
if self.is_asset():
99+
return PageAssetView
100+
101+
return PageView

plain-pages/plain/pages/registry.py

+41-14
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
import os
22

3+
from plain.urls import path
4+
35
from .exceptions import PageNotFoundError
46
from .pages import Page
57

@@ -14,34 +16,59 @@ def __init__(self):
1416
# url path -> file path
1517
self.registered_pages = {}
1618

17-
def register_page(self, url_path, relative_path, absolute_path):
18-
self.registered_pages[url_path] = (url_path, relative_path, absolute_path)
19+
def get_page_urls(self):
20+
"""
21+
Generate a list of real urls based on the files that exist.
22+
This way, you get a concrete url reversingerror if you try
23+
to refer to a page/url that isn't going to work.
24+
"""
25+
paths = []
26+
27+
for url_path in self.registered_pages.keys():
28+
if url_path == "":
29+
# The root index is a special case and should be
30+
# referred to as pages:index
31+
url = ""
32+
name = "index"
33+
else:
34+
url = url_path
35+
name = url_path
36+
37+
page = self.get_page(url_path)
38+
view_class = page.get_view_class()
1939

20-
def url_paths(self):
21-
return self.registered_pages.keys()
40+
paths.append(
41+
path(
42+
url,
43+
view_class,
44+
name=name,
45+
kwargs={"url_path": url_path},
46+
)
47+
)
48+
49+
return paths
2250

2351
def discover_pages(self, pages_dir):
24-
for root, dirs, files in os.walk(pages_dir):
52+
for root, _, files in os.walk(pages_dir):
2553
for file in files:
2654
relative_path = os.path.relpath(os.path.join(root, file), pages_dir)
27-
url_path = os.path.splitext(relative_path)[0]
2855
absolute_path = os.path.join(root, file)
2956

30-
# Skip template files
31-
if ".template." in absolute_path:
32-
continue
57+
page_args = (relative_path, absolute_path)
58+
url_path = Page(*page_args).get_url_path()
3359

34-
if os.path.basename(url_path) == "index":
35-
url_path = os.path.dirname(url_path)
60+
# Some pages don't get a url (like templates)
61+
if url_path is None:
62+
continue
3663

37-
self.register_page(url_path, relative_path, absolute_path)
64+
self.registered_pages[url_path] = page_args
3865

3966
def get_page(self, url_path):
4067
try:
41-
url_path, relative_path, absolute_path = self.registered_pages[url_path]
68+
page_args = self.registered_pages[url_path]
4269
# Instantiate the page here, so we don't store a ton of cached data over time
4370
# as we render all the pages
44-
return Page(url_path, relative_path, absolute_path)
71+
return Page(*page_args)
4572
except KeyError:
4673
raise PageNotFoundError(f"Could not find a page for {url_path}")
4774

plain-pages/plain/pages/urls.py

+1-35
Original file line numberDiff line numberDiff line change
@@ -1,44 +1,10 @@
1-
from plain.runtime import settings
21
from plain.urls import include, path
3-
from plain.utils.module_loading import import_string
42

53
from .registry import registry
64

75
default_namespace = "pages"
86

97

10-
def get_page_urls():
11-
"""
12-
Generate a list of real urls based on the files that exist.
13-
This way, you get a concrete url reversingerror if you try
14-
to refer to a page/url that isn't going to work.
15-
"""
16-
paths = []
17-
18-
view_class = import_string(settings.PAGES_VIEW_CLASS)
19-
20-
for url_path in registry.url_paths():
21-
if url_path == "":
22-
# The root index is a special case and should be
23-
# referred to as pages:index
24-
url = ""
25-
name = "index"
26-
else:
27-
url = url_path + "/"
28-
name = url_path
29-
30-
paths.append(
31-
path(
32-
url,
33-
view_class,
34-
name=name,
35-
kwargs={"url_path": url_path},
36-
)
37-
)
38-
39-
return paths
40-
41-
428
urlpatterns = [
43-
path("", include(get_page_urls())),
9+
path("", include(registry.get_page_urls())),
4410
]

plain-pages/plain/pages/views.py

+29-15
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,13 @@
1+
from plain.assets.views import AssetView
12
from plain.http import Http404, ResponsePermanentRedirect, ResponseRedirect
23
from plain.utils.functional import cached_property
3-
from plain.views import TemplateView
4+
from plain.views import TemplateView, View
45

56
from .exceptions import PageNotFoundError, RedirectPageError
67
from .registry import registry
78

89

9-
class PageView(TemplateView):
10-
template_name = "page.html"
11-
10+
class PageViewMixin:
1211
@cached_property
1312
def page(self):
1413
# Passed manually by the kwargs in the path definition
@@ -19,6 +18,10 @@ def page(self):
1918
except PageNotFoundError:
2019
raise Http404()
2120

21+
22+
class PageView(PageViewMixin, TemplateView):
23+
template_name = "page.html"
24+
2225
def get_template_names(self) -> list[str]:
2326
"""
2427
Allow for more specific user templates like
@@ -31,18 +34,29 @@ def get_template_context(self):
3134
context["page"] = self.page
3235
return context
3336

37+
38+
class PageRedirectView(PageViewMixin, View):
3439
def get(self):
35-
if self.page.content_type == "redirect":
36-
url = self.page.vars.get("url")
40+
# Passed manually by the kwargs in the path definition
41+
url_path = self.url_kwargs.get("url_path", "index")
42+
43+
url = self.page.vars.get("url")
44+
45+
if not url:
46+
raise RedirectPageError(f"Redirect page {url_path} is missing a url")
47+
48+
if self.page.vars.get("temporary", True):
49+
return ResponseRedirect(url)
50+
else:
51+
return ResponsePermanentRedirect(url)
52+
3753

38-
if not url:
39-
raise RedirectPageError(
40-
f"Redirect page {self.page.url_path} is missing a url"
41-
)
54+
class PageAssetView(PageViewMixin, AssetView):
55+
def get_url_path(self):
56+
return self.url_kwargs["url_path"]
4257

43-
if self.page.vars.get("temporary", True):
44-
return ResponseRedirect(url)
45-
else:
46-
return ResponsePermanentRedirect(url)
58+
def get_asset_path(self, path):
59+
return self.page.absolute_path
4760

48-
return super().get()
61+
def get_debug_asset_path(self, path):
62+
return self.page.absolute_path

0 commit comments

Comments
 (0)