Commit fc5d7084b765d96a4733137292fde96bfcdad623

Authored by AntonioTerceiro
1 parent 13851777

ActionItem152: implenting a tags blocks


git-svn-id: https://svn.colivre.coop.br/svn/noosfero/trunk@1259 3f533792-8f58-4932-b0fe-aaf55b0a4547
app/controllers/application.rb
... ... @@ -53,7 +53,7 @@ class ApplicationController < ActionController::Base
53 53 def render_not_found(path = nil)
54 54 @path ||= request.path
55 55 # raise "#{@path} not found"
56   - render(:file => File.join(RAILS_ROOT, 'app', 'views', 'shared', 'not_found.rhtml'), :layout => 'not_found', :status => 404) && fal
  56 + render(:file => File.join(RAILS_ROOT, 'app', 'views', 'shared', 'not_found.rhtml'), :layout => 'not_found', :status => 404)
57 57 end
58 58  
59 59 def user
... ...
app/controllers/my_profile/profile_design_controller.rb
... ... @@ -3,7 +3,7 @@ class ProfileDesignController < BoxOrganizerController
3 3 needs_profile
4 4  
5 5 def available_blocks
6   - @available_blocks ||= [ Block, ArticleBlock ]
  6 + @available_blocks ||= [ Block, ArticleBlock, TagsBlock ]
7 7 end
8 8  
9 9 end
... ...
app/controllers/public/profile_controller.rb
1 1 class ProfileController < ApplicationController
2 2 needs_profile
  3 +
  4 + helper TagsHelper
  5 +
  6 + def index
  7 + @tags = profile.tags
  8 + end
  9 +
  10 + def tag
  11 + @tag = profile.content_tagged_with(params[:id])
  12 + end
  13 +
3 14 end
... ...
app/controllers/public/search_controller.rb
1 1 class SearchController < ApplicationController
2 2  
  3 + helper TagsHelper
  4 +
3 5 SEARCHES = []
4 6  
5 7 def self.search(&block)
... ... @@ -44,7 +46,10 @@ class SearchController &lt; ApplicationController
44 46 end
45 47  
46 48 def tags
47   - @tags = Tag.find(:all)
  49 + @tags = Tag.find(:all).inject({}) do |memo,tag|
  50 + memo[tag.name] = tag.taggings.count
  51 + memo
  52 + end
48 53 end
49 54  
50 55 def tag
... ...
app/helpers/tags_helper.rb 0 → 100644
... ... @@ -0,0 +1,51 @@
  1 +module TagsHelper
  2 +
  3 + module Cloud
  4 + MAX_SIZE = 32
  5 + MIN_SIZE = 12
  6 + end
  7 +
  8 + # <tt>tags</tt> must be a hash where the keys are tag names and the values
  9 + # the count of elements tagged with the tag, as returned by
  10 + # Profile#find_tagged_with. If not tags were returned, just returns
  11 + # _('No tags yet.')
  12 + #
  13 + # <tagname_option> must be a symbol representing the key to be inserted in
  14 + # <tt>url</tt> with the tag name as value, if <tt>url</tt> is a Hash. If
  15 + # <tt>url_options</tt> is a String, then the tag name is just appended to it.
  16 + #
  17 + # Example:
  18 + #
  19 + # tag_cloud({ 'first-tag' => 10, 'second-tag' => 2, 'third-tag' => 1 }, :id, { :action => 'show_tag' })
  20 + #
  21 + # <tt>options</tt> can include one or more of the following:
  22 + #
  23 + # * <tt>:max_size</tt>: font size for the tag with largest count
  24 + # * <tt>:min_size</tt>: font size for the tag with smallest count
  25 + #
  26 + # The algorithm for generating the different sizes and positions is a
  27 + # courtesy of Aurelio: http://www.colivre.coop.br/Aurium/Nuvem
  28 + # (pt_BR only).
  29 + def tag_cloud(tags, tagname_option, url, options = {})
  30 +
  31 + return _('No tags yet.') if tags.empty?
  32 +
  33 + max_size = options[:max_size] || Cloud::MAX_SIZE
  34 + min_size = options[:min_size] || Cloud::MIN_SIZE
  35 +
  36 + delta = max_size - min_size
  37 + max = tags.values.max.to_f
  38 +
  39 + tags.map do |tag,count|
  40 + v = count.to_f / max
  41 + style = <<-EOS
  42 + font-size: #{ (v * delta).round + min_size }px;
  43 + top: #{ -4 - (v * 4).round }px;
  44 + EOS
  45 + destination = url.kind_of?(Hash) ? url_for(url.merge(tagname_option => tag)) : (url.to_s + tag)
  46 +
  47 + link_to "#{tag} (#{count})", destination, :style => style
  48 + end.join("\n")
  49 + end
  50 +
  51 +end
