module Taginator::Tags include Radiant::Taggable include ActionView::Helpers::TextHelper class TagError < StandardError; end desc %{ Expands if a
call would return items. Takes the same options as the 'tagged' tag. The
is also available. } tag 'if_tagged' do |tag| if tag.attr[:with] tag.locals.tagged_results = find_with_tag_options(tag) tag.expand unless tag.locals.tagged_results.empty? else tag.expand unless tag.locals.page.tag_list.empty? end end tag 'unless_tagged' do |tag| if tag.attr[:with] tag.expand if find_with_tag_options(tag).empty? else tag.expand if tag.locals.page.tag_list.empty? end end desc %{ Find all pages with certain tags, within in an optional scope. Additionally, you may set with_any to true to select pages that have any of the listed tags (opposed to all listed tags which is the provided default). *Usage:*
...
} tag 'tagged' do |tag| tag.attr[:exclude_id] ||= tag.locals.page.id tag.locals.tagged_results ||= find_with_tag_options(tag) tag.locals.tagged_results.map do |page| tag.locals.page = page tag.expand end end desc %{ Find all pages related to the current page, based on all or any of the current page's tags. A scope attribute may be given to limit results to a certain site area. *Usage:*
...
} tag "related_by_tags" do |tag| options = tag.attr.slice(:offset, :limit, :order).symbolize_keys results = tag.locals.page.find_related_on_categories(options).to_a return if results.size < 1 if scope = tag.attr[:scope].presence results.reject! {|page| not page.url.starts_with?(scope) } end results.map do |page| tag.locals.page = page tag.locals.first = results.first == page tag.expand end end tag "if_has_related_by_tags" do |tag| options = tag.attr.slice(:offset, :limit, :order).symbolize_keys results = tag.locals.page.find_related_on_categories(options) tag.expand if results.size > 0 end tag "related_by_tags:if_first" do |tag| tag.expand if tag.locals.first end desc %{ Render a Tag cloud The results_page attribute will default to #{Radiant::Config['tags.results_page_url']} * :start_at - Restrict the tags to those created after a certain time * :end_at - Restrict the tags to those created before a certain time * :conditions - A piece of SQL conditions to add to the query * :limit - The maximum number of tags to return * :order - A piece of SQL to order by. Eg 'tags.count desc' or 'taggings.created_at desc' * :at_least - Exclude tags with a frequency less than the given value * :at_most - Exclude tags with a frequency greater than the given value *Usage:*
} tag "tag_cloud" do |tag| options = tag.attr.except(:scope, :results_page).symbolize_keys tags = tag.locals.page.class.tag_counts_on(:categories, options) tags = filter_tags_to_url_scope(tags, tag.attr['scope']) unless tag.attr['scope'].nil? content_tag :ol, :class => :tag_cloud do if tags.length > 0 build_tag_cloud(tags, %w(size1 size2 size3 size4 size5 size6 size7 size8 size9)) do |t, cloud_class, amount| puts t, cloud_class, amount content_tag :li, :class => cloud_class do content_tag(:span, pluralize(amount, 'page is', 'pages are') << ' tagged with ') + link_to(t, results_page(tag) << t, :class => :tag) end end else return "

No tags found.

" end end end desc %{ Render a Tag list, more for 'categories'-ish usage, i.e.: Cats (2) Logs (1) ... The results_page attribute will default to #{Radiant::Config['tags.results_page_url']} *Usage:*
} tag "tag_cloud_list" do |tag| options = {} tags = tag.locals.page.class.all_tag_counts(options).order('count desc') tags = filter_tags_to_url_scope(tags, tag.attr[:scope]) unless tag.attr[:scope].nil? results_page = tag.attr[:results_page] || Radiant::Config['tags.results_page_url'] content_tag :ul, :class => :tag_list do if tags.length > 0 build_tag_cloud(tags, %w(size1 size2 size3 size4 size5 size6 size7 size8 size9)) do |t, cloud_class, amount| content_tag :li, :class => cloud_class do link_to("#{t} (#{amount})", results_page(tag) << t, :class => :tag) end end else return "

No tags found.

