Commit a6151343194e4273a3828152d7a1664e304ccaba

Authored by Antonio Terceiro
2 parents 45964060 b3068b11

Merge branch 'article_privacity_to_followers' into 'master'

Article privacity to followers

https://gitlab.com/participa/noosfero/issues/324

Feitos para amigos de perfis de pessoas também!

See merge request !422
app/helpers/article_helper.rb
@@ -77,12 +77,59 @@ module ArticleHelper @@ -77,12 +77,59 @@ module ArticleHelper
77 content_tag('div', 77 content_tag('div',
78 radio_button(:article, :published, false) + 78 radio_button(:article, :published, false) +
79 content_tag('label', _('Private'), :for => 'article_published_false', :id => "label_private") 79 content_tag('label', _('Private'), :for => 'article_published_false', :id => "label_private")
80 - ) +  
81 - (article.profile.community? ? content_tag('div',  
82 - content_tag('label', _('Fill in the search field to add the exception users to see this content'), :id => "text-input-search-exception-users") +  
83 - token_input_field_tag(:q, 'search-article-privacy-exceptions', {:action => 'search_article_privacy_exceptions'},  
84 - {:focus => false, :hint_text => _('Type in a search term for a user'), :pre_populate => tokenized_children})) :  
85 - '')) 80 + ) +
  81 + privacity_exceptions(article, tokenized_children)
  82 + )
  83 + end
  84 +
  85 + def privacity_exceptions(article, tokenized_children)
  86 + content_tag('div',
  87 + content_tag('div',
  88 + (
  89 + if article.profile
  90 + add_option_to_followers(article, tokenized_children)
  91 + else
  92 + ''
  93 + end
  94 + )
  95 + ),
  96 + :style => "margin-left:10px"
  97 + )
  98 + end
  99 +
  100 + def add_option_to_followers(article, tokenized_children)
  101 + label_message = article.profile.organization? ? _('For all community members') : _('For all your friends')
  102 +
  103 + check_box(
  104 + :article,
  105 + :show_to_followers,
  106 + {:class => "custom_privacy_option"}
  107 + ) +
  108 + content_tag(
  109 + 'label',
  110 + label_message,
  111 + :for => 'article_show_to_followers',
  112 + :id => 'label_show_to_followers'
  113 + ) +
  114 + (article.profile.community? ?
  115 + content_tag(
  116 + 'div',
  117 + content_tag(
  118 + 'label',
  119 + _('Fill in the search field to add the exception users to see this content'),
  120 + :id => "text-input-search-exception-users"
  121 + ) +
  122 + token_input_field_tag(
  123 + :q,
  124 + 'search-article-privacy-exceptions',
  125 + {:action => 'search_article_privacy_exceptions'},
  126 + {
  127 + :focus => false,
  128 + :hint_text => _('Type in a search term for a user'),
  129 + :pre_populate => tokenized_children
  130 + }
  131 + )
  132 + ) : '')
86 end 133 end
87 134
88 def prepare_to_token_input(array) 135 def prepare_to_token_input(array)
app/models/article.rb
@@ -2,7 +2,14 @@ require 'hpricot' @@ -2,7 +2,14 @@ require 'hpricot'
2 2
3 class Article < ActiveRecord::Base 3 class Article < ActiveRecord::Base
4 4
5 - attr_accessible :name, :body, :abstract, :profile, :tag_list, :parent, :allow_members_to_edit, :translation_of_id, :language, :license_id, :parent_id, :display_posts_in_current_language, :category_ids, :posts_per_page, :moderate_comments, :accept_comments, :feed, :published, :source, :highlighted, :notify_comments, :display_hits, :slug, :external_feed_builder, :display_versions, :external_link, :image_builder 5 + attr_accessible :name, :body, :abstract, :profile, :tag_list, :parent,
  6 + :allow_members_to_edit, :translation_of_id, :language,
  7 + :license_id, :parent_id, :display_posts_in_current_language,
  8 + :category_ids, :posts_per_page, :moderate_comments,
  9 + :accept_comments, :feed, :published, :source,
  10 + :highlighted, :notify_comments, :display_hits, :slug,
  11 + :external_feed_builder, :display_versions, :external_link,
  12 + :image_builder, :show_to_followers
