]> git.za3k.com Git - blog.git/commitdiff
Add pagination
authorZachary Vance <za3k@za3k.com>
Sat, 3 Aug 2024 23:29:58 +0000 (19:29 -0400)
committerZachary Vance <za3k@za3k.com>
Sat, 3 Aug 2024 23:29:58 +0000 (19:29 -0400)
blog
config.yaml
templates/author.mustache.html
templates/category.mustache.html
templates/feed.mustache.html
templates/index.mustache.html
templates/layout.mustache.html
templates/links.mustache.html [deleted file]
templates/pagination.mustache.html [new file with mode: 0644]
templates/tag.mustache.html
templates/tagcloud.mustache.html

diff --git a/blog b/blog
index c7c1b68b330b7c20a5653b9fdf592cec282bd58b..2223f3147feedf60fdc4caeea1295a36ffeafb98 100755 (executable)
--- a/blog
+++ b/blog
@@ -51,35 +51,6 @@ def url_slug(title):
     title = "".join(x for x in title if x in allowable)
     return title
 
-def paginated_property(f):
-    # Add <PROPERTY>.pages and <PROPERTY>.first10 with deep python magic
-    class Paginated():
-        def __init__(self, lst):
-            self.lst = sorted(lst, key=lambda x: x.date, reverse=True)
-        def __iter__(self):
-            return iter(self.lst)
-        def __len__(self):
-            return len(self.lst)
-        @property
-        def pages(self, per_page=10):
-            for start in range(0, len(self.lst), per_page):
-                yield self.lst[start:start+per_page]
-        @property
-        def first10(self):
-            return self.lst[:10]
-        @property
-        def length(self):
-            return len(self.lst)
-    class AnonProperty():
-        def __init__(self, fget):
-            self.fget = fget
-        def __set_name__(self, owner, name):
-            self._name = "_" + name
-        def __get__(self, obj, objtype=None):
-            return Paginated(self.fget(obj))
-
-    return AnonProperty(f)
-
 def flag_last(l):
     l = list(l)
     for x in l[:-1]:
@@ -106,6 +77,7 @@ def scale(i1, range1, range2):
 
 class Templatable(PseudoMap):
     use_layout = True
+    is_paginated = False
     def __init__(self, blog):
         self.blog = blog
     
@@ -143,11 +115,56 @@ class Templatable(PseudoMap):
             return content.encode("utf8")
 
     def output(self):
-        output = self.content()
-        self.output_path.parent.mkdir(parents=True, exist_ok=True)
-        with open(self.output_path, "wb") as f:
-            f.write(output)
-        # TODO: Add a 'paginated' property that Tag, Category, Author, and index.html can all use
+        per_page = self.blog.posts_per_page
+        if not self.is_paginated or len(self.posts) <= per_page:
+            if self.is_paginated:
+                self.current_page = {"posts": self.posts}
+            self.pagination = ""
+            output = self.content()
+            self.output_path.parent.mkdir(parents=True, exist_ok=True)
+            with open(self.output_path, "wb") as f:
+                f.write(output)
+        else: # Paginated output
+            paginated_url_template = self.blog["{}_paginated_url".format(self.type)]
+            output_path_template = self.blog["{}_paginated_destination".format(self.type)]
+
+            pages = []
+            for start in range(0, len(self.posts), per_page):
+                number = start//per_page + 1
+                p = {
+                    "posts": self.posts[start:start+per_page],
+                    "page_num": number,
+                }
+                p["url"] = mustache.render(paginated_url_template, collections.ChainMap(p, self.context))
+                p["output_path"] = Path(mustache.render(output_path_template, collections.ChainMap(p, self.context)))
+                pages.append(p)
+
+            for page in pages:
+
+                self.current_page = page
+                pagination_context = collections.ChainMap({
+                    "current_page": page,
+                    "pages": [
+                        {
+                            "is_current": p == page,
+                            "page_num": p["page_num"],
+                            "url": p["url"],
+                        } for i, p in enumerate(pages)
+                    ]
+                }, self.context)
+
+                self.pagination = self.render_template(self.blog, "pagination", pagination_context)
+                content = self.render_template(self.blog, self.type, self.context)
+                assert self.use_layout
+                if self.use_layout:
+                    content = self.render_template(self.blog, "layout", collections.ChainMap({
+                        "content": content,
+                    }, pagination_context, self, self.blog))
+                content = content.encode("utf8")
+
+                page["output_path"].parent.mkdir(parents=True, exist_ok=True)
+                with open(page["output_path"], "wb") as f:
+                    f.write(content)
 
     @property
     def context(self):
