Patch for pagination in static-rendering mode

Norman Yarvin <[email protected]> Fri, 16 Mar 2012 05:03:34 -0400
Newsgroups gmane.comp.web.pyblosxom.devel
Message-ID <[email protected]>
--AqsLC8rIMeq19msA
Content-Type: text/plain; charset=utf-8
Content-Disposition: inline

Attached is a patch that enables pagination in static-rendering mode,
pagination being the rendering of index pages which are longer than
num_entries by writing them out to multiple pages, combined with the
production of navigation strings which the user can include in the
templates for those pages.  Since:

	-- doing this as a plugin seemed somewhere between painful and
	impossible, and

	-- I think this really should be core functionality anyway,

I went ahead and implemented the whole thing in the core.  It should work
for dynamic rendering, too (or whatever one calls the opposite of static
rendering), but I haven't tested that mode.  It is meant to supersede the
present "paginate" plugin, but the latter should still work (again,
untested).  It uses all the same config variables, except for
"paginate_count_from", which I thought was far too much in the
bikeshedding direction to be worth saving.  The variable
"paginate_linkstyle" accepts, besides the old settings (0 or 1), the more
descriptive settings "1 of 4" (same as style 1) or "1234" (same as style 0).

For static rendering, instead of the page number being sent in the HTML
query, as in

	http://example.com/blog/index.html?page=2

it has to be part of the filename, as in

	http://example.com/blog/index_page2.html

which is now this change does it.

I haven't yet fixed up the documentation to reflect the changes.

(As for why I think this should be core functionality, I can't imagine
anyone not wanting it, at least for normal weblog use.)



-- 
Norman Yarvin					http://yarchive.net/blog

--AqsLC8rIMeq19msA
Content-Type: text/plain; charset=utf-8
Content-Disposition: attachment; filename="static_pagination.patch"
Content-Transfer-Encoding: quoted-printable

=46rom 0f284d9b991c40f553da83315fcfd67114c2a931 Mon Sep 17 00:00:00 2001
=46rom: Norman Yarvin <[email protected]>
Date: Thu, 15 Mar 2012 16:53:45 -0400
Subject: [PATCH] Allow for pagination of index URLs in static mode, via
 folding the pagination plugin into the main code

---
 Pyblosxom/pyblosxom.py |  126 ++++++++++++++++++++++++++++++++++++++++++++=
++-
 Pyblosxom/tools.py     |   56 +++++++++++++++------
 2 files changed, 163 insertions(+), 19 deletions(-)

diff --git a/Pyblosxom/pyblosxom.py b/Pyblosxom/pyblosxom.py
index 3a1888f..e869d58 100644
--- a/Pyblosxom/pyblosxom.py
+++ b/Pyblosxom/pyblosxom.py
@@ -1062,6 +1062,14 @@ def blosxom_file_list_handler(args):
                                    donefunc=3Dlambda x: x !=3D None,
                                    defaultfunc=3Dblosxom_sort_list_handler)
