Commit fc5d7084b765d96a4733137292fde96bfcdad623
1 parent
13851777
Exists in
master
and in
29 other branches
ActionItem152: implenting a tags blocks
git-svn-id: https://svn.colivre.coop.br/svn/noosfero/trunk@1259 3f533792-8f58-4932-b0fe-aaf55b0a4547
Showing
14 changed files
with
177 additions
and
13 deletions
Show diff stats
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 < ApplicationController | @@ -44,7 +46,10 @@ class SearchController < 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 |
@@ -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
app/models/profile.rb
@@ -187,4 +187,23 @@ class Profile < ActiveRecord::Base | @@ -187,4 +187,23 @@ class Profile < 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 |
@@ -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 < Test::Unit::TestCase | @@ -231,21 +231,43 @@ class ProfileTest < 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 |
@@ -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 |