diff --git a/content/pages/a_page.md b/content/pages/a_page.md index 19c7922..f37368f 100644 --- a/content/pages/a_page.md +++ b/content/pages/a_page.md @@ -11,5 +11,4 @@ template_engine jinja2

Page 1

-

Go to page 2

- +

Go to page 2

\ No newline at end of file diff --git a/content/pages/another_page.md b/content/pages/another_page.md index 9e1c210..17f11b8 100644 --- a/content/pages/another_page.md +++ b/content/pages/another_page.md @@ -11,4 +11,4 @@ template_engine jinja2

Page 2

-

Go to page 1

+

Go to page 1

diff --git a/content/templates/jinja2/blocks/en/navbar.html b/content/templates/jinja2/blocks/en/navbar.html index d2ddb7b..1793de7 100644 --- a/content/templates/jinja2/blocks/en/navbar.html +++ b/content/templates/jinja2/blocks/en/navbar.html @@ -4,7 +4,7 @@ CTA_LABEL="See pricing", CTA_TARGET="", CTA_URL="#pricing", - CHANGE_LANG_URL="fr-index", + CHANGE_LANG_URL="/fr-index", CHANGE_LANG_FLAG_URL="assets/img/lang-fr.svg", CHANGE_LANG_ALT="Version française" ) }} \ No newline at end of file diff --git a/content/templates/jinja2/blocks/fr/navbar.html b/content/templates/jinja2/blocks/fr/navbar.html index 29345f5..1baace1 100644 --- a/content/templates/jinja2/blocks/fr/navbar.html +++ b/content/templates/jinja2/blocks/fr/navbar.html @@ -4,7 +4,7 @@ CTA_LABEL="Découvrir les tarifs", CTA_TARGET="", CTA_URL="#pricing", - CHANGE_LANG_URL="en-index", + CHANGE_LANG_URL="/en-index", CHANGE_LANG_FLAG_URL="assets/img/lang-gb.svg", CHANGE_LANG_ALT="English version" ) }} \ No newline at end of file diff --git a/content/templates/jinja2/widgets/navbar.html b/content/templates/jinja2/widgets/navbar.html index ed7983c..dada97a 100644 --- a/content/templates/jinja2/widgets/navbar.html +++ b/content/templates/jinja2/widgets/navbar.html @@ -19,7 +19,7 @@ diff --git a/jssg/jinja2.py b/jssg/jinja2.py index 5bea8bb..dcbbf73 100644 --- a/jssg/jinja2.py +++ b/jssg/jinja2.py @@ -5,8 +5,85 @@ from jssg.templatetags.filter_opengraph_metadata import filter_opengraph_metadata -from jssg.models import Document -from django.conf import settings +from jssg.models import Page + +import re + +def url_for_slug(slug) : + """ + @param slug: the slug of the page to search + @return: the string of the url corresponding to the page + @error: raise an exception if the slug does not exist or it is not unique (eg same slug found in several folders) + + >>> url_for_slug('index') # the slug exists and is unique + /en/index.html + + >>> url_for_slug('index-duplicated') # the slug exists in several pages + Traceback (most recent call last): + ... + Exception: slug 'index-duplicated' is not unique, found in : [pages/fr-index.md, pages/en-index.md] ; use url_for_slug_path() + + >>> url_for_slug('index-removed') # the slug does not exists + Traceback (most recent call last): + ... + Exception: slug 'index-removed' not found + + """ + + url = "" + pages_with_slug = [] + + for page in Page.load_glob(all=True) : + if page.slug == slug : # the page exists + if pages_with_slug == [] : # the slug has not been found yet + if page.rel_folder_path != '' : + url = "/" + page.rel_folder_path + "/" + page.slug + ".html" + else : + url = "/" + page.slug + ".html" + else : # the slug already exists + url = "" + pages_with_slug.append(str(page.path.relative_to(page.content_page_dir.parent))) + + if url == "" and pages_with_slug != [] : + raise Exception("slug '%s' is not unique, found in : [%s] ; use url_for_slug_path()" % (slug, ", ".join(pages_with_slug))) + elif url == "" : + raise Exception("slug '%s' not found" % slug) + return url + +def url_for_slug_path(url_path) : + """ + @param url_path: the url of the page to search (absolute path) + @return: the string of the slug url corresponding to the page + @error: raise an exception if the url is a dead link + + >>> url_for_slug_path('/en/index') # the page exists + /en/index.html + + >>> url_for_slug_path('/en/index-removed') # the page does not exist + Traceback (most recent call last): + ... + Exception: page for '/en/index-removed' url not found (dead link) + + >>> url_for_slug_path('folder/index') + Traceback (most recent call last): + ... + Exception: url 'folder/index' is not valid ; correct urls are // or / + + """ + # Valid url are //.html or /.html + # Example: if url_path is "/tmp/folder/subfolder/thefile.html", then slug will be "thefile" and the dir will be "tmp/folder/subfolder" + # Note : the dir does not start with '/' since the url parsed in url.py do not either + try : + _, dir, slug = re.findall(r"(^|^/([a-zA-Z0-9/-]+))/([a-zA-Z0-9-]+)$", url_path)[0] + except : + raise Exception("url '%s' is not valid ; correct urls are // or /" % url_path) + + # Verify that the page exists + for page in Page.load_glob(all=True) : + if page.slug == slug and page.rel_folder_path == dir : + return url_path + ".html" + + raise Exception("page for '%s' url not found (dead link)" % url_path) def environment(**options): env = Environment(**options) @@ -14,6 +91,8 @@ def environment(**options): { "static": static, "url": reverse, + "url_for_slug": url_for_slug, + "url_for_slug_path" : url_for_slug_path } ) env.filters.update( diff --git a/jssg/models.py b/jssg/models.py index a44cc22..e8e5b15 100644 --- a/jssg/models.py +++ b/jssg/models.py @@ -196,7 +196,7 @@ def load(cls, path: Path) -> "Document": @classmethod def load_glob( - cls, path: Optional[List[Path]] = None, glob: str = "*.md" + cls, path: Optional[List[Path]] = None, dir = "", glob: str = "*.md", all=False ) -> Iterator["Document"]: """Load multiple document. @@ -212,7 +212,10 @@ def load_glob( files = [] for p in path : - files += p.glob(glob) + if all : + files += (p / dir).rglob(glob) + else : + files += (p / dir).glob(glob) # print(files) return map(cls.load, files) @@ -235,20 +238,29 @@ def __init__(self, content: str, **metadata) -> None: except KeyError: self.slug = slugify(self.title) + self.content_page_dir = self.path + while (self.content_page_dir not in self.BASE_DIR) : + self.content_page_dir = self.content_page_dir.parent + + # page folder path relative to its content_page_dir + self.rel_folder_path = str(self.path.relative_to(self.content_page_dir).parent) + if self.rel_folder_path == '.' : + self.rel_folder_path = '' + @classmethod - def load_page_with_slug(cls, slug: str) -> "Page": - return next(filter(lambda p: p.slug == slug, cls.load_glob())) + def load_page_with_slug(cls, slug: str, dir : str) -> "Page": + return next(filter(lambda p: p.slug == slug, cls.load_glob(dir = dir))) @classmethod def load_glob( - cls, path: Optional[List[Path]] = None, glob: str = "*.md" + cls, path: Optional[List[Path]] = None, dir = "", glob: str = "*.md", all = False ) -> Iterator["Page"]: """Overridden only to make the static typing happy.""" - return super().load_glob(path, glob) + return super().load_glob(path, dir, glob, all) @classmethod - def get_pages(cls): - return ({"slug": p.slug} for p in Page.load_glob()) + def get_pages(cls) : + return ({"slug": p.slug} if p.rel_folder_path == '' else {"dir": p.rel_folder_path, "slug" : p.slug} for p in Page.load_glob(all = True)) class Post(Page): @@ -267,14 +279,14 @@ def __init__(self, content: str, **metadata) -> None: @classmethod def load_glob( - cls, path: Optional[List[Path]] = None, glob: str = "*.md" + cls, path: Optional[List[Path]] = None, dir = "", glob: str = "*.md", all = False ) -> Iterator["Post"]: """Overridden only to make the static typing happy.""" - return super().load_glob(path, glob) - + return super().load_glob(path, dir, glob, all) + @classmethod - def get_posts(cls): - return ({"slug": p.slug} for p in Post.load_glob()) + def get_posts(cls) : + return ({"slug": p.slug} if p.rel_folder_path == '' else {"dir": p.rel_folder_path, "slug" : p.slug} for p in Post.load_glob(all = True)) class Sitemap : BASE_DIR = settings.JFME_PAGES_DIRS + settings.JFME_POSTS_DIRS diff --git a/jssg/urls.py b/jssg/urls.py index d53b63d..513dc1a 100644 --- a/jssg/urls.py +++ b/jssg/urls.py @@ -16,14 +16,24 @@ from jssg import views from jssg.models import Page, Post +from jssg import settings +# print([p for p in Page.get_pages()]) + urlpatterns = [ distill_path( "", views.IndexView.as_view(), name="index", distill_file="index.html" ), - distill_path( - "pages/.html", + distill_path("atom.xml", views.PostFeedsView(), name="atom_feed"), + distill_re_path( + r'^(?!posts/)(?P[a-zA-Z0-9-]+).html$', + views.PageView.as_view(), + name="page", + distill_func=Page.get_pages, + ), + distill_re_path( + r'^(?!posts/)(?P[a-zA-Z-/]+)/(?P[a-zA-Z0-9-]+).html$', views.PageView.as_view(), name="page", distill_func=Page.get_pages, @@ -35,6 +45,12 @@ name="post", distill_func=Post.get_posts, ), + distill_path( + "posts//.html", + views.PostView.as_view(), + name="post", + distill_func=Post.get_posts, + ), distill_path( "sitemap.xml", views.SitemapView.as_view(), diff --git a/jssg/views.py b/jssg/views.py index ead7530..2aa9004 100644 --- a/jssg/views.py +++ b/jssg/views.py @@ -51,8 +51,11 @@ class PageView(DetailView): template_name = "page.html" def get_object(self, queryset=None) -> Model: - print(self.kwargs["slug"]) - model = self.model.load_page_with_slug(self.kwargs["slug"]) + if "dir" not in self.kwargs.keys() : + self.kwargs["dir"] = "" + print("dir : " + self.kwargs["dir"]) + print("slug : " + self.kwargs["slug"]) + model = self.model.load_page_with_slug(self.kwargs["slug"], self.kwargs["dir"]) return model