@@ -182,9 +199,6 @@ class Post(Templatable):
     def post(self):
         return '<div class="entry-content">{}</div>'.format(markdown2html(self.md))
 
-    def content_combined(self):
-        return self.render_template(self.blog, "post", self.context).encode("utf8")
-
     @property
     def date_rfc822(self):
         return self.date.strftime(RFC822)
@@ -234,29 +248,41 @@ class Post(Templatable):
     def has_categories(self):
         return len(self.categories) > 0
 
+    def content(self):
+        c = super().content()
+        if self.blog.local:
+            return self.blog.localize_absolute_links(c.decode("utf8")).encode("utf8")
+        else:
+            return c
+
     @property
     def html(self):
-        return self.render_template(self.blog, self.type, collections.ChainMap({
+        h = self.render_template(self.blog, self.type, collections.ChainMap({
             "main_display": False,
         }, self.context))
+        if self.blog.local:
+            h = self.blog.localize_absolute_links(h)
+        return h
+
 
 class Tag(Templatable):
+    is_paginated = True
     def __init__(self, tag, blog):
         super().__init__(blog)
         self.tag = tag
         self._posts = set()
         self.slug = url_slug(tag)
 
+    @property
+    def posts(self):
+        return sorted(self._posts, key=lambda p: p.date, reverse=True)
+
     def add_post(self, post):
         self._posts.add(post)
 
-    @paginated_property
-    def posts(self):
-        return self._posts
-
     @property
     def num_posts(self):
-        return len(self.posts)
+        return len(self._posts)
 
     def __hash__(self):
         return hash(self.tag)
@@ -264,20 +290,20 @@ class Tag(Templatable):
 class Category(Tag):
     pass
 
+class Author(Tag):
+    pass
+
 class Page(Templatable):
-    def __init__(self, page_name, blog, use_layout=None):
+    def __init__(self, page_name, blog, **kw):
         super().__init__(blog)
         self.page_name = page_name
-        if use_layout is not None:
-            self.use_layout = use_layout
+        for k, v in kw.items():
+            setattr(self, k, v)
 
     @property
     def type(self):
         return self.page_name
 
-class Author(Tag):
-    pass
-
 class Image(Templatable):
     use_layout = False
     pass # TODO
@@ -287,7 +313,7 @@ class Blog(PseudoMap):
         self.tags = {} # Tag -> str
         self.categories = {}
         self.authors = {}
-        self._posts = []
+        self.posts = []
         self.now = datetime.datetime.now(datetime.timezone.utc)
         self.now_rfc822 = self.now.strftime(RFC822)
 
@@ -307,10 +333,6 @@ class Blog(PseudoMap):
             self[k] = v
         self.feed_url = mustache.render(self.feed_url, self)
 
-    @paginated_property
-    def posts(self):
-        return self._posts
-
     def load_comments(self, stem):
         comments_path = Path(self.comments_dir) / (stem + ".html")
         if comments_path.exists():
@@ -323,9 +345,10 @@ class Blog(PseudoMap):
             comments = self.load_comments(post_input_path.stem)
             assert fm["has-comments"] == (comments is not None)
             self.add_post(Post(fm, self, comments))
+        self.posts = sorted(self.posts, key=lambda post: post.date, reverse=True)
 
     def add_post(self, post):
-        self._posts.append(post)
+        self.posts.append(post)
 
         for tag in post.tags:
             tag.add_post(post)
@@ -360,8 +383,8 @@ class Blog(PseudoMap):
     @property
     def pages(self):
         return [
-            Page("index", self),
-            Page("feed", self, use_layout=False),
+            Page("index", self, is_paginated=True, posts=blog.posts),
+            Page("feed", self, use_layout=False, posts=blog.posts[:10]),
         ]
 
     @property
@@ -400,6 +423,9 @@ class Blog(PseudoMap):
         
         return Templatable.render_template(Templatable, blog, "tagcloud", self)
 
+    def localize_absolute_links(self, t):
+        return t.replace('href="/', f"href=\"{self.web_root}/").replace('src="/', f"src=\"{self.web_root}/")
+
     def clean(self):
         assert self.destination
         os.system("rm -rf \"{}\"/*".format(self.destination))
index ea1ec3297337d003eda3b22badece584e832ed8b..9c7531f48bd389cd0fe0ef06c6c8a6c7e7d0ed26 100644 (file)
@@ -6,6 +6,7 @@ web_root: "https://blog2.za3k.com"
 title: 'blog of zachary "za3k" vance'
 # For search bar
 domain: "blog2.za3k.com"
+posts_per_page: 10
 
 post_dir: "posts"
 comments_dir: "comments"
@@ -18,24 +19,37 @@ category_template: "templates/category.mustache.html"
 index_template: "templates/index.mustache.html"
 feed_template: "templates/feed.mustache.html"
 layout_template: "templates/layout.mustache.html"
+pagination_template: "templates/pagination.mustache.html"
 post_template: "templates/post.mustache.html"
 tag_template: "templates/tag.mustache.html"
 tagcloud_template: "templates/tagcloud.mustache.html"
 
 # Non-local mode, preferred link address
 author_url: "{{web_root}}/author/{{slug}}/"
+author_paginated_url: "{{web_root}}/author/{{slug}}/{{page_num}}/"
 category_url: "{{web_root}}/category/{{slug}}/"
+category_paginated_url: "{{web_root}}/category/{{slug}}/{{page_num}}/"
 tag_url: "{{web_root}}/tag/{{slug}}/"
+tag_paginated_url: "{{web_root}}/tag/{{slug}}/{{page_num}}/"
 index_url: "{{web_root}}"
+index_paginated_url: "{{web_root}}/page/{{page_num}}"
 post_url: "{{web_root}}/{{id}}/"
 feed_url: "{{web_root}}/feed/"
 
 author_destination: "{{destination}}/author/{{slug}}.html"
+author_paginated_destination: "{{destination}}/author/{{slug}}.{{page_num}}.html"
 category_destination: "{{destination}}/category/{{slug}}.html"
+category_paginated_destination: "{{destination}}/category/{{slug}}.{{page_num}}.html"
 index_destination: "{{destination}}/page/index.html"
+index_paginated_destination: "{{destination}}/page/index.{{page_num}}.html"
 feed_destination: "{{destination}}/page/feed.xml"
 image_destination: "{{destination}}/images/{{image}}"
 page_destination: "{{destination}}/{{page}}"
 post_destination: "{{destination}}/posts/{{id}}.html"
 static_destination: "{{destination}}/{{relative_path}}"
 tag_destination: "{{destination}}/tag/{{slug}}.html"
+tag_paginated_destination: "{{destination}}/tag/{{slug}}.{{page_num}}.html"
+
+category_destination: "{{destination}}/category/{{slug}}.html"
+index_destination: "{{destination}}/page/index.html"
+tag_destination: "{{destination}}/tag/{{slug}}.html"
index c2b63f1798cc161b517984731976e0c8a2125072..ddfe15d41685428d01ef067c26827187c4582072 100644 (file)
@@ -2,6 +2,6 @@
     <h1 class="page-title author">Author Archives: <span class="vcard"><a class="url fn n" href="{{ url }}/" title="admin" rel="me">{{ tag }}</a></span></h1>
 </header>
 
-{{# posts.first10 }}
+{{# current_page.posts }}
     {{& post }}
-{{/ posts.first10 }}
+{{/ current_page.posts }}
index c3d16137c89881a0a134a731c8de36f193b703bc..bb4effe674dfd6adf33ebf0444b5466a0447d1e3 100644 (file)
@@ -2,6 +2,6 @@
     <h1 class="page-title">Category Archives: <span>{{ tag }}</span></h1>
 </header>
 
-{{# posts.first10 }}
+{{# current_page.posts }}
     {{& html }}
-{{/ posts.first10 }}
+{{/ current_page.posts }}
index 7ab22f9d4f74e1287c27d8e3e8ba52a812c269a4..fc9d110450b9950acc75497865d6135c07b0b8fa 100644 (file)
@@ -16,7 +16,7 @@
        <language>en-US</language>
        <sy:updatePeriod>hourly</sy:updatePeriod>
        <sy:updateFrequency>1</sy:updateFrequency>
-    {{# posts.first10 }}
+    {{# posts }}
        <item>
                <title>{{title}}</title>
                <link>{{url}}</link>
@@ -32,6 +32,6 @@
         <!--<description><![CDATA[SHORT_DESCRIPTION]]></description>-->
         <content:encoded><![CDATA[{{& post}}]]></content:encoded>
     </item>
-    {{/ posts.first10 }}
+    {{/ posts }}
 </channel>
 </rss>
index 3a61f0ea5fe5d5c8cf358f81e7353f27a519675e..c2ccfdc2d9df0112f1d98ee4d0807b876b79d8f7 100644 (file)
@@ -1,3 +1,3 @@
-{{# posts.first10 }}
+{{# current_page.posts }}
     {{& html }}
-{{/ posts.first10 }}
+{{/ current_page.posts }}
index 85dfdeb99a235b028d5dea02dd602d897a72a54c..4e649e50d07f38b5fcbdc400ab7d7db17f0145e5 100644 (file)
@@ -3,10 +3,8 @@
     <title>{{ title }}</title>
     <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
     <meta name="viewport" content="width=device-width, user-scalable=yes, initial-scale=1.0, minimum-scale=1.0, maximum-scale=3.0">
-    <link rel="profile" href="http://gmpg.org/xfn/11">
     <meta name="robots" content="max-image-preview:large">
     <link rel="alternate" type="application/rss+xml" title=" ยป Feed" href="{{feed_url}}">
-
     <link rel="stylesheet" href="{{web_root}}/css/style-wordpress.css" type="text/css" media="all">
 </head>
 
@@ -49,6 +47,7 @@
                <section id="container">
                        <div id="content" role="main">
                 {{& content }}
+                {{& pagination }}
             </div>
                        <div id="primary" class="widget-area" role="complementary">
                 <ul class="xoxo">
diff --git a/templates/links.mustache.html b/templates/links.mustache.html
deleted file mode 100644 (file)
index 7ece4c2..0000000
+++ /dev/null
@@ -1,5 +0,0 @@
-<ol>
-{{# alllinks }}
-<li><a href="{{static}}">{{partial}}</a> <a href="{{wordpress}}">[orig]</a> <a href="{{source}}">[src]</a>
-{{/ alllinks }}
-</ol>
diff --git a/templates/pagination.mustache.html b/templates/pagination.mustache.html
new file mode 100644 (file)
index 0000000..b84eb4c
--- /dev/null
@@ -0,0 +1,12 @@
+<div class="pagination_container">
+    <nav class="pagination">
+        {{# pages }}
+            {{# is_current }}
+                <span class="current">{{ page_num }}</span>
+            {{/ is_current }}
+            {{^ is_current }}
+                <a href="{{ url }}" class="inactive">{{ page_num }}</a>
+            {{/ is_current }}
+        {{/ pages }}
+    </nav>
+</div>
index 9db9051d4bc8ac91d2060fe86b43dc90b082fd5c..1dd541cc1f78c81f4b6633989db1be864b0a233b 100644 (file)
@@ -2,6 +2,6 @@
     <h1 class="page-title">Tag Archives: <span>{{ tag }}</span></h1>
 </header>
 
-{{# posts.first10 }}
+{{# current_page.posts }}
     {{& html }}
-{{/ posts.first10 }}
+{{/ current_page.posts }}
index 34de6f3699d7d8b9b40f49e48bb4b24911d9b829..6c57e295c2f4b6b2965bf68ea3f736941bbc0541 100644 (file)
@@ -1,4 +1,3 @@
-
 <p class="wp-block-tag-cloud">
     {{# top_tags }}
         <a href="{{ url }}" class="tag-cloud-link" style="font-size: {{ font_size }}pt;" aria-label="{{ tag }} ({{ num_posts }} items)">{{ tag }}</a>