Commit fc5d7084b765d96a4733137292fde96bfcdad623
1 parent
13851777
Exists in
master
and in
28 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 | 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
app/controllers/public/profile_controller.rb
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 < 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 | ... | ... |
| ... | ... | @@ -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 | 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 | ... | ... |
| ... | ... | @@ -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
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 < 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 | ... | ... |
| ... | ... | @@ -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 | ... | ... |