Commit dc0f987c9dbaa6c21eb65c0e937339eda03553d2

Authored by Evandro Junior
2 parents 7a09f62f 3a9e3895

Merge branch 'stable' of gitlab.com:participa/noosfero into stable

Showing 37 changed files with 263 additions and 107 deletions   Show diff stats
app/controllers/public/search_controller.rb
@@ -3,7 +3,10 @@ class SearchController < PublicController @@ -3,7 +3,10 @@ class SearchController < PublicController
3 helper TagsHelper 3 helper TagsHelper
4 include SearchHelper 4 include SearchHelper
5 include ActionView::Helpers::NumberHelper 5 include ActionView::Helpers::NumberHelper
  6 + include SanitizeParams
6 7
  8 +
  9 + before_filter :sanitize_params
7 before_filter :redirect_asset_param, :except => [:assets, :suggestions] 10 before_filter :redirect_asset_param, :except => [:assets, :suggestions]
8 before_filter :load_category, :except => :suggestions 11 before_filter :load_category, :except => :suggestions
9 before_filter :load_search_assets, :except => :suggestions 12 before_filter :load_search_assets, :except => :suggestions
app/helpers/blog_helper.rb
@@ -17,28 +17,28 @@ module BlogHelper @@ -17,28 +17,28 @@ module BlogHelper
17 _('Configure blog') 17 _('Configure blog')
18 end 18 end
19 19
20 - def list_posts(articles, format = 'full', paginate = true) 20 + def list_posts(articles, conf = { format: 'full', paginate: true })
21 pagination = will_paginate(articles, { 21 pagination = will_paginate(articles, {
22 :param_name => 'npage', 22 :param_name => 'npage',
23 :previous_label => _('« Newer posts'), 23 :previous_label => _('« Newer posts'),
24 :next_label => _('Older posts »'), 24 :next_label => _('Older posts »'),
25 :params => {:action=>"view_page", :page=>articles.first.parent.path.split('/'), :controller=>"content_viewer"} 25 :params => {:action=>"view_page", :page=>articles.first.parent.path.split('/'), :controller=>"content_viewer"}
26 - }) if articles.present? && paginate 26 + }) if articles.present? && conf[:paginate]
27 content = [] 27 content = []
28 artic_len = articles.length 28 artic_len = articles.length
29 articles.each_with_index{ |art,i| 29 articles.each_with_index{ |art,i|
30 - css_add = [ 'position-'+(i+1).to_s() ] 30 + css_add = [ 'blog-post', 'position-'+(i+1).to_s() ]
31 position = (i%2 == 0) ? 'odd-post' : 'even-post' 31 position = (i%2 == 0) ? 'odd-post' : 'even-post'
32 css_add << 'first' if i == 0 32 css_add << 'first' if i == 0
33 css_add << 'last' if i == (artic_len-1) 33 css_add << 'last' if i == (artic_len-1)
34 css_add << 'not-published' if !art.published? 34 css_add << 'not-published' if !art.published?
35 - css_add << position + '-inner'  
36 - content << content_tag('div',  
37 - content_tag('div',  
38 - display_post(art, format).html_safe + '<br style="clear:both"/>'.html_safe,  
39 - :class => 'blog-post ' + css_add.join(' '),  
40 - :id => "post-#{art.id}"), :class => position  
41 - ) 35 + css_add << position
  36 + content << (content_tag 'div', id: "post-#{art.id}", class: css_add do
  37 + content_tag 'div', class: position + '-inner blog-post-inner' do
  38 + display_post(art, conf[:format]).html_safe +
  39 + '<br style="clear:both"/>'.html_safe
  40 + end
  41 + end)
42 } 42 }
43 content.join("\n<hr class='sep-posts'/>\n") + (pagination or '') 43 content.join("\n<hr class='sep-posts'/>\n") + (pagination or '')
44 end 44 end
@@ -46,7 +46,16 @@ module BlogHelper @@ -46,7 +46,16 @@ module BlogHelper
46 def display_post(article, format = 'full') 46 def display_post(article, format = 'full')
47 no_comments = (format == 'full') ? false : true 47 no_comments = (format == 'full') ? false : true
48 title = article_title(article, :no_comments => no_comments) 48 title = article_title(article, :no_comments => no_comments)
49 - html = send("display_#{format}_format", FilePresenter.for(article)).html_safe 49 + method = "display_#{format.split('+')[0]}_format"
  50 + html = send(method, FilePresenter.for(article)).html_safe
  51 + if format.split('+')[1] == 'pic'
  52 + img = article.first_image
  53 + if img.blank?
  54 + '<div class="post-pic empty"></div>'
  55 + else
  56 + '<div class="post-pic" style="background-image:url('+img+')"></div>'
  57 + end
  58 + end.to_s +
50 title + html 59 title + html
51 end 60 end
52 61
app/models/article.rb
@@ -784,7 +784,9 @@ class Article &lt; ActiveRecord::Base @@ -784,7 +784,9 @@ class Article &lt; ActiveRecord::Base
784 end 784 end
785 785
786 def first_image 786 def first_image
787 - img = Nokogiri::HTML.fragment(self.lead.to_s).css('img[src]').first || Nokogiri::HTML.fragment(self.body.to_s).search('img').first 787 + img = ( image.present? && { 'src' => image.public_filename } ) ||
  788 + Nokogiri::HTML.fragment(self.lead.to_s).css('img[src]').first ||
  789 + Nokogiri::HTML.fragment(self.body.to_s).search('img').first
