Browse code

Added support for pagination with a blog index in any directory (helps with landing pages). Added configuration for pagination directory base

Brandon Mathis authored on 16/08/2011 at 03:59:28
Showing 3 changed files
... ...
@@ -2,4 +2,41 @@
2 2
 layout: default
3 3
 ---
4 4
 
5
-{% include blog_index.html %}
5
+<div class="blog-index">
6
+{% assign index = true %}
7
+{% for post in paginator.posts %}
8
+{% assign content = post.content %}
9
+  <article>
10
+    {% include article.html %}
11
+  </article>
12
+{% endfor %}
13
+<nav role="pagination">
14
+  <div>
15
+    {% if paginator.next_page %}
16
+      <a class="prev" href="{{paginator.next_page}}">&larr; Older</a>
17
+    {% endif %}
18
+    <a href="/blog/archives">Blog Archives</a>
19
+    {% if paginator.previous_page %}
20
+    <a class="next" href="{{paginator.previous_page}}">Newer &rarr;</a>
21
+    {% endif %}
22
+  </div>
23
+</nav>
24
+{% if site.disqus_short_name %}
25
+<script type="text/javascript">
26
+    var disqus_shortname = '{{ site.disqus_short_name }}';
27
+    (function () {
28
+      var s = document.createElement('script'); s.async = true;
29
+      s.type = 'text/javascript';
30
+      s.src = 'http://' + disqus_shortname + '.disqus.com/count.js';
31
+      (document.getElementsByTagName('HEAD')[0] || document.getElementsByTagName('BODY')[0]).appendChild(s);
32
+    }());
33
+</script>
34
+{% endif %}
35
+</div>
36
+<aside role=sidebar>
37
+  {% if site.blog_index_asides.size %}
38
+    {% include_array blog_index_asides %}
39
+  {% else %}
40
+    {% include_array default_asides %}
41
+  {% endif %}
42
+</aside>
... ...
@@ -29,8 +29,9 @@ category_dir: blog/categories
29 29
 markdown: rdiscount
30 30
 pygments: false # default python pygments have been replaced by pygments.rb
31 31
 
32
-paginate: 10 # Posts per page on the blog index
33
-recent_posts: 5 # Posts in the sidebar Recent Posts section
32
+paginate: 10          # Posts per page on the blog index
33
+pagination_dir: blog  # Directory base for pagination URLs eg. /blog/page/2/
34
+recent_posts: 5       # Posts in the sidebar Recent Posts section
34 35
 
35 36
 # list each of the sidebar modules you want to include, in the order you want them to appear.
36 37
 # To add custom asides, create files in /source/_includes/custom/asides/ and add them to the list like 'custom/asides/custom_aside_name.html'
