Commit 841621ab0a17b37c13f2e8f3d9259bbd943c8d0e

Authored by Leandro Santos
Committed by Joenio Costa
1 parent 3c6a2941

Improving profile/communities listing and show a profile balloon when trigger is clicked

(ActionItem1587,ActionItem1588)
app/controllers/public/browse_controller.rb 0 → 100644
... ... @@ -0,0 +1,58 @@
  1 +class BrowseController < PublicController
  2 +
  3 + no_design_blocks
  4 +
  5 + FILTERS = %w(
  6 + more_recent
  7 + more_active
  8 + more_popular
  9 + )
  10 +
  11 + def people
  12 + @filter = filter
  13 + @title = self.filter_description(params[:action] + '_' + @filter )
  14 +
  15 + @results = @environment.people.send(@filter)
  16 +
  17 + if params[:query].blank?
  18 + @results = @results.paginate(:per_page => 27, :page => params[:page])
  19 + else
  20 + @results = @results.find_by_contents(params[:query]).paginate(:per_page => 27, :page => params[:page])
  21 + end
  22 + end
  23 +
  24 + def communities
  25 + @filter = filter
  26 + @title = self.filter_description(params[:action] + '_' + @filter )
  27 +
  28 + @results = @environment.communities.send(@filter)
  29 +
  30 + if params[:query].blank?
  31 + @results = @results.paginate(:per_page => 27, :page => params[:page])
  32 + else
  33 + @results = @results.find_by_contents(params[:query]).paginate(:per_page => 27, :page => params[:page])
  34 + end
  35 + end
  36 +
  37 + protected
  38 +
  39 + def filter
  40 + if FILTERS.include?(params[:filter])
  41 + params[:filter]
  42 + else
  43 + 'more_recent'
  44 + end
  45 + end
  46 +
  47 + def filter_description(str)
  48 + {
  49 + 'people_more_recent' => _('People more recent'),
  50 + 'people_more_active' => _('People more active'),
  51 + 'people_more_popular' => _('People more popular'),
  52 + 'communities_more_recent' => _('Communities more recent'),
  53 + 'communities_more_active' => _('Communities more active'),
  54 + 'communities_more_popular' => _('Communities more popular'),
  55 + }[str] || str
  56 + end
  57 +
  58 +end
... ...
app/helpers/application_helper.rb
... ... @@ -479,9 +479,38 @@ module ApplicationHelper
479 479 :class => the_class
480 480 end
481 481  
  482 + def links_for_balloon(profile)
  483 + if environment.enabled?(:show_balloon_with_profile_links_when_clicked)
  484 + if profile.kind_of?(Person)
  485 + [
  486 + {_('Home Page') => url_for(profile.url)},
  487 + {_('Profile') => url_for(profile.public_profile_url)},
  488 + {_('Friends') => url_for(:controller => :profile, :action => :friends, :profile => profile.identifier)},
  489 + {_('Communities') => url_for(:controller => :profile, :action => :communities, :profile => profile.identifier)}
  490 + ]
  491 + elsif profile.kind_of?(Community)
  492 + [
  493 + {_('Home Page') => url_for(profile.url)},
  494 + {_('Profile') => url_for(profile.public_profile_url)},
  495 + {_('Members') => url_for(:controller => :profile, :action => :members, :profile => profile.identifier)},
  496 + {_('Agenda') => url_for(:controller => :profile, :action => :events, :profile => profile.identifier)}
  497 + ]
  498 + elsif profile.kind_of?(Enterprise)
  499 + [
  500 + {_('Home Page') => url_for(profile.url)},
  501 + {_('Products') => catalog_path(profile.identifier)},
  502 + {_('Members') => url_for(:controller => :profile, :action => :members, :profile => profile.identifier)},
  503 + {_('Agenda') => url_for(:controller => :profile, :action => :events, :profile => profile.identifier)}
  504 + ]
  505 + else
  506 + []
  507 + end
  508 + end
  509 + end
  510 +
482 511 # displays a link to the profile homepage with its image (as generated by
483 512 # #profile_image) and its name below it.
484   - def profile_image_link( profile, size=:portrait, tag='li' )
  513 + def profile_image_link( profile, size=:portrait, tag='li', extra_info = nil )
485 514 if profile.class == Person
486 515 name = profile.short_name
487 516 city = content_tag 'span', content_tag( 'span', profile.city, :class => 'locality' ), :class => 'adr'
... ... @@ -489,11 +518,14 @@ module ApplicationHelper
489 518 name = profile.short_name
490 519 city = ''
491 520 end
  521 + extra_info = extra_info.nil? ? '' : content_tag( 'span', extra_info, :class => 'extra_info' )
  522 + links = links_for_balloon(profile)
492 523 content_tag tag,
  524 + (environment.enabled?(:show_balloon_with_profile_links_when_clicked) ? link_to( content_tag( 'span', _('Profile links')), '#', :onclick => "toggleSubmenu(this, '#{profile.short_name}', #{links.to_json}); return false", :class => 'menu-submenu-trigger' ) : "") +
493 525 link_to(
494 526 content_tag( 'span', profile_image( profile, size ), :class => 'profile-image' ) +
495 527 content_tag( 'span', h(name), :class => ( profile.class == Person ? 'fn' : 'org' ) ) +
496   - city + profile_sex_icon( profile ) + profile_cat_icons( profile ),
  528 + city + extra_info + profile_sex_icon( profile ) + profile_cat_icons( profile ),
497 529 profile.url,
498 530 :onclick => 'document.location.href = this.href', # work-arround for ie.
499 531 :class => 'profile_link url',
... ... @@ -1019,4 +1051,34 @@ module ApplicationHelper
1019 1051 ") : '')
1020 1052 end
1021 1053  
  1054 + def browse_people_menu
  1055 + links = [
  1056 + {_('More Recent') => url_for({:controller => 'browse', :action => 'people', :filter => 'more_recent'})},
  1057 + {_('More Active') => url_for({:controller => 'browse', :action => 'people', :filter => 'more_active'})},
  1058 + {_('More Popular') => url_for({:controller => 'browse', :action => 'people', :filter => 'more_popular'})}
  1059 + ]
  1060 + if logged_in?
  1061 + links.push(_('My friends') => url_for({:profile => current_user.login, :controller => 'friends'}))
  1062 + links.push(_('Invite friends') => url_for({:profile => current_user.login, :controller => 'invite', :action => 'friends'}))
  1063 + end
  1064 +
  1065 + link_to(content_tag(:span, _('People'), :class => 'icon-menu-people'), {:controller => "browse", :action => 'people'}, :id => 'submenu-people') +
  1066 + link_to(content_tag(:span, _('People Menu')), '#', :onclick => "toggleSubmenu(this,'',#{links.to_json})", :class => 'menu-submenu-trigger up', :id => 'submenu-people-trigger')
  1067 + end
  1068 +
  1069 + def browse_communities_menu
  1070 + links = [
  1071 + {_('More Recent') => url_for({:controller => 'browse', :action => 'communities', :filter => 'more_recent'})},
  1072 + {_('More Active') => url_for({:controller => 'browse', :action => 'communities', :filter => 'more_active'})},
  1073 + {_('More Popular') => url_for({:controller => 'browse', :action => 'communities', :filter => 'more_popular'})}
  1074 + ]
  1075 + if logged_in?
  1076 + links.push(_('My communities') => url_for({:profile => current_user.login, :controller => 'memberships'}))
  1077 + links.push(_('New community') => url_for({:profile => current_user.login, :controller => 'memberships', :action => 'new_community'}))
  1078 + end
  1079 +
  1080 + link_to(content_tag(:span, _('Communities'), :class => 'icon-menu-community'), {:controller => "browse", :action => 'communities'}, :id => 'submenu-communities') +
  1081 + link_to(content_tag(:span, _('Communities Menu')), '#', :onclick => "toggleSubmenu(this,'',#{links.to_json})", :class => 'menu-submenu-trigger up', :id => 'submenu-communities-trigger')
  1082 + end
  1083 +
1022 1084 end
... ...
app/models/enterprise.rb
... ... @@ -4,7 +4,7 @@ class Enterprise &lt; Organization
4 4  
5 5 N_('Enterprise')
6 6  
7   - has_many :products, :dependent => :destroy
  7 + has_many :products, :dependent => :destroy, :order => 'name ASC'
8 8  
9 9 extra_data_for_index :product_categories
10 10  
... ...
app/models/environment.rb
... ... @@ -108,6 +108,7 @@ class Environment &lt; ActiveRecord::Base
108 108 'enable_organization_url_change' => _("Allow organizations to change their URL"),
109 109 'admin_must_approve_new_communities' => _("Admin must approve creation of communities"),
110 110 'enterprises_are_disabled_when_created' => __('Enterprises are disabled when created'),
  111 + 'show_balloon_with_profile_links_when_clicked' => _('Show a balloon with profile links when a profile image is clicked'),
111 112 }
112 113 end
113 114  
... ...
app/models/person.rb
... ... @@ -8,6 +8,12 @@ class Person &lt; Profile
8 8  
9 9 has_many :requested_tasks, :class_name => 'Task', :foreign_key => :requestor_id, :dependent => :destroy
10 10  
  11 + named_scope :more_popular,
  12 + :select => "#{Profile.qualified_column_names}, count(friend_id) as total",
  13 + :group => Profile.qualified_column_names,
  14 + :joins => :friendships,
  15 + :order => "total DESC"
  16 +