788 img.nil? ? '' : img['src'] 790 img.nil? ? '' : img['src']
789 end 791 end
790 792
app/models/blog.rb
@@ -76,7 +76,7 @@ class Blog &lt; Folder @@ -76,7 +76,7 @@ class Blog &lt; Folder
76 end 76 end
77 77
78 settings_items :visualization_format, :type => :string, :default => 'full' 78 settings_items :visualization_format, :type => :string, :default => 'full'
79 - validates_inclusion_of :visualization_format, :in => [ 'full', 'short' ], :if => :visualization_format 79 + validates_inclusion_of :visualization_format, :in => [ 'full', 'short', 'short+pic' ], :if => :visualization_format
80 80
81 settings_items :display_posts_in_current_language, :type => :boolean, :default => false 81 settings_items :display_posts_in_current_language, :type => :boolean, :default => false
82 82
app/views/cms/_blog.html.erb
@@ -64,7 +64,11 @@ @@ -64,7 +64,11 @@
64 <%= labelled_check_box(_('Remove cover image'),'remove_image',true,false)%> 64 <%= labelled_check_box(_('Remove cover image'),'remove_image',true,false)%>
65 <% end %> 65 <% end %>
66 66
67 -<%= labelled_form_field(_('How to display posts:'), f.select(:visualization_format, [ [ _('Full post'), 'full'], [ _('First paragraph'), 'short'] ])) %> 67 +<%= labelled_form_field(_('How to display posts:'), f.select(:visualization_format, [
  68 + [ _('Full post'), 'full'],
  69 + [ _('First paragraph'), 'short'],
  70 + [ _('First paragraph, with post picture'), 'short+pic']
  71 +])) %>
68 72
69 <%= labelled_form_field(_('Posts per page:'), f.select(:posts_per_page, Blog.posts_per_page_options)) %> 73 <%= labelled_form_field(_('Posts per page:'), f.select(:posts_per_page, Blog.posts_per_page_options)) %>
70 74
app/views/content_viewer/blog_page.html.erb
@@ -18,6 +18,9 @@ @@ -18,6 +18,9 @@
18 format = inside_block.visualization_format 18 format = inside_block.visualization_format
19 paginate = false 19 paginate = false
20 end 20 end
21 - (blog.empty? ? content_tag('em', _('(no posts)')) : list_posts(posts, format, paginate)) 21 + (blog.empty? ?
  22 + content_tag('em', _('(no posts)')) :
  23 + list_posts(posts, format: format, paginate: paginate)
  24 + )
22 %> 25 %>
23 </div> 26 </div>
app/views/features/manage_fields.html.erb
1 <h1><%= _('Manage fields displayed for profiles') %></h1> 1 <h1><%= _('Manage fields displayed for profiles') %></h1>
2 2
  3 +<%= javascript_include_tag "manage-fields.js" %>
  4 +
3 <% tabs = [] %> 5 <% tabs = [] %>
4 <% tabs << {:title => _("Person's fields"), :id => 'person-fields', 6 <% tabs << {:title => _("Person's fields"), :id => 'person-fields',
5 :content => (render :partial => 'manage_person_fields')} %> 7 :content => (render :partial => 'manage_person_fields')} %>
@@ -11,5 +13,3 @@ @@ -11,5 +13,3 @@
11 <% end %> 13 <% end %>
12 14
13 <%= render_tabs(tabs) %> 15 <%= render_tabs(tabs) %>
14 -  
15 -<%= javascript_include_tag "manage-fields.js" %>  
lib/noosfero/api/entities.rb
@@ -53,9 +53,11 @@ module Noosfero @@ -53,9 +53,11 @@ module Noosfero
53 expose :image, :using => Image 53 expose :image, :using => Image
54 end 54 end
55 55
56 - class ArticleChild < Entity 56 + class ArticleBase < Entity
57 root 'articles', 'article' 57 root 'articles', 'article'
58 - expose :id, :body, :abstract 58 + expose :id
  59 + expose :body
  60 + expose :abstract
59 expose :created_at, :format_with => :timestamp 61 expose :created_at, :format_with => :timestamp
60 expose :title, :documentation => {:type => "String", :desc => "Title of the article"} 62 expose :title, :documentation => {:type => "String", :desc => "Title of the article"}
61 expose :created_by, :as => :author, :using => Profile 63 expose :created_by, :as => :author, :using => Profile
@@ -64,18 +66,10 @@ module Noosfero @@ -64,18 +66,10 @@ module Noosfero
64 expose :image, :using => Image 66 expose :image, :using => Image
65 end 67 end
66 68
67 - class Article < Entity 69 + class Article < ArticleBase
68 root 'articles', 'article' 70 root 'articles', 'article'
69 - expose :id, :body, :abstract  
70 - expose :created_at, :format_with => :timestamp  
71 - expose :title, :documentation => {:type => "String", :desc => "Title of the article"}  
72 - expose :created_by, :as => :author, :using => Profile  
73 - expose :profile, :using => Profile  
74 - expose :categories, :using => Category  
75 - # FIXME: create a method that overrides expose and include conditions for return attributes  
76 - expose :parent, :using => Article  
77 - expose :children, :using => ArticleChild  
78 - expose :image, :using => Image 71 + expose :parent, :using => ArticleBase
  72 + expose :children, :using => ArticleBase
