Commit 2da2072eb0931333f22edc4b8783260b34ac23f8
1 parent
fc7e02f2
Exists in
master
and in
29 other branches
Added Contents on navigation bar
Included new named_scopes for browsing contents: * more_recent * more_comments * more_views A named_scope to filter only text articles * text_articles Included new browse_contents_menu on base theme (ActionItem2069)
Showing
13 changed files
with
291 additions
and
8 deletions
Show diff stats
app/controllers/public/browse_controller.rb
... | ... | @@ -6,6 +6,8 @@ class BrowseController < PublicController |
6 | 6 | more_recent |
7 | 7 | more_active |
8 | 8 | more_popular |
9 | + more_comments | |
10 | + more_views | |
9 | 11 | ) |
10 | 12 | |
11 | 13 | def per_page |
... | ... | @@ -36,6 +38,18 @@ class BrowseController < PublicController |
36 | 38 | @results = @results.compact.paginate(:per_page => per_page, :page => params[:page]) |
37 | 39 | end |
38 | 40 | |
41 | + def contents | |
42 | + @filter = filter | |
43 | + @title = self.filter_description(params[:action] + '_' + @filter ) | |
44 | + | |
45 | + @results = @environment.articles.published.text_articles.send(@filter) | |
46 | + | |
47 | + if !params[:query].blank? | |
48 | + @results = @results.find_by_contents(params[:query]) | |
49 | + end | |
50 | + @results = @results.compact.paginate(:per_page => per_page, :page => params[:page]) | |
51 | + end | |
52 | + | |
39 | 53 | protected |
40 | 54 | |
41 | 55 | def filter |
... | ... | @@ -54,6 +68,9 @@ class BrowseController < PublicController |
54 | 68 | 'communities_more_recent' => _('More recent communities'), |
55 | 69 | 'communities_more_active' => _('More active communities'), |
56 | 70 | 'communities_more_popular' => _('More popular communities'), |
71 | + 'contents_more_recent' => _('More recent contents'), | |
72 | + 'contents_more_views' => _('Most viewed contents'), | |
73 | + 'contents_more_comments' => _('Most commented contents'), | |
57 | 74 | }[str] || str |
58 | 75 | end |
59 | 76 | ... | ... |
app/helpers/application_helper.rb
... | ... | @@ -1134,6 +1134,17 @@ module ApplicationHelper |
1134 | 1134 | link_to(content_tag(:span, _('Communities Menu')), '#', :onclick => "toggleSubmenu(this,'',#{links.to_json}); return false", :class => 'menu-submenu-trigger up', :id => 'submenu-communities-trigger') |
1135 | 1135 | end |
1136 | 1136 | |
1137 | + def browse_contents_menu | |
1138 | + links = [ | |
1139 | + {s_('contents|More Comments') => {:href => url_for({:controller => 'browse', :action => 'contents', :filter => 'more_comments'})}}, | |
1140 | + {s_('contents|More Views') => {:href => url_for({:controller => 'browse', :action => 'contents', :filter => 'more_views'})}}, | |
1141 | + {s_('contents|More Recent') => {:href => url_for({:controller => 'browse', :action => 'contents', :filter => 'more_recent'})}} | |
1142 | + ] | |
1143 | + | |
1144 | + link_to(content_tag(:span, _('Contents'), :class => 'icon-blog'), {:controller => "browse", :action => 'contents'}, :id => 'submenu-contents') + | |
1145 | + link_to(content_tag(:span, _('Contents Menu')), '#', :onclick => "toggleSubmenu(this,'',#{links.to_json}); return false", :class => 'menu-submenu-trigger up', :id => 'submenu-contents-trigger') | |
1146 | + end | |
1147 | + | |
1137 | 1148 | def pagination_links(collection, options={}) |
1138 | 1149 | options = {:prev_label => '« ' + _('Previous'), :next_label => _('Next') + ' »'}.merge(options) |
1139 | 1150 | will_paginate(collection, options) | ... | ... |
app/models/article.rb
... | ... | @@ -355,11 +355,20 @@ class Article < ActiveRecord::Base |
355 | 355 | ['Folder', 'Blog', 'Forum', 'Gallery'] |
356 | 356 | end |
357 | 357 | |
358 | + def self.text_article_types | |
359 | + ['TextArticle', 'TextileArticle', 'TinyMceArticle'] | |
360 | + end | |
361 | + | |
358 | 362 | named_scope :published, :conditions => { :published => true } |
359 | 363 | named_scope :folders, :conditions => { :type => folder_types} |
360 | 364 | named_scope :no_folders, :conditions => ['type NOT IN (?)', folder_types] |
361 | 365 | named_scope :galleries, :conditions => { :type => 'Gallery' } |
362 | 366 | named_scope :images, :conditions => { :is_image => true } |
367 | + named_scope :text_articles, :conditions => [ 'articles.type IN (?)', text_article_types ] | |
368 | + | |
369 | + named_scope :more_comments, :order => "comments_count DESC" | |
370 | + named_scope :more_views, :order => "hits DESC" | |
371 | + named_scope :more_recent, :order => "created_at DESC" | |
363 | 372 | |
364 | 373 | def self.display_filter(user, profile) |
365 | 374 | return {:conditions => ['published = ?', true]} if !user |
... | ... | @@ -528,6 +537,28 @@ class Article < ActiveRecord::Base |
528 | 537 | end |
529 | 538 | end |
530 | 539 | |
540 | + def more_comments_label | |
541 | + amount = self.comments_count | |
542 | + { | |
543 | + 0 => _('no comments'), | |
544 | + 1 => _('one comment') | |
545 | + }[amount] || _("%s comments") % amount | |
546 | + | |
547 | + end | |
548 | + | |
549 | + def more_views_label | |
550 | + amount = self.hits | |
551 | + { | |
552 | + 0 => _('no views'), | |
553 | + 1 => _('one view') | |
554 | + }[amount] || _("%s views") % amount | |
555 | + | |
556 | + end | |
557 | + | |
558 | + def more_recent_label | |
559 | + _('Created at: ') | |
560 | + end | |
561 | + | |
531 | 562 | private |
532 | 563 | |
533 | 564 | def sanitize_tag_list | ... | ... |
... | ... | @@ -0,0 +1,11 @@ |
1 | +<li class="<%= 'browse-results-type-content ' + icon_for_article(result) %>"> | |
2 | + <strong><%= link_to(result.title, result.view_url) %></strong> | |
3 | + <div class="item_meta"> | |
4 | + <span class="item_by"> | |
5 | + <%= _('by %s') % link_to(result.author.name, result.author.url) %> | |
6 | + </span> | |
7 | + <span class="extra-info"> | |
8 | + <%= (@filter == 'more_recent' ? result.send(@filter + '_label') + show_date(result.created_at) : result.send(@filter + '_label')) %> | |
9 | + </span> | |
10 | + </div> | |
11 | +</li> | ... | ... |
app/views/browse/_display_results.rhtml
... | ... | @@ -8,7 +8,7 @@ |
8 | 8 | <% end %> |
9 | 9 | <ul class='common-profile-list-block'> |
10 | 10 | <% @results.each do |result| %> |
11 | - <%= render :partial => partial_for_class(result.class), :locals => {:profile => result} %> | |
11 | + <%= render :partial => partial_for_class(result.class), :locals => {:result => result} %> | |
12 | 12 | <% end %> |
13 | 13 | </ul> |
14 | 14 | <br style='clear: both;'> | ... | ... |
app/views/browse/_person.rhtml
1 | -<%= profile_image_link profile, :portrait, 'li', | |
2 | - "<span class='adr'>#{profile.city}</span>" + | |
3 | - (@filter == 'more_recent' ? profile.send(@filter + '_label') + show_date(profile.created_at) : profile.send(@filter + '_label')) %> | |
1 | +<%= profile_image_link result, :portrait, 'li', | |
2 | + "<span class='adr'>#{result.city}</span>" + | |
3 | + (@filter == 'more_recent' ? result.send(@filter + '_label') + show_date(result.created_at) : result.send(@filter + '_label')) %> | ... | ... |
app/views/browse/_profile.rhtml
1 | -<%= profile_image_link profile, :portrait, 'li', @filter == 'more_recent' ? profile.send(@filter + '_label') + show_date(profile.created_at) : profile.send(@filter + '_label') %> | |
1 | +<%= profile_image_link result, :portrait, 'li', @filter == 'more_recent' ? result.send(@filter + '_label') + show_date(result.created_at) : result.send(@filter + '_label') %> | ... | ... |
features/browse.feature
... | ... | @@ -84,3 +84,26 @@ Feature: browse |
84 | 84 | And I should not see "Pedro Silva" |
85 | 85 | And I should not see "Paulo Neto" |
86 | 86 | And I should not see "Community Silva" |
87 | + | |
88 | + @selenium | |
89 | + Scenario: Show contents browse menu | |
90 | + Given I should not see "More Comments" | |
91 | + And I should not see "More Views" | |
92 | + And I should not see "More Recent" | |
93 | + When I click "#submenu-contents-trigger" | |
94 | + Then I should see "More Comments" | |
95 | + And I should see "More Views" | |
96 | + And I should see "More Recent" | |
97 | + | |
98 | + Scenario: Browse contents by query | |
99 | + Given the following articles | |
100 | + | owner | name | body | | |
101 | + | joaosilva | Bees can fly | this is an article | | |
102 | + | joaosilva | Bees and ants are insects | this is another article | | |
103 | + | joaosilva | Ants are small | this is another article | | |
104 | + When I go to /browse/contents | |
105 | + And I fill in "bees" for "query" | |
106 | + And I press "Search" | |
107 | + Then I should see "Bees can fly" | |
108 | + And I should see "Bees and ants are insects" | |
109 | + And I should not see "Ants are small" | ... | ... |
public/designs/themes/base/navigation.rhtml
public/stylesheets/application.css
... | ... | @@ -4503,23 +4503,30 @@ h1#agenda-title { |
4503 | 4503 | } |
4504 | 4504 | |
4505 | 4505 | .controller-search #content .search-results-type-article li, |
4506 | -.controller-search #content .search-results-type-event li { | |
4506 | +.controller-search #content .search-results-type-event li, | |
4507 | +.controller-browse #content li.browse-results-type-content { | |
4507 | 4508 | padding: 0px 0px 4px 20px; |
4508 | 4509 | background-repeat: no-repeat; |
4509 | 4510 | border-color: transparent; |
4510 | 4511 | } |
4511 | 4512 | |
4512 | 4513 | .controller-search #content .search-results-type-article li:hover, |
4513 | -.controller-search #content .search-results-type-event li:hover { | |
4514 | +.controller-search #content .search-results-type-event li:hover, | |
4515 | +.controller-browse #content li.browse-results-type-content:hover { | |
4514 | 4516 | background-color: transparent; |
4515 | 4517 | } |
4516 | 4518 | |
4517 | 4519 | .controller-search .search-results-type-article .item_meta, |
4518 | -.controller-search .search-results-type-event .item_meta { | |
4520 | +.controller-search .search-results-type-event .item_meta, | |
4521 | +.controller-browse .browse-results-type-content .item_meta { | |
4519 | 4522 | font-size: 10px; |
4520 | 4523 | color: #888; |
4521 | 4524 | } |
4522 | 4525 | |
4526 | +.controller-browse .browse-results-type-content .item_meta span { | |
4527 | + width: auto; | |
4528 | +} | |
4529 | + | |
4523 | 4530 | .search-results-type-article.search-results-innerbox { |
4524 | 4531 | overflow: auto; |
4525 | 4532 | } | ... | ... |
test/functional/browse_controller_test.rb
... | ... | @@ -19,7 +19,10 @@ class BrowseControllerTest < Test::Unit::TestCase |
19 | 19 | user.stubs(:email).returns('some@test.com') |
20 | 20 | user.stubs(:save!).returns(true) |
21 | 21 | Person.any_instance.stubs(:user).returns(user) |
22 | + @profile = create_user('testinguser').person | |
23 | + Article.destroy_all | |
22 | 24 | end |
25 | + attr_reader :profile | |
23 | 26 | |
24 | 27 | should 'search for people' do |
25 | 28 | Person.delete_all |
... | ... | @@ -274,4 +277,98 @@ class BrowseControllerTest < Test::Unit::TestCase |
274 | 277 | assert_not_includes assigns(:results), p1 |
275 | 278 | end |
276 | 279 | |
280 | + should 'search for contents' do | |
281 | + small = create(TinyMceArticle, :name => 'Testing article', :body => 'A small article for testing', :profile => profile) | |
282 | + create(TinyMceArticle, :name => 'Testing article 2', :body => 'A big article for testing', :profile => profile) | |
283 | + | |
284 | + get :contents, :query => 'small' | |
285 | + assert_equal [small], assigns(:results) | |
286 | + end | |
287 | + | |
288 | + should 'list all contents ordered by more recent by default' do | |
289 | + c1 = create(TinyMceArticle, :name => 'Testing article 1', :body => 'Article body 1', :profile => profile, :created_at => DateTime.now - 2) | |
290 | + c2 = create(TinyMceArticle, :name => 'Testing article 2', :body => 'Article body 2', :profile => profile, :created_at => DateTime.now - 1) | |
291 | + c3 = create(TinyMceArticle, :name => 'Testing article 3', :body => 'Article body 3', :profile => profile) | |
292 | + | |
293 | + get :contents | |
294 | + assert_equal [c3,c2,c1], assigns(:results) | |
295 | + end | |
296 | + | |
297 | + should 'paginate search of contents in groups of 27' do | |
298 | + 1.upto(30).map do |n| | |
299 | + create(TinyMceArticle, :name => "Testing article #{n}", :body => "Article body #{n}", :profile => profile) | |
300 | + end | |
301 | + | |
302 | + get :contents | |
303 | + assert_equal 27 , assigns(:results).count | |
304 | + assert_tag :a, '', :attributes => {:class => 'next_page'} | |
305 | + end | |
306 | + | |
307 | + should 'paginate ferret search of contents in groups of 27' do | |
308 | + 1.upto(30).map do |n| | |
309 | + create(TinyMceArticle, :name => "Testing article #{n}", :body => "Article body #{n}", :profile => profile) | |
310 | + end | |
311 | + | |
312 | + get :contents, :query => 'Testing' | |
313 | + assert_equal 27 , assigns(:results).count | |
314 | + assert_tag :a, '', :attributes => {:class => 'next_page'} | |
315 | + end | |
316 | + | |
317 | + should 'list all contents filter by more comments' do | |
318 | + article1 = fast_create(TinyMceArticle, :body => '<p>Article to test browse contents', :profile_id => profile.id, :comments_count => 5) | |
319 | + article2 = fast_create(TinyMceArticle, :body => '<p>Another article to test browse contents</p>', :profile_id => profile.id, :comments_count => 10) | |
320 | + article3 = fast_create(TinyMceArticle, :body => '<p>Another article to test browse contents</p>', :profile_id => profile.id, :comments_count => 1) | |
321 | + | |
322 | + get :contents, :filter => 'more_comments' | |
323 | + assert_equal [article2,article1,article3] , assigns(:results) | |
324 | + end | |
325 | + | |
326 | + should 'list all contents filter by more views' do | |
327 | + article1 = fast_create(TinyMceArticle, :body => '<p>Article to test browse contents', :profile_id => profile.id, :hits => 5) | |
328 | + article2 = fast_create(TinyMceArticle, :body => '<p>Another article to test browse contents</p>', :profile_id => profile.id, :hits => 10) | |
329 | + article3 = fast_create(TinyMceArticle, :body => '<p>Another article to test browse contents</p>', :profile_id => profile.id, :hits => 1) | |
330 | + | |
331 | + get :contents, :filter => 'more_views' | |
332 | + assert_equal [article2,article1,article3], assigns(:results) | |
333 | + end | |
334 | + | |
335 | + should 'have the more_recent filter by default' do | |
336 | + get :contents, :filter => 'more_recent' | |
337 | + assert_equal 'more_recent' , assigns(:filter) | |
338 | + | |
339 | + get :contents, :filter => 'more_comments' | |
340 | + assert_equal 'more_comments' , assigns(:filter) | |
341 | + | |
342 | + get :contents, :filter => 'more_views' | |
343 | + assert_equal 'more_views' , assigns(:filter) | |
344 | + | |
345 | + get :contents, :filter => 'more_anything' | |
346 | + assert_equal 'more_recent' , assigns(:filter) | |
347 | + end | |
348 | + | |
349 | + should 'the contents filter define the title' do | |
350 | + get :contents, :filter => 'more_recent' | |
351 | + assert_equal 'More recent contents' , assigns(:title) | |
352 | + assert_tag :h1, :content => 'More recent contents' | |
353 | + | |
354 | + get :contents, :filter => 'more_views' | |
355 | + assert_equal 'Most viewed contents' , assigns(:title) | |
356 | + assert_tag :h1, :content => 'Most viewed contents' | |
357 | + | |
358 | + get :contents, :filter => 'more_comments' | |
359 | + assert_equal 'Most commented contents' , assigns(:title) | |
360 | + assert_tag :h1, :content => 'Most commented contents' | |
361 | + | |
362 | + get :contents, :filter => 'more_anything' | |
363 | + assert_equal 'More recent contents' , assigns(:title) | |
364 | + assert_tag :h1, :content => 'More recent contents' | |
365 | + end | |
366 | + | |
367 | + should "only include published contents in more_recent filter" do | |
368 | + # assuming that all filters behave the same! | |
369 | + article = fast_create(TinyMceArticle, :body => '<p>Article to test browse contents', :profile_id => profile.id, :published => false) | |
370 | + get :contents, :filter => 'more_recent' | |
371 | + assert_not_includes assigns(:results), article | |
372 | + end | |
373 | + | |
277 | 374 | end | ... | ... |
test/unit/article_test.rb
... | ... | @@ -1562,4 +1562,78 @@ class ArticleTest < Test::Unit::TestCase |
1562 | 1562 | end |
1563 | 1563 | end |
1564 | 1564 | |
1565 | + should 'find more recent contents' do | |
1566 | + Article.delete_all | |
1567 | + | |
1568 | + c1 = fast_create(TinyMceArticle, :name => 'Testing article 1', :body => 'Article body 1', :profile_id => profile.id, :created_at => DateTime.now - 4) | |
1569 | + c2 = fast_create(TinyMceArticle, :name => 'Testing article 2', :body => 'Article body 2', :profile_id => profile.id, :created_at => DateTime.now - 1) | |
1570 | + c3 = fast_create(TinyMceArticle, :name => 'Testing article 3', :body => 'Article body 3', :profile_id => profile.id, :created_at => DateTime.now - 3) | |
1571 | + | |
1572 | + assert_equal [c2,c3,c1] , Article.more_recent | |
1573 | + | |
1574 | + c4 = fast_create(TinyMceArticle, :name => 'Testing article 4', :body => 'Article body 4', :profile_id => profile.id, :created_at => DateTime.now - 2) | |
1575 | + assert_equal [c2,c4,c3,c1] , Article.more_recent | |
1576 | + end | |
1577 | + | |
1578 | + should 'respond to more comments' do | |
1579 | + assert_respond_to Article, :more_comments | |
1580 | + end | |
1581 | + | |
1582 | + should 'respond to more views' do | |
1583 | + assert_respond_to Article, :more_views | |
1584 | + end | |
1585 | + | |
1586 | + should "return the more recent label" do | |
1587 | + a = Article.new | |
1588 | + assert_equal "Created at: ", a.more_recent_label | |
1589 | + end | |
1590 | + | |
1591 | + should "return no comments if profile has 0 comments" do | |
1592 | + a = Article.new | |
1593 | + assert_equal 0, a.comments_count | |
1594 | + assert_equal "no comments", a.more_comments_label | |
1595 | + end | |
1596 | + | |
1597 | + should "return 1 comment on label if the content has 1 comment" do | |
1598 | + a = Article.new(:comments_count => 1) | |
1599 | + assert_equal 1, a.comments_count | |
1600 | + assert_equal "one comment", a.more_comments_label | |
1601 | + end | |
1602 | + | |
1603 | + should "return number of comments on label if the content has more than one comment" do | |
1604 | + a = Article.new(:comments_count => 4) | |
1605 | + assert_equal 4, a.comments_count | |
1606 | + assert_equal "4 comments", a.more_comments_label | |
1607 | + end | |
1608 | + | |
1609 | + should "return no views if profile has 0 views" do | |
1610 | + a = Article.new | |
1611 | + assert_equal 0, a.hits | |
1612 | + assert_equal "no views", a.more_views_label | |
1613 | + end | |
1614 | + | |
1615 | + should "return 1 view on label if the content has 1 view" do | |
1616 | + a = Article.new(:hits => 1) | |
1617 | + assert_equal 1, a.hits | |
1618 | + assert_equal "one view", a.more_views_label | |
1619 | + end | |
1620 | + | |
1621 | + should "return number of views on label if the content has more than one view" do | |
1622 | + a = Article.new(:hits => 4) | |
1623 | + assert_equal 4, a.hits | |
1624 | + assert_equal "4 views", a.more_views_label | |
1625 | + end | |
1626 | + | |
1627 | + should 'return only text articles' do | |
1628 | + Article.delete_all | |
1629 | + | |
1630 | + c1 = fast_create(TinyMceArticle, :name => 'Testing article 1', :body => 'Article body 1', :profile_id => profile.id) | |
1631 | + c2 = fast_create(TextArticle, :name => 'Testing article 2', :body => 'Article body 2', :profile_id => profile.id) | |
1632 | + c3 = fast_create(Event, :name => 'Testing article 3', :body => 'Article body 3', :profile_id => profile.id) | |
1633 | + c4 = fast_create(RssFeed, :name => 'Testing article 4', :body => 'Article body 4', :profile_id => profile.id) | |
1634 | + c5 = fast_create(TextileArticle, :name => 'Testing article 5', :body => 'Article body 5', :profile_id => profile.id) | |
1635 | + | |
1636 | + assert_equal [c1,c2,c5], Article.text_articles | |
1637 | + end | |
1638 | + | |
1565 | 1639 | end | ... | ... |