11 17 after_destroy do |person|
12 18 Friendship.find(:all, :conditions => { :friend_id => person.id}).each { |friendship| friendship.destroy }
13 19 end
... ... @@ -286,4 +292,13 @@ class Person &lt; Profile
286 292 def relationships_cache_key
287 293 cache_key + '-profile-relationships'
288 294 end
  295 +
  296 + def more_popular_label
  297 + amount = self.friends.count
  298 + {
  299 + 0 => _('none'),
  300 + 1 => _('one friend')
  301 + }[amount] || _("%s friends") % amount
  302 + end
  303 +
289 304 end
... ...
app/models/profile.rb
... ... @@ -57,7 +57,23 @@ class Profile &lt; ActiveRecord::Base
57 57  
58 58 acts_as_taggable
59 59  
  60 + def self.qualified_column_names
  61 + Profile.column_names.map{|n| [Profile.table_name, n].join('.')}.join(',')
  62 + end
  63 +
60 64 named_scope :visible, :conditions => { :visible => true }
  65 + named_scope :more_recent, :order => "created_at DESC"
  66 + named_scope :more_popular,
  67 + :select => "#{Profile.qualified_column_names}, count(resource_id) as total",
  68 + :group => Profile.qualified_column_names,
  69 + :joins => :role_assignments,
  70 + :order => "total DESC"
  71 + named_scope :more_active,
  72 + :select => "#{Profile.qualified_column_names}, count(articles.id) as total, sum(articles.comments_count) as total_comments",
  73 + :joins => :articles,
  74 + :group => Profile.qualified_column_names,
  75 + :order => "total DESC, total_comments DESC",
  76 + :conditions => ["articles.created_at BETWEEN ? AND ?", 7.days.ago, DateTime.now]
61 77  
62 78 # FIXME ugly workaround
63 79 def self.human_attribute_name(attrib)
... ... @@ -675,6 +691,26 @@ private :generate_url, :url_options
675 691 cache_key + '-members-page-' + page
676 692 end
677 693  
  694 + def more_recent_label
  695 + _("Since: ")
  696 + end
  697 +
  698 + def more_active_label
  699 + amount = self.articles.count
  700 + {
  701 + 0 => _('none'),
  702 + 1 => _('one article')
  703 + }[amount] || _("%s articles") % amount
  704 + end
  705 +
  706 + def more_popular_label
  707 + amount = self.members.count
  708 + {
  709 + 0 => _('none'),
  710 + 1 => _('one member')
  711 + }[amount] || _("%s members") % amount
  712 + end
  713 +
678 714 protected
679 715  
680 716 def display_private_info_to?(user)
... ... @@ -684,4 +720,5 @@ private :generate_url, :url_options
684 720 (user == self) || (user.is_admin?(self.environment)) || user.is_admin?(self) || user.memberships.include?(self)
685 721 end
686 722 end
  723 +
687 724 end
... ...
app/views/browse/_display_results.rhtml 0 → 100644
... ... @@ -0,0 +1,19 @@
  1 +<div id="browse-results">
  2 +
  3 + <div class='browse-results-innerbox'>
  4 + <% if @results.empty? %>
  5 + <div class="browse-results-type-empty">
  6 + <div> <%= _('None') %> </div>
  7 + </div><!-- end class="browse-results-innerbox" -->
  8 + <% end %>
  9 + <ul class='common-profile-list-block'>
  10 + <% @results.each do |result| %>
  11 + <%= render :partial => partial_for_class(result.class), :locals => {:profile => result} %>
  12 + <% end %>
  13 + </ul>
  14 + <br style='clear: both;'>
  15 + </div>
  16 +
  17 + <br style="clear:both" />
  18 +</div><!-- end id="browse-results" -->
  19 +
... ...
app/views/browse/_profile.rhtml 0 → 100644
... ... @@ -0,0 +1 @@
  1 +<%= profile_image_link profile, :portrait, 'li', @filter == 'more_recent' ? profile.send(@filter + '_label') + show_date(profile.created_at) : profile.send(@filter + '_label') %>
... ...
app/views/browse/_search_form.rhtml 0 → 100644
... ... @@ -0,0 +1,10 @@
  1 +<% form_tag( { :controller => 'browse', :action => action}, :method => 'get', :class => 'search_form' ) do %>
  2 +
  3 + <div class="search-field">
  4 + <span class="formfield">
  5 + <%= text_field_tag 'query', @query, :size => 50 %>
  6 + </span>
  7 + <%= submit_button(:search, _('Search')) %>
  8 + </div>
  9 +
  10 +<% end %>
... ...
app/views/browse/communities.rhtml 0 → 100644
... ... @@ -0,0 +1,9 @@
  1 +<%= search_page_title( @title, { :query => @query} ) %>
  2 +
  3 +<%= render :partial => 'search_form', :locals => {:action => 'communities'} %>
  4 +
  5 +<%= render :partial => 'display_results' %>
  6 +
  7 +<%= will_paginate @results %>
  8 +
  9 +<br style="clear:both" />
... ...
app/views/browse/people.rhtml 0 → 100644
... ... @@ -0,0 +1,9 @@
  1 +<%= search_page_title( @title, { :query => @query} ) %>
  2 +
  3 +<%= render :partial => 'search_form', :locals => {:action => 'people'} %>
  4 +
  5 +<%= render :partial => 'display_results' %>
  6 +
  7 +<%= will_paginate @results %>
  8 +
  9 +<br style="clear:both" />
... ...
config/routes.rb
... ... @@ -51,6 +51,10 @@ ActionController::Routing::Routes.draw do |map|
51 51 # search
52 52 map.connect 'search/:action/*category_path', :controller => 'search'
53 53  
  54 + # Browse
  55 + map.connect 'browse/:action/:filter', :controller => 'browse'
  56 + map.connect 'browse/:action', :controller => 'browse'
  57 +