79 end 73 end
80 74
81 class Comment < Entity 75 class Comment < Entity
lib/noosfero/api/v1/articles.rb
@@ -71,7 +71,7 @@ module Noosfero @@ -71,7 +71,7 @@ module Noosfero
71 if !article.save 71 if !article.save
72 render_api_errors!(article.errors.full_messages) 72 render_api_errors!(article.errors.full_messages)
73 end 73 end
74 - present article, :with => Entities::Article 74 + present article, :with => Entities::Article, :fields => params[:fields]
75 end 75 end
76 76
77 77
@@ -110,7 +110,7 @@ module Noosfero @@ -110,7 +110,7 @@ module Noosfero
110 if !article.save 110 if !article.save
111 render_api_errors!(article.errors.full_messages) 111 render_api_errors!(article.errors.full_messages)
112 end 112 end
113 - present article, :with => Entities::Article 113 + present article, :with => Entities::Article, :fields => params[:fields]
114 end 114 end
115 115
116 end 116 end
@@ -149,7 +149,7 @@ module Noosfero @@ -149,7 +149,7 @@ module Noosfero
149 if !article.save 149 if !article.save
150 render_api_errors!(article.errors.full_messages) 150 render_api_errors!(article.errors.full_messages)
151 end 151 end
152 - present article, :with => Entities::Article 152 + present article, :with => Entities::Article, :fields => params[:fields]
153 end 153 end
154 154
155 end 155 end
@@ -188,7 +188,7 @@ module Noosfero @@ -188,7 +188,7 @@ module Noosfero
188 if !article.save 188 if !article.save
189 render_api_errors!(article.errors.full_messages) 189 render_api_errors!(article.errors.full_messages)
190 end 190 end
191 - present article, :with => Entities::Article 191 + present article, :with => Entities::Article, :fields => params[:fields]
192 end 192 end
193 193
194 end 194 end
lib/sanitize_params.rb 0 → 100644
@@ -0,0 +1,34 @@ @@ -0,0 +1,34 @@
  1 +module SanitizeParams
  2 +
  3 + protected
  4 +
  5 + # Check each request parameter for
  6 + # improper HTML or Script tags
  7 + def sanitize_params
  8 + request.params.each { |k, v|
  9 + if v.is_a?(String)
  10 + params[k] = sanitize_param v
  11 + elsif v.is_a?(Array)
  12 + params[k] = sanitize_array v
  13 + end
  14 + }
  15 + end
  16 +
  17 + # If the parameter was an array,
  18 + # try to sanitize each element in the array
  19 + def sanitize_array(array)
  20 + array.map! { |e|
  21 + if e.is_a?(String)
  22 + sanitize_param e
  23 + end
  24 + }
  25 + return array
  26 + end
  27 +
  28 + # Santitize a single value
  29 + def sanitize_param(value)
  30 + allowed_tags = %w(a acronym b strong i em li ul ol h1 h2 h3 h4 h5 h6 blockquote br cite sub sup ins p)
  31 + ActionController::Base.helpers.sanitize(value, tags: allowed_tags, attributes: %w(href title))
  32 + end
  33 +
  34 +end
plugins/google_analytics/lib/ext/profile.rb
@@ -2,4 +2,9 @@ require_dependency &#39;profile&#39; @@ -2,4 +2,9 @@ require_dependency &#39;profile&#39;
2 2
3 class Profile 3 class Profile
4 settings_items :google_analytics_profile_id 4 settings_items :google_analytics_profile_id
  5 + attr_accessible :google_analytics_profile_id
  6 +
  7 + descendants.each do |descendant|
  8 + descendant.attr_accessible :google_analytics_profile_id
  9 + end
5 end 10 end
plugins/google_analytics/lib/google_analytics_plugin.rb
@@ -19,12 +19,15 @@ class GoogleAnalyticsPlugin &lt; Noosfero::Plugin @@ -19,12 +19,15 @@ class GoogleAnalyticsPlugin &lt; Noosfero::Plugin
19 19
20 def head_ending 20 def head_ending
21 unless profile_id.blank? 21 unless profile_id.blank?
22 - expanded_template('tracking-code.rhtml',{:profile_id => profile_id}) 22 + expanded_template('tracking-code.html.erb',{:profile_id => profile_id})
23 end 23 end
24 end 24 end
25 25
26 def profile_editor_extras 26 def profile_editor_extras
27 - expanded_template('profile-editor-extras.rhtml',{:profile_id => profile_id}) 27 + analytics_id = profile_id
  28 + lambda {
  29 + render :file => 'profile-editor-extras', :locals => { :profile_id => analytics_id }
  30 + }