6 13
7 acts_as_having_image 14 acts_as_having_image
8 15
@@ -333,7 +340,7 @@ class Article &lt; ActiveRecord::Base @@ -333,7 +340,7 @@ class Article &lt; ActiveRecord::Base
333 def belongs_to_blog? 340 def belongs_to_blog?
334 self.parent and self.parent.blog? 341 self.parent and self.parent.blog?
335 end 342 end
336 - 343 +
337 def belongs_to_forum? 344 def belongs_to_forum?
338 self.parent and self.parent.forum? 345 self.parent and self.parent.forum?
339 end 346 end
@@ -445,6 +452,7 @@ class Article &lt; ActiveRecord::Base @@ -445,6 +452,7 @@ class Article &lt; ActiveRecord::Base
445 if self.parent && !self.parent.published? 452 if self.parent && !self.parent.published?
446 return false 453 return false
447 end 454 end
  455 +
448 true 456 true
449 else 457 else
450 false 458 false
@@ -476,14 +484,17 @@ class Article &lt; ActiveRecord::Base @@ -476,14 +484,17 @@ class Article &lt; ActiveRecord::Base
476 {:conditions => [" articles.published = ? OR 484 {:conditions => [" articles.published = ? OR
477 articles.last_changed_by_id = ? OR 485 articles.last_changed_by_id = ? OR
478 articles.profile_id = ? OR 486 articles.profile_id = ? OR
479 - ?",  
480 - true, user.id, user.id, user.has_permission?(:view_private_content, profile)] } 487 + ? OR articles.show_to_followers = ? AND ?",
  488 + true, user.id, user.id, user.has_permission?(:view_private_content, profile),
  489 + true, user.follows?(profile)]}
481 end 490 end
482 491
  492 +
483 def display_unpublished_article_to?(user) 493 def display_unpublished_article_to?(user)
484 user == author || allow_view_private_content?(user) || user == profile || 494 user == author || allow_view_private_content?(user) || user == profile ||
485 user.is_admin?(profile.environment) || user.is_admin?(profile) || 495 user.is_admin?(profile.environment) || user.is_admin?(profile) ||
486 - article_privacy_exceptions.include?(user) 496 + article_privacy_exceptions.include?(user) ||
  497 + (self.show_to_followers && user.follows?(profile))
487 end 498 end
488 499
489 def display_to?(user = nil) 500 def display_to?(user = nil)
db/migrate/20150113131617_add_show_to_followers_for_article.rb 0 → 100644
@@ -0,0 +1,9 @@ @@ -0,0 +1,9 @@
  1 +class AddShowToFollowersForArticle < ActiveRecord::Migration
  2 + def up
  3 + add_column :articles, :show_to_followers, :boolean, :default => false
  4 + end
  5 +
  6 + def down
  7 + remove_column :articles, :show_to_followers
  8 + end
  9 +end
features/edit_article.feature
@@ -47,6 +47,28 @@ Feature: edit article @@ -47,6 +47,28 @@ Feature: edit article
47 Then I should see "Access denied" 47 Then I should see "Access denied"
48 48
49 @selenium 49 @selenium
  50 + Scenario: Hide token field when show to members is activated
  51 + Given the following communities
  52 + | name | identifier | owner |
  53 + | Free Software | freesoftware | joaosilva |
  54 + And the following users
  55 + | login | name |
  56 + | mario | Mario Souto |
  57 + | maria | Maria Silva |
  58 + And "Mario Souto" is a member of "Free Software"
  59 + And "Maria Silva" is a member of "Free Software"
  60 + And I am on freesoftware's control panel
  61 + And I follow "Manage Content"
  62 + And I should see "New content"
  63 + And I follow "New content"
  64 + And I should see "Folder"
  65 + When I follow "Folder"
  66 + And I fill in "Title" with "My Folder"
  67 + And I choose "article_published_false"
  68 + And I check "article_show_to_followers"
  69 + Then I should not see "Fill in the search"
  70 +
  71 + @selenium