54 58 # events
55 59 map.events 'profile/:profile/events_by_day', :controller => 'events', :action => 'events_by_day', :profile => /#{Noosfero.identifier_format}/
56 60 map.events 'profile/:profile/events/:year/:month/:day', :controller => 'events', :action => 'events', :year => /\d*/, :month => /\d*/, :day => /\d*/, :profile => /#{Noosfero.identifier_format}/
... ...
features/balloon.feature 0 → 100644
... ... @@ -0,0 +1,62 @@
  1 +Feature: balloon
  2 + I want to view a balloon when mouse clicks on profile trigger
  3 +
  4 + Background:
  5 + Given I am on the homepage
  6 + Given the following users
  7 + | login | name |
  8 + | joaosilva | Joao Silva |
  9 + And the following communities
  10 + | identifier | name |
  11 + | sample | Sample |
  12 +
  13 + @selenium
  14 + Scenario: I should not see trigger if not enabled
  15 + Given the following blocks
  16 + | owner | type |
  17 + | environment | PeopleBlock |
  18 + And feature "show_balloon_with_profile_links_when_clicked" is disabled on environment
  19 + And I go to the homepage
  20 + Then I should not see "Friends"
  21 +
  22 + @selenium
  23 + Scenario: I should not see trigger by default
  24 + Given the following blocks
  25 + | owner | type |
  26 + | environment | PeopleBlock |
  27 + And feature "show_balloon_with_profile_links_when_clicked" is enabled on environment
  28 + And I go to the homepage
  29 + Then I should not see "Friends"
  30 +
  31 + @selenium
  32 + Scenario: I should see balloon when clicked on block trigger
  33 + Given the following blocks
  34 + | owner | type |
  35 + | environment | PeopleBlock |
  36 + And feature "show_balloon_with_profile_links_when_clicked" is enabled on environment
  37 + And I go to the homepage
  38 + When I click ".menu-submenu-trigger"
  39 + Then I should see "Profile"
  40 + And I should see "Friends"
  41 + And I should see "Home Page"
  42 +
  43 + @selenium
  44 + Scenario: I should not see trigger if not enabled on page
  45 + Given feature "show_balloon_with_profile_links_when_clicked" is disabled on environment
  46 + And I go to /assets/communities
  47 + Then I should not see "Members"
  48 +
  49 + @selenium
  50 + Scenario: I should not see trigger by default on page
  51 + Given feature "show_balloon_with_profile_links_when_clicked" is enabled on environment
  52 + And I go to /assets/communities
  53 + Then I should not see "Members"
  54 +
  55 + @selenium
  56 + Scenario: I should see balloon when clicked on page trigger
  57 + Given feature "show_balloon_with_profile_links_when_clicked" is enabled on environment
  58 + And I go to /assets/communities
  59 + When I click ".menu-submenu-trigger"
  60 + Then I should see "Members"
  61 + And I should see "Agenda"
  62 + And I should see "Home Page"
... ...
features/browse.feature 0 → 100644
... ... @@ -0,0 +1,89 @@
  1 +Feature: browse
  2 + As a noosfero visitor
  3 + I want to browse people and communities
  4 +
  5 + Background:
  6 + Given I am on the homepage
  7 + And the following users
  8 + | login | name |
  9 + | joaosilva | Joao Silva |
  10 + | pedrosilva | Pedro Silva |
  11 + | pauloneto | Paulo Neto |
  12 + And the following communities
  13 + | identifier | name |
  14 + | comunity-silva | Community Silva |
  15 + | comunity-neto | Community Neto |
  16 +
  17 + @selenium
  18 + Scenario: Show people browse menu
  19 + Given I should not see "More Recent"
  20 + And I should not see "More Active"
  21 + And I should not see "More Popular"
  22 + When I click "#submenu-people-trigger"
  23 + Then I should see "More Recent"
  24 + And I should see "More Active"
  25 + And I should see "More Popular"
  26 +
  27 + @selenium
  28 + Scenario: People browse menu should add logged information
  29 + Given I am logged in as "joaosilva"
  30 + And I should not see "More Recent"
  31 + And I should not see "More Active"
  32 + And I should not see "More Popular"
  33 + And I should not see "Invite friends"
  34 + And I should not see "My friends"
  35 + When I click "#submenu-people-trigger"
  36 + Then I should see "More Recent"
  37 + And I should see "More Active"
  38 + And I should see "More Popular"
  39 + And I should see "Invite friends"
  40 + And I should see "My friends"
  41 +
  42 + @selenium
  43 + Scenario: Browse people by query
  44 + Given I go to /browse/people
  45 + When I fill in "query" with "Silva"
  46 + And I press "Search"
  47 + Then I should see "Joao Silva"
  48 + And I should see "Pedro Silva"
  49 + And I should not see "Paulo Neto"
  50 + And I should not see "Community Silva"
  51 + And I should not see "Community Neto"
  52 +
  53 + @selenium
  54 + Scenario: Communities browse menu should add logged information
  55 + Given I am logged in as "joaosilva"
  56 + And I am on the homepage
  57 + And I should not see "More Recent"
  58 + And I should not see "More Active"
  59 + And I should not see "More Popular"
  60 + And I should not see "My communities"
  61 + And I should not see "New community"
  62 + When I click "#submenu-communities-trigger"
  63 + Then I should see "More Recent"
  64 + And I should see "More Active"
  65 + And I should see "More Popular"
  66 + And I should see "My communities"
  67 + And I should see "New community"
  68 +
  69 + @selenium
  70 + Scenario: Show communities browse menu
  71 + Given I should not see "More Recent"
  72 + And I should not see "More Active"
  73 + And I should not see "More Popular"
  74 + When I click "#submenu-communities-trigger"
  75 + Then I should see "More Recent"
  76 + And I should see "More Active"
  77 + And I should see "More Popular"
  78 +
  79 + @selenium
  80 + Scenario: Browse communities by query
  81 + Given I go to /browse/communities
  82 + When I fill in "query" with "Silva"
  83 + And I press "Search"
  84 + Then I should see "Community Silva"
  85 + And I should not see "Joao Silva"
  86 + And I should not see "Pedro Silva"
  87 + And I should not see "Paulo Neto"
  88 + And I should not see "Community Neto"
  89 +
... ...
features/manage_products.feature
... ... @@ -17,31 +17,31 @@ Feature: manage products
17 17 | Bicycle |
18 18 And the following products
19 19 | owner | category | name | description |
20   - | redemoinho | bicycle | Bike 1 | bicycle 1 |
21   - | redemoinho | bicycle | Bike 2 | bicycle 2 |
22   - | redemoinho | bicycle | Bike 3 | bicycle 3 |
23   - | redemoinho | bicycle | Bike 4 | bicycle 4 |
24   - | redemoinho | bicycle | Bike 5 | bicycle 5 |
25   - | redemoinho | bicycle | Bike 6 | bicycle 6 |
26   - | redemoinho | bicycle | Bike 7 | bicycle 7 |
27   - | redemoinho | bicycle | Bike 8 | bicycle 8 |
28   - | redemoinho | bicycle | Bike 9 | bicycle 9 |
29   - | redemoinho | bicycle | Bike 10| bicycle 10 |
30   - | redemoinho | bicycle | Bike 11| bicycle 11 |
  20 + | redemoinho | bicycle | Bike A | bicycle 1 |
  21 + | redemoinho | bicycle | Bike B | bicycle 2 |
  22 + | redemoinho | bicycle | Bike C | bicycle 3 |
  23 + | redemoinho | bicycle | Bike D | bicycle 4 |
  24 + | redemoinho | bicycle | Bike E | bicycle 5 |
  25 + | redemoinho | bicycle | Bike F | bicycle 6 |
  26 + | redemoinho | bicycle | Bike G | bicycle 7 |
  27 + | redemoinho | bicycle | Bike H | bicycle 8 |
  28 + | redemoinho | bicycle | Bike I | bicycle 9 |
  29 + | redemoinho | bicycle | Bike J | bicycle 10 |
  30 + | redemoinho | bicycle | Bike K | bicycle 11 |
31 31 When I go to /catalog/redemoinho
32   - Then I should see "Bike 1"
33   - And I should see "Bike 2"
34   - And I should see "Bike 3"
35   - And I should see "Bike 4"
36   - And I should see "Bike 5"
37   - And I should see "Bike 6"
38   - And I should see "Bike 7"
39   - And I should see "Bike 8"
40   - And I should see "Bike 9"
41   - And I should see "Bike 10"
42   - And I should not see "Bike 11"
  32 + Then I should see "Bike A" within "#product_list"
  33 + And I should see "Bike B" within "#product_list"
  34 + And I should see "Bike C" within "#product_list"
  35 + And I should see "Bike D" within "#product_list"
  36 + And I should see "Bike E" within "#product_list"
  37 + And I should see "Bike F" within "#product_list"
  38 + And I should see "Bike G" within "#product_list"
  39 + And I should see "Bike H" within "#product_list"
  40 + And I should see "Bike I" within "#product_list"
  41 + And I should see "Bike J" within "#product_list"
  42 + And I should not see "Bike K" within "#product_list"
43 43 When I follow "Next"
44   - Then I should see "Bike 11"
  44 + Then I should see "Bike K" within "#product_list"
