Commit a6151343194e4273a3828152d7a1664e304ccaba
Exists in
master
and in
21 other branches
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
Showing
6 changed files
with
185 additions
and
39 deletions
Show diff stats
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 < ActiveRecord::Base | @@ -333,7 +340,7 @@ class Article < 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 < ActiveRecord::Base | @@ -445,6 +452,7 @@ class Article < 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 < ActiveRecord::Base | @@ -476,14 +484,17 @@ class Article < 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
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 < ActionController::TestCase | @@ -661,8 +661,8 @@ class ContentViewerControllerTest < 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 < ActionController::TestCase | @@ -1296,14 +1296,14 @@ class ContentViewerControllerTest < 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 < ActionController::TestCase | @@ -1373,20 +1373,20 @@ class ContentViewerControllerTest < 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 < ActionController::TestCase | @@ -1447,4 +1447,35 @@ class ContentViewerControllerTest < 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 |