... ...
app/models/block.rb
1 1 class Block < ActiveRecord::Base
  2 +
  3 + # to be able to generate HTML
  4 + include ActionView::Helpers::TagHelper
  5 +
2 6 acts_as_list :scope => :box
3 7 belongs_to :box
4 8  
... ...
app/models/profile.rb
... ... @@ -187,4 +187,23 @@ class Profile &lt; ActiveRecord::Base
187 187 options
188 188 end
189 189  
  190 + def tags(public_only = false)
  191 + totals = {}
  192 + articles.each do |article|
  193 + article.tags.each do |tag|
  194 + if totals[tag.name]
  195 + totals[tag.name] += 1
  196 + else
  197 + totals[tag.name] = 1
  198 + end
  199 + end
  200 + end
  201 + totals
  202 + end
  203 +
  204 + def find_tagged_with(tag)
  205 + # FIXME: this can be SLOW
  206 + articles.select {|item| item.tags.map(&:name).include?(tag) }
  207 + end
  208 +
190 209 end
... ...
app/models/tags_block.rb 0 → 100644
... ... @@ -0,0 +1,15 @@
  1 +class TagsBlock < Block
  2 +
  3 + include TagsHelper
  4 + include ActionView::Helpers::UrlHelper
  5 +
  6 + def self.description
  7 + _('List count of contents by tag')
  8 + end
  9 +
  10 + def content(main_content = nil)
  11 + content_tag('h3', _('Tags'), :class => 'block-title') +
  12 + tag_cloud(owner.tags, :id, owner.generate_url(:controller => 'profile', :action => 'tag') + '/', :max_size => 20, :min_size => 10)
  13 + end
  14 +
  15 +end
... ...
app/views/profile/index.rhtml
... ... @@ -13,4 +13,9 @@
13 13 <%# FIXME %>
14 14 <li><%= link_to_function _('Friends'), 'alert("not yet")' %></li>
15 15 <li><%= link_to_function _('Communities'), 'alert("not yet")' %></li>
  16 +
  17 + <li>
  18 + <%= _('Tags:') %>
  19 + <%= tag_cloud @tags, :id, { :action => 'tag' }, :max_size => 18, :min_size => 10%>
  20 + </li>
16 21 </ul>
... ...
app/views/search/tags.rhtml
1 1 <h2><%= _('Tag cloud') %></h2>
2 2  
3   -<% @tags.each do |t| %>
4   - <%= link_to("#{t.name} (#{t.taggings.count})", { :action => 'tag', :tag => t.name }, :style => "font-size: #{14 + 2 * t.taggings.count}px;") %>
5   -<% end %>
  3 +<%= tag_cloud(@tags, :tag, :action => 'tag') %>
  4 +
... ...
config/routes.rb
... ... @@ -44,7 +44,7 @@ ActionController::Routing::Routes.draw do |map|
44 44 map.controllers 'block/:profile/:controller/:action/:id', :controller => Noosfero.pattern_for_controllers_from_design_blocks
45 45  
46 46 # public profile information
47   - map.profile 'profile/:profile/:action', :controller => 'profile', :action => 'index'
  47 + map.profile 'profile/:profile/:action/:id', :controller => 'profile', :action => 'index'
48 48  
49 49 ######################################################
50 50 ## Controllers that are profile-specific (for profile admins )
... ...
test/test_helper.rb
... ... @@ -56,6 +56,13 @@ class Test::Unit::TestCase
56 56 admin_user.login
57 57 end
58 58  
  59 + def create_environment(domainname)
  60 + env = Environment.create!(:name => domainname)
  61 + env.domains << Domain.new(:name => domainname)
  62 + env.save!
  63 + env
  64 + end
  65 +