45 45  
46 46 Scenario: listing products and services
47 47 Given I am logged in as "joaosilva"
... ...
public/designs/themes/base/navigation.rhtml
1   -<li><a href="/assets/people"><span class='icon-menu-people'><%= _('People') %></span></a></li>
2   -<li><a href="/assets/communities"><span class='icon-menu-community'><%= _('Communities') %></span></a></li>
3   -<li><a href="/assets/enterprises"><span class='icon-menu-enterprise'><%= _('Enterprises') %></span></a></li>
4   -<li><a href="/assets/products"><span class='icon-menu-product'><%= _('Products') %></span></a></li>
  1 +<li>
  2 + <%= browse_people_menu %>
  3 +</li>
  4 +<li>
  5 + <%= browse_communities_menu %>
  6 +</li>
5 7 <li><a href="/assets/events"><span class='icon-menu-events'><%= _('Events') %></span></a></li>
... ...
public/designs/themes/base/style.css
... ... @@ -201,18 +201,20 @@ body, th, td, input {
201 201 margin: 0px;
202 202 padding: 0px 10px 0px 0px;
203 203 float: right;
204   - overflow: hidden;
205 204 }
206 205  
207 206 #navigation li {
208 207 list-style: none;
209 208 display: block;
210   - height: 54px;
  209 + height: auto;
  210 + width: 125px;
211 211 float: left;
212   - border-left: 1px solid #888a85;
  212 + text-align: center;
  213 + position: relative;
213 214 }
214 215  
215 216 #navigation a {
  217 + border-left: 1px solid #888a85;
216 218 display: block;
217 219 font-size: 15px;
218 220 line-height: 42px;
... ... @@ -225,11 +227,71 @@ body, th, td, input {
225 227 }
226 228  
227 229 #navigation span {
228   - padding: 0px 10px 0px 35px;
229   - background-position: 15px 50%;
  230 + padding: 0px 0px 0px 30px;
  231 + background-position: 5px 50%;
230 232 background-repeat: no-repeat;
231 233 }
232 234  
  235 +#navigation .menu-submenu ul, #navigation .menu-submenu li{
  236 + float: none;
  237 + text-align: right;
  238 + border-left: 0px;
  239 +}
  240 +
  241 +#navigation .menu-submenu ul{
  242 + border: 1px solid #888a85;
  243 + border-top: 0px;
  244 + border-left: 0px;
  245 + background-color: #ccc;
  246 + padding-right: 2px;
  247 + height: auto;
  248 + display: block;
  249 +}
  250 +
  251 +#navigation .menu-submenu{
  252 + bottom: -75px;
  253 + width: 126px;
  254 + top: 0px;
  255 + right: 0px;
  256 + position: relative;
  257 +}
  258 +
  259 +#navigation .menu-submenu a{
  260 + padding: 4px 5px;
  261 + font-size: 12px;
  262 + line-height: normal;
  263 +}
  264 +
  265 +#navigation .menu-submenu-trigger {
  266 + background: #eee url(/images/down-arrow.png) center center no-repeat;
  267 + border: 0px;
  268 + height: 8px;
  269 + width: 124px;
  270 +}
  271 +#navigation li:hover .menu-submenu-trigger:hover {
  272 + background: #eee url(/images/down-arrow.png) center center no-repeat;
  273 +}
  274 +
  275 +#navigation li:hover .menu-submenu-trigger{
  276 + display: block;
  277 +}
  278 +
  279 +#navigation li .menu-submenu-trigger{
  280 + display: none;
  281 +}
  282 +
  283 +#navigation .menu-submenu-header, #navigation .menu-submenu-footer, #navigation .menu-submenu h4{
  284 + display: none;
  285 +}
  286 +
  287 +#navigation .common-profile-list-block .vcard .menu-submenu-trigger,
  288 +.menu-submenu-trigger {
  289 + left: 1px;
  290 + top: 33px;
  291 + -moz-border-radius: 0px;
  292 + -webkit-border-radius: 0px;
  293 +}
  294 +
