pattex / jekyll-tagging

Jekyll plugin to automatically generate a tag cloud and tag pages.
385 stars 76 forks source link

Request: 2 Mods #1

Closed bbohling closed 13 years ago

bbohling commented 13 years ago

I'm not a ruby expert otherwise I'm sure I could easily do this myself. How hard would it be to make the following changes:

  1. Change the tag page creation so instead of generating paths like /tag/programming.html it creating paths like /tag/programming/index.html (so one could use pretty urls /tag/programming).
  2. Change to use Jekyll's default Categories "section" instead of creating a new "section" called tags. Just want to align with Jekyll core as much as possible.

Sorry if these are dumb requests...I'm a newbie. ;)

pattex commented 13 years ago

Hi ebohling,

sorry for the late answer.

I thought about these /tag/<tagname>/index.html urls, like for instance permalink: pretty generates, too. But i decided to name files corresponding to their contents and use (in my/our setup) apache's Content Negotiation to make the file extensions needless. But i should put this, as a configurable feature, on my to do list.

I used 'tags' for the tag cloud, because i think there are some differences in the meaning of tags and categories. But the plugin doesn't invent 'tags' for posts. This is already implemented in jekyll. The jekyll-tagging plugin just uses this.

bbohling commented 13 years ago

thanks for the very thoughtful response...and i agree with your logic. i was unsuccessful at changing the .htaccess to handle the no extensions, but it's not a big deal...i can let it go. however, if you were to add the feature i'd definitely use it. ;)

thanks for the excellent plug-in!

http://brandonbohling.com

pattex commented 13 years ago

There is something i forgot. Maybe, because looked at this to long ago. When you use permalink: pretty in your _config.yaml, you should get your urls. But other permalink definitions will be ignored and lead to tag/tagname.html. And now, this is on my to do list ;)

bbohling commented 13 years ago

ahhhh, so maybe that was my issue. i had: permalink: /:year/:month/:day/:title. which permalink: pretty is really the same thing, but with the added bonus of having your tag plugin have "pretty" URLs too. at least it appears that way in my 60 second test. ;)

bbohling commented 13 years ago

actually if i use permalink: pretty the tags page no longer works because it still generates a link with .html in it. if i manually remove the html extension from the URL then the tag page successfully loads. so i guess until the code generated by "{{ page | tags }}" is updated to remove the HTML extension i will have to keep permalink set to /:year/:month/:day/:title and be content with an HTML extension on the tag pages (but no other page).

bbohling commented 13 years ago

actually i just fixed it by modifying line 86 in your tagging.rb file. simply removed the .html, so the line now looks like:

"/#{dir}/#{ERB::Util.u(tag)}"

OMG, just found out another "glitch". if i switch to permalink: pretty and remove the .html extension in your plugin things are mostly good. however, that changes the post url to include tags so /year/month/date/title becomes /tags/year/month/date/title which means all my post cross references break, which would take too long to fix. ick.

i guess i'm living with the defaults for now. until i figure out how to keep the permalink set to /year/month/date/title (no extension) and make tags behave the same way.

i'm guessing i'd do this by changing the code below to generate a folder with tag name, then index.html (similar to the way archives work) so it would become: /tag/[tagname]/index.html so the url would not need to contain index.html

def new_tag_page(site, base, dir, tag, posts)
  TagPage.new(site, base, dir, "#{tag}.html", {
    'layout' => @tag_page_layout,
    'posts'  => posts,
  })
end
bbohling commented 13 years ago

this works, but i'm sure any ruby developer would be appalled by line 59...

require 'nuggets/range/quantile' require 'erb'

module Jekyll

class Tagger < Generator

safe true

DEFAULT_TAG_PAGE_DIR = 'tag'

def generate(site)
  @tag_page_dir    = site.config['tag_page_dir'] || DEFAULT_TAG_PAGE_DIR
  @tag_page_layout = site.config['tag_page_layout']

  generate_tag_pages(site) if @tag_page_layout
  site.config.update({ 'tag_data' => calculate_tag_cloud(site) })
end

# Generates a page per tag and adds them to all the pages of +site+.
# A <tt>tag_page_layout</tt> have to be defined in your <tt>_config.yml</tt>
# to use this.
def generate_tag_pages(site)
  site.tags.each { |tag, posts|
    site.pages << new_tag_page(site, site.source, @tag_page_dir, tag, posts.sort.reverse)
  }
end

def new_tag_page(site, base, dir, tag, posts)
  TagPage.new(site, base, dir + '/' + tag, "index.html", {
    'layout' => @tag_page_layout,
    'posts'  => posts,
  })
end

# Calculates the css class of every tag for a tag cloud. The possible
# classes are: set-1..set-5.
#
# [[<TAG>, <CLASS>], ...]
def calculate_tag_cloud(site, num = 5)
  tags = site.tags.map { |tag, posts| [tag, posts.size] }.sort
  range = 1..tags.map { |_, size| size }.max unless tags.empty?

  tags.map { |tag, size| [tag, range.quantile(size, num)] }
end

end

class TagPage < Page

def initialize(site, base, dir, name, data = {})
  self.content = data.delete('content') || ''
  self.data    = data

  dir = dir[-1, 1] == '/' ? dir : '/' + dir

  super(site, base, dir, name)

  self.data['tag'] = dir[5,30]
end

def read_yaml(_, __)
  # Do nothing
end

end

module Filters

def tag_cloud(site)
  dir = site['tag_page_dir']

  site['tag_data'].map { |tag, set|
    tag_link(tag, tag_url(tag, dir), { :class => "set-#{set}" })
  }.join(' ')
end

def tag_link(tag, url = tag_url(tag), html_opts = nil)
  unless html_opts.nil?
    html_opts = ' ' + html_opts.map { |k, v| %Q{#{k}="#{v}"} }.join(' ')
  end
  %Q{<a href="#{url}"#{html_opts}>#{tag}</a>}
end

def tag_url(tag, dir = Tagger::DEFAULT_TAG_PAGE_DIR)
  "/#{dir}/#{ERB::Util.u(tag)}"
end

def tags(obj)
  tags = obj['tags'][0].is_a?(Array) ? obj['tags'].map{ |t| t[0]} : obj['tags']
  tags.map { |t| tag_link(t, tag_url(t)) if t.is_a?(String) }.compact.join(', ')
end

end

end

pattex commented 13 years ago

o_O That's a lot of stuff. Like i said, it's on my list. But, of course you are welcome to fork the repository and make your changes.

pattex commented 13 years ago

Ok, i just pushed a new version of the plugin, which should solve the problem. URLs for tag pages should be pretty but only if there is permalink pretty defined in the config file. Other definitions should have no effect, like jekyll handles this stuff.

I could not reproduce the broken post link problem. Maybe there was little relative-url-foobar?

I hope, this solution fits your needs. If you have any more issues, please post. :)

bbohling commented 13 years ago

so i finally got a chance to test out your update. now the tagging works like i want, but i'm not a fan how archiving works...maybe it is a "feature" of jekyll. instead of a blog post having a url like:

/technology/2011/03/06/Text-File-Revolution/

it now looks like:

/2011/03/06/Text-File-Revolution/

Looks like this is a behavior of permalink: pretty. Doh. Definitely not a fan. If I get time I'll look at your updated code and see if I can update any of my "hacks" with your code. The edits I made gave me both the tag folders (like your latest update provided) but also kept my post URLs without the tag details.

Thanks so much for your efforts! If I knew Ruby better I'd give you code, but I'm just a hack. :)