diff --git a/app/controllers/my_profile/cms_controller.rb b/app/controllers/my_profile/cms_controller.rb index a1e007e..81ee8de 100644 --- a/app/controllers/my_profile/cms_controller.rb +++ b/app/controllers/my_profile/cms_controller.rb @@ -2,6 +2,8 @@ class CmsController < MyProfileController protect 'edit_profile', :profile, :only => [:set_home_page] + include ArticleHelper + def self.protect_if(*args) before_filter(*args) do |c| user, profile = c.send(:user), c.send(:profile) @@ -70,6 +72,14 @@ class CmsController < MyProfileController translations if @article.translatable? continue = params[:continue] + @article.article_privacy_exceptions = params[:q].split(/,/).map{|n| environment.people.find n.to_i} unless params[:q].nil? + + @tokenized_children = prepare_to_token_input( + profile.members.map{|m| + m if @article.article_privacy_exceptions.include?(m) + }.compact + ) + refuse_blocks record_coming if request.post? @@ -130,6 +140,8 @@ class CmsController < MyProfileController continue = params[:continue] if request.post? + @article.article_privacy_exceptions = params[:q].split(/,/).map{|n| environment.people.find n.to_i} unless params[:q].nil? + if @article.save if continue redirect_to :action => 'edit', :id => @article @@ -290,6 +302,12 @@ class CmsController < MyProfileController render :text => article_list_to_json(results), :content_type => 'application/json' end + def search_article_privacy_exceptions + arg = params[:q].downcase + result = profile.members.find(:all, :conditions => ['LOWER(name) LIKE ?', "%#{arg}%"]) + render :text => prepare_to_token_input(result).to_json + end + def media_upload files_uploaded = [] parent = check_parent(params[:parent_id]) diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index 7068422..5a1a669 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -40,6 +40,8 @@ module ApplicationHelper include Noosfero::Gravatar + include TokenHelper + def locale (@page && !@page.language.blank?) ? @page.language : FastGettext.locale end @@ -1299,10 +1301,6 @@ module ApplicationHelper content_tag(:div, content_tag(:ul, titles) + raw(contents), :class => 'ui-tabs') end - def jquery_token_input_messages_json(hintText = _('Type in an keyword'), noResultsText = _('No results'), searchingText = _('Searching...')) - "hintText: '#{hintText}', noResultsText: '#{noResultsText}', searchingText: '#{searchingText}'" - end - def delete_article_message(article) if article.folder? _("Are you sure that you want to remove the folder \"#{article.name}\"? Note that all the items inside it will also be removed!") @@ -1343,50 +1341,6 @@ module ApplicationHelper ) end - def token_input_field_tag(name, element_id, search_action, options = {}, text_field_options = {}, html_options = {}) - options[:min_chars] ||= 3 - options[:hint_text] ||= _("Type in a search term") - options[:no_results_text] ||= _("No results") - options[:searching_text] ||= _("Searching...") - options[:search_delay] ||= 1000 - options[:prevent_duplicates] ||= true - options[:backspace_delete_item] ||= false - options[:focus] ||= false - options[:avoid_enter] ||= true - options[:on_result] ||= 'null' - options[:on_add] ||= 'null' - options[:on_delete] ||= 'null' - options[:on_ready] ||= 'null' - - result = text_field_tag(name, nil, text_field_options.merge(html_options.merge({:id => element_id}))) - result += javascript_tag("jQuery('##{element_id}') - .tokenInput('#{url_for(search_action)}', { - minChars: #{options[:min_chars].to_json}, - prePopulate: #{options[:pre_populate].to_json}, - hintText: #{options[:hint_text].to_json}, - noResultsText: #{options[:no_results_text].to_json}, - searchingText: #{options[:searching_text].to_json}, - searchDelay: #{options[:serach_delay].to_json}, - preventDuplicates: #{options[:prevent_duplicates].to_json}, - backspaceDeleteItem: #{options[:backspace_delete_item].to_json}, - queryParam: #{name.to_json}, - tokenLimit: #{options[:token_limit].to_json}, - onResult: #{options[:on_result]}, - onAdd: #{options[:on_add]}, - onDelete: #{options[:on_delete]}, - onReady: #{options[:on_ready]}, - }); - ") - result += javascript_tag("jQuery('##{element_id}').focus();") if options[:focus] - if options[:avoid_enter] - result += javascript_tag("jQuery('#token-input-#{element_id}') - .live('keydown', function(event){ - if(event.keyCode == '13') return false; - });") - end - result - end - def expirable_content_reference(content, action, text, url, options = {}) reason = @plugins.dispatch("content_expire_#{action.to_s}", content).first options[:title] = reason diff --git a/app/helpers/article_helper.rb b/app/helpers/article_helper.rb index c532393..6588979 100644 --- a/app/helpers/article_helper.rb +++ b/app/helpers/article_helper.rb @@ -1,18 +1,11 @@ module ArticleHelper - def custom_options_for_article(article) + include TokenHelper + + def custom_options_for_article(article, tokenized_children) @article = article - content_tag('h4', _('Visibility')) + - content_tag('div', - content_tag('div', - radio_button(:article, :published, true) + - content_tag('label', _('Public (visible to other people)'), :for => 'article_published_true') - ) + - content_tag('div', - radio_button(:article, :published, false) + - content_tag('label', _('Private'), :for => 'article_published_false') - ) - ) + + + visibility_options(@article, tokenized_children) + content_tag('h4', _('Options')) + content_tag('div', (article.profile.has_members? ? @@ -53,6 +46,28 @@ module ArticleHelper ) end + def visibility_options(article, tokenized_children) + content_tag('h4', _('Visibility')) + + content_tag('div', + content_tag('div', + radio_button(:article, :published, true) + + content_tag('label', _('Public (visible to other people)'), :for => 'article_published_true') + ) + + content_tag('div', + radio_button(:article, :published, false) + + content_tag('label', _('Private'), :for => 'article_published_false', :id => "label_private") + ) + + (article.profile.class == Community ? content_tag('div', + content_tag('label', _('Fill in the search field to add the exception users to see this content'), :id => "text-input-search-exception-users") + + token_input_field_tag(:q, 'search-article-privacy-exceptions', {:action => 'search_article_privacy_exceptions'}, + {:focus => false, :hint_text => _('Type in a search term for a user'), :pre_populate => tokenized_children})) : + '')) + end + + def prepare_to_token_input(array) + array.map { |object| {:id => object.id, :name => object.name} } + end + def cms_label_for_new_children _('New article') end diff --git a/app/helpers/blog_helper.rb b/app/helpers/blog_helper.rb index 4960e41..740b721 100644 --- a/app/helpers/blog_helper.rb +++ b/app/helpers/blog_helper.rb @@ -1,9 +1,12 @@ module BlogHelper - def custom_options_for_article(article) + include ArticleHelper + + def custom_options_for_article(article,tokenized_children) @article = article hidden_field_tag('article[published]', 1) + - hidden_field_tag('article[accept_comments]', 0) + hidden_field_tag('article[accept_comments]', 0) + + visibility_options(article,tokenized_children) end def cms_label_for_new_children diff --git a/app/helpers/cms_helper.rb b/app/helpers/cms_helper.rb index a858c32..6bd36ca 100644 --- a/app/helpers/cms_helper.rb +++ b/app/helpers/cms_helper.rb @@ -22,9 +22,9 @@ module CmsHelper attr_reader :environment - def options_for_article(article) + def options_for_article(article, tokenized_children=nil) article_helper = helper_for_article(article) - article_helper.custom_options_for_article(article) + article_helper.custom_options_for_article(article, tokenized_children) end def link_to_article(article) diff --git a/app/helpers/folder_helper.rb b/app/helpers/folder_helper.rb index 9df1226..c9d0a43 100644 --- a/app/helpers/folder_helper.rb +++ b/app/helpers/folder_helper.rb @@ -1,6 +1,7 @@ module FolderHelper include ShortFilename + include ArticleHelper def list_articles(articles, recursive = false) if !articles.blank? @@ -60,19 +61,10 @@ module FolderHelper "icon-new icon-new%s" % klass.icon_name end - def custom_options_for_article(article) + def custom_options_for_article(article,tokenized_children) @article = article - content_tag('h4', _('Visibility')) + - content_tag('div', - content_tag('div', - radio_button(:article, :published, true) + - content_tag('label', _('Public (visible to other people)'), :for => 'article_published_true') - ) + - content_tag('div', - radio_button(:article, :published, false) + - content_tag('label', _('Private'), :for => 'article_published_false') - ) - ) + + + visibility_options(article,tokenized_children) + content_tag('div', hidden_field_tag('article[accept_comments]', 0) ) diff --git a/app/helpers/token_helper.rb b/app/helpers/token_helper.rb new file mode 100644 index 0000000..e33d77e --- /dev/null +++ b/app/helpers/token_helper.rb @@ -0,0 +1,51 @@ +module TokenHelper + + def jquery_token_input_messages_json(hintText = _('Type in an keyword'), noResultsText = _('No results'), searchingText = _('Searching...')) + "hintText: '#{hintText}', noResultsText: '#{noResultsText}', searchingText: '#{searchingText}'" + end + + def token_input_field_tag(name, element_id, search_action, options = {}, text_field_options = {}, html_options = {}) + options[:min_chars] ||= 3 + options[:hint_text] ||= _("Type in a search term") + options[:no_results_text] ||= _("No results") + options[:searching_text] ||= _("Searching...") + options[:search_delay] ||= 1000 + options[:prevent_duplicates] ||= true + options[:backspace_delete_item] ||= false + options[:focus] ||= false + options[:avoid_enter] ||= true + options[:on_result] ||= 'null' + options[:on_add] ||= 'null' + options[:on_delete] ||= 'null' + options[:on_ready] ||= 'null' + + result = text_field_tag(name, nil, text_field_options.merge(html_options.merge({:id => element_id}))) + result += javascript_tag("jQuery('##{element_id}') + .tokenInput('#{url_for(search_action)}', { + minChars: #{options[:min_chars].to_json}, + prePopulate: #{options[:pre_populate].to_json}, + hintText: #{options[:hint_text].to_json}, + noResultsText: #{options[:no_results_text].to_json}, + searchingText: #{options[:searching_text].to_json}, + searchDelay: #{options[:serach_delay].to_json}, + preventDuplicates: #{options[:prevent_duplicates].to_json}, + backspaceDeleteItem: #{options[:backspace_delete_item].to_json}, + queryParam: #{name.to_json}, + tokenLimit: #{options[:token_limit].to_json}, + onResult: #{options[:on_result]}, + onAdd: #{options[:on_add]}, + onDelete: #{options[:on_delete]}, + onReady: #{options[:on_ready]}, + }); + ") + result += javascript_tag("jQuery('##{element_id}').focus();") if options[:focus] + if options[:avoid_enter] + result += javascript_tag("jQuery('#token-input-#{element_id}') + .live('keydown', function(event){ + if(event.keyCode == '13') return false; + });") + end + result + end + +end \ No newline at end of file diff --git a/app/models/article.rb b/app/models/article.rb index 32b316a..f18ddb3 100644 --- a/app/models/article.rb +++ b/app/models/article.rb @@ -69,6 +69,7 @@ class Article < ActiveRecord::Base settings_items :allow_members_to_edit, :type => :boolean, :default => false settings_items :moderate_comments, :type => :boolean, :default => false settings_items :followers, :type => Array, :default => [] + has_and_belongs_to_many :article_privacy_exceptions, :class_name => 'Person', :join_table => 'article_privacy_exceptions' belongs_to :reference_article, :class_name => "Article", :foreign_key => 'reference_article_id' @@ -470,7 +471,8 @@ class Article < ActiveRecord::Base def display_unpublished_article_to?(user) user == author || allow_view_private_content?(user) || user == profile || - user.is_admin?(profile.environment) || user.is_admin?(profile) + user.is_admin?(profile.environment) || user.is_admin?(profile) || + article_privacy_exceptions.include?(user) end def display_to?(user = nil) diff --git a/app/models/person.rb b/app/models/person.rb index ba9d0f0..02e3be3 100644 --- a/app/models/person.rb +++ b/app/models/person.rb @@ -63,6 +63,8 @@ class Person < Profile has_many :scraps_sent, :class_name => 'Scrap', :foreign_key => :sender_id, :dependent => :destroy + has_and_belongs_to_many :articles_with_access, :class_name => 'Article', :join_table => 'article_privacy_exceptions' + named_scope :more_popular, :select => "#{Profile.qualified_column_names}, count(friend_id) as total", :group => Profile.qualified_column_names, diff --git a/app/views/cms/edit.rhtml b/app/views/cms/edit.rhtml index 566ae87..fcd7b1e 100644 --- a/app/views/cms/edit.rhtml +++ b/app/views/cms/edit.rhtml @@ -1,4 +1,5 @@ <%= error_messages_for 'article' %> +<%= javascript_include_tag "article.js" %>
'> <% labelled_form_for 'article', @article, :html => { :multipart => true, :class => @type } do |f| %> @@ -33,7 +34,7 @@ <%= content_tag( 'small', _('Separate tags with commas') ) %>
- <%= options_for_article(@article) %> + <%= options_for_article(@article, @tokenized_children) %>
<% button_bar do %> diff --git a/db/migrate/20140108132730_article_privacy_exceptions.rb b/db/migrate/20140108132730_article_privacy_exceptions.rb new file mode 100644 index 0000000..4aa035e --- /dev/null +++ b/db/migrate/20140108132730_article_privacy_exceptions.rb @@ -0,0 +1,12 @@ +class ArticlePrivacyExceptions < ActiveRecord::Migration + def self.up + create_table :article_privacy_exceptions, :id => false do |t| + t.integer :article_id + t.integer :person_id + end + end + + def self.down + drop_table :article_privacy_exceptions + end +end diff --git a/db/schema.rb b/db/schema.rb index 97a5921..4c79265 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -9,8 +9,7 @@ # # It's strongly recommended to check this file into your version control system. -ActiveRecord::Schema.define(:version => 20131121162641) do - +ActiveRecord::Schema.define(:version => 20140108132730) do create_table "abuse_reports", :force => true do |t| t.integer "reporter_id" t.integer "abuse_complaint_id" @@ -46,6 +45,11 @@ ActiveRecord::Schema.define(:version => 20131121162641) do add_index "action_tracker_notifications", ["profile_id", "action_tracker_id"], :name => "index_action_tracker_notif_on_prof_id_act_tracker_id", :unique => true add_index "action_tracker_notifications", ["profile_id"], :name => "index_action_tracker_notifications_on_profile_id" + create_table "article_privacy_exceptions", :id => false, :force => true do |t| + t.integer "article_id" + t.integer "person_id" + end + create_table "article_versions", :force => true do |t| t.integer "article_id" t.integer "version" @@ -460,6 +464,7 @@ ActiveRecord::Schema.define(:version => 20131121162641) do t.boolean "is_template", :default => false t.integer "template_id" t.string "redirection_after_login" + t.text "settings" end add_index "profiles", ["environment_id"], :name => "index_profiles_on_environment_id" @@ -566,6 +571,11 @@ ActiveRecord::Schema.define(:version => 20131121162641) do add_index "tasks", ["spam"], :name => "index_tasks_on_spam" + create_table "terms_forum_people", :id => false, :force => true do |t| + t.integer "forum_id" + t.integer "person_id" + end + create_table "thumbnails", :force => true do |t| t.integer "size" t.string "content_type" diff --git a/features/edit_article.feature b/features/edit_article.feature index d576793..ae7b10a 100644 --- a/features/edit_article.feature +++ b/features/edit_article.feature @@ -22,6 +22,70 @@ Feature: edit article And I go to joaosilva's control panel Then I should see "My Folder" + @selenium + Scenario: denied access folder for a not logged user + Given the following communities + | name | identifier | owner | + | Free Software | freesoftware | joaosilva | + And the following users + | login | name | + | mario | Mario Souto | + | maria | Maria Silva | + And "Mario Souto" is a member of "Free Software" + And "Maria Silva" is a member of "Free Software" + And I am on freesoftware's control panel + And I follow "Manage Content" + And I follow "New content" + When I follow "Folder" + And I fill in "Title" with "My Folder" + And I choose "article_published_false" + And I press "Save" + And I log off + And I go to /freesoftware/my-folder + Then I should see "Access denied" + + @selenium + Scenario: show exception users field when you choose the private option + Given the following communities + | name | identifier | owner | + | Free Software | freesoftware | joaosilva | + And the following users + | login | name | + | mario | Mario Souto | + | maria | Maria Silva | + And "Mario Souto" is a member of "Free Software" + And "Maria Silva" is a member of "Free Software" + And I am on freesoftware's control panel + And I follow "Manage Content" + And I follow "New content" + When I follow "Folder" + And I fill in "Title" with "My Folder" + And I choose "article_published_false" + Then I should see "Fill in the search field to add the exception users to see this content" + + @selenium + Scenario: allowed user should see the content of a folder + Given the following communities + | name | identifier | owner | + | Free Software | freesoftware | joaosilva | + And the following users + | login | name | + | mario | Mario Souto | + | maria | Maria Silva | + And the following articles + | owner | name | body | + | freesoftware | My Folder | ... | + And "Mario Souto" is a member of "Free Software" + And "Maria Silva" is a member of "Free Software" + And I go to /freesoftware/my-folder + When I follow "Edit" + And I choose "article_published_false" + And I press "Save" + And I add to "My Folder" the following exception "Maria Silva" + And I am logged in as "maria" + And I go to /freesoftware/my-folder + Then I should see "My Folder" + Scenario: redirect to the created folder Given I am on joaosilva's control panel And I follow "Manage Content" @@ -40,6 +104,7 @@ Feature: edit article When I follow "Cancel" within ".main-block" Then I should be on joaosilva's cms + @selenium Scenario: display tag list field when creating event Given I am on joaosilva's control panel And I follow "Manage Content" diff --git a/features/step_definitions/content_steps.rb b/features/step_definitions/content_steps.rb index 20ebd3c..3a99ed5 100644 --- a/features/step_definitions/content_steps.rb +++ b/features/step_definitions/content_steps.rb @@ -8,3 +8,15 @@ When /^I create a content of type "([^\"]*)" with the following data$/ do |conte click_button "Save" end + +And /^I add to "([^\"]*)" the following exception "([^\"]*)"$/ do |article_name, user_exception| + article = Article.find_by_name(article_name) + community = article.profile + raise "The article profile is not a community." unless community.class == Community + + my_user = community.members.find_by_name(user_exception) + raise "Could not find #{user_exception} in #{community.name} community." if my_user.nil? + + article.article_privacy_exceptions << my_user + article.save +end \ No newline at end of file diff --git a/public/javascripts/article.js b/public/javascripts/article.js index c26b4e5..9427679 100644 --- a/public/javascripts/article.js +++ b/public/javascripts/article.js @@ -172,4 +172,19 @@ jQuery(function($) { return false; }); + function show_hide_token_input() { + if($("#article_published_false").attr('checked')) + $("#text-input-search-exception-users").parent("div").css('display', 'block'); + else + $("#text-input-search-exception-users").parent("div").css('display', 'none'); + } + + if( $("#token-input-search-article-privacy-exceptions").length == 1 ) { + show_hide_token_input(); + + //Hide / Show the text area + $("#article_published_false").click(show_hide_token_input); + $("#article_published_true").click(show_hide_token_input); + } + }); -- libgit2 0.21.2