233 295 /***************************** boxes ********************************/
234 296  
235 297 .template-default .box-1 { /* Center */
... ... @@ -1119,12 +1181,12 @@ hr.pre-posts, hr.sep-posts {
1119 1181 /******** controller-friends action-friends-index ********/
1120 1182  
1121 1183 .action-friends-index .profile-list li,
1122   -.common-profile-list-block .vcard a {
  1184 +.common-profile-list-block .vcard {
1123 1185 border: 1px solid transparent;
1124 1186 }
1125 1187  
1126 1188 .action-friends-index .profile-list li:hover,
1127   -.common-profile-list-block .vcard a:hover {
  1189 +.common-profile-list-block .vcard:hover {
1128 1190 border: 1px solid #CCC;
1129 1191 -moz-border-radius: 5px;
1130 1192 -webkit-border-radius: 5px;
... ... @@ -1132,6 +1194,11 @@ hr.pre-posts, hr.sep-posts {
1132 1194 text-decoration: none;
1133 1195 }
1134 1196  
  1197 +.common-profile-list-block .vcard a,
  1198 +.common-profile-list-block .vcard a:hover {
  1199 + background: transparent;
  1200 + border: 0;
  1201 +}
1135 1202  
1136 1203 /******** controller-profile action-profile-index ********/
1137 1204  
... ... @@ -1205,4 +1272,56 @@ table.profile th {
1205 1272 border-bottom: 2px solid black;
1206 1273 }
1207 1274  
  1275 +/**************************** Browse *******************************/
1208 1276  
  1277 +.controller-browse #content .search_form {
  1278 + background: #DDD;
  1279 + -moz-border-radius: 5px;
  1280 + -webkit-border-radius: 5px;
  1281 + padding: 12px 15px 13px 15px;
  1282 + position: relative;
  1283 + text-align: left;
  1284 +}
  1285 +
  1286 +.controller-browse a {
  1287 + color: #555753;
  1288 +}
  1289 +
  1290 +.browse-results-innerbox {
  1291 + margin-top: 10px;
  1292 + border-bottom: none;
  1293 + border: 1px solid #CCC;
  1294 + background: url(imgs/comment-bg-S.png) 0% 100% repeat-x;
  1295 + padding-bottom: 10px;
  1296 +
  1297 +}
  1298 +.browse-results-innerbox ul{
  1299 + padding: 0px;
  1300 +}
  1301 +
  1302 +.browse-results-innerbox .browse-results-type-empty div {
  1303 + text-align: center;
  1304 + color: #DDD;
  1305 + font-weight: bold;
  1306 + font-size: 30px;
  1307 + padding: 70px 10px 0px 0px;
  1308 + border: none;
  1309 +
  1310 +}
  1311 +
  1312 +.search-results-type-article.search-results-innerbox {
  1313 + padding: 5px 0px 5px 10px;
  1314 + max-height: 215px;
  1315 + height: 225px;
  1316 +}
  1317 +
  1318 +#content .search-results-type-article li {
  1319 + padding: 5px 0px;
  1320 +}
  1321 +
  1322 +.search-results-type-article a {
  1323 + text-decoration: none;
  1324 +}
  1325 +.search-results-type-article a:hover {
  1326 + text-decoration: underline;
  1327 +}
... ...
public/images/balloon-footer.png 0 → 100644

1.91 KB

public/images/balloon-header.png 0 → 100644

1.01 KB

public/images/balloon-middle.png 0 → 100644

267 Bytes

public/images/down-arrow.png 0 → 100644

407 Bytes

public/images/top-arrow.png 0 → 100644

261 Bytes

public/javascripts/application.js
... ... @@ -214,3 +214,52 @@ function expandCategory(block, id) {
214 214 }
215 215 }
216 216 }
  217 +
  218 +function toggleSubmenu(trigger, title, link_list) {
  219 + trigger.onclick = function() {
  220 + var submenu = jQuery(trigger).siblings('.menu-submenu');
  221 + var hide = false;
  222 + if (submenu.length > 0 && submenu.is(':visible')) hide = true;
  223 + hideAllSubmenus();
  224 + // Hide or show this submenu if it already exists
  225 + if (submenu.length > 0) {
  226 + if (!hide) {
  227 + var direction = 'down';
  228 + if (submenu.hasClass('up')) direction = 'up';
  229 + submenu.show('slide', { 'direction' : direction }, 'slow');
  230 + }
  231 + }
  232 + return false;
  233 + }
  234 +
  235 + hideAllSubmenus();
  236 + // Build and show this submenu if it doesn't exist yet
  237 + var direction = 'down';
  238 + if (jQuery(trigger).hasClass('up')) direction = 'up';
  239 + var submenu = jQuery('<div></div>').attr('class', 'menu-submenu ' + direction).attr('style', 'display: none');
  240 + var header = jQuery('<div></div>').attr('class', 'menu-submenu-header');
  241 + var content = jQuery('<div></div>').attr('class', 'menu-submenu-content');
  242 + var list = jQuery('<ul></ul>').attr('class', 'menu-submenu-list');
  243 + var footer = jQuery('<div></div>').attr('class', 'menu-submenu-footer');
  244 + content.append('<h4>' + title + '</h4>');
  245 + jQuery.each(link_list, function(index, link_hash) {
  246 + for (label in link_hash) {
  247 + list.append('<li><a href="' + link_hash[label] + '">' + label + '</a></li>');
  248 + }
  249 + });
  250 + content.append(list);
  251 + submenu.append(header).append(content).append(footer);
  252 + jQuery(trigger).before(submenu);
  253 + submenu.show('slide', { 'direction' : direction }, 'slow');
  254 +}
  255 +
  256 +function hideAllSubmenus() {
  257 + jQuery('.menu-submenu.up:visible').hide('slide', { 'direction' : 'up' }, 'slow');
  258 + jQuery('.menu-submenu.down:visible').hide('slide', { 'direction' : 'down' }, 'slow');
  259 +}
  260 +
  261 +// Hide visible ballons when clicked outside them
  262 +jQuery(document).ready(function() {
  263 + jQuery('body').click(function() { hideAllSubmenus(); });
  264 + jQuery('.menu-submenu-trigger').click(function(e) { e.stopPropagation(); });
  265 +});
... ...
public/stylesheets/application.css
... ... @@ -1644,13 +1644,13 @@ input.disabled {
1644 1644 margin: 0px;
1645 1645 padding: 0px;
1646 1646 list-style: none;
1647   - display: inline;
  1647 + position: relative;
1648 1648 }
1649 1649  
1650 1650 .common-profile-list-block .vcard a {
1651 1651 display: block;
1652   - height: 95px;
1653   - max-height: 92px;
  1652 + height: 112px;
  1653 + max-height: 112px;
1654 1654 padding-top: 3px;
1655 1655 margin: 0px 2px;
1656 1656 float: left;
... ... @@ -1738,6 +1738,12 @@ input.disabled {
1738 1738 display: none;
1739 1739 }
1740 1740  
  1741 +.common-profile-list-block .extra_info {
  1742 + font-size: 9px;
  1743 + opacity: 0.5;
  1744 + filter: alpha(opacity=50);
  1745 +}
  1746 +
1741 1747  
1742 1748 /* ==> blocks/recent-documents-block.css <<= */
1743 1749  
... ... @@ -3603,7 +3609,6 @@ h1#agenda-title {
3603 3609 border: 1px solid #2A5896;
3604 3610 padding: 10px 0px 10px 10px;
3605 3611 height: 205px;
3606   - overflow: auto;
3607 3612 position: relative; /* work-arround-bug fo MSIE */
3608 3613 }
3609 3614  
... ... @@ -3630,6 +3635,7 @@ h1#agenda-title {
3630 3635 .controller-search .only-one-result-box .search-results-innerbox,
3631 3636 .controller-search .only-one-result-box .search-results-innerbox.common-profile-list-block {
3632 3637 height: auto;
  3638 + float: left;
3633 3639 padding: 10px 4px 10px 10px;
3634 3640 }
3635 3641  
... ... @@ -4088,3 +4094,100 @@ h1#agenda-title {
4088 4094 .categories-block .ui-icon {
4089 4095 margin-top: 2px;
4090 4096 }
  4097 +
  4098 +/* Profile balloon */
  4099 +
  4100 +.common-profile-list-block .vcard {
  4101 + position: relative !important;
  4102 + float: left;
  4103 +}
  4104 +
  4105 +.common-profile-list-block .vcard .menu-submenu-trigger,
  4106 +.menu-submenu-trigger {
  4107 + display: none;
  4108 + width: 16px;
  4109 + height: 16px;
  4110 + position: absolute;
  4111 + top: -1px;
  4112 + left: -3px;
  4113 + overflow: hidden;
  4114 + background: #efefef url(/images/top-arrow.png) center center no-repeat;
  4115 + border: 1px solid #ccc;
  4116 + z-index: 2;
  4117 + -moz-border-radius: 5px;
  4118 + -webkit-border-radius: 5px;
  4119 +}
  4120 +
  4121 +.common-profile-list-block .vcard .menu-submenu-trigger:hover,
  4122 +.menu-submenu-trigger:hover {
  4123 + background: #fff url(/images/top-arrow.png) center center no-repeat;
  4124 + border: 1px solid #ccc;
  4125 +}
  4126 +
  4127 +.menu-submenu-trigger span {
  4128 + display: none;
  4129 +}
  4130 +
  4131 +.common-profile-list-block .vcard:hover .menu-submenu-trigger {
  4132 + display: block;
  4133 +}
  4134 +
  4135 +.menu-submenu {
  4136 + position: absolute;
  4137 + bottom: 115px;
  4138 + right: -27px;
  4139 + z-index: 11;
  4140 + width: 150px;
  4141 + padding: 0;
  4142 + text-align: left;
  4143 +}
  4144 +
  4145 +.menu-submenu-footer {
  4146 + background: transparent url(/images/balloon-footer.png) center center no-repeat;
  4147 + height: 29px;
  4148 + margin: 0;
  4149 +}
  4150 +
  4151 +.menu-submenu-header {
  4152 + background: transparent url(/images/balloon-header.png) center center no-repeat;
  4153 + height: 17px;
  4154 + margin: 0;
  4155 +}
  4156 +
  4157 +.menu-submenu-content {
  4158 + background: transparent url(/images/balloon-middle.png) top left repeat-y;
  4159 + margin: 0;
  4160 + padding: 0;
  4161 + width: 100%;
  4162 +}
  4163 +
  4164 +#content .menu-submenu-content ul {
  4165 + margin: 0;
  4166 + padding: 0;
  4167 + width: 100% !important;
  4168 + min-width: 100% !important;
  4169 +}
  4170 +
  4171 +#content .menu-submenu-content ul a {
  4172 + text-align: left;
  4173 + padding-left: 12px;
  4174 +}
  4175 +
  4176 +.common-profile-list-block .vcard .menu-submenu a {
  4177 + float: none;
  4178 + display: block;
  4179 + height: auto;
  4180 + font-size: 12px;
  4181 +}
  4182 +
  4183 +#content .menu-submenu h4 {
  4184 + margin: 0;
  4185 + font-size: 12px;
  4186 + font-weight: bold;
  4187 + text-align: left;
  4188 + margin: 0 auto;
  4189 + padding-bottom: 3px;
  4190 + margin-bottom: 3px;
  4191 + border-bottom: 1px solid #000;
  4192 + width: 90%;
  4193 +}
