Commit 3ea04c54b00333b383bb1ac250c1f586ba822231
Exists in
master
and in
29 other branches
Merge commit 'refs/merge-requests/188' of git://gitorious.org/noosfero/noosfero …
…into merge-requests/188
Showing
14 changed files
with
276 additions
and
31 deletions
Show diff stats
app/helpers/application_helper.rb
... | ... | @@ -30,6 +30,10 @@ module ApplicationHelper |
30 | 30 | |
31 | 31 | include AccountHelper |
32 | 32 | |
33 | + include BlogHelper | |
34 | + | |
35 | + include ContentViewerHelper | |
36 | + | |
33 | 37 | def locale |
34 | 38 | (@page && !@page.language.blank?) ? @page.language : FastGettext.locale |
35 | 39 | end |
... | ... | @@ -994,6 +998,36 @@ module ApplicationHelper |
994 | 998 | content |
995 | 999 | end |
996 | 1000 | |
1001 | + # Please, use link_to by default! | |
1002 | + # This method was created to work around to inexplicable | |
1003 | + # chain of problems when display_short_format was called | |
1004 | + # from Article model for an ArticleBlock. | |
1005 | + def link_to_article(text, article, anchor=nil) | |
1006 | + if article.profile.domains.empty? | |
1007 | + href = "/#{article.url[:profile]}/" | |
1008 | + else | |
1009 | + href = "http://#{article.profile.domains.first.name}/" | |
1010 | + end | |
1011 | + href += article.url[:page].join('/') | |
1012 | + href += '#' + anchor if anchor | |
1013 | + content_tag('a', text, :href => href) | |
1014 | + end | |
1015 | + | |
1016 | + def display_short_format(article, options={}) | |
1017 | + options[:comments_link] ||= true | |
1018 | + options[:read_more_link] ||= true | |
1019 | + html = content_tag('div', | |
1020 | + article.lead + | |
1021 | + content_tag('div', | |
1022 | + (options[:comments_link] ? link_to_comments(article) : '') + | |
1023 | + (options[:read_more_link] ? link_to_article( _('Read more'), article) : ''), | |
1024 | + :class => 'read-more' | |
1025 | + ), | |
1026 | + :class => 'short-post' | |
1027 | + ) | |
1028 | + html | |
1029 | + end | |
1030 | + | |
997 | 1031 | def colorpicker_field(object_name, method, options = {}) |
998 | 1032 | text_field(object_name, method, options.merge(:class => 'colorpicker_field')) |
999 | 1033 | end | ... | ... |
app/helpers/blog_helper.rb
... | ... | @@ -18,7 +18,8 @@ module BlogHelper |
18 | 18 | pagination = will_paginate(articles, { |
19 | 19 | :param_name => 'npage', |
20 | 20 | :previous_label => _('« Newer posts'), |
21 | - :next_label => _('Older posts »') | |
21 | + :next_label => _('Older posts »'), | |
22 | + :params => {:action=>"view_page", :page=>articles.first.parent.path.split('/'), :controller=>"content_viewer"} | |
22 | 23 | }) |
23 | 24 | content = [] |
24 | 25 | artic_len = articles.length |
... | ... | @@ -46,18 +47,6 @@ module BlogHelper |
46 | 47 | article_title(article, :no_comments => no_comments) + html |
47 | 48 | end |
48 | 49 | |
49 | - def display_short_format(article) | |
50 | - html = content_tag('div', | |
51 | - article.lead + | |
52 | - content_tag('div', | |
53 | - link_to_comments(article) + | |
54 | - link_to( _('Read more'), article.url), | |
55 | - :class => 'read-more'), | |
56 | - :class => 'short-post' | |
57 | - ) | |
58 | - html | |
59 | - end | |
60 | - | |
61 | 50 | def display_full_format(article) |
62 | 51 | html = article_to_html(article) |
63 | 52 | html = content_tag('p', html) if ! html.include?('</p>') | ... | ... |
app/helpers/content_viewer_helper.rb
... | ... | @@ -2,6 +2,7 @@ module ContentViewerHelper |
2 | 2 | |
3 | 3 | include BlogHelper |
4 | 4 | include ForumHelper |
5 | + include ActionView::Helpers::TagHelper | |
5 | 6 | |
6 | 7 | def number_of_comments(article) |
7 | 8 | n = article.comments.without_spam.count |
... | ... | @@ -36,7 +37,7 @@ module ContentViewerHelper |
36 | 37 | |
37 | 38 | def link_to_comments(article, args = {}) |
38 | 39 | return '' unless article.accept_comments? |
39 | - link_to(number_of_comments(article), article.url.merge(:anchor => 'comments_list') ) | |
40 | + link_to_article number_of_comments(article), article, 'comments_list' | |
40 | 41 | end |
41 | 42 | |
42 | 43 | def article_translations(article) | ... | ... |
app/helpers/dates_helper.rb
... | ... | @@ -23,11 +23,13 @@ module DatesHelper |
23 | 23 | end |
24 | 24 | |
25 | 25 | # formats a date for displaying. |
26 | - def show_date(date, use_numbers = false) | |
26 | + def show_date(date, use_numbers = false, year=true) | |
27 | 27 | if date && use_numbers |
28 | - _('%{month}/%{day}/%{year}') % { :day => date.day, :month => date.month, :year => date.year } | |
28 | + date_format = year ? _('%{month}/%{day}/%{year}') : _('%{month}/%{day}') | |
29 | + date_format % { :day => date.day, :month => date.month, :year => date.year } | |
29 | 30 | elsif date |
30 | - _('%{month} %{day}, %{year}') % { :day => date.day, :month => month_name(date.month), :year => date.year } | |
31 | + date_format = year ? _('%{month_name} %{day}, %{year}') : _('%{month_name} %{day}') | |
32 | + date_format % { :day => date.day, :month_name => month_name(date.month), :year => date.year } | |
31 | 33 | else |
32 | 34 | '' |
33 | 35 | end |
... | ... | @@ -46,7 +48,27 @@ module DatesHelper |
46 | 48 | if (date1 == date2) || (date2.nil?) |
47 | 49 | show_date(date1, use_numbers) |
48 | 50 | else |
49 | - _('from %{date1} to %{date2}') % {:date1 => show_date(date1, use_numbers), :date2 => show_date(date2, use_numbers)} | |
51 | + if date1.year == date2.year | |
52 | + if date1.month == date2.month | |
53 | + _('from %{month} %{day1} to %{day2}, %{year}') % { | |
54 | + :day1 => date1.day, | |
55 | + :day2 => date2.day, | |
56 | + :month => use_numbers ? date1.month : month_name(date1.month), | |
57 | + :year => date1.year | |
58 | + } | |
59 | + else | |
60 | + _('from %{date1} to %{date2}, %{year}') % { | |
61 | + :date1 => show_date(date1, use_numbers, false), | |
62 | + :date2 => show_date(date2, use_numbers, false), | |
63 | + :year => date1.year | |
64 | + } | |
65 | + end | |
66 | + else | |
67 | + _('from %{date1} to %{date2}') % { | |
68 | + :date1 => show_date(date1, use_numbers), | |
69 | + :date2 => show_date(date2, use_numbers) | |
70 | + } | |
71 | + end | |
50 | 72 | end |
51 | 73 | end |
52 | 74 | ... | ... |
app/models/article.rb
... | ... | @@ -236,8 +236,13 @@ class Article < ActiveRecord::Base |
236 | 236 | # The implementation in this class just provides the +body+ attribute as the |
237 | 237 | # HTML. Other article types can override this method to provide customized |
238 | 238 | # views of themselves. |
239 | + # (To override short format representation, override the lead method) | |
239 | 240 | def to_html(options = {}) |
240 | - body || '' | |
241 | + if options[:format] == 'short' | |
242 | + display_short_format(self) | |
243 | + else | |
244 | + body || '' | |
245 | + end | |
241 | 246 | end |
242 | 247 | |
243 | 248 | include ApplicationHelper | ... | ... |
app/models/article_block.rb
... | ... | @@ -12,7 +12,11 @@ class ArticleBlock < Block |
12 | 12 | block = self |
13 | 13 | lambda do |
14 | 14 | block_title(block.title) + |
15 | - (block.article ? article_to_html(block.article, :gallery_view => false) : _('Article not selected yet.')) | |
15 | + (block.article ? article_to_html(block.article, | |
16 | + :gallery_view => false, | |
17 | + :inside_block => block, # For Blogs and folders | |
18 | + :format => block.visualization_format # For Articles and contents | |
19 | + ) : _('Article not selected yet.')) | |
16 | 20 | end |
17 | 21 | end |
18 | 22 | |
... | ... | @@ -49,4 +53,14 @@ class ArticleBlock < Block |
49 | 53 | self.box.owner.kind_of?(Environment) ? self.box.owner.portal_community.articles : self.box.owner.articles |
50 | 54 | end |
51 | 55 | |
56 | + def posts_per_page | |
57 | + self.settings[:posts_per_page] or 1 | |
58 | + end | |
59 | + | |
60 | + def posts_per_page= value | |
61 | + value = value.to_i | |
62 | + self.settings[:posts_per_page] = value if value > 0 | |
63 | + end | |
64 | + | |
65 | + settings_items :visualization_format, :type => :string, :default => 'short' | |
52 | 66 | end | ... | ... |
app/models/blog.rb
... | ... | @@ -24,8 +24,9 @@ class Blog < Folder |
24 | 24 | # FIXME isn't this too much including just to be able to generate some HTML? |
25 | 25 | include ActionView::Helpers::TagHelper |
26 | 26 | def to_html(options = {}) |
27 | + me = self | |
27 | 28 | lambda do |
28 | - render :file => 'content_viewer/blog_page' | |
29 | + render :file => 'content_viewer/blog_page', :locals => { :blog=>me, :inside_block=>options[:inside_block] } | |
29 | 30 | end |
30 | 31 | end |
31 | 32 | ... | ... |
app/models/event.rb
... | ... | @@ -104,18 +104,30 @@ class Event < Article |
104 | 104 | } |
105 | 105 | } |
106 | 106 | |
107 | + # TODO: some good soul, please clean this ugly hack: | |
107 | 108 | if self.body |
108 | 109 | html.div('_____XXXX_DESCRIPTION_GOES_HERE_XXXX_____', :class => 'event-description') |
109 | 110 | end |
110 | 111 | } |
111 | 112 | |
112 | 113 | if self.body |
113 | - result.sub!('_____XXXX_DESCRIPTION_GOES_HERE_XXXX_____', self.body) | |
114 | + if options[:format] == 'short' | |
115 | + result.sub!('_____XXXX_DESCRIPTION_GOES_HERE_XXXX_____', display_short_format(self)) | |
116 | + else | |
117 | + result.sub!('_____XXXX_DESCRIPTION_GOES_HERE_XXXX_____', self.body) | |
118 | + end | |
114 | 119 | end |
115 | 120 | |
116 | 121 | result |
117 | 122 | end |
118 | 123 | |
124 | + def lead | |
125 | + content_tag('div', | |
126 | + show_period(start_date, end_date), | |
127 | + :class => 'event-dates' | |
128 | + ) + super | |
129 | + end | |
130 | + | |
119 | 131 | def event? |
120 | 132 | true |
121 | 133 | end | ... | ... |
app/views/box_organizer/_article_block.rhtml
1 | -<div class='article-block-edition'> | |
1 | +<div class="article-block-edition"> | |
2 | 2 | <% if @block.box.owner.kind_of?(Environment) and @block.box.owner.portal_community.nil? %> |
3 | - <p id='no_portal_community'> | |
3 | + <p id="no_portal_community"> | |
4 | 4 | <%= _("You don't have an community defined as the portal community. Define it before use this block properly.") %> |
5 | 5 | </p> |
6 | 6 | <% else %> |
7 | - <% articles = @block.available_articles.select {|article| !article.folder? } %> | |
8 | - <%= select_tag('block[article_id]', options_for_select_with_title(articles.map {|item| [item.path, item.id]}, @block.article ? @block.article.id : nil)) %> | |
7 | + <% | |
8 | + articles = @block.available_articles.select {|a| !a.folder? || a.blog? } | |
9 | + firstText = articles[articles.find_index{|a| a.kind_of?TextArticle}||-1] | |
10 | + selected = @block.article || firstText | |
11 | + %> | |
12 | + <%= select_tag( | |
13 | + 'block[article_id]', | |
14 | + options_for_select_with_title(articles.map {|item| [item.path, item.id]}, selected.id), | |
15 | + :onchange => 'this.changedTo(this.value)' | |
16 | + )%> | |
17 | + <div id="block_blog_options" style="display:none"> | |
18 | + <%= labelled_form_field( | |
19 | + _('Number of posts:'), | |
20 | + text_field_tag('block[posts_per_page]', @block.posts_per_page) | |
21 | + )%> | |
22 | + </div> | |
23 | + <%= labelled_form_field( | |
24 | + _('How to display this content:'), | |
25 | + select_tag( | |
26 | + 'block[visualization_format]', | |
27 | + options_for_select([[_('Lead'), 'short'], [_('Full post'), 'full']], @block.visualization_format) | |
28 | + ) | |
29 | + )%> | |
30 | + <% blogs = @block.available_articles.select{|a|a.blog?} %> | |
31 | + <script> | |
32 | + var select = jQuery("#block_article_id")[0]; | |
33 | + select.blogs = <%= blogs.map{|b| b.id.to_s }.to_json %>; | |
34 | + select.changedTo = function(articleId) { | |
35 | + var blogSelected = this.blogs.indexOf(articleId) != -1; | |
36 | + jQuery("#block_blog_options").toggle(blogSelected); | |
37 | + } | |
38 | + select.changedTo('<%= selected.id %>'); | |
39 | + </script> | |
9 | 40 | <% end %> |
10 | 41 | </div> | ... | ... |
app/views/content_viewer/blog_page.rhtml
1 | -<% add_rss_feed_to_head(@page.name, @page.feed.url) if @page.blog? && @page.feed %> | |
1 | +<% add_rss_feed_to_head(blog.name, blog.feed.url) if blog.blog? && blog.feed %> | |
2 | 2 | |
3 | -<%= content_tag('em', _('(external feed was not loaded yet)'), :id => 'external-feed-info', :class => 'metadata') if @page.blog? && @page.external_feed && @page.external_feed.enabled && @page.external_feed.fetched_at.nil? %> | |
3 | +<%= content_tag('em', _('(external feed was not loaded yet)'), :id => 'external-feed-info', :class => 'metadata') if blog.blog? && blog.external_feed && blog.external_feed.enabled && blog.external_feed.fetched_at.nil? %> | |
4 | 4 | |
5 | 5 | <div> |
6 | 6 | <div class='blog-description'> |
7 | - <%= @page.body %> | |
7 | + <%= blog.body %> | |
8 | 8 | </div> |
9 | 9 | </div> |
10 | 10 | <hr class="pre-posts"/> |
11 | 11 | <div class="blog-posts"> |
12 | - <%= (@page.empty? ? content_tag('em', _('(no posts)')) : list_posts(@posts, @page.visualization_format)) %> | |
12 | + <%= | |
13 | + posts = @posts | |
14 | + format = blog.visualization_format | |
15 | + if inside_block | |
16 | + posts = blog.posts.paginate(:page=>1, :per_page=>inside_block.posts_per_page) | |
17 | + format = inside_block.visualization_format | |
18 | + end | |
19 | + (blog.empty? ? content_tag('em', _('(no posts)')) : list_posts(posts, format)) | |
20 | + %> | |
13 | 21 | </div> | ... | ... |
... | ... | @@ -0,0 +1,56 @@ |
1 | +require "#{File.dirname(__FILE__)}/../test_helper" | |
2 | + | |
3 | +class BlocksIntegrationTest < ActionController::IntegrationTest | |
4 | + def blog_on_article_block_bootstrap | |
5 | + profile = fast_create(Profile) | |
6 | + blog = fast_create(Blog, :name => 'Blog', :profile_id => profile.id) | |
7 | + fast_create(TinyMceArticle, :name => "First Post", :profile_id => profile.id, :parent_id => blog.id, :body => '<p> Wasserstoffbombe </p>') | |
8 | + fast_create(TinyMceArticle, :name => "A Post", :profile_id => profile.id, :parent_id => blog.id, :body => '<p>Lorem ipsum dolor sit amet</p> <p>Second paragraph</p>') | |
9 | + block = ArticleBlock.new | |
10 | + block.article = blog | |
11 | + profile.boxes << Box.new | |
12 | + profile.boxes.first.blocks << block | |
13 | + return block | |
14 | + end | |
15 | + | |
16 | + should 'allow blog as article block content' do | |
17 | + block = blog_on_article_block_bootstrap | |
18 | + get "/profile/#{block.owner.identifier}" | |
19 | + assert_match(/Lorem ipsum dolor sit amet/, @response.body) | |
20 | + end | |
21 | + | |
22 | + should 'display short version for block posts on article block' do | |
23 | + block = blog_on_article_block_bootstrap | |
24 | + get "/profile/#{block.owner.identifier}" | |
25 | + assert_no_match(/Second paragraph/, @response.body) | |
26 | + end | |
27 | + | |
28 | + should 'display full version for block posts on article block' do | |
29 | + block = blog_on_article_block_bootstrap | |
30 | + block.visualization_format = 'full' | |
31 | + block.save! | |
32 | + get "/profile/#{block.owner.identifier}" | |
33 | + assert_match(/Second paragraph/, @response.body) | |
34 | + end | |
35 | + | |
36 | + should 'display configured number of blog posts on article block' do | |
37 | + block = blog_on_article_block_bootstrap | |
38 | + block.posts_per_page = 2 | |
39 | + block.save! | |
40 | + get "/profile/#{block.owner.identifier}" | |
41 | + assert_match(/Lorem ipsum dolor sit amet/, @response.body) | |
42 | + assert_match(/Wasserstoffbombe/, @response.body) | |
43 | + end | |
44 | + | |
45 | + should 'link correctly in pagination' do | |
46 | + block = blog_on_article_block_bootstrap | |
47 | + p = block.owner | |
48 | + b = block.article | |
49 | + f = fast_create(Folder, :name => 'Folder1', :profile_id => p.id) | |
50 | + b.parent = f | |
51 | + b.save! | |
52 | + get "/profile/#{block.owner.identifier}" | |
53 | + assert_tag :tag => 'a', :attributes => { :href => "/#{p.identifier}/#{f.slug}/#{b.slug}?npage=2" } | |
54 | + end | |
55 | + | |
56 | +end | ... | ... |
test/unit/application_helper_test.rb
... | ... | @@ -657,6 +657,43 @@ class ApplicationHelperTest < ActiveSupport::TestCase |
657 | 657 | assert_not_nil add_zoom_to_images |
658 | 658 | end |
659 | 659 | |
660 | + should 'link to article' do | |
661 | + c = fast_create(Community) | |
662 | + a = fast_create(TinyMceArticle, :profile_id => c.id) | |
663 | + assert_equal( | |
664 | + "<a href=\"/#{c.identifier}/#{a.slug}\">x</a>", | |
665 | + link_to_article('x', a) ) | |
666 | + end | |
667 | + | |
668 | + should 'link to article, with anchor' do | |
669 | + c = fast_create(Community) | |
670 | + a = fast_create(TinyMceArticle, :profile_id => c.id) | |
671 | + assert_equal( | |
672 | + "<a href=\"/#{c.identifier}/#{a.slug}#place\">x</a>", | |
673 | + link_to_article('x', a, 'place') ) | |
674 | + end | |
675 | + | |
676 | + should 'link to article, in a blog' do | |
677 | + c = fast_create(Community) | |
678 | + b = fast_create(Blog, :profile_id => c.id) | |
679 | + a = fast_create(TinyMceArticle, :profile_id => c.id, :parent_id => b.id) | |
680 | + a.save! # needed to link to the parent blog | |
681 | + assert_equal( | |
682 | + "<a href=\"/#{c.identifier}/#{b.slug}/#{a.slug}\">x</a>", | |
683 | + link_to_article('x', a) ) | |
684 | + end | |
685 | + | |
686 | + should 'link to article, in a profile with domain' do | |
687 | + c = fast_create(Community) | |
688 | + c.domains << Domain.new(:name=>'domain.xyz') | |
689 | + b = fast_create(Blog, :profile_id => c.id) | |
690 | + a = fast_create(TinyMceArticle, :profile_id => c.id, :parent_id => b.id) | |
691 | + a.save! | |
692 | + assert_equal( | |
693 | + "<a href=\"http://domain.xyz/#{b.slug}/#{a.slug}\">x</a>", | |
694 | + link_to_article('x', a) ) | |
695 | + end | |
696 | + | |
660 | 697 | protected |
661 | 698 | include NoosferoTestHelper |
662 | 699 | ... | ... |
test/unit/article_test.rb
... | ... | @@ -97,6 +97,21 @@ class ArticleTest < ActiveSupport::TestCase |
97 | 97 | assert_equal '', a.to_html |
98 | 98 | end |
99 | 99 | |
100 | + should 'provide short html version' do | |
101 | + a = fast_create(Article, :body => 'full body', :abstract => 'lead') | |
102 | + a.stubs(:url).returns({:x=>'none'}) | |
103 | + a.stubs(:link_to).returns('<link>') | |
104 | + def a.content_tag (tag, content, c=nil) | |
105 | + "<#{tag}>#{content}</#{tag}>" | |
106 | + end | |
107 | + assert_match /<div>lead.*/, a.to_html(:format=>'short') | |
108 | + end | |
109 | + | |
110 | + should 'provide full html version' do | |
111 | + a = fast_create(Article, :body => 'full body', :abstract => 'lead') | |
112 | + assert_equal 'full body', a.to_html(:format=>'full body') | |
113 | + end | |
114 | + | |
100 | 115 | should 'provide first paragraph of HTML version' do |
101 | 116 | profile = create_user('testinguser').person |
102 | 117 | a = fast_create(Article, :name => 'my article', :profile_id => profile.id) | ... | ... |
test/unit/dates_helper_test.rb
... | ... | @@ -10,20 +10,40 @@ class DatesHelperTest < ActiveSupport::TestCase |
10 | 10 | end |
11 | 11 | |
12 | 12 | should 'display date with translation' do |
13 | - expects(:_).with('%{month} %{day}, %{year}').returns('%{day} de %{month} de %{year}') | |
13 | + expects(:_).with('%{month_name} %{day}, %{year}').returns('%{day} de %{month_name} de %{year}') | |
14 | 14 | expects(:_).with('January').returns('Janeiro') |
15 | 15 | assert_equal '11 de Janeiro de 2008', show_date(Date.new(2008, 1, 11)) |
16 | 16 | end |
17 | 17 | |
18 | 18 | should 'generate period with two dates' do |
19 | 19 | date1 = mock |
20 | + date1.stubs(:year).returns('A') | |
20 | 21 | expects(:show_date).with(date1, anything).returns('XXX') |
21 | 22 | date2 = mock |
23 | + date2.stubs(:year).returns('B') | |
22 | 24 | expects(:show_date).with(date2, anything).returns('YYY') |
23 | 25 | expects(:_).with('from %{date1} to %{date2}').returns('from %{date1} to %{date2}') |
24 | 26 | assert_equal 'from XXX to YYY', show_period(date1, date2) |
25 | 27 | end |
26 | 28 | |
29 | + should 'generate period with in two diferent years' do | |
30 | + date1 = Date.new(1920, 1, 2) | |
31 | + date2 = Date.new(1992, 4, 6) | |
32 | + assert_equal 'from January 2, 1920 to April 6, 1992', show_period(date1, date2) | |
33 | + end | |
34 | + | |
35 | + should 'generate period with in two diferent months of the same year' do | |
36 | + date1 = Date.new(2013, 2, 1) | |
37 | + date2 = Date.new(2013, 3, 1) | |
38 | + assert_equal 'from February 1 to March 1, 2013', show_period(date1, date2) | |
39 | + end | |
40 | + | |
41 | + should 'generate period with in two days of the same month' do | |
42 | + date1 = Date.new(2013, 3, 27) | |
43 | + date2 = Date.new(2013, 3, 28) | |
44 | + assert_equal 'from March 27 to 28, 2013', show_period(date1, date2) | |
45 | + end | |
46 | + | |
27 | 47 | should 'generate period with two equal dates' do |
28 | 48 | date1 = mock |
29 | 49 | expects(:show_date).with(date1, anything).returns('XXX') | ... | ... |