" end end end desc "List the current page's tags" tag "tag_list" do |tag| tag.locals.page.tag_list.map{|t| link_to(t, results_page(tag) << t, :class => :tag)}.join(", ") end desc "List the current page's tagsi as technorati tags. this should be included in the body of a post or in your rss feed" tag "tag_list_technorati" do |tag| tag.locals.page.tag_list.map{|t| link_to(t, 'http://technorati.com/tag/' << t, :rel => :tag)}.join(", ") end tag "tags" do |tag| tag.expand end #desc "Iterates over the tags of the current page" tag "tags:each" do |tag| tag.locals.page.categories.map do |category| tag.locals.category = category tag.expand end end tag "tags:each:name" do |tag| tag.locals.category.name end tag "tags:each:link" do |tag| name = tag.locals.category.name link_to name, results_page(tag) << name, :class => 'tag' end #desc "Set the scope for all tags in the database" tag "all_tags" do |tag| tag.expand end desc %{ Iterates through each tag and allows you to specify the order: by popularity or by name. The default is by name. You may also limit the search; the default is 5 results. Usage:
...
} tag "all_tags:each" do |tag| options = tag.attr.slice(:order, :limit).symbolize_keys if names = tag.attr[:names] names = names.split(",").map{|t| t.strip } options[:conditions] = ["name IN (?)", names] if names.length > 0 end Page.all_tag_counts(options).map do |t| tag.locals.tag = t tag.expand end end desc "Renders the tag's name" tag "all_tags:each:name" do |tag| tag.locals.tag.name end tag "all_tags:each:link" do |tag| name = tag.locals.tag.name link_to tag.expand.presence || name, results_page(tag) << name, tag.attr.reverse_merge(:class => 'tag') end desc "Set the scope for the tag's pages" tag "all_tags:each:pages" do |tag| tag.expand end desc "Iterates through each page" tag "all_tags:each:pages:each" do |tag| Page.tagged_with(tag.locals.tag.name, tag.attr.symbolize_keys).map do |page| tag.locals.page = page tag.expand end end private def build_tag_cloud(tag_cloud, style_list) max, min = 0, 0 counts = tag_cloud.map(&:count) min, max = counts.min, counts.max divisor = ((max - min) / style_list.size) + 1 tag_cloud.map do |tag| yield tag.name, style_list[(tag.count - min) / divisor], tag.count end end def tag_item_url(name) "#{Radiant::Config['tags.results_page_url']}/#{name}" end def find_with_tag_options(tag) options = tagged_with_options(tag) with_any = tag.attr[:with_any] || false scope_attr = tag.attr[:scope] || '/' raise TagError, "`tagged' tag must contain a `with' attribute." unless (tag.attr['with'] || tag.locals.page.class_name = TagSearchPage) ttag = tag.attr['with'] || @request.parameters[:tag] scope = scope_attr == 'current_page' ? Page.find_by_url(@request.request_uri) : Page.find_by_url(scope_attr) return "The scope attribute must be a valid url to an existing page." if scope.nil? || scope.class_name.eql?('FileNotFoundPage') page_ids = Page.tagged_with(ttag, options.merge(:any => with_any)).map do |page| page.id if page.ancestors.include?(scope) || page == scope end.compact Page.find_all_by_id page_ids, options end def tagged_with_options(tag) options = {} [:limit, :offset].each do |symbol| if number = tag.attr[symbol] if number =~ /^\d{1,4}$/ options[symbol] = number.to_i else raise TagError.new("`#{symbol}' attribute of `each' tag must be a positive number between 1 and 4 digits") end end end by = (tag.attr[:by] || 'published_at').strip order = (tag.attr[:order] || 'desc').strip order_string = '' if self.attributes.keys.include?(by) order_string << by else raise TagError.new("`by' attribute of `each' tag must be set to a valid field name") end if order =~ /^(asc|desc)$/i order_string << " #{$1.upcase}" else raise TagError.new(%{`order' attribute of `each' tag must be set to either "asc" or "desc"}) end options[:order] = order_string status = (tag.attr[:status] || 'published').downcase exclude = tag.attr[:exclude_id] ? "AND pages.id != #{tag.attr[:exclude_id]}" : "" unless status == 'all' stat = Status[status] unless stat.nil? options[:conditions] = ["(virtual = ?) and (status_id = ?) #{exclude} and (published_at <= ?)", false, stat.id, Time.current] else raise TagError.new(%{`status' attribute of `each' tag must be set to a valid status}) end else options[:conditions] = ["virtual = ? #{exclude}", false] end options end def filter_tags_to_url_scope(tags, scope) tags.select{ |tag| tag.taggings.any?{ |tagging| tagging.taggable.url.starts_with? scope } } end delegate :template, :to => :response # delegate :link_to, :to => :template, :allow_nil => true delegate :content_tag, :to => :template def link_to *args args.second.gsub!(' ', '+') if args.second.respond_to?(:gsub) template.send(:link_to, *args) end def results_page(tag) (tag.attr['results_page'] || Radiant::Config['tags.results_page_url']).dup << "/" end end