59 66 def create_user(name)
60 67 User.create!(:login => name,
61 68 :email => name + '@noosfero.org',
... ...
test/unit/profile_test.rb
... ... @@ -231,21 +231,43 @@ class ProfileTest &lt; Test::Unit::TestCase
231 231 end
232 232  
233 233 should 'provide url to itself' do
234   - profile = Profile.create!(:name => "Test Profile", :identifier => 'testprofile', :environment_id => create_environment('colivre.net').id)
  234 + profile = Profile.create!(:name => "Test Profile", :identifier => 'testprofile', :environment_id => create_environment('mycolivre.net').id)
235 235  
236   - assert_equal 'http://colivre.net/testprofile', profile.url
  236 + assert_equal 'http://mycolivre.net/testprofile', profile.url
237 237 end
238 238  
239 239 should 'generate URL' do
240   - profile = Profile.create!(:name => "Test Profile", :identifier => 'testprofile', :environment_id => create_environment('colivre.net').id)
  240 + profile = Profile.create!(:name => "Test Profile", :identifier => 'testprofile', :environment_id => create_environment('mycolivre.net').id)
241 241  
242   - assert_equal 'http://colivre.net/profile/testprofile/friends', profile.generate_url(:controller => 'profile', :action => 'friends')
  242 + assert_equal 'http://mycolivre.net/profile/testprofile/friends', profile.generate_url(:controller => 'profile', :action => 'friends')
243 243 end
244 244  
245 245 should 'provide URL options' do
246   - profile = Profile.create!(:name => "Test Profile", :identifier => 'testprofile', :environment_id => create_environment('colivre.net').id)
  246 + profile = Profile.create!(:name => "Test Profile", :identifier => 'testprofile', :environment_id => create_environment('mycolivre.net').id)
247 247  
248   - assert_equal({:host => 'colivre.net', :profile => 'testprofile'}, profile.url_options)
  248 + assert_equal({:host => 'mycolivre.net', :profile => 'testprofile'}, profile.url_options)
  249 + end
  250 +
  251 + should 'list tags for profile' do
  252 + profile = Profile.create!(:name => "Test Profile", :identifier => 'testprofile')
  253 + profile.articles.build(:name => 'first', :tag_list => 'first-tag').save!
  254 + profile.articles.build(:name => 'second', :tag_list => 'first-tag, second-tag').save!
  255 + profile.articles.build(:name => 'third', :tag_list => 'first-tag, second-tag, third-tag').save!
  256 +
  257 + assert_equal({ 'first-tag' => 3, 'second-tag' => 2, 'third-tag' => 1 }, profile.tags)
  258 +
  259 + end
  260 +
  261 + should 'find content tagged with given tag' do
  262 + profile = Profile.create!(:name => "Test Profile", :identifier => 'testprofile')
  263 + first = profile.articles.build(:name => 'first', :tag_list => 'first-tag'); first.save!
  264 + second = profile.articles.build(:name => 'second', :tag_list => 'first-tag, second-tag'); second.save!
  265 + third = profile.articles.build(:name => 'third', :tag_list => 'first-tag, second-tag, third-tag'); third.save!
  266 + profile.reload
  267 +
  268 + assert_equivalent [ first, second, third], profile.find_tagged_with('first-tag')
  269 + assert_equivalent [ second, third ], profile.find_tagged_with('second-tag')
  270 + assert_equivalent [ third], profile.find_tagged_with('third-tag')
249 271 end
250 272  
251 273 private
... ...
test/unit/tags_block_test.rb 0 → 100644
... ... @@ -0,0 +1,26 @@
  1 +require File.dirname(__FILE__) + '/../test_helper'
  2 +
  3 +class TagsBlockTest < Test::Unit::TestCase
  4 +
  5 + def setup
  6 + user = create_user('testinguser').person
  7 + user.articles.build(:name => 'article 1', :tag_list => 'first-tag').save!
  8 + user.articles.build(:name => 'article 2', :tag_list => 'first-tag, second-tag').save!
  9 + user.articles.build(:name => 'article 3', :tag_list => 'first-tag, second-tag, third-tag').save!
  10 +
  11 + box = Box.create!(:owner => user)
  12 + @block = TagsBlock.create!(:box => box)
  13 + end
  14 + attr_reader :block
  15 +
  16 + should 'describe itself' do
  17 + assert_not_equal Block.description, TagsBlock.description
  18 + end
  19 +
  20 + should 'generate links to tags' do
  21 + assert_match /profile\/testinguser\/tag\/first-tag/, block.content
  22 + assert_match /profile\/testinguser\/tag\/second-tag/, block.content
  23 + assert_match /profile\/testinguser\/tag\/third-tag/, block.content
  24 + end
  25 +
  26 +end
... ...