28 end 31 end
29 32
30 end 33 end
plugins/google_analytics/test/functional/profile_editor_controller_test.rb 0 → 100644
@@ -0,0 +1,31 @@ @@ -0,0 +1,31 @@
  1 +require 'test_helper'
  2 +require 'profile_editor_controller'
  3 +
  4 +# Re-raise errors caught by the controller.
  5 +class ProfileEditorController; def rescue_action(e) raise e end; end
  6 +
  7 +class ProfileEditorControllerTest < ActionController::TestCase
  8 +
  9 + def setup
  10 + @controller = ProfileEditorController.new
  11 + @request = ActionController::TestRequest.new
  12 + @response = ActionController::TestResponse.new
  13 + @profile = create_user('default_user').person
  14 + login_as(@profile.identifier)
  15 + Environment.default.enable_plugin(GoogleAnalyticsPlugin.name)
  16 + end
  17 +
  18 + attr_accessor :profile
  19 +
  20 + should 'add extra fields to profile editor info and settings' do
  21 + get :edit, :profile => profile.identifier
  22 + assert_tag_in_string @response.body, :tag => 'label', :content => /Google Analytics/, :attributes => { :for => 'profile_data_google_analytics_profile_id' }
  23 + assert_tag_in_string @response.body, :tag => 'input', :attributes => { :id => 'profile_data_google_analytics_profile_id' }
  24 + end
  25 +
  26 + should 'save code filled in on field' do
  27 + post :edit, :profile => profile.identifier, :profile_data => {:google_analytics_profile_id => 12345678}
  28 + assert_equal '12345678', Person.find(profile.id).google_analytics_profile_id
  29 + end
  30 +
  31 +end
plugins/google_analytics/test/unit/google_analytics_plugin_test.rb
@@ -27,11 +27,6 @@ class GoogleAnalyticsPluginTest &lt; ActiveSupport::TestCase @@ -27,11 +27,6 @@ class GoogleAnalyticsPluginTest &lt; ActiveSupport::TestCase
27 assert_equal 'content', @plugin.head_ending 27 assert_equal 'content', @plugin.head_ending
28 end 28 end
29 29
30 - should 'add extra fields to profile editor info and settings' do  
31 - assert_tag_in_string @plugin.profile_editor_extras,  
32 - :tag => 'input', :attributes => {:id => 'profile_data_google_analytics_profile_id', :value => 10}  
33 - end  
34 -  
35 should 'extends Profile with attr google_analytics_profile_id' do 30 should 'extends Profile with attr google_analytics_profile_id' do
36 assert_respond_to Profile.new, :google_analytics_profile_id 31 assert_respond_to Profile.new, :google_analytics_profile_id
37 end 32 end
plugins/google_analytics/views/profile-editor-extras.html.erb 0 → 100644
@@ -0,0 +1,3 @@ @@ -0,0 +1,3 @@
  1 +<h2><%= c_('Statistics') %></h2>
  2 +<%= labelled_form_field(_('Google Analytics Profile ID'), text_field(:profile_data, :google_analytics_profile_id, :value => profile_id)) %>
  3 +<%= link_to(_('See how to configure statistics for your profile'), '/doc/plugins/google_analytics', :target => '_blank') %>
plugins/google_analytics/views/profile-editor-extras.rhtml
@@ -1,5 +0,0 @@ @@ -1,5 +0,0 @@
1 -<% extend ApplicationHelper %>  
2 -  
3 -<h2><%= c_('Statistics') %></h2>  
4 -<%= labelled_form_field(_('Google Analytics Profile ID'), text_field(:profile_data, :google_analytics_profile_id, :value => profile_id)) %>  
5 -<%= link_to(_('See how to configure statistics for your profile'), '/doc/plugins/google_analytics', :target => '_blank') %>  
plugins/google_analytics/views/tracking-code.html.erb 0 → 100644
@@ -0,0 +1,9 @@ @@ -0,0 +1,9 @@
  1 +<script>
  2 + (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
  3 + (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
  4 + m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
  5 + })(window,document,'script','//www.google-analytics.com/analytics.js','ga');
  6 +
  7 + ga('create', '<%= escape_javascript locals[:profile_id] %>', 'auto');
  8 + ga('send', 'pageview');
  9 +</script>
plugins/google_analytics/views/tracking-code.rhtml
@@ -1,10 +0,0 @@ @@ -1,10 +0,0 @@
1 -<script type="text/javascript">  
2 - var _gaq = _gaq || [];  
3 - _gaq.push(['_setAccount', '<%= escape_javascript locals[:profile_id] %>']);  
4 - _gaq.push(['_trackPageview']);  
5 - (function() {  
6 - var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;  
7 - ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';  
8 - var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);  
9 - })();  
10 -</script>  
plugins/proposals_discussion
1 -Subproject commit 32e6b1fef12caf0eb674f516a39ac88ea418bd82 1 +Subproject commit 3a32ff13a05df2d443410642172c2a4df1585ca1
plugins/send_email/controllers/send_email_plugin_base_controller.rb
@@ -11,7 +11,8 @@ module SendEmailPluginBaseController @@ -11,7 +11,8 @@ module SendEmailPluginBaseController
11 ) 11 )
12 @mail.subject = params[:subject] unless params[:subject].blank? 12 @mail.subject = params[:subject] unless params[:subject].blank?
13 if @mail.valid? 13 if @mail.valid?
14 - SendEmailPlugin::Sender.send_message(request.referer, @context_url, @mail).deliver 14 + @referer = request.referer
  15 + SendEmailPlugin::Sender.send_message(@referer, @context_url, @mail).deliver
