~ 4 min read

Porting a Wordpress blog to Jekyll: Part 2

In the second of my posts in porting a wordpress blog over to jekyll, I outline how I’ve replicated wordpress’s default permalink structure on my own blog.

Creating Category and Tag Pages

Jekyll doesn’t create category or tag index pages for those categories listed in your _posts source. Instead, you’ll need to use a plugin to recreate them. I used the tutorial here as the basis for my category/tag generator, with my plugins/generate_cats_and_tags.rb source below:

module Jekyll
    class CatsAndTags < Generator
        def generate(site)
          site.categories.each do |category|
            build_subpages(site, "category", category)
          end

          site.tags.each do |tag|
            build_subpages(site, "tag", tag)
          end
        end

        # Do the actual generation.
        def build_subpages(site, type, posts)
          posts[1] = posts[1].sort_by { |p| -p.date.to_f }
          atomize(site, type, posts)
          paginate(site, type, posts)
        end

        def atomize(site, type, posts)
          path = "/#{type}/#{posts[0]}"
          atom = AtomPage.new(site, site.source, path, type, posts[0], posts[1])
          site.pages << atom
        end

        def paginate(site, type, posts)
          pages = Pager.calculate_pages(posts[1], site.config['paginate'].to_i)
          (1..pages).each do |num_page|
            pager = Pager.new(site, num_page, posts[1], pages)
            slug = posts[0].gsub(' ', '-')
            base_path = "/#{type}/#{slug}"
            path = base_path
            if num_page > 1
              path += "/page/#{num_page}"
            end
            newpage = GroupSubPage.new(site, site.source, path, base_path, type, posts[0])
            newpage.pager = pager
            site.pages << newpage

          end
        end
    end

    class GroupSubPage < Page
        def initialize(site, base, dir, base_path, type, val)
          @site = site
          @base = base
          @dir = dir
          @name = 'index.html'

          self.process(@name)
          self.read_yaml(File.join(base, '_layouts'), "group_index.html")
          self.data[type] = val
          self.data['base'] = base_path
        end
    end

    class AtomPage < Page
        def initialize(site, base, dir, type, val, posts)
            @site = site
            @base = base
            @dir = dir
            @name = 'atom.xml'

            self.process(@name)
            self.read_yaml(File.join(base, '_layouts'), "group_atom.xml")
            self.data[type] = val
            self.data["grouptype"] = type
            self.data["posts"] = posts[0..9]
        end
    end
end

I chose to pass in an extra parameter to the Pager, defining the “base_path” for which an index page has been generated. This enables absolute links to pages and prevents situations like /category/javascript/category/javascript/page/1, which occur when relative pagination is generated when on a category page.

A view making use of it would look like the following:

<div class="pagination">
        {% if paginator.previous_page %}
          <a href="{{ page.base }}/page/{{ paginator.previous_page }}" class="previous">Previous</a>
        {% else %}
          <span class="previous">Previous</span>
        {% endif %}
        <span class="page_number ">Page: {{ paginator.page }} of {{ paginator.total_pages }}</span>
        {% if paginator.next_page %}
          <a href="{{ page.base }}/page/{{ paginator.next_page }}" class="next">Next</a>
        {% else %}
          <span class="next ">Next</span>
        {% endif %}
</div>

Creating Archive Pages

In a similar fashion, Jekyll doesn’t create pages or pagination for previous years or months. Even if they are not oft used by others, I find it useful myself to hit up my archives for previous years on occasion and laugh at the random work I did for my undergraduate.

I created the following plugin to generate indexes at /{year} /{year}/{month} and /{year}/page/{pageNo} and /{year}/{month}/page/{pageNo} if necessary.

module Jekyll

  class ArchiveGenerator < Generator
    safe true
    def generate(site)
      if site.layouts.key? 'archive_index'
        site.posts.group_by{ |c| {"month" => c.date.month, "year" => c.date.year} }.each do |period, posts|
          posts = posts.sort_by { |p| -p.date.to_f }
          archive_dir = File.join(period["year"].to_s(), "%02d" % period["month"].to_s())
          paginate(site, archive_dir, posts)
        end
        site.posts.group_by{ |c| {"year" => c.date.year} }.each do |period, posts|
          posts = posts.sort_by { |p| -p.date.to_f }
          archive_dir = period["year"].to_s()
          paginate(site, archive_dir, posts)
        end
      end
    end

    def paginate(site, dir, posts)
      pages = Pager.calculate_pages(posts, site.config['paginate'].to_i)
      (1..pages).each do |num_page|
        pager = Pager.new(site, num_page, posts, pages)
        archive_path = "/" + dir
        path = archive_path
        if num_page > 1
          path += "/page/#{num_page}"
        end
        newpage = ArchiveIndex.new(site, site.source, path, archive_path, posts)
        newpage.pager = pager
        site.pages << newpage

      end
    end
  end

  class ArchiveIndex < Page
    def initialize(site, base, dir, base_path, posts)
      @site = site
      @base = base
      @dir = dir
      @name = 'index.html'

      self.process(@name)
      self.read_yaml(File.join(base, '_layouts'), 'archive_index.html')
      self.data['base'] = base_path
    end
  end
end

The layout of the paginator for the archives is the same as that for categories and tags.

That’s it for my jekyll roundup, I other long time wordpress users find this information useful.