50 Scenario: show exception users field when you choose the private option 72 Scenario: show exception users field when you choose the private option
51 Given the following communities 73 Given the following communities
52 | name | identifier | owner | 74 | name | identifier | owner |
public/javascripts/article.js
@@ -136,7 +136,7 @@ jQuery(function($) { @@ -136,7 +136,7 @@ jQuery(function($) {
136 if (data.length && data.length > 0) { 136 if (data.length && data.length > 0) {
137 $('#media-search-results').slideDown(); 137 $('#media-search-results').slideDown();
138 } 138 }
139 - $('#media-search-box .header').toggleClass('icon-loading'); 139 + $('#media-search-box .header').toggleClass('icon-loading');
140 }); 140 });
141 return false; 141 return false;
142 }); 142 });
@@ -144,20 +144,20 @@ jQuery(function($) { @@ -144,20 +144,20 @@ jQuery(function($) {
144 $('#media-upload-form form').ajaxForm({ 144 $('#media-upload-form form').ajaxForm({
145 resetForm: true, 145 resetForm: true,
146 beforeSubmit: 146 beforeSubmit:
147 - function() {  
148 - $('#media-upload-form').slideUp();  
149 - $('#media-upload-box .header').toggleClass('icon-loading');  
150 - }, 147 + function() {
  148 + $('#media-upload-form').slideUp();
  149 + $('#media-upload-box .header').toggleClass('icon-loading');
  150 + },
151 success: 151 success:
152 - function(text) {  
153 - text = text.replace('<pre>', '').replace('</pre>', ''); // old firefox  
154 - var data = $.parseJSON(text);  
155 - list_items(data, '#media-upload-results .items', true);  
156 - if (data.length && data.length > 0) {  
157 - $('#media-upload-results').slideDown();  
158 - }  
159 - $('#media-upload-box .header').toggleClass('icon-loading'); 152 + function(text) {
  153 + text = text.replace('<pre>', '').replace('</pre>', ''); // old firefox
  154 + var data = $.parseJSON(text);
  155 + list_items(data, '#media-upload-results .items', true);
  156 + if (data.length && data.length > 0) {
  157 + $('#media-upload-results').slideDown();
160 } 158 }
  159 + $('#media-upload-box .header').toggleClass('icon-loading');
  160 + }
161 }); 161 });
162 162
163 $('#media-upload-more-files').click(function() { 163 $('#media-upload-more-files').click(function() {
@@ -166,19 +166,45 @@ jQuery(function($) { @@ -166,19 +166,45 @@ jQuery(function($) {
166 return false; 166 return false;
167 }); 167 });
168 168
  169 + function is_public_article() {
  170 + return $("#article_published_true").attr('checked');
  171 + }
  172 +
  173 + function show_hide_privacy_options() {
  174 + var show_privacy_options = $("#article_published_false").attr('checked');
  175 + var custom_privacy_option = $(".custom_privacy_option").parent("div");
  176 +
  177 + if( show_privacy_options ) {
  178 + custom_privacy_option.show();
  179 + } else {
  180 + custom_privacy_option.hide();
  181 + }
  182 + show_hide_token_input();
  183 + }
  184 +
169 function show_hide_token_input() { 185 function show_hide_token_input() {
170 - if($("#article_published_false").attr('checked'))  
171 - $("#text-input-search-exception-users").parent("div").css('display', 'block');  
172 - else  
173 - $("#text-input-search-exception-users").parent("div").css('display', 'none'); 186 + var display_token = $(".custom_privacy_option:checked").length == 0;
  187 + var token_field = $("#text-input-search-exception-users").parent("div");
  188 +
  189 + if( display_token && !is_public_article() ) {
  190 + token_field.css('display', 'block');
  191 + } else {
  192 + token_field.css('display', 'none');
  193 + }
174 } 194 }
175 195
176 if( $("#token-input-search-article-privacy-exceptions").length == 1 ) { 196 if( $("#token-input-search-article-privacy-exceptions").length == 1 ) {
  197 + show_hide_privacy_options();
177 show_hide_token_input(); 198 show_hide_token_input();
178 -  
179 - //Hide / Show the text area  
180 - $("#article_published_false").click(show_hide_token_input);  
181 - $("#article_published_true").click(show_hide_token_input);  
182 } 199 }
183 200
  201 + $(document).ready(function(){
  202 + show_hide_privacy_options();
  203 + });
  204 +
  205 + //Hide / Show the text area
  206 + $("#article_published_false").click(show_hide_privacy_options);
  207 + $("#article_published_true").click(show_hide_privacy_options);
  208 + $(".custom_privacy_option").click(show_hide_token_input);
  209 +
184 }); 210 });
test/functional/content_viewer_controller_test.rb
@@ -661,8 +661,8 @@ class ContentViewerControllerTest &lt; ActionController::TestCase @@ -661,8 +661,8 @@ class ContentViewerControllerTest &lt; ActionController::TestCase
661 get :view_page, :profile => owner.identifier, :page => folder.path 661 get :view_page, :profile => owner.identifier, :page => folder.path
662 assert_response :success 662 assert_response :success
663 assert_select '.image-gallery-item', 0 663 assert_select '.image-gallery-item', 0
664 - end  
665 - 664 + end
  665 +
666 666
667 should 'display default image in the slideshow if thumbnails were not processed' do 667 should 'display default image in the slideshow if thumbnails were not processed' do
668 @controller.stubs(:per_page).returns(1) 668 @controller.stubs(:per_page).returns(1)
@@ -1296,14 +1296,14 @@ class ContentViewerControllerTest &lt; ActionController::TestCase @@ -1296,14 +1296,14 @@ class ContentViewerControllerTest &lt; ActionController::TestCase
1296 def comment_form_extra_contents(args) 1296 def comment_form_extra_contents(args)
1297 proc { 1297 proc {
1298 hidden_field_tag('comment[some_field_id]', 1) 1298 hidden_field_tag('comment[some_field_id]', 1)
1299 - } 1299 + }
1300 end 1300 end
1301 end 1301 end
1302 class Plugin2 < Noosfero::Plugin 1302 class Plugin2 < Noosfero::Plugin
1303 def comment_form_extra_contents(args) 1303 def comment_form_extra_contents(args)
1304 proc { 1304 proc {
1305 hidden_field_tag('comment[another_field_id]', 1) 1305 hidden_field_tag('comment[another_field_id]', 1)
1306 - } 1306 + }
1307 end 1307 end
1308 end 1308 end
1309 Noosfero::Plugin.stubs(:all).returns([Plugin1.name, Plugin2.name]) 1309 Noosfero::Plugin.stubs(:all).returns([Plugin1.name, Plugin2.name])
@@ -1373,20 +1373,20 @@ class ContentViewerControllerTest &lt; ActionController::TestCase @@ -1373,20 +1373,20 @@ class ContentViewerControllerTest &lt; ActionController::TestCase
1373 get :view_page, :profile => profile.identifier, :page => [blog.path] 1373 get :view_page, :profile => profile.identifier, :page => [blog.path]
1374 assert_tag :tag => 'strong', :content => /bold/ 1374 assert_tag :tag => 'strong', :content => /bold/
1375 end 1375 end
1376 - 1376 +
1377 should 'add extra content on article header from plugins' do 1377 should 'add extra content on article header from plugins' do
1378 class Plugin1 < Noosfero::Plugin 1378 class Plugin1 < Noosfero::Plugin
1379 def article_header_extra_contents(args) 1379 def article_header_extra_contents(args)
1380 proc { 1380 proc {
1381 content_tag('div', '', :class => 'plugin1') 1381 content_tag('div', '', :class => 'plugin1')
1382 - } 1382 + }
1383 end 1383 end
1384 end 1384 end
1385 class Plugin2 < Noosfero::Plugin 1385 class Plugin2 < Noosfero::Plugin
1386 def article_header_extra_contents(args) 1386 def article_header_extra_contents(args)
1387 proc { 1387 proc {
1388 content_tag('div', '', :class => 'plugin2') 1388 content_tag('div', '', :class => 'plugin2')
1389 - } 1389 + }
1390 end 1390 end
1391 end 1391 end
1392 Noosfero::Plugin.stubs(:all).returns([Plugin1.name, Plugin2.name]) 1392 Noosfero::Plugin.stubs(:all).returns([Plugin1.name, Plugin2.name])
@@ -1447,4 +1447,35 @@ class ContentViewerControllerTest &lt; ActionController::TestCase @@ -1447,4 +1447,35 @@ class ContentViewerControllerTest &lt; ActionController::TestCase
1447 assert_tag :tag => 'meta', :attributes => { :property => 'og:image', :content => /\/images\/x.png/ } 1447 assert_tag :tag => 'meta', :attributes => { :property => 'og:image', :content => /\/images\/x.png/ }
1448 end 1448 end
1449 1449
  1450 + should 'manage private article visualization' do
  1451 + community = Community.create(:name => 'test-community')
  1452 + community.add_member(@profile)
  1453 + community.save!
  1454 +
  1455 + blog = community.articles.find_by_name("Blog")
  1456 +
  1457 + article = TinyMceArticle.create(:name => 'Article to be shared with images',
  1458 + :body => 'This article should be shared with all social networks',
  1459 + :profile => @profile,
  1460 + :published => false,
  1461 + :show_to_followers => true)
  1462 + article.parent = blog
  1463 + article.save!
  1464 +
  1465 + otheruser = create_user('otheruser').person
  1466 + community.add_member(otheruser)
  1467 + login_as(otheruser.identifier)
  1468 +
  1469 + get :view_page, :profile => community.identifier, "page" => 'blog'
  1470 +
  1471 + assert_response :success
  1472 + assert_tag :tag => 'h1', :attributes => { :class => /title/ }, :content => article.name
  1473 +
  1474 + article.show_to_followers = false
  1475 + article.save!
  1476 +
  1477 + get :view_page, :profile => community.identifier, "page" => 'blog'
  1478 +
  1479 + assert_no_tag :tag => 'h1', :attributes => { :class => /title/ }, :content => article.name
  1480 + end
1450 end 1481 end