15 if request.xhr? 16 if request.xhr?
16 render :text => _('Message sent') 17 render :text => _('Message sent')
17 else 18 else
plugins/send_email/lib/send_email_plugin.rb
@@ -16,9 +16,9 @@ class SendEmailPlugin &lt; Noosfero::Plugin @@ -16,9 +16,9 @@ class SendEmailPlugin &lt; Noosfero::Plugin
16 16
17 def parse_content(html, source) 17 def parse_content(html, source)
18 if context.profile 18 if context.profile
19 - html.gsub!(/\{sendemail\}/, "/profile/#{context.profile.identifier}/plugin/send_email/deliver") 19 + html.gsub!(/({|%7[Bb])sendemail(}|%7[Dd])/, "/profile/#{context.profile.identifier}/plugin/send_email/deliver")
20 else 20 else
21 - html.gsub!(/\{sendemail\}/, '/plugin/send_email/deliver') 21 + html.gsub!(/({|%7[Bb])sendemail(}|%7[Dd])/, '/plugin/send_email/deliver')
22 end 22 end
23 [html, source] 23 [html, source]
24 end 24 end
plugins/send_email/lib/send_email_plugin/mail.rb
@@ -10,12 +10,11 @@ class SendEmailPlugin::Mail @@ -10,12 +10,11 @@ class SendEmailPlugin::Mail
10 validate :recipients_format 10 validate :recipients_format
11 11
12 def initialize(attributes = {:subject => 'New mail'}) 12 def initialize(attributes = {:subject => 'New mail'})
13 - @environment = attributes[:environment]  
14 - @from = attributes[:from]  
15 - @to = attributes[:to]  
16 - @subject = attributes[:subject]  
17 - @message = attributes[:message]  
18 - @params = attributes[:params] 13 + if attributes
  14 + attributes.each do |attr,value|
  15 + self.send("#{attr}=", value)
  16 + end
  17 + end
19 end 18 end
20 19
21 def recipients_format 20 def recipients_format
@@ -36,7 +35,7 @@ class SendEmailPlugin::Mail @@ -36,7 +35,7 @@ class SendEmailPlugin::Mail
36 end 35 end
37 36
38 def params=(value = {}) 37 def params=(value = {})
39 - [:action, :controller, :to, :message, :subject, :from].each{|k| value.delete(k)} 38 + [:profile, :action, :controller, :to, :message, :subject, :from, :commit].each{|k| value.delete(k)}
40 @params = value 39 @params = value
41 end 40 end
42 41
plugins/send_email/lib/send_email_plugin/sender.rb
@@ -7,9 +7,9 @@ class SendEmailPlugin::Sender &lt; Noosfero::Plugin::MailerBase @@ -7,9 +7,9 @@ class SendEmailPlugin::Sender &lt; Noosfero::Plugin::MailerBase
7 @params = mail.params 7 @params = mail.params
8 8
9 mail( 9 mail(
  10 + content_type: 'text/plain',
10 to: mail.to, 11 to: mail.to,
11 from: mail.from, 12 from: mail.from,
12 - body: mail.params,  
13 subject: "[#{mail.environment.name}] #{mail.subject}" 13 subject: "[#{mail.environment.name}] #{mail.subject}"
14 ) 14 )
15 end 15 end
plugins/send_email/test/functional/send_email_plugin_base_controller_test.rb
@@ -54,6 +54,13 @@ def run_common_tests @@ -54,6 +54,13 @@ def run_common_tests
54 post :deliver, @extra_args.merge(:to => 'john@example.com', :message => 'Hi john', :subject => 'Hello john') 54 post :deliver, @extra_args.merge(:to => 'john@example.com', :message => 'Hi john', :subject => 'Hello john')
55 assert_equal '[Colivre.net] Hello john', ActionMailer::Base.deliveries.first.subject 55 assert_equal '[Colivre.net] Hello john', ActionMailer::Base.deliveries.first.subject
56 end 56 end
  57 +
  58 + should 'deliver mail with message from view' do
  59 + Environment.any_instance.stubs(:send_email_plugin_allow_to).returns('john@example.com')
  60 + post :deliver, @extra_args.merge(:to => 'john@example.com', :message => 'Hi john', :subject => 'Hello john')
  61 + assert_match /Contact from/, ActionMailer::Base.deliveries.first.body.to_s
  62 + end
  63 +
57 end 64 end
58 65
59 class SendEmailPluginProfileControllerTest < ActionController::TestCase 66 class SendEmailPluginProfileControllerTest < ActionController::TestCase
plugins/send_email/test/unit/send_email_plugin_sender_test.rb
@@ -15,12 +15,14 @@ class SendEmailPluginSenderTest &lt; ActiveSupport::TestCase @@ -15,12 +15,14 @@ class SendEmailPluginSenderTest &lt; ActiveSupport::TestCase
15 end 15 end
16 16
17 should 'be able to deliver mail' do 17 should 'be able to deliver mail' do
  18 + @mail.expects(:params).returns({})
18 response = SendEmailPlugin::Sender.send_message("http://localhost/contact", 'http//profile', @mail) 19 response = SendEmailPlugin::Sender.send_message("http://localhost/contact", 'http//profile', @mail)
19 assert_equal 'noreply@localhost', response.from.join 20 assert_equal 'noreply@localhost', response.from.join
20 assert_equal "[Noosfero] #{@mail.subject}", response.subject 21 assert_equal "[Noosfero] #{@mail.subject}", response.subject
21 end 22 end
22 23
23 should 'deliver mail to john@example.com' do 24 should 'deliver mail to john@example.com' do
  25 + @mail.expects(:params).returns({})