... ...
test/functional/browse_controller_test.rb 0 → 100644
... ... @@ -0,0 +1,221 @@
  1 +require File.dirname(__FILE__) + '/../test_helper'
  2 +require 'browse_controller'
  3 +
  4 +# Re-raise errors caught by the controller.
  5 +class BrowseController; def rescue_action(e) raise e end; end
  6 +
  7 +class BrowseControllerTest < Test::Unit::TestCase
  8 +
  9 + def setup
  10 + @controller = BrowseController.new
  11 + @request = ActionController::TestRequest.new
  12 + @request.stubs(:ssl?).returns(false)
  13 + @response = ActionController::TestResponse.new
  14 +
  15 + # By pass user validation on person creation
  16 + user = mock()
  17 + user.stubs(:id).returns(1)
  18 + user.stubs(:valid?).returns(true)
  19 + user.stubs(:email).returns('some@test.com')
  20 + user.stubs(:save!).returns(true)
  21 + Person.any_instance.stubs(:user).returns(user)
  22 + end
  23 +
  24 + should 'search for people' do
  25 + Person.delete_all
  26 + small = create(Person, :name => 'A small person for testing', :user_id => 1)
  27 + create(Person, :name => 'A big person for testing', :user_id => 2)
  28 +
  29 + get :people, :query => 'small'
  30 + assert_equal [small], assigns(:results)
  31 + end
  32 +
  33 + should 'list all people order by more recent one by default' do
  34 + Person.delete_all
  35 + p1 = create(Person, :name => 'Testing person 1', :user_id => 1, :created_at => DateTime.now - 2)
  36 + p2 = create(Person, :name => 'Testing person 2', :user_id => 2, :created_at => DateTime.now - 1)
  37 + p3 = create(Person, :name => 'Testing person 3', :user_id => 3)
  38 +
  39 + get :people
  40 + assert_equal [p3,p2,p1] , assigns(:results)
  41 + end
  42 +
  43 + should 'paginate search of people in groups of 27' do
  44 + Person.delete_all
  45 +
  46 + 1.upto(30).map do |n|
  47 + create(Person, :name => 'Testing person', :user_id => n)
  48 + end
  49 +
  50 + get :people
  51 + assert_equal 30 , Person.count
  52 + assert_equal 27 , assigns(:results).count
  53 + assert_tag :a, '', :attributes => {:class => 'next_page'}
  54 + end
  55 +
  56 + should 'paginate ferret search of people in groups of 27' do
  57 + Person.delete_all
  58 +
  59 + 1.upto(30).map do |n|
  60 + create(Person, :name => 'Testing person', :user_id => n)
  61 + end
  62 +
  63 + get :people, :query => 'Testing'
  64 + assert_equal 27 , assigns(:results).count
  65 + assert_tag :a, '', :attributes => {:class => 'next_page'}
  66 + end
  67 +
  68 + should 'list all people filter by more active' do
  69 + Person.delete_all
  70 + p1 = create(Person, :name => 'Testing person 1', :user_id => 1)
  71 + p2 = create(Person, :name => 'Testing person 2', :user_id => 2)
  72 + p3 = create(Person, :name => 'Testing person 3', :user_id => 3)
  73 + Article.delete_all
  74 + fast_create(Article, :profile_id => p1, :created_at => 1.day.ago)
  75 + fast_create(Article, :profile_id => p2, :created_at => DateTime.now.beginning_of_day)
  76 + get :people, :filter => 'more_active'
  77 + assert_equal [p1,p2] , assigns(:results)
  78 + end
  79 +
  80 + should 'filter more popular people' do
  81 + Person.delete_all
  82 + p1 = create(Person, :name => 'Testing person 1', :user_id => 1)
  83 + p2 = create(Person, :name => 'Testing person 2', :user_id => 2)
  84 + p3 = create(Person, :name => 'Testing person 3', :user_id => 3)
  85 +
  86 + p1.add_friend(p2)
  87 + p2.add_friend(p1)
  88 + p2.add_friend(p3)
  89 + get :people, :filter => 'more_popular'
  90 + assert_equal [p2,p1] , assigns(:results)
  91 + end
  92 +
  93 + should 'the people filter be only the hardcoded one' do
  94 + get :people, :filter => 'more_recent'
  95 + assert_equal 'more_recent' , assigns(:filter)
  96 +
  97 + get :people, :filter => 'more_active'
  98 + assert_equal 'more_active' , assigns(:filter)
  99 +
  100 + get :people, :filter => 'more_popular'
  101 + assert_equal 'more_popular' , assigns(:filter)
  102 +
  103 + get :people, :filter => 'more_anything'
  104 + assert_equal 'more_recent' , assigns(:filter)
  105 + end
  106 +
  107 + should 'the people filter define the title' do
  108 + get :people, :filter => 'more_recent'
  109 + assert_equal 'People more recent' , assigns(:title)
  110 + assert_tag :h1, :content => 'People more recent'
  111 +
  112 + get :people, :filter => 'more_active'
  113 + assert_equal 'People more active' , assigns(:title)
  114 + assert_tag :h1, :content => 'People more active'
  115 +
  116 + get :people, :filter => 'more_popular'
  117 + assert_equal 'People more popular' , assigns(:title)
  118 + assert_tag :h1, :content => 'People more popular'
  119 +
  120 + get :people, :filter => 'more_anything'
  121 + assert_equal 'People more recent' , assigns(:title)
  122 + assert_tag :h1, :content => 'People more recent'
  123 + end
  124 +
  125 + should 'search for community' do
  126 + small = create(Community, :name => 'A small community for testing')
  127 + create(Community, :name => 'A big community for testing')
  128 +
  129 + get :communities, :query => 'small'
  130 + assert_equal [small], assigns(:results)
  131 + end
  132 +
  133 + should 'list all community order by more recent one by default' do
  134 + c1 = create(Community, :name => 'Testing community 1', :created_at => DateTime.now - 2)
  135 + c2 = create(Community, :name => 'Testing community 2', :created_at => DateTime.now - 1)
  136 + c3 = create(Community, :name => 'Testing community 3')
  137 +
  138 + get :communities
  139 + assert_equal [c3,c2,c1] , assigns(:results)
  140 + end
  141 +
  142 + should 'paginate search of communities in groups of 27' do
  143 + 1.upto(30).map do |n|
  144 + create(Community, :name => 'Testing community')
  145 + end
  146 +
  147 + get :communities
  148 + assert_equal 30 , Community.count
  149 + assert_equal 27 , assigns(:results).count
  150 + assert_tag :a, '', :attributes => {:class => 'next_page'}
  151 + end
  152 +
  153 + should 'paginate ferret search of communities in groups of 27' do
  154 + 1.upto(30).map do |n|
  155 + create(Community, :name => 'Testing community')
  156 + end
  157 +
  158 + get :communities, :query => 'Testing'
  159 + assert_equal 27 , assigns(:results).count
  160 + assert_tag :a, '', :attributes => {:class => 'next_page'}
  161 + end
  162 +
  163 + should 'list all communities filter by more active' do
  164 + c1 = create(Community, :name => 'Testing community 1')
  165 + c2 = create(Community, :name => 'Testing community 2')
  166 + c3 = create(Community, :name => 'Testing community 3')
  167 + Article.delete_all
  168 + fast_create(Article, :profile_id => c1, :created_at => 1.day.ago)
  169 + fast_create(Article, :profile_id => c2, :created_at => DateTime.now.beginning_of_day)
  170 + get :communities, :filter => 'more_active'
  171 + assert_equal [c1,c2] , assigns(:results)
  172 + end
  173 +
  174 + should 'filter more popular communities' do
  175 + Person.delete_all
  176 + c1 = create(Community, :name => 'Testing community 1')
  177 + c2 = create(Community, :name => 'Testing community 2')
  178 + create(Community, :name => 'Testing community 3')
  179 +
  180 + p1 = create(Person, :name => 'Testing person 1', :user_id => 1)
  181 + p2 = create(Person, :name => 'Testing person 2', :user_id => 2)
  182 + c1.add_member(p1)
  183 + c2.add_member(p1)
  184 + c2.add_member(p2)
  185 + get :communities, :filter => 'more_popular'
  186 + assert_equal [c2,c1] , assigns(:results)
  187 + end
  188 +
  189 + should 'the communities filter be only the hardcoded one' do
  190 + get :communities, :filter => 'more_recent'
  191 + assert_equal 'more_recent' , assigns(:filter)
  192 +
  193 + get :communities, :filter => 'more_active'
  194 + assert_equal 'more_active' , assigns(:filter)
  195 +
  196 + get :communities, :filter => 'more_popular'
  197 + assert_equal 'more_popular' , assigns(:filter)
  198 +
  199 + get :communities, :filter => 'more_anything'
  200 + assert_equal 'more_recent' , assigns(:filter)
  201 + end
  202 +
  203 + should 'the communities filter define the title' do
  204 + get :communities, :filter => 'more_recent'
  205 + assert_equal 'Communities more recent' , assigns(:title)
  206 + assert_tag :h1, :content => 'Communities more recent'
  207 +
  208 + get :communities, :filter => 'more_active'
  209 + assert_equal 'Communities more active' , assigns(:title)
  210 + assert_tag :h1, :content => 'Communities more active'
  211 +
  212 + get :communities, :filter => 'more_popular'
  213 + assert_equal 'Communities more popular' , assigns(:title)
  214 + assert_tag :h1, :content => 'Communities more popular'
  215 +
  216 + get :communities, :filter => 'more_anything'
  217 + assert_equal 'Communities more recent' , assigns(:title)
  218 + assert_tag :h1, :content => 'Communities more recent'
  219 + end
  220 +
  221 +end
