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