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,7 +53,7 @@ class ApplicationController < ActionController::Base
53 def render_not_found(path = nil) 53 def render_not_found(path = nil)
54 @path ||= request.path 54 @path ||= request.path
55 # raise "#{@path} not found" 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 end 57 end
58 58
59 def user 59 def user
app/controllers/my_profile/profile_design_controller.rb
@@ -3,7 +3,7 @@ class ProfileDesignController < BoxOrganizerController @@ -3,7 +3,7 @@ class ProfileDesignController < BoxOrganizerController
3 needs_profile 3 needs_profile
4 4
5 def available_blocks 5 def available_blocks
6 - @available_blocks ||= [ Block, ArticleBlock ] 6 + @available_blocks ||= [ Block, ArticleBlock, TagsBlock ]
7 end 7 end
8 8
9 end 9 end
app/controllers/public/profile_controller.rb
1 class ProfileController < ApplicationController 1 class ProfileController < ApplicationController
2 needs_profile 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 end 14 end
app/controllers/public/search_controller.rb
1 class SearchController < ApplicationController 1 class SearchController < ApplicationController
2 2
  3 + helper TagsHelper
  4 +
3 SEARCHES = [] 5 SEARCHES = []
4 6
5 def self.search(&block) 7 def self.search(&block)
@@ -44,7 +46,10 @@ class SearchController &lt; ApplicationController @@ -44,7 +46,10 @@ class SearchController &lt; ApplicationController
44 end 46 end
45 47
46 def tags 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 end 53 end
49 54
50 def tag 55 def tag
app/helpers/tags_helper.rb 0 → 100644
@@ -0,0 +1,51 @@ @@ -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 class Block < ActiveRecord::Base 1 class Block < ActiveRecord::Base
  2 +
  3 + # to be able to generate HTML
  4 + include ActionView::Helpers::TagHelper
  5 +
2 acts_as_list :scope => :box 6 acts_as_list :scope => :box
3 belongs_to :box 7 belongs_to :box
4 8
app/models/profile.rb
@@ -187,4 +187,23 @@ class Profile &lt; ActiveRecord::Base @@ -187,4 +187,23 @@ class Profile &lt; ActiveRecord::Base
187 options 187 options
188 end 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 end 209 end
app/models/tags_block.rb 0 → 100644
@@ -0,0 +1,15 @@ @@ -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,4 +13,9 @@
13 <%# FIXME %> 13 <%# FIXME %>
14 <li><%= link_to_function _('Friends'), 'alert("not yet")' %></li> 14 <li><%= link_to_function _('Friends'), 'alert("not yet")' %></li>
15 <li><%= link_to_function _('Communities'), 'alert("not yet")' %></li> 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 </ul> 21 </ul>
app/views/search/tags.rhtml
1 <h2><%= _('Tag cloud') %></h2> 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,7 +44,7 @@ ActionController::Routing::Routes.draw do |map|
44 map.controllers 'block/:profile/:controller/:action/:id', :controller => Noosfero.pattern_for_controllers_from_design_blocks 44 map.controllers 'block/:profile/:controller/:action/:id', :controller => Noosfero.pattern_for_controllers_from_design_blocks
45 45
46 # public profile information 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 ## Controllers that are profile-specific (for profile admins ) 50 ## Controllers that are profile-specific (for profile admins )
test/test_helper.rb
@@ -56,6 +56,13 @@ class Test::Unit::TestCase @@ -56,6 +56,13 @@ class Test::Unit::TestCase
56 admin_user.login 56 admin_user.login
57 end 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 def create_user(name) 66 def create_user(name)
60 User.create!(:login => name, 67 User.create!(:login => name,
61 :email => name + '@noosfero.org', 68 :email => name + '@noosfero.org',
test/unit/profile_test.rb
@@ -231,21 +231,43 @@ class ProfileTest &lt; Test::Unit::TestCase @@ -231,21 +231,43 @@ class ProfileTest &lt; Test::Unit::TestCase
231 end 231 end
232 232
233 should 'provide url to itself' do 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 end 237 end
238 238
239 should 'generate URL' do 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 end 243 end
244 244
245 should 'provide URL options' do 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 end 271 end
250 272
251 private 273 private
test/unit/tags_block_test.rb 0 → 100644
@@ -0,0 +1,26 @@ @@ -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