... ...
test/integration/routing_test.rb
... ... @@ -216,4 +216,11 @@ class RoutingTest &lt; ActionController::IntegrationTest
216 216 assert_routing('/doc', :controller => 'doc', :action => 'index')
217 217 end
218 218  
  219 + # browse controller
  220 + def test_browse_routing
  221 + assert_routing('/browse/people', :controller => 'browse', :action => 'people')
  222 + assert_routing('/browse/people/more_popular', :controller => 'browse', :action => 'people', :filter => 'more_popular')
  223 + assert_routing('/browse/communities', :controller => 'browse', :action => 'communities')
  224 + assert_routing('/browse/communities/more_active', :controller => 'browse', :action => 'communities', :filter => 'more_active')
  225 + end
219 226 end
... ...
test/unit/application_helper_test.rb
... ... @@ -567,6 +567,60 @@ class ApplicationHelperTest &lt; Test::Unit::TestCase
567 567 assert_equal environment.theme, current_theme
568 568 end
569 569  
  570 + should 'trunc to 15 chars the big filename' do
  571 + assert_equal 'AGENDA(...).mp3', short_filename('AGENDA_CULTURA_-_FESTA_DE_VAQUEIROS_PONTO_DE_SERRA_PRETA_BAIXA.mp3',15)
  572 + end
  573 +
  574 + should 'trunc to default limit the big filename' do
  575 + assert_equal 'AGENDA_CULTURA_-_FESTA_DE_VAQUEIRO(...).mp3', short_filename('AGENDA_CULTURA_-_FESTA_DE_VAQUEIROS_PONTO_DE_SERRA_PRETA_BAIXA.mp3')
  576 + end
  577 +
  578 + should 'does not trunc short filename' do
  579 + assert_equal 'filename.mp3', short_filename('filename.mp3')
  580 + end
  581 +
  582 + should 'return nil when :show_balloon_with_profile_links_when_clicked is not enabled in environment' do
  583 + env = Environment.default
  584 + env.stubs(:enabled?).with(:show_balloon_with_profile_links_when_clicked).returns(false)
  585 + stubs(:environment).returns(env)
  586 + profile = Profile.new
  587 + assert_nil links_for_balloon(profile)
  588 + end
  589 +
  590 + should 'return ordered list of links to balloon to Person' do
  591 + env = Environment.default
  592 + env.stubs(:enabled?).with(:show_balloon_with_profile_links_when_clicked).returns(true)
  593 + stubs(:environment).returns(env)
  594 + person = Person.new
  595 + person.stubs(:url).returns('url for person')
  596 + person.stubs(:public_profile_url).returns('url for person')
  597 + links = links_for_balloon(person)
  598 + assert_equal ['Home Page', 'Profile', 'Friends', 'Communities'], links.map{|i| i.keys.first}
  599 + end
  600 +
  601 + should 'return ordered list of links to balloon to Community' do
  602 + env = Environment.default
  603 + env.stubs(:enabled?).with(:show_balloon_with_profile_links_when_clicked).returns(true)
  604 + stubs(:environment).returns(env)
  605 + community = Community.new
  606 + community.stubs(:url).returns('url for community')
  607 + community.stubs(:public_profile_url).returns('url for community')
  608 + links = links_for_balloon(community)
  609 + assert_equal ['Home Page', 'Profile', 'Members', 'Agenda'], links.map{|i| i.keys.first}
  610 + end
  611 +
  612 + should 'return ordered list of links to balloon to Enterprise' do
  613 + env = Environment.default
  614 + env.stubs(:enabled?).with(:show_balloon_with_profile_links_when_clicked).returns(true)
  615 + stubs(:environment).returns(env)
  616 + enterprise = Enterprise.new
  617 + enterprise.stubs(:url).returns('url for enterprise')
  618 + enterprise.stubs(:public_profile_url).returns('url for enterprise')
  619 + stubs(:catalog_path)
  620 + links = links_for_balloon(enterprise)
  621 + assert_equal ['Home Page', 'Products', 'Members', 'Agenda'], links.map{|i| i.keys.first}
  622 + end
  623 +
570 624 protected
571 625  
572 626 def url_for(args = {})
... ...
test/unit/person_test.rb
... ... @@ -588,4 +588,51 @@ class PersonTest &lt; Test::Unit::TestCase
588 588 end
589 589 end
590 590  
  591 + should "return none on label if the person hasn't friends" do
  592 + p = fast_create(Person)
  593 + assert_equal 0, p.friends.count
  594 + assert_equal "none", p.more_popular_label
  595 + end
  596 +
  597 + should "return one friend on label if the profile has one member" do
  598 + p1 = fast_create(Person)
  599 + p2 = fast_create(Person)
  600 + p1.add_friend(p2)
  601 + assert_equal 1, p1.friends.count
  602 + assert_equal "one friend", p1.more_popular_label
  603 + end
  604 +
  605 + should "return the number of friends on label if the person has more than one friend" do
  606 + p1 = fast_create(Person)
  607 + p2 = fast_create(Person)
  608 + p3 = fast_create(Person)
  609 + p1.add_friend(p2)
  610 + p1.add_friend(p3)
  611 + assert_equal 2, p1.friends.count
  612 + assert_equal "2 friends", p1.more_popular_label
  613 +
  614 + p4 = fast_create(Person)
  615 + p1.add_friend(p4)
  616 + assert_equal 3, p1.friends.count
  617 + assert_equal "3 friends", p1.more_popular_label
  618 + end
  619 +
  620 + should 'find more popular people' do
  621 + Person.delete_all
  622 + env = fast_create(Environment)
  623 + p1 = fast_create(Person)
  624 + p2 = fast_create(Person)
  625 + p3 = fast_create(Person)
  626 +
  627 + p1.add_friend(p2)
  628 + assert_equal [p1], Person.more_popular
  629 +
  630 + p2.add_friend(p1)
  631 + p2.add_friend(p3)
  632 + assert_equal [p2,p1] , Person.more_popular
  633 +
  634 + p2.remove_friend(p3)
  635 + assert_equal [p1,p2] , Person.more_popular
  636 + end
  637 +