=20
+    # if we are to be chopping up the list and rendering only a part of it,
+    # report how many other parts there are to be rendered:
+    num_entries =3D config.get("num_entries",5)
+    truncate =3D data.get("truncate",0)
+    if num_entries > 0 and truncate:
+        max_page =3D (len(entrylist) - 1) / num_entries + 1
+        data["MAX_PAGE"] =3D max_page
+
     args =3D {"request": request, "entry_list": entrylist}   =20
     entrylist =3D tools.run_callback("truncatelist",
                                    args,
@@ -1092,7 +1100,11 @@ def blosxom_sort_list_handler(args):
 def blosxom_truncate_list_handler(args):
     """If ``config["num_entries"]`` is not 0 and ``data["truncate"]``
     is not 0, then this truncates ``args["entry_list"]`` by
-    ``config["num_entries"]``.
+    ``config["num_entries"]``.  Or if ``data["CURRENT_PAGE"]`` is set
+    (in static-rendering mode), or if there is a "page=3D" query (in
+    dynamic mode) rather than just truncating (rendering the first
+    page), it renders the N'th page.  If there is more than one
+    subpage, it also fills the page navigation string.
=20
     :param args: args dict with ``request`` object and ``entry_list``
                  list of entries
@@ -1105,12 +1117,120 @@ def blosxom_truncate_list_handler(args):
     data =3D request.data
     config =3D request.config
=20
+    static =3D data.get("STATIC",0) =3D=3D 1
+
     num_entries =3D config.get("num_entries", 5)
     truncate =3D data.get("truncate", 0)
-    if num_entries and truncate:
-        entrylist =3D entrylist[:num_entries]
+    max_page =3D (len(entrylist) - 1) / num_entries + 1
+
+    if num_entries and truncate and max_page > 1:
+        if static:
+            page =3D data.get("CURRENT_PAGE",1)
+        else:
+            form =3D request.get_form()
+            page =3D 1
+            if form:
+                try:
+                    page =3D int(form.getvalue("page"))
+                except:
+                    page =3D 1
+
+        begin =3D (page - 1) * num_entries
+        end =3D page * num_entries
+        entrylist =3D entrylist[begin:end]
+        make_page_navigation_string(request,page,max_page)
+    else:
+        data["page_navigation"] =3D ""
+
     return entrylist
=20
+def make_page_navigation_string(request,page,max_page):
+    """Make HTML code for navigating among the parts of an index page
+    which has been broken up due to excessive length.  The output
+    is stashed in ``data["page_navigation"]``.
+
+    :param request: the ``request`` object
+    :param page: the page being rendered
+    :param max_page: the number of pages this index page is sliced into
+    """
+
+    http =3D request.http
+    data =3D request.data
+    config =3D request.config
+
+    static =3D data.get("STATIC",0) =3D=3D 1
+
+    previous_text =3D config.get("paginate_previous_text", "&lt;&lt;")
+    next_text =3D config.get("paginate_next_text", "&gt;&gt;")
+    linkstyle =3D config.get("paginate_linkstyle", 1)
+
+    if linkstyle =3D=3D 0 or linkstyle =3D=3D "1234":
+        linkstyle =3D "1234"
+    elif linkstyle =3D=3D 1 or linkstyle =3D=3D "1 of 4":
+        linkstyle =3D "1 of 4"
+    else:
+        linkstyle =3D ""
+
+    #
+    # first task: get the url strings to use in referring to other slices
+    # of this index pages
+    #
+
+    url =3D http.get("REQUEST_URI", http.get("HTTP_REQUEST_URI", ""))
+
+    pos =3D url.find("?")
+    if pos !=3D -1:
+        query =3D url[pos + 1:]
+        url =3D url[:pos]
+    else:
+        query =3D ""
+
+    if static:
+        pos =3D url.rfind(".")
+        baseurl =3D url[1:pos]
+        extension =3D url[pos:]
+        url =3D baseurl + extension
+        url_n =3D baseurl + "_page%d" + extension
+    else:
+        query =3D query.split("&")
+        query =3D [m for m in query if not m.startswith("page=3D")]
+        if len(query) =3D=3D 0:
+            url_n =3D url + "?" + "page=3D%d"
+        else:
+            url_n =3D url + "?" + "&amp;".join(query) + "&amp;page=3D%d"
+
+    #
+    # produce the actual navigation string:
+    #
+
+    output =3D []
+
+    if page !=3D 1:
+        if page =3D=3D 2:
+            previous_url =3D url
+        else:
+            previous_url =3D url_n % (page-1)
+        output.append('<a href=3D"%s">%s</a>&nbsp;' % (previous_url, previ=
ous_text))
+
+    if linkstyle =3D=3D "1234":
+        for i in range(1,max_page+1):
+            if i =3D=3D page:
+                output.append('[%d]' % i)
+            else:
+                if i =3D=3D 1:
+                    url_i =3D url
+                else:
+                    url_i =3D url_n % i
+                output.append('<a href=3D"%s">%d</a>' % (url_i, i))
+    elif linkstyle =3D=3D "1 of 4":
+        output.append(' Page %s of %s ' % (page, max_page))
+
+    if page < max_page:
+        next_url =3D url_n % (page+1)
+        output.append('&nbsp;<a href=3D"%s">%s</a>' % (next_url, next_text=
))
+
+    data["page_navigation"] =3D " ".join(output)
+
 def blosxom_process_path_info(args):
     """Process HTTP ``PATH_INFO`` for URI according to path
     specifications, fill in data dict accordingly.
diff --git a/Pyblosxom/tools.py b/Pyblosxom/tools.py
index 394df8c..17c362b 100644
--- a/Pyblosxom/tools.py
+++ b/Pyblosxom/tools.py
@@ -942,8 +942,15 @@ def update_static_entry(cdict, entry_filename):
         render_url_statically(cdict, mem[0], mem[1])
=20
 def render_url_statically(cdict, url, querystring):
-    """Renders a url and saves the rendered output to the
-    filesystem.
+    """Renders a url and saves the rendered output to the filesystem.
+    If an index page is too long (as determined in rendering, and
+    ultimately by the config parameter num_entries) it breaks it into
+    multiple pages and renders all the pages, saving the pages past
+    the first into files whose names are derived from the URL, but
+    with "_pageN" inserted.  Only ".html" files (or files of whichever
+    extensions are named in the configuration parameter
+    "paged_extensions") are broken into multiple pages; the rest are
+    just truncated.
=20
     :param cdict: config dict
     :param url: url to render
@@ -956,22 +963,34 @@ def render_url_statically(cdict, url, querystring):
     if not staticdir:
         raise Exception("You must set static_dir in your config file.")
=20
-    staticdir =3D cdict.get("static_dir", "")
+    paged_extensions =3D cdict.get("paged_extensions",[".html"])
=20
-    response =3D render_url(cdict, url, querystring)
-    response.seek(0)
+    page =3D 1
+    while True:
+        response =3D render_url(cdict, url, querystring, page)
+        response.seek(0)
=20
-    fn =3D os.path.normpath(staticdir + os.sep + url)
-    if not os.path.isdir(os.path.dirname(fn)):
-        os.makedirs(os.path.dirname(fn))
+        fn =3D os.path.normpath(staticdir + os.sep + url)
+        fn_base, fn_ext =3D os.path.splitext(fn)
+        if page > 1:
+            fn =3D fn_base + "_page" + str(page) + fn_ext
+
+        if not os.path.isdir(os.path.dirname(fn)):
+            os.makedirs(os.path.dirname(fn))
+
+        f =3D open(fn, "w")
+        f.write(response.read())
+        f.close()
+
+        if fn_ext not in paged_extensions:
+            break
+
+        page +=3D 1
+        if page > response.max_page:
+            break
=20
-    # by using the response object the cheesy part of removing the
-    # HTTP headers from the file is history.
-    f =3D open(fn, "w")
-    f.write(response.read())
-    f.close()
=20
-def render_url(cdict, pathinfo, querystring=3D""):
+def render_url(cdict, pathinfo, querystring=3D"", page=3D1):
     """
     Takes a url and a querystring and renders the page that
     corresponds with that by creating a Request and a Pyblosxom object
@@ -981,6 +1000,9 @@ def render_url(cdict, pathinfo, querystring=3D""):
     :param pathinfo: the ``PATH_INFO`` string;
                      example: ``/dev/pyblosxom/firstpost.html``
     :param querystring: the querystring (if any); example: debug=3Dyes
+    :param page: the page number to render; this is for long index pages
+        that are to be split into multiple pages; it designates
+        which of those subpages to render
=20
     :returns: a Pyblosxom ``Response`` object.=20
     """
@@ -999,10 +1021,12 @@ def render_url(cdict, pathinfo, querystring=3D""):
         "wsgi.errors": sys.stderr,
         "wsgi.input": None
     }
-    data =3D {"STATIC": 1}
+    data =3D {"STATIC": 1, "CURRENT_PAGE": page }
     p =3D Pyblosxom(cdict, env, data)
     p.run(static=3DTrue)
-    return p.get_response()
+    response =3D p.get_response()
+    response.max_page =3D data.get("MAX_PAGE",1)
+    return response
=20
=20
 #******************************
--=20
1.7.8.5


--AqsLC8rIMeq19msA
Content-Type: text/plain; charset="us-ascii"
MIME-Version: 1.0
Content-Transfer-Encoding: 7bit
Content-Disposition: inline

------------------------------------------------------------------------------
This SF email is sponsosred by:
Try Windows Azure free for 90 days Click Here 
http://p.sf.net/sfu/sfd2d-msazure
--AqsLC8rIMeq19msA
Content-Type: text/plain; charset="us-ascii"
MIME-Version: 1.0
Content-Transfer-Encoding: 7bit
Content-Disposition: inline

_______________________________________________
Pyblosxom-devel mailing list
Pyblosxom-devel-5NWGOfrQmneRv+LV9MX5uipxlwaOVQ5f@public.gmane.org
https://lists.sourceforge.net/lists/listinfo/pyblosxom-devel

--AqsLC8rIMeq19msA--