24 response = SendEmailPlugin::Sender.send_message("http://localhost/contact", 'http//profile', @mail) 26 response = SendEmailPlugin::Sender.send_message("http://localhost/contact", 'http//profile', @mail)
25 assert_equal ['john@example.com'], response.to 27 assert_equal ['john@example.com'], response.to
26 end 28 end
plugins/send_email/test/unit/send_email_plugin_test.rb
@@ -26,4 +26,12 @@ class SendEmailPluginTest &lt; ActiveSupport::TestCase @@ -26,4 +26,12 @@ class SendEmailPluginTest &lt; ActiveSupport::TestCase
26 assert_match /profile\/#{@plugin.context.profile.identifier}\/plugin\/send_email\/deliver/, @plugin.parse_content("expand this macro {sendemail}", nil).first 26 assert_match /profile\/#{@plugin.context.profile.identifier}\/plugin\/send_email\/deliver/, @plugin.parse_content("expand this macro {sendemail}", nil).first
27 end 27 end
28 28
  29 + should 'expand macro used on form on profile context' do
  30 + profile = fast_create(Community)
  31 + @plugin.context.stubs(:profile).returns(profile)
  32 + article = RawHTMLArticle.create!(:name => 'Raw HTML', :body => "<form action='{sendemail}'></form>", :profile => profile)
  33 +
  34 + assert_match /profile\/#{profile.identifier}\/plugin\/send_email\/deliver/, @plugin.parse_content(article.to_html, nil).first
  35 + end
  36 +
29 end 37 end
plugins/send_email/views/send_email_plugin/sender/message.html.erb
@@ -1,8 +0,0 @@ @@ -1,8 +0,0 @@
1 -<%= _('Contact from %s') % @referer %>  
2 -  
3 -<%= word_wrap(@message || @mail.message) %>  
4 -<% (@params || @mail.params).each_pair do |key, value| %>  
5 -<%= key %>: <%= word_wrap(value) %>  
6 -<% end %>  
7 ----  
8 -<%= url_for @context_url %>  
plugins/send_email/views/send_email_plugin/sender/send_message.html.erb 0 → 100644
@@ -0,0 +1,9 @@ @@ -0,0 +1,9 @@
  1 +<%= _('Contact from %s') % @referer %>
  2 +
  3 +<%= word_wrap(@message || @mail.message) %>
  4 +
  5 +<% (@params || @mail.params).each_pair do |key, value| %>
  6 +<%= key %>: <%= word_wrap(value) %>
  7 +<% end %>
  8 +---
  9 +<%= url_for @context_url %>
plugins/send_email/views/send_email_plugin/success.html.erb
@@ -2,7 +2,7 @@ @@ -2,7 +2,7 @@
2 2
3 <table class='sendemail-plugin-message-sent'> 3 <table class='sendemail-plugin-message-sent'>
4 <tr><td class='label'><strong><%= c_('Subject') %>:</strong></td><td class='value'><em><%=h @mail.subject %></em></td></tr> 4 <tr><td class='label'><strong><%= c_('Subject') %>:</strong></td><td class='value'><em><%=h @mail.subject %></em></td></tr>
5 - <tr><td class='label'><strong><%= c_('Message') %>:</strong></td><td class='value'><pre><%=h render :file => 'send_email_plugin/sender/message' %></pre></td></tr> 5 + <tr><td class='label'><strong><%= c_('Message') %>:</strong></td><td class='value'><pre><%=h render :file => 'send_email_plugin/sender/send_message' %></pre></td></tr>
6 </table> 6 </table>
7 7
8 <p><%= button :back, c_('Back'), :back %></p> 8 <p><%= button :back, c_('Back'), :back %></p>
public/javascripts/manage-fields.js
@@ -57,7 +57,7 @@ jQuery(document).ready(function(){ @@ -57,7 +57,7 @@ jQuery(document).ready(function(){
57 } 57 }
58 58
59 var checkbox = jQuery(checkboxes[i+3]).attr("id").split("_") 59 var checkbox = jQuery(checkboxes[i+3]).attr("id").split("_")
60 - jQuery("#" + checkbox.first() + "_" + checkbox.last()).attr("checked", allchecked) 60 + jQuery("#" + checkbox[0] + "_" + checkbox[checkbox.length-1]).attr("checked", allchecked)
61 } 61 }
62 } 62 }
63 63
@@ -74,10 +74,10 @@ jQuery(document).ready(function(){ @@ -74,10 +74,10 @@ jQuery(document).ready(function(){
74 74
75 jQuery("input[type='checkbox']").click(function (){ 75 jQuery("input[type='checkbox']").click(function (){
76 var checkbox = jQuery(this).attr("id").split("_") 76 var checkbox = jQuery(this).attr("id").split("_")
77 - verify_checked(checkbox.first()) 77 + verify_checked(checkbox[0])
78 78
79 if(this.checked == false) { 79 if(this.checked == false) {
80 - jQuery("#" + checkbox.first() + "_" + checkbox.last()).attr("checked", false) 80 + jQuery("#" + checkbox[0] + "_" + checkbox[checkbox.length-1]).attr("checked", false)
81 } 81 }
82 }) 82 })
83 }) 83 })
public/proposal-app
1 -Subproject commit 3cf28fe31f56dea7d695e6a619fd211b05be5f5d 1 +Subproject commit 318967386e6f99b0debb2e3a9076bae3502e65fc
public/stylesheets/application.css
@@ -1502,6 +1502,14 @@ a.comment-picture { @@ -1502,6 +1502,14 @@ a.comment-picture {
1502 #content .title { 1502 #content .title {
1503 margin-bottom: 2px; 1503 margin-bottom: 2px;
1504 } 1504 }
  1505 +.blog-post .post-pic {
  1506 + background-position: 50% 40%;
  1507 + background-size: cover;
  1508 + height: 150px;
  1509 +}
  1510 +.blog-post .post-pic.empty {
  1511 + display: none;
  1512 +}