37 38
new file mode 100644
... ...
@@ -0,0 +1,121 @@
0
+module Jekyll
1
+
2
+  class Pagination < Generator
3
+    # This generator is safe from arbitrary code execution.
4
+    safe true
5
+
6
+    # Generate paginated pages if necessary.
7
+    #
8
+    # site - The Site.
9
+    #
10
+    # Returns nothing.
11
+    def generate(site)
12
+      site.pages.dup.each do |page|
13
+        paginate(site, page) if Pager.pagination_enabled?(site.config, page)
14
+      end
15
+    end
16
+
17
+    # Paginates the blog's posts. Renders the index.html file into paginated
18
+    # directories, e.g.: page2/index.html, page3/index.html, etc and adds more
19
+    # site-wide data.
20
+    #
21
+    # site - The Site.
22
+    # page - The index.html Page that requires pagination.
23
+    #
24
+    # {"paginator" => { "page" => <Number>,
25
+    #                   "per_page" => <Number>,
26
+    #                   "posts" => [<Post>],
27
+    #                   "total_posts" => <Number>,
28
+    #                   "total_pages" => <Number>,
29
+    #                   "previous_page" => <Number>,
30
+    #                   "next_page" => <Number> }}
31
+    def paginate(site, page)
32
+      all_posts = site.site_payload['site']['posts']
33
+      pages = Pager.calculate_pages(all_posts, site.config['paginate'].to_i)
34
+      page_dir = page.destination('').sub(/\/[^\/]+$/, '')
35
+      page_dir_config = site.config['pagination_dir']
36
+      dir = ((page_dir_config || page_dir) + '/').sub(/^\/+/, '')
37
+
38
+      (1..pages).each do |num_page|
39
+        pager = Pager.new(site.config, num_page, all_posts, pages, page_dir+'/', '/'+dir)
40
+        if num_page > 1
41
+          newpage = Page.new(site, site.source, page_dir, page.name)
42
+          newpage.pager = pager
43
+          newpage.dir = File.join(page.dir, "#{dir}page/#{num_page}")
44
+          site.pages << newpage
45
+        else
46
+          page.pager = pager
47
+        end
48
+      end
49
+    end
50
+  end
51
+
52
+  class Pager
53
+    attr_reader :page, :per_page, :posts, :total_posts, :total_pages, :previous_page, :next_page
54
+
55
+    # Calculate the number of pages.
56
+    #
57
+    # all_posts - The Array of all Posts.
58
+    # per_page  - The Integer of entries per page.
59
+    #
60
+    # Returns the Integer number of pages.
61
+    def self.calculate_pages(all_posts, per_page)
62
+      (all_posts.size.to_f / per_page.to_i).ceil
63
+    end
64
+
65
+    # Determine if pagination is enabled for a given file.
66
+    #
67
+    # config - The configuration Hash.
68
+    # file   - The String filename of the file.
69
+    #
70
+    # Returns true if pagination is enabled, false otherwise.
71
+    def self.pagination_enabled?(config, file)
72
+      file.name == 'index.html' && !config['paginate'].nil? && file.content =~ /paginator\./
73
+    end
74
+
75
+    # Initialize a new Pager.
76
+    #
77
+    # config    - The Hash configuration of the site.
78
+    # page      - The Integer page number.
79
+    # all_posts - The Array of all the site's Posts.
80
+    # num_pages - The Integer number of pages or nil if you'd like the number
81
+    #             of pages calculated.
82
+    def initialize(config, page, all_posts, num_pages = nil, index_dir, pagination_dir)
83
+      @page = page
84
+      @per_page = config['paginate'].to_i
85
+      @page_dir = pagination_dir + 'page/'
86
+      @total_pages = num_pages || Pager.calculate_pages(all_posts, @per_page)
87
+      @previous_page = nil
88
+
89
+      if @page > @total_pages
90
+        raise RuntimeError, "page number can't be greater than total pages: #{@page} > #{@total_pages}"
91
+      end
92
+
93
+      init = (@page - 1) * @per_page
94
+      offset = (init + @per_page - 1) >= all_posts.size ? all_posts.size : (init + @per_page - 1)
95
+
96
+      @total_posts = all_posts.size
97
+      @posts = all_posts[init..offset]
98
+      @previous_page = @page != 1 ? @page_dir + (@page - 1).to_s + '/' : nil
99
+      @previous_page = index_dir if @page - 1 == 1
100
+      @next_page = @page != @total_pages ? @page_dir + (@page + 1).to_s + '/' : nil
101
+    end
102
+
103
+    # Convert this Pager's data to a Hash suitable for use by Liquid.
104
+    #
105
+    # Returns the Hash representation of this Pager.
106
+    def to_liquid
107
+      {
108
+        'page' => page,
109
+        'per_page' => per_page,
110
+        'posts' => posts,
111
+        'total_posts' => total_posts,
112
+        'total_pages' => total_pages,
113
+        'previous_page' => previous_page,
114
+        'next_page' => next_page
115
+      }
116
+    end
117
+  end
118
+
119
+end
120
+