591 638 end
... ...
test/unit/profile_test.rb
... ... @@ -1563,6 +1563,212 @@ class ProfileTest &lt; Test::Unit::TestCase
1563 1563 assert_match /<!-- .* --> <h1> Wellformed html code <\/h1>/, profile.custom_footer
1564 1564 end
1565 1565  
  1566 + should 'find more recent people' do
  1567 + Person.delete_all
  1568 + p1 = fast_create(Person,:created_at => 4.days.ago)
  1569 + p2 = fast_create(Person, :created_at => DateTime.now)
  1570 + p3 = fast_create(Person, :created_at => 2.days.ago)
  1571 +
  1572 + assert_equal [p2,p3,p1] , Person.more_recent
  1573 +
  1574 + p4 = fast_create(Person, :created_at => 3.days.ago)
  1575 + assert_equal [p2,p3,p4,p1] , Person.more_recent
  1576 + end
  1577 +
  1578 + should 'find more active people' do
  1579 + Person.delete_all
  1580 + p1 = fast_create(Person)
  1581 + p2 = fast_create(Person)
  1582 + p3 = fast_create(Person)
  1583 + Article.delete_all
  1584 + fast_create(Article, :profile_id => p1, :created_at => 7.days.ago)
  1585 + fast_create(Article, :profile_id => p1, :created_at => DateTime.now.beginning_of_day)
  1586 + fast_create(Article, :profile_id => p2, :created_at => DateTime.now.beginning_of_day)
  1587 + assert_equal [p1,p2] , Person.more_active
  1588 +
  1589 + fast_create(Article, :profile_id => p2, :created_at => 1.day.ago)
  1590 + fast_create(Article, :profile_id => p2, :created_at => 5.days.ago)
  1591 + fast_create(Article, :profile_id => p3, :created_at => 2.days.ago)
  1592 + assert_equal [p2,p1,p3] , Person.more_active
  1593 + end
  1594 +
  1595 + should 'the ties on more active people be solved by the number of comments' do
  1596 + Person.delete_all
  1597 + p1 = fast_create(Person)
  1598 + p2 = fast_create(Person)
  1599 + Article.delete_all
  1600 + a1 = fast_create(Article, :profile_id => p1, :created_at => DateTime.now.beginning_of_day)
  1601 + a2 = fast_create(Article, :profile_id => p2, :created_at => DateTime.now.beginning_of_day)
  1602 + assert_equal [], [p1,p2] - Person.more_active
  1603 + assert_equal [], Person.more_active - [p1, p2]
  1604 +
  1605 + a2.comments.build(:title => 'test comment', :body => 'anything', :author => p1).save!
  1606 + assert_equal [p2,p1] , Person.more_active
  1607 +
  1608 + a1.comments.build(:title => 'test comment', :body => 'anything', :author => p2).save!
  1609 + a1.comments.build(:title => 'test comment', :body => 'anything', :author => p2).save!
  1610 + assert_equal [p1,p2] , Person.more_active
  1611 + end
  1612 +
  1613 + should 'more active people take in consideration only articles created current the last week' do
  1614 + Person.delete_all
  1615 + env = fast_create(Environment)
  1616 + p1 = fast_create(Person)
  1617 + p2 = fast_create(Person)
  1618 + p3 = fast_create(Person)
  1619 + Article.delete_all
  1620 + fast_create(Article, :profile_id => p1, :created_at => DateTime.now.beginning_of_day)
  1621 + fast_create(Article, :profile_id => p2, :created_at => 10.days.ago)
  1622 + assert_equal [p1] , Person.more_active
  1623 +
  1624 + fast_create(Article, :profile_id => p2, :created_at => DateTime.now.beginning_of_day)
  1625 + fast_create(Article, :profile_id => p2, :created_at => 7.days.ago)
  1626 + fast_create(Article, :profile_id => p3, :created_at => 8.days.ago)
  1627 + assert_equal [p2,p1] , Person.more_active
  1628 + end
  1629 +
  1630 + should 'find more recent community' do
  1631 + c1 = fast_create(Community, :created_at => 3.days.ago)
  1632 + c2 = fast_create(Community, :created_at => 1.day.ago)
  1633 + c3 = fast_create(Community, :created_at => DateTime.now)
  1634 +
  1635 + assert_equal [c3,c2,c1] , Community.more_recent
  1636 +
  1637 + c4 = fast_create(Community, :created_at => 2.days.ago)
  1638 + assert_equal [c3,c2,c4,c1] , Community.more_recent
  1639 + end
  1640 +
  1641 + should 'find more active community' do
  1642 + c1 = fast_create(Community)
  1643 + c2 = fast_create(Community)
  1644 + c3 = fast_create(Community)
  1645 +
  1646 + Article.delete_all
  1647 + fast_create(Article, :profile_id => c1, :created_at => 1.day.ago)
  1648 + fast_create(Article, :profile_id => c1, :created_at => DateTime.now.beginning_of_day)
  1649 + fast_create(Article, :profile_id => c2, :created_at => DateTime.now.beginning_of_day)
  1650 + assert_equal [c1,c2], Community.more_active
  1651 +
  1652 + fast_create(Article, :profile_id => c2, :created_at => 2.days.ago)
  1653 + fast_create(Article, :profile_id => c2, :created_at => 7.days.ago)
  1654 + fast_create(Article, :profile_id => c3, :created_at => 1.day.ago)
  1655 + assert_equal [c2,c1,c3] , Community.more_active
  1656 + end
  1657 +
  1658 + should 'the ties on more active communities be solved by the number of comments' do
  1659 + env = create(Environment)
  1660 + Community.delete_all
  1661 + c1 = fast_create(Community)
  1662 + c2 = fast_create(Community)
  1663 + Article.delete_all
  1664 + a1 = fast_create(Article, :profile_id => c1, :created_at => DateTime.now.beginning_of_day)
  1665 + a2 = fast_create(Article, :profile_id => c2, :created_at => DateTime.now.beginning_of_day)
  1666 + assert_equal [c1,c2] , Community.more_active
  1667 +
  1668 + p1 = fast_create(Person)
  1669 + a2.comments.build(:title => 'test comment', :body => 'anything', :author => p1).save!
  1670 + assert_equal [c2,c1] , Community.more_active
  1671 +
  1672 + a1.comments.build(:title => 'test comment', :body => 'anything', :author => p1).save!
  1673 + a1.comments.build(:title => 'test comment', :body => 'anything', :author => p1).save!
  1674 + assert_equal [c1,c2] , Community.more_active
  1675 + end
  1676 +
  1677 + should 'more active communities take in consideration only articles created current the last week' do
  1678 + c1 = fast_create(Community)
  1679 + c2 = fast_create(Community)
  1680 + c3 = fast_create(Community)
  1681 + Article.delete_all
  1682 + fast_create(Article, :profile_id => c1, :created_at => DateTime.now.beginning_of_day)
  1683 + fast_create(Article, :profile_id => c2, :created_at => 10.days.ago)
  1684 + assert_equal [c1] , Community.more_active
  1685 +
  1686 + fast_create(Article, :profile_id => c2, :created_at => DateTime.now.beginning_of_day)
  1687 + fast_create(Article, :profile_id => c2, :created_at => 7.days.ago)
  1688 + fast_create(Article, :profile_id => c3, :created_at => 8.days.ago)
  1689 + assert_equal [c2,c1] , Community.more_active
  1690 + end
  1691 +
  1692 + should 'find more popular communities' do
  1693 + Community.delete_all
  1694 +
  1695 + c1 = fast_create(Community)
  1696 + c2 = fast_create(Community)
  1697 + fast_create(Community)
  1698 +
  1699 + p1 = fast_create(Person)
  1700 + p2 = fast_create(Person)
  1701 + c1.add_member(p1)
  1702 + assert_equal [c1] , Community.more_popular
  1703 +
  1704 + c2.add_member(p1)
  1705 + c2.add_member(p2)
  1706 + assert_equal [c2,c1] , Community.more_popular
  1707 +
  1708 + c2.remove_member(p2)
  1709 + c2.remove_member(p1)
  1710 + assert_equal [c1] , Community.more_popular
  1711 + end
  1712 +
  1713 + should "return the more recent label" do
  1714 + p = fast_create(Profile)
  1715 + assert_equal "Since: ", p.more_recent_label
  1716 + end
  1717 +
  1718 + should "return none on label if the profile hasn't articles" do
  1719 + p = fast_create(Profile)
  1720 + assert_equal 0, p.articles.count
  1721 + assert_equal "none", p.more_active_label
  1722 + end
  1723 +
  1724 + should "return one article on label if the profile has one article" do
  1725 + p = fast_create(Profile)
  1726 + fast_create(Article, :profile_id => p.id)
  1727 + assert_equal 1, p.articles.count
  1728 + assert_equal "one article", p.more_active_label
  1729 + end
  1730 +
  1731 + should "return number of artciles on label if the profile has more than one article" do
  1732 + p = fast_create(Profile)
  1733 + fast_create(Article, :profile_id => p.id)
  1734 + fast_create(Article, :profile_id => p.id)
  1735 + assert_equal 2, p.articles.count
  1736 + assert_equal "2 articles", p.more_active_label
  1737 +
  1738 + fast_create(Article, :profile_id => p.id)
  1739 + assert_equal 3, p.articles.count
  1740 + assert_equal "3 articles", p.more_active_label
  1741 + end
  1742 +
  1743 + should "return none on label if the profile hasn't members" do
  1744 + p = fast_create(Profile)
  1745 + assert_equal 0, p.members.count
  1746 + assert_equal "none", p.more_popular_label
  1747 + end
  1748 +
  1749 + should "return one member on label if the profile has one member" do
  1750 + p = fast_create(Person)
  1751 + c = fast_create(Community)
  1752 + c.add_member(p)
  1753 + assert_equal 1, c.members.count
  1754 + assert_equal "one member", c.more_popular_label
  1755 + end
  1756 +
  1757 + should "return the number of members on label if the profile has more than one member" do
  1758 + p1 = fast_create(Profile)
  1759 + p2 = fast_create(Person)
  1760 + c = fast_create(Community)
  1761 + c.add_member(p1)
  1762 + c.add_member(p2)
  1763 + assert_equal 2, c.members.count
  1764 + assert_equal "2 members", c.more_popular_label
  1765 +
  1766 + p3 = fast_create(Person)
  1767 + c.add_member(p3)
  1768 + assert_equal 3, c.members.count
  1769 + assert_equal "3 members", c.more_popular_label
  1770 + end
  1771 +
1566 1772 private
1567 1773  
1568 1774 def assert_invalid_identifier(id)
... ...