1505 .metadata, .blog-post .metadata { 1513 .metadata, .blog-post .metadata {
1506 display: block; 1514 display: block;
1507 text-align: center; 1515 text-align: center;
script/production
@@ -55,7 +55,7 @@ do_restart() { @@ -55,7 +55,7 @@ do_restart() {
55 environments_loop start 55 environments_loop start
56 56
57 clear_cache 57 clear_cache
58 - ruby -S bundle exec thin -C config/thin.yml restart --onebyone 58 + RUBY_GC_MALLOC_LIMIT=250000000 RUBY_HEAP_MIN_SLOTS=800000 RUBY_FREE_MIN=32768 ruby -S bundle exec thin -C config/thin.yml restart --onebyone
59 } 59 }
60 60
61 stop_via_pid_file() { 61 stop_via_pid_file() {
@@ -85,7 +85,9 @@ environments_loop() { @@ -85,7 +85,9 @@ environments_loop() {
85 } 85 }
86 86
87 do_running() { 87 do_running() {
88 - pgrep -u noosfero -f 'thin server' > /dev/null 88 + pids=$(sed "s/.*/& /" tmp/pids/thin.*.pid | tr -d '\n' 2>/dev/null || true)
  89 + # passes if any of $pids exist, fails otherwise
  90 + kill -0 $pids > /dev/null 2>&1
89 } 91 }
90 92
91 case "$ACTION" in 93 case "$ACTION" in
test/functional/content_viewer_controller_test.rb
@@ -780,6 +780,20 @@ class ContentViewerControllerTest &lt; ActionController::TestCase @@ -780,6 +780,20 @@ class ContentViewerControllerTest &lt; ActionController::TestCase
780 assert_no_tag :tag => 'div', :attributes => { :class => 'short-post'}, :content => /Anything/ 780 assert_no_tag :tag => 'div', :attributes => { :class => 'short-post'}, :content => /Anything/
781 end 781 end
782 782
  783 + should 'show only first paragraph with picture of posts if visualization_format is short+pic' do
  784 + login_as(profile.identifier)
  785 +
  786 + blog = Blog.create!(:name => 'A blog test', :profile => profile, :visualization_format => 'short+pic')
  787 +
  788 + blog.posts << TinyMceArticle.create!(:name => 'first post', :parent => blog, :profile => profile, :body => '<p>Content to be displayed.</p> <img src="pic.jpg">')
  789 +
  790 + get :view_page, :profile => profile.identifier, :page => blog.path
  791 +
  792 + assert_select '.blog-post .post-pic' do |el|
  793 + assert_match /background-image:url\(pic.jpg\)/, el.to_s
  794 + end
  795 + end
  796 +
783 should 'display link to edit blog for allowed' do 797 should 'display link to edit blog for allowed' do
784 blog = fast_create(Blog, :profile_id => profile.id, :path => 'blog') 798 blog = fast_create(Blog, :profile_id => profile.id, :path => 'blog')
785 login_as(profile.identifier) 799 login_as(profile.identifier)
test/functional/search_controller_test.rb
@@ -769,6 +769,22 @@ class SearchControllerTest &lt; ActionController::TestCase @@ -769,6 +769,22 @@ class SearchControllerTest &lt; ActionController::TestCase
769 assert_equivalent [t1,t2,c1,c2,c3,c4] , assigns(:searches)[:communities][:results] 769 assert_equivalent [t1,t2,c1,c2,c3,c4] , assigns(:searches)[:communities][:results]
770 end 770 end
771 771
  772 + should 'not allow query injection' do
  773 + injection = '<iMg SrC=x OnErRoR=document.documentElement.innerHTML=1>SearchParam'
  774 + get :tag, :tag => injection
  775 + tag = assigns(:tag)
  776 + assert !tag.upcase.include?('IMG') && tag.include?('SearchParam')
  777 + end
  778 +
  779 + should 'not allow query injection array' do
  780 + injection = ['<iMg SrC=x OnErRoR=document.documentElement.innerHTML=1>', '<script>document.innerHTML = \'x\'</script>']
  781 + get :tag, :tag => injection
  782 + tag = assigns(:tag)
  783 + tag.each { |t|
  784 + assert !t.upcase.include?('IMG') && !t.upcase.include?('SCRIPT')
  785 + }
  786 + end
  787 +
772 protected 788 protected
773 789
774 def create_event(profile, options) 790 def create_event(profile, options)
test/unit/article_test.rb
@@ -1715,6 +1715,18 @@ class ArticleTest &lt; ActiveSupport::TestCase @@ -1715,6 +1715,18 @@ class ArticleTest &lt; ActiveSupport::TestCase
1715 assert_equal 'bar.png', a.first_image 1715 assert_equal 'bar.png', a.first_image
1716 end 1716 end
1717 1717
  1718 + should 'get first image from having_image' do
  1719 + a = fast_create(Article,
  1720 + :body => '<p>Foo</p><p><img src="bar.png" /></p>',
  1721 + :abstract => '<p>Lead</p><p><img src="lead.png" /></p>'
  1722 + )
  1723 + img = {}
  1724 + img.expects(:present?).returns true
  1725 + img.expects(:public_filename).returns 'pic.jpg'
  1726 + a.expects(:image).at_least_once.returns img
  1727 + assert_equal 'pic.jpg', a.first_image
  1728 + end
  1729 +
1718 should 'not get first image from anywhere' do 1730 should 'not get first image from anywhere' do
1719 a = fast_create(Article, :body => '<p>Foo</p><p>Bar</p>') 1731 a = fast_create(Article, :body => '<p>Foo</p><p>Bar</p>')
1720 assert_equal '', a.first_image 1732 assert_equal '', a.first_image
test/unit/blog_helper_test.rb
@@ -20,30 +20,36 @@ class BlogHelperTest &lt; ActionView::TestCase @@ -20,30 +20,36 @@ class BlogHelperTest &lt; ActionView::TestCase
20 def _(s); s; end 20 def _(s); s; end
21 def h(s); s; end 21 def h(s); s; end
22 22
23 - should 'list published posts with class blog-post' do  
24 - blog.children << published_post = create(TextileArticle, :name => 'Post', :profile => profile, :parent => blog, :published => true)  
25 -  
26 - expects(:display_post).with(anything, anything).returns('POST')  
27 - expects(:content_tag).with('div', "POST<br style=\"clear:both\"/>", :class => 'blog-post position-1 first last odd-post-inner', :id => "post-#{published_post.id}").returns('POST')  
28 - expects(:content_tag).with('div', 'POST', {:class => 'odd-post'}).returns('RESULT')  
29 -  
30 - assert_equal 'RESULT', list_posts(blog.posts)  
31 - end  
32 -  
33 - should 'list even/odd posts with a different class' do  
34 - blog.children << older_post = create(TextileArticle, :name => 'First post', :profile => profile, :parent => blog, :published => true)  
35 -  
36 - blog.children << newer_post = create(TextileArticle, :name => 'Second post', :profile => profile, :parent => blog, :published => true)  
37 -  
38 - expects(:display_post).with(anything, anything).returns('POST').times(2)  
39 -  
40 - expects(:content_tag).with('div', "POST<br style=\"clear:both\"/>", :class => 'blog-post position-1 first odd-post-inner', :id => "post-#{newer_post.id}").returns('POST 1')  
41 - expects(:content_tag).with('div', "POST 1", :class => 'odd-post').returns('ODD-POST')  
42 -  
43 - expects(:content_tag).with('div', "POST<br style=\"clear:both\"/>", :class => 'blog-post position-2 last even-post-inner', :id => "post-#{older_post.id}").returns('POST 2')  
44 - expects(:content_tag).with('div', "POST 2", :class => 'even-post').returns('EVEN-POST')  
45 -  
46 - assert_equal "ODD-POST\n<hr class='sep-posts'/>\nEVEN-POST", list_posts(blog.posts) 23 + should 'list blog posts with identifiers and classes' do
  24 + blog.children << older_post = create(TextileArticle, :name => 'First post',
  25 + :profile => profile, :parent => blog, :published => true)
  26 + blog.children << some_post = create(TextileArticle, :name => 'Some post',
  27 + :profile => profile, :parent => blog, :published => true)
  28 + blog.children << hidden_post = create(TextileArticle, :name => 'Hidden post',
  29 + :profile => profile, :parent => blog, :published => false)
  30 + blog.children << newer_post = create(TextileArticle, :name => 'Last post',
  31 + :profile => profile, :parent => blog, :published => true)
  32 +
  33 + def content_tag(tag, content_or_options_with_block = nil, options = nil, &block)
  34 + if block_given?
  35 + options = content_or_options_with_block
  36 + content = block.call
  37 + else
  38 + content = content_or_options_with_block
  39 + end
  40 + options ||= {}
  41 + "<#{tag}#{options.map{|k,v| " #{k}=\"#{[v].flatten.join(' ')}\""}.join}>#{content}</#{tag}>"
  42 + end
  43 +
  44 + html = HTML::Document.new(list_posts(blog.posts)).root
  45 + assert_select html, "div#post-#{newer_post.id}.blog-post.position-1.first.odd-post" +
  46 + " > div.odd-post-inner.blog-post-inner > .title", 'Last post'
  47 + assert_select html, "div#post-#{hidden_post.id}.blog-post.position-2.not-published.even-post" +
  48 + " > div.even-post-inner.blog-post-inner > .title", 'Hidden post'
  49 + assert_select html, "div#post-#{some_post.id}.blog-post.position-3.odd-post" +
  50 + " > div.odd-post-inner.blog-post-inner > .title", 'Some post'
  51 + assert_select html, "div#post-#{older_post.id}.blog-post.position-4.last.even-post" +
  52 + " > div.even-post-inner.blog-post-inner > .title", 'First post'
47 